Merge branch 'dagger3' of github.com:MilosKozak/AndroidAPS into update-oref

This commit is contained in:
Tim Gunn 2020-04-05 00:45:47 +13:00
commit acab0b87be
No known key found for this signature in database
GPG key ID: C9BC1E9D0D0AED8C
202 changed files with 5214 additions and 2582 deletions

View file

@ -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'

View file

@ -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)
}
}

View file

@ -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);
}
}

View file

@ -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<NavigationView>(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<View>(R.id.drawer_layout) as DrawerLayout).closeDrawers()
true
}
}
}
val mPager = findViewById<ViewPager>(R.id.pager)
mPager.adapter = pageAdapter
//if (switchToLast)
// mPager.setCurrentItem(pageAdapter.getCount() - 1, false);
checkPluginPreferences(mPager)
}
private fun setupTabs() {
val viewPager = findViewById<ViewPager>(R.id.pager)
val normalTabs = findViewById<TabLayout>(R.id.tabs_normal)
normalTabs.setupWithViewPager(viewPager, true)
val compactTabs = findViewById<TabLayout>(R.id.tabs_compact)
compactTabs.setupWithViewPager(viewPager, true)
val toolbar = findViewById<Toolbar>(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<String>, 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<View>(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<ViewPager>(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)
}
}

View file

@ -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<PluginBase> 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();

View file

@ -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<Any>
override fun androidInjector(): AndroidInjector<Any> = 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)
}
}

View file

@ -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) {

View file

@ -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);
}

View file

@ -95,6 +95,7 @@ interface AppComponent : AndroidInjector<MainApp> {
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<MainApp> {
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)

View file

@ -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<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>>,
@PluginsModule.NotNSClient notNsClient: Lazy<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>>,
@PluginsModule.APS aps: Lazy<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>>): 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

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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

View file

@ -0,0 +1,8 @@
package info.nightscout.androidaps.events
class EventBTChange constructor(val state: Change, val deviceName: String) : Event() {
enum class Change {
CONNECT,
DISCONNECT
}
}

View file

@ -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)

View file

@ -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)
}
}

View file

@ -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 ****

View file

@ -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))
}
}

View file

@ -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<LogElement> = 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"),
}

View file

@ -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)
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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) {

View file

@ -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

View file

@ -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;

View file

@ -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<PluginBase>()
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<PluginBase>?
// 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(
</T> */
private fun <T> determineActivePlugin(pluginsInCategory: ArrayList<PluginBase>,
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<PluginBase> = plugins
override fun getPluginsList(): ArrayList<PluginBase> = ArrayList(plugins)
}

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.plugins.configBuilder
package info.nightscout.androidaps.plugins.configBuilder.events
import info.nightscout.androidaps.events.EventUpdateGui

View file

@ -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 {

View file

@ -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<AutomationEvent>()
var executionLog: MutableList<String> = ArrayList()
var btConnects : MutableList<EventBTChange> = 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)
)
}
}

View file

@ -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"))

View file

@ -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<String> {
val list: MutableList<String> = 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)
}
}

View file

@ -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<CharSequence> = 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<CharSequence>) {
itemList = ArrayList(values)
}
// For testing only
fun add(item: String) {
itemList.add(item)
}
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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<Int?> = 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<CharSequence> {
val s = ArrayList<CharSequence>()
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
}
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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<String, ?> prefsMap = prefs.getAll();
for (Map.Entry<String, ?> 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);
}
});
}
}

View file

@ -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<PrefsMetadataKey, PrefMetadata> {
val metadata: MutableMap<PrefsMetadataKey, PrefMetadata> = 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<String, String> = 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<PrefsMetadataKey, PrefMetadata>): Map<PrefsMetadataKey, PrefMetadata> {
val meta = metadata.toMutableMap()
meta[PrefsMetadataKey.AAPS_FLAVOUR]?.let { flavour ->
val flavourOfPrefs = flavour.value
if (flavour.value != BuildConfig.FLAVOR) {
flavour.status = PrefsStatus.WARN
flavour.info = resourceHelper.gs(R.string.metadata_warning_different_flavour, flavourOfPrefs, BuildConfig.FLAVOR)
}
}
meta[PrefsMetadataKey.DEVICE_MODEL]?.let { model ->
if (model.value != getCurrentDeviceModelString()) {
model.status = PrefsStatus.WARN
model.info = resourceHelper.gs(R.string.metadata_warning_different_device)
}
}
meta[PrefsMetadataKey.CREATED_AT]?.let { createdAt ->
try {
val date1 = DateTime.parse(createdAt.value);
val date2 = DateTime.now()
val daysOld = Days.daysBetween(date1.toLocalDate(), date2.toLocalDate()).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)
})
}
}

View file

@ -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)) }
}

View file

@ -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()
}
}

View file

@ -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<String>
val entries: MutableMap<String, String> = mutableMapOf()
val metadata: MutableMap<PrefsMetadataKey, PrefMetadata> = 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)
}
}
}

View file

@ -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<String, String> = mutableMapOf()
val metadata: MutableMap<PrefsMetadataKey, PrefMetadata> = mutableMapOf()
val issues = LinkedList<String>()
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)
}
}
}

View file

@ -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<String, PrefsMetadataKey>()
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<String, String>, var metadata : Map<PrefsMetadataKey, PrefMetadata>)
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)

View file

@ -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);

View file

@ -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;

View file

@ -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

View file

@ -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<BgReading> bgReadingsArray;
private String units;
private List<Series> 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<BgReading> predictions) {
double maxBgValue = Double.MIN_VALUE;
//bgReadingsArray = MainApp.getDbHelper().getBgreadingsDataFromTime(fromTime, true);
bgReadingsArray = iobCobCalculatorPlugin.getBgReadings();
List<DataPointWithLabelInterface> 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<DoubleDataPoint> 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<ScaledDataPoint> basalsLineSeries;
LineGraphSeries<ScaledDataPoint> absoluteBasalsLineSeries;
LineGraphSeries<ScaledDataPoint> baseBasalsSeries;
LineGraphSeries<ScaledDataPoint> tempBasalsSeries;
double maxBasalValueFound = 0d;
Scale basalScale = new Scale();
List<ScaledDataPoint> baseBasalArray = new ArrayList<>();
List<ScaledDataPoint> tempBasalArray = new ArrayList<>();
List<ScaledDataPoint> basalLineArray = new ArrayList<>();
List<ScaledDataPoint> 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<DataPoint> targetsSeries;
Scale targetsScale = new Scale();
targetsScale.setMultiplier(1);
List<DataPoint> 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<DataPointWithLabelInterface> filteredTreatments = new ArrayList<>();
List<Treatment> 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<ProfileSwitch> 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<ExtendedBolus> 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<CareportalEvent> 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<ScaledDataPoint> actSeriesHist;
List<ScaledDataPoint> actArrayHist = new ArrayList<>();
FixedLineGraphSeries<ScaledDataPoint> actSeriesPred;
List<ScaledDataPoint> 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<ScaledDataPoint> iobSeries;
List<ScaledDataPoint> 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<DataPointWithLabelInterface> 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<DataPointWithLabelInterface> 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<DataPointWithLabelInterface> minFailoverActiveList = new ArrayList<>();
FixedLineGraphSeries<ScaledDataPoint> cobSeries;
List<ScaledDataPoint> 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<DeviationDataPoint> devSeries;
List<DeviationDataPoint> 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<ScaledDataPoint> ratioSeries;
List<ScaledDataPoint> 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<ScaledDataPoint> dsMaxSeries;
LineGraphSeries<ScaledDataPoint> dsMinSeries;
List<ScaledDataPoint> dsMaxArray = new ArrayList<>();
List<ScaledDataPoint> 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<DataPoint> 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);
}
}

View file

@ -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<BgReading>? = null
private val units: String
private val series: MutableList<Series<*>> = 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<BgReading>?) {
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<DataPointWithLabelInterface> = 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<DoubleDataPoint>
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<ScaledDataPoint> = ArrayList()
val tempBasalArray: MutableList<ScaledDataPoint> = ArrayList()
val basalLineArray: MutableList<ScaledDataPoint> = ArrayList()
val absoluteBasalLineArray: MutableList<ScaledDataPoint> = 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<DataPoint> = 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<DataPointWithLabelInterface> = 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<ScaledDataPoint> = ArrayList()
val actArrayPred: MutableList<ScaledDataPoint> = 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<ScaledDataPoint?>
val iobArray: MutableList<ScaledDataPoint> = 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<DataPointWithLabelInterface> = 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<DataPointWithLabelInterface> = 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<DataPointWithLabelInterface> = ArrayList()
val cobArray: MutableList<ScaledDataPoint> = 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<DeviationDataPoint> = 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<ScaledDataPoint> = 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<ScaledDataPoint> = ArrayList()
val dsMinArray: MutableList<ScaledDataPoint> = 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)
}
}

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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)))
}
}

View file

@ -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;

View file

@ -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<Notification> = 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<Notification>) : RecyclerView.Adapter<NotificationRecyclerViewAdapter.NotificationsViewHolder>() {
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)

View file

@ -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
}
}

View file

@ -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)))

View file

@ -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)

View file

@ -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)
}

View file

@ -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()

View file

@ -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("<br>")
return stringBuilder
}
}

View file

@ -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<String, Long>()
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
}
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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;

View file

@ -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) {

View file

@ -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 {

View file

@ -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 {

View file

@ -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() {

View file

@ -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() {

View file

@ -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);
}
}

View file

@ -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");

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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));
}
}

View file

@ -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))
}
}
}

View file

@ -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))
}
}
}

View file

@ -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;
}
}

View file

@ -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 }
}
}

View file

@ -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));
}
}

View file

@ -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))
}
}

View file

@ -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)

View file

@ -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;
}
}

View file

@ -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<Network> = 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
}
}

View file

@ -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) }
}
}

View file

@ -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()

View file

@ -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)
}
}

View file

@ -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<SWScreen> = ArrayList()
fun getScreens(): List<SWScreen> {
@ -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)

View file

@ -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()
}
}

View file

@ -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));
}
}
}

View file

@ -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<String>, 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))
}
}
}

View file

@ -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
}
}
}

View file

@ -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);

View file

@ -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<String> = 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) }

View file

@ -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)

View file

@ -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) {

View file

@ -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<View>(R.id.alertdialog_title) as TextView).text = notEmptytitle
(titleLayout.findViewById<View>(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<View>(R.id.alertdialog_title) as TextView).text = notEmptytitle
(titleLayout.findViewById<View>(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<View>(R.id.alertdialog_title) as TextView).text = title
(titleLayout.findViewById<View>(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<View>(R.id.alertdialog_title) as TextView).text = title
(titleLayout.findViewById<View>(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<View>(R.id.alertdialog_title) as TextView).text = title
(titleLayout.findViewById<View>(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<View>(R.id.alertdialog_title) as TextView).text = title
(titleLayout.findViewById<View>(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<View>(R.id.alertdialog_title) as TextView).text = title
(titleLayout.findViewById<View>(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)

View file

@ -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,

View file

@ -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) }
}

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