diff --git a/app/build.gradle b/app/build.gradle index adc28fb221..88e2b42202 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -28,6 +28,8 @@ ext { ormLiteVersion = "4.46" powermockVersion = "1.7.3" dexmakerVersion = "1.2" + retrofit2Version = '2.8.1' + okhttp3Version="4.4.1" } @@ -125,12 +127,6 @@ android { jvmTarget = '1.8' } lintOptions { - // TODO remove once wear dependency com.google.android.gms:play-services-wearable:7.3.0 - // has been upgraded (requiring significant code changes), which currently fails release - // build with a deprecation warning - // abortOnError false - // (disabled entirely to avoid reports on the error, which would still be displayed - // and it's easy to overlook that it's ignored) checkReleaseBuilds false disable 'MissingTranslation' disable 'ExtraTranslation' @@ -230,9 +226,9 @@ dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'com.google.android.gms:play-services-wearable:17.0.0' implementation "com.google.android.gms:play-services-location:17.0.0" - implementation 'com.google.firebase:firebase-core:17.2.1' - implementation 'com.google.firebase:firebase-auth:19.2.0' - implementation 'com.google.firebase:firebase-database:19.2.0' + implementation 'com.google.firebase:firebase-core:17.2.3' + implementation 'com.google.firebase:firebase-auth:19.3.0' + implementation 'com.google.firebase:firebase-database:19.2.1' implementation('com.crashlytics.sdk.android:crashlytics:2.10.1@aar') { transitive = true; } @@ -246,7 +242,7 @@ dependencies { implementation 'androidx.gridlayout:gridlayout:1.0.0' implementation 'androidx.percentlayout:percentlayout:1.0.0' implementation "androidx.preference:preference-ktx:1.1.0" - implementation 'com.google.android.material:material:1.0.0' + implementation 'com.google.android.material:material:1.1.0' implementation 'com.wdullaer:materialdatetimepicker:4.2.3' implementation "io.reactivex.rxjava2:rxandroid:2.1.1" @@ -257,7 +253,7 @@ dependencies { exclude group: "com.google.android", module: "android" } implementation "org.apache.commons:commons-lang3:3.9" - implementation "org.slf4j:slf4j-api:1.7.29" + implementation 'org.slf4j:slf4j-api:1.7.30' // Graphview cannot be upgraded implementation "com.jjoe64:graphview:4.0.1" implementation "com.joanzapata.iconify:android-iconify-fontawesome:2.2.2" @@ -294,28 +290,23 @@ dependencies { testImplementation "org.powermock:powermock-module-junit4-rule:${powermockVersion}" testImplementation "org.powermock:powermock-module-junit4:${powermockVersion}" testImplementation "joda-time:joda-time:2.10.5" - testImplementation("com.google.truth:truth:0.39") { + testImplementation('com.google.truth:truth:1.0.1') { exclude group: "com.google.guava", module: "guava" exclude group: "com.google.code.findbugs", module: "jsr305" } testImplementation "org.skyscreamer:jsonassert:1.5.0" testImplementation "org.hamcrest:hamcrest-all:1.3" -/* - testImplementation("uk.org.lidalia:slf4j-test:1.2.0") { - exclude group: "com.google.guava", module: "guava" - } -*/ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" // new for tidepool - implementation 'com.squareup.okhttp3:okhttp:4.2.2' - implementation 'com.squareup.okhttp3:logging-interceptor:4.2.2' - implementation "com.squareup.retrofit2:retrofit:2.6.2" - implementation "com.squareup.retrofit2:adapter-rxjava2:2.6.2" - implementation "com.squareup.retrofit2:converter-gson:2.6.2" + implementation "com.squareup.okhttp3:okhttp:$okhttp3Version" + implementation "com.squareup.okhttp3:logging-interceptor:$okhttp3Version" + implementation "com.squareup.retrofit2:retrofit:$retrofit2Version" + implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofit2Version" + implementation "com.squareup.retrofit2:converter-gson:$retrofit2Version" // Phone checker implementation 'com.scottyab:rootbeer-lib:0.0.7' diff --git a/app/src/androidTest/java/info/nightscout/androidaps/RealPumpTest.kt b/app/src/androidTest/java/info/nightscout/androidaps/RealPumpTest.kt index c188d59963..a327989975 100644 --- a/app/src/androidTest/java/info/nightscout/androidaps/RealPumpTest.kt +++ b/app/src/androidTest/java/info/nightscout/androidaps/RealPumpTest.kt @@ -37,8 +37,6 @@ import javax.inject.Inject @RunWith(AndroidJUnit4::class) class RealPumpTest { - private val log = LoggerFactory.getLogger(L.CORE) - companion object { const val R_PASSWORD = 1234 const val R_SERIAL = "PBB00013LR_P" @@ -120,12 +118,12 @@ class RealPumpTest { preparePlugins() while (!pump.isInitialized) { - log.debug("Waiting for initialization") + //log.debug("Waiting for initialization") SystemClock.sleep(1000) } while (true) { - log.debug("Tick") + //log.debug("Tick") SystemClock.sleep(1000) } } diff --git a/app/src/main/java/info/nightscout/androidaps/MainActivity.java b/app/src/main/java/info/nightscout/androidaps/MainActivity.java deleted file mode 100644 index 0d65cbb834..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/MainActivity.java +++ /dev/null @@ -1,370 +0,0 @@ -package info.nightscout.androidaps; - -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.graphics.Rect; -import android.os.Bundle; -import android.os.PersistableBundle; -import android.text.SpannableString; -import android.text.method.LinkMovementMethod; -import android.text.util.Linkify; -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.LinearLayout; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.ActionBarDrawerToggle; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.widget.Toolbar; -import androidx.core.app.ActivityCompat; -import androidx.drawerlayout.widget.DrawerLayout; -import androidx.viewpager.widget.ViewPager; - -import com.google.android.material.navigation.NavigationView; -import com.google.android.material.tabs.TabLayout; -import com.joanzapata.iconify.Iconify; -import com.joanzapata.iconify.fonts.FontAwesomeModule; - -import javax.inject.Inject; - -import dagger.android.AndroidInjection; -import info.nightscout.androidaps.activities.NoSplashAppCompatActivity; -import info.nightscout.androidaps.activities.PreferencesActivity; -import info.nightscout.androidaps.activities.SingleFragmentActivity; -import info.nightscout.androidaps.activities.StatsActivity; -import info.nightscout.androidaps.events.EventAppExit; -import info.nightscout.androidaps.events.EventPreferenceChange; -import info.nightscout.androidaps.events.EventRebuildTabs; -import info.nightscout.androidaps.historyBrowser.HistoryBrowseActivity; -import info.nightscout.androidaps.interfaces.ActivePluginProvider; -import info.nightscout.androidaps.interfaces.PluginBase; -import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; -import info.nightscout.androidaps.plugins.bus.RxBusWrapper; -import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerUtils; -import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus; -import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin; -import info.nightscout.androidaps.setupwizard.SetupWizardActivity; -import info.nightscout.androidaps.tabs.TabPageAdapter; -import info.nightscout.androidaps.utils.AndroidPermission; -import info.nightscout.androidaps.utils.FabricPrivacy; -import info.nightscout.androidaps.utils.LocaleHelper; -import info.nightscout.androidaps.utils.OKDialog; -import info.nightscout.androidaps.utils.buildHelper.BuildHelper; -import info.nightscout.androidaps.utils.resources.ResourceHelper; -import info.nightscout.androidaps.utils.sharedPreferences.SP; -import info.nightscout.androidaps.utils.protection.ProtectionCheck; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.CompositeDisposable; - -import static info.nightscout.androidaps.utils.extensions.EspressoTestHelperKt.isRunningRealPumpTest; - -public class MainActivity extends NoSplashAppCompatActivity { - - private CompositeDisposable disposable = new CompositeDisposable(); - private ActionBarDrawerToggle actionBarDrawerToggle; - private MenuItem pluginPreferencesMenuItem; - - @Inject AAPSLogger aapsLogger; - @Inject RxBusWrapper rxBus; - @Inject SP sp; - @Inject ResourceHelper resourceHelper; - @Inject VersionCheckerUtils versionCheckerUtils; - @Inject SmsCommunicatorPlugin smsCommunicatorPlugin; - @Inject LoopPlugin loopPlugin; - @Inject NSSettingsStatus nsSettingsStatus; - @Inject BuildHelper buildHelper; - @Inject ActivePluginProvider activePlugin; - @Inject FabricPrivacy fabricPrivacy; - @Inject ProtectionCheck protectionCheck; - - - @Override - public void onCreate(Bundle savedInstanceState) { - AndroidInjection.inject(this); - super.onCreate(savedInstanceState); - - Iconify.with(new FontAwesomeModule()); - LocaleHelper.INSTANCE.update(getApplicationContext()); - - 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 - processPreferenceChange(new EventPreferenceChange(resourceHelper.gs(R.string.key_keep_screen_on))); - - 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) { - } - }); - - //Check here if loop plugin is disabled. Else check via constraints - if (!loopPlugin.isEnabled(PluginType.LOOP)) - versionCheckerUtils.triggerCheckVersion(); - - fabricPrivacy.setUserStats(); - - setupTabs(); - setupViews(); - - disposable.add(rxBus - .toObservable(EventRebuildTabs.class) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(event -> { - LocaleHelper.INSTANCE.update(getApplicationContext()); - if (event.getRecreate()) { - recreate(); - } else { - setupTabs(); - setupViews(); - } - setWakeLock(); - }, exception -> fabricPrivacy.logException(exception)) - ); - disposable.add(rxBus - .toObservable(EventPreferenceChange.class) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(this::processPreferenceChange, exception -> fabricPrivacy.logException(exception)) - ); - - if (!sp.getBoolean(R.string.key_setupwizard_processed, false) && !isRunningRealPumpTest()) { - Intent intent = new Intent(this, SetupWizardActivity.class); - startActivity(intent); - } - - AndroidPermission.notifyForStoragePermission(this); - AndroidPermission.notifyForBatteryOptimizationPermission(this); - if (Config.PUMPDRIVERS) { - AndroidPermission.notifyForLocationPermissions(this); - AndroidPermission.notifyForSMSPermissions(this, smsCommunicatorPlugin); - AndroidPermission.notifyForSystemWindowPermissions(this); - } - } - - 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 - public void onDestroy() { - super.onDestroy(); - disposable.clear(); - } - - @Override - protected void onResume() { - super.onResume(); - protectionCheck.queryProtection(this, ProtectionCheck.Protection.APPLICATION, null, this::finish, this::finish); - } - - private void setWakeLock() { - boolean keepScreenOn = 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); - } - - public void processPreferenceChange(final EventPreferenceChange ev) { - if (ev.isChanged(resourceHelper, R.string.key_keep_screen_on)) - setWakeLock(); - } - - private void setupViews() { - TabPageAdapter pageAdapter = new TabPageAdapter(getSupportFragmentManager(), this); - NavigationView navigationView = findViewById(R.id.navigation_view); - navigationView.setNavigationItemSelectedListener(menuItem -> true); - Menu menu = navigationView.getMenu(); - menu.clear(); - for (PluginBase p : activePlugin.getPluginsList()) { - pageAdapter.registerNewFragment(p); - if (p.hasFragment() && !p.isFragmentVisible() && p.isEnabled(p.getPluginDescription().getType()) && !p.getPluginDescription().neverVisible) { - MenuItem menuItem = menu.add(p.getName()); - menuItem.setCheckable(true); - menuItem.setOnMenuItemClickListener(item -> { - Intent intent = new Intent(this, SingleFragmentActivity.class); - intent.putExtra("plugin", activePlugin.getPluginsList().indexOf(p)); - startActivity(intent); - ((DrawerLayout) findViewById(R.id.drawer_layout)).closeDrawers(); - return true; - }); - } - } - ViewPager mPager = findViewById(R.id.pager); - mPager.setAdapter(pageAdapter); - //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()))); - } - } - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull 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 - OKDialog.show(this, "", resourceHelper.gs(R.string.alert_dialog_storage_permission_text)); - break; - case AndroidPermission.CASE_LOCATION: - case AndroidPermission.CASE_SMS: - case AndroidPermission.CASE_BATTERY: - case AndroidPermission.CASE_PHONE_STATE: - case AndroidPermission.CASE_SYSTEM_WINDOW: - break; - } - } - } - } - - @Override - public boolean dispatchTouchEvent(MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_DOWN) { - View v = getCurrentFocus(); - if (v instanceof EditText) { - Rect outRect = new Rect(); - v.getGlobalVisibleRect(outRect); - if (!outRect.contains((int) event.getRawX(), (int) event.getRawY())) { - v.clearFocus(); - InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(v.getWindowToken(), 0); - } - } - } - return super.dispatchTouchEvent(event); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.menu_main, menu); - pluginPreferencesMenuItem = menu.findItem(R.id.nav_plugin_preferences); - checkPluginPreferences(findViewById(R.id.pager)); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - int id = item.getItemId(); - switch (id) { - case R.id.nav_preferences: - protectionCheck.queryProtection(this, ProtectionCheck.Protection.PREFERENCES, () -> { - Intent i = new Intent(this, PreferencesActivity.class); - i.putExtra("id", -1); - startActivity(i); - }); - 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_about: - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(resourceHelper.gs(R.string.app_name) + " " + BuildConfig.VERSION); - builder.setIcon(resourceHelper.getIcon()); - String message = "Build: " + BuildConfig.BUILDVERSION + "\n"; - message += "Flavor: " + BuildConfig.FLAVOR + BuildConfig.BUILD_TYPE + "\n"; - message += resourceHelper.gs(R.string.configbuilder_nightscoutversion_label) + " " + nsSettingsStatus.getNightscoutVersionName(); - if (buildHelper.isEngineeringMode()) - message += "\n" + resourceHelper.gs(R.string.engineering_mode_enabled); - message += resourceHelper.gs(R.string.about_link_urls); - final SpannableString messageSpanned = new SpannableString(message); - Linkify.addLinks(messageSpanned, Linkify.WEB_URLS); - builder.setMessage(messageSpanned); - builder.setPositiveButton(resourceHelper.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: - aapsLogger.debug(LTag.CORE, "Exiting"); - rxBus.send(new EventAppExit()); - 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()); - protectionCheck.queryProtection(this, ProtectionCheck.Protection.PREFERENCES, () -> { - Intent i = new Intent(this, PreferencesActivity.class); - i.putExtra("id", plugin.getPreferencesId()); - startActivity(i); - }); - return true; -/* - case R.id.nav_survey: - startActivity(new Intent(this, SurveyActivity.class)); - return true; -*/ - case R.id.nav_stats: - startActivity(new Intent(this, StatsActivity.class)); - return true; - } - return actionBarDrawerToggle.onOptionsItemSelected(item); - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/MainActivity.kt b/app/src/main/java/info/nightscout/androidaps/MainActivity.kt new file mode 100644 index 0000000000..90cf353d13 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/MainActivity.kt @@ -0,0 +1,333 @@ +package info.nightscout.androidaps + +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.graphics.Rect +import android.os.Bundle +import android.os.PersistableBundle +import android.text.SpannableString +import android.text.method.LinkMovementMethod +import android.text.util.Linkify +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.LinearLayout +import android.widget.TextView +import androidx.appcompat.app.ActionBarDrawerToggle +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.widget.Toolbar +import androidx.core.app.ActivityCompat +import androidx.drawerlayout.widget.DrawerLayout +import androidx.viewpager.widget.ViewPager +import com.google.android.material.navigation.NavigationView +import com.google.android.material.tabs.TabLayout +import com.joanzapata.iconify.Iconify +import com.joanzapata.iconify.fonts.FontAwesomeModule +import info.nightscout.androidaps.activities.NoSplashAppCompatActivity +import info.nightscout.androidaps.activities.PreferencesActivity +import info.nightscout.androidaps.activities.SingleFragmentActivity +import info.nightscout.androidaps.activities.StatsActivity +import info.nightscout.androidaps.events.EventAppExit +import info.nightscout.androidaps.events.EventPreferenceChange +import info.nightscout.androidaps.events.EventRebuildTabs +import info.nightscout.androidaps.historyBrowser.HistoryBrowseActivity +import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.PluginType +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerUtils +import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus +import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin +import info.nightscout.androidaps.setupwizard.SetupWizardActivity +import info.nightscout.androidaps.tabs.TabPageAdapter +import info.nightscout.androidaps.utils.AndroidPermission +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.LocaleHelper.update +import info.nightscout.androidaps.utils.OKDialog.show +import info.nightscout.androidaps.utils.buildHelper.BuildHelper +import info.nightscout.androidaps.utils.extensions.isRunningRealPumpTest +import info.nightscout.androidaps.utils.protection.ProtectionCheck +import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.androidaps.utils.sharedPreferences.SP +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import kotlinx.android.synthetic.main.activity_main.* +import javax.inject.Inject +import kotlin.system.exitProcess + +class MainActivity : NoSplashAppCompatActivity() { + private val disposable = CompositeDisposable() + + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var rxBus: RxBusWrapper + @Inject lateinit var androidPermission: AndroidPermission + @Inject lateinit var sp: SP + @Inject lateinit var resourceHelper: ResourceHelper + @Inject lateinit var versionCheckerUtils: VersionCheckerUtils + @Inject lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin + @Inject lateinit var loopPlugin: LoopPlugin + @Inject lateinit var nsSettingsStatus: NSSettingsStatus + @Inject lateinit var buildHelper: BuildHelper + @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var fabricPrivacy: FabricPrivacy + @Inject lateinit var protectionCheck: ProtectionCheck + + private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle + private var pluginPreferencesMenuItem: MenuItem? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + Iconify.with(FontAwesomeModule()) + update(applicationContext) + setContentView(R.layout.activity_main) + setSupportActionBar(toolbar) + supportActionBar?.setDisplayShowTitleEnabled(false) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + supportActionBar?.setHomeButtonEnabled(true) + actionBarDrawerToggle = ActionBarDrawerToggle(this, drawer_layout, R.string.open_navigation, R.string.close_navigation).also { + drawer_layout.addDrawerListener(it) + it.syncState() + } + + // initialize screen wake lock + processPreferenceChange(EventPreferenceChange(resourceHelper.gs(R.string.key_keep_screen_on))) + pager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { + override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {} + override fun onPageSelected(position: Int) { + checkPluginPreferences(pager) + } + + override fun onPageScrollStateChanged(state: Int) {} + }) + + //Check here if loop plugin is disabled. Else check via constraints + if (!loopPlugin.isEnabled(PluginType.LOOP)) versionCheckerUtils.triggerCheckVersion() + fabricPrivacy.setUserStats() + setupTabs() + setupViews() + disposable.add(rxBus + .toObservable(EventRebuildTabs::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + update(applicationContext) + if (it.recreate) recreate() + else { + setupTabs() + setupViews() + } + setWakeLock() + }) { fabricPrivacy.logException(it) } + ) + disposable.add(rxBus + .toObservable(EventPreferenceChange::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ processPreferenceChange(it) }) { fabricPrivacy.logException(it) } + ) + if (!sp.getBoolean(R.string.key_setupwizard_processed, false) && !isRunningRealPumpTest()) { + val intent = Intent(this, SetupWizardActivity::class.java) + startActivity(intent) + } + androidPermission.notifyForStoragePermission(this) + androidPermission.notifyForBatteryOptimizationPermission(this) + if (Config.PUMPDRIVERS) { + androidPermission.notifyForLocationPermissions(this) + androidPermission.notifyForSMSPermissions(this, smsCommunicatorPlugin) + androidPermission.notifyForSystemWindowPermissions(this) + } + } + + private fun checkPluginPreferences(viewPager: ViewPager) { + pluginPreferencesMenuItem?.isEnabled = (viewPager.adapter as TabPageAdapter).getPluginAt(viewPager.currentItem).preferencesId != -1 + } + + override fun onPostCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) { + super.onPostCreate(savedInstanceState, persistentState) + actionBarDrawerToggle.syncState() + } + + public override fun onDestroy() { + super.onDestroy() + disposable.clear() + } + + override fun onResume() { + super.onResume() + protectionCheck.queryProtection(this, ProtectionCheck.Protection.APPLICATION, null, Runnable { finish() }, Runnable { finish() }) + } + + private fun setWakeLock() { + val keepScreenOn = sp.getBoolean(R.string.key_keep_screen_on, false) + if (keepScreenOn) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) else window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + } + + private fun processPreferenceChange(ev: EventPreferenceChange) { + if (ev.isChanged(resourceHelper, R.string.key_keep_screen_on)) setWakeLock() + } + + private fun setupViews() { + val pageAdapter = TabPageAdapter(supportFragmentManager, this) + val navigationView = findViewById(R.id.navigation_view) + navigationView.setNavigationItemSelectedListener { true } + val menu = navigationView.menu.also { it.clear() } + for (p in activePlugin.pluginsList) { + pageAdapter.registerNewFragment(p) + if (p.hasFragment() && !p.isFragmentVisible() && p.isEnabled(p.pluginDescription.type) && !p.pluginDescription.neverVisible) { + val menuItem = menu.add(p.name) + menuItem.isCheckable = true + menuItem.setOnMenuItemClickListener { + val intent = Intent(this, SingleFragmentActivity::class.java) + intent.putExtra("plugin", activePlugin.pluginsList.indexOf(p)) + startActivity(intent) + (findViewById(R.id.drawer_layout) as DrawerLayout).closeDrawers() + true + } + } + } + val mPager = findViewById(R.id.pager) + mPager.adapter = pageAdapter + //if (switchToLast) + // mPager.setCurrentItem(pageAdapter.getCount() - 1, false); + checkPluginPreferences(mPager) + } + + private fun setupTabs() { + val viewPager = findViewById(R.id.pager) + val normalTabs = findViewById(R.id.tabs_normal) + normalTabs.setupWithViewPager(viewPager, true) + val compactTabs = findViewById(R.id.tabs_compact) + compactTabs.setupWithViewPager(viewPager, true) + val toolbar = findViewById(R.id.toolbar) + if (sp.getBoolean(R.string.key_short_tabtitles, false)) { + normalTabs.visibility = View.GONE + compactTabs.visibility = View.VISIBLE + toolbar.layoutParams = LinearLayout.LayoutParams(Toolbar.LayoutParams.MATCH_PARENT, resources.getDimension(R.dimen.compact_height).toInt()) + } else { + normalTabs.visibility = View.VISIBLE + compactTabs.visibility = View.GONE + val typedValue = TypedValue() + if (theme.resolveAttribute(R.attr.actionBarSize, typedValue, true)) { + toolbar.layoutParams = LinearLayout.LayoutParams(Toolbar.LayoutParams.MATCH_PARENT, + TypedValue.complexToDimensionPixelSize(typedValue.data, resources.displayMetrics)) + } + } + } + + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + if (permissions.isNotEmpty()) { + if (ActivityCompat.checkSelfPermission(this, permissions[0]) == PackageManager.PERMISSION_GRANTED) { + when (requestCode) { + AndroidPermission.CASE_STORAGE -> //show dialog after permission is granted + show(this, "", resourceHelper.gs(R.string.alert_dialog_storage_permission_text)) + + AndroidPermission.CASE_LOCATION, AndroidPermission.CASE_SMS, AndroidPermission.CASE_BATTERY, AndroidPermission.CASE_PHONE_STATE, AndroidPermission.CASE_SYSTEM_WINDOW -> { + } + } + } + } + } + + override fun dispatchTouchEvent(event: MotionEvent): Boolean { + if (event.action == MotionEvent.ACTION_DOWN) { + val v = currentFocus + if (v is EditText) { + val outRect = Rect() + v.getGlobalVisibleRect(outRect) + if (!outRect.contains(event.rawX.toInt(), event.rawY.toInt())) { + v.clearFocus() + val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + imm.hideSoftInputFromWindow(v.getWindowToken(), 0) + } + } + } + return super.dispatchTouchEvent(event) + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.menu_main, menu) + pluginPreferencesMenuItem = menu.findItem(R.id.nav_plugin_preferences) + checkPluginPreferences(findViewById(R.id.pager)) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.nav_preferences -> { + protectionCheck.queryProtection(this, ProtectionCheck.Protection.PREFERENCES, Runnable { + val i = Intent(this, PreferencesActivity::class.java) + i.putExtra("id", -1) + startActivity(i) + }) + return true + } + + R.id.nav_historybrowser -> { + startActivity(Intent(this, HistoryBrowseActivity::class.java)) + return true + } + + R.id.nav_setupwizard -> { + startActivity(Intent(this, SetupWizardActivity::class.java)) + return true + } + + R.id.nav_about -> { + var message = "Build: ${BuildConfig.BUILDVERSION}\n" + message += "Flavor: ${BuildConfig.FLAVOR}${BuildConfig.BUILD_TYPE}\n" + message += "${resourceHelper.gs(R.string.configbuilder_nightscoutversion_label)} ${nsSettingsStatus.nightscoutVersionName}" + if (buildHelper.isEngineeringMode()) message += "\n${resourceHelper.gs(R.string.engineering_mode_enabled)}" + message += resourceHelper.gs(R.string.about_link_urls) + val messageSpanned = SpannableString(message) + Linkify.addLinks(messageSpanned, Linkify.WEB_URLS) + AlertDialog.Builder(this) + .setTitle(resourceHelper.gs(R.string.app_name) + " " + BuildConfig.VERSION) + .setIcon(resourceHelper.getIcon()) + .setMessage(messageSpanned) + .setPositiveButton(resourceHelper.gs(R.string.ok), null) + .create().also { + it.show() + (it.findViewById(android.R.id.message) as TextView).movementMethod = LinkMovementMethod.getInstance() + } + return true + } + + R.id.nav_exit -> { + aapsLogger.debug(LTag.CORE, "Exiting") + rxBus.send(EventAppExit()) + finish() + System.runFinalization() + exitProcess(0) + } + + R.id.nav_plugin_preferences -> { + val viewPager = findViewById(R.id.pager) + val plugin = (viewPager.adapter as TabPageAdapter).getPluginAt(viewPager.currentItem) + protectionCheck.queryProtection(this, ProtectionCheck.Protection.PREFERENCES, Runnable { + val i = Intent(this, PreferencesActivity::class.java) + i.putExtra("id", plugin.preferencesId) + startActivity(i) + }) + return true + } +/* + R.id.nav_survey -> { + startActivity(Intent(this, SurveyActivity::class.java)) + return true + } +*/ + R.id.nav_stats -> { + startActivity(Intent(this, StatsActivity::class.java)) + return true + } + } + return actionBarDrawerToggle.onOptionsItemSelected(item) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/MainApp.java b/app/src/main/java/info/nightscout/androidaps/MainApp.java index cfa6594374..22b278abba 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainApp.java +++ b/app/src/main/java/info/nightscout/androidaps/MainApp.java @@ -3,6 +3,7 @@ package info.nightscout.androidaps; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; +import android.bluetooth.BluetoothDevice; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -25,6 +26,8 @@ import net.danlew.android.joda.JodaTimeAndroid; import org.json.JSONException; +import java.util.List; + import javax.inject.Inject; import dagger.android.AndroidInjector; @@ -33,69 +36,20 @@ import dagger.android.HasAndroidInjector; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.db.DatabaseHelper; import info.nightscout.androidaps.dependencyInjection.DaggerAppComponent; +import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.logging.AAPSLogger; import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; -import info.nightscout.androidaps.plugins.aps.openAPSAMA.OpenAPSAMAPlugin; -import info.nightscout.androidaps.plugins.aps.openAPSMA.OpenAPSMAPlugin; -import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.PluginStore; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction; -import info.nightscout.androidaps.plugins.constraints.dstHelper.DstHelperPlugin; -import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin; -import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin; -import info.nightscout.androidaps.plugins.constraints.signatureVerifier.SignatureVerifierPlugin; -import info.nightscout.androidaps.plugins.constraints.storage.StorageConstraintPlugin; -import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerPlugin; import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerUtils; -import info.nightscout.androidaps.plugins.general.actions.ActionsPlugin; -import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin; -import info.nightscout.androidaps.plugins.general.careportal.CareportalPlugin; -import info.nightscout.androidaps.plugins.general.dataBroadcaster.DataBroadcastPlugin; -import info.nightscout.androidaps.plugins.general.food.FoodPlugin; -import info.nightscout.androidaps.plugins.general.maintenance.MaintenancePlugin; -import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; -import info.nightscout.androidaps.plugins.general.overview.OverviewPlugin; -import info.nightscout.androidaps.plugins.general.persistentNotification.PersistentNotificationPlugin; -import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin; -import info.nightscout.androidaps.plugins.general.tidepool.TidepoolPlugin; -import info.nightscout.androidaps.plugins.general.wear.WearPlugin; -import info.nightscout.androidaps.plugins.general.xdripStatusline.StatusLinePlugin; -import info.nightscout.androidaps.plugins.insulin.InsulinOrefFreePeakPlugin; -import info.nightscout.androidaps.plugins.insulin.InsulinOrefRapidActingPlugin; -import info.nightscout.androidaps.plugins.insulin.InsulinOrefUltraRapidActingPlugin; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; -import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin; -import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin; -import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin; -import info.nightscout.androidaps.plugins.pump.danaR.DanaRPlugin; -import info.nightscout.androidaps.plugins.pump.danaRKorean.DanaRKoreanPlugin; -import info.nightscout.androidaps.plugins.pump.danaRS.DanaRSPlugin; -import info.nightscout.androidaps.plugins.pump.danaRv2.DanaRv2Plugin; -import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin; -import info.nightscout.androidaps.plugins.pump.mdi.MDIPlugin; -import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin; -import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin; -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.source.DexcomPlugin; -import info.nightscout.androidaps.plugins.source.EversensePlugin; -import info.nightscout.androidaps.plugins.source.GlimpPlugin; -import info.nightscout.androidaps.plugins.source.MM640gPlugin; -import info.nightscout.androidaps.plugins.source.NSClientSourcePlugin; -import info.nightscout.androidaps.plugins.source.PoctechPlugin; -import info.nightscout.androidaps.plugins.source.RandomBgPlugin; -import info.nightscout.androidaps.plugins.source.TomatoPlugin; -import info.nightscout.androidaps.plugins.source.XdripPlugin; -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; +import info.nightscout.androidaps.receivers.BTReceiver; import info.nightscout.androidaps.receivers.ChargingStateReceiver; import info.nightscout.androidaps.receivers.DataReceiver; import info.nightscout.androidaps.receivers.KeepAliveReceiver; import info.nightscout.androidaps.receivers.NetworkChangeReceiver; +import info.nightscout.androidaps.receivers.ReceiverStatusStore; import info.nightscout.androidaps.receivers.TimeDateOrTZChangeReceiver; import info.nightscout.androidaps.services.Intents; import info.nightscout.androidaps.utils.ActivityMonitor; @@ -114,9 +68,6 @@ public class MainApp extends DaggerApplication { static DatabaseHelper sDatabaseHelper = null; - DataReceiver dataReceiver = new DataReceiver(); - TimeDateOrTZChangeReceiver timeDateOrTZChangeReceiver; - private String CHANNEL_ID = "AndroidAPS-Ongoing"; // TODO: move to OngoingNotificationProvider (and dagger) private int ONGOING_NOTIFICATION_ID = 4711; // TODO: move to OngoingNotificationProvider (and dagger) private Notification notification; // TODO: move to OngoingNotificationProvider (and dagger) @@ -124,6 +75,7 @@ public class MainApp extends DaggerApplication { @Inject PluginStore pluginStore; @Inject public HasAndroidInjector injector; @Inject AAPSLogger aapsLogger; + @Inject ReceiverStatusStore receiverStatusStore; @Inject ActivityMonitor activityMonitor; @Inject FabricPrivacy fabricPrivacy; @Inject ResourceHelper resourceHelper; @@ -131,60 +83,9 @@ public class MainApp extends DaggerApplication { @Inject SP sp; @Inject ProfileFunction profileFunction; - @Inject ActionsPlugin actionsPlugin; - @Inject AutomationPlugin automationPlugin; - @Inject ComboPlugin comboPlugin; - @Inject CareportalPlugin careportalPlugin; @Inject ConfigBuilderPlugin configBuilderPlugin; - @Inject DanaRPlugin danaRPlugin; - @Inject DanaRSPlugin danaRSPlugin; - @Inject DanaRv2Plugin danaRv2Plugin; - @Inject DanaRKoreanPlugin danaRKoreanPlugin; - @Inject DataBroadcastPlugin dataBroadcastPlugin; - @Inject DstHelperPlugin dstHelperPlugin; - @Inject FoodPlugin foodPlugin; - @Inject InsulinOrefFreePeakPlugin insulinOrefFreePeakPlugin; - @Inject InsulinOrefRapidActingPlugin insulinOrefRapidActingPlugin; - @Inject InsulinOrefUltraRapidActingPlugin insulinOrefUltraRapidActingPlugin; - @Inject IobCobCalculatorPlugin iobCobCalculatorPlugin; - @Inject LocalInsightPlugin localInsightPlugin; - @Inject LocalProfilePlugin localProfilePlugin; - @Inject LoopPlugin loopPlugin; - @Inject MedtronicPumpPlugin medtronicPumpPlugin; - @Inject MDIPlugin mdiPlugin; - @Inject NSProfilePlugin nsProfilePlugin; - @Inject ObjectivesPlugin objectivesPlugin; - @Inject SafetyPlugin safetyPlugin; - @Inject SmsCommunicatorPlugin smsCommunicatorPlugin; - @Inject OpenAPSMAPlugin openAPSMAPlugin; - @Inject OpenAPSAMAPlugin openAPSAMAPlugin; - @Inject OpenAPSSMBPlugin openAPSSMBPlugin; - @Inject OverviewPlugin overviewPlugin; - @Inject PersistentNotificationPlugin persistentNotificationPlugin; - @Inject RandomBgPlugin randomBgPlugin; - @Inject SensitivityOref1Plugin sensitivityOref1Plugin; - @Inject SensitivityAAPSPlugin sensitivityAAPSPlugin; - @Inject SensitivityOref0Plugin sensitivityOref0Plugin; - @Inject SensitivityWeightedAveragePlugin sensitivityWeightedAveragePlugin; - @Inject SignatureVerifierPlugin signatureVerifierPlugin; - @Inject StorageConstraintPlugin storageConstraintPlugin; - @Inject DexcomPlugin dexcomPlugin; - @Inject EversensePlugin eversensePlugin; - @Inject GlimpPlugin glimpPlugin; - @Inject MaintenancePlugin maintenancePlugin; - @Inject MM640gPlugin mM640GPlugin; - @Inject NSClientPlugin nsClientPlugin; - @Inject NSClientSourcePlugin nSClientSourcePlugin; - @Inject PoctechPlugin poctechPlugin; - @Inject TomatoPlugin tomatoPlugin; - @Inject XdripPlugin xdripPlugin; - @Inject StatusLinePlugin statusLinePlugin; - @Inject TidepoolPlugin tidepoolPlugin; - @Inject TreatmentsPlugin treatmentsPlugin; - @Inject VirtualPumpPlugin virtualPumpPlugin; - @Inject VersionCheckerPlugin versionCheckerPlugin; - @Inject WearPlugin wearPlugin; @Inject KeepAliveReceiver.KeepAliveManager keepAliveManager; + @Inject List plugins; @Override public void onCreate() { @@ -197,15 +98,13 @@ public class MainApp extends DaggerApplication { generateEmptyNotification(); sDatabaseHelper = OpenHelperManager.getHelper(sInstance, DatabaseHelper.class); -/* TODO: put back Thread.setDefaultUncaughtExceptionHandler((thread, ex) -> { if (ex instanceof InternalError) { // usually the app trying to spawn a thread while being killed return; } - log.error("Uncaught exception crashing app", ex); + aapsLogger.error("Uncaught exception crashing app", ex); }); -*/ try { if (fabricPrivacy.fabricEnabled()) { @@ -232,62 +131,7 @@ public class MainApp extends DaggerApplication { versionCheckersUtils.triggerCheckVersion(); // Register all tabs in app here - pluginStore.add(overviewPlugin); - pluginStore.add(iobCobCalculatorPlugin); - if (!Config.NSCLIENT) pluginStore.add(actionsPlugin); - pluginStore.add(insulinOrefRapidActingPlugin); - pluginStore.add(insulinOrefUltraRapidActingPlugin); - pluginStore.add(insulinOrefFreePeakPlugin); - pluginStore.add(sensitivityOref0Plugin); - pluginStore.add(sensitivityAAPSPlugin); - pluginStore.add(sensitivityWeightedAveragePlugin); - pluginStore.add(sensitivityOref1Plugin); - if (Config.PUMPDRIVERS) pluginStore.add(danaRPlugin); - if (Config.PUMPDRIVERS) pluginStore.add(danaRKoreanPlugin); - if (Config.PUMPDRIVERS) pluginStore.add(danaRv2Plugin); - if (Config.PUMPDRIVERS) pluginStore.add(danaRSPlugin); - if (Config.PUMPDRIVERS) pluginStore.add(localInsightPlugin); - if (Config.PUMPDRIVERS) pluginStore.add(comboPlugin); - if (Config.PUMPDRIVERS) pluginStore.add(medtronicPumpPlugin); - if (!Config.NSCLIENT) pluginStore.add(mdiPlugin); - if (!Config.NSCLIENT) pluginStore.add(virtualPumpPlugin); - if (Config.NSCLIENT) pluginStore.add(careportalPlugin); - if (Config.APS) pluginStore.add(loopPlugin); - if (Config.APS) pluginStore.add(openAPSMAPlugin); - if (Config.APS) pluginStore.add(openAPSAMAPlugin); - if (Config.APS) pluginStore.add(openAPSSMBPlugin); - pluginStore.add(nsProfilePlugin); - if (!Config.NSCLIENT) pluginStore.add(localProfilePlugin); - pluginStore.add(treatmentsPlugin); - if (!Config.NSCLIENT) pluginStore.add(safetyPlugin); - if (!Config.NSCLIENT) pluginStore.add(versionCheckerPlugin); - if (Config.APS) pluginStore.add(storageConstraintPlugin); - if (Config.APS) pluginStore.add(signatureVerifierPlugin); - if (Config.APS) pluginStore.add(objectivesPlugin); - pluginStore.add(xdripPlugin); - pluginStore.add(nSClientSourcePlugin); - pluginStore.add(mM640GPlugin); - pluginStore.add(glimpPlugin); - pluginStore.add(dexcomPlugin); - pluginStore.add(poctechPlugin); - pluginStore.add(tomatoPlugin); - pluginStore.add(eversensePlugin); - pluginStore.add(randomBgPlugin); - if (!Config.NSCLIENT) pluginStore.add(smsCommunicatorPlugin); - pluginStore.add(foodPlugin); - - pluginStore.add(wearPlugin); - pluginStore.add(statusLinePlugin); - pluginStore.add(persistentNotificationPlugin); - pluginStore.add(nsClientPlugin); -// if (engineeringMode) pluginsList.add(tidepoolPlugin); - pluginStore.add(maintenancePlugin); - pluginStore.add(automationPlugin); - pluginStore.add(dstHelperPlugin); - pluginStore.add(dataBroadcastPlugin); - - pluginStore.add(configBuilderPlugin); - + pluginStore.setPlugins(plugins); configBuilderPlugin.initialize(); NSUpload.uploadAppStart(); @@ -330,24 +174,38 @@ public class MainApp extends DaggerApplication { } private void registerLocalBroadcastReceiver() { - LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this); - lbm.registerReceiver(dataReceiver, new IntentFilter(Intents.ACTION_NEW_TREATMENT)); - lbm.registerReceiver(dataReceiver, new IntentFilter(Intents.ACTION_CHANGED_TREATMENT)); - lbm.registerReceiver(dataReceiver, new IntentFilter(Intents.ACTION_REMOVED_TREATMENT)); - lbm.registerReceiver(dataReceiver, new IntentFilter(Intents.ACTION_NEW_SGV)); - lbm.registerReceiver(dataReceiver, new IntentFilter(Intents.ACTION_NEW_PROFILE)); - lbm.registerReceiver(dataReceiver, new IntentFilter(Intents.ACTION_NEW_MBG)); - lbm.registerReceiver(dataReceiver, new IntentFilter(Intents.ACTION_NEW_CAL)); + IntentFilter filter = new IntentFilter(); + filter.addAction(Intents.ACTION_NEW_TREATMENT); + filter.addAction(Intents.ACTION_CHANGED_TREATMENT); + filter.addAction(Intents.ACTION_REMOVED_TREATMENT); + filter.addAction(Intents.ACTION_NEW_SGV); + filter.addAction(Intents.ACTION_NEW_PROFILE); + filter.addAction(Intents.ACTION_NEW_MBG); + filter.addAction(Intents.ACTION_NEW_CAL); + LocalBroadcastManager.getInstance(this).registerReceiver(new DataReceiver(), filter); - this.timeDateOrTZChangeReceiver = new TimeDateOrTZChangeReceiver(); - this.timeDateOrTZChangeReceiver.registerBroadcasts(this); + filter = new IntentFilter(); + filter.addAction(Intent.ACTION_TIME_CHANGED); + filter.addAction(Intent.ACTION_DATE_CHANGED); + filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); + registerReceiver(new TimeDateOrTZChangeReceiver(), filter); - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); - intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); - intentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); - registerReceiver(new NetworkChangeReceiver(), intentFilter); - registerReceiver(new ChargingStateReceiver(), new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + filter = new IntentFilter(); + filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); + filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); + filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); + registerReceiver(new NetworkChangeReceiver(), filter); + + filter = new IntentFilter(); + filter.addAction(Intent.ACTION_POWER_CONNECTED); + filter.addAction(Intent.ACTION_POWER_DISCONNECTED); + filter.addAction(Intent.ACTION_BATTERY_CHANGED); + registerReceiver(new ChargingStateReceiver(), filter); + + filter = new IntentFilter(); + filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); + filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); + registerReceiver(new BTReceiver(), filter); } @Deprecated @@ -421,11 +279,7 @@ public class MainApp extends DaggerApplication { @Override public void onTerminate() { - aapsLogger.debug(LTag.CORE, "onTerminate"); - - if (timeDateOrTZChangeReceiver != null) - unregisterReceiver(timeDateOrTZChangeReceiver); unregisterActivityLifecycleCallbacks(activityMonitor); keepAliveManager.cancelAlarm(this); super.onTerminate(); diff --git a/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt index 86bee1bca2..187ab7b33d 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt @@ -52,6 +52,7 @@ import info.nightscout.androidaps.plugins.source.PoctechPlugin import info.nightscout.androidaps.plugins.source.TomatoPlugin import info.nightscout.androidaps.utils.OKDialog.show import info.nightscout.androidaps.utils.SafeParse +import info.nightscout.androidaps.utils.protection.PasswordCheck import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP @@ -98,6 +99,8 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang @Inject lateinit var wearPlugin: WearPlugin @Inject lateinit var maintenancePlugin: MaintenancePlugin + @Inject lateinit var passwordCheck: PasswordCheck + @Inject lateinit var androidInjector: DispatchingAndroidInjector override fun androidInjector(): AndroidInjector = androidInjector @@ -185,7 +188,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang addPreferencesFromResource(R.xml.pref_datachoices, rootKey) addPreferencesFromResourceIfEnabled(maintenancePlugin, rootKey) } - initSummary(preferenceScreen) + initSummary(preferenceScreen, pluginId != -1) for (plugin in pluginStore.plugins) { plugin.preprocessPreferences(this) } @@ -254,19 +257,19 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang // Preferences if (pref.getKey() == resourceHelper.gs(R.string.key_settings_protection)) { val pass: Preference? = findPreference(resourceHelper.gs(R.string.key_settings_password)) - if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.PASSWORD.ordinal.toString() + if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.CUSTOM_PASSWORD.ordinal.toString() } // Application // Application if (pref.getKey() == resourceHelper.gs(R.string.key_application_protection)) { val pass: Preference? = findPreference(resourceHelper.gs(R.string.key_application_password)) - if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.PASSWORD.ordinal.toString() + if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.CUSTOM_PASSWORD.ordinal.toString() } // Bolus // Bolus if (pref.getKey() == resourceHelper.gs(R.string.key_bolus_protection)) { val pass: Preference? = findPreference(resourceHelper.gs(R.string.key_bolus_password)) - if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.PASSWORD.ordinal.toString() + if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.CUSTOM_PASSWORD.ordinal.toString() } } if (pref is EditTextPreference) { @@ -281,17 +284,66 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang } } } + + val hmacPasswords = arrayOf( + resourceHelper.gs(R.string.key_bolus_password), + resourceHelper.gs(R.string.key_master_password), + resourceHelper.gs(R.string.key_application_password), + resourceHelper.gs(R.string.key_settings_password) + ) + + if (pref is Preference) { + if ((pref.key != null) && (hmacPasswords.contains(pref.key))) { + if (sp.getString(pref.key, "").startsWith("hmac:")) { + pref.summary = "******" + } else { + pref.summary = resourceHelper.gs(R.string.password_not_set) + } + } + } pref?.let { adjustUnitDependentPrefs(it) } } - private fun initSummary(p: Preference) { + private fun initSummary(p: Preference, isSinglePreference: Boolean) { p.isIconSpaceReserved = false // remove extra spacing on left after migration to androidx + // expand single plugin preference by default + if (p is PreferenceScreen && isSinglePreference) { + if (p.size > 0 && p.getPreference(0) is PreferenceCategory) + (p.getPreference(0) as PreferenceCategory).initialExpandedChildrenCount = Int.MAX_VALUE + } if (p is PreferenceGroup) { for (i in 0 until p.preferenceCount) { - initSummary(p.getPreference(i)) + initSummary(p.getPreference(i), isSinglePreference) } } else { updatePrefSummary(p) } } + + // We use Preference and custom editor instead of EditTextPreference + // to hash password while it is saved and never have to show it, even hashed + + override fun onPreferenceTreeClick(preference: Preference?): Boolean { + context?.let { context -> + if (preference != null) { + if (preference.key == resourceHelper.gs(R.string.key_master_password)) { + passwordCheck.setPassword(context, R.string.master_password, R.string.key_master_password) + return true + } + if (preference.key == resourceHelper.gs(R.string.key_settings_password)) { + passwordCheck.setPassword(context, R.string.settings_password, R.string.key_settings_password) + return true + } + if (preference.key == resourceHelper.gs(R.string.key_bolus_password)) { + passwordCheck.setPassword(context, R.string.bolus_password, R.string.key_bolus_password) + return true + } + if (preference.key == resourceHelper.gs(R.string.key_application_password)) { + passwordCheck.setPassword(context, R.string.application_password, R.string.key_application_password) + return true + } + } + } + return super.onPreferenceTreeClick(preference) + } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/activities/NoSplashAppCompatActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/NoSplashAppCompatActivity.kt index b4ac7eff51..aaf85c6adf 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/NoSplashAppCompatActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/NoSplashAppCompatActivity.kt @@ -10,8 +10,8 @@ import info.nightscout.androidaps.utils.LocaleHelper open class NoSplashAppCompatActivity : DaggerAppCompatActivity() { public override fun onCreate(savedInstanceState: Bundle?) { - setTheme(R.style.AppTheme_NoActionBar) super.onCreate(savedInstanceState) + setTheme(R.style.AppTheme_NoActionBar) } public override fun attachBaseContext(newBase: Context) { 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 bf570f08a7..b6f22aae07 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/Profile.java +++ b/app/src/main/java/info/nightscout/androidaps/data/Profile.java @@ -2,6 +2,7 @@ package info.nightscout.androidaps.data; import androidx.collection.LongSparseArray; +import org.joda.time.DateTime; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -607,13 +608,15 @@ public class Profile { } public static int secondsFromMidnight() { - long passed = DateUtil.now() - MidnightTime.calc(); + // long passed = DateUtil.now() - MidnightTime.calc(); + long passed = new DateTime().getMillisOfDay(); return (int) (passed / 1000); } public static int secondsFromMidnight(long date) { - long midnight = MidnightTime.calc(date); - long passed = date - midnight; + //long midnight = MidnightTime.calc(date); + //long passed = date - midnight; + long passed = new DateTime(date).getMillisOfDay(); return (int) (passed / 1000); } diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt index 421a983563..68c95efe66 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt @@ -95,6 +95,7 @@ interface AppComponent : AndroidInjector { fun injectTrigger(triggerAutosensValue: TriggerAutosensValue) fun injectTrigger(triggerBg: TriggerBg) fun injectTrigger(triggerBolusAgo: TriggerBolusAgo) + fun injectTrigger(triggerBTDevice: TriggerBTDevice) fun injectTrigger(triggerCOB: TriggerCOB) fun injectTrigger(triggerConnector: TriggerConnector) fun injectTrigger(triggerDelta: TriggerDelta) @@ -127,8 +128,10 @@ interface AppComponent : AndroidInjector { fun injectElement(inputButton: InputButton) fun injectElement(comparator: Comparator) fun injectElement(comparatorExists: ComparatorExists) + fun injectElement(comparatorConnect: ComparatorConnect) fun injectElement(inputDateTime: InputDateTime) fun injectElement(inputDelta: InputDelta) + fun injectElement(inputDropdownMenu: InputDropdownMenu) fun injectElement(inputDouble: InputDouble) fun injectElement(inputDuration: InputDuration) fun injectElement(inputInsulin: InputInsulin) diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt index 28429bf870..933873bc5e 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt @@ -3,10 +3,12 @@ package info.nightscout.androidaps.dependencyInjection import android.content.Context import androidx.preference.PreferenceManager import dagger.Binds +import dagger.Lazy import dagger.Module import dagger.Provides import dagger.android.ContributesAndroidInjector import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.Config import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.data.ProfileStore @@ -15,6 +17,7 @@ import info.nightscout.androidaps.db.BgReading import info.nightscout.androidaps.db.ProfileSwitch import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.CommandQueueProvider +import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLoggerProduction import info.nightscout.androidaps.plugins.aps.loop.APSResult @@ -23,7 +26,6 @@ import info.nightscout.androidaps.plugins.aps.openAPSMA.DetermineBasalResultMA import info.nightscout.androidaps.plugins.aps.openAPSMA.LoggerCallback import info.nightscout.androidaps.plugins.aps.openAPSSMB.DetermineBasalAdapterSMBJS import info.nightscout.androidaps.plugins.aps.openAPSSMB.DetermineBasalResultSMB -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin import info.nightscout.androidaps.plugins.configBuilder.PluginStore import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctionImplementation @@ -33,6 +35,9 @@ import info.nightscout.androidaps.plugins.general.automation.actions.* import info.nightscout.androidaps.plugins.general.automation.elements.* import info.nightscout.androidaps.plugins.general.automation.triggers.* import info.nightscout.androidaps.plugins.general.overview.graphData.GraphData +import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefs +import info.nightscout.androidaps.plugins.general.maintenance.formats.ClassicPrefsFormat +import info.nightscout.androidaps.plugins.general.maintenance.formats.EncryptedPrefsFormat import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationWithAction import info.nightscout.androidaps.plugins.general.smsCommunicator.AuthRequest import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensData @@ -42,7 +47,6 @@ import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobThread import info.nightscout.androidaps.plugins.treatments.Treatment import info.nightscout.androidaps.queue.CommandQueue import info.nightscout.androidaps.queue.commands.* -import info.nightscout.androidaps.setupwizard.SWDefinition import info.nightscout.androidaps.setupwizard.SWEventListener import info.nightscout.androidaps.setupwizard.SWScreen import info.nightscout.androidaps.setupwizard.elements.* @@ -51,11 +55,13 @@ import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelperImplementation import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SPImplementation +import info.nightscout.androidaps.utils.storage.FileStorage +import info.nightscout.androidaps.utils.storage.Storage import info.nightscout.androidaps.utils.wizard.BolusWizard import info.nightscout.androidaps.utils.wizard.QuickWizardEntry import javax.inject.Singleton -@Module(includes = [AppModule.AppBindings::class]) +@Module(includes = [AppModule.AppBindings::class, PluginsModule::class]) open class AppModule { @Provides @@ -88,6 +94,24 @@ open class AppModule { */ } + @Provides + fun providesPlugins(@PluginsModule.AllConfigs allConfigs: Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>, + @PluginsModule.PumpDriver pumpDrivers: Lazy>, + @PluginsModule.NotNSClient notNsClient: Lazy>, + @PluginsModule.APS aps: Lazy>): List<@JvmSuppressWildcards PluginBase> { + val plugins = allConfigs.toMutableMap() + if (Config.PUMPDRIVERS) plugins += pumpDrivers.get() + if (Config.APS) plugins += aps.get() + if (!Config.NSCLIENT) plugins += notNsClient.get() + return plugins.toList().sortedBy { it.first }.map { it.second } + } + + @Provides + @Singleton + fun provideStorage(): Storage { + return FileStorage() + } + @Module interface AppBindings { @@ -156,6 +180,7 @@ open class AppModule { @ContributesAndroidInjector fun triggerPumpLastConnectionInjector(): TriggerPumpLastConnection + @ContributesAndroidInjector fun triggerBTDeviceInjector(): TriggerBTDevice @ContributesAndroidInjector fun triggerRecurringTimeInjector(): TriggerRecurringTime @ContributesAndroidInjector fun triggerTempTargetInjector(): TriggerTempTarget @ContributesAndroidInjector fun triggerTime(): TriggerTime @@ -182,10 +207,12 @@ open class AppModule { @ContributesAndroidInjector fun inputBgInjector(): InputBg @ContributesAndroidInjector fun inputButtonInjector(): InputButton @ContributesAndroidInjector fun comparatorInjector(): Comparator + @ContributesAndroidInjector fun comparatorConnectInjector(): ComparatorConnect @ContributesAndroidInjector fun comparatorExistsInjector(): ComparatorExists @ContributesAndroidInjector fun inputDateTimeInjector(): InputDateTime @ContributesAndroidInjector fun inputDeltaInjector(): InputDelta @ContributesAndroidInjector fun inputDoubleInjector(): InputDouble + @ContributesAndroidInjector fun inputDropdownMenuInjector(): InputDropdownMenu @ContributesAndroidInjector fun inputDurationInjector(): InputDuration @ContributesAndroidInjector fun inputInsulinInjector(): InputInsulin @ContributesAndroidInjector fun inputLocationModeInjector(): InputLocationMode @@ -234,6 +261,10 @@ open class AppModule { @ContributesAndroidInjector fun graphDataInjector(): GraphData + @ContributesAndroidInjector fun importExportPrefsInjector(): ImportExportPrefs + @ContributesAndroidInjector fun encryptedPrefsFormatInjector(): EncryptedPrefsFormat + @ContributesAndroidInjector fun classicPrefsFormatInjector(): ClassicPrefsFormat + @Binds fun bindContext(mainApp: MainApp): Context @Binds fun bindInjector(mainApp: MainApp): HasAndroidInjector diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/FragmentsModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/FragmentsModule.kt index dbd9cdf424..d151912151 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/FragmentsModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/FragmentsModule.kt @@ -28,6 +28,7 @@ import info.nightscout.androidaps.plugins.general.overview.OverviewFragment import info.nightscout.androidaps.plugins.general.overview.dialogs.EditQuickWizardDialog import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorFragment import info.nightscout.androidaps.plugins.general.tidepool.TidepoolFragment +import info.nightscout.androidaps.plugins.general.wear.WearFragment import info.nightscout.androidaps.plugins.insulin.InsulinFragment import info.nightscout.androidaps.plugins.profile.local.LocalProfileFragment import info.nightscout.androidaps.plugins.profile.ns.NSProfileFragment @@ -39,6 +40,7 @@ import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpFragment import info.nightscout.androidaps.plugins.source.BGSourceFragment import info.nightscout.androidaps.plugins.treatments.TreatmentsFragment import info.nightscout.androidaps.plugins.treatments.fragments.* +import info.nightscout.androidaps.utils.protection.PasswordCheck @Module @Suppress("unused") @@ -71,6 +73,7 @@ abstract class FragmentsModule { @ContributesAndroidInjector abstract fun contributesNSProfileFragment(): NSProfileFragment @ContributesAndroidInjector abstract fun contributesNSClientFragment(): NSClientFragment @ContributesAndroidInjector abstract fun contributesSmsCommunicatorFragment(): SmsCommunicatorFragment + @ContributesAndroidInjector abstract fun contributesWearFragment(): WearFragment @ContributesAndroidInjector abstract fun contributesTidepoolFragment(): TidepoolFragment @ContributesAndroidInjector abstract fun contributesTreatmentsFragment(): TreatmentsFragment @@ -110,4 +113,6 @@ abstract class FragmentsModule { @ContributesAndroidInjector abstract fun contributesTreatmentDialog(): TreatmentDialog @ContributesAndroidInjector abstract fun contributesWizardDialog(): WizardDialog @ContributesAndroidInjector abstract fun contributesWizardInfoDialog(): WizardInfoDialog + + @ContributesAndroidInjector abstract fun contributesPasswordCheck(): PasswordCheck } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PluginsModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PluginsModule.kt new file mode 100644 index 0000000000..cc3694874f --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PluginsModule.kt @@ -0,0 +1,379 @@ +package info.nightscout.androidaps.dependencyInjection + +import dagger.Binds +import dagger.Lazy +import dagger.Module +import dagger.Provides +import dagger.multibindings.IntKey +import dagger.multibindings.IntoMap +import dagger.multibindings.IntoSet +import info.nightscout.androidaps.Config +import info.nightscout.androidaps.interfaces.PluginBase +import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin +import info.nightscout.androidaps.plugins.aps.openAPSAMA.OpenAPSAMAPlugin +import info.nightscout.androidaps.plugins.aps.openAPSMA.OpenAPSMAPlugin +import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin +import info.nightscout.androidaps.plugins.constraints.dstHelper.DstHelperPlugin +import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin +import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin +import info.nightscout.androidaps.plugins.constraints.signatureVerifier.SignatureVerifierPlugin +import info.nightscout.androidaps.plugins.constraints.storage.StorageConstraintPlugin +import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerPlugin +import info.nightscout.androidaps.plugins.general.actions.ActionsPlugin +import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin +import info.nightscout.androidaps.plugins.general.careportal.CareportalPlugin +import info.nightscout.androidaps.plugins.general.dataBroadcaster.DataBroadcastPlugin +import info.nightscout.androidaps.plugins.general.food.FoodPlugin +import info.nightscout.androidaps.plugins.general.maintenance.MaintenancePlugin +import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin +import info.nightscout.androidaps.plugins.general.overview.OverviewPlugin +import info.nightscout.androidaps.plugins.general.persistentNotification.PersistentNotificationPlugin +import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin +import info.nightscout.androidaps.plugins.general.wear.WearPlugin +import info.nightscout.androidaps.plugins.general.xdripStatusline.StatusLinePlugin +import info.nightscout.androidaps.plugins.insulin.InsulinOrefFreePeakPlugin +import info.nightscout.androidaps.plugins.insulin.InsulinOrefRapidActingPlugin +import info.nightscout.androidaps.plugins.insulin.InsulinOrefUltraRapidActingPlugin +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin +import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin +import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin +import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin +import info.nightscout.androidaps.plugins.pump.danaR.DanaRPlugin +import info.nightscout.androidaps.plugins.pump.danaRKorean.DanaRKoreanPlugin +import info.nightscout.androidaps.plugins.pump.danaRS.DanaRSPlugin +import info.nightscout.androidaps.plugins.pump.danaRv2.DanaRv2Plugin +import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin +import info.nightscout.androidaps.plugins.pump.mdi.MDIPlugin +import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin +import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin +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.source.* +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin +import javax.inject.Qualifier + +@Module +abstract class PluginsModule { + + @Binds + @AllConfigs + @IntoMap + @IntKey(0) + abstract fun bindOverviewPlugin(plugin: OverviewPlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(10) + abstract fun bindIobCobCalculatorPlugin(plugin: IobCobCalculatorPlugin): PluginBase + + @Binds + @NotNSClient + @IntoMap + @IntKey(20) + abstract fun bindActionsPlugin(plugin: ActionsPlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(30) + abstract fun bindInsulinOrefRapidActingPlugin(plugin: InsulinOrefRapidActingPlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(40) + abstract fun bindInsulinOrefUltraRapidActingPlugin(plugin: InsulinOrefUltraRapidActingPlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(50) + abstract fun bindInsulinOrefFreePeakPlugin(plugin: InsulinOrefFreePeakPlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(60) + abstract fun bindSensitivityOref0Plugin(plugin: SensitivityOref0Plugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(70) + abstract fun bindSensitivityAAPSPlugin(plugin: SensitivityAAPSPlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(80) + abstract fun bindSensitivityWeightedAveragePlugin(plugin: SensitivityWeightedAveragePlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(90) + abstract fun bindSensitivityOref1Plugin(plugin: SensitivityOref1Plugin): PluginBase + + @Binds + @PumpDriver + @IntoMap + @IntKey(100) + abstract fun bindDanaRPlugin(plugin: DanaRPlugin): PluginBase + + @Binds + @PumpDriver + @IntoMap + @IntKey(110) + abstract fun bindDanaRKoreanPlugin(plugin: DanaRKoreanPlugin): PluginBase + + @Binds + @PumpDriver + @IntoMap + @IntKey(120) + abstract fun bindDanaRv2Plugin(plugin: DanaRv2Plugin): PluginBase + + @Binds + @PumpDriver + @IntoMap + @IntKey(130) + abstract fun bindDanaRSPlugin(plugin: DanaRSPlugin): PluginBase + + @Binds + @PumpDriver + @IntoMap + @IntKey(140) + abstract fun bindLocalInsightPlugin(plugin: LocalInsightPlugin): PluginBase + + @Binds + @PumpDriver + @IntoMap + @IntKey(150) + abstract fun bindComboPlugin(plugin: ComboPlugin): PluginBase + + @Binds + @PumpDriver + @IntoMap + @IntKey(160) + abstract fun bindMedtronicPumpPlugin(plugin: MedtronicPumpPlugin): PluginBase + + @Binds + @NotNSClient + @IntoMap + @IntKey(170) + abstract fun bindMDIPlugin(plugin: MDIPlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(180) + abstract fun bindVirtualPumpPlugin(plugin: VirtualPumpPlugin): PluginBase + + @Binds + @NotNSClient + @IntoMap + @IntKey(190) + abstract fun bindCareportalPlugin(plugin: CareportalPlugin): PluginBase + + @Binds + @APS + @IntoMap + @IntKey(200) + abstract fun bindLoopPlugin(plugin: LoopPlugin): PluginBase + + @Binds + @APS + @IntoMap + @IntKey(210) + abstract fun bindOpenAPSMAPlugin(plugin: OpenAPSMAPlugin): PluginBase + + @Binds + @APS + @IntoMap + @IntKey(220) + abstract fun bindOpenAPSAMAPlugin(plugin: OpenAPSAMAPlugin): PluginBase + + @Binds + @APS + @IntoMap + @IntKey(230) + abstract fun bindOpenAPSSMBPlugin(plugin: OpenAPSSMBPlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(240) + abstract fun bindNSProfilePlugin(plugin: NSProfilePlugin): PluginBase + + @Binds + @NotNSClient + @IntoMap + @IntKey(250) + abstract fun bindLocalProfilePlugin(plugin: LocalProfilePlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(255) + abstract fun bindAutomationPlugin(plugin: AutomationPlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(260) + abstract fun bindTreatmentsPlugin(plugin: TreatmentsPlugin): PluginBase + + @Binds + @NotNSClient + @IntoSet + abstract fun bindSafetyPlugin(plugin: SafetyPlugin): PluginBase + + @Binds + @NotNSClient + @IntoMap + @IntKey(270) + abstract fun bindVersionCheckerPlugin(plugin: VersionCheckerPlugin): PluginBase + + @Binds + @NotNSClient + @IntoMap + @IntKey(280) + abstract fun bindSmsCommunicatorPlugin(plugin: SmsCommunicatorPlugin): PluginBase + + + @Binds + @APS + @IntoMap + @IntKey(290) + abstract fun bindStorageConstraintPlugin(plugin: StorageConstraintPlugin): PluginBase + + @Binds + @APS + @IntoMap + @IntKey(300) + abstract fun bindSignatureVerifierPlugin(plugin: SignatureVerifierPlugin): PluginBase + + @Binds + @APS + @IntoMap + @IntKey(310) + abstract fun bindObjectivesPlugin(plugin: ObjectivesPlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(320) + abstract fun bindFoodPlugin(plugin: FoodPlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(330) + abstract fun bindWearPlugin(plugin: WearPlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(340) + abstract fun bindStatusLinePlugin(plugin: StatusLinePlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(350) + abstract fun bindPersistentNotificationPlugin(plugin: PersistentNotificationPlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(360) + abstract fun bindNSClientPlugin(plugin: NSClientPlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(370) + abstract fun bindMaintenancePlugin(plugin: MaintenancePlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(380) + abstract fun bindDstHelperPlugin(plugin: DstHelperPlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(390) + abstract fun bindDataBroadcastPlugin(plugin: DataBroadcastPlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(400) + abstract fun bindXdripPlugin(plugin: XdripPlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(410) + abstract fun bindNSClientSourcePlugin(plugin: NSClientSourcePlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(420) + abstract fun bindMM640gPlugin(plugin: MM640gPlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(430) + abstract fun bindGlimpPlugin(plugin: GlimpPlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(440) + abstract fun bindDexcomPlugin(plugin: DexcomPlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(450) + abstract fun bindPoctechPlugin(plugin: PoctechPlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(460) + abstract fun bindTomatoPlugin(plugin: TomatoPlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(470) + abstract fun bindRandomBgPlugin(plugin: RandomBgPlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(480) + abstract fun bindConfigBuilderPlugin(plugin: ConfigBuilderPlugin): PluginBase + + @Qualifier + annotation class AllConfigs + + @Qualifier + annotation class PumpDriver + + @Qualifier + annotation class NotNSClient + + @Qualifier + annotation class APS + +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ReceiversModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ReceiversModule.kt index 20e320c428..85e4850391 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ReceiversModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ReceiversModule.kt @@ -3,14 +3,17 @@ package info.nightscout.androidaps.dependencyInjection import dagger.Module import dagger.android.ContributesAndroidInjector import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkBluetoothStateReceiver -import info.nightscout.androidaps.receivers.KeepAliveReceiver -import info.nightscout.androidaps.receivers.TimeDateOrTZChangeReceiver +import info.nightscout.androidaps.receivers.* @Module @Suppress("unused") abstract class ReceiversModule { + @ContributesAndroidInjector abstract fun contributesBTReceiver(): BTReceiver + @ContributesAndroidInjector abstract fun contributesChargingStateReceiver(): ChargingStateReceiver + @ContributesAndroidInjector abstract fun contributesDataReceiver(): DataReceiver @ContributesAndroidInjector abstract fun contributesKeepAliveReceiver(): KeepAliveReceiver - @ContributesAndroidInjector abstract fun contributesTimeDateOrTZChangeReceiver(): TimeDateOrTZChangeReceiver + @ContributesAndroidInjector abstract fun contributesNetworkChangeReceiver(): NetworkChangeReceiver @ContributesAndroidInjector abstract fun contributesRileyLinkBluetoothStateReceiver(): RileyLinkBluetoothStateReceiver + @ContributesAndroidInjector abstract fun contributesTimeDateOrTZChangeReceiver(): TimeDateOrTZChangeReceiver } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ServicesModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ServicesModule.kt index 5bf16440f9..c43a8dfb7d 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ServicesModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ServicesModule.kt @@ -3,6 +3,7 @@ package info.nightscout.androidaps.dependencyInjection import dagger.Module import dagger.android.ContributesAndroidInjector import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService +import info.nightscout.androidaps.plugins.general.overview.notifications.DismissNotificationService import info.nightscout.androidaps.plugins.general.persistentNotification.DummyService import info.nightscout.androidaps.plugins.general.wear.wearintegration.WatchUpdaterService import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkService @@ -25,6 +26,7 @@ abstract class ServicesModule { @ContributesAndroidInjector abstract fun contributesAbstractDanaRExecutionService(): AbstractDanaRExecutionService @ContributesAndroidInjector abstract fun contributesAlarmSoundService(): AlarmSoundService @ContributesAndroidInjector abstract fun contributesDataService(): DataService + @ContributesAndroidInjector abstract fun contributesDismissNotificationService(): DismissNotificationService @ContributesAndroidInjector abstract fun contributesDummyService(): DummyService @ContributesAndroidInjector abstract fun contributesLocationService(): LocationService @ContributesAndroidInjector abstract fun contributesNSClientService(): NSClientService diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventBTChange.kt b/app/src/main/java/info/nightscout/androidaps/events/EventBTChange.kt new file mode 100644 index 0000000000..0531a4d00d --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventBTChange.kt @@ -0,0 +1,8 @@ +package info.nightscout.androidaps.events + +class EventBTChange constructor(val state: Change, val deviceName: String) : Event() { + enum class Change { + CONNECT, + DISCONNECT + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventNetworkChange.kt b/app/src/main/java/info/nightscout/androidaps/events/EventNetworkChange.kt index 62c8bdd13e..87599a9373 100644 --- a/app/src/main/java/info/nightscout/androidaps/events/EventNetworkChange.kt +++ b/app/src/main/java/info/nightscout/androidaps/events/EventNetworkChange.kt @@ -6,9 +6,11 @@ class EventNetworkChange : Event() { var mobileConnected = false var wifiConnected = false + var vpnConnected = false var ssid = "" var roaming = false + var metered = false fun connectedSsid(): String { return StringUtils.removeSurroundingQuotes(ssid) diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventPreferenceChange.kt b/app/src/main/java/info/nightscout/androidaps/events/EventPreferenceChange.kt index 830667c6b6..1f7856b7cd 100644 --- a/app/src/main/java/info/nightscout/androidaps/events/EventPreferenceChange.kt +++ b/app/src/main/java/info/nightscout/androidaps/events/EventPreferenceChange.kt @@ -15,17 +15,7 @@ class EventPreferenceChange : Event { changedKey = resourceHelper.gs(resourceID) } - @Deprecated("use injected version") - constructor(resources: Resources, id: Int) { - changedKey == resources.getString(id) - } - fun isChanged(resourceHelper: ResourceHelper, id: Int): Boolean { return changedKey == resourceHelper.gs(id) } - - @Deprecated("use injected version") - fun isChanged(resources: Resources, id: Int): Boolean { - return changedKey == resources.getString(id) - } } diff --git a/app/src/main/java/info/nightscout/androidaps/historyBrowser/HistoryBrowseActivity.java b/app/src/main/java/info/nightscout/androidaps/historyBrowser/HistoryBrowseActivity.java index 3c081e3309..6a47e3749b 100644 --- a/app/src/main/java/info/nightscout/androidaps/historyBrowser/HistoryBrowseActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/historyBrowser/HistoryBrowseActivity.java @@ -303,7 +303,7 @@ public class HistoryBrowseActivity extends NoSplashAppCompatActivity { // add basal data if (pump.getPumpDescription().isTempBasalCapable && showBasal) { - graphData.addBasals(fromTime, toTime, lowLine / graphData.maxY / 1.2d); + graphData.addBasals(fromTime, toTime, lowLine / graphData.getMaxY() / 1.2d); } // **** NOW line **** diff --git a/app/src/main/java/info/nightscout/androidaps/logging/AAPSLoggerTest.kt b/app/src/main/java/info/nightscout/androidaps/logging/AAPSLoggerTest.kt new file mode 100644 index 0000000000..0503db143f --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/logging/AAPSLoggerTest.kt @@ -0,0 +1,60 @@ +package info.nightscout.androidaps.logging + +/** + * Created by adrian on 2019-12-27. + */ + +class AAPSLoggerTest : AAPSLogger { + + override fun debug(message: String) { + println("DEBUG: $message") + } + + override fun debug(enable: Boolean, tag: LTag, message: String) { + println("DEBUG: " + message) + } + + override fun debug(tag: LTag, message: String) { + println("DEBUG: : " + tag.tag + " " + message) + } + + override fun debug(tag: LTag, format: String, vararg arguments: Any?) { + println("DEBUG: : " + tag.tag + " " + String.format(format, arguments)) + } + + override fun warn(tag: LTag, message: String) { + println("WARN: " + tag.tag + " " + message) + } + + override fun info(tag: LTag, message: String) { + println("INFO: " + tag.tag + " " + message) + } + + override fun info(tag: LTag, format: String, vararg arguments: Any?) { + println("INFO: : " + tag.tag + " " + String.format(format, arguments)) + } + + override fun error(tag: LTag, message: String) { + println("ERROR: " + tag.tag + " " + message) + } + + override fun error(message: String) { + println("ERROR: " + message) + } + + override fun error(message: String, throwable: Throwable) { + println("ERROR: " + message + " " + throwable) + } + + override fun error(format: String, vararg arguments: Any?) { + println("ERROR: : " + String.format(format, arguments)) + } + + override fun error(tag: LTag, message: String, throwable: Throwable) { + println("ERROR: " + tag.tag + " " + message + " " + throwable) + } + + override fun error(tag: LTag, format: String, vararg arguments: Any?) { + println("ERROR: : " + tag.tag + " " + String.format(format, arguments)) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/logging/L.kt b/app/src/main/java/info/nightscout/androidaps/logging/L.kt index ad13015378..2411e76c5e 100644 --- a/app/src/main/java/info/nightscout/androidaps/logging/L.kt +++ b/app/src/main/java/info/nightscout/androidaps/logging/L.kt @@ -1,65 +1,25 @@ package info.nightscout.androidaps.logging -import info.nightscout.androidaps.utils.SP +import androidx.preference.PreferenceManager +import info.nightscout.androidaps.MainApp import java.util.* object L { private var logElements: MutableList = ArrayList() const val CORE = "CORE" - const val AUTOSENS = "AUTOSENS" - const val AUTOMATION = "AUTOMATION" - const val EVENTS = "EVENTS" - const val GLUCOSE = "GLUCOSE" const val BGSOURCE = "BGSOURCE" - const val OVERVIEW = "OVERVIEW" - const val NOTIFICATION = "NOTIFICATION" const val DATASERVICE = "DATASERVICE" const val DATABASE = "DATABASE" const val DATAFOOD = "DATAFOOD" const val DATATREATMENTS = "DATATREATMENTS" const val NSCLIENT = "NSCLIENT" - const val TIDEPOOL = "TIDEPOOL" - const val CONSTRAINTS = "CONSTRAINTS" const val PUMP = "PUMP" - const val PUMPQUEUE = "PUMPQUEUE" const val PUMPCOMM = "PUMPCOMM" const val PUMPBTCOMM = "PUMPBTCOMM" - const val APS = "APS" - const val PROFILE = "PROFILE" - const val CONFIGBUILDER = "CONFIGBUILDER" - const val UI = "UI" - const val LOCATION = "LOCATION" - const val SMS = "SMS" - const val WEAR = "WEAR" init { - logElements.add(LogElement(APS, defaultValue = true)) - logElements.add(LogElement(AUTOMATION, defaultValue = true)) - logElements.add(LogElement(AUTOSENS, defaultValue = false)) - logElements.add(LogElement(BGSOURCE, defaultValue = true)) - logElements.add(LogElement(GLUCOSE, defaultValue = false)) - logElements.add(LogElement(CONFIGBUILDER, defaultValue = false)) - logElements.add(LogElement(CONSTRAINTS, defaultValue = true)) - logElements.add(LogElement(CORE, defaultValue = true)) - logElements.add(LogElement(DATABASE, defaultValue = true)) - logElements.add(LogElement(DATAFOOD, false)) - logElements.add(LogElement(DATASERVICE, true)) - logElements.add(LogElement(DATATREATMENTS, true)) - logElements.add(LogElement(EVENTS, false, requiresRestart = true)) - logElements.add(LogElement(LOCATION, true)) - logElements.add(LogElement(NOTIFICATION, true)) - logElements.add(LogElement(NSCLIENT, true)) - logElements.add(LogElement(TIDEPOOL, true)) - logElements.add(LogElement(OVERVIEW, true)) - logElements.add(LogElement(PROFILE, true)) - logElements.add(LogElement(PUMP, true)) - logElements.add(LogElement(PUMPBTCOMM, false)) - logElements.add(LogElement(PUMPCOMM, true)) - logElements.add(LogElement(PUMPQUEUE, true)) - logElements.add(LogElement(SMS, true)) - logElements.add(LogElement(UI, true)) - logElements.add(LogElement(WEAR, true)) + LTag.values().forEach { logElements.add(LogElement(it)) } } private fun findByName(name: String): LogElement { @@ -90,17 +50,13 @@ object L { var enabled: Boolean private var requiresRestart = false - internal constructor(name: String, defaultValue: Boolean) { - this.name = name - this.defaultValue = defaultValue - enabled = SP.getBoolean(getSPName(), defaultValue) - } - - internal constructor(name: String, defaultValue: Boolean, requiresRestart: Boolean) { - this.name = name - this.defaultValue = defaultValue - this.requiresRestart = requiresRestart - enabled = SP.getBoolean(getSPName(), defaultValue) + internal constructor(tag: LTag) { + this.name = tag.tag + this.defaultValue = tag.defaultValue + this.requiresRestart = tag.requiresRestart + //TODO: remove after getting rid of old logging style "if (L.isEnabled(...))" + @Suppress("DEPRECATION") + enabled = PreferenceManager.getDefaultSharedPreferences(MainApp.instance()).getBoolean(getSPName(), defaultValue) } internal constructor(defaultValue: Boolean) { @@ -113,40 +69,12 @@ object L { fun enable(enabled: Boolean) { this.enabled = enabled - SP.putBoolean(getSPName(), enabled) + @Suppress("DEPRECATION") + PreferenceManager.getDefaultSharedPreferences(MainApp.instance()).edit().putBoolean(getSPName(), enabled).apply() } fun resetToDefault() { enable(defaultValue) } } -} - -enum class LTag(val tag: String) { - CORE("CORE"), - AUTOSENS("AUTOSENS"), - AUTOMATION("AUTOMATION"), - EVENTS("EVENTS"), - GLUCOSE("GLUCOSE"), - BGSOURCE("BGSOURCE"), - OVERVIEW("OVERVIEW"), - NOTIFICATION("NOTIFICATION"), - DATASERVICE("DATASERVICE"), - DATABASE("DATABASE"), - DATAFOOD("DATAFOOD"), - DATATREATMENTS("DATATREATMENTS"), - NSCLIENT("NSCLIENT"), - TIDEPOOL("TIDEPOOL"), - CONSTRAINTS("CONSTRAINTS"), - PUMP("PUMP"), - PUMPQUEUE("PUMPQUEUE"), - PUMPCOMM("PUMPCOMM"), - PUMPBTCOMM("PUMPBTCOMM"), - APS("APS"), - PROFILE("PROFILE"), - CONFIGBUILDER("CONFIGBUILDER"), - UI("UI"), - LOCATION("LOCATION"), - WEAR("WEAR"), - SMS("SMS"), } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/logging/LTag.kt b/app/src/main/java/info/nightscout/androidaps/logging/LTag.kt new file mode 100644 index 0000000000..fc88377d6b --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/logging/LTag.kt @@ -0,0 +1,30 @@ +package info.nightscout.androidaps.logging + +enum class LTag(val tag: String, val defaultValue : Boolean = true, val requiresRestart: Boolean = false) { + CORE("CORE"), + APS("APS"), + AUTOSENS("AUTOSENS", defaultValue = false), + AUTOMATION("AUTOMATION"), + BGSOURCE("BGSOURCE"), + CONFIGBUILDER("CONFIGBUILDER"), + CONSTRAINTS("CONSTRAINTS"), + DATABASE("DATABASE"), + DATAFOOD("DATAFOOD", defaultValue = false), + DATASERVICE("DATASERVICE"), + DATATREATMENTS("DATATREATMENTS"), + EVENTS("EVENTS", defaultValue = false, requiresRestart = true), + GLUCOSE("GLUCOSE"), + LOCATION("LOCATION"), + NOTIFICATION("NOTIFICATION"), + NSCLIENT("NSCLIENT"), + OVERVIEW("OVERVIEW", defaultValue = false), + PUMP("PUMP"), + PUMPBTCOMM("PUMPBTCOMM", defaultValue = false), + PUMPCOMM("PUMPCOMM"), + PUMPQUEUE("PUMPQUEUE"), + PROFILE("PROFILE"), + SMS("SMS"), + TIDEPOOL("TIDEPOOL"), + UI("UI", defaultValue = false), + WEAR("WEAR", defaultValue = false) +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/DeviceStatus.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/DeviceStatus.java index 5222926a9e..42f25cce8f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/DeviceStatus.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/DeviceStatus.java @@ -366,7 +366,7 @@ import info.nightscout.androidaps.logging.StacktraceLoggerWrapper; */ public class DeviceStatus { - private static Logger log = StacktraceLoggerWrapper.getLogger(L.APS); + private static Logger log = StacktraceLoggerWrapper.getLogger(L.NSCLIENT); public String device = null; public JSONObject pump = null; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java index 4493d51275..79c3f5cd33 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java @@ -334,8 +334,7 @@ public class LoopPlugin extends PluginBase { Profile profile = profileFunction.getProfile(); if (profile == null || !profileFunction.isProfileValid("Loop")) { - if (L.isEnabled(L.APS)) - getAapsLogger().debug(LTag.APS, resourceHelper.gs(R.string.noprofileselected)); + getAapsLogger().debug(LTag.APS, resourceHelper.gs(R.string.noprofileselected)); rxBus.send(new EventLoopSetLastRunGui(resourceHelper.gs(R.string.noprofileselected))); return; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/DetermineBasalAdapterMAJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/DetermineBasalAdapterMAJS.java index cc32ba807b..c7733d4eb7 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/DetermineBasalAdapterMAJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/DetermineBasalAdapterMAJS.java @@ -109,8 +109,7 @@ public class DetermineBasalAdapterMAJS { // Parse the jsResult object to a JSON-String String result = NativeJSON.stringify(rhino, scope, jsResult, null, null).toString(); - if (L.isEnabled(L.APS)) - aapsLogger.debug(LTag.APS, "Result: " + result); + aapsLogger.debug(LTag.APS, "Result: " + result); try { determineBasalResultMA = new DetermineBasalResultMA(injector, jsResult, new JSONObject(result)); } catch (JSONException e) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt index f9cba58951..c864313b27 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt @@ -17,6 +17,7 @@ import info.nightscout.androidaps.activities.PreferencesActivity import info.nightscout.androidaps.events.EventRebuildTabs import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.configBuilder.events.EventConfigBuilderUpdateGui import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.extensions.plusAssign import info.nightscout.androidaps.utils.protection.ProtectionCheck 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 b779943cdb..a6608b07c0 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 @@ -9,7 +9,6 @@ import java.util.ArrayList; import javax.inject.Inject; import javax.inject.Singleton; -import dagger.Lazy; import dagger.android.HasAndroidInjector; import info.nightscout.androidaps.R; import info.nightscout.androidaps.events.EventAppInitialized; @@ -18,7 +17,6 @@ import info.nightscout.androidaps.events.EventRebuildTabs; import info.nightscout.androidaps.interfaces.APSInterface; import info.nightscout.androidaps.interfaces.ActivePluginProvider; import info.nightscout.androidaps.interfaces.BgSourceInterface; -import info.nightscout.androidaps.interfaces.CommandQueueProvider; import info.nightscout.androidaps.interfaces.InsulinInterface; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginDescription; @@ -30,13 +28,7 @@ import info.nightscout.androidaps.interfaces.TreatmentsInterface; import info.nightscout.androidaps.logging.AAPSLogger; import info.nightscout.androidaps.logging.LTag; import info.nightscout.androidaps.plugins.bus.RxBusWrapper; -import info.nightscout.androidaps.plugins.insulin.InsulinOrefRapidActingPlugin; -import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin; -import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin; -import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin; -import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref0Plugin; -import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin; -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; +import info.nightscout.androidaps.plugins.configBuilder.events.EventConfigBuilderUpdateGui; import info.nightscout.androidaps.utils.OKDialog; import info.nightscout.androidaps.utils.resources.ResourceHelper; import info.nightscout.androidaps.utils.sharedPreferences.SP; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginStore.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginStore.kt index 30014d38e3..558e23df2b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginStore.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginStore.kt @@ -1,9 +1,9 @@ package info.nightscout.androidaps.plugins.configBuilder +import info.nightscout.androidaps.Config import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag -import java.util.* import javax.inject.Inject import javax.inject.Singleton @@ -27,7 +27,7 @@ class PluginStore @Inject constructor( } } - var plugins = ArrayList() + lateinit var plugins: List<@JvmSuppressWildcards PluginBase> private var activeBgSource: BgSourceInterface? = null private var activePump: PumpInterface? = null @@ -41,11 +41,6 @@ class PluginStore @Inject constructor( verifySelectionInCategories() } - fun add(pluginBase: PluginBase): ActivePluginProvider { - plugins.add(pluginBase) - return this - } - fun getDefaultPlugin(type: PluginType): PluginBase { for (p in plugins) if (p.getType() == type && p.isDefault()) return p @@ -88,14 +83,16 @@ class PluginStore @Inject constructor( var pluginsInCategory: ArrayList? // PluginType.APS - pluginsInCategory = getSpecificPluginsList(PluginType.APS) - activeAPS = getTheOneEnabledInArray(pluginsInCategory, PluginType.APS) as APSInterface? - if (activeAPS == null) { - activeAPS = getDefaultPlugin(PluginType.APS) as APSInterface - (activeAPS as PluginBase).setPluginEnabled(PluginType.APS, true) - aapsLogger.debug(LTag.CONFIGBUILDER, "Defaulting APSInterface") + if (!Config.NSCLIENT && !Config.PUMPCONTROL) { + pluginsInCategory = getSpecificPluginsList(PluginType.APS) + activeAPS = getTheOneEnabledInArray(pluginsInCategory, PluginType.APS) as APSInterface? + if (activeAPS == null) { + activeAPS = getDefaultPlugin(PluginType.APS) as APSInterface + (activeAPS as PluginBase).setPluginEnabled(PluginType.APS, true) + aapsLogger.debug(LTag.CONFIGBUILDER, "Defaulting APSInterface") + } + setFragmentVisiblities((activeAPS as PluginBase).name, pluginsInCategory, PluginType.APS) } - setFragmentVisiblities((activeAPS as PluginBase).name, pluginsInCategory, PluginType.APS) // PluginType.INSULIN pluginsInCategory = getSpecificPluginsList(PluginType.INSULIN) @@ -190,6 +187,7 @@ class PluginStore @Inject constructor( */ private fun determineActivePlugin(pluginsInCategory: ArrayList, pluginType: PluginType): T? { + @Suppress("UNCHECKED_CAST") val activePlugin = getTheOneEnabledInArray(pluginsInCategory, pluginType) as T? if (activePlugin != null) { setFragmentVisiblities((activePlugin as PluginBase).name, pluginsInCategory, pluginType) @@ -242,6 +240,6 @@ class PluginStore @Inject constructor( override fun getActiveTreatments(): TreatmentsInterface = activeTreatments ?: checkNotNull(activeTreatments) { "No treatments selected" } - override fun getPluginsList(): ArrayList = plugins + override fun getPluginsList(): ArrayList = ArrayList(plugins) } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/EventConfigBuilderUpdateGui.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/events/EventConfigBuilderUpdateGui.kt similarity index 64% rename from app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/EventConfigBuilderUpdateGui.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/events/EventConfigBuilderUpdateGui.kt index e57fd79983..26904127dd 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/EventConfigBuilderUpdateGui.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/events/EventConfigBuilderUpdateGui.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.configBuilder +package info.nightscout.androidaps.plugins.configBuilder.events import info.nightscout.androidaps.events.EventUpdateGui diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt index 909ceac2b4..44f82fb97e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt @@ -26,7 +26,7 @@ import info.nightscout.androidaps.plugins.constraints.objectives.dialogs.NtpProg import info.nightscout.androidaps.plugins.constraints.objectives.events.EventNtpStatus import info.nightscout.androidaps.plugins.constraints.objectives.events.EventObjectivesUpdateGui import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective.ExamTask -import info.nightscout.androidaps.receivers.NetworkChangeReceiver +import info.nightscout.androidaps.receivers.ReceiverStatusStore import info.nightscout.androidaps.setupwizard.events.EventSWUpdate import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy @@ -48,6 +48,7 @@ class ObjectivesFragment : DaggerFragment() { @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var objectivesPlugin: ObjectivesPlugin + @Inject lateinit var receiverStatusStore: ReceiverStatusStore private val objectivesAdapter = ObjectivesAdapter() private val handler = Handler(Looper.getMainLooper()) @@ -223,7 +224,7 @@ class ObjectivesFragment : DaggerFragment() { holder.accomplished.text = resourceHelper.gs(R.string.accomplished, DateUtil.dateAndTimeString(objective.accomplishedOn)) holder.accomplished.setTextColor(-0x3e3e3f) holder.verify.setOnClickListener { - NetworkChangeReceiver.grabNetworkStatus(context) + receiverStatusStore.updateNetworkStatus() if (objectives_fake.isChecked) { objective.accomplishedOn = DateUtil.now() scrollToCurrentObjective() @@ -257,12 +258,12 @@ class ObjectivesFragment : DaggerFragment() { rxBus.send(EventNtpStatus(resourceHelper.gs(R.string.failedretrievetime), 99)) } } - }, NetworkChangeReceiver.isConnected()) + }, receiverStatusStore.isConnected) }.start() } } holder.start.setOnClickListener { - NetworkChangeReceiver.grabNetworkStatus(context) + receiverStatusStore.updateNetworkStatus() if (objectives_fake.isChecked) { objective.startedOn = DateUtil.now() scrollToCurrentObjective() @@ -292,7 +293,7 @@ class ObjectivesFragment : DaggerFragment() { rxBus.send(EventNtpStatus(resourceHelper.gs(R.string.failedretrievetime), 99)) } } - }, NetworkChangeReceiver.isConnected()) + }, receiverStatusStore.isConnected) }.start() } holder.unStart.setOnClickListener { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt index 9052055573..6e1aad8871 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt @@ -6,6 +6,7 @@ import android.os.Build import android.os.Handler import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R +import info.nightscout.androidaps.events.EventBTChange import info.nightscout.androidaps.events.EventChargingState import info.nightscout.androidaps.events.EventLocationChange import info.nightscout.androidaps.events.EventNetworkChange @@ -38,6 +39,7 @@ import org.json.JSONObject import java.util.* import javax.inject.Inject import javax.inject.Singleton +import kotlin.collections.ArrayList @Singleton class AutomationPlugin @Inject constructor( @@ -65,6 +67,7 @@ class AutomationPlugin @Inject constructor( val automationEvents = ArrayList() var executionLog: MutableList = ArrayList() + var btConnects : MutableList = ArrayList() private val loopHandler = Handler() private lateinit var refreshLoop: Runnable @@ -123,6 +126,14 @@ class AutomationPlugin @Inject constructor( .toObservable(EventAutosensCalculationFinished::class.java) .observeOn(Schedulers.io()) .subscribe({ processActions() }, { fabricPrivacy.logException(it) }) + disposable += rxBus + .toObservable(EventBTChange::class.java) + .observeOn(Schedulers.io()) + .subscribe({ + aapsLogger.debug(LTag.AUTOMATION, "Grabbed new BT event: $it") + btConnects.add(it) + processActions() + }, { fabricPrivacy.logException(it) }) } override fun onStop() { @@ -197,6 +208,12 @@ class AutomationPlugin @Inject constructor( event.lastRun = DateUtil.now() } } + // we cannot detect connected BT devices + // so let's collect all connection/disconnections between 2 runs of processActions() + // TriggerBTDevice can pick up and process these events + // after processing clear events to prevent repeated actions + btConnects.clear() + storeToSP() // save last run time } @@ -231,8 +248,8 @@ class AutomationPlugin @Inject constructor( TriggerLocation(injector), TriggerAutosensValue(injector), TriggerBolusAgo(injector), - TriggerPumpLastConnection(injector) + TriggerPumpLastConnection(injector), + TriggerBTDevice(injector) ) } - } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionNotification.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionNotification.kt index 8260f341e2..76152b8861 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionNotification.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionNotification.kt @@ -13,6 +13,7 @@ import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuil import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification import info.nightscout.androidaps.plugins.general.overview.notifications.Notification +import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationUserMessage import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.utils.JsonHelper import info.nightscout.androidaps.utils.resources.ResourceHelper @@ -30,7 +31,7 @@ class ActionNotification(injector: HasAndroidInjector) : Action(injector) { @DrawableRes override fun icon(): Int = R.drawable.ic_notifications override fun doAction(callback: Callback) { - val notification = Notification(Notification.USERMESSAGE, text.value, Notification.URGENT) + val notification = NotificationUserMessage(text.value) rxBus.send(EventNewNotification(notification)) NSUpload.uploadError(text.value) rxBus.send(EventRefreshOverview("ActionNotification")) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/ComparatorConnect.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/ComparatorConnect.kt new file mode 100644 index 0000000000..3e293fef5a --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/ComparatorConnect.kt @@ -0,0 +1,60 @@ +package info.nightscout.androidaps.plugins.general.automation.elements + +import android.view.View +import android.widget.AdapterView +import android.widget.ArrayAdapter +import android.widget.LinearLayout +import android.widget.Spinner +import androidx.annotation.StringRes +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.R +import info.nightscout.androidaps.utils.resources.ResourceHelper +import java.util.* +import javax.inject.Inject + +class ComparatorConnect(injector: HasAndroidInjector) : Element(injector) { + @Inject lateinit var resourceHelper: ResourceHelper + + enum class Compare { + ON_CONNECT, ON_DISCONNECT; + + @get:StringRes val stringRes: Int + get() = when (this) { + ON_CONNECT -> R.string.onconnect + ON_DISCONNECT -> R.string.ondisconnect + } + + companion object { + fun labels(resourceHelper: ResourceHelper): List { + val list: MutableList = ArrayList() + for (c in values()) list.add(resourceHelper.gs(c.stringRes)) + return list + } + } + } + + constructor(injector: HasAndroidInjector, value: Compare) : this(injector) { + this.value = value + } + + var value = Compare.ON_CONNECT + + override fun addToLayout(root: LinearLayout) { + val spinner = Spinner(root.context) + val spinnerArrayAdapter = ArrayAdapter(root.context, R.layout.spinner_centered, Compare.labels(resourceHelper)) + spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + spinner.adapter = spinnerArrayAdapter + val spinnerParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT) + spinnerParams.setMargins(0, resourceHelper.dpToPx(4), 0, resourceHelper.dpToPx(4)) + spinner.layoutParams = spinnerParams + spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>?, view: View, position: Int, id: Long) { + value = Compare.values()[position] + } + + override fun onNothingSelected(parent: AdapterView<*>?) {} + } + spinner.setSelection(value.ordinal) + root.addView(spinner) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputDropdownMenu.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputDropdownMenu.kt new file mode 100644 index 0000000000..7f894293f9 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputDropdownMenu.kt @@ -0,0 +1,68 @@ +package info.nightscout.androidaps.plugins.general.automation.elements + +import android.view.View +import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.ArrayAdapter +import android.widget.LinearLayout +import android.widget.Spinner +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.R +import info.nightscout.androidaps.utils.resources.ResourceHelper +import java.util.* +import javax.inject.Inject + +class InputDropdownMenu(injector: HasAndroidInjector) : Element(injector) { + @Inject lateinit var resourceHelper: ResourceHelper + + private var itemList: ArrayList = ArrayList() + var value: String = "" + + constructor(injector: HasAndroidInjector, name: String) : this(injector) { + value = name + } + + constructor(injector: HasAndroidInjector, another: InputDropdownMenu) : this(injector) { + value = another.value + } + + override fun addToLayout(root: LinearLayout) { + val spinner = Spinner(root.context) + spinner.adapter = ArrayAdapter(root.context, + R.layout.spinner_centered, itemList).also { + it.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + } + spinner.layoutParams = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.WRAP_CONTENT + ).also { it.setMargins(0, resourceHelper.dpToPx(4), 0, resourceHelper.dpToPx(4)) } + + spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>?, view: View, position: Int, id: Long) { + setValue(itemList[position].toString()) + } + + override fun onNothingSelected(parent: AdapterView<*>?) {} + } + spinner.setSelection(0) + root.addView(LinearLayout(root.context).also { + it.orientation = LinearLayout.VERTICAL + it.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) + it.addView(spinner) + }) + } + + fun setValue(name: String): InputDropdownMenu { + value = name + return this + } + + fun setList(values: ArrayList) { + itemList = ArrayList(values) + } + + // For testing only + fun add(item: String) { + itemList.add(item) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputTime.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputTime.kt index 34bc74560c..01a7854e35 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputTime.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputTime.kt @@ -57,7 +57,7 @@ class InputTime(injector: HasAndroidInjector) : Element(injector) { root.addView(l) } - private fun toMills(minutesSinceMidnight: Int): Long = MidnightTime.calc() + T.mins(minutesSinceMidnight.toLong()).msecs() + private fun toMills(minutesSinceMidnight: Int): Long = MidnightTime.calcPlusMinutes(minutesSinceMidnight) private fun getMinSinceMidnight(time: Long): Int = Profile.secondsFromMidnight(time) / 60 } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputTimeRange.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputTimeRange.kt index 18cf98fc73..4775990d16 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputTimeRange.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputTimeRange.kt @@ -80,7 +80,7 @@ class InputTimeRange(injector: HasAndroidInjector) : Element(injector) { root.addView(l) } - private fun toMills(minutesSinceMidnight: Int): Long = MidnightTime.calc() + T.mins(minutesSinceMidnight.toLong()).msecs() + private fun toMills(minutesSinceMidnight: Int): Long = MidnightTime.calcPlusMinutes(minutesSinceMidnight) private fun getMinSinceMidnight(time: Long): Int = Profile.secondsFromMidnight(time) / 60 } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBTDevice.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBTDevice.kt new file mode 100644 index 0000000000..6dfb45be67 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBTDevice.kt @@ -0,0 +1,94 @@ +package info.nightscout.androidaps.plugins.general.automation.triggers + +import android.bluetooth.BluetoothAdapter +import android.content.Context +import android.widget.LinearLayout +import com.google.common.base.Optional +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.R +import info.nightscout.androidaps.events.EventBTChange +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin +import info.nightscout.androidaps.plugins.general.automation.elements.ComparatorConnect +import info.nightscout.androidaps.plugins.general.automation.elements.InputDropdownMenu +import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder +import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel +import info.nightscout.androidaps.utils.JsonHelper +import org.json.JSONObject +import java.util.* +import javax.inject.Inject + +class TriggerBTDevice(injector: HasAndroidInjector) : Trigger(injector) { + @Inject lateinit var context: Context + @Inject lateinit var automationPlugin: AutomationPlugin + + var btDevice = InputDropdownMenu(injector, "") + var comparator: ComparatorConnect = ComparatorConnect(injector) + + private constructor(injector: HasAndroidInjector, triggerBTDevice: TriggerBTDevice) : this(injector) { + comparator = ComparatorConnect(injector, triggerBTDevice.comparator.value) + btDevice.value = triggerBTDevice.btDevice.value + } + + @Synchronized + override fun shouldRun(): Boolean { + if (eventExists()) { + aapsLogger.debug(LTag.AUTOMATION, "Ready for execution: " + friendlyDescription()) + return true + } + return false + } + + @Synchronized override fun toJSON(): String { + val data = JSONObject() + .put("comparator", comparator.value.toString()) + .put("name", btDevice.value) + return JSONObject() + .put("type", this::class.java.name) + .put("data", data) + .toString() + } + + override fun fromJSON(data: String): Trigger { + val d = JSONObject(data) + btDevice.value = JsonHelper.safeGetString(d, "name")!! + comparator.value = ComparatorConnect.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")!!) + return this + } + + override fun friendlyName(): Int = R.string.btdevice + + override fun friendlyDescription(): String = + resourceHelper.gs(R.string.btdevicecompared, btDevice.value, resourceHelper.gs(comparator.value.stringRes)) + + override fun icon(): Optional = Optional.of(R.drawable.ic_bluetooth_white_48dp) + + override fun duplicate(): Trigger = TriggerBTDevice(injector, this) + + override fun generateDialog(root: LinearLayout) { + val pairedDevices = devicesPaired() + btDevice.setList(pairedDevices) + LayoutBuilder() + .add(StaticLabel(injector, R.string.btdevice, this)) + .add(btDevice) + .add(comparator) + .build(root) + } + + // Get the list of paired BT devices to use in dropdown menu + private fun devicesPaired(): ArrayList { + val s = ArrayList() + BluetoothAdapter.getDefaultAdapter()?.bondedDevices?.forEach { s.add(it.name) } + return s + } + + private fun eventExists(): Boolean { + automationPlugin.btConnects.forEach { + if (btDevice.value == it.deviceName) { + if (comparator.value == ComparatorConnect.Compare.ON_CONNECT && it.state == EventBTChange.Change.CONNECT) return true + if (comparator.value == ComparatorConnect.Compare.ON_DISCONNECT && it.state == EventBTChange.Change.DISCONNECT) return true + } + } + return false + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerRecurringTime.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerRecurringTime.kt index 11dd1e0475..8982a12669 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerRecurringTime.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerRecurringTime.kt @@ -93,7 +93,7 @@ class TriggerRecurringTime(injector: HasAndroidInjector) : Trigger(injector) { override fun duplicate(): Trigger = TriggerRecurringTime(injector, this) - private fun toMills(minutesSinceMidnight: Int): Long = MidnightTime.calc() + T.mins(minutesSinceMidnight.toLong()).msecs() + private fun toMills(minutesSinceMidnight: Int): Long = MidnightTime.calcPlusMinutes(minutesSinceMidnight) private fun getMinSinceMidnight(time: Long): Int = Profile.secondsFromMidnight(time) / 60 diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTimeRange.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTimeRange.kt index 46edd9a0b2..2c565fa733 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTimeRange.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTimeRange.kt @@ -76,7 +76,7 @@ class TriggerTimeRange(injector: HasAndroidInjector) : Trigger(injector) { override fun duplicate(): Trigger = TriggerTimeRange(injector, range.start, range.end) - private fun toMills(minutesSinceMidnight: Int): Long = MidnightTime.calc() + T.mins(minutesSinceMidnight.toLong()).msecs() + private fun toMills(minutesSinceMidnight: Int): Long = MidnightTime.calcPlusMinutes(minutesSinceMidnight) private fun getMinSinceMidnight(time: Long): Int = Profile.secondsFromMidnight(time) / 60 diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerWifiSsid.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerWifiSsid.kt index 341a8fdf68..cef73c1339 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerWifiSsid.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerWifiSsid.kt @@ -11,10 +11,14 @@ import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithE import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel import info.nightscout.androidaps.receivers.NetworkChangeReceiver +import info.nightscout.androidaps.receivers.ReceiverStatusStore import info.nightscout.androidaps.utils.JsonHelper import org.json.JSONObject +import javax.inject.Inject class TriggerWifiSsid(injector: HasAndroidInjector) : Trigger(injector) { + @Inject lateinit var receiverStatusStore: ReceiverStatusStore + var ssid = InputString(injector) var comparator = Comparator(injector) @@ -39,7 +43,7 @@ class TriggerWifiSsid(injector: HasAndroidInjector) : Trigger(injector) { } override fun shouldRun(): Boolean { - val eventNetworkChange = NetworkChangeReceiver.getLastEvent() ?: return false + val eventNetworkChange = receiverStatusStore.lastNetworkEvent ?: return false if (!eventNetworkChange.wifiConnected && comparator.value == Comparator.Compare.IS_NOT_AVAILABLE) { aapsLogger.debug(LTag.AUTOMATION, "Ready for execution: " + friendlyDescription()) return true diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.java deleted file mode 100644 index 59c8e87144..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.java +++ /dev/null @@ -1,129 +0,0 @@ -package info.nightscout.androidaps.plugins.general.maintenance; - -import android.Manifest; -import android.app.Activity; -import android.content.Context; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.os.Environment; -import androidx.preference.PreferenceManager; - -import androidx.core.content.ContextCompat; -import androidx.fragment.app.Fragment; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.Map; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.events.EventAppExit; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.logging.StacktraceLoggerWrapper; -import info.nightscout.androidaps.plugins.bus.RxBus; -import info.nightscout.androidaps.utils.OKDialog; -import info.nightscout.androidaps.utils.SP; -import info.nightscout.androidaps.utils.ToastUtils; - -/** - * Created by mike on 03.07.2016. - */ - -public class ImportExportPrefs { - private static Logger log = StacktraceLoggerWrapper.getLogger(L.CORE); - private static File path = new File(Environment.getExternalStorageDirectory().toString()); - 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 = { - Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.WRITE_EXTERNAL_STORAGE - }; - - static void verifyStoragePermissions(Fragment fragment) { - int permission = ContextCompat.checkSelfPermission(fragment.getContext(), - Manifest.permission.WRITE_EXTERNAL_STORAGE); - - if (permission != PackageManager.PERMISSION_GRANTED) { - // We don't have permission so prompt the user - fragment.requestPermissions(PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE); - } - - } - - static void exportSharedPreferences(final Fragment f) { - exportSharedPreferences(f.getContext()); - } - - private static void exportSharedPreferences(final Context context) { - OKDialog.showConfirmation(context, MainApp.gs(R.string.maintenance), MainApp.gs(R.string.export_to) + " " + file + " ?", () -> { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - try { - FileWriter fw = new FileWriter(file); - PrintWriter pw = new PrintWriter(fw); - Map prefsMap = prefs.getAll(); - for (Map.Entry entry : prefsMap.entrySet()) { - pw.println(entry.getKey() + "::" + entry.getValue().toString()); - } - pw.close(); - fw.close(); - ToastUtils.showToastInUiThread(context, MainApp.gs(R.string.exported)); - } catch (FileNotFoundException e) { - ToastUtils.showToastInUiThread(context, MainApp.gs(R.string.filenotfound) + " " + file); - log.error("Unhandled exception", e); - } catch (IOException e) { - log.error("Unhandled exception", e); - } - }); - } - - static void importSharedPreferences(final Fragment fragment) { - importSharedPreferences(fragment.getContext()); - } - - public static void importSharedPreferences(final Context context) { - OKDialog.showConfirmation(context, MainApp.gs(R.string.maintenance), MainApp.gs(R.string.import_from) + " " + file + " ?", () -> { - String line; - String[] lineParts; - try { - SP.clear(); - - BufferedReader reader = new BufferedReader(new FileReader(file)); - while ((line = reader.readLine()) != null) { - lineParts = line.split("::"); - if (lineParts.length == 2) { - if (lineParts[1].equals("true") || lineParts[1].equals("false")) { - SP.putBoolean(lineParts[0], Boolean.parseBoolean(lineParts[1])); - } else { - SP.putString(lineParts[0], lineParts[1]); - } - } - } - reader.close(); - SP.putBoolean(R.string.key_setupwizard_processed, true); - OKDialog.show(context, MainApp.gs(R.string.setting_imported), MainApp.gs(R.string.restartingapp), () -> { - log.debug("Exiting"); - RxBus.Companion.getINSTANCE().send(new EventAppExit()); - if (context instanceof Activity) { - ((Activity) context).finish(); - } - System.runFinalization(); - System.exit(0); - }); - } catch (FileNotFoundException e) { - ToastUtils.showToastInUiThread(context, MainApp.gs(R.string.filenotfound) + " " + file); - log.error("Unhandled exception", e); - } catch (IOException e) { - log.error("Unhandled exception", e); - } - }); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.kt new file mode 100644 index 0000000000..356fc918e1 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.kt @@ -0,0 +1,340 @@ +package info.nightscout.androidaps.plugins.general.maintenance + +import android.Manifest +import android.app.Activity +import android.bluetooth.BluetoothAdapter +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Build +import android.os.Environment +import android.provider.Settings +import androidx.annotation.StringRes +import androidx.core.content.ContextCompat +import androidx.fragment.app.Fragment +import info.nightscout.androidaps.BuildConfig +import info.nightscout.androidaps.R +import info.nightscout.androidaps.activities.PreferencesActivity +import info.nightscout.androidaps.events.EventAppExit +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.general.maintenance.formats.* +import info.nightscout.androidaps.plugins.general.smsCommunicator.otp.OneTimePassword +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.OKDialog +import info.nightscout.androidaps.utils.OKDialog.show +import info.nightscout.androidaps.utils.ToastUtils +import info.nightscout.androidaps.utils.alertDialogs.PrefImportSummaryDialog +import info.nightscout.androidaps.utils.alertDialogs.TwoMessagesAlertDialog +import info.nightscout.androidaps.utils.alertDialogs.WarningDialog +import info.nightscout.androidaps.utils.buildHelper.BuildHelper +import info.nightscout.androidaps.utils.protection.PasswordCheck +import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.androidaps.utils.sharedPreferences.SP +import org.joda.time.DateTime +import org.joda.time.Days +import java.io.File +import java.io.FileNotFoundException +import java.io.IOException +import java.util.* +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Created by mike on 03.07.2016. + */ + +private const val REQUEST_EXTERNAL_STORAGE = 1 +private val PERMISSIONS_STORAGE = arrayOf( + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE +) + +private const val IMPORT_AGE_NOT_YET_OLD_DAYS = 60 + +@Singleton +class ImportExportPrefs @Inject constructor( + private var log: AAPSLogger, + private val resourceHelper: ResourceHelper, + private val sp: SP, + private val buildHelper: BuildHelper, + private val otp: OneTimePassword, + private val rxBus: RxBusWrapper, + private val passwordCheck: PasswordCheck, + private val classicPrefsFormat: ClassicPrefsFormat, + private val encryptedPrefsFormat: EncryptedPrefsFormat +) { + + val TAG = LTag.CORE + + private val path = File(Environment.getExternalStorageDirectory().toString()) + + private val file = File(path, resourceHelper.gs(R.string.app_name) + "Preferences") + private val encFile = File(path, resourceHelper.gs(R.string.app_name) + "Preferences.json") + + fun prefsImportFile(): File { + return if (encFile.exists()) encFile else file + } + + fun prefsFileExists(): Boolean { + return encFile.exists() || file.exists() + } + + + fun exportSharedPreferences(f: Fragment) { + f.activity?.let { exportSharedPreferences(it) } + } + + fun verifyStoragePermissions(fragment: Fragment) { + fragment.context?.let { + val permission = ContextCompat.checkSelfPermission(it, + Manifest.permission.WRITE_EXTERNAL_STORAGE) + if (permission != PackageManager.PERMISSION_GRANTED) { + // We don't have permission so prompt the user + fragment.requestPermissions(PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE) + } + } + } + + private fun prepareMetadata(context: Context): Map { + + val metadata: MutableMap = mutableMapOf() + + metadata[PrefsMetadataKey.DEVICE_NAME] = PrefMetadata(detectUserName(context), PrefsStatus.OK) + metadata[PrefsMetadataKey.CREATED_AT] = PrefMetadata(DateUtil.toISOString(Date()), PrefsStatus.OK) + metadata[PrefsMetadataKey.AAPS_VERSION] = PrefMetadata(BuildConfig.VERSION_NAME, PrefsStatus.OK) + metadata[PrefsMetadataKey.AAPS_FLAVOUR] = PrefMetadata(BuildConfig.FLAVOR, PrefsStatus.OK) + metadata[PrefsMetadataKey.DEVICE_MODEL] = PrefMetadata(getCurrentDeviceModelString(), PrefsStatus.OK) + + if (prefsEncryptionIsDisabled()) { + metadata[PrefsMetadataKey.ENCRYPTION] = PrefMetadata("Disabled", PrefsStatus.DISABLED) + } else { + metadata[PrefsMetadataKey.ENCRYPTION] = PrefMetadata("Enabled", PrefsStatus.OK) + } + + return metadata + } + + private fun detectUserName(context: Context): String { + // based on https://medium.com/@pribble88/how-to-get-an-android-device-nickname-4b4700b3068c + val n1 = Settings.System.getString(context.contentResolver, "bluetooth_name") + val n2 = Settings.Secure.getString(context.contentResolver, "bluetooth_name") + val n3 = BluetoothAdapter.getDefaultAdapter()?.name + val n4 = Settings.System.getString(context.contentResolver, "device_name") + val n5 = Settings.Secure.getString(context.contentResolver, "lock_screen_owner_info") + val n6 = Settings.Global.getString(context.contentResolver, "device_name") + + // name we use for SMS OTP token in communicator + val otpName = otp.name().trim() + val defaultOtpName = resourceHelper.gs(R.string.smscommunicator_default_user_display_name) + + // name we detect from OS + val systemName = n1 ?: n2 ?: n3 ?: n4 ?: n5 ?: n6 ?: defaultOtpName + val name = if (otpName.length > 0 && otpName != defaultOtpName) otpName else systemName + return name + } + + private fun getCurrentDeviceModelString() = + Build.MANUFACTURER + " " + Build.MODEL + " (" + Build.DEVICE + ")" + + private fun prefsEncryptionIsDisabled() = + buildHelper.isEngineeringMode() && !sp.getBoolean(resourceHelper.gs(R.string.key_maintenance_encrypt_exported_prefs), true) + + private fun askForMasterPass(activity: Activity, @StringRes canceledMsg: Int, then: ((password: String) -> Unit)) { + passwordCheck.queryPassword(activity, R.string.master_password, R.string.key_master_password, { password -> + then(password) + }, { + ToastUtils.warnToast(activity, resourceHelper.gs(canceledMsg)) + }) + } + + private fun askForMasterPassIfNeeded(activity: Activity, @StringRes canceledMsg: Int, then: ((password: String) -> Unit)) { + if (prefsEncryptionIsDisabled()) { + then("") + } else { + askForMasterPass(activity, canceledMsg, then) + } + } + + private fun assureMasterPasswordSet(activity: Activity, @StringRes wrongPwdTitle: Int): Boolean { + if (!sp.contains(R.string.key_master_password) || (sp.getString(R.string.key_master_password, "") == "")) { + WarningDialog.showWarning(activity, + resourceHelper.gs(wrongPwdTitle), + resourceHelper.gs(R.string.master_password_missing, resourceHelper.gs(R.string.configbuilder_general), resourceHelper.gs(R.string.protection)), + R.string.nav_preferences, { + val intent = Intent(activity, PreferencesActivity::class.java).apply { + putExtra("id", R.xml.pref_general) + } + activity.startActivity(intent) + }) + return false + } + return true + } + + private fun askToConfirmExport(activity: Activity, then: ((password: String) -> Unit)) { + if (!prefsEncryptionIsDisabled() && !assureMasterPasswordSet(activity, R.string.nav_export)) return + + TwoMessagesAlertDialog.showAlert(activity, resourceHelper.gs(R.string.nav_export), + resourceHelper.gs(R.string.export_to) + " " + encFile + " ?", + resourceHelper.gs(R.string.password_preferences_encrypt_prompt), { + askForMasterPassIfNeeded(activity, R.string.preferences_export_canceled, then) + }, null, R.drawable.ic_header_export) + } + + private fun askToConfirmImport(activity: Activity, fileToImport: File, then: ((password: String) -> Unit)) { + + if (encFile.exists()) { + if (!assureMasterPasswordSet(activity, R.string.nav_import)) return + + TwoMessagesAlertDialog.showAlert(activity, resourceHelper.gs(R.string.nav_import), + resourceHelper.gs(R.string.import_from) + " " + fileToImport + " ?", + resourceHelper.gs(R.string.password_preferences_decrypt_prompt), { + askForMasterPass(activity, R.string.preferences_import_canceled, then) + }, null, R.drawable.ic_header_import) + + } else { + OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.nav_import), + resourceHelper.gs(R.string.import_from) + " " + fileToImport + " ?", + Runnable { then("") }) + } + } + + private fun exportSharedPreferences(activity: Activity) { + askToConfirmExport(activity) { password -> + try { + val entries: MutableMap = mutableMapOf() + for ((key, value) in sp.getAll()) { + entries[key] = value.toString() + } + + val prefs = Prefs(entries, prepareMetadata(activity)) + + classicPrefsFormat.savePreferences(file, prefs) + encryptedPrefsFormat.savePreferences(encFile, prefs, password) + + ToastUtils.okToast(activity, resourceHelper.gs(R.string.exported)) + } catch (e: FileNotFoundException) { + ToastUtils.errorToast(activity, resourceHelper.gs(R.string.filenotfound) + " " + encFile) + log.error(TAG, "Unhandled exception", e) + } catch (e: IOException) { + ToastUtils.errorToast(activity, e.message) + log.error(TAG, "Unhandled exception", e) + } + } + } + + fun importSharedPreferences(fragment: Fragment) { + fragment.activity?.let { importSharedPreferences(it) } + } + + fun importSharedPreferences(activity: Activity) { + + val importFile = prefsImportFile() + + askToConfirmImport(activity, importFile) { password -> + + val format: PrefsFormat = if (encFile.exists()) encryptedPrefsFormat else classicPrefsFormat + + try { + + val prefs = format.loadPreferences(importFile, password) + prefs.metadata = checkMetadata(prefs.metadata) + + // import is OK when we do not have errors (warnings are allowed) + val importOk = checkIfImportIsOk(prefs) + + // if at end we allow to import preferences + val importPossible = (importOk || buildHelper.isEngineeringMode()) && (prefs.values.size > 0) + + PrefImportSummaryDialog.showSummary(activity, importOk, importPossible, prefs, { + if (importPossible) { + sp.clear() + for ((key, value) in prefs.values) { + if (value == "true" || value == "false") { + sp.putBoolean(key, value.toBoolean()) + } else { + sp.putString(key, value) + } + } + + restartAppAfterImport(activity) + } else { + // for impossible imports it should not be called + ToastUtils.errorToast(activity, "Cannot import preferences!") + } + }) + + } catch (e: PrefFileNotFoundError) { + ToastUtils.errorToast(activity, resourceHelper.gs(R.string.filenotfound) + " " + importFile) + log.error(TAG, "Unhandled exception", e) + } catch (e: PrefIOError) { + log.error(TAG, "Unhandled exception", e) + ToastUtils.errorToast(activity, e.message) + } + } + } + + // check metadata for known issues, change their status and add info with explanations + private fun checkMetadata(metadata: Map): Map { + val meta = metadata.toMutableMap() + + meta[PrefsMetadataKey.AAPS_FLAVOUR]?.let { flavour -> + val flavourOfPrefs = flavour.value + if (flavour.value != BuildConfig.FLAVOR) { + flavour.status = PrefsStatus.WARN + flavour.info = resourceHelper.gs(R.string.metadata_warning_different_flavour, flavourOfPrefs, BuildConfig.FLAVOR) + } + } + + meta[PrefsMetadataKey.DEVICE_MODEL]?.let { model -> + if (model.value != getCurrentDeviceModelString()) { + model.status = PrefsStatus.WARN + model.info = resourceHelper.gs(R.string.metadata_warning_different_device) + } + } + + meta[PrefsMetadataKey.CREATED_AT]?.let { createdAt -> + try { + val date1 = DateTime.parse(createdAt.value); + val date2 = DateTime.now() + + val daysOld = Days.daysBetween(date1.toLocalDate(), date2.toLocalDate()).getDays() + + if (daysOld > IMPORT_AGE_NOT_YET_OLD_DAYS) { + createdAt.status = PrefsStatus.WARN + createdAt.info = resourceHelper.gs(R.string.metadata_warning_old_export, daysOld.toString()) + } + } catch (e: Exception) { + createdAt.status = PrefsStatus.WARN + createdAt.info = resourceHelper.gs(R.string.metadata_warning_date_format) + } + } + + return meta + } + + private fun checkIfImportIsOk(prefs: Prefs): Boolean { + var importOk = true + + for ((_, value) in prefs.metadata) { + if (value.status == PrefsStatus.ERROR) + importOk = false; + } + return importOk + } + + private fun restartAppAfterImport(context: Context) { + sp.putBoolean(R.string.key_setupwizard_processed, true) + show(context, resourceHelper.gs(R.string.setting_imported), resourceHelper.gs(R.string.restartingapp), Runnable { + log.debug(TAG, "Exiting") + rxBus.send(EventAppExit()) + if (context is Activity) { + context.finish() + } + System.runFinalization() + System.exit(0) + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenanceFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenanceFragment.kt index 7de5d303ed..2e42294fc1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenanceFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenanceFragment.kt @@ -23,6 +23,7 @@ class MaintenanceFragment : DaggerFragment() { @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var treatmentsPlugin: TreatmentsPlugin @Inject lateinit var foodPlugin: FoodPlugin + @Inject lateinit var importExportPrefs: ImportExportPrefs override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.maintenance_fragment, container, false) @@ -45,13 +46,13 @@ class MaintenanceFragment : DaggerFragment() { } nav_export.setOnClickListener { // start activity for checking permissions... - ImportExportPrefs.verifyStoragePermissions(this) - ImportExportPrefs.exportSharedPreferences(this) + importExportPrefs.verifyStoragePermissions(this) + importExportPrefs.exportSharedPreferences(this) } nav_import.setOnClickListener { // start activity for checking permissions... - ImportExportPrefs.verifyStoragePermissions(this) - ImportExportPrefs.importSharedPreferences(this) + importExportPrefs.verifyStoragePermissions(this) + importExportPrefs.importSharedPreferences(this) } nav_logsettings.setOnClickListener { startActivity(Intent(activity, LogSettingActivity::class.java)) } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenancePlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenancePlugin.kt index cd4d333995..4a3e5e83ab 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenancePlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenancePlugin.kt @@ -4,6 +4,8 @@ import android.content.Context import android.content.Intent import android.net.Uri import androidx.core.content.FileProvider +import androidx.preference.PreferenceFragmentCompat +import androidx.preference.SwitchPreference import dagger.android.HasAndroidInjector import info.nightscout.androidaps.BuildConfig import info.nightscout.androidaps.Config @@ -16,6 +18,7 @@ import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP +import info.nightscout.androidaps.utils.textValidator.ValidatingEditTextPreference import java.io.* import java.util.* import java.util.zip.ZipEntry @@ -203,4 +206,13 @@ class MaintenancePlugin @Inject constructor( emailIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) return emailIntent } + + override fun preprocessPreferences(preferenceFragment: PreferenceFragmentCompat) { + super.preprocessPreferences(preferenceFragment) + val encryptSwitch = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_maintenance_encrypt_exported_prefs)) as SwitchPreference? + ?: return + encryptSwitch.isVisible = buildHelper.isEngineeringMode() + encryptSwitch.isEnabled = buildHelper.isEngineeringMode() + } + } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/ClassicPrefsFormat.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/ClassicPrefsFormat.kt new file mode 100644 index 0000000000..4689f549a6 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/ClassicPrefsFormat.kt @@ -0,0 +1,60 @@ +package info.nightscout.androidaps.plugins.general.maintenance.formats + +import info.nightscout.androidaps.R +import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.androidaps.utils.storage.Storage +import java.io.File +import java.io.FileNotFoundException +import java.io.IOException +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ClassicPrefsFormat @Inject constructor( + private var resourceHelper: ResourceHelper, + private var storage: Storage +) : PrefsFormat { + + companion object { + val FORMAT_KEY = "aaps_old" + } + + override fun savePreferences(file: File, prefs: Prefs, masterPassword: String?) { + try { + val contents = prefs.values.entries.joinToString("\n") { entry -> + "${entry.key}::${entry.value}" + } + storage.putFileContents(file, contents) + } catch (e: FileNotFoundException) { + throw PrefFileNotFoundError(file.absolutePath) + } catch (e: IOException) { + throw PrefIOError(file.absolutePath) + } + } + + override fun loadPreferences(file: File, masterPassword: String?): Prefs { + var lineParts: Array + val entries: MutableMap = mutableMapOf() + val metadata: MutableMap = mutableMapOf() + try { + + val rawLines = storage.getFileContents(file).split("\n") + rawLines.forEach { line -> + lineParts = line.split("::").toTypedArray() + if (lineParts.size == 2) { + entries[lineParts[0]] = lineParts[1] + } + } + + metadata[PrefsMetadataKey.FILE_FORMAT] = PrefMetadata(FORMAT_KEY, PrefsStatus.WARN, resourceHelper.gs(R.string.metadata_warning_outdated_format)) + + return Prefs(entries, metadata) + + } catch (e: FileNotFoundException) { + throw PrefFileNotFoundError(file.absolutePath) + } catch (e: IOException) { + throw PrefIOError(file.absolutePath) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/EncryptedPrefsFormat.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/EncryptedPrefsFormat.kt new file mode 100644 index 0000000000..cec2ae9b5f --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/EncryptedPrefsFormat.kt @@ -0,0 +1,225 @@ +package info.nightscout.androidaps.plugins.general.maintenance.formats + +import info.nightscout.androidaps.R +import info.nightscout.androidaps.utils.CryptoUtil +import info.nightscout.androidaps.utils.hexStringToByteArray +import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.androidaps.utils.storage.Storage +import info.nightscout.androidaps.utils.toHex +import org.json.JSONException +import org.json.JSONObject +import java.io.File +import java.io.FileNotFoundException +import java.io.IOException +import java.util.* +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class EncryptedPrefsFormat @Inject constructor( + private var resourceHelper: ResourceHelper, + private var storage: Storage +) : PrefsFormat { + + companion object { + val FORMAT_KEY_ENC = "aaps_encrypted" + val FORMAT_KEY_NOENC = "aaps_structured" + + private val KEY_CONSCIENCE = "if you remove/change this, please make sure you know the consequences!" + } + + override fun savePreferences(file: File, prefs: Prefs, masterPassword: String?) { + + val container = JSONObject() + val content = JSONObject() + val meta = JSONObject() + + val encStatus = prefs.metadata[PrefsMetadataKey.ENCRYPTION]?.status ?: PrefsStatus.OK + var encrypted = encStatus == PrefsStatus.OK && masterPassword != null + + try { + for ((key, value) in prefs.values.toSortedMap()) { + content.put(key, value) + } + + for ((metaKey, metaEntry) in prefs.metadata) { + if (metaKey == PrefsMetadataKey.FILE_FORMAT) + continue; + if (metaKey == PrefsMetadataKey.ENCRYPTION) + continue; + meta.put(metaKey.key, metaEntry.value) + } + + container.put(PrefsMetadataKey.FILE_FORMAT.key, if (encrypted) FORMAT_KEY_ENC else FORMAT_KEY_NOENC); + container.put("metadata", meta) + + val security = JSONObject() + security.put("file_hash", "--to-be-calculated--") + var encodedContent = "" + + if (encrypted) { + val salt = CryptoUtil.mineSalt() + val rawContent = content.toString() + val contentAttempt = CryptoUtil.encrypt(masterPassword!!, salt, rawContent) + if (contentAttempt != null) { + encodedContent = contentAttempt + security.put("algorithm", "v1") + security.put("salt", salt.toHex()) + security.put("content_hash", CryptoUtil.sha256(rawContent)) + } else { + // fallback when encryption does not work + encrypted = false + } + } + + if (!encrypted) { + security.put("algorithm", "none") + } + + container.put("security", security) + container.put("content", if (encrypted) encodedContent else content) + + var fileContents = container.toString(2) + val fileHash = CryptoUtil.hmac256(fileContents, KEY_CONSCIENCE) + + fileContents = fileContents.replace(Regex("(\\\"file_hash\\\"\\s*\\:\\s*\\\")(--to-be-calculated--)(\\\")"), "$1" + fileHash + "$3") + + storage.putFileContents(file, fileContents) + + } catch (e: FileNotFoundException) { + throw PrefFileNotFoundError(file.absolutePath) + } catch (e: IOException) { + throw PrefIOError(file.absolutePath) + } + } + + override fun loadPreferences(file: File, masterPassword: String?): Prefs { + + val entries: MutableMap = mutableMapOf() + val metadata: MutableMap = mutableMapOf() + val issues = LinkedList() + try { + + val jsonBody = storage.getFileContents(file) + val fileContents = jsonBody.replace(Regex("(?is)(\\\"file_hash\\\"\\s*\\:\\s*\\\")([^\"]*)(\\\")"), "$1--to-be-calculated--$3") + val calculatedFileHash = CryptoUtil.hmac256(fileContents, KEY_CONSCIENCE) + val container = JSONObject(jsonBody) + + if (container.has(PrefsMetadataKey.FILE_FORMAT.key) && container.has("security") && container.has("content") && container.has("metadata")) { + val fileFormat = container.getString(PrefsMetadataKey.FILE_FORMAT.key) + + if ((fileFormat != FORMAT_KEY_ENC) && (fileFormat != FORMAT_KEY_NOENC)) { + throw PrefFormatError("Unsupported file format: " + fileFormat) + } + + val meta = container.getJSONObject("metadata") + val security = container.getJSONObject("security") + + metadata[PrefsMetadataKey.FILE_FORMAT] = PrefMetadata(fileFormat, PrefsStatus.OK) + for (key in meta.keys()) { + val metaKey = PrefsMetadataKey.fromKey(key) + if (metaKey != null) { + metadata[metaKey] = PrefMetadata(meta.getString(key), PrefsStatus.OK) + } + } + + val encrypted = fileFormat == FORMAT_KEY_ENC + var secure: PrefsStatus = PrefsStatus.OK + var decryptedOk = false + var contentJsonObj: JSONObject? = null + var insecurityReason = resourceHelper.gs(R.string.prefdecrypt_settings_tampered) + + if (security.has("file_hash")) { + if (calculatedFileHash != security.getString("file_hash")) { + secure = PrefsStatus.ERROR + issues.add(resourceHelper.gs(R.string.prefdecrypt_issue_modified)) + } + } else { + secure = PrefsStatus.ERROR + issues.add(resourceHelper.gs(R.string.prefdecrypt_issue_missing_file_hash)) + } + + if (encrypted) { + if (security.has("algorithm") && security.get("algorithm") == "v1") { + if (security.has("salt") && security.has("content_hash")) { + + val salt = security.getString("salt").hexStringToByteArray() + val decrypted = CryptoUtil.decrypt(masterPassword!!, salt, container.getString("content")) + + if (decrypted != null) { + try { + val contentHash = CryptoUtil.sha256(decrypted) + + if (contentHash == security.getString("content_hash")) { + contentJsonObj = JSONObject(decrypted) + decryptedOk = true + } else { + secure = PrefsStatus.ERROR + issues.add(resourceHelper.gs(R.string.prefdecrypt_issue_modified)) + } + + } catch (e: JSONException) { + secure = PrefsStatus.ERROR + issues.add(resourceHelper.gs(R.string.prefdecrypt_issue_parsing)) + } + + } else { + secure = PrefsStatus.ERROR + issues.add(resourceHelper.gs(R.string.prefdecrypt_issue_wrong_pass)) + insecurityReason = resourceHelper.gs(R.string.prefdecrypt_wrong_password) + } + + } else { + secure = PrefsStatus.ERROR + issues.add(resourceHelper.gs(R.string.prefdecrypt_issue_wrong_format)) + } + } else { + secure = PrefsStatus.ERROR + issues.add(resourceHelper.gs(R.string.prefdecrypt_issue_wrong_algorithm)) + } + + } else { + + if (secure == PrefsStatus.OK) { + secure = PrefsStatus.WARN + } + + if (!(security.has("algorithm") && security.get("algorithm") == "none")) { + secure = PrefsStatus.ERROR + issues.add(resourceHelper.gs(R.string.prefdecrypt_issue_wrong_algorithm)) + } + + contentJsonObj = container.getJSONObject("content") + decryptedOk = true + } + + if (decryptedOk && contentJsonObj != null) { + for (key in contentJsonObj.keys()) { + entries.put(key, contentJsonObj[key].toString()) + } + } + + val issuesStr: String? = if (issues.size > 0) issues.joinToString("\n") else null + val encryptionDescStr = if (encrypted) { + if (secure == PrefsStatus.OK) resourceHelper.gs(R.string.prefdecrypt_settings_secure) else insecurityReason + } else { + if (secure != PrefsStatus.ERROR) resourceHelper.gs(R.string.prefdecrypt_settings_unencrypted) else resourceHelper.gs(R.string.prefdecrypt_settings_tampered) + } + + metadata[PrefsMetadataKey.ENCRYPTION] = PrefMetadata(encryptionDescStr, secure, issuesStr) + } else { + metadata[PrefsMetadataKey.FILE_FORMAT] = PrefMetadata(resourceHelper.gs(R.string.prefdecrypt_wrong_json), PrefsStatus.ERROR) + } + + return Prefs(entries, metadata) + + } catch (e: FileNotFoundException) { + throw PrefFileNotFoundError(file.absolutePath) + } catch (e: IOException) { + throw PrefIOError(file.absolutePath) + } catch (e: JSONException) { + throw PrefFormatError("Mallformed preferences JSON file: " + e) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/PrefsFormat.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/PrefsFormat.kt new file mode 100644 index 0000000000..a47add0dd7 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/PrefsFormat.kt @@ -0,0 +1,73 @@ +package info.nightscout.androidaps.plugins.general.maintenance.formats + +import android.content.Context +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import info.nightscout.androidaps.R +import java.io.File + +enum class PrefsMetadataKey(val key: String, @DrawableRes val icon:Int, @StringRes val label:Int) { + + FILE_FORMAT("format", R.drawable.ic_meta_format, R.string.metadata_label_format), + CREATED_AT("created_at", R.drawable.ic_meta_date, R.string.metadata_label_created_at), + AAPS_VERSION("aaps_version", R.drawable.ic_meta_version, R.string.metadata_label_aaps_version), + AAPS_FLAVOUR("aaps_flavour", R.drawable.ic_meta_flavour, R.string.metadata_label_aaps_flavour), + DEVICE_NAME("device_name", R.drawable.ic_meta_name, R.string.metadata_label_device_name), + DEVICE_MODEL("device_model", R.drawable.ic_meta_model, R.string.metadata_label_device_model), + ENCRYPTION("encryption", R.drawable.ic_meta_encryption, R.string.metadata_label_encryption); + + companion object { + private val keyToEnumMap = HashMap() + + init { + for (value in values()) { + keyToEnumMap.put(value.key, value) + } + } + + fun fromKey(key: String): PrefsMetadataKey? { + if (keyToEnumMap.containsKey(key)) { + return keyToEnumMap.get(key) + } else { + return null + } + } + + + } + + fun formatForDisplay(context: Context, value:String): String { + return when (this) { + FILE_FORMAT -> when (value) { + ClassicPrefsFormat.FORMAT_KEY -> context.getString(R.string.metadata_format_old) + EncryptedPrefsFormat.FORMAT_KEY_ENC -> context.getString(R.string.metadata_format_new) + EncryptedPrefsFormat.FORMAT_KEY_NOENC -> context.getString(R.string.metadata_format_debug) + else -> context.getString(R.string.metadata_format_other) + } + CREATED_AT -> value.replace("T", " ").replace("Z", " (UTC)") + else -> value + } + } + +} + +data class PrefMetadata(var value : String, var status : PrefsStatus, var info : String? = null) + +data class Prefs(val values : Map, var metadata : Map) + +interface PrefsFormat { + fun savePreferences(file: File, prefs: Prefs, masterPassword: String? = null) + fun loadPreferences(file: File, masterPassword: String? = null) : Prefs +} + +enum class PrefsStatus(@DrawableRes val icon:Int) { + OK(R.drawable.ic_meta_ok), + WARN(R.drawable.ic_meta_warning), + ERROR(R.drawable.ic_meta_error), + UNKNOWN(R.drawable.ic_meta_error), + DISABLED(R.drawable.ic_meta_error) +} + +class PrefFileNotFoundError(message: String) : Exception(message) +class PrefIOError(message: String) : Exception(message) +class PrefFormatError(message: String) : Exception(message) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.java index 30a0291059..8c6480ea86 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.java @@ -71,7 +71,7 @@ public class NSClientPlugin extends PluginBase { public NSClientService nsClientService = null; - private NsClientReceiverDelegate nsClientReceiverDelegate = new NsClientReceiverDelegate(); + private NsClientReceiverDelegate nsClientReceiverDelegate; @Inject public NSClientPlugin( @@ -80,7 +80,8 @@ public class NSClientPlugin extends PluginBase { RxBusWrapper rxBus, ResourceHelper resourceHelper, Context context, - SP sp + SP sp, + NsClientReceiverDelegate nsClientReceiverDelegate ) { super(new PluginDescription() .mainType(PluginType.GENERAL) @@ -97,6 +98,7 @@ public class NSClientPlugin extends PluginBase { this.resourceHelper = resourceHelper; this.context = context; this.sp = sp; + this.nsClientReceiverDelegate = nsClientReceiverDelegate; if (Config.NSCLIENT) { getPluginDescription().alwaysEnabled(true).visibleByDefault(true); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegate.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegate.java index 2b104008c2..3bc2bde171 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegate.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegate.java @@ -1,50 +1,55 @@ package info.nightscout.androidaps.plugins.general.nsclient; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.net.ConnectivityManager; -import android.net.wifi.WifiManager; +import javax.inject.Inject; +import javax.inject.Singleton; -import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.events.EventChargingState; import info.nightscout.androidaps.events.EventNetworkChange; import info.nightscout.androidaps.events.EventPreferenceChange; -import info.nightscout.androidaps.plugins.bus.RxBus; -import info.nightscout.androidaps.receivers.ChargingStateReceiver; -import info.nightscout.androidaps.receivers.NetworkChangeReceiver; -import info.nightscout.androidaps.utils.SP; +import info.nightscout.androidaps.plugins.bus.RxBusWrapper; +import info.nightscout.androidaps.receivers.ReceiverStatusStore; +import info.nightscout.androidaps.utils.resources.ResourceHelper; +import info.nightscout.androidaps.utils.sharedPreferences.SP; +@Singleton class NsClientReceiverDelegate { private boolean allowedChargingState = true; private boolean allowedNetworkState = true; boolean allowed = true; + private RxBusWrapper rxBus; + private ResourceHelper resourceHelper; + private SP sp; + private ReceiverStatusStore receiverStatusStore; + + @Inject + public NsClientReceiverDelegate( + RxBusWrapper rxBus, + ResourceHelper resourceHelper, + SP sp, + ReceiverStatusStore receiverStatusStore + ) { + this.rxBus = rxBus; + this.resourceHelper = resourceHelper; + this.sp = sp; + this.receiverStatusStore = receiverStatusStore; + } + void grabReceiversState() { - Context context = MainApp.instance().getApplicationContext(); - - EventNetworkChange event = NetworkChangeReceiver.grabNetworkStatus(context); - if (event != null) RxBus.Companion.getINSTANCE().send(event); - - EventChargingState eventChargingState = ChargingStateReceiver.grabChargingState(context); - if (eventChargingState != null) RxBus.Companion.getINSTANCE().send(eventChargingState); + receiverStatusStore.updateNetworkStatus(); } void onStatusEvent(EventPreferenceChange ev) { - if (ev.isChanged(MainApp.resources(), R.string.key_ns_wifionly) || - ev.isChanged(MainApp.resources(), R.string.key_ns_wifi_ssids) || - ev.isChanged(MainApp.resources(), R.string.key_ns_allowroaming) + if (ev.isChanged(resourceHelper, R.string.key_ns_wifionly) || + ev.isChanged(resourceHelper, R.string.key_ns_wifi_ssids) || + ev.isChanged(resourceHelper, R.string.key_ns_allowroaming) ) { - EventNetworkChange event = NetworkChangeReceiver.grabNetworkStatus(MainApp.instance().getApplicationContext()); - if (event != null) - RxBus.Companion.getINSTANCE().send(event); - } else if (ev.isChanged(MainApp.resources(), R.string.key_ns_chargingonly)) { - EventChargingState event = ChargingStateReceiver.grabChargingState(MainApp.instance().getApplicationContext()); - if (event != null) - RxBus.Companion.getINSTANCE().send(event); + receiverStatusStore.updateNetworkStatus(); + } else if (ev.isChanged(resourceHelper, R.string.key_ns_chargingonly)) { + receiverStatusStore.broadcastChargingState(); } } @@ -70,12 +75,12 @@ class NsClientReceiverDelegate { boolean newAllowedState = allowedChargingState && allowedNetworkState; if (newAllowedState != allowed) { allowed = newAllowedState; - RxBus.Companion.getINSTANCE().send(new EventPreferenceChange(MainApp.gs(R.string.key_nsclientinternal_paused))); + rxBus.send(new EventPreferenceChange(resourceHelper.gs(R.string.key_nsclientinternal_paused))); } } boolean calculateStatus(final EventChargingState ev) { - boolean chargingOnly = SP.getBoolean(R.string.key_ns_chargingonly, false); + boolean chargingOnly = sp.getBoolean(R.string.key_ns_chargingonly, false); boolean newAllowedState = true; if (!ev.isCharging() && chargingOnly) { @@ -86,9 +91,9 @@ class NsClientReceiverDelegate { } boolean calculateStatus(final EventNetworkChange ev) { - boolean wifiOnly = SP.getBoolean(R.string.key_ns_wifionly, false); - String allowedSSIDs = SP.getString(R.string.key_ns_wifi_ssids, ""); - boolean allowRoaming = SP.getBoolean(R.string.key_ns_allowroaming, true); + boolean wifiOnly = sp.getBoolean(R.string.key_ns_wifionly, false); + String allowedSSIDs = sp.getString(R.string.key_ns_wifi_ssids, ""); + boolean allowRoaming = sp.getBoolean(R.string.key_ns_allowroaming, true); boolean newAllowedState = true; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java index a192aa8526..a3d86716bb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java @@ -1008,6 +1008,63 @@ public class OverviewFragment extends DaggerFragment implements View.OnClickList } } + private void processInsulinCarbsButtonsVisibility() { + BgReading lastBG = iobCobCalculatorPlugin.lastBg(); + + final PumpInterface pump = activePlugin.getActivePump(); + + final Profile profile = profileFunction.getProfile(); + final String profileName = profileFunction.getProfileName(); + + // QuickWizard button + QuickWizardEntry quickWizardEntry = quickWizard.getActive(); + if (quickWizardEntry != null && lastBG != null && profile != null && pump.isInitialized() && !pump.isSuspended()) { + quickWizardButton.setVisibility(View.VISIBLE); + String text = quickWizardEntry.buttonText() + "\n" + DecimalFormatter.to0Decimal(quickWizardEntry.carbs()) + "g"; + BolusWizard wizard = quickWizardEntry.doCalc(profile, profileName, lastBG, false); + text += " " + DecimalFormatter.toPumpSupportedBolus(wizard.getCalculatedTotalInsulin(), pump) + "U"; + quickWizardButton.setText(text); + if (wizard.getCalculatedTotalInsulin() <= 0) + quickWizardButton.setVisibility(View.GONE); + } else + quickWizardButton.setVisibility(View.GONE); + + // **** Various treatment buttons **** + if (carbsButton != null) { + if ((!activePlugin.getActivePump().getPumpDescription().storesCarbInfo || (pump.isInitialized() && !pump.isSuspended())) && + profile != null && + sp.getBoolean(R.string.key_show_carbs_button, true)) + carbsButton.setVisibility(View.VISIBLE); + else + carbsButton.setVisibility(View.GONE); + } + + if (treatmentButton != null) { + if (pump.isInitialized() && !pump.isSuspended() && + profile != null && + sp.getBoolean(R.string.key_show_treatment_button, false)) + treatmentButton.setVisibility(View.VISIBLE); + else + treatmentButton.setVisibility(View.GONE); + } + if (wizardButton != null) { + if (pump.isInitialized() && !pump.isSuspended() && + profile != null && + sp.getBoolean(R.string.key_show_wizard_button, true)) + wizardButton.setVisibility(View.VISIBLE); + else + wizardButton.setVisibility(View.GONE); + } + if (insulinButton != null) { + if (pump.isInitialized() && !pump.isSuspended() && + profile != null && + sp.getBoolean(R.string.key_show_insulin_button, true)) + insulinButton.setVisibility(View.VISIBLE); + else + insulinButton.setVisibility(View.GONE); + } + } + private void scheduleUpdateGUI(final String from) { class UpdateRunnable implements Runnable { public void run() { @@ -1060,7 +1117,6 @@ public class OverviewFragment extends DaggerFragment implements View.OnClickList final Profile profile = profileFunction.getProfile(); if (profile == null) return; - final String profileName = profileFunction.getProfileName(); final String units = profileFunction.getUnits(); final double lowLine = defaultValueHelper.determineLowLine(); @@ -1234,53 +1290,7 @@ public class OverviewFragment extends DaggerFragment implements View.OnClickList activeProfileView.setTextColor(resourceHelper.gc(R.color.ribbonTextDefault)); } - // QuickWizard button - QuickWizardEntry quickWizardEntry = quickWizard.getActive(); - if (quickWizardEntry != null && lastBG != null && pump.isInitialized() && !pump.isSuspended()) { - quickWizardButton.setVisibility(View.VISIBLE); - String text = quickWizardEntry.buttonText() + "\n" + DecimalFormatter.to0Decimal(quickWizardEntry.carbs()) + "g"; - BolusWizard wizard = quickWizardEntry.doCalc(profile, profileName, lastBG, false); - text += " " + DecimalFormatter.toPumpSupportedBolus(wizard.getCalculatedTotalInsulin(), pump) + "U"; - quickWizardButton.setText(text); - if (wizard.getCalculatedTotalInsulin() <= 0) - quickWizardButton.setVisibility(View.GONE); - } else - quickWizardButton.setVisibility(View.GONE); - - // **** Various treatment buttons **** - if (carbsButton != null) { - if (sp.getBoolean(R.string.key_show_carbs_button, true) - && (!activePlugin.getActivePump().getPumpDescription().storesCarbInfo || - (pump.isInitialized() && !pump.isSuspended()))) { - carbsButton.setVisibility(View.VISIBLE); - } else { - carbsButton.setVisibility(View.GONE); - } - } - - if (pump.isInitialized() && !pump.isSuspended()) { - if (treatmentButton != null) { - if (sp.getBoolean(R.string.key_show_treatment_button, false)) { - treatmentButton.setVisibility(View.VISIBLE); - } else { - treatmentButton.setVisibility(View.GONE); - } - } - if (pump.isInitialized() && !pump.isSuspended() && wizardButton != null) { - if (sp.getBoolean(R.string.key_show_wizard_button, true)) { - wizardButton.setVisibility(View.VISIBLE); - } else { - wizardButton.setVisibility(View.GONE); - } - } - if (pump.isInitialized() && !pump.isSuspended() && insulinButton != null) { - if (sp.getBoolean(R.string.key_show_insulin_button, true)) { - insulinButton.setVisibility(View.VISIBLE); - } else { - insulinButton.setVisibility(View.GONE); - } - } - } + processInsulinCarbsButtonsVisibility(); // **** BG value **** if (lastBG == null) { //left this here as it seems you want to exit at this point if it is null... @@ -1453,7 +1463,7 @@ public class OverviewFragment extends DaggerFragment implements View.OnClickList // add basal data if (pump.getPumpDescription().isTempBasalCapable && sp.getBoolean("showbasals", true)) { - graphData.addBasals(fromTime, now, lowLine / graphData.maxY / 1.2d); + graphData.addBasals(fromTime, now, lowLine / graphData.getMaxY() / 1.2d); } // add target line diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.java deleted file mode 100644 index bcb11363c8..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.java +++ /dev/null @@ -1,726 +0,0 @@ -package info.nightscout.androidaps.plugins.general.overview.graphData; - -import android.graphics.Color; -import android.graphics.DashPathEffect; -import android.graphics.Paint; - -import com.jjoe64.graphview.GraphView; -import com.jjoe64.graphview.series.BarGraphSeries; -import com.jjoe64.graphview.series.DataPoint; -import com.jjoe64.graphview.series.LineGraphSeries; -import com.jjoe64.graphview.series.Series; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import javax.inject.Inject; - -import dagger.android.HasAndroidInjector; -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.CareportalEvent; -import info.nightscout.androidaps.db.ExtendedBolus; -import info.nightscout.androidaps.db.ProfileSwitch; -import info.nightscout.androidaps.db.TempTarget; -import info.nightscout.androidaps.interfaces.ActivePluginProvider; -import info.nightscout.androidaps.interfaces.TreatmentsInterface; -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.aps.loop.APSResult; -import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; -import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults; -import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction; -import info.nightscout.androidaps.plugins.general.overview.graphExtensions.AreaGraphSeries; -import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface; -import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DoubleDataPoint; -import info.nightscout.androidaps.plugins.general.overview.graphExtensions.FixedLineGraphSeries; -import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries; -import info.nightscout.androidaps.plugins.general.overview.graphExtensions.Scale; -import info.nightscout.androidaps.plugins.general.overview.graphExtensions.ScaledDataPoint; -import info.nightscout.androidaps.plugins.general.overview.graphExtensions.TimeAsXAxisLabelFormatter; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensData; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.BasalData; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; -import info.nightscout.androidaps.plugins.treatments.Treatment; -import info.nightscout.androidaps.utils.DecimalFormatter; -import info.nightscout.androidaps.utils.Round; -import info.nightscout.androidaps.utils.resources.ResourceHelper; - -/** - * Created by mike on 18.10.2017. - */ - -public class GraphData { - - @Inject AAPSLogger aapsLogger; - @Inject ProfileFunction profileFunction; - @Inject ResourceHelper resourceHelper; - @Inject ActivePluginProvider activePlugin; - - private GraphView graph; - public double maxY = Double.MIN_VALUE; - private double minY = Double.MAX_VALUE; - private List bgReadingsArray; - private String units; - private List series = new ArrayList<>(); - private TreatmentsInterface treatmentsPlugin; - - - private IobCobCalculatorPlugin iobCobCalculatorPlugin; // Cannot be injected: HistoryBrowser - - public GraphData(HasAndroidInjector injector, GraphView graph, IobCobCalculatorPlugin iobCobCalculatorPlugin) { - injector.androidInjector().inject(this); - units = profileFunction.getUnits(); - this.graph = graph; - this.iobCobCalculatorPlugin = iobCobCalculatorPlugin; - treatmentsPlugin = activePlugin.getActiveTreatments(); - } - - public void addBgReadings(long fromTime, long toTime, double lowLine, double highLine, List predictions) { - double maxBgValue = Double.MIN_VALUE; - //bgReadingsArray = MainApp.getDbHelper().getBgreadingsDataFromTime(fromTime, true); - bgReadingsArray = iobCobCalculatorPlugin.getBgReadings(); - List bgListArray = new ArrayList<>(); - - if (bgReadingsArray == null || bgReadingsArray.size() == 0) { - aapsLogger.debug(LTag.OVERVIEW, "No BG data."); - maxY = 10; - minY = 0; - return; - } - - for (BgReading bg : bgReadingsArray) { - if (bg.date < fromTime || bg.date > toTime) continue; - if (bg.value > maxBgValue) maxBgValue = bg.value; - bgListArray.add(bg); - } - if (predictions != null) { - Collections.sort(predictions, (o1, o2) -> Double.compare(o1.getX(), o2.getX())); - for (BgReading prediction : predictions) { - if (prediction.value >= 40) - bgListArray.add(prediction); - } - } - - maxBgValue = Profile.fromMgdlToUnits(maxBgValue, units); - maxBgValue = units.equals(Constants.MGDL) ? Round.roundTo(maxBgValue, 40d) + 80 : Round.roundTo(maxBgValue, 2d) + 4; - if (highLine > maxBgValue) maxBgValue = highLine; - int numOfVertLines = units.equals(Constants.MGDL) ? (int) (maxBgValue / 40 + 1) : (int) (maxBgValue / 2 + 1); - - DataPointWithLabelInterface[] bg = new DataPointWithLabelInterface[bgListArray.size()]; - bg = bgListArray.toArray(bg); - - - maxY = maxBgValue; - minY = 0; - // set manual y bounds to have nice steps - graph.getGridLabelRenderer().setNumVerticalLabels(numOfVertLines); - - addSeries(new PointsWithLabelGraphSeries<>(bg)); - } - - public void addInRangeArea(long fromTime, long toTime, double lowLine, double highLine) { - AreaGraphSeries inRangeAreaSeries; - - DoubleDataPoint[] inRangeAreaDataPoints = new DoubleDataPoint[]{ - new DoubleDataPoint(fromTime, lowLine, highLine), - new DoubleDataPoint(toTime, lowLine, highLine) - }; - inRangeAreaSeries = new AreaGraphSeries<>(inRangeAreaDataPoints); - inRangeAreaSeries.setColor(0); - inRangeAreaSeries.setDrawBackground(true); - inRangeAreaSeries.setBackgroundColor(resourceHelper.gc(R.color.inrangebackground)); - - addSeries(inRangeAreaSeries); - } - - // scale in % of vertical size (like 0.3) - public void addBasals(long fromTime, long toTime, double scale) { - LineGraphSeries basalsLineSeries; - LineGraphSeries absoluteBasalsLineSeries; - LineGraphSeries baseBasalsSeries; - LineGraphSeries tempBasalsSeries; - - double maxBasalValueFound = 0d; - Scale basalScale = new Scale(); - - List baseBasalArray = new ArrayList<>(); - List tempBasalArray = new ArrayList<>(); - List basalLineArray = new ArrayList<>(); - List absoluteBasalLineArray = new ArrayList<>(); - double lastLineBasal = 0; - double lastAbsoluteLineBasal = -1; - double lastBaseBasal = 0; - double lastTempBasal = 0; - for (long time = fromTime; time < toTime; time += 60 * 1000L) { - Profile profile = profileFunction.getProfile(time); - if (profile == null) continue; - BasalData basalData = iobCobCalculatorPlugin.getBasalData(profile, time); - double baseBasalValue = basalData.basal; - double absoluteLineValue = baseBasalValue; - double tempBasalValue = 0; - double basal = 0d; - if (basalData.isTempBasalRunning) { - absoluteLineValue = tempBasalValue = basalData.tempBasalAbsolute; - if (tempBasalValue != lastTempBasal) { - tempBasalArray.add(new ScaledDataPoint(time, lastTempBasal, basalScale)); - tempBasalArray.add(new ScaledDataPoint(time, basal = tempBasalValue, basalScale)); - } - if (lastBaseBasal != 0d) { - baseBasalArray.add(new ScaledDataPoint(time, lastBaseBasal, basalScale)); - baseBasalArray.add(new ScaledDataPoint(time, 0d, basalScale)); - lastBaseBasal = 0d; - } - } else { - if (baseBasalValue != lastBaseBasal) { - baseBasalArray.add(new ScaledDataPoint(time, lastBaseBasal, basalScale)); - baseBasalArray.add(new ScaledDataPoint(time, basal = baseBasalValue, basalScale)); - lastBaseBasal = baseBasalValue; - } - if (lastTempBasal != 0) { - tempBasalArray.add(new ScaledDataPoint(time, lastTempBasal, basalScale)); - tempBasalArray.add(new ScaledDataPoint(time, 0d, basalScale)); - } - } - - if (baseBasalValue != lastLineBasal) { - basalLineArray.add(new ScaledDataPoint(time, lastLineBasal, basalScale)); - basalLineArray.add(new ScaledDataPoint(time, baseBasalValue, basalScale)); - } - if (absoluteLineValue != lastAbsoluteLineBasal) { - absoluteBasalLineArray.add(new ScaledDataPoint(time, lastAbsoluteLineBasal, basalScale)); - absoluteBasalLineArray.add(new ScaledDataPoint(time, basal, basalScale)); - } - - lastAbsoluteLineBasal = absoluteLineValue; - lastLineBasal = baseBasalValue; - lastTempBasal = tempBasalValue; - maxBasalValueFound = Math.max(maxBasalValueFound, Math.max(tempBasalValue, baseBasalValue)); - } - - basalLineArray.add(new ScaledDataPoint(toTime, lastLineBasal, basalScale)); - baseBasalArray.add(new ScaledDataPoint(toTime, lastBaseBasal, basalScale)); - tempBasalArray.add(new ScaledDataPoint(toTime, lastTempBasal, basalScale)); - absoluteBasalLineArray.add(new ScaledDataPoint(toTime, lastAbsoluteLineBasal, basalScale)); - - ScaledDataPoint[] baseBasal = new ScaledDataPoint[baseBasalArray.size()]; - baseBasal = baseBasalArray.toArray(baseBasal); - baseBasalsSeries = new LineGraphSeries<>(baseBasal); - baseBasalsSeries.setDrawBackground(true); - baseBasalsSeries.setBackgroundColor(resourceHelper.gc(R.color.basebasal)); - baseBasalsSeries.setThickness(0); - - ScaledDataPoint[] tempBasal = new ScaledDataPoint[tempBasalArray.size()]; - tempBasal = tempBasalArray.toArray(tempBasal); - tempBasalsSeries = new LineGraphSeries<>(tempBasal); - tempBasalsSeries.setDrawBackground(true); - tempBasalsSeries.setBackgroundColor(resourceHelper.gc(R.color.tempbasal)); - tempBasalsSeries.setThickness(0); - - ScaledDataPoint[] basalLine = new ScaledDataPoint[basalLineArray.size()]; - basalLine = basalLineArray.toArray(basalLine); - basalsLineSeries = new LineGraphSeries<>(basalLine); - Paint paint = new Paint(); - paint.setStyle(Paint.Style.STROKE); - paint.setStrokeWidth(resourceHelper.getDisplayMetrics().scaledDensity * 2); - paint.setPathEffect(new DashPathEffect(new float[]{2, 4}, 0)); - paint.setColor(resourceHelper.gc(R.color.basal)); - basalsLineSeries.setCustomPaint(paint); - - ScaledDataPoint[] absoluteBasalLine = new ScaledDataPoint[absoluteBasalLineArray.size()]; - absoluteBasalLine = absoluteBasalLineArray.toArray(absoluteBasalLine); - absoluteBasalsLineSeries = new LineGraphSeries<>(absoluteBasalLine); - Paint absolutePaint = new Paint(); - absolutePaint.setStyle(Paint.Style.STROKE); - absolutePaint.setStrokeWidth(resourceHelper.getDisplayMetrics().scaledDensity * 2); - absolutePaint.setColor(resourceHelper.gc(R.color.basal)); - absoluteBasalsLineSeries.setCustomPaint(absolutePaint); - - basalScale.setMultiplier(maxY * scale / maxBasalValueFound); - - addSeries(baseBasalsSeries); - addSeries(tempBasalsSeries); - addSeries(basalsLineSeries); - addSeries(absoluteBasalsLineSeries); - } - - public void addTargetLine(long fromTime, long toTime, Profile profile, LoopPlugin.LastRun lastRun) { - LineGraphSeries targetsSeries; - - Scale targetsScale = new Scale(); - targetsScale.setMultiplier(1); - - List targetsSeriesArray = new ArrayList<>(); - double lastTarget = -1; - - if (lastRun != null && lastRun.constraintsProcessed != null) { - APSResult apsResult = lastRun.constraintsProcessed; - long latestPredictionsTime = apsResult.getLatestPredictionsTime(); - if (latestPredictionsTime > toTime) { - toTime = latestPredictionsTime; - } - } - - for (long time = fromTime; time < toTime; time += 5 * 60 * 1000L) { - TempTarget tt = treatmentsPlugin.getTempTargetFromHistory(time); - double value; - if (tt == null) { - value = Profile.fromMgdlToUnits((profile.getTargetLowMgdl(time) + profile.getTargetHighMgdl(time)) / 2, profileFunction.getUnits()); - } else { - value = Profile.fromMgdlToUnits(tt.target(), profileFunction.getUnits()); - } - if (lastTarget != value) { - if (lastTarget != -1) - targetsSeriesArray.add(new DataPoint(time, lastTarget)); - targetsSeriesArray.add(new DataPoint(time, value)); - } - lastTarget = value; - } - targetsSeriesArray.add(new DataPoint(toTime, lastTarget)); - - DataPoint[] targets = new DataPoint[targetsSeriesArray.size()]; - targets = targetsSeriesArray.toArray(targets); - targetsSeries = new LineGraphSeries<>(targets); - targetsSeries.setDrawBackground(false); - targetsSeries.setColor(resourceHelper.gc(R.color.tempTargetBackground)); - targetsSeries.setThickness(2); - - addSeries(targetsSeries); - } - - public void addTreatments(long fromTime, long endTime) { - List filteredTreatments = new ArrayList<>(); - - List treatments = treatmentsPlugin.getTreatmentsFromHistory(); - - for (int tx = 0; tx < treatments.size(); tx++) { - Treatment t = treatments.get(tx); - if (t.getX() < fromTime || t.getX() > endTime) continue; - if (t.isSMB && !t.isValid) continue; - t.setY(getNearestBg((long) t.getX())); - filteredTreatments.add(t); - } - - // ProfileSwitch - List profileSwitches = treatmentsPlugin.getProfileSwitchesFromHistory().getList(); - - for (int tx = 0; tx < profileSwitches.size(); tx++) { - DataPointWithLabelInterface t = profileSwitches.get(tx); - if (t.getX() < fromTime || t.getX() > endTime) continue; - filteredTreatments.add(t); - } - - // Extended bolus - if (!activePlugin.getActivePump().isFakingTempsByExtendedBoluses()) { - List extendedBoluses = treatmentsPlugin.getExtendedBolusesFromHistory().getList(); - - for (int tx = 0; tx < extendedBoluses.size(); tx++) { - DataPointWithLabelInterface t = extendedBoluses.get(tx); - if (t.getX() + t.getDuration() < fromTime || t.getX() > endTime) continue; - if (t.getDuration() == 0) continue; - t.setY(getNearestBg((long) t.getX())); - filteredTreatments.add(t); - } - } - - // Careportal - List careportalEvents = MainApp.getDbHelper().getCareportalEventsFromTime(fromTime - 6 * 60 * 60 * 1000, true); - - for (int tx = 0; tx < careportalEvents.size(); tx++) { - DataPointWithLabelInterface t = careportalEvents.get(tx); - if (t.getX() + t.getDuration() < fromTime || t.getX() > endTime) continue; - t.setY(getNearestBg((long) t.getX())); - filteredTreatments.add(t); - } - - DataPointWithLabelInterface[] treatmentsArray = new DataPointWithLabelInterface[filteredTreatments.size()]; - treatmentsArray = filteredTreatments.toArray(treatmentsArray); - addSeries(new PointsWithLabelGraphSeries<>(treatmentsArray)); - } - - private double getNearestBg(long date) { - if (bgReadingsArray == null) - return Profile.fromMgdlToUnits(100, units); - for (int r = 0; r < bgReadingsArray.size(); r++) { - BgReading reading = bgReadingsArray.get(r); - if (reading.date > date) continue; - return Profile.fromMgdlToUnits(reading.value, units); - } - return bgReadingsArray.size() > 0 - ? Profile.fromMgdlToUnits(bgReadingsArray.get(0).value, units) : Profile.fromMgdlToUnits(100, units); - } - - public void addActivity(long fromTime, long toTime, boolean useForScale, double scale) { - FixedLineGraphSeries actSeriesHist; - List actArrayHist = new ArrayList<>(); - FixedLineGraphSeries actSeriesPred; - List actArrayPred = new ArrayList<>(); - - double now = System.currentTimeMillis(); - Scale actScale = new Scale(); - IobTotal total; - double maxIAValue = 0; - - for (long time = fromTime; time <= toTime; time += 5 * 60 * 1000L) { - Profile profile = profileFunction.getProfile(time); - double act; - if (profile == null) continue; - total = iobCobCalculatorPlugin.calculateFromTreatmentsAndTempsSynchronized(time, profile); - act = total.activity; - - if (time <= now) - actArrayHist.add(new ScaledDataPoint(time, act, actScale)); - else - actArrayPred.add(new ScaledDataPoint(time, act, actScale)); - - maxIAValue = Math.max(maxIAValue, Math.abs(act)); - } - - ScaledDataPoint[] actData = new ScaledDataPoint[actArrayHist.size()]; - actData = actArrayHist.toArray(actData); - actSeriesHist = new FixedLineGraphSeries<>(actData); - actSeriesHist.setDrawBackground(false); - actSeriesHist.setColor(resourceHelper.gc(R.color.activity)); - actSeriesHist.setThickness(3); - - addSeries(actSeriesHist); - - actData = new ScaledDataPoint[actArrayPred.size()]; - actData = actArrayPred.toArray(actData); - actSeriesPred = new FixedLineGraphSeries<>(actData); - - Paint paint = new Paint(); - paint.setStyle(Paint.Style.STROKE); - paint.setStrokeWidth(3); - paint.setPathEffect(new DashPathEffect(new float[]{4, 4}, 0)); - paint.setColor(resourceHelper.gc(R.color.activity)); - actSeriesPred.setCustomPaint(paint); - - if (useForScale) { - maxY = maxIAValue; - minY = -maxIAValue; - } - actScale.setMultiplier(maxY * scale / maxIAValue); - - addSeries(actSeriesPred); - } - - // scale in % of vertical size (like 0.3) - public void addIob(long fromTime, long toTime, boolean useForScale, double scale, boolean showPrediction) { - FixedLineGraphSeries iobSeries; - List iobArray = new ArrayList<>(); - Double maxIobValueFound = Double.MIN_VALUE; - double lastIob = 0; - Scale iobScale = new Scale(); - - for (long time = fromTime; time <= toTime; time += 5 * 60 * 1000L) { - Profile profile = profileFunction.getProfile(time); - double iob = 0d; - if (profile != null) - iob = iobCobCalculatorPlugin.calculateFromTreatmentsAndTempsSynchronized(time, profile).iob; - if (Math.abs(lastIob - iob) > 0.02) { - if (Math.abs(lastIob - iob) > 0.2) - iobArray.add(new ScaledDataPoint(time, lastIob, iobScale)); - iobArray.add(new ScaledDataPoint(time, iob, iobScale)); - maxIobValueFound = Math.max(maxIobValueFound, Math.abs(iob)); - lastIob = iob; - } - } - - ScaledDataPoint[] iobData = new ScaledDataPoint[iobArray.size()]; - iobData = iobArray.toArray(iobData); - iobSeries = new FixedLineGraphSeries<>(iobData); - iobSeries.setDrawBackground(true); - iobSeries.setBackgroundColor(0x80FFFFFF & resourceHelper.gc(R.color.iob)); //50% - iobSeries.setColor(resourceHelper.gc(R.color.iob)); - iobSeries.setThickness(3); - - if (showPrediction) { - AutosensResult lastAutosensResult; - AutosensData autosensData = iobCobCalculatorPlugin.getLastAutosensDataSynchronized("GraphData"); - if (autosensData == null) - lastAutosensResult = new AutosensResult(); - else - lastAutosensResult = autosensData.autosensResult; - boolean isTempTarget = treatmentsPlugin.getTempTargetFromHistory(System.currentTimeMillis()) != null; - - List iobPred = new ArrayList<>(); - IobTotal[] iobPredArray = iobCobCalculatorPlugin.calculateIobArrayForSMB(lastAutosensResult, SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget); - for (IobTotal i : iobPredArray) { - iobPred.add(i.setColor(resourceHelper.gc(R.color.iobPredAS))); - maxIobValueFound = Math.max(maxIobValueFound, Math.abs(i.iob)); - } - DataPointWithLabelInterface[] iobp = new DataPointWithLabelInterface[iobPred.size()]; - iobp = iobPred.toArray(iobp); - addSeries(new PointsWithLabelGraphSeries<>(iobp)); - - - List iobPred2 = new ArrayList<>(); - IobTotal[] iobPredArray2 = iobCobCalculatorPlugin.calculateIobArrayForSMB(new AutosensResult(), SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget); - for (IobTotal i : iobPredArray2) { - iobPred2.add(i.setColor(resourceHelper.gc(R.color.iobPred))); - maxIobValueFound = Math.max(maxIobValueFound, Math.abs(i.iob)); - } - DataPointWithLabelInterface[] iobp2 = new DataPointWithLabelInterface[iobPred2.size()]; - iobp2 = iobPred2.toArray(iobp2); - addSeries(new PointsWithLabelGraphSeries<>(iobp2)); - - aapsLogger.debug(LTag.AUTOSENS, "IOB pred for AS=" + DecimalFormatter.to2Decimal(lastAutosensResult.ratio) + ": " + iobCobCalculatorPlugin.iobArrayToString(iobPredArray)); - aapsLogger.debug(LTag.AUTOSENS, "IOB pred for AS=" + DecimalFormatter.to2Decimal(1) + ": " + iobCobCalculatorPlugin.iobArrayToString(iobPredArray2)); - } - - if (useForScale) { - maxY = maxIobValueFound; - minY = -maxIobValueFound; - } - - iobScale.setMultiplier(maxY * scale / maxIobValueFound); - - addSeries(iobSeries); - } - - // scale in % of vertical size (like 0.3) - public void addCob(long fromTime, long toTime, boolean useForScale, double scale) { - List minFailoverActiveList = new ArrayList<>(); - FixedLineGraphSeries cobSeries; - List cobArray = new ArrayList<>(); - Double maxCobValueFound = 0d; - int lastCob = 0; - Scale cobScale = new Scale(); - - for (long time = fromTime; time <= toTime; time += 5 * 60 * 1000L) { - AutosensData autosensData = iobCobCalculatorPlugin.getAutosensData(time); - if (autosensData != null) { - int cob = (int) autosensData.cob; - if (cob != lastCob) { - if (autosensData.carbsFromBolus > 0) - cobArray.add(new ScaledDataPoint(time, lastCob, cobScale)); - cobArray.add(new ScaledDataPoint(time, cob, cobScale)); - maxCobValueFound = Math.max(maxCobValueFound, cob); - lastCob = cob; - } - if (autosensData.failoverToMinAbsorbtionRate) { - autosensData.setScale(cobScale); - autosensData.setChartTime(time); - minFailoverActiveList.add(autosensData); - } - } - } - - // COB - ScaledDataPoint[] cobData = new ScaledDataPoint[cobArray.size()]; - cobData = cobArray.toArray(cobData); - cobSeries = new FixedLineGraphSeries<>(cobData); - cobSeries.setDrawBackground(true); - cobSeries.setBackgroundColor(0x80FFFFFF & resourceHelper.gc(R.color.cob)); //50% - cobSeries.setColor(resourceHelper.gc(R.color.cob)); - cobSeries.setThickness(3); - - if (useForScale) { - maxY = maxCobValueFound; - minY = 0; - } - - cobScale.setMultiplier(maxY * scale / maxCobValueFound); - - addSeries(cobSeries); - - DataPointWithLabelInterface[] minFailover = new DataPointWithLabelInterface[minFailoverActiveList.size()]; - minFailover = minFailoverActiveList.toArray(minFailover); - addSeries(new PointsWithLabelGraphSeries<>(minFailover)); - } - - // scale in % of vertical size (like 0.3) - public void addDeviations(long fromTime, long toTime, boolean useForScale, double scale) { - class DeviationDataPoint extends ScaledDataPoint { - public int color; - - private DeviationDataPoint(double x, double y, int color, Scale scale) { - super(x, y, scale); - this.color = color; - } - } - - BarGraphSeries devSeries; - List devArray = new ArrayList<>(); - Double maxDevValueFound = 0d; - Scale devScale = new Scale(); - - for (long time = fromTime; time <= toTime; time += 5 * 60 * 1000L) { - AutosensData autosensData = iobCobCalculatorPlugin.getAutosensData(time); - if (autosensData != null) { - int color = resourceHelper.gc(R.color.deviationblack); // "=" - if (autosensData.type.equals("") || autosensData.type.equals("non-meal")) { - if (autosensData.pastSensitivity.equals("C")) - color = resourceHelper.gc(R.color.deviationgrey); - if (autosensData.pastSensitivity.equals("+")) - color = resourceHelper.gc(R.color.deviationgreen); - if (autosensData.pastSensitivity.equals("-")) - color = resourceHelper.gc(R.color.deviationred); - } else if (autosensData.type.equals("uam")) { - color = resourceHelper.gc(R.color.uam); - } else if (autosensData.type.equals("csf")) { - color = resourceHelper.gc(R.color.deviationgrey); - } - devArray.add(new DeviationDataPoint(time, autosensData.deviation, color, devScale)); - maxDevValueFound = Math.max(maxDevValueFound, Math.abs(autosensData.deviation)); - } - } - - // DEVIATIONS - DeviationDataPoint[] devData = new DeviationDataPoint[devArray.size()]; - devData = devArray.toArray(devData); - devSeries = new BarGraphSeries<>(devData); - devSeries.setValueDependentColor(data -> data.color); - - if (useForScale) { - maxY = maxDevValueFound; - minY = -maxY; - } - - devScale.setMultiplier(maxY * scale / maxDevValueFound); - - addSeries(devSeries); - } - - // scale in % of vertical size (like 0.3) - public void addRatio(long fromTime, long toTime, boolean useForScale, double scale) { - LineGraphSeries ratioSeries; - List ratioArray = new ArrayList<>(); - 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.getAutosensData(time); - if (autosensData != null) { - 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); - } - } - - // RATIOS - ScaledDataPoint[] ratioData = new ScaledDataPoint[ratioArray.size()]; - ratioData = ratioArray.toArray(ratioData); - ratioSeries = new LineGraphSeries<>(ratioData); - ratioSeries.setColor(resourceHelper.gc(R.color.ratio)); - ratioSeries.setThickness(3); - - if (useForScale) { - maxY = Math.max(maxRatioValueFound, Math.abs(minRatioValueFound)); - minY = -maxY; - } - - ratioScale.setMultiplier(maxY * scale / Math.max(maxRatioValueFound, Math.abs(minRatioValueFound))); - - addSeries(ratioSeries); - } - - // scale in % of vertical size (like 0.3) - public void addDeviationSlope(long fromTime, long toTime, boolean useForScale, double scale) { - LineGraphSeries dsMaxSeries; - LineGraphSeries dsMinSeries; - List dsMaxArray = new ArrayList<>(); - List dsMinArray = new ArrayList<>(); - double maxFromMaxValueFound = 0d; - double maxFromMinValueFound = 0d; - Scale dsMaxScale = new Scale(); - Scale dsMinScale = new Scale(); - - for (long time = fromTime; time <= toTime; time += 5 * 60 * 1000L) { - AutosensData autosensData = iobCobCalculatorPlugin.getAutosensData(time); - if (autosensData != null) { - dsMaxArray.add(new ScaledDataPoint(time, autosensData.slopeFromMaxDeviation, dsMaxScale)); - dsMinArray.add(new ScaledDataPoint(time, autosensData.slopeFromMinDeviation, dsMinScale)); - maxFromMaxValueFound = Math.max(maxFromMaxValueFound, Math.abs(autosensData.slopeFromMaxDeviation)); - maxFromMinValueFound = Math.max(maxFromMinValueFound, Math.abs(autosensData.slopeFromMinDeviation)); - } - } - - // Slopes - ScaledDataPoint[] ratioMaxData = new ScaledDataPoint[dsMaxArray.size()]; - ratioMaxData = dsMaxArray.toArray(ratioMaxData); - dsMaxSeries = new LineGraphSeries<>(ratioMaxData); - dsMaxSeries.setColor(resourceHelper.gc(R.color.devslopepos)); - dsMaxSeries.setThickness(3); - - ScaledDataPoint[] ratioMinData = new ScaledDataPoint[dsMinArray.size()]; - ratioMinData = dsMinArray.toArray(ratioMinData); - dsMinSeries = new LineGraphSeries<>(ratioMinData); - dsMinSeries.setColor(resourceHelper.gc(R.color.devslopeneg)); - dsMinSeries.setThickness(3); - - if (useForScale) { - maxY = Math.max(maxFromMaxValueFound, maxFromMinValueFound); - minY = -maxY; - } - - dsMaxScale.setMultiplier(maxY * scale / maxFromMaxValueFound); - dsMinScale.setMultiplier(maxY * scale / maxFromMinValueFound); - - addSeries(dsMaxSeries); - addSeries(dsMinSeries); - } - - // scale in % of vertical size (like 0.3) - public void addNowLine(long now) { - LineGraphSeries seriesNow; - DataPoint[] nowPoints = new DataPoint[]{ - new DataPoint(now, 0), - new DataPoint(now, maxY) - }; - - seriesNow = new LineGraphSeries<>(nowPoints); - seriesNow.setDrawDataPoints(false); - // custom paint to make a dotted line - Paint paint = new Paint(); - paint.setStyle(Paint.Style.STROKE); - paint.setStrokeWidth(2); - paint.setPathEffect(new DashPathEffect(new float[]{10, 20}, 0)); - paint.setColor(Color.WHITE); - seriesNow.setCustomPaint(paint); - - addSeries(seriesNow); - } - - public void formatAxis(long fromTime, long endTime) { - graph.getViewport().setMaxX(endTime); - graph.getViewport().setMinX(fromTime); - graph.getViewport().setXAxisBoundsManual(true); - graph.getGridLabelRenderer().setLabelFormatter(new TimeAsXAxisLabelFormatter("HH")); - graph.getGridLabelRenderer().setNumHorizontalLabels(7); // only 7 because of the space - } - - private void addSeries(Series s) { - series.add(s); - } - - public void performUpdate() { - // clear old data - graph.getSeries().clear(); - - // add precalculated series - for (Series s : series) { - if (!s.isEmpty()) { - s.onGraphViewAttached(graph); - graph.getSeries().add(s); - } - } - - double step = 1d; - if (maxY < 1) step = 0.1d; - graph.getViewport().setMaxY(Round.ceilTo(maxY, step)); - graph.getViewport().setMinY(Round.floorTo(minY, step)); - graph.getViewport().setYAxisBoundsManual(true); - - // draw it - graph.onDataChanged(false, false); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.kt new file mode 100644 index 0000000000..fc7d1eb2a1 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.kt @@ -0,0 +1,569 @@ +package info.nightscout.androidaps.plugins.general.overview.graphData + +import android.graphics.Color +import android.graphics.DashPathEffect +import android.graphics.Paint +import com.jjoe64.graphview.GraphView +import com.jjoe64.graphview.series.BarGraphSeries +import com.jjoe64.graphview.series.DataPoint +import com.jjoe64.graphview.series.LineGraphSeries +import com.jjoe64.graphview.series.Series +import dagger.android.HasAndroidInjector +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.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.TreatmentsInterface +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin.LastRun +import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction +import info.nightscout.androidaps.plugins.general.overview.graphExtensions.* +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin +import info.nightscout.androidaps.utils.DecimalFormatter +import info.nightscout.androidaps.utils.Round +import info.nightscout.androidaps.utils.resources.ResourceHelper +import java.util.* +import javax.inject.Inject +import kotlin.math.abs +import kotlin.math.max +import kotlin.math.min + +class GraphData(injector: HasAndroidInjector, private val graph: GraphView, private val iobCobCalculatorPlugin: IobCobCalculatorPlugin) { + + // IobCobCalculatorPlugin Cannot be injected: HistoryBrowser + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var profileFunction: ProfileFunction + @Inject lateinit var resourceHelper: ResourceHelper + @Inject lateinit var activePlugin: ActivePluginProvider + + private val treatmentsPlugin: TreatmentsInterface + + var maxY = Double.MIN_VALUE + private var minY = Double.MAX_VALUE + private var bgReadingsArray: List? = null + private val units: String + private val series: MutableList> = ArrayList() + + init { + injector.androidInjector().inject(this) + units = profileFunction.getUnits() + treatmentsPlugin = activePlugin.activeTreatments + } + + @Suppress("UNUSED_PARAMETER") + fun addBgReadings(fromTime: Long, toTime: Long, lowLine: Double, highLine: Double, predictions: MutableList?) { + var maxBgValue = Double.MIN_VALUE + bgReadingsArray = iobCobCalculatorPlugin.bgReadings + if (bgReadingsArray?.isEmpty() != false) { + aapsLogger.debug(LTag.OVERVIEW, "No BG data.") + maxY = 10.0 + minY = 0.0 + return + } + val bgListArray: MutableList = ArrayList() + for (bg in bgReadingsArray!!) { + if (bg.date < fromTime || bg.date > toTime) continue + if (bg.value > maxBgValue) maxBgValue = bg.value + bgListArray.add(bg) + } + if (predictions != null) { + predictions.sortWith(Comparator { o1: BgReading, o2: BgReading -> o1.x.compareTo(o2.x) }) + for (prediction in predictions) if (prediction.value >= 40) bgListArray.add(prediction) + } + maxBgValue = Profile.fromMgdlToUnits(maxBgValue, units) + maxBgValue = if (units == Constants.MGDL) Round.roundTo(maxBgValue, 40.0) + 80 else Round.roundTo(maxBgValue, 2.0) + 4 + if (highLine > maxBgValue) maxBgValue = highLine + val numOfVerticalLines = if (units == Constants.MGDL) (maxBgValue / 40 + 1).toInt() else (maxBgValue / 2 + 1).toInt() + maxY = maxBgValue + minY = 0.0 + // set manual y bounds to have nice steps + graph.gridLabelRenderer.numVerticalLabels = numOfVerticalLines + addSeries(PointsWithLabelGraphSeries(Array(bgListArray.size) { i -> bgListArray[i] })) + } + + fun addInRangeArea(fromTime: Long, toTime: Long, lowLine: Double, highLine: Double) { + val inRangeAreaSeries: AreaGraphSeries + val inRangeAreaDataPoints = arrayOf( + DoubleDataPoint(fromTime.toDouble(), lowLine, highLine), + DoubleDataPoint(toTime.toDouble(), lowLine, highLine) + ) + inRangeAreaSeries = AreaGraphSeries(inRangeAreaDataPoints) + inRangeAreaSeries.color = 0 + inRangeAreaSeries.isDrawBackground = true + inRangeAreaSeries.backgroundColor = resourceHelper.gc(R.color.inrangebackground) + addSeries(inRangeAreaSeries) + } + + // scale in % of vertical size (like 0.3) + fun addBasals(fromTime: Long, toTime: Long, scale: Double) { + var maxBasalValueFound = 0.0 + val basalScale = Scale() + val baseBasalArray: MutableList = ArrayList() + val tempBasalArray: MutableList = ArrayList() + val basalLineArray: MutableList = ArrayList() + val absoluteBasalLineArray: MutableList = ArrayList() + var lastLineBasal = 0.0 + var lastAbsoluteLineBasal = -1.0 + var lastBaseBasal = 0.0 + var lastTempBasal = 0.0 + var time = fromTime + while (time < toTime) { + val profile = profileFunction.getProfile(time) + if (profile == null) { + time += 60 * 1000L + continue + } + val basalData = iobCobCalculatorPlugin.getBasalData(profile, time) + val baseBasalValue = basalData.basal + var absoluteLineValue = baseBasalValue + var tempBasalValue = 0.0 + var basal = 0.0 + if (basalData.isTempBasalRunning) { + tempBasalValue = basalData.tempBasalAbsolute + absoluteLineValue = tempBasalValue + if (tempBasalValue != lastTempBasal) { + tempBasalArray.add(ScaledDataPoint(time, lastTempBasal, basalScale)) + tempBasalArray.add(ScaledDataPoint(time, tempBasalValue.also { basal = it }, basalScale)) + } + if (lastBaseBasal != 0.0) { + baseBasalArray.add(ScaledDataPoint(time, lastBaseBasal, basalScale)) + baseBasalArray.add(ScaledDataPoint(time, 0.0, basalScale)) + lastBaseBasal = 0.0 + } + } else { + if (baseBasalValue != lastBaseBasal) { + baseBasalArray.add(ScaledDataPoint(time, lastBaseBasal, basalScale)) + baseBasalArray.add(ScaledDataPoint(time, baseBasalValue.also { basal = it }, basalScale)) + lastBaseBasal = baseBasalValue + } + if (lastTempBasal != 0.0) { + tempBasalArray.add(ScaledDataPoint(time, lastTempBasal, basalScale)) + tempBasalArray.add(ScaledDataPoint(time, 0.0, basalScale)) + } + } + if (baseBasalValue != lastLineBasal) { + basalLineArray.add(ScaledDataPoint(time, lastLineBasal, basalScale)) + basalLineArray.add(ScaledDataPoint(time, baseBasalValue, basalScale)) + } + if (absoluteLineValue != lastAbsoluteLineBasal) { + absoluteBasalLineArray.add(ScaledDataPoint(time, lastAbsoluteLineBasal, basalScale)) + absoluteBasalLineArray.add(ScaledDataPoint(time, basal, basalScale)) + } + lastAbsoluteLineBasal = absoluteLineValue + lastLineBasal = baseBasalValue + lastTempBasal = tempBasalValue + maxBasalValueFound = max(maxBasalValueFound, max(tempBasalValue, baseBasalValue)) + time += 60 * 1000L + } + + // final points + basalLineArray.add(ScaledDataPoint(toTime, lastLineBasal, basalScale)) + baseBasalArray.add(ScaledDataPoint(toTime, lastBaseBasal, basalScale)) + tempBasalArray.add(ScaledDataPoint(toTime, lastTempBasal, basalScale)) + absoluteBasalLineArray.add(ScaledDataPoint(toTime, lastAbsoluteLineBasal, basalScale)) + + // create series + addSeries(LineGraphSeries(Array(baseBasalArray.size) { i -> baseBasalArray[i] }).also { + it.isDrawBackground = true + it.backgroundColor = resourceHelper.gc(R.color.basebasal) + it.thickness = 0 + }) + addSeries(LineGraphSeries(Array(tempBasalArray.size) { i -> tempBasalArray[i] }).also { + it.isDrawBackground = true + it.backgroundColor = resourceHelper.gc(R.color.tempbasal) + it.thickness = 0 + }) + addSeries(LineGraphSeries(Array(basalLineArray.size) { i -> basalLineArray[i] }).also { + it.setCustomPaint(Paint().also { paint -> + paint.style = Paint.Style.STROKE + paint.strokeWidth = resourceHelper.getDisplayMetrics().scaledDensity * 2 + paint.pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f) + paint.color = resourceHelper.gc(R.color.basal) + }) + }) + addSeries(LineGraphSeries(Array(absoluteBasalLineArray.size) { i -> absoluteBasalLineArray[i] }).also { + it.setCustomPaint(Paint().also { absolutePaint -> + absolutePaint.style = Paint.Style.STROKE + absolutePaint.strokeWidth = resourceHelper.getDisplayMetrics().scaledDensity * 2 + absolutePaint.color = resourceHelper.gc(R.color.basal) + }) + }) + basalScale.setMultiplier(maxY * scale / maxBasalValueFound) + } + + fun addTargetLine(fromTime: Long, toTimeParam: Long, profile: Profile, lastRun: LastRun?) { + var toTime = toTimeParam + val targetsSeriesArray: MutableList = ArrayList() + var lastTarget = -1.0 + lastRun?.constraintsProcessed?.let { toTime = max(it.latestPredictionsTime, toTime) } + var time = fromTime + while (time < toTime) { + val tt = treatmentsPlugin.getTempTargetFromHistory(time) + var value: Double + value = if (tt == null) { + Profile.fromMgdlToUnits((profile.getTargetLowMgdl(time) + profile.getTargetHighMgdl(time)) / 2, units) + } else { + Profile.fromMgdlToUnits(tt.target(), units) + } + if (lastTarget != value) { + if (lastTarget != -1.0) targetsSeriesArray.add(DataPoint(time.toDouble(), lastTarget)) + targetsSeriesArray.add(DataPoint(time.toDouble(), value)) + } + lastTarget = value + time += 5 * 60 * 1000L + } + // final point + targetsSeriesArray.add(DataPoint(toTime.toDouble(), lastTarget)) + // create series + addSeries(LineGraphSeries(Array(targetsSeriesArray.size) { i -> targetsSeriesArray[i] }).also { + it.isDrawBackground = false + it.color = resourceHelper.gc(R.color.tempTargetBackground) + it.thickness = 2 + }) + } + + fun addTreatments(fromTime: Long, endTime: Long) { + val filteredTreatments: MutableList = ArrayList() + val treatments = treatmentsPlugin.treatmentsFromHistory + for (tx in treatments.indices) { + val t = treatments[tx] + if (t.x < fromTime || t.x > endTime) continue + if (t.isSMB && !t.isValid) continue + t.y = getNearestBg(t.x.toLong()) + filteredTreatments.add(t) + } + + // ProfileSwitch + val profileSwitches = treatmentsPlugin.profileSwitchesFromHistory.list + for (tx in profileSwitches.indices) { + val t: DataPointWithLabelInterface = profileSwitches[tx] + if (t.x < fromTime || t.x > endTime) continue + filteredTreatments.add(t) + } + + // Extended bolus + if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) { + val extendedBoluses = treatmentsPlugin.extendedBolusesFromHistory.list + for (tx in extendedBoluses.indices) { + val t: DataPointWithLabelInterface = extendedBoluses[tx] + if (t.x + t.duration < fromTime || t.x > endTime) continue + if (t.duration == 0L) continue + t.y = getNearestBg(t.x.toLong()) + filteredTreatments.add(t) + } + } + + // Careportal + val careportalEvents = MainApp.getDbHelper().getCareportalEventsFromTime(fromTime - 6 * 60 * 60 * 1000, true) + for (tx in careportalEvents.indices) { + val t: DataPointWithLabelInterface = careportalEvents[tx] + if (t.x + t.duration < fromTime || t.x > endTime) continue + t.y = getNearestBg(t.x.toLong()) + filteredTreatments.add(t) + } + addSeries(PointsWithLabelGraphSeries(Array(filteredTreatments.size) { i -> filteredTreatments[i] })) + } + + private fun getNearestBg(date: Long): Double { + bgReadingsArray?.let { bgReadingsArray -> + for (r in bgReadingsArray.indices) { + val reading = bgReadingsArray[r] + if (reading.date > date) continue + return Profile.fromMgdlToUnits(reading.value, units) + } + return if (bgReadingsArray.isNotEmpty()) Profile.fromMgdlToUnits(bgReadingsArray[0].value, units) else Profile.fromMgdlToUnits(100.0, units) + } ?: return Profile.fromMgdlToUnits(100.0, units) + } + + fun addActivity(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double) { + val actArrayHist: MutableList = ArrayList() + val actArrayPred: MutableList = ArrayList() + val now = System.currentTimeMillis().toDouble() + val actScale = Scale() + var total: IobTotal + var maxIAValue = 0.0 + var time = fromTime + while (time <= toTime) { + val profile = profileFunction.getProfile(time) + if (profile == null) { + time += 5 * 60 * 1000L + continue + } + total = iobCobCalculatorPlugin.calculateFromTreatmentsAndTempsSynchronized(time, profile) + val act: Double = total.activity + if (time <= now) actArrayHist.add(ScaledDataPoint(time, act, actScale)) else actArrayPred.add(ScaledDataPoint(time, act, actScale)) + maxIAValue = max(maxIAValue, abs(act)) + time += 5 * 60 * 1000L + } + addSeries(FixedLineGraphSeries(Array(actArrayHist.size) { i -> actArrayHist[i] }).also { + it.isDrawBackground = false + it.color = resourceHelper.gc(R.color.activity) + it.thickness = 3 + }) + addSeries(FixedLineGraphSeries(Array(actArrayPred.size) { i -> actArrayPred[i] }).also { + it.setCustomPaint(Paint().also { paint -> + paint.style = Paint.Style.STROKE + paint.strokeWidth = 3f + paint.pathEffect = DashPathEffect(floatArrayOf(4f, 4f), 0f) + paint.color = resourceHelper.gc(R.color.activity) + }) + }) + if (useForScale) { + maxY = maxIAValue + minY = -maxIAValue + } + actScale.setMultiplier(maxY * scale / maxIAValue) + } + + // scale in % of vertical size (like 0.3) + fun addIob(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double, showPrediction: Boolean) { + val iobSeries: FixedLineGraphSeries + val iobArray: MutableList = ArrayList() + var maxIobValueFound = Double.MIN_VALUE + var lastIob = 0.0 + val iobScale = Scale() + var time = fromTime + while (time <= toTime) { + val profile = profileFunction.getProfile(time) + var iob = 0.0 + if (profile != null) iob = iobCobCalculatorPlugin.calculateFromTreatmentsAndTempsSynchronized(time, profile).iob + if (abs(lastIob - iob) > 0.02) { + if (abs(lastIob - iob) > 0.2) iobArray.add(ScaledDataPoint(time, lastIob, iobScale)) + iobArray.add(ScaledDataPoint(time, iob, iobScale)) + maxIobValueFound = max(maxIobValueFound, abs(iob)) + lastIob = iob + } + time += 5 * 60 * 1000L + } + iobSeries = FixedLineGraphSeries(Array(iobArray.size) { i -> iobArray[i] }).also { + it.isDrawBackground = true + it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.iob) //50% + it.color = resourceHelper.gc(R.color.iob) + it.thickness = 3 + } + if (showPrediction) { + val autosensData = iobCobCalculatorPlugin.getLastAutosensDataSynchronized("GraphData") + val lastAutosensResult = autosensData?.autosensResult ?: AutosensResult() + val isTempTarget = treatmentsPlugin.getTempTargetFromHistory(System.currentTimeMillis()) != null + val iobPred: MutableList = ArrayList() + val iobPredArray = iobCobCalculatorPlugin.calculateIobArrayForSMB(lastAutosensResult, SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget) + for (i in iobPredArray) { + iobPred.add(i.setColor(resourceHelper.gc(R.color.iobPredAS))) + maxIobValueFound = max(maxIobValueFound, abs(i.iob)) + } + addSeries(PointsWithLabelGraphSeries(Array(iobPred.size) { i -> iobPred[i] })) + val iobPred2: MutableList = ArrayList() + val iobPredArray2 = iobCobCalculatorPlugin.calculateIobArrayForSMB(AutosensResult(), SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget) + for (i in iobPredArray2) { + iobPred2.add(i.setColor(resourceHelper.gc(R.color.iobPred))) + maxIobValueFound = max(maxIobValueFound, abs(i.iob)) + } + addSeries(PointsWithLabelGraphSeries(Array(iobPred2.size) { i -> iobPred2[i] })) + aapsLogger.debug(LTag.AUTOSENS, "IOB pred for AS=" + DecimalFormatter.to2Decimal(lastAutosensResult.ratio) + ": " + iobCobCalculatorPlugin.iobArrayToString(iobPredArray)) + aapsLogger.debug(LTag.AUTOSENS, "IOB pred for AS=" + DecimalFormatter.to2Decimal(1.0) + ": " + iobCobCalculatorPlugin.iobArrayToString(iobPredArray2)) + } + if (useForScale) { + maxY = maxIobValueFound + minY = -maxIobValueFound + } + iobScale.setMultiplier(maxY * scale / maxIobValueFound) + addSeries(iobSeries) + } + + // scale in % of vertical size (like 0.3) + fun addCob(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double) { + val minFailOverActiveList: MutableList = ArrayList() + val cobArray: MutableList = ArrayList() + var maxCobValueFound = 0.0 + var lastCob = 0 + val cobScale = Scale() + var time = fromTime + while (time <= toTime) { + iobCobCalculatorPlugin.getAutosensData(time)?.let { autosensData -> + val cob = autosensData.cob.toInt() + if (cob != lastCob) { + if (autosensData.carbsFromBolus > 0) cobArray.add(ScaledDataPoint(time, lastCob.toDouble(), cobScale)) + cobArray.add(ScaledDataPoint(time, cob.toDouble(), cobScale)) + maxCobValueFound = max(maxCobValueFound, cob.toDouble()) + lastCob = cob + } + if (autosensData.failoverToMinAbsorbtionRate) { + autosensData.setScale(cobScale) + autosensData.setChartTime(time) + minFailOverActiveList.add(autosensData) + } + } + time += 5 * 60 * 1000L + } + + // COB + addSeries(FixedLineGraphSeries(Array(cobArray.size) { i -> cobArray[i] }).also { + it.isDrawBackground = true + it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.cob) //50% + it.color = resourceHelper.gc(R.color.cob) + it.thickness = 3 + }) + if (useForScale) { + maxY = maxCobValueFound + minY = 0.0 + } + cobScale.setMultiplier(maxY * scale / maxCobValueFound) + addSeries(PointsWithLabelGraphSeries(Array(minFailOverActiveList.size) { i -> minFailOverActiveList[i] })) + } + + // scale in % of vertical size (like 0.3) + fun addDeviations(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double) { + class DeviationDataPoint(x: Double, y: Double, var color: Int, scale: Scale) : ScaledDataPoint(x, y, scale) + + val devArray: MutableList = ArrayList() + var maxDevValueFound = 0.0 + val devScale = Scale() + var time = fromTime + while (time <= toTime) { + iobCobCalculatorPlugin.getAutosensData(time)?.let { autosensData -> + var color = resourceHelper.gc(R.color.deviationblack) // "=" + if (autosensData.type == "" || autosensData.type == "non-meal") { + if (autosensData.pastSensitivity == "C") color = resourceHelper.gc(R.color.deviationgrey) + if (autosensData.pastSensitivity == "+") color = resourceHelper.gc(R.color.deviationgreen) + if (autosensData.pastSensitivity == "-") color = resourceHelper.gc(R.color.deviationred) + } else if (autosensData.type == "uam") { + color = resourceHelper.gc(R.color.uam) + } else if (autosensData.type == "csf") { + color = resourceHelper.gc(R.color.deviationgrey) + } + devArray.add(DeviationDataPoint(time.toDouble(), autosensData.deviation, color, devScale)) + maxDevValueFound = max(maxDevValueFound, abs(autosensData.deviation)) + } + time += 5 * 60 * 1000L + } + + // DEVIATIONS + addSeries(BarGraphSeries(Array(devArray.size) { i -> devArray[i] }).also { + it.setValueDependentColor { data: DeviationDataPoint -> data.color } + }) + if (useForScale) { + maxY = maxDevValueFound + minY = -maxY + } + devScale.setMultiplier(maxY * scale / maxDevValueFound) + } + + // scale in % of vertical size (like 0.3) + fun addRatio(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double) { + val ratioArray: MutableList = ArrayList() + var maxRatioValueFound = Double.MIN_VALUE + var minRatioValueFound = Double.MAX_VALUE + val ratioScale = Scale() + var time = fromTime + while (time <= toTime) { + iobCobCalculatorPlugin.getAutosensData(time)?.let { autosensData -> + ratioArray.add(ScaledDataPoint(time, autosensData.autosensResult.ratio - 1, ratioScale)) + maxRatioValueFound = max(maxRatioValueFound, autosensData.autosensResult.ratio - 1) + minRatioValueFound = min(minRatioValueFound, autosensData.autosensResult.ratio - 1) + } + time += 5 * 60 * 1000L + } + + // RATIOS + addSeries(LineGraphSeries(Array(ratioArray.size) { i -> ratioArray[i] }).also { + it.color = resourceHelper.gc(R.color.ratio) + it.thickness = 3 + }) + if (useForScale) { + maxY = max(maxRatioValueFound, abs(minRatioValueFound)) + minY = -maxY + } + ratioScale.setMultiplier(maxY * scale / max(maxRatioValueFound, abs(minRatioValueFound))) + } + + // scale in % of vertical size (like 0.3) + fun addDeviationSlope(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double) { + val dsMaxArray: MutableList = ArrayList() + val dsMinArray: MutableList = ArrayList() + var maxFromMaxValueFound = 0.0 + var maxFromMinValueFound = 0.0 + val dsMaxScale = Scale() + val dsMinScale = Scale() + var time = fromTime + while (time <= toTime) { + iobCobCalculatorPlugin.getAutosensData(time)?.let { autosensData -> + dsMaxArray.add(ScaledDataPoint(time, autosensData.slopeFromMaxDeviation, dsMaxScale)) + dsMinArray.add(ScaledDataPoint(time, autosensData.slopeFromMinDeviation, dsMinScale)) + maxFromMaxValueFound = max(maxFromMaxValueFound, abs(autosensData.slopeFromMaxDeviation)) + maxFromMinValueFound = max(maxFromMinValueFound, abs(autosensData.slopeFromMinDeviation)) + } + time += 5 * 60 * 1000L + } + + // Slopes + addSeries(LineGraphSeries(Array(dsMaxArray.size) { i -> dsMaxArray[i] }).also { + it.color = resourceHelper.gc(R.color.devslopepos) + it.thickness = 3 + }) + addSeries(LineGraphSeries(Array(dsMinArray.size) { i -> dsMinArray[i] }).also { + it.color = resourceHelper.gc(R.color.devslopeneg) + it.thickness = 3 + }) + if (useForScale) { + maxY = max(maxFromMaxValueFound, maxFromMinValueFound) + minY = -maxY + } + dsMaxScale.setMultiplier(maxY * scale / maxFromMaxValueFound) + dsMinScale.setMultiplier(maxY * scale / maxFromMinValueFound) + } + + // scale in % of vertical size (like 0.3) + fun addNowLine(now: Long) { + val nowPoints = arrayOf( + DataPoint(now.toDouble(), 0.0), + DataPoint(now.toDouble(), maxY) + ) + addSeries(LineGraphSeries(nowPoints).also { + it.isDrawDataPoints = false + // custom paint to make a dotted line + it.setCustomPaint(Paint().also { paint -> + paint.style = Paint.Style.STROKE + paint.strokeWidth = 2f + paint.pathEffect = DashPathEffect(floatArrayOf(10f, 20f), 0f) + paint.color = Color.WHITE + }) + }) + } + + fun formatAxis(fromTime: Long, endTime: Long) { + graph.viewport.setMaxX(endTime.toDouble()) + graph.viewport.setMinX(fromTime.toDouble()) + graph.viewport.isXAxisBoundsManual = true + graph.gridLabelRenderer.labelFormatter = TimeAsXAxisLabelFormatter("HH") + graph.gridLabelRenderer.numHorizontalLabels = 7 // only 7 because of the space + } + + private fun addSeries(s: Series<*>) = series.add(s) + + fun performUpdate() { + // clear old data + graph.series.clear() + + // add pre calculated series + for (s in series) { + if (!s.isEmpty) { + s.onGraphViewAttached(graph) + graph.series.add(s) + } + } + var step = 1.0 + if (maxY < 1) step = 0.1 + graph.viewport.setMaxY(Round.ceilTo(maxY, step)) + graph.viewport.setMinY(Round.floorTo(minY, step)) + graph.viewport.isYAxisBoundsManual = true + + // draw it + graph.onDataChanged(false, false) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/ScaledDataPoint.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/ScaledDataPoint.java index ac63790a71..8a00446092 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/ScaledDataPoint.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/ScaledDataPoint.java @@ -23,6 +23,12 @@ public class ScaledDataPoint implements DataPointInterface, Serializable { this.scale = scale; } + public ScaledDataPoint(long x, double y, Scale scale) { + this.x=x; + this.y=y; + this.scale = scale; + } + public ScaledDataPoint(Date x, double y, Scale scale) { this.x = x.getTime(); this.y = y; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/DismissNotificationService.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/DismissNotificationService.java deleted file mode 100644 index 7491b8abd6..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/DismissNotificationService.java +++ /dev/null @@ -1,38 +0,0 @@ -package info.nightscout.androidaps.plugins.general.overview.notifications; - -import android.app.IntentService; -import android.app.PendingIntent; -import android.content.Intent; - -import androidx.annotation.Nullable; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.plugins.bus.RxBus; -import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; - -public class DismissNotificationService extends IntentService { - - /** - * Creates an IntentService. Invoked by your subclass's constructor. - * - * @param name Used to name the worker thread, important only for debugging. - */ - public DismissNotificationService(String name) { - super(name); - } - - public DismissNotificationService() { - super("DismissNotificationService"); - } - - @Override - protected void onHandleIntent(@Nullable Intent intent) { - RxBus.Companion.getINSTANCE().send(new EventDismissNotification(intent.getIntExtra("alertID", -1))); - } - - public static PendingIntent deleteIntent(int id) { - Intent intent = new Intent(MainApp.instance(), DismissNotificationService.class); - intent.putExtra("alertID", id); - return PendingIntent.getService(MainApp.instance(), id, intent, PendingIntent.FLAG_UPDATE_CURRENT); - } -} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/DismissNotificationService.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/DismissNotificationService.kt new file mode 100644 index 0000000000..3d4e2d0600 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/DismissNotificationService.kt @@ -0,0 +1,15 @@ +package info.nightscout.androidaps.plugins.general.overview.notifications + +import android.content.Intent +import dagger.android.DaggerIntentService +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification +import javax.inject.Inject + +class DismissNotificationService : DaggerIntentService(DismissNotificationService::class.simpleName) { + @Inject lateinit var rxBus: RxBusWrapper + + override fun onHandleIntent(intent: Intent) { + rxBus.send(EventDismissNotification(intent.getIntExtra("alertID", -1))) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/Notification.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/Notification.java index 85effcb113..3089ca1a7c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/Notification.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/Notification.java @@ -64,11 +64,11 @@ public class Notification { public static final int DST_IN_24H = 50; public static final int DISKFULL = 51; public static final int OLDVERSION = 52; - public static final int USERMESSAGE = 53; public static final int OVER_24H_TIME_CHANGE_REQUESTED = 54; public static final int INVALID_VERSION = 55; public static final int PERMISSION_SYSTEM_WINDOW = 56; + public static final int USERMESSAGE = 1000; public int id; public long date; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/NotificationStore.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/NotificationStore.kt index 43d4327450..a14f33e33c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/NotificationStore.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/NotificationStore.kt @@ -3,6 +3,7 @@ package info.nightscout.androidaps.plugins.general.overview.notifications import android.annotation.SuppressLint import android.app.NotificationChannel import android.app.NotificationManager +import android.app.PendingIntent import android.content.Context import android.content.Intent import android.media.AudioManager @@ -16,7 +17,6 @@ import android.widget.TextView import androidx.cardview.widget.CardView import androidx.core.app.NotificationCompat import androidx.recyclerview.widget.RecyclerView -import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.R import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag @@ -36,7 +36,7 @@ class NotificationStore @Inject constructor( private val sp: SP, private val rxBus: RxBusWrapper, private val resourceHelper: ResourceHelper, - private val mainApp: MainApp + private val context: Context ) { var store: MutableList = ArrayList() @@ -66,15 +66,15 @@ class NotificationStore @Inject constructor( if (sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, false) && n !is NotificationWithAction) { raiseSystemNotification(n) if (usesChannels && n.soundId != null && n.soundId != 0) { - val alarm = Intent(mainApp, AlarmSoundService::class.java) + val alarm = Intent(context, AlarmSoundService::class.java) alarm.putExtra("soundid", n.soundId) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) mainApp.startForegroundService(alarm) else mainApp.startService(alarm) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) context.startForegroundService(alarm) else context.startService(alarm) } } else { if (n.soundId != null && n.soundId != 0) { - val alarm = Intent(mainApp, AlarmSoundService::class.java) + val alarm = Intent(context, AlarmSoundService::class.java) alarm.putExtra("soundid", n.soundId) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) mainApp.startForegroundService(alarm) else mainApp.startService(alarm) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) context.startForegroundService(alarm) else context.startService(alarm) } } Collections.sort(store, NotificationComparator()) @@ -85,8 +85,8 @@ class NotificationStore @Inject constructor( for (i in store.indices) { if (store[i].id == id) { if (store[i].soundId != null) { - val alarm = Intent(mainApp, AlarmSoundService::class.java) - mainApp.stopService(alarm) + val alarm = Intent(context, AlarmSoundService::class.java) + context.stopService(alarm) } store.removeAt(i) return true @@ -108,16 +108,16 @@ class NotificationStore @Inject constructor( } private fun raiseSystemNotification(n: Notification) { - val mgr = mainApp.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + val mgr = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val largeIcon = resourceHelper.decodeResource(resourceHelper.getIcon()) val smallIcon = resourceHelper.getNotificationIcon() val sound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM) - val notificationBuilder = NotificationCompat.Builder(mainApp, CHANNEL_ID) + val notificationBuilder = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(smallIcon) .setLargeIcon(largeIcon) .setContentText(n.text) .setPriority(NotificationCompat.PRIORITY_MAX) - .setDeleteIntent(DismissNotificationService.deleteIntent(n.id)) + .setDeleteIntent(deleteIntent(n.id)) if (n.level == Notification.URGENT) { notificationBuilder.setVibrate(longArrayOf(1000, 1000, 1000, 1000)) .setContentTitle(resourceHelper.gs(R.string.urgent_alarm)) @@ -129,10 +129,16 @@ class NotificationStore @Inject constructor( mgr.notify(n.id, notificationBuilder.build()) } + private fun deleteIntent(id: Int): PendingIntent { + val intent = Intent(context, DismissNotificationService::class.java) + intent.putExtra("alertID", id) + return PendingIntent.getService(context, id, intent, PendingIntent.FLAG_UPDATE_CURRENT) + } + fun createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { usesChannels = true - val mNotificationManager = mainApp.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + val mNotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager @SuppressLint("WrongConstant") val channel = NotificationChannel(CHANNEL_ID, CHANNEL_ID, NotificationManager.IMPORTANCE_HIGH) @@ -159,17 +165,19 @@ class NotificationStore @Inject constructor( clone.addAll(store) return clone } -/* - private fun unSnooze() { - if (sp.getBoolean(R.string.key_nsalarm_staledata, false)) { - val notification = Notification(Notification.NSALARM, resourceHelper.gs(R.string.nsalarm_staledata), Notification.URGENT) - sp.putLong(R.string.key_snoozedTo, System.currentTimeMillis()) - add(notification) - aapsLogger.debug(LTag.NOTIFICATION, "Snoozed to current time and added back notification!") + + /* + private fun unSnooze() { + if (sp.getBoolean(R.string.key_nsalarm_staledata, false)) { + val notification = Notification(Notification.NSALARM, resourceHelper.gs(R.string.nsalarm_staledata), Notification.URGENT) + sp.putLong(R.string.key_snoozedTo, System.currentTimeMillis()) + add(notification) + aapsLogger.debug(LTag.NOTIFICATION, "Snoozed to current time and added back notification!") + } } - } -*/ + */ inner class NotificationRecyclerViewAdapter internal constructor(private val notificationsList: List) : RecyclerView.Adapter() { + override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): NotificationsViewHolder { val v = LayoutInflater.from(viewGroup.context).inflate(R.layout.overview_notification_item, viewGroup, false) return NotificationsViewHolder(v) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/NotificationUserMessage.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/NotificationUserMessage.kt new file mode 100644 index 0000000000..3d198dfc0b --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/NotificationUserMessage.kt @@ -0,0 +1,12 @@ +package info.nightscout.androidaps.plugins.general.overview.notifications + +class NotificationUserMessage (text :String): Notification() { + + init { + var hash = text.hashCode() + if (hash < USERMESSAGE) hash += USERMESSAGE + id = hash + this.text = text + level = URGENT + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt index 78a0bf0ffc..350e5f08db 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt @@ -703,13 +703,11 @@ class SmsCommunicatorPlugin @Inject constructor( var grams = SafeParse.stringToInt(splitted[1]) var time = DateUtil.now() if (splitted.size > 2) { - val seconds = DateUtil.toSeconds(splitted[2].toUpperCase(Locale.getDefault())) - val midnight = MidnightTime.calc() - if (seconds == 0 && (!splitted[2].startsWith("00:00") || !splitted[2].startsWith("12:00"))) { + time = DateUtil.toTodayTime(splitted[2].toUpperCase(Locale.getDefault())) + if (time == 0L) { sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat))) return } - time = midnight + T.secs(seconds.toLong()).msecs() } grams = constraintChecker.applyCarbsConstraints(Constraint(grams)).value() if (grams == 0) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat))) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/TidepoolPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/TidepoolPlugin.kt index ce535e41fd..701221e361 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/TidepoolPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/TidepoolPlugin.kt @@ -25,8 +25,7 @@ import info.nightscout.androidaps.plugins.general.tidepool.events.EventTidepoolR import info.nightscout.androidaps.plugins.general.tidepool.events.EventTidepoolStatus import info.nightscout.androidaps.plugins.general.tidepool.events.EventTidepoolUpdateGUI import info.nightscout.androidaps.plugins.general.tidepool.utils.RateLimit -import info.nightscout.androidaps.receivers.ChargingStateReceiver -import info.nightscout.androidaps.receivers.NetworkChangeReceiver +import info.nightscout.androidaps.receivers.ReceiverStatusStore import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.T @@ -50,7 +49,9 @@ class TidepoolPlugin @Inject constructor( private val fabricPrivacy: FabricPrivacy, private val tidepoolUploader: TidepoolUploader, private val uploadChunk: UploadChunk, - private val sp: SP + private val sp: SP, + private val rateLimit: RateLimit, + private val receiverStatusStore: ReceiverStatusStore ) : PluginBase(PluginDescription() .mainType(PluginType.GENERAL) .pluginName(R.string.tidepool) @@ -101,9 +102,9 @@ class TidepoolPlugin @Inject constructor( if (bgReading!!.date < uploadChunk.getLastEnd()) uploadChunk.setLastEnd(bgReading.date) if (isEnabled(PluginType.GENERAL) - && (!sp.getBoolean(R.string.key_tidepool_only_while_charging, false) || ChargingStateReceiver.isCharging()) - && (!sp.getBoolean(R.string.key_tidepool_only_while_unmetered, false) || NetworkChangeReceiver.isWifiConnected()) - && RateLimit.rateLimit("tidepool-new-data-upload", T.mins(4).secs().toInt())) + && (!sp.getBoolean(R.string.key_tidepool_only_while_charging, false) || receiverStatusStore.isCharging) + && (!sp.getBoolean(R.string.key_tidepool_only_while_unmetered, false) || receiverStatusStore.isWifiConnected) + && rateLimit.rateLimit("tidepool-new-data-upload", T.mins(4).secs().toInt())) doUpload() }, { fabricPrivacy.logException(it) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/InfoInterceptor.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/InfoInterceptor.kt index c1a9f55dc6..59268f49b0 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/InfoInterceptor.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/InfoInterceptor.kt @@ -1,32 +1,22 @@ package info.nightscout.androidaps.plugins.general.tidepool.comm -import info.nightscout.androidaps.logging.L -import info.nightscout.androidaps.logging.StacktraceLoggerWrapper +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag import okhttp3.Interceptor import okhttp3.Response import okio.Buffer -import org.slf4j.LoggerFactory import java.io.IOException -class InfoInterceptor(tag: String) : Interceptor { - - private val log = StacktraceLoggerWrapper.getLogger(L.TIDEPOOL) - private var tag = "interceptor" - - init { - this.tag = tag - } +class InfoInterceptor(val tag: String = "interceptor", val aapsLogger: AAPSLogger) : Interceptor { @Throws(IOException::class) override fun intercept(chain: Interceptor.Chain): Response { val request = chain.request() request.body?.let { - if (L.isEnabled(L.TIDEPOOL)) { - log.debug("Interceptor Body size: " + it.contentLength()) - val requestBuffer = Buffer() - it.writeTo(requestBuffer) - log.debug("Interceptor Body: " + requestBuffer.readUtf8()) - } + aapsLogger.debug(LTag.TIDEPOOL, "Interceptor Body size: " + it.contentLength()) + val requestBuffer = Buffer() + it.writeTo(requestBuffer) + aapsLogger.debug(LTag.TIDEPOOL, "Interceptor Body: " + requestBuffer.readUtf8()) } return chain.proceed(request) } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/TidepoolUploader.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/TidepoolUploader.kt index a6d89f8971..51c16af342 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/TidepoolUploader.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/TidepoolUploader.kt @@ -68,7 +68,7 @@ class TidepoolUploader @Inject constructor( val client = OkHttpClient.Builder() .addInterceptor(httpLoggingInterceptor) - .addInterceptor(InfoInterceptor(TidepoolUploader::class.java.name)) + .addInterceptor(InfoInterceptor(TidepoolUploader::class.java.name, aapsLogger)) .build() retrofit = Retrofit.Builder() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/events/EventTidepoolStatus.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/events/EventTidepoolStatus.kt index bbb86c45b8..aff358ba08 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/events/EventTidepoolStatus.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/events/EventTidepoolStatus.kt @@ -1,23 +1,13 @@ package info.nightscout.androidaps.plugins.general.tidepool.events import info.nightscout.androidaps.events.Event -import info.nightscout.androidaps.logging.L -import info.nightscout.androidaps.logging.StacktraceLoggerWrapper import info.nightscout.androidaps.utils.DateUtil -import org.slf4j.LoggerFactory import java.text.SimpleDateFormat import java.util.* class EventTidepoolStatus(val status: String) : Event() { - private val log = StacktraceLoggerWrapper.getLogger(L.TIDEPOOL) - var date: Long = DateUtil.now() - init { - if (L.isEnabled(L.TIDEPOOL)) - log.debug("New status: $status") - } - private var timeFormat = SimpleDateFormat("HH:mm:ss", Locale.getDefault()) fun toPreparedHtml(): StringBuilder { @@ -29,5 +19,4 @@ class EventTidepoolStatus(val status: String) : Event() { stringBuilder.append("
") return stringBuilder } - } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/utils/RateLimit.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/utils/RateLimit.kt index 6f23a3f9c1..9e48d0b90f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/utils/RateLimit.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/utils/RateLimit.kt @@ -1,26 +1,27 @@ package info.nightscout.androidaps.plugins.general.tidepool.utils -import info.nightscout.androidaps.logging.L -import info.nightscout.androidaps.logging.StacktraceLoggerWrapper +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.T -import org.slf4j.LoggerFactory import java.util.* +import javax.inject.Inject +import javax.inject.Singleton -object RateLimit { +@Singleton +class RateLimit @Inject constructor( + val aapsLogger: AAPSLogger +) { private val rateLimits = HashMap() - private val log = StacktraceLoggerWrapper.getLogger(L.TIDEPOOL) - // return true if below rate limit @Synchronized fun rateLimit(name: String, seconds: Int): Boolean { // check if over limit rateLimits[name]?.let { if (DateUtil.now() - it < T.secs(seconds.toLong()).msecs()) { - if (L.isEnabled(L.TIDEPOOL)) - log.debug("$name rate limited: $seconds seconds") + aapsLogger.debug(LTag.TIDEPOOL, "$name rate limited: $seconds seconds") return false } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/AutosensResult.java b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/AutosensResult.java index 3f478cc478..e1debef726 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/AutosensResult.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/AutosensResult.java @@ -2,17 +2,14 @@ package info.nightscout.androidaps.plugins.iob.iobCobCalculator; import org.json.JSONException; import org.json.JSONObject; -import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.logging.StacktraceLoggerWrapper; +import info.nightscout.androidaps.logging.LTag; /** * Created by mike on 06.01.2017. */ public class AutosensResult { - private static Logger log = StacktraceLoggerWrapper.getLogger(L.AUTOSENS); //default values to show when autosens algorithm is not called public double ratio = 1d; @@ -30,7 +27,7 @@ public class AutosensResult { ret.put("sensResult", sensResult); ret.put("ratio", ratio); } catch (JSONException e) { - log.error("Unhandled exception", e); + LoggerFactory.getLogger(LTag.CORE.getTag()).error("Unhandled exception", e); } return ret; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobOref1Thread.java b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobOref1Thread.java index c3d6685538..505b9a799f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobOref1Thread.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobOref1Thread.java @@ -196,17 +196,15 @@ public class IobCobOref1Thread extends Thread { try { for (; past < 12; past++) { AutosensData ad = autosensDataTable.valueAt(initialIndex + past); - if (L.isEnabled(L.AUTOSENS)) { - aapsLogger.debug(">>>>> past=" + past + " ad=" + (ad != null ? ad.toString() : null)); - if (ad == null) { - aapsLogger.debug(autosensDataTable.toString()); - aapsLogger.debug(bucketed_data.toString()); - aapsLogger.debug(iobCobCalculatorPlugin.getBgReadings().toString()); - Notification notification = new Notification(Notification.SENDLOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW); - rxBus.send(new EventNewNotification(notification)); - sp.putBoolean("log_AUTOSENS", true); - break; - } + aapsLogger.debug(LTag.AUTOSENS, ">>>>> past=" + past + " ad=" + (ad != null ? ad.toString() : null)); + if (ad == null) { + aapsLogger.debug(LTag.AUTOSENS, autosensDataTable.toString()); + aapsLogger.debug(LTag.AUTOSENS, bucketed_data.toString()); + aapsLogger.debug(LTag.AUTOSENS, iobCobCalculatorPlugin.getBgReadings().toString()); + Notification notification = new Notification(Notification.SENDLOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW); + rxBus.send(new EventNewNotification(notification)); + sp.putBoolean("log_AUTOSENS", true); + break; } // let it here crash on NPE to get more data as i cannot reproduce this bug double deviationSlope = (ad.avgDeviation - avgDeviation) / (ad.time - bgTime) * 1000 * 60 * 5; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobThread.java b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobThread.java index 950add8cbe..6e30d776b2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobThread.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobThread.java @@ -21,7 +21,6 @@ import info.nightscout.androidaps.events.Event; import info.nightscout.androidaps.interfaces.ActivePluginProvider; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.logging.LTag; import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults; import info.nightscout.androidaps.plugins.bus.RxBusWrapper; @@ -193,17 +192,15 @@ public class IobCobThread extends Thread { try { for (; past < 12; past++) { AutosensData ad = autosensDataTable.valueAt(initialIndex + past); - if (L.isEnabled(L.AUTOSENS)) { - aapsLogger.debug(">>>>> past=" + past + " ad=" + (ad != null ? ad.toString() : null)); - if (ad == null) { - aapsLogger.debug(autosensDataTable.toString()); - aapsLogger.debug(bucketed_data.toString()); - aapsLogger.debug(iobCobCalculatorPlugin.getBgReadings().toString()); - Notification notification = new Notification(Notification.SENDLOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW); - rxBus.send(new EventNewNotification(notification)); - sp.putBoolean("log_AUTOSENS", true); - break; - } + aapsLogger.debug(LTag.AUTOSENS, ">>>>> past=" + past + " ad=" + (ad != null ? ad.toString() : null)); + if (ad == null) { + aapsLogger.debug(LTag.AUTOSENS, autosensDataTable.toString()); + aapsLogger.debug(LTag.AUTOSENS, bucketed_data.toString()); + aapsLogger.debug(LTag.AUTOSENS, iobCobCalculatorPlugin.getBgReadings().toString()); + Notification notification = new Notification(Notification.SENDLOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW); + rxBus.send(new EventNewNotification(notification)); + sp.putBoolean("log_AUTOSENS", true); + break; } // let it here crash on NPE to get more data as i cannot reproduce this bug double deviationSlope = (ad.avgDeviation - avgDeviation) / (ad.time - bgTime) * 1000 * 60 * 5; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/AbstractDanaRExecutionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/AbstractDanaRExecutionService.java index a4f66ffb8a..743b0109f9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/AbstractDanaRExecutionService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/AbstractDanaRExecutionService.java @@ -3,7 +3,6 @@ package info.nightscout.androidaps.plugins.pump.danaR.services; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothSocket; -import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.IBinder; @@ -20,6 +19,9 @@ import dagger.android.HasAndroidInjector; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.PumpEnactResult; +import info.nightscout.androidaps.events.EventAppExit; +import info.nightscout.androidaps.events.EventBTChange; +import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.events.EventPumpStatusChanged; import info.nightscout.androidaps.logging.AAPSLogger; import info.nightscout.androidaps.logging.LTag; @@ -41,9 +43,12 @@ import info.nightscout.androidaps.plugins.pump.danaR.comm.MsgPCCommStop; import info.nightscout.androidaps.plugins.pump.danaR.comm.RecordTypes; import info.nightscout.androidaps.plugins.treatments.Treatment; import info.nightscout.androidaps.utils.DateUtil; +import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.ToastUtils; import info.nightscout.androidaps.utils.resources.ResourceHelper; import info.nightscout.androidaps.utils.sharedPreferences.SP; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; /** * Created by mike on 28.01.2018. @@ -57,6 +62,9 @@ public abstract class AbstractDanaRExecutionService extends DaggerService { @Inject Context context; @Inject ResourceHelper resourceHelper; @Inject DanaRPump danaRPump; + @Inject FabricPrivacy fabricPrivacy; + + private CompositeDisposable disposable = new CompositeDisposable(); protected String mDevName; @@ -101,22 +109,41 @@ public abstract class AbstractDanaRExecutionService extends DaggerService { public abstract PumpEnactResult setUserOptions(); - protected BroadcastReceiver receiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); - String action = intent.getAction(); - if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) { - aapsLogger.debug(LTag.PUMP, "Device was disconnected " + device.getName());//Device was disconnected - if (mBTDevice != null && mBTDevice.getName() != null && mBTDevice.getName().equals(device.getName())) { - if (mSerialIOThread != null) { - mSerialIOThread.disconnect("BT disconnection broadcast"); + @Override public void onCreate() { + super.onCreate(); + disposable.add(rxBus + .toObservable(EventBTChange.class) + .observeOn(Schedulers.io()) + .subscribe(event -> { + if (event.getState() == EventBTChange.Change.DISCONNECT) { + aapsLogger.debug(LTag.PUMP, "Device was disconnected " + event.getDeviceName());//Device was disconnected + if (mBTDevice != null && mBTDevice.getName() != null && mBTDevice.getName().equals(event.getDeviceName())) { + if (mSerialIOThread != null) { + mSerialIOThread.disconnect("BT disconnection broadcast"); + } + rxBus.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED)); + } } - rxBus.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED)); - } - } - } - }; + }, fabricPrivacy::logException) + ); + disposable.add(rxBus + .toObservable(EventAppExit.class) + .observeOn(Schedulers.io()) + .subscribe(event -> { + aapsLogger.debug(LTag.PUMP, "EventAppExit received"); + if (mSerialIOThread != null) + mSerialIOThread.disconnect("Application exit"); + stopSelf(); + }, fabricPrivacy::logException) + ); + + } + + @Override + public void onDestroy() { + disposable.clear(); + super.onDestroy(); + } @Override public IBinder onBind(Intent intent) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/DanaRExecutionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/DanaRExecutionService.java index b71ed5abea..1a677f60e6 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/DanaRExecutionService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/DanaRExecutionService.java @@ -92,42 +92,13 @@ public class DanaRExecutionService extends AbstractDanaRExecutionService { @Inject ProfileFunction profileFunction; @Inject SP sp; - private CompositeDisposable disposable = new CompositeDisposable(); - public DanaRExecutionService() { - } @Override public void onCreate() { super.onCreate(); mBinder = new LocalBinder(); - context.registerReceiver(receiver, new IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECTED)); - disposable.add(rxBus - .toObservable(EventPreferenceChange.class) - .observeOn(Schedulers.io()) - .subscribe(event -> { - if (mSerialIOThread != null) - mSerialIOThread.disconnect("EventPreferenceChange"); - }, exception -> FabricPrivacy.getInstance().logException(exception)) - ); - disposable.add(rxBus - .toObservable(EventAppExit.class) - .observeOn(Schedulers.io()) - .subscribe(event -> { - aapsLogger.debug(LTag.PUMP, "EventAppExit received"); - if (mSerialIOThread != null) - mSerialIOThread.disconnect("Application exit"); - context.unregisterReceiver(receiver); - stopSelf(); - }, exception -> FabricPrivacy.getInstance().logException(exception)) - ); - } - - @Override - public void onDestroy() { - disposable.clear(); - super.onDestroy(); } public class LocalBinder extends Binder { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/services/DanaRKoreanExecutionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/services/DanaRKoreanExecutionService.java index f695448820..a0b11102b4 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/services/DanaRKoreanExecutionService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/services/DanaRKoreanExecutionService.java @@ -83,8 +83,6 @@ public class DanaRKoreanExecutionService extends AbstractDanaRExecutionService { @Inject ActivePluginProvider activePlugin; @Inject ProfileFunction profileFunction; - private CompositeDisposable disposable = new CompositeDisposable(); - public DanaRKoreanExecutionService() { } @@ -92,33 +90,6 @@ public class DanaRKoreanExecutionService extends AbstractDanaRExecutionService { public void onCreate() { super.onCreate(); mBinder = new LocalBinder(); - context.registerReceiver(receiver, new IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECTED)); - disposable.add(rxBus - .toObservable(EventPreferenceChange.class) - .observeOn(Schedulers.io()) - .subscribe(event -> { - if (mSerialIOThread != null) - mSerialIOThread.disconnect("EventPreferenceChange"); - }, exception -> FabricPrivacy.getInstance().logException(exception)) - ); - disposable.add(rxBus - .toObservable(EventAppExit.class) - .observeOn(Schedulers.io()) - .subscribe(event -> { - aapsLogger.debug(LTag.PUMP, "EventAppExit received"); - - if (mSerialIOThread != null) - mSerialIOThread.disconnect("Application exit"); - context.unregisterReceiver(receiver); - stopSelf(); - }, exception -> FabricPrivacy.getInstance().logException(exception)) - ); - } - - @Override - public void onDestroy() { - disposable.clear(); - super.onDestroy(); } public class LocalBinder extends Binder { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/services/DanaRv2ExecutionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/services/DanaRv2ExecutionService.java index 76f3083385..264c7dadf1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/services/DanaRv2ExecutionService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/services/DanaRv2ExecutionService.java @@ -108,8 +108,6 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { @Inject ProfileFunction profileFunction; @Inject SP sp; - private CompositeDisposable disposable = new CompositeDisposable(); - private long lastHistoryFetched = 0; public DanaRv2ExecutionService() { @@ -125,33 +123,6 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { public void onCreate() { super.onCreate(); mBinder = new LocalBinder(); - context.registerReceiver(receiver, new IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECTED)); - disposable.add(rxBus - .toObservable(EventPreferenceChange.class) - .observeOn(Schedulers.io()) - .subscribe(event -> { - if (mSerialIOThread != null) - mSerialIOThread.disconnect("EventPreferenceChange"); - }, exception -> FabricPrivacy.getInstance().logException(exception)) - ); - disposable.add(rxBus - .toObservable(EventAppExit.class) - .observeOn(Schedulers.io()) - .subscribe(event -> { - aapsLogger.debug(LTag.PUMP, "EventAppExit received"); - - if (mSerialIOThread != null) - mSerialIOThread.disconnect("Application exit"); - context.getApplicationContext().unregisterReceiver(receiver); - stopSelf(); - }, exception -> FabricPrivacy.getInstance().logException(exception)) - ); - } - - @Override - public void onDestroy() { - disposable.clear(); - super.onDestroy(); } public void connect() { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicFragment.kt index ad1d1054f6..05ff1cf463 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicFragment.kt @@ -37,6 +37,7 @@ import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.events.EventQueueChanged import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.OKDialog import info.nightscout.androidaps.utils.SetWarnColor import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.extensions.plusAssign @@ -86,13 +87,13 @@ class MedtronicFragment : DaggerFragment() { if (MedtronicUtil.getPumpStatus().verifyConfiguration()) { startActivity(Intent(context, MedtronicHistoryActivity::class.java)) } else { - MedtronicUtil.displayNotConfiguredDialog(context) + displayNotConfiguredDialog() } } medtronic_refresh.setOnClickListener { if (!MedtronicUtil.getPumpStatus().verifyConfiguration()) { - MedtronicUtil.displayNotConfiguredDialog(context) + displayNotConfiguredDialog() } else { medtronic_refresh.isEnabled = false medtronicPumpPlugin.resetStatusState() @@ -108,7 +109,7 @@ class MedtronicFragment : DaggerFragment() { if (MedtronicUtil.getPumpStatus().verifyConfiguration()) { startActivity(Intent(context, RileyLinkStatusActivity::class.java)) } else { - MedtronicUtil.displayNotConfiguredDialog(context) + displayNotConfiguredDialog() } } } @@ -247,6 +248,13 @@ class MedtronicFragment : DaggerFragment() { } } + private fun displayNotConfiguredDialog() { + context?.let { + OKDialog.show(it, resourceHelper.gs(R.string.combo_warning), + resourceHelper.gs(R.string.medtronic_error_operation_not_possible_no_configuration), null) + } + } + // GUI functions @Synchronized fun updateGUI() { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUIComm.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUIComm.java index b2a5c97233..b4cebbb1cf 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUIComm.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUIComm.java @@ -1,7 +1,6 @@ package info.nightscout.androidaps.plugins.pump.medtronic.comm.ui; import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.logging.LTag; import info.nightscout.androidaps.plugins.bus.RxBusWrapper; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst; @@ -43,8 +42,7 @@ public class MedtronicUIComm { public synchronized MedtronicUITask executeCommand(MedtronicCommandType commandType, Object... parameters) { - if (isLogEnabled()) - aapsLogger.warn(LTag.PUMP, "Execute Command: " + commandType.name()); + aapsLogger.warn(LTag.PUMP, "Execute Command: " + commandType.name()); MedtronicUITask task = new MedtronicUITask(commandType, parameters); @@ -78,7 +76,7 @@ public class MedtronicUIComm { // } // } - if (!task.isReceived() && isLogEnabled()) { + if (!task.isReceived()) { aapsLogger.warn(LTag.PUMP, "Reply not received for " + commandType); } @@ -112,9 +110,4 @@ public class MedtronicUIComm { public void startTunning() { RileyLinkUtil.sendBroadcastMessage(RileyLinkConst.IPC.MSG_PUMP_tunePump); } - - private boolean isLogEnabled() { - return L.isEnabled(L.PUMP); - } - } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/service/RileyLinkMedtronicService.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/service/RileyLinkMedtronicService.java index 84beb5871d..c3e403f723 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/service/RileyLinkMedtronicService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/service/RileyLinkMedtronicService.java @@ -64,11 +64,6 @@ public class RileyLinkMedtronicService extends RileyLinkService { } - public static MedtronicCommunicationManager getCommunicationManager() { - return instance.medtronicCommunicationManager; - } - - @Override public void onConfigurationChanged(Configuration newConfig) { aapsLogger.warn(LTag.PUMPCOMM, "onConfigurationChanged"); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicUtil.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicUtil.java index 2ac9d4ef56..7f02a42d21 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicUtil.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicUtil.java @@ -1,13 +1,10 @@ package info.nightscout.androidaps.plugins.pump.medtronic.util; -import android.content.Context; - import com.google.gson.Gson; import com.google.gson.GsonBuilder; import org.joda.time.LocalTime; import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -15,8 +12,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.logging.StacktraceLoggerWrapper; @@ -41,7 +36,6 @@ import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpDeviceState; import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus; import info.nightscout.androidaps.plugins.pump.medtronic.events.EventMedtronicDeviceStatusChange; import info.nightscout.androidaps.plugins.pump.medtronic.service.RileyLinkMedtronicService; -import info.nightscout.androidaps.utils.OKDialog; import info.nightscout.androidaps.utils.resources.ResourceHelper; /** @@ -532,9 +526,4 @@ public class MedtronicUtil extends RileyLinkUtil { } - public static void displayNotConfiguredDialog(Context context) { - OKDialog.show(context, MainApp.gs(R.string.combo_warning), - MainApp.gs(R.string.medtronic_error_operation_not_possible_no_configuration), null); - } - } 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 6615e47e22..9704d82f43 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 @@ -54,6 +54,7 @@ import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult; +import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin; import info.nightscout.androidaps.plugins.pump.medtronic.data.MedtronicHistoryData; import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; import info.nightscout.androidaps.utils.DateUtil; @@ -571,7 +572,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface // return true if new record is created @Override public boolean addToHistoryTreatment(DetailedBolusInfo detailedBolusInfo, boolean allowUpdate) { - boolean medtronicPump = MedtronicUtil.isMedtronicPump(); + boolean medtronicPump = activePlugin.getActivePump() instanceof MedtronicPumpPlugin; getAapsLogger().debug(MedtronicHistoryData.doubleBolusDebug, LTag.DATATREATMENTS, "DoubleBolusDebug: addToHistoryTreatment::isMedtronicPump={} " + medtronicPump); diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/AutoStartReceiver.java b/app/src/main/java/info/nightscout/androidaps/receivers/AutoStartReceiver.java deleted file mode 100644 index 8abfb22953..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/receivers/AutoStartReceiver.java +++ /dev/null @@ -1,21 +0,0 @@ -package info.nightscout.androidaps.receivers; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.os.Build; - -import info.nightscout.androidaps.plugins.general.persistentNotification.DummyService; - -public class AutoStartReceiver extends BroadcastReceiver { - public AutoStartReceiver() { - } - - @Override - public void onReceive(Context context, Intent intent) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) - context.startForegroundService(new Intent(context, DummyService.class)); - else - context.startService(new Intent(context, DummyService.class)); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/AutoStartReceiver.kt b/app/src/main/java/info/nightscout/androidaps/receivers/AutoStartReceiver.kt new file mode 100644 index 0000000000..7b2eb03f96 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/receivers/AutoStartReceiver.kt @@ -0,0 +1,18 @@ +package info.nightscout.androidaps.receivers + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.os.Build +import info.nightscout.androidaps.plugins.general.persistentNotification.DummyService + +class AutoStartReceiver : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + if (intent.action == Intent.ACTION_BOOT_COMPLETED) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + context.startForegroundService(Intent(context, DummyService::class.java)) + else + context.startService(Intent(context, DummyService::class.java)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/BTReceiver.kt b/app/src/main/java/info/nightscout/androidaps/receivers/BTReceiver.kt new file mode 100644 index 0000000000..a3b54e924e --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/receivers/BTReceiver.kt @@ -0,0 +1,25 @@ +package info.nightscout.androidaps.receivers + +import android.bluetooth.BluetoothDevice +import android.content.Context +import android.content.Intent +import dagger.android.DaggerBroadcastReceiver +import info.nightscout.androidaps.events.EventBTChange +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import javax.inject.Inject + +class BTReceiver : DaggerBroadcastReceiver() { + @Inject lateinit var rxBus: RxBusWrapper + + override fun onReceive(context: Context, intent: Intent) { + super.onReceive(context, intent) + val device : BluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) + + when (intent.action) { + BluetoothDevice.ACTION_ACL_CONNECTED -> + rxBus.send(EventBTChange(EventBTChange.Change.CONNECT, device.name)) + BluetoothDevice.ACTION_ACL_DISCONNECTED -> + rxBus.send(EventBTChange(EventBTChange.Change.DISCONNECT, device.name)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/ChargingStateReceiver.java b/app/src/main/java/info/nightscout/androidaps/receivers/ChargingStateReceiver.java deleted file mode 100644 index bf8e199c58..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/receivers/ChargingStateReceiver.java +++ /dev/null @@ -1,45 +0,0 @@ -package info.nightscout.androidaps.receivers; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.os.BatteryManager; - -import info.nightscout.androidaps.events.EventChargingState; -import info.nightscout.androidaps.plugins.bus.RxBus; - -public class ChargingStateReceiver extends BroadcastReceiver { - - private static EventChargingState lastEvent; - - @Override - public void onReceive(Context context, Intent intent) { - EventChargingState event = grabChargingState(context); - - if (event != null) - RxBus.Companion.getINSTANCE().send(event); - lastEvent = event; - } - - public static EventChargingState grabChargingState(Context context) { - BatteryManager bm = (BatteryManager) context.getSystemService(Context.BATTERY_SERVICE); - - if (bm == null) - return new EventChargingState(false); - - int status = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_STATUS); - boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING - || status == BatteryManager.BATTERY_STATUS_FULL; - - EventChargingState event = new EventChargingState(isCharging); - return event; - } - - static public boolean isCharging() { - return lastEvent != null && lastEvent.isCharging(); - } - - static public EventChargingState getLastEvent() { - return lastEvent; - } -} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/ChargingStateReceiver.kt b/app/src/main/java/info/nightscout/androidaps/receivers/ChargingStateReceiver.kt new file mode 100644 index 0000000000..f6456fc749 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/receivers/ChargingStateReceiver.kt @@ -0,0 +1,34 @@ +package info.nightscout.androidaps.receivers + +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.os.BatteryManager +import dagger.android.DaggerBroadcastReceiver +import info.nightscout.androidaps.events.EventChargingState +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import javax.inject.Inject + +class ChargingStateReceiver : DaggerBroadcastReceiver() { + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var rxBus: RxBusWrapper + @Inject lateinit var receiverStatusStore: ReceiverStatusStore + + override fun onReceive(context: Context, intent: Intent) { + super.onReceive(context, intent) + rxBus.send(grabChargingState(context)) + aapsLogger.debug(LTag.CORE, receiverStatusStore.lastChargingEvent!!.toString()) + } + + private fun grabChargingState(context: Context): EventChargingState { + val batteryStatus: Intent? = IntentFilter(Intent.ACTION_BATTERY_CHANGED).let { iFilter -> + context.registerReceiver(null, iFilter) + } + val status: Int = batteryStatus?.getIntExtra(BatteryManager.EXTRA_STATUS, -1) ?: -1 + val isCharging: Boolean = status == BatteryManager.BATTERY_STATUS_CHARGING + || status == BatteryManager.BATTERY_STATUS_FULL + return EventChargingState(isCharging).also { receiverStatusStore.lastChargingEvent = it } + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/DataReceiver.java b/app/src/main/java/info/nightscout/androidaps/receivers/DataReceiver.java deleted file mode 100644 index 48b5d479f7..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/receivers/DataReceiver.java +++ /dev/null @@ -1,25 +0,0 @@ -package info.nightscout.androidaps.receivers; - -import android.content.Context; -import android.content.Intent; -import androidx.legacy.content.WakefulBroadcastReceiver; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.logging.StacktraceLoggerWrapper; -import info.nightscout.androidaps.services.DataService; - -public class DataReceiver extends WakefulBroadcastReceiver { - private static Logger log = StacktraceLoggerWrapper.getLogger(L.DATASERVICE); - - @Override - public void onReceive(Context context, Intent intent) { - if (L.isEnabled(L.DATASERVICE)) - log.debug("onReceive " + intent); - startWakefulService(context, new Intent(context, DataService.class) - .setAction(intent.getAction()) - .putExtras(intent)); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/DataReceiver.kt b/app/src/main/java/info/nightscout/androidaps/receivers/DataReceiver.kt new file mode 100644 index 0000000000..b5fd362e57 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/receivers/DataReceiver.kt @@ -0,0 +1,24 @@ +package info.nightscout.androidaps.receivers + +import android.content.Context +import android.content.Intent +import androidx.legacy.content.* +import dagger.android.AndroidInjection +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.services.DataService +import javax.inject.Inject + +// We are not ready to switch to JobScheduler +@Suppress("DEPRECATION") +open class DataReceiver : WakefulBroadcastReceiver() { + @Inject lateinit var aapsLogger: AAPSLogger + + override fun onReceive(context: Context, intent: Intent) { + AndroidInjection.inject(this, context) + aapsLogger.debug(LTag.DATASERVICE, "onReceive $intent") + startWakefulService(context, Intent(context, DataService::class.java) + .setAction(intent.action) + .putExtras(intent)) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.kt b/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.kt index a7d2e4e145..521537e2d4 100644 --- a/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.kt +++ b/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.kt @@ -98,13 +98,12 @@ class KeepAliveReceiver : DaggerBroadcastReceiver() { // if there is no BG available, we have to upload anyway to have correct // IOB displayed in NS private fun checkAPS() { - val usedAPS = activePlugin.activeAPS var shouldUploadStatus = false if (Config.NSCLIENT) return if (Config.PUMPCONTROL) shouldUploadStatus = true - if (!loopPlugin.isEnabled() || iobCobCalculatorPlugin.actualBg() == null) + else if (!loopPlugin.isEnabled() || iobCobCalculatorPlugin.actualBg() == null) shouldUploadStatus = true - else if (DateUtil.isOlderThan(usedAPS.lastAPSRun, 5)) shouldUploadStatus = true + else if (DateUtil.isOlderThan(activePlugin.activeAPS.lastAPSRun, 5)) shouldUploadStatus = true if (DateUtil.isOlderThan(lastIobUpload, IOB_UPDATE_FREQUENCY) && shouldUploadStatus) { lastIobUpload = DateUtil.now() NSUpload.uploadDeviceStatus(loopPlugin, iobCobCalculatorPlugin, profileFunction, activePlugin.activePump) diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/NetworkChangeReceiver.java b/app/src/main/java/info/nightscout/androidaps/receivers/NetworkChangeReceiver.java deleted file mode 100644 index b5931ccc9a..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/receivers/NetworkChangeReceiver.java +++ /dev/null @@ -1,83 +0,0 @@ -package info.nightscout.androidaps.receivers; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.net.wifi.SupplicantState; -import android.net.wifi.WifiInfo; -import android.net.wifi.WifiManager; -import androidx.annotation.Nullable; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.events.EventNetworkChange; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.logging.StacktraceLoggerWrapper; -import info.nightscout.androidaps.plugins.bus.RxBus; - -public class NetworkChangeReceiver extends BroadcastReceiver { - - private static Logger log = StacktraceLoggerWrapper.getLogger(L.CORE); - - private static EventNetworkChange lastEvent = null; - - @Override - public void onReceive(final Context context, final Intent intent) { - EventNetworkChange event = grabNetworkStatus(context); - if (event != null) - RxBus.Companion.getINSTANCE().send(event); - } - - @Nullable - public static EventNetworkChange grabNetworkStatus(final Context context) { - EventNetworkChange event = new EventNetworkChange(); - - ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - if (cm == null) return null; - NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); - - if (activeNetwork != null) { - if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI && activeNetwork.isConnected()) { - event.setWifiConnected(true); - WifiManager wifiManager = (WifiManager) MainApp.instance().getApplicationContext().getSystemService(Context.WIFI_SERVICE); - if (wifiManager != null) { - WifiInfo wifiInfo = wifiManager.getConnectionInfo(); - if (wifiInfo.getSupplicantState() == SupplicantState.COMPLETED) { - event.setSsid(wifiInfo.getSSID()); - } - if (L.isEnabled(L.CORE)) - log.debug("NETCHANGE: Wifi connected. SSID: " + event.connectedSsid()); - } - } - - if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) { - event.setMobileConnected(true); - event.setRoaming(activeNetwork.isRoaming()); - if (L.isEnabled(L.CORE)) - log.debug("NETCHANGE: Mobile connected. Roaming: " + event.getRoaming()); - } - } else { - if (L.isEnabled(L.CORE)) - log.debug("NETCHANGE: Disconnected."); - } - - lastEvent = event; - return event; - } - - public static boolean isWifiConnected() { - return lastEvent != null && lastEvent.getWifiConnected(); - } - - public static boolean isConnected() { - return lastEvent != null && (lastEvent.getWifiConnected() || lastEvent.getMobileConnected()); - } - - public static EventNetworkChange getLastEvent() { - return lastEvent; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/NetworkChangeReceiver.kt b/app/src/main/java/info/nightscout/androidaps/receivers/NetworkChangeReceiver.kt new file mode 100644 index 0000000000..52a8894079 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/receivers/NetworkChangeReceiver.kt @@ -0,0 +1,59 @@ +package info.nightscout.androidaps.receivers + +import android.content.Context +import android.content.Intent +import android.net.ConnectivityManager +import android.net.Network +import android.net.NetworkCapabilities +import android.net.wifi.SupplicantState +import android.net.wifi.WifiManager +import dagger.android.DaggerBroadcastReceiver +import info.nightscout.androidaps.events.EventNetworkChange +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import javax.inject.Inject + +class NetworkChangeReceiver : DaggerBroadcastReceiver() { + @Inject lateinit var rxBus: RxBusWrapper + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var receiverStatusStore: ReceiverStatusStore + + override fun onReceive(context: Context, intent: Intent) { + super.onReceive(context, intent) + rxBus.send(grabNetworkStatus(context, aapsLogger)) + } + + fun grabNetworkStatus(context: Context, aapsLogger: AAPSLogger): EventNetworkChange { + val event = EventNetworkChange() + val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + val networks: Array = cm.allNetworks + networks.forEach { + val capabilities = cm.getNetworkCapabilities(it) + event.wifiConnected = event.wifiConnected || (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) + || capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) + event.mobileConnected = event.mobileConnected || capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) + event.vpnConnected = event.vpnConnected || capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN) + // if (event.vpnConnected) aapsLogger.debug(LTag.CORE, "NETCHANGE: VPN connected.") + if (event.wifiConnected) { + val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager + val wifiInfo = wifiManager.connectionInfo + if (wifiInfo.supplicantState == SupplicantState.COMPLETED) { + event.ssid = wifiInfo.ssid + // aapsLogger.debug(LTag.CORE, "NETCHANGE: Wifi connected. SSID: ${event.connectedSsid()}") + } + } + if (event.mobileConnected) { + event.mobileConnected = true + event.roaming = event.roaming || !capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING) + event.metered = event.metered || !capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) + aapsLogger.debug(LTag.CORE, "NETCHANGE: Mobile connected. Roaming: ${event.roaming} Metered: ${event.metered}") + } + // aapsLogger.info(LTag.CORE, "Network: $it") + } + + aapsLogger.debug(LTag.CORE, event.toString()) + receiverStatusStore.lastNetworkEvent = event + return event + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/ReceiverStatusStore.kt b/app/src/main/java/info/nightscout/androidaps/receivers/ReceiverStatusStore.kt new file mode 100644 index 0000000000..a55b95f650 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/receivers/ReceiverStatusStore.kt @@ -0,0 +1,34 @@ +package info.nightscout.androidaps.receivers + +import android.content.Context +import android.content.Intent +import info.nightscout.androidaps.events.EventChargingState +import info.nightscout.androidaps.events.EventNetworkChange +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ReceiverStatusStore @Inject constructor(val context: Context, val rxBus: RxBusWrapper) { + + var lastNetworkEvent: EventNetworkChange? = null + + val isWifiConnected: Boolean + get() = lastNetworkEvent?.wifiConnected ?: false + + val isConnected: Boolean + get() = lastNetworkEvent?.wifiConnected ?: false || lastNetworkEvent?.mobileConnected ?: false + + fun updateNetworkStatus() { + context.sendBroadcast(Intent(context, NetworkChangeReceiver::class.java)) + } + + var lastChargingEvent: EventChargingState? = null + + val isCharging: Boolean + get() = lastChargingEvent?.isCharging ?: false + + fun broadcastChargingState() { + lastChargingEvent?.let { rxBus.send(it) } + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/SmsReceiver.java b/app/src/main/java/info/nightscout/androidaps/receivers/SmsReceiver.kt similarity index 70% rename from app/src/main/java/info/nightscout/androidaps/receivers/SmsReceiver.java rename to app/src/main/java/info/nightscout/androidaps/receivers/SmsReceiver.kt index f527375349..ed5b1201cf 100644 --- a/app/src/main/java/info/nightscout/androidaps/receivers/SmsReceiver.java +++ b/app/src/main/java/info/nightscout/androidaps/receivers/SmsReceiver.kt @@ -1,7 +1,7 @@ -package info.nightscout.androidaps.receivers; +package info.nightscout.androidaps.receivers /** * Forward received SMS intents. This is a separate class, because unlike local broadcasts handled by DataReceiver, * receiving SMS requires a special permission in the manifest, which necessitates a separate receiver. */ -public class SmsReceiver extends DataReceiver {} +class SmsReceiver : DataReceiver() \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/TimeDateOrTZChangeReceiver.kt b/app/src/main/java/info/nightscout/androidaps/receivers/TimeDateOrTZChangeReceiver.kt index 14ace4c893..3aa1c2122d 100644 --- a/app/src/main/java/info/nightscout/androidaps/receivers/TimeDateOrTZChangeReceiver.kt +++ b/app/src/main/java/info/nightscout/androidaps/receivers/TimeDateOrTZChangeReceiver.kt @@ -1,9 +1,7 @@ package info.nightscout.androidaps.receivers -import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import android.content.IntentFilter import dagger.android.DaggerBroadcastReceiver import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.PumpInterface @@ -18,19 +16,11 @@ class TimeDateOrTZChangeReceiver : DaggerBroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { super.onReceive(context, intent) val action = intent.action - val activePump: PumpInterface = activePlugin.getActivePump() + val activePump: PumpInterface = activePlugin.activePump aapsLogger.debug(LTag.PUMP, "Date, Time and/or TimeZone changed.") if (action != null) { aapsLogger.debug(LTag.PUMP, "Date, Time and/or TimeZone changed. Notifying pump driver.") activePump.timeDateOrTimeZoneChanged() } } - - fun registerBroadcasts(context: Context) { - val filter = IntentFilter() - filter.addAction(Intent.ACTION_TIME_CHANGED) - filter.addAction(Intent.ACTION_DATE_CHANGED) - filter.addAction(Intent.ACTION_TIMEZONE_CHANGED) - context.registerReceiver(this, filter) - } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.kt b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.kt index 940635d555..3912b48433 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.kt +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.kt @@ -1,12 +1,12 @@ package info.nightscout.androidaps.setupwizard import android.Manifest +import android.content.Context import android.content.Intent import androidx.appcompat.app.AppCompatActivity import dagger.android.HasAndroidInjector import info.nightscout.androidaps.Config import info.nightscout.androidaps.Constants -import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.PreferencesActivity import info.nightscout.androidaps.dialogs.ProfileSwitchDialog @@ -46,7 +46,7 @@ import javax.inject.Singleton class SWDefinition @Inject constructor( injector: HasAndroidInjector, private val rxBus: RxBusWrapper, - private val mainApp: MainApp, + private val context: Context, resourceHelper: ResourceHelper, private val sp: SP, private val profileFunction: ProfileFunction, @@ -58,10 +58,12 @@ class SWDefinition @Inject constructor( private val loopPlugin: LoopPlugin, private val nsClientPlugin: NSClientPlugin, private val nsProfilePlugin: NSProfilePlugin, - private val protectionCheck: ProtectionCheck + private val protectionCheck: ProtectionCheck, + private val importExportPrefs: ImportExportPrefs, + private val androidPermission: AndroidPermission ) { - var activity: AppCompatActivity? = null + lateinit var activity: AppCompatActivity private val screens: MutableList = ArrayList() fun getScreens(): List { @@ -83,7 +85,7 @@ class SWDefinition @Inject constructor( .preferenceId(R.string.key_language).label(R.string.language) .comment(R.string.setupwizard_language_prompt)) .validator(SWValidator { - update(mainApp) + update(context) sp.contains(R.string.key_language) }) private val screenEula = SWScreen(injector, R.string.end_user_license_agreement) @@ -127,10 +129,10 @@ class SWDefinition @Inject constructor( .add(SWBreak(injector)) .add(SWButton(injector) .text(R.string.askforpermission) - .visibility(SWValidator { AndroidPermission.permissionNotGranted(activity, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) }) - .action(Runnable { AndroidPermission.askForPermission(activity, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, AndroidPermission.CASE_BATTERY) })) - .visibility(SWValidator { AndroidPermission.permissionNotGranted(activity, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) }) - .validator(SWValidator { !AndroidPermission.permissionNotGranted(activity, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) }) + .visibility(SWValidator { androidPermission.permissionNotGranted(context, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) }) + .action(Runnable { androidPermission.askForPermission(activity, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, AndroidPermission.CASE_BATTERY) })) + .visibility(SWValidator { androidPermission.permissionNotGranted(activity, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) }) + .validator(SWValidator { !androidPermission.permissionNotGranted(activity, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) }) private val screenPermissionBt = SWScreen(injector, R.string.permission) .skippable(false) .add(SWInfotext(injector) @@ -138,10 +140,10 @@ class SWDefinition @Inject constructor( .add(SWBreak(injector)) .add(SWButton(injector) .text(R.string.askforpermission) - .visibility(SWValidator { AndroidPermission.permissionNotGranted(activity, Manifest.permission.ACCESS_FINE_LOCATION) }) - .action(Runnable { AndroidPermission.askForPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION, AndroidPermission.CASE_LOCATION) })) - .visibility(SWValidator { AndroidPermission.permissionNotGranted(activity, Manifest.permission.ACCESS_FINE_LOCATION) }) - .validator(SWValidator { !AndroidPermission.permissionNotGranted(activity, Manifest.permission.ACCESS_FINE_LOCATION) }) + .visibility(SWValidator { androidPermission.permissionNotGranted(activity, Manifest.permission.ACCESS_FINE_LOCATION) }) + .action(Runnable { androidPermission.askForPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION, AndroidPermission.CASE_LOCATION) })) + .visibility(SWValidator { androidPermission.permissionNotGranted(activity, Manifest.permission.ACCESS_FINE_LOCATION) }) + .validator(SWValidator { !androidPermission.permissionNotGranted(activity, Manifest.permission.ACCESS_FINE_LOCATION) }) private val screenPermissionStore = SWScreen(injector, R.string.permission) .skippable(false) .add(SWInfotext(injector) @@ -149,18 +151,18 @@ class SWDefinition @Inject constructor( .add(SWBreak(injector)) .add(SWButton(injector) .text(R.string.askforpermission) - .visibility(SWValidator { AndroidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) }) - .action(Runnable { AndroidPermission.askForPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE, AndroidPermission.CASE_STORAGE) })) - .visibility(SWValidator { AndroidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) }) - .validator(SWValidator { !AndroidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) }) + .visibility(SWValidator { androidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) }) + .action(Runnable { androidPermission.askForPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE, AndroidPermission.CASE_STORAGE) })) + .visibility(SWValidator { androidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) }) + .validator(SWValidator { !androidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) }) private val screenImport = SWScreen(injector, R.string.nav_import) .add(SWInfotext(injector) .label(R.string.storedsettingsfound)) .add(SWBreak(injector)) .add(SWButton(injector) .text(R.string.nav_import) - .action(Runnable { ImportExportPrefs.importSharedPreferences(activity) })) - .visibility(SWValidator { ImportExportPrefs.file.exists() && !AndroidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) }) + .action(Runnable { importExportPrefs.importSharedPreferences(activity) })) + .visibility(SWValidator { importExportPrefs.prefsFileExists() && !androidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) }) private val screenNsClient = SWScreen(injector, R.string.nsclientinternal_title) .skippable(true) .add(SWInfotext(injector) @@ -219,13 +221,11 @@ class SWDefinition @Inject constructor( .text(R.string.insulinsourcesetup) .action(Runnable { val plugin = activePlugin.activeInsulin as PluginBase - activity?.let { activity -> - protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, Runnable { - val i = Intent(activity, PreferencesActivity::class.java) - i.putExtra("id", plugin.preferencesId) - activity.startActivity(i) - }, null) - } + protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, Runnable { + val i = Intent(activity, PreferencesActivity::class.java) + i.putExtra("id", plugin.preferencesId) + activity.startActivity(i) + }, null) }) .visibility(SWValidator { (activePlugin.activeInsulin as PluginBase).preferencesId > 0 })) private val screenBgSource = SWScreen(injector, R.string.configbuilder_bgsource) @@ -238,13 +238,11 @@ class SWDefinition @Inject constructor( .text(R.string.bgsourcesetup) .action(Runnable { val plugin = activePlugin.activeBgSource as PluginBase - activity?.let { activity -> - protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, Runnable { - val i = Intent(activity, PreferencesActivity::class.java) - i.putExtra("id", plugin.preferencesId) - activity.startActivity(i) - }, null) - } + protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, Runnable { + val i = Intent(activity, PreferencesActivity::class.java) + i.putExtra("id", plugin.preferencesId) + activity.startActivity(i) + }, null) }) .visibility(SWValidator { (activePlugin.activeBgSource as PluginBase).preferencesId > 0 })) private val screenProfile = SWScreen(injector, R.string.configbuilder_profile) @@ -275,7 +273,7 @@ class SWDefinition @Inject constructor( .label(R.string.profileswitch_ismissing)) .add(SWButton(injector) .text(R.string.doprofileswitch) - .action(Runnable { ProfileSwitchDialog().show(activity!!.supportFragmentManager, "SetupWizard") })) + .action(Runnable { ProfileSwitchDialog().show(activity.supportFragmentManager, "SetupWizard") })) .validator(SWValidator { profileFunction.getProfile() != null }) .visibility(SWValidator { profileFunction.getProfile() == null }) private val screenPump = SWScreen(injector, R.string.configbuilder_pump) @@ -288,13 +286,11 @@ class SWDefinition @Inject constructor( .text(R.string.pumpsetup) .action(Runnable { val plugin = activePlugin.activePump as PluginBase - activity?.let { activity -> - protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, Runnable { - val i = Intent(activity, PreferencesActivity::class.java) - i.putExtra("id", plugin.preferencesId) - activity.startActivity(i) - }, null) - } + protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, Runnable { + val i = Intent(activity, PreferencesActivity::class.java) + i.putExtra("id", plugin.preferencesId) + activity.startActivity(i) + }, null) }) .visibility(SWValidator { (activePlugin.activePump as PluginBase).preferencesId > 0 })) .add(SWButton(injector) @@ -317,13 +313,11 @@ class SWDefinition @Inject constructor( .text(R.string.apssetup) .action(Runnable { val plugin = activePlugin.activeAPS as PluginBase - activity?.let { activity -> - protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, Runnable { - val i = Intent(activity, PreferencesActivity::class.java) - i.putExtra("id", plugin.preferencesId) - activity.startActivity(i) - }, null) - } + protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, Runnable { + val i = Intent(activity, PreferencesActivity::class.java) + i.putExtra("id", plugin.preferencesId) + activity.startActivity(i) + }, null) }) .visibility(SWValidator { (activePlugin.activeAPS as PluginBase).preferencesId > 0 })) .visibility(SWValidator { Config.APS }) @@ -367,13 +361,11 @@ class SWDefinition @Inject constructor( .text(R.string.sensitivitysetup) .action(Runnable { val plugin = activePlugin.activeSensitivity as PluginBase - activity?.let { activity -> - protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, Runnable { - val i = Intent(activity, PreferencesActivity::class.java) - i.putExtra("id", plugin.preferencesId) - activity.startActivity(i) - }, null) - } + protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, Runnable { + val i = Intent(activity, PreferencesActivity::class.java) + i.putExtra("id", plugin.preferencesId) + activity.startActivity(i) + }, null) }) .visibility(SWValidator { (activePlugin.activeSensitivity as PluginBase).preferencesId > 0 })) private val getScreenObjectives = SWScreen(injector, R.string.objectives) diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWFragment.kt b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWFragment.kt index 72ac956251..d90890f068 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWFragment.kt @@ -5,7 +5,7 @@ import androidx.fragment.app.Fragment import dagger.android.HasAndroidInjector import info.nightscout.androidaps.setupwizard.SWDefinition -class SWFragment(injecto:HasAndroidInjector, private var definition: SWDefinition) : SWItem(injecto, Type.FRAGMENT) { +class SWFragment(injector:HasAndroidInjector, private var definition: SWDefinition) : SWItem(injector, Type.FRAGMENT) { lateinit var fragment: Fragment fun add(fragment: Fragment): SWFragment { @@ -14,6 +14,6 @@ class SWFragment(injecto:HasAndroidInjector, private var definition: SWDefinitio } override fun generateDialog(layout: LinearLayout) { - definition.activity?.supportFragmentManager?.beginTransaction()?.add(layout.id, fragment, fragment.tag)?.commit() + definition.activity.supportFragmentManager.beginTransaction().add(layout.id, fragment, fragment.tag).commit() } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.java b/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.java deleted file mode 100644 index 2770e26ed3..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.java +++ /dev/null @@ -1,155 +0,0 @@ -package info.nightscout.androidaps.utils; - -import android.Manifest; -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.ActivityNotFoundException; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.net.Uri; -import android.os.Build; -import android.os.PowerManager; -import android.provider.Settings; - -import androidx.core.app.ActivityCompat; -import androidx.core.content.ContextCompat; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.plugins.bus.RxBus; -import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; -import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; -import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; -import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationWithAction; -import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin; - -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 final int CASE_PHONE_STATE = 0x5; - public static final int CASE_SYSTEM_WINDOW = 0x6; - - private static boolean permission_battery_optimization_failed = false; - - @SuppressLint("BatteryLife") - private static void askForPermission(Activity activity, String[] permission, Integer requestCode) { - boolean test = false; - boolean testBattery = false; - for (String s : permission) { - test = test || (ContextCompat.checkSelfPermission(activity, s) != PackageManager.PERMISSION_GRANTED); - if (s.equals(Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)) { - PowerManager powerManager = (PowerManager) activity.getSystemService(Context.POWER_SERVICE); - String packageName = activity.getPackageName(); - testBattery = testBattery || !powerManager.isIgnoringBatteryOptimizations(packageName); - } - } - if (test) { - ActivityCompat.requestPermissions(activity, permission, requestCode); - } - if (testBattery) { - try { - Intent i = new Intent(); - i.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); - i.setData(Uri.parse("package:" + activity.getPackageName())); - activity.startActivityForResult(i, CASE_BATTERY); - } catch (ActivityNotFoundException e) { - permission_battery_optimization_failed = true; - OKDialog.show(activity, MainApp.gs(R.string.permission), MainApp.gs(R.string.alert_dialog_permission_battery_optimization_failed), activity::recreate); - } - } - } - - public static void askForPermission(Activity activity, String permission, Integer requestCode) { - String[] permissions = {permission}; - askForPermission(activity, permissions, requestCode); - } - - public static boolean permissionNotGranted(Context context, String permission) { - boolean selfCheck = ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED; - if (permission.equals(Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)) { - if (!permission_battery_optimization_failed) { - PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - String packageName = context.getPackageName(); - selfCheck = selfCheck && powerManager.isIgnoringBatteryOptimizations(packageName); - } - } - return !selfCheck; - } - - public static synchronized void notifyForSMSPermissions(Activity activity, SmsCommunicatorPlugin smsCommunicatorPlugin) { - if (smsCommunicatorPlugin.isEnabled(PluginType.GENERAL)) { - if (permissionNotGranted(activity, Manifest.permission.RECEIVE_SMS)) { - NotificationWithAction notification = new NotificationWithAction(MainApp.instance(), Notification.PERMISSION_SMS, MainApp.gs(R.string.smscommunicator_missingsmspermission), Notification.URGENT); - notification.action(R.string.request, () -> AndroidPermission.askForPermission(activity, new String[]{Manifest.permission.RECEIVE_SMS, - Manifest.permission.SEND_SMS, - Manifest.permission.RECEIVE_MMS}, AndroidPermission.CASE_SMS)); - RxBus.Companion.getINSTANCE().send(new EventNewNotification(notification)); - } else - RxBus.Companion.getINSTANCE().send(new EventDismissNotification(Notification.PERMISSION_SMS)); - // Following is a bug in Android 8 - if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O) { - if (permissionNotGranted(activity, Manifest.permission.READ_PHONE_STATE)) { - NotificationWithAction notification = new NotificationWithAction(MainApp.instance(), Notification.PERMISSION_PHONESTATE, MainApp.gs(R.string.smscommunicator_missingphonestatepermission), Notification.URGENT); - notification.action(R.string.request, () -> - AndroidPermission.askForPermission(activity, new String[]{Manifest.permission.READ_PHONE_STATE}, AndroidPermission.CASE_PHONE_STATE)); - RxBus.Companion.getINSTANCE().send(new EventNewNotification(notification)); - } else - RxBus.Companion.getINSTANCE().send(new EventDismissNotification(Notification.PERMISSION_PHONESTATE)); - } - } - } - - public static synchronized void notifyForBatteryOptimizationPermission(Activity activity) { - if (permissionNotGranted(activity, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)) { - NotificationWithAction notification = new NotificationWithAction(MainApp.instance(), Notification.PERMISSION_BATTERY, String.format(MainApp.gs(R.string.needwhitelisting), MainApp.gs(R.string.app_name)), Notification.URGENT); - notification.action(R.string.request, () -> AndroidPermission.askForPermission(activity, new String[]{Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS}, AndroidPermission.CASE_BATTERY)); - RxBus.Companion.getINSTANCE().send(new EventNewNotification(notification)); - } else - RxBus.Companion.getINSTANCE().send(new EventDismissNotification(Notification.PERMISSION_BATTERY)); - } - - public static synchronized void notifyForStoragePermission(Activity activity) { - if (permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { - NotificationWithAction notification = new NotificationWithAction(MainApp.instance(), Notification.PERMISSION_STORAGE, MainApp.gs(R.string.needstoragepermission), Notification.URGENT); - notification.action(R.string.request, () -> AndroidPermission.askForPermission(activity, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.WRITE_EXTERNAL_STORAGE}, AndroidPermission.CASE_STORAGE)); - RxBus.Companion.getINSTANCE().send(new EventNewNotification(notification)); - } else - RxBus.Companion.getINSTANCE().send(new EventDismissNotification(Notification.PERMISSION_STORAGE)); - } - - public static synchronized void notifyForLocationPermissions(Activity activity) { - if (permissionNotGranted(activity, Manifest.permission.ACCESS_FINE_LOCATION)) { - NotificationWithAction notification = new NotificationWithAction(MainApp.instance(), Notification.PERMISSION_LOCATION, MainApp.gs(R.string.needlocationpermission), Notification.URGENT); - notification.action(R.string.request, () -> AndroidPermission.askForPermission(activity, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, AndroidPermission.CASE_LOCATION)); - RxBus.Companion.getINSTANCE().send(new EventNewNotification(notification)); - } else - RxBus.Companion.getINSTANCE().send(new EventDismissNotification(Notification.PERMISSION_LOCATION)); - } - - public static synchronized void notifyForSystemWindowPermissions(Activity activity) { - // Check if Android Q or higher - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { - if (!Settings.canDrawOverlays(activity)) { - NotificationWithAction notification = new NotificationWithAction(MainApp.instance(), Notification.PERMISSION_SYSTEM_WINDOW, MainApp.gs(R.string.needsystemwindowpermission), Notification.URGENT); - notification.action(R.string.request, () -> { - // Check if Android Q or higher - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { - // Show alert dialog to the user saying a separate permission is needed - // Launch the settings activity if the user prefers - Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, - Uri.parse("package:" + activity.getPackageName())); - activity.startActivity(intent); - } - }); - RxBus.Companion.getINSTANCE().send(new EventNewNotification(notification)); - } else - RxBus.Companion.getINSTANCE().send(new EventDismissNotification(Notification.PERMISSION_SYSTEM_WINDOW)); - } - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.kt b/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.kt new file mode 100644 index 0000000000..583aa2bf25 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.kt @@ -0,0 +1,163 @@ +package info.nightscout.androidaps.utils + +import android.Manifest +import android.annotation.SuppressLint +import android.app.Activity +import android.content.ActivityNotFoundException +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.net.Uri +import android.os.Build +import android.os.PowerManager +import android.provider.Settings +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.R +import info.nightscout.androidaps.interfaces.PluginType +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification +import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification +import info.nightscout.androidaps.plugins.general.overview.notifications.Notification +import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationWithAction +import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin +import info.nightscout.androidaps.utils.OKDialog.show +import info.nightscout.androidaps.utils.resources.ResourceHelper +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AndroidPermission @Inject constructor( + val resourceHelper: ResourceHelper, + val rxBus: RxBusWrapper, + val injector: HasAndroidInjector +) { + + companion object { + const val CASE_STORAGE = 0x1 + const val CASE_SMS = 0x2 + const val CASE_LOCATION = 0x3 + const val CASE_BATTERY = 0x4 + const val CASE_PHONE_STATE = 0x5 + const val CASE_SYSTEM_WINDOW = 0x6 + } + + private var permission_battery_optimization_failed = false + + @SuppressLint("BatteryLife") + private fun askForPermission(activity: Activity, permission: Array, requestCode: Int) { + var test = false + var testBattery = false + for (s in permission) { + test = test || ContextCompat.checkSelfPermission(activity, s) != PackageManager.PERMISSION_GRANTED + if (s == Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) { + val powerManager = activity.getSystemService(Context.POWER_SERVICE) as PowerManager + val packageName = activity.packageName + testBattery = testBattery || !powerManager.isIgnoringBatteryOptimizations(packageName) + } + } + if (test) { + ActivityCompat.requestPermissions(activity, permission, requestCode) + } + if (testBattery) { + try { + val i = Intent() + i.action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS + i.data = Uri.parse("package:" + activity.packageName) + activity.startActivityForResult(i, CASE_BATTERY) + } catch (e: ActivityNotFoundException) { + permission_battery_optimization_failed = true + show(activity, resourceHelper.gs(R.string.permission), resourceHelper.gs(R.string.alert_dialog_permission_battery_optimization_failed), Runnable { activity.recreate() }) + } + } + } + + fun askForPermission(activity: Activity, permission: String, requestCode: Int) { + val permissions = arrayOf(permission) + askForPermission(activity, permissions, requestCode) + } + + fun permissionNotGranted(context: Context, permission: String): Boolean { + var selfCheck = ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED + if (permission == Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) { + if (!permission_battery_optimization_failed) { + val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager + val packageName = context.packageName + selfCheck = selfCheck && powerManager.isIgnoringBatteryOptimizations(packageName) + } + } + return !selfCheck + } + + @Synchronized + fun notifyForSMSPermissions(activity: Activity, smsCommunicatorPlugin: SmsCommunicatorPlugin) { + if (smsCommunicatorPlugin.isEnabled(PluginType.GENERAL)) { + if (permissionNotGranted(activity, Manifest.permission.RECEIVE_SMS)) { + val notification = NotificationWithAction(injector, Notification.PERMISSION_SMS, resourceHelper.gs(R.string.smscommunicator_missingsmspermission), Notification.URGENT) + notification.action(R.string.request, Runnable { + askForPermission(activity, arrayOf(Manifest.permission.RECEIVE_SMS, + Manifest.permission.SEND_SMS, + Manifest.permission.RECEIVE_MMS), CASE_SMS) + }) + rxBus.send(EventNewNotification(notification)) + } else rxBus.send(EventDismissNotification(Notification.PERMISSION_SMS)) + // Following is a bug in Android 8 + if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O) { + if (permissionNotGranted(activity, Manifest.permission.READ_PHONE_STATE)) { + val notification = NotificationWithAction(injector, Notification.PERMISSION_PHONESTATE, resourceHelper.gs(R.string.smscommunicator_missingphonestatepermission), Notification.URGENT) + notification.action(R.string.request, Runnable { askForPermission(activity, arrayOf(Manifest.permission.READ_PHONE_STATE), CASE_PHONE_STATE) }) + rxBus.send(EventNewNotification(notification)) + } else rxBus.send(EventDismissNotification(Notification.PERMISSION_PHONESTATE)) + } + } + } + + @Synchronized + fun notifyForBatteryOptimizationPermission(activity: Activity) { + if (permissionNotGranted(activity, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)) { + val notification = NotificationWithAction(injector, Notification.PERMISSION_BATTERY, String.format(resourceHelper.gs(R.string.needwhitelisting), resourceHelper.gs(R.string.app_name)), Notification.URGENT) + notification.action(R.string.request, Runnable { askForPermission(activity, arrayOf(Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS), CASE_BATTERY) }) + rxBus.send(EventNewNotification(notification)) + } else rxBus.send(EventDismissNotification(Notification.PERMISSION_BATTERY)) + } + + @Synchronized fun notifyForStoragePermission(activity: Activity) { + if (permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + val notification = NotificationWithAction(injector, Notification.PERMISSION_STORAGE, resourceHelper.gs(R.string.needstoragepermission), Notification.URGENT) + notification.action(R.string.request, Runnable { + askForPermission(activity, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE), CASE_STORAGE) + }) + rxBus.send(EventNewNotification(notification)) + } else rxBus.send(EventDismissNotification(Notification.PERMISSION_STORAGE)) + } + + @Synchronized fun notifyForLocationPermissions(activity: Activity) { + if (permissionNotGranted(activity, Manifest.permission.ACCESS_FINE_LOCATION)) { + val notification = NotificationWithAction(injector, Notification.PERMISSION_LOCATION, resourceHelper.gs(R.string.needlocationpermission), Notification.URGENT) + notification.action(R.string.request, Runnable { askForPermission(activity, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), CASE_LOCATION) }) + rxBus.send(EventNewNotification(notification)) + } else rxBus.send(EventDismissNotification(Notification.PERMISSION_LOCATION)) + } + + @Synchronized fun notifyForSystemWindowPermissions(activity: Activity) { + // Check if Android Q or higher + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { + if (!Settings.canDrawOverlays(activity)) { + val notification = NotificationWithAction(injector, Notification.PERMISSION_SYSTEM_WINDOW, resourceHelper.gs(R.string.needsystemwindowpermission), Notification.URGENT) + notification.action(R.string.request, Runnable { + // Check if Android Q or higher + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { + // Show alert dialog to the user saying a separate permission is needed + // Launch the settings activity if the user prefers + val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, + Uri.parse("package:" + activity.packageName)) + activity.startActivity(intent) + } + }) + rxBus.send(EventNewNotification(notification)) + } else rxBus.send(EventDismissNotification(Notification.PERMISSION_SYSTEM_WINDOW)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/CryptoUtil.kt b/app/src/main/java/info/nightscout/androidaps/utils/CryptoUtil.kt new file mode 100644 index 0000000000..745f4449ee --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/CryptoUtil.kt @@ -0,0 +1,142 @@ +package info.nightscout.androidaps.utils + +import org.spongycastle.util.encoders.Base64 +import java.nio.ByteBuffer +import java.security.MessageDigest +import java.security.SecureRandom +import java.security.spec.KeySpec +import javax.crypto.Cipher +import javax.crypto.Mac +import javax.crypto.SecretKey +import javax.crypto.SecretKeyFactory +import javax.crypto.spec.GCMParameterSpec +import javax.crypto.spec.PBEKeySpec +import javax.crypto.spec.SecretKeySpec + +private val HEX_CHARS = "0123456789abcdef" +private val HEX_CHARS_ARRAY = "0123456789abcdef".toCharArray() + +fun String.hexStringToByteArray() : ByteArray { + + val upperCased = this.toLowerCase() + val result = ByteArray(length / 2) + for (i in 0 until length step 2) { + val firstIndex = HEX_CHARS.indexOf(upperCased[i]); + val secondIndex = HEX_CHARS.indexOf(upperCased[i + 1]); + + val octet = firstIndex.shl(4).or(secondIndex) + result.set(i.shr(1), octet.toByte()) + } + + return result +} + +fun ByteArray.toHex() : String{ + val result = StringBuffer() + + forEach { + val octet = it.toInt() + val firstIndex = (octet and 0xF0).ushr(4) + val secondIndex = octet and 0x0F + result.append(HEX_CHARS_ARRAY[firstIndex]) + result.append(HEX_CHARS_ARRAY[secondIndex]) + } + + return result.toString() +} + +object CryptoUtil { + + private const val IV_LENGTH_BYTE = 12 + private const val TAG_LENGTH_BIT = 128 + private const val AES_KEY_SIZE_BIT = 256 + private const val PBKDF2_ITERATIONS = 50000 // check delays it cause on real device + private const val SALT_SIZE_BYTE = 32 + + private val secureRandom: SecureRandom = SecureRandom() + + fun sha256(source: String): String { + val digest = MessageDigest.getInstance("SHA-256") + val hashRaw = digest.digest(source.toByteArray()) + return hashRaw.toHex() + } + + fun hmac256(str: String, secret: String): String? { + val sha256_HMAC = Mac.getInstance("HmacSHA256") + val secretKey = SecretKeySpec(secret.toByteArray(), "HmacSHA256") + sha256_HMAC.init(secretKey) + return sha256_HMAC.doFinal(str.toByteArray()).toHex() + } + + private fun prepCipherKey(passPhrase: String, salt:ByteArray, iterationCount:Int = PBKDF2_ITERATIONS, keyStrength:Int = AES_KEY_SIZE_BIT): SecretKeySpec { + val factory: SecretKeyFactory = SecretKeyFactory.getInstance("PBKDF2withHmacSHA1") + val spec: KeySpec = PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount, keyStrength) + val tmp: SecretKey = factory.generateSecret(spec) + return SecretKeySpec(tmp.getEncoded(), "AES") + } + + fun mineSalt(len :Int = SALT_SIZE_BYTE): ByteArray { + val salt = ByteArray(len) + secureRandom.nextBytes(salt) + return salt + } + + fun encrypt(passPhrase: String, salt:ByteArray, rawData: String ): String? { + val iv: ByteArray? + val encrypted: ByteArray? + return try { + iv = ByteArray(IV_LENGTH_BYTE) + secureRandom.nextBytes(iv) + val cipherEnc: Cipher = Cipher.getInstance("AES/GCM/NoPadding") + cipherEnc.init(Cipher.ENCRYPT_MODE, prepCipherKey(passPhrase, salt), GCMParameterSpec(TAG_LENGTH_BIT, iv)) + encrypted = cipherEnc.doFinal(rawData.toByteArray()) + val byteBuffer: ByteBuffer = ByteBuffer.allocate(1 + iv.size + encrypted.size) + byteBuffer.put(iv.size.toByte()) + byteBuffer.put(iv) + byteBuffer.put(encrypted) + String(Base64.encode(byteBuffer.array())) + } catch (e: Exception) { + null + } + } + + fun decrypt(passPhrase: String, salt:ByteArray, encryptedData: String): String? { + val iv: ByteArray? + val encrypted: ByteArray? + return try { + val byteBuffer = ByteBuffer.wrap(Base64.decode(encryptedData)) + val ivLength = byteBuffer.get().toInt() + iv = ByteArray(ivLength) + byteBuffer[iv] + encrypted = ByteArray(byteBuffer.remaining()) + byteBuffer[encrypted] + val cipherDec: Cipher = Cipher.getInstance("AES/GCM/NoPadding") + cipherDec.init(Cipher.DECRYPT_MODE, prepCipherKey(passPhrase, salt), GCMParameterSpec(TAG_LENGTH_BIT, iv)) + val dec = cipherDec.doFinal(encrypted) + String(dec) + } catch (e: Exception) { + null + } + } + + fun checkPassword(password: String, referenceHash: String): Boolean { + return if (referenceHash.startsWith("hmac:")) { + val hashSegments = referenceHash.split(":") + if (hashSegments.size != 3) + return false + return hmac256(password, hashSegments[1]) == hashSegments[2] + } else { + password == referenceHash + } + } + + fun hashPassword(password: String): String { + return if (!password.startsWith("hmac:")) { + val salt = mineSalt().toHex() + return "hmac:${salt}:${hmac256(password, salt)}" + } else { + password + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java b/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java index 7d2790990f..82fabc52b7 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java @@ -108,6 +108,28 @@ public class DateUtil { return retval; } + public static long toTodayTime(String hh_colon_mm) { + Pattern p = Pattern.compile("(\\d+):(\\d+)( a.m.| p.m.| AM| PM|AM|PM|)"); + Matcher m = p.matcher(hh_colon_mm); + long retval = 0; + + if (m.find()) { + int hours = SafeParse.stringToInt(m.group(1)); + int minutes = SafeParse.stringToInt(m.group(2)); + if ((m.group(3).equals(" a.m.") || m.group(3).equals(" AM") || m.group(3).equals("AM")) && m.group(1).equals("12")) + hours -= 12; + if ((m.group(3).equals(" p.m.") || m.group(3).equals(" PM") || m.group(3).equals("PM")) && !(m.group(1).equals("12"))) + hours += 12; + Calendar c = Calendar.getInstance(); + c.set(Calendar.HOUR_OF_DAY, hours); + c.set(Calendar.MINUTE, minutes); + c.set(Calendar.SECOND, 0); + c.set(Calendar.MILLISECOND, 0); + retval = c.getTimeInMillis(); + } + return retval; + } + public static String dateString(Date date) { DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT); return df.format(date); diff --git a/app/src/main/java/info/nightscout/androidaps/utils/FabricPrivacy.kt b/app/src/main/java/info/nightscout/androidaps/utils/FabricPrivacy.kt index 3ec3f1f451..19d25e008b 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/FabricPrivacy.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/FabricPrivacy.kt @@ -4,6 +4,7 @@ import android.os.Bundle import com.crashlytics.android.Crashlytics import com.google.firebase.analytics.FirebaseAnalytics 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.ActivePluginProvider @@ -132,7 +133,8 @@ class FabricPrivacy @Inject constructor( val hashes: List = signatureVerifierPlugin.shortHashes() if (hashes.isNotEmpty()) mainApp.firebaseAnalytics.setUserProperty("Hash", hashes[0]) activePlugin.activePump.let { mainApp.firebaseAnalytics.setUserProperty("Pump", it::class.java.simpleName) } - activePlugin.activeAPS.let { mainApp.firebaseAnalytics.setUserProperty("Aps", it::class.java.simpleName) } + if (!Config.NSCLIENT && !Config.PUMPCONTROL) + activePlugin.activeAPS.let { mainApp.firebaseAnalytics.setUserProperty("Aps", it::class.java.simpleName) } activePlugin.activeBgSource.let { mainApp.firebaseAnalytics.setUserProperty("BgSource", it::class.java.simpleName) } mainApp.firebaseAnalytics.setUserProperty("Profile", activePlugin.activeProfileInterface.javaClass.simpleName) activePlugin.activeSensitivity.let { mainApp.firebaseAnalytics.setUserProperty("Sensitivity", it::class.java.simpleName) } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/LocaleHelper.kt b/app/src/main/java/info/nightscout/androidaps/utils/LocaleHelper.kt index 447d076d93..62da88a15d 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/LocaleHelper.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/LocaleHelper.kt @@ -4,16 +4,19 @@ import android.content.Context import android.content.ContextWrapper import android.os.Build import android.os.LocaleList +import androidx.preference.PreferenceManager import info.nightscout.androidaps.R import java.util.* - object LocaleHelper { - fun currentLanguage(): String = - SP.getString(R.string.key_language, Locale.getDefault().language) + private fun currentLanguage(context: Context): String = + PreferenceManager.getDefaultSharedPreferences(context).getString(context.getString(R.string.key_language), "en") + ?: "en" + // injection not possible because of use in attachBaseContext + //SP.getString(R.string.key_language, Locale.getDefault().language) - private fun currentLocale(): Locale { - val language = currentLanguage() + private fun currentLocale(context: Context): Locale { + val language = currentLanguage(context) var locale = Locale(language) if (language.contains("_")) { // language with country like pt_BR defined in arrays.xml @@ -26,7 +29,7 @@ object LocaleHelper { @Suppress("DEPRECATION") fun update(context: Context) { - val locale = currentLocale() + val locale = currentLocale(context) Locale.setDefault(locale) val resources = context.resources val configuration = resources.configuration @@ -39,7 +42,7 @@ object LocaleHelper { fun wrap(ctx: Context): ContextWrapper { val res = ctx.resources val configuration = res.configuration - val newLocale = currentLocale() + val newLocale = currentLocale(ctx) val context = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { configuration.setLocale(newLocale) val localeList = LocaleList(newLocale) diff --git a/app/src/main/java/info/nightscout/androidaps/utils/MidnightTime.java b/app/src/main/java/info/nightscout/androidaps/utils/MidnightTime.java index 235bb9c6e9..501f6147e7 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/MidnightTime.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/MidnightTime.java @@ -21,6 +21,17 @@ public class MidnightTime { return c.getTimeInMillis(); } + public static long calcPlusMinutes(int minutes) { + int h = minutes / 60; + int m = minutes % 60; + Calendar c = Calendar.getInstance(); + c.set(Calendar.HOUR_OF_DAY, h); + c.set(Calendar.MINUTE, m); + c.set(Calendar.SECOND, 0); + c.set(Calendar.MILLISECOND, 0); + return c.getTimeInMillis(); + } + public static long calc(long time) { Long m; synchronized (times) { diff --git a/app/src/main/java/info/nightscout/androidaps/utils/OKDialog.kt b/app/src/main/java/info/nightscout/androidaps/utils/OKDialog.kt index 4ce0071900..186b81ae36 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/OKDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/OKDialog.kt @@ -4,17 +4,10 @@ import android.annotation.SuppressLint import android.app.Activity import android.content.Context import android.content.DialogInterface -import android.os.Handler import android.os.SystemClock import android.text.Spanned -import android.view.LayoutInflater -import android.view.View -import android.widget.ImageView -import android.widget.TextView -import androidx.appcompat.app.AlertDialog -import androidx.appcompat.view.ContextThemeWrapper -import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.R +import info.nightscout.androidaps.utils.alertDialogs.AlertDialogHelper object OKDialog { @SuppressLint("InflateParams") @@ -23,11 +16,9 @@ object OKDialog { fun show(context: Context, title: String, message: String, runnable: Runnable? = null) { var notEmptytitle = title if (notEmptytitle.isEmpty()) notEmptytitle = context.getString(R.string.message) - val titleLayout = LayoutInflater.from(context).inflate(R.layout.dialog_alert_custom, null) - (titleLayout.findViewById(R.id.alertdialog_title) as TextView).text = notEmptytitle - (titleLayout.findViewById(R.id.alertdialog_icon) as ImageView).setImageResource(R.drawable.ic_check_while_48dp) - AlertDialog.Builder(ContextThemeWrapper(context, R.style.AppTheme)) - .setCustomTitle(titleLayout) + + AlertDialogHelper.Builder(context) + .setCustomTitle(AlertDialogHelper.buildCustomTitle(context, notEmptytitle)) .setMessage(message) .setPositiveButton(context.getString(R.string.ok)) { dialog: DialogInterface, _: Int -> dialog.dismiss() @@ -38,22 +29,15 @@ object OKDialog { .setCanceledOnTouchOutside(false) } - fun runOnUiThread(theRunnable: Runnable?) { - val mainHandler = Handler(MainApp.instance().applicationContext.mainLooper) - theRunnable?.let { mainHandler.post(it) } - } - @SuppressLint("InflateParams") @JvmStatic @JvmOverloads fun show(activity: Activity, title: String, message: Spanned, runnable: Runnable? = null) { var notEmptytitle = title if (notEmptytitle.isEmpty()) notEmptytitle = activity.getString(R.string.message) - val titleLayout = activity.layoutInflater.inflate(R.layout.dialog_alert_custom, null) - (titleLayout.findViewById(R.id.alertdialog_title) as TextView).text = notEmptytitle - (titleLayout.findViewById(R.id.alertdialog_icon) as ImageView).setImageResource(R.drawable.ic_check_while_48dp) - AlertDialog.Builder(ContextThemeWrapper(activity, R.style.AppTheme)) - .setCustomTitle(titleLayout) + + AlertDialogHelper.Builder(activity) + .setCustomTitle(AlertDialogHelper.buildCustomTitle(activity, notEmptytitle)) .setMessage(message) .setPositiveButton(activity.getString(R.string.ok)) { dialog: DialogInterface, _: Int -> dialog.dismiss() @@ -78,12 +62,9 @@ object OKDialog { @JvmStatic @JvmOverloads fun showConfirmation(activity: Activity, title: String, message: Spanned, ok: Runnable?, cancel: Runnable? = null) { - val titleLayout = activity.layoutInflater.inflate(R.layout.dialog_alert_custom, null) - (titleLayout.findViewById(R.id.alertdialog_title) as TextView).text = title - (titleLayout.findViewById(R.id.alertdialog_icon) as ImageView).setImageResource(R.drawable.ic_check_while_48dp) - AlertDialog.Builder(ContextThemeWrapper(activity, R.style.AppTheme)) + AlertDialogHelper.Builder(activity) .setMessage(message) - .setCustomTitle(titleLayout) + .setCustomTitle(AlertDialogHelper.buildCustomTitle(activity, title)) .setPositiveButton(android.R.string.ok) { dialog: DialogInterface, _: Int -> dialog.dismiss() SystemClock.sleep(100) @@ -102,12 +83,9 @@ object OKDialog { @SuppressLint("InflateParams") @JvmStatic fun showConfirmation(activity: Activity, title: String, message: String, ok: Runnable?, cancel: Runnable? = null) { - val titleLayout = activity.layoutInflater.inflate(R.layout.dialog_alert_custom, null) - (titleLayout.findViewById(R.id.alertdialog_title) as TextView).text = title - (titleLayout.findViewById(R.id.alertdialog_icon) as ImageView).setImageResource(R.drawable.ic_check_while_48dp) - AlertDialog.Builder(ContextThemeWrapper(activity, R.style.AppTheme)) + AlertDialogHelper.Builder(activity) .setMessage(message) - .setCustomTitle(titleLayout) + .setCustomTitle(AlertDialogHelper.buildCustomTitle(activity, title)) .setPositiveButton(android.R.string.ok) { dialog: DialogInterface, _: Int -> dialog.dismiss() SystemClock.sleep(100) @@ -132,12 +110,9 @@ object OKDialog { @JvmStatic @JvmOverloads fun showConfirmation(context: Context, title: String, message: Spanned, ok: Runnable?, cancel: Runnable? = null) { - val titleLayout = LayoutInflater.from(context).inflate(R.layout.dialog_alert_custom, null) - (titleLayout.findViewById(R.id.alertdialog_title) as TextView).text = title - (titleLayout.findViewById(R.id.alertdialog_icon) as ImageView).setImageResource(R.drawable.ic_check_while_48dp) - AlertDialog.Builder(ContextThemeWrapper(context, R.style.AppTheme)) + AlertDialogHelper.Builder(context) .setMessage(message) - .setCustomTitle(titleLayout) + .setCustomTitle(AlertDialogHelper.buildCustomTitle(context, title)) .setPositiveButton(android.R.string.ok) { dialog: DialogInterface, _: Int -> dialog.dismiss() SystemClock.sleep(100) @@ -163,12 +138,9 @@ object OKDialog { @JvmStatic @JvmOverloads fun showConfirmation(context: Context, title: String, message: String, ok: Runnable?, cancel: Runnable? = null) { - val titleLayout = LayoutInflater.from(context).inflate(R.layout.dialog_alert_custom, null) - (titleLayout.findViewById(R.id.alertdialog_title) as TextView).text = title - (titleLayout.findViewById(R.id.alertdialog_icon) as ImageView).setImageResource(R.drawable.ic_check_while_48dp) - AlertDialog.Builder(ContextThemeWrapper(context, R.style.AppTheme)) + AlertDialogHelper.Builder(context) .setMessage(message) - .setCustomTitle(titleLayout) + .setCustomTitle(AlertDialogHelper.buildCustomTitle(context, title)) .setPositiveButton(android.R.string.ok) { dialog: DialogInterface, _: Int -> dialog.dismiss() SystemClock.sleep(100) @@ -187,12 +159,9 @@ object OKDialog { @JvmStatic @JvmOverloads fun showConfirmation(context: Context, title: String, message: String, ok: DialogInterface.OnClickListener?, cancel: DialogInterface.OnClickListener? = null) { - val titleLayout = LayoutInflater.from(context).inflate(R.layout.dialog_alert_custom, null) - (titleLayout.findViewById(R.id.alertdialog_title) as TextView).text = title - (titleLayout.findViewById(R.id.alertdialog_icon) as ImageView).setImageResource(R.drawable.ic_check_while_48dp) - AlertDialog.Builder(ContextThemeWrapper(context, R.style.AppTheme)) + AlertDialogHelper.Builder(context) .setMessage(message) - .setCustomTitle(titleLayout) + .setCustomTitle(AlertDialogHelper.buildCustomTitle(context, title)) .setPositiveButton(android.R.string.ok) { dialog: DialogInterface, which: Int -> dialog.dismiss() SystemClock.sleep(100) diff --git a/app/src/main/java/info/nightscout/androidaps/utils/ToastUtils.java b/app/src/main/java/info/nightscout/androidaps/utils/ToastUtils.java index 7822e8a80f..26062011f6 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/ToastUtils.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/ToastUtils.java @@ -1,26 +1,94 @@ package info.nightscout.androidaps.utils; +import android.annotation.SuppressLint; import android.content.Context; import android.media.MediaPlayer; import android.os.Handler; import android.os.Looper; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.DrawableRes; +import androidx.appcompat.view.ContextThemeWrapper; + import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.R; import info.nightscout.androidaps.plugins.bus.RxBusWrapper; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; public class ToastUtils { + + public static class Long { + + public static void warnToast(final Context ctx, final String string) { + graphicalToast(ctx, string, R.drawable.ic_toast_warn, false); + } + + public static void infoToast(final Context ctx, final String string) { + graphicalToast(ctx, string, R.drawable.ic_toast_info,false); + } + + public static void okToast(final Context ctx, final String string) { + graphicalToast(ctx, string, R.drawable.ic_toast_check,false); + } + + public static void errorToast(final Context ctx, final String string) { + graphicalToast(ctx, string, R.drawable.ic_toast_error,false); + } + } + public static void showToastInUiThread(final Context ctx, final int stringId) { showToastInUiThread(ctx, MainApp.gs(stringId)); } + public static void warnToast(final Context ctx, final String string) { + graphicalToast(ctx, string, R.drawable.ic_toast_warn, true); + } + + public static void infoToast(final Context ctx, final String string) { + graphicalToast(ctx, string, R.drawable.ic_toast_info, true); + } + + public static void okToast(final Context ctx, final String string) { + graphicalToast(ctx, string, R.drawable.ic_toast_check, true); + } + + public static void errorToast(final Context ctx, final String string) { + graphicalToast(ctx, string, R.drawable.ic_toast_error, true); + } + + public static void graphicalToast(final Context ctx, final String string, @DrawableRes int iconId) { + graphicalToast(ctx, string, iconId, true); + } + + @SuppressLint("InflateParams") + public static void graphicalToast(final Context ctx, final String string, @DrawableRes int iconId, boolean isShort) { + Handler mainThread = new Handler(Looper.getMainLooper()); + mainThread.post(() -> { + View toastRoot =LayoutInflater.from(new ContextThemeWrapper(ctx, R.style.AppTheme)).inflate(R.layout.toast, null); + TextView toastMessage = toastRoot.findViewById(android.R.id.message); + toastMessage.setText(string); + + ImageView toastIcon = toastRoot.findViewById(android.R.id.icon); + toastIcon.setImageResource(iconId); + + Toast toast = new Toast(ctx); + toast.setDuration(isShort ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG); + toast.setView(toastRoot); + toast.show(); + }); + } + public static void showToastInUiThread(final Context ctx, final String string) { Handler mainThread = new Handler(Looper.getMainLooper()); - mainThread.post(() -> Toast.makeText(ctx, string, Toast.LENGTH_SHORT).show()); + mainThread.post(() -> { + Toast.makeText(ctx, string, Toast.LENGTH_SHORT).show(); + }); } public static void showToastInUiThread(final Context ctx, final RxBusWrapper rxBus, diff --git a/app/src/main/java/info/nightscout/androidaps/utils/UIUtils.kt b/app/src/main/java/info/nightscout/androidaps/utils/UIUtils.kt index 99c4a4af31..ad57e519da 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/UIUtils.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/UIUtils.kt @@ -1,6 +1,8 @@ package info.nightscout.androidaps.utils +import android.os.Handler import android.view.View +import info.nightscout.androidaps.MainApp /** * Created by adrian on 2019-12-20. @@ -8,3 +10,7 @@ import android.view.View fun Boolean.toVisibility() = if (this) View.VISIBLE else View.GONE +fun runOnUiThread(theRunnable: Runnable?) { + val mainHandler = Handler(MainApp.instance().applicationContext.mainLooper) + theRunnable?.let { mainHandler.post(it) } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/AlertDialogHelper.kt b/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/AlertDialogHelper.kt new file mode 100644 index 0000000000..afc5491652 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/AlertDialogHelper.kt @@ -0,0 +1,31 @@ +package info.nightscout.androidaps.utils.alertDialogs + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.widget.ImageView +import android.widget.TextView +import androidx.annotation.DrawableRes +import androidx.annotation.LayoutRes +import androidx.annotation.StyleRes +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.view.ContextThemeWrapper +import info.nightscout.androidaps.R + +object AlertDialogHelper { + + @Suppress("FunctionName") + fun Builder(context: Context, @StyleRes themeResId: Int = R.style.AppTheme) = + AlertDialog.Builder(ContextThemeWrapper(context, themeResId)) + + fun buildCustomTitle(context: Context, title: String, + @DrawableRes iconResource: Int = R.drawable.ic_check_while_48dp, + @StyleRes themeResId: Int = R.style.AppTheme, + @LayoutRes layoutResource: Int = R.layout.dialog_alert_custom): View? { + val titleLayout = LayoutInflater.from(ContextThemeWrapper(context, themeResId)).inflate(layoutResource, null) + (titleLayout.findViewById(R.id.alertdialog_title) as TextView).text = title + (titleLayout.findViewById(R.id.alertdialog_icon) as ImageView).setImageResource(iconResource) + return titleLayout + } + +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/PrefImportSummaryDialog.kt b/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/PrefImportSummaryDialog.kt new file mode 100644 index 0000000000..d5b6900386 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/PrefImportSummaryDialog.kt @@ -0,0 +1,138 @@ +package info.nightscout.androidaps.utils.alertDialogs + +import android.annotation.SuppressLint +import android.content.Context +import android.content.DialogInterface +import android.graphics.Color +import android.os.SystemClock +import android.view.LayoutInflater +import android.view.View +import android.webkit.WebView +import android.widget.Button +import android.widget.ImageView +import android.widget.TableLayout +import android.widget.TextView +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import androidx.annotation.StyleRes +import androidx.appcompat.view.ContextThemeWrapper +import info.nightscout.androidaps.R +import info.nightscout.androidaps.plugins.general.maintenance.formats.Prefs +import info.nightscout.androidaps.plugins.general.maintenance.formats.PrefsStatus +import info.nightscout.androidaps.utils.ToastUtils +import info.nightscout.androidaps.utils.runOnUiThread +import java.util.* + +object PrefImportSummaryDialog { + + @SuppressLint("InflateParams") + fun showSummary(context: Context, importOk: Boolean, importPossible: Boolean, prefs: Prefs, ok: (() -> Unit)?, cancel: (() -> Unit)? = null) { + + @StyleRes val theme: Int = if (importOk) R.style.AppTheme else { + if (importPossible) R.style.AppThemeWarningDialog else R.style.AppThemeErrorDialog + } + + @StringRes val messageRes: Int = if (importOk) R.string.check_preferences_before_import else { + if (importPossible) R.string.check_preferences_dangerous_import else R.string.check_preferences_cannot_import + } + + @DrawableRes val headerIcon: Int = if (importOk) R.drawable.ic_header_import else { + if (importPossible) R.drawable.ic_header_warning else R.drawable.ic_header_error + } + + val themedCtx = ContextThemeWrapper(context, theme) + + val innerLayout = LayoutInflater.from(themedCtx).inflate(R.layout.dialog_alert_import_summary, null) + val table = (innerLayout.findViewById(R.id.summary_table) as TableLayout) + val detailsBtn = (innerLayout.findViewById(R.id.summary_details_btn) as Button) + + var idx = 0 + val details = LinkedList() + + + for ((metaKey, metaEntry) in prefs.metadata) { + val rowLayout = LayoutInflater.from(themedCtx).inflate(R.layout.import_summary_item, null) + val label = (rowLayout.findViewById(R.id.summary_text) as TextView) + label.setText(metaKey.formatForDisplay(context, metaEntry.value)) + (rowLayout.findViewById(R.id.summary_icon) as ImageView).setImageResource(metaKey.icon) + (rowLayout.findViewById(R.id.status_icon) as ImageView).setImageResource(metaEntry.status.icon) + + if (metaEntry.status == PrefsStatus.WARN) label.setTextColor(themedCtx.getColor(R.color.metadataTextWarning)) + else if (metaEntry.status == PrefsStatus.ERROR) label.setTextColor(themedCtx.getColor(R.color.metadataTextError)) + + if (metaEntry.info != null) { + details.add("${context.getString(metaKey.label)}: ${metaEntry.value}
${metaEntry.info}") + rowLayout.isClickable = true + rowLayout.setOnClickListener { + val msg = "[${context.getString(metaKey.label)}] ${metaEntry.info}" + when (metaEntry.status) { + PrefsStatus.WARN -> ToastUtils.Long.warnToast(context, msg) + PrefsStatus.ERROR -> ToastUtils.Long.errorToast(context, msg) + else -> ToastUtils.Long.infoToast(context, msg) + } + } + } else { + rowLayout.isClickable = true + rowLayout.setOnClickListener { ToastUtils.Long.infoToast(context, context.getString(metaKey.label)) } + } + + table.addView(rowLayout, idx++) + } + + if (details.size > 0) { + detailsBtn.visibility = View.VISIBLE + detailsBtn.setOnClickListener { + val detailsLayout = LayoutInflater.from(context).inflate(R.layout.import_summary_details, null) + val wview = detailsLayout.findViewById(R.id.details_webview) as WebView + wview.loadData("" + details.joinToString("
"), "text/html; charset=utf-8", "utf-8"); + wview.setBackgroundColor(Color.TRANSPARENT); + wview.setLayerType(WebView.LAYER_TYPE_SOFTWARE, null); + + AlertDialogHelper.Builder(context, R.style.AppTheme) + .setCustomTitle(AlertDialogHelper.buildCustomTitle( + context, + context.getString(R.string.check_preferences_details_title), + R.drawable.ic_header_log, + R.style.AppTheme)) + .setView(detailsLayout) + .setPositiveButton(android.R.string.ok) { dialogInner: DialogInterface, _: Int -> + dialogInner.dismiss() + } + .show() + } + } + + val builder = AlertDialogHelper.Builder(context, theme) + .setMessage(context.getString(messageRes)) + .setCustomTitle(AlertDialogHelper.buildCustomTitle(context, context.getString(R.string.nav_import), headerIcon, theme)) + .setView(innerLayout) + + .setNegativeButton(android.R.string.cancel) { dialog: DialogInterface, _: Int -> + dialog.dismiss() + SystemClock.sleep(100) + if (cancel != null) { + runOnUiThread(Runnable { + cancel() + }) + } + } + + if (importPossible) { + builder.setPositiveButton( + if (importOk) R.string.check_preferences_import_btn else R.string.check_preferences_import_anyway_btn + ) { dialog: DialogInterface, _: Int -> + dialog.dismiss() + SystemClock.sleep(100) + if (ok != null) { + runOnUiThread(Runnable { + ok() + }) + } + } + } + + val dialog = builder.show() + dialog.setCanceledOnTouchOutside(false) + } + +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/TwoMessagesAlertDialog.kt b/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/TwoMessagesAlertDialog.kt new file mode 100644 index 0000000000..9c1fbf37aa --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/TwoMessagesAlertDialog.kt @@ -0,0 +1,49 @@ +package info.nightscout.androidaps.utils.alertDialogs + +import android.annotation.SuppressLint +import android.content.Context +import android.content.DialogInterface +import android.os.SystemClock +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import androidx.annotation.DrawableRes +import info.nightscout.androidaps.R +import info.nightscout.androidaps.utils.runOnUiThread + +object TwoMessagesAlertDialog { + + @SuppressLint("InflateParams") + fun showAlert(context: Context, title: String, message: String, secondMessage: String, ok: (() -> Unit)?, cancel: (() -> Unit)? = null, @DrawableRes icon: Int? = null) { + + val secondMessageLayout = LayoutInflater.from(context).inflate(R.layout.dialog_alert_two_messages, null) + (secondMessageLayout.findViewById(R.id.password_prompt_title) as TextView).text = secondMessage + + val dialog = AlertDialogHelper.Builder(context) + .setMessage(message) + .setCustomTitle(AlertDialogHelper.buildCustomTitle(context, title, icon + ?: R.drawable.ic_check_while_48dp)) + .setView(secondMessageLayout) + .setPositiveButton(android.R.string.ok) { dialog: DialogInterface, _: Int -> + dialog.dismiss() + SystemClock.sleep(100) + if (ok != null) { + runOnUiThread(Runnable { + ok() + }) + } + } + .setNegativeButton(android.R.string.cancel) { dialog: DialogInterface, _: Int -> + dialog.dismiss() + SystemClock.sleep(100) + if (cancel != null) { + runOnUiThread(Runnable { + cancel() + }) + } + } + .show() + dialog.setCanceledOnTouchOutside(false) + } + +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/WarningDialog.kt b/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/WarningDialog.kt new file mode 100644 index 0000000000..cd0654f3bf --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/WarningDialog.kt @@ -0,0 +1,49 @@ +package info.nightscout.androidaps.utils.alertDialogs + +import android.annotation.SuppressLint +import android.content.Context +import android.content.DialogInterface +import android.os.SystemClock +import androidx.annotation.StringRes +import info.nightscout.androidaps.R +import info.nightscout.androidaps.utils.runOnUiThread + +// if you need error dialog - duplicate to ErrorDialog and make it and use: AppThemeErrorDialog & R.drawable.ic_header_error instead + +object WarningDialog { + + @SuppressLint("InflateParams") + @JvmStatic + @JvmOverloads + fun showWarning(context: Context, title: String, message: String, @StringRes positiveButton: Int = -1, ok: (() -> Unit)? = null, cancel: (() -> Unit)? = null) { + + val builder = AlertDialogHelper.Builder(context, R.style.AppThemeWarningDialog) + .setMessage(message) + .setCustomTitle(AlertDialogHelper.buildCustomTitle(context, title, R.drawable.ic_header_warning, R.style.AppThemeWarningDialog)) + .setNegativeButton(R.string.dismiss) { dialog: DialogInterface, _: Int -> + dialog.dismiss() + SystemClock.sleep(100) + if (cancel != null) { + runOnUiThread(Runnable { + cancel() + }) + } + } + + if (positiveButton != -1) { + builder.setPositiveButton(positiveButton) { dialog: DialogInterface, _: Int -> + dialog.dismiss() + SystemClock.sleep(100) + if (ok != null) { + runOnUiThread(Runnable { + ok() + }) + } + } + } + + val dialog = builder.show() + dialog.setCanceledOnTouchOutside(true) + } + +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/protection/PasswordCheck.kt b/app/src/main/java/info/nightscout/androidaps/utils/protection/PasswordCheck.kt index 6a2e18c732..1907ac01b5 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/protection/PasswordCheck.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/protection/PasswordCheck.kt @@ -1,51 +1,104 @@ package info.nightscout.androidaps.utils.protection import android.annotation.SuppressLint -import android.app.AlertDialog +import android.content.Context +import android.os.Build import android.view.LayoutInflater import android.view.View import android.widget.EditText -import android.widget.TextView import androidx.annotation.StringRes -import androidx.fragment.app.FragmentActivity import info.nightscout.androidaps.R +import info.nightscout.androidaps.utils.CryptoUtil import info.nightscout.androidaps.utils.ToastUtils +import info.nightscout.androidaps.utils.alertDialogs.AlertDialogHelper import info.nightscout.androidaps.utils.sharedPreferences.SP import javax.inject.Inject import javax.inject.Singleton +// since androidx.autofill.HintConstants are not available +val AUTOFILL_HINT_NEW_PASSWORD = "newPassword" + @Singleton class PasswordCheck @Inject constructor(val sp: SP) { @SuppressLint("InflateParams") - fun queryPassword(activity: FragmentActivity, @StringRes labelId: Int, @StringRes preference: Int, ok: Runnable?, cancel: Runnable? = null, fail: Runnable? = null) { + fun queryPassword(context: Context, @StringRes labelId: Int, @StringRes preference: Int, ok: ( (String) -> Unit)?, cancel: (()->Unit)? = null, fail: (()->Unit)? = null) { val password = sp.getString(preference, "") if (password == "") { - ok?.run() + ok?.invoke("") return } - val promptsView = LayoutInflater.from(activity).inflate(R.layout.passwordprompt, null) - val alertDialogBuilder = AlertDialog.Builder(activity) + val promptsView = LayoutInflater.from(context).inflate(R.layout.passwordprompt, null) + val alertDialogBuilder = AlertDialogHelper.Builder(context) alertDialogBuilder.setView(promptsView) - val label = promptsView.findViewById(R.id.passwordprompt_text) as TextView - label.text = activity.getString(labelId) val userInput = promptsView.findViewById(R.id.passwordprompt_pass) as EditText + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val autoFillHintPasswordKind = context.getString(preference) + userInput.setAutofillHints(View.AUTOFILL_HINT_PASSWORD, "aaps_${autoFillHintPasswordKind}") + userInput.importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_YES + } alertDialogBuilder .setCancelable(false) - .setPositiveButton(activity.getString(R.string.ok)) { _, _ -> + .setCustomTitle(AlertDialogHelper.buildCustomTitle(context, context.getString(labelId), R.drawable.ic_header_key)) + .setPositiveButton(context.getString(R.string.ok)) { _, _ -> val enteredPassword = userInput.text.toString() - if (password == enteredPassword) ok?.run() + if (CryptoUtil.checkPassword(enteredPassword, password)) ok?.invoke(enteredPassword) else { - ToastUtils.showToastInUiThread(activity, activity.getString(R.string.wrongpassword)) - fail?.run() + ToastUtils.errorToast(context, context.getString(R.string.wrongpassword)) + fail?.invoke() } } - .setNegativeButton(activity.getString(R.string.cancel) + .setNegativeButton(context.getString(R.string.cancel) ) { dialog, _ -> - cancel?.run() + cancel?.invoke() + dialog.cancel() + } + + alertDialogBuilder.create().show() + } + + @SuppressLint("InflateParams") + fun setPassword(context: Context, @StringRes labelId: Int, @StringRes preference: Int, ok: ( (String) -> Unit)? = null, cancel: (()->Unit)? = null, clear: (()->Unit)? = null) { + val promptsView = LayoutInflater.from(context).inflate(R.layout.passwordprompt, null) + val alertDialogBuilder = AlertDialogHelper.Builder(context) + alertDialogBuilder.setView(promptsView) + + val userInput = promptsView.findViewById(R.id.passwordprompt_pass) as EditText + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val autoFillHintPasswordKind = context.getString(preference) + userInput.setAutofillHints(AUTOFILL_HINT_NEW_PASSWORD, "aaps_${autoFillHintPasswordKind}") + userInput.importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_YES + } + + alertDialogBuilder + .setCancelable(false) + .setCustomTitle(AlertDialogHelper.buildCustomTitle(context, context.getString(labelId), R.drawable.ic_header_key)) + .setPositiveButton(context.getString(R.string.ok)) { _, _ -> + val enteredPassword = userInput.text.toString() + if (enteredPassword.isNotEmpty()) { + sp.putString(preference, CryptoUtil.hashPassword(enteredPassword)) + ToastUtils.okToast(context, context.getString(R.string.password_set)) + ok?.invoke(enteredPassword) + } else { + if (sp.contains(preference)) { + sp.remove(preference) + ToastUtils.graphicalToast(context, context.getString(R.string.password_cleared), R.drawable.ic_toast_delete_confirm) + clear?.invoke() + } else { + ToastUtils.warnToast(context, context.getString(R.string.password_not_changed)) + cancel?.invoke() + } + } + + } + .setNegativeButton(context.getString(R.string.cancel) + ) { dialog, _ -> + ToastUtils.infoToast(context, context.getString(R.string.password_not_changed)) + cancel?.invoke() dialog.cancel() } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/protection/ProtectionCheck.kt b/app/src/main/java/info/nightscout/androidaps/utils/protection/ProtectionCheck.kt index c4960f4999..a1cca9116e 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/protection/ProtectionCheck.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/protection/ProtectionCheck.kt @@ -21,7 +21,8 @@ class ProtectionCheck @Inject constructor( enum class ProtectionType { NONE, BIOMETRIC, - PASSWORD + MASTER_PASSWORD, + CUSTOM_PASSWORD } private val passwordsResourceIDs = listOf( @@ -43,7 +44,8 @@ class ProtectionCheck @Inject constructor( return when (ProtectionType.values()[sp.getInt(protectionTypeResourceIDs[protection.ordinal], ProtectionType.NONE.ordinal)]) { ProtectionType.NONE -> false ProtectionType.BIOMETRIC -> true - ProtectionType.PASSWORD -> sp.getString(passwordsResourceIDs[protection.ordinal], "") != "" + ProtectionType.MASTER_PASSWORD -> sp.getString(R.string.key_master_password, "") != "" + ProtectionType.CUSTOM_PASSWORD -> sp.getString(passwordsResourceIDs[protection.ordinal], "") != "" } } @@ -55,8 +57,10 @@ class ProtectionCheck @Inject constructor( ok?.run() ProtectionType.BIOMETRIC -> BiometricCheck.biometricPrompt(activity, titleResourceIDs[protection.ordinal], ok, cancel, fail) - ProtectionType.PASSWORD -> - passwordCheck.queryPassword(activity, titleResourceIDs[protection.ordinal], passwordsResourceIDs[protection.ordinal], ok, cancel, fail) + ProtectionType.MASTER_PASSWORD -> + passwordCheck.queryPassword(activity, R.string.master_password, R.string.key_master_password, { ok?.run() }, { cancel?.run() }, { fail?.run() }) + ProtectionType.CUSTOM_PASSWORD -> + passwordCheck.queryPassword(activity, titleResourceIDs[protection.ordinal], passwordsResourceIDs[protection.ordinal], { ok?.run() }, { cancel?.run() }, { fail?.run() }) } } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/stats/TddCalculator.kt b/app/src/main/java/info/nightscout/androidaps/utils/stats/TddCalculator.kt index 0444513952..247d4c22d4 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/stats/TddCalculator.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/stats/TddCalculator.kt @@ -40,7 +40,7 @@ class TddCalculator @Inject constructor( fun calculate(days: Long): LongSparseArray { val range = T.days(days + 1).msecs() - val startTime = MidnightTime.calc(DateUtil.now()) - T.days(days).msecs() + val startTime = MidnightTime.calc(DateUtil.now() - T.days(days).msecs()) val endTime = MidnightTime.calc(DateUtil.now()) initializeData(range) diff --git a/app/src/main/java/info/nightscout/androidaps/utils/stats/TirCalculator.kt b/app/src/main/java/info/nightscout/androidaps/utils/stats/TirCalculator.kt index b8d29af3d5..f1f13cbf0d 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/stats/TirCalculator.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/stats/TirCalculator.kt @@ -23,7 +23,7 @@ class TirCalculator @Inject constructor( fun calculate(days: Long, lowMgdl: Double, highMgdl: Double): LongSparseArray { if (lowMgdl < 39) throw RuntimeException("Low below 39") if (lowMgdl > highMgdl) throw RuntimeException("Low > High") - val startTime = MidnightTime.calc(DateUtil.now()) - T.days(days).msecs() + val startTime = MidnightTime.calc(DateUtil.now() - T.days(days).msecs()) val endTime = MidnightTime.calc(DateUtil.now()) val bgReadings = MainApp.getDbHelper().getBgreadingsDataFromTime(startTime, endTime, true) diff --git a/app/src/main/java/info/nightscout/androidaps/utils/storage/FileStrorage.kt b/app/src/main/java/info/nightscout/androidaps/utils/storage/FileStrorage.kt new file mode 100644 index 0000000000..40be1e65c3 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/storage/FileStrorage.kt @@ -0,0 +1,17 @@ +package info.nightscout.androidaps.utils.storage + +import java.io.File +import javax.inject.Singleton + +@Singleton +class FileStorage : Storage { + + override fun getFileContents(file: File): String { + return file.readText() + } + + override fun putFileContents(file: File, contents: String) { + file.writeText(contents) + } + +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/storage/Storage.kt b/app/src/main/java/info/nightscout/androidaps/utils/storage/Storage.kt new file mode 100644 index 0000000000..475cd2675e --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/storage/Storage.kt @@ -0,0 +1,11 @@ +package info.nightscout.androidaps.utils.storage + +import java.io.File + +// This may seems unnecessary abstraction - but it will simplify testing +interface Storage { + + fun getFileContents(file: File) : String + fun putFileContents(file: File, contents: String) + +} diff --git a/app/src/main/res/drawable/alert_border_error.xml b/app/src/main/res/drawable/alert_border_error.xml new file mode 100644 index 0000000000..d1bcae1348 --- /dev/null +++ b/app/src/main/res/drawable/alert_border_error.xml @@ -0,0 +1,15 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/alert_border_warning.xml b/app/src/main/res/drawable/alert_border_warning.xml new file mode 100644 index 0000000000..c73a9517a5 --- /dev/null +++ b/app/src/main/res/drawable/alert_border_warning.xml @@ -0,0 +1,15 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_header_error.xml b/app/src/main/res/drawable/ic_header_error.xml new file mode 100644 index 0000000000..09c29c1558 --- /dev/null +++ b/app/src/main/res/drawable/ic_header_error.xml @@ -0,0 +1,16 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_header_export.xml b/app/src/main/res/drawable/ic_header_export.xml new file mode 100644 index 0000000000..d59103ab07 --- /dev/null +++ b/app/src/main/res/drawable/ic_header_export.xml @@ -0,0 +1,16 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_header_import.xml b/app/src/main/res/drawable/ic_header_import.xml new file mode 100644 index 0000000000..e929fad69b --- /dev/null +++ b/app/src/main/res/drawable/ic_header_import.xml @@ -0,0 +1,16 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_header_key.xml b/app/src/main/res/drawable/ic_header_key.xml new file mode 100644 index 0000000000..416cc50bd3 --- /dev/null +++ b/app/src/main/res/drawable/ic_header_key.xml @@ -0,0 +1,16 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_header_log.xml b/app/src/main/res/drawable/ic_header_log.xml new file mode 100644 index 0000000000..6ef0e68d81 --- /dev/null +++ b/app/src/main/res/drawable/ic_header_log.xml @@ -0,0 +1,16 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_header_warning.xml b/app/src/main/res/drawable/ic_header_warning.xml new file mode 100644 index 0000000000..3d7afd8ef3 --- /dev/null +++ b/app/src/main/res/drawable/ic_header_warning.xml @@ -0,0 +1,16 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_key.xml b/app/src/main/res/drawable/ic_key.xml new file mode 100644 index 0000000000..4d4a6fa28d --- /dev/null +++ b/app/src/main/res/drawable/ic_key.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_meta_date.xml b/app/src/main/res/drawable/ic_meta_date.xml new file mode 100644 index 0000000000..190392aaa1 --- /dev/null +++ b/app/src/main/res/drawable/ic_meta_date.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_meta_encryption.xml b/app/src/main/res/drawable/ic_meta_encryption.xml new file mode 100644 index 0000000000..597b35a77a --- /dev/null +++ b/app/src/main/res/drawable/ic_meta_encryption.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_meta_error.xml b/app/src/main/res/drawable/ic_meta_error.xml new file mode 100644 index 0000000000..e5d213f774 --- /dev/null +++ b/app/src/main/res/drawable/ic_meta_error.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_meta_flavour.xml b/app/src/main/res/drawable/ic_meta_flavour.xml new file mode 100644 index 0000000000..a7fe7f3471 --- /dev/null +++ b/app/src/main/res/drawable/ic_meta_flavour.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_meta_format.xml b/app/src/main/res/drawable/ic_meta_format.xml new file mode 100644 index 0000000000..2023a4934d --- /dev/null +++ b/app/src/main/res/drawable/ic_meta_format.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_meta_model.xml b/app/src/main/res/drawable/ic_meta_model.xml new file mode 100644 index 0000000000..eca3e69c59 --- /dev/null +++ b/app/src/main/res/drawable/ic_meta_model.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_meta_name.xml b/app/src/main/res/drawable/ic_meta_name.xml new file mode 100644 index 0000000000..ad120e037a --- /dev/null +++ b/app/src/main/res/drawable/ic_meta_name.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_meta_ok.xml b/app/src/main/res/drawable/ic_meta_ok.xml new file mode 100644 index 0000000000..663897c160 --- /dev/null +++ b/app/src/main/res/drawable/ic_meta_ok.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_meta_version.xml b/app/src/main/res/drawable/ic_meta_version.xml new file mode 100644 index 0000000000..ddf95f2fa9 --- /dev/null +++ b/app/src/main/res/drawable/ic_meta_version.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_meta_warning.xml b/app/src/main/res/drawable/ic_meta_warning.xml new file mode 100644 index 0000000000..9cf5ce2c36 --- /dev/null +++ b/app/src/main/res/drawable/ic_meta_warning.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_toast_check.xml b/app/src/main/res/drawable/ic_toast_check.xml new file mode 100644 index 0000000000..26b9f05b49 --- /dev/null +++ b/app/src/main/res/drawable/ic_toast_check.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_toast_delete_confirm.xml b/app/src/main/res/drawable/ic_toast_delete_confirm.xml new file mode 100644 index 0000000000..de6371045a --- /dev/null +++ b/app/src/main/res/drawable/ic_toast_delete_confirm.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_toast_error.xml b/app/src/main/res/drawable/ic_toast_error.xml new file mode 100644 index 0000000000..fb49272c0a --- /dev/null +++ b/app/src/main/res/drawable/ic_toast_error.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_toast_info.xml b/app/src/main/res/drawable/ic_toast_info.xml new file mode 100644 index 0000000000..9d54be6827 --- /dev/null +++ b/app/src/main/res/drawable/ic_toast_info.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_toast_warn.xml b/app/src/main/res/drawable/ic_toast_warn.xml new file mode 100644 index 0000000000..8864b16500 --- /dev/null +++ b/app/src/main/res/drawable/ic_toast_warn.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/toast_border_ok.xml b/app/src/main/res/drawable/toast_border_ok.xml new file mode 100644 index 0000000000..1c62848b31 --- /dev/null +++ b/app/src/main/res/drawable/toast_border_ok.xml @@ -0,0 +1,12 @@ + + + + + + + diff --git a/app/src/main/res/layout/dialog_alert_custom.xml b/app/src/main/res/layout/dialog_alert_custom.xml index eea5fc6882..f006387d28 100644 --- a/app/src/main/res/layout/dialog_alert_custom.xml +++ b/app/src/main/res/layout/dialog_alert_custom.xml @@ -1,4 +1,8 @@ + + android:layout_height="wrap_content" + android:tint="?dialogTitleIconTint" /> + android:textAppearance="?android:attr/textAppearanceLarge" + android:textColor="?dialogTitleColor" /> + - \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_alert_import_summary.xml b/app/src/main/res/layout/dialog_alert_import_summary.xml new file mode 100644 index 0000000000..fb6e5a0708 --- /dev/null +++ b/app/src/main/res/layout/dialog_alert_import_summary.xml @@ -0,0 +1,30 @@ + + + + + +