db00404e43
* commit 'origin/dev^': (313 commits) cleanup draw profile in dana profile viewer optimize imports draw basal in profile viewer BG autobackfill configurable travis 2nd try Save new profile when updating it Add environment variable to handle testCoverage do not pass DIA to SMB determine_basal show Dana model properly after communication error fix deviation slope calculations Read maximum basal and bolus amount from pump update maxIOB hard limits Allow bolus record only in new treatment dialog use safeGetString JsonHelper test food code cleanup Show bolus progress Missing import statement Insight: add treatment to progress event ...
434 lines
19 KiB
Java
434 lines
19 KiB
Java
package info.nightscout.androidaps;
|
|
|
|
import android.Manifest;
|
|
import android.app.Activity;
|
|
import android.content.ActivityNotFoundException;
|
|
import android.content.Context;
|
|
import android.content.DialogInterface;
|
|
import android.content.Intent;
|
|
import android.content.pm.PackageManager;
|
|
import android.graphics.Rect;
|
|
import android.net.Uri;
|
|
import android.os.Build;
|
|
import android.os.Bundle;
|
|
import android.os.PowerManager;
|
|
import android.provider.Settings;
|
|
import android.support.v4.app.ActivityCompat;
|
|
import android.support.v4.content.ContextCompat;
|
|
import android.support.v4.view.ViewPager;
|
|
import android.support.v7.app.AlertDialog;
|
|
import android.support.v7.app.AppCompatActivity;
|
|
import android.support.v7.widget.PopupMenu;
|
|
import android.text.SpannableString;
|
|
import android.text.method.LinkMovementMethod;
|
|
import android.text.util.Linkify;
|
|
import android.view.MenuInflater;
|
|
import android.view.MenuItem;
|
|
import android.view.MotionEvent;
|
|
import android.view.View;
|
|
import android.view.WindowManager;
|
|
import android.view.inputmethod.InputMethodManager;
|
|
import android.widget.EditText;
|
|
import android.widget.ImageButton;
|
|
import android.widget.TextView;
|
|
|
|
import com.joanzapata.iconify.Iconify;
|
|
import com.joanzapata.iconify.fonts.FontAwesomeModule;
|
|
import com.squareup.otto.Subscribe;
|
|
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import info.nightscout.androidaps.data.Profile;
|
|
import info.nightscout.androidaps.events.EventAppExit;
|
|
import info.nightscout.androidaps.events.EventFeatureRunning;
|
|
import info.nightscout.androidaps.events.EventPreferenceChange;
|
|
import info.nightscout.androidaps.events.EventRefreshGui;
|
|
import info.nightscout.androidaps.interfaces.PluginBase;
|
|
import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin;
|
|
import info.nightscout.androidaps.plugins.Food.FoodPlugin;
|
|
import info.nightscout.androidaps.plugins.Overview.events.EventSetWakeLock;
|
|
import info.nightscout.androidaps.tabs.SlidingTabLayout;
|
|
import info.nightscout.androidaps.tabs.TabPageAdapter;
|
|
import info.nightscout.utils.ImportExportPrefs;
|
|
import info.nightscout.utils.LocaleHelper;
|
|
import info.nightscout.utils.LogDialog;
|
|
import info.nightscout.utils.OKDialog;
|
|
import info.nightscout.utils.PasswordProtection;
|
|
import info.nightscout.utils.SP;
|
|
import info.nightscout.utils.ToastUtils;
|
|
|
|
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
|
|
private static Logger log = LoggerFactory.getLogger(MainActivity.class);
|
|
|
|
static final int CASE_STORAGE = 0x1;
|
|
static final int CASE_SMS = 0x2;
|
|
static final int CASE_LOCATION = 0x3;
|
|
|
|
private boolean askForSMS = false;
|
|
private boolean askForLocation = true;
|
|
|
|
ImageButton menuButton;
|
|
|
|
protected PowerManager.WakeLock mWakeLock;
|
|
|
|
@Override
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
Iconify.with(new FontAwesomeModule());
|
|
LocaleHelper.onCreate(this, "en");
|
|
setContentView(R.layout.activity_main);
|
|
menuButton = (ImageButton) findViewById(R.id.overview_menuButton);
|
|
menuButton.setOnClickListener(this);
|
|
|
|
checkEula();
|
|
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
|
|
askForPermission(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE,
|
|
Manifest.permission.WRITE_EXTERNAL_STORAGE}, CASE_STORAGE);
|
|
}
|
|
askForBatteryOptimizationPermission();
|
|
doMigrations();
|
|
if (Config.logFunctionCalls)
|
|
log.debug("onCreate");
|
|
|
|
onStatusEvent(new EventSetWakeLock(SP.getBoolean("lockscreen", false)));
|
|
|
|
registerBus();
|
|
setUpTabs(false);
|
|
}
|
|
|
|
@Subscribe
|
|
public void onStatusEvent(final EventSetWakeLock ev) {
|
|
final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
|
if (ev.lock) {
|
|
mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "AAPS");
|
|
if (!mWakeLock.isHeld())
|
|
mWakeLock.acquire();
|
|
} else {
|
|
if (mWakeLock != null && mWakeLock.isHeld())
|
|
mWakeLock.release();
|
|
}
|
|
}
|
|
|
|
@Subscribe
|
|
public void onStatusEvent(final EventRefreshGui ev) {
|
|
String lang = SP.getString("language", "en");
|
|
LocaleHelper.setLocale(getApplicationContext(), lang);
|
|
runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (ev.recreate) {
|
|
recreate();
|
|
} else {
|
|
try { // activity may be destroyed
|
|
setUpTabs(true);
|
|
} catch (IllegalStateException e) {
|
|
log.error("Unhandled exception", e);
|
|
}
|
|
}
|
|
|
|
boolean lockScreen = BuildConfig.NSCLIENTOLNY && SP.getBoolean("lockscreen", false);
|
|
if (lockScreen)
|
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
|
else
|
|
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
|
}
|
|
});
|
|
}
|
|
|
|
private void setUpTabs(boolean switchToLast) {
|
|
TabPageAdapter pageAdapter = new TabPageAdapter(getSupportFragmentManager(), this);
|
|
for (PluginBase p : MainApp.getPluginsList()) {
|
|
pageAdapter.registerNewFragment(p);
|
|
}
|
|
ViewPager mPager = (ViewPager) findViewById(R.id.pager);
|
|
mPager.setAdapter(pageAdapter);
|
|
SlidingTabLayout mTabs = (SlidingTabLayout) findViewById(R.id.tabs);
|
|
mTabs.setViewPager(mPager);
|
|
if (switchToLast)
|
|
mPager.setCurrentItem(pageAdapter.getCount() - 1, false);
|
|
}
|
|
|
|
private void registerBus() {
|
|
try {
|
|
MainApp.bus().unregister(this);
|
|
} catch (RuntimeException x) {
|
|
// Ignore
|
|
}
|
|
MainApp.bus().register(this);
|
|
}
|
|
|
|
private void checkEula() {
|
|
//SP.removeBoolean(R.string.key_i_understand);
|
|
boolean IUnderstand = SP.getBoolean(R.string.key_i_understand, false);
|
|
if (!IUnderstand) {
|
|
Intent intent = new Intent(getApplicationContext(), AgreementActivity.class);
|
|
startActivity(intent);
|
|
finish();
|
|
}
|
|
}
|
|
|
|
private void doMigrations() {
|
|
|
|
checkUpgradeToProfileTarget();
|
|
|
|
// guarantee that the unreachable threshold is at least 30 and of type String
|
|
// Added in 1.57 at 21.01.2018
|
|
Integer unreachable_threshold = SP.getInt(R.string.key_pump_unreachable_threshold, 30);
|
|
SP.remove(R.string.key_pump_unreachable_threshold);
|
|
if (unreachable_threshold < 30) unreachable_threshold = 30;
|
|
SP.putString(R.string.key_pump_unreachable_threshold, unreachable_threshold.toString());
|
|
}
|
|
|
|
|
|
private void checkUpgradeToProfileTarget() { // TODO: can be removed in the future
|
|
boolean oldKeyExists = SP.contains("openapsma_min_bg");
|
|
if (oldKeyExists) {
|
|
Profile profile = MainApp.getConfigBuilder().getProfile();
|
|
String oldRange = SP.getDouble("openapsma_min_bg", 0d) + " - " + SP.getDouble("openapsma_max_bg", 0d);
|
|
String newRange = "";
|
|
if (profile != null) {
|
|
newRange = profile.getTargetLow() + " - " + profile.getTargetHigh();
|
|
}
|
|
String message = "Target range is changed in current version.\n\nIt's not taken from preferences but from profile.\n\n!!! REVIEW YOUR SETTINGS !!!";
|
|
message += "\n\nOld settings: " + oldRange;
|
|
message += "\nProfile settings: " + newRange;
|
|
OKDialog.show(this, "Target range change", message, new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
SP.remove("openapsma_min_bg");
|
|
SP.remove("openapsma_max_bg");
|
|
SP.remove("openapsma_target_bg");
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
//check for sms permission if enable in prefernces
|
|
@Subscribe
|
|
public void onStatusEvent(final EventPreferenceChange ev) {
|
|
if (ev.isChanged(R.string.key_smscommunicator_remotecommandsallowed)) {
|
|
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
|
|
synchronized (this) {
|
|
if (SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)) {
|
|
setAskForSMS();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private synchronized void setAskForSMS() {
|
|
askForSMS = true;
|
|
}
|
|
|
|
@Override
|
|
protected void onResume() {
|
|
super.onResume();
|
|
askForSMSPermissions();
|
|
askForLocationPermissions();
|
|
MainApp.bus().post(new EventFeatureRunning(EventFeatureRunning.Feature.MAIN));
|
|
}
|
|
|
|
@Override
|
|
public void onDestroy() {
|
|
if (mWakeLock != null)
|
|
if (mWakeLock.isHeld())
|
|
mWakeLock.release();
|
|
super.onDestroy();
|
|
}
|
|
|
|
private void askForBatteryOptimizationPermission() {
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
final String packageName = getPackageName();
|
|
|
|
final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
|
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
|
|
log.debug("Requesting ignore battery optimization");
|
|
|
|
OKDialog.show(this, getString(R.string.pleaseallowpermission), String.format(getString(R.string.needwhitelisting), getString(R.string.app_name)), new Runnable() {
|
|
|
|
@Override
|
|
public void run() {
|
|
try {
|
|
final Intent intent = new Intent();
|
|
|
|
// ignoring battery optimizations required for constant connection
|
|
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
|
|
intent.setData(Uri.parse("package:" + packageName));
|
|
startActivity(intent);
|
|
|
|
} catch (ActivityNotFoundException e) {
|
|
final String msg = getString(R.string.batteryoptimalizationerror);
|
|
ToastUtils.showToastInUiThread(getApplicationContext(), msg);
|
|
log.error(msg);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
private synchronized void askForSMSPermissions() {
|
|
if (askForSMS) { //only when settings were changed an MainActivity resumes.
|
|
askForSMS = false;
|
|
if (SP.getBoolean(R.string.smscommunicator_remotecommandsallowed, false)) {
|
|
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
|
|
askForPermission(new String[]{Manifest.permission.RECEIVE_SMS,
|
|
Manifest.permission.SEND_SMS,
|
|
Manifest.permission.RECEIVE_MMS}, CASE_SMS);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private synchronized void askForLocationPermissions() {
|
|
if (askForLocation) { //only when settings were changed an MainActivity resumes.
|
|
askForLocation = false;
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
askForPermission(new String[]{Manifest.permission.ACCESS_FINE_LOCATION,
|
|
Manifest.permission.ACCESS_FINE_LOCATION,
|
|
Manifest.permission.ACCESS_FINE_LOCATION}, CASE_LOCATION);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void askForPermission(String[] permission, Integer requestCode) {
|
|
boolean test = false;
|
|
for (int i = 0; i < permission.length; i++) {
|
|
test = test || (ContextCompat.checkSelfPermission(this, permission[i]) != PackageManager.PERMISSION_GRANTED);
|
|
}
|
|
if (test) {
|
|
ActivityCompat.requestPermissions(this, permission, requestCode);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
|
if (permissions.length != 0) {
|
|
if (ActivityCompat.checkSelfPermission(this, permissions[0]) == PackageManager.PERMISSION_GRANTED) {
|
|
switch (requestCode) {
|
|
case CASE_STORAGE:
|
|
//show dialog after permission is granted
|
|
AlertDialog.Builder alert = new AlertDialog.Builder(this);
|
|
alert.setMessage(R.string.alert_dialog_storage_permission_text);
|
|
alert.setPositiveButton(R.string.ok, null);
|
|
alert.show();
|
|
break;
|
|
case CASE_LOCATION:
|
|
case CASE_SMS:
|
|
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 void onClick(final View v) {
|
|
final Activity activity = this;
|
|
switch (v.getId()) {
|
|
case R.id.overview_menuButton:
|
|
PopupMenu popup = new PopupMenu(v.getContext(), v);
|
|
MenuInflater inflater = popup.getMenuInflater();
|
|
inflater.inflate(R.menu.menu_main, popup.getMenu());
|
|
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
|
@Override
|
|
public boolean onMenuItemClick(MenuItem item) {
|
|
int id = item.getItemId();
|
|
switch (id) {
|
|
case R.id.nav_preferences:
|
|
PasswordProtection.QueryPassword(v.getContext(), R.string.settings_password, "settings_password", new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
Intent i = new Intent(v.getContext(), PreferencesActivity.class);
|
|
i.putExtra("id", -1);
|
|
startActivity(i);
|
|
}
|
|
}, null);
|
|
break;
|
|
case R.id.nav_historybrowser:
|
|
startActivity(new Intent(v.getContext(), HistoryBrowseActivity.class));
|
|
break;
|
|
case R.id.nav_resetdb:
|
|
new AlertDialog.Builder(v.getContext())
|
|
.setTitle(R.string.nav_resetdb)
|
|
.setMessage(R.string.reset_db_confirm)
|
|
.setNegativeButton(android.R.string.cancel, null)
|
|
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
MainApp.getDbHelper().resetDatabases();
|
|
// should be handled by Plugin-Interface and
|
|
// additional service interface and plugin registry
|
|
MainApp.getSpecificPlugin(FoodPlugin.class).getService().resetFood();
|
|
}
|
|
})
|
|
.create()
|
|
.show();
|
|
break;
|
|
case R.id.nav_export:
|
|
ImportExportPrefs.verifyStoragePermissions(activity);
|
|
ImportExportPrefs.exportSharedPreferences(activity);
|
|
break;
|
|
case R.id.nav_import:
|
|
ImportExportPrefs.verifyStoragePermissions(activity);
|
|
ImportExportPrefs.importSharedPreferences(activity);
|
|
break;
|
|
case R.id.nav_show_logcat:
|
|
LogDialog.showLogcat(v.getContext());
|
|
break;
|
|
case R.id.nav_about:
|
|
AlertDialog.Builder builder = new AlertDialog.Builder(v.getContext());
|
|
builder.setTitle(getString(R.string.app_name) + " " + BuildConfig.VERSION);
|
|
if (Config.NSCLIENT || Config.G5UPLOADER)
|
|
builder.setIcon(R.mipmap.yellowowl);
|
|
else
|
|
builder.setIcon(R.mipmap.blueowl);
|
|
String message = "Build: " + BuildConfig.BUILDVERSION + "\n";
|
|
message += MainApp.sResources.getString(R.string.configbuilder_nightscoutversion_label) + " " + ConfigBuilderPlugin.nightscoutVersionName;
|
|
message += getString(R.string.about_link_urls);
|
|
final SpannableString messageSpanned = new SpannableString(message);
|
|
Linkify.addLinks(messageSpanned, Linkify.WEB_URLS);
|
|
builder.setMessage(messageSpanned);
|
|
builder.setPositiveButton(MainApp.sResources.getString(R.string.ok), null);
|
|
AlertDialog alertDialog = builder.create();
|
|
alertDialog.show();
|
|
((TextView)alertDialog.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());
|
|
break;
|
|
case R.id.nav_exit:
|
|
log.debug("Exiting");
|
|
MainApp.instance().stopKeepAliveService();
|
|
MainApp.bus().post(new EventAppExit());
|
|
MainApp.closeDbHelper();
|
|
finish();
|
|
System.runFinalization();
|
|
System.exit(0);
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
popup.show();
|
|
break;
|
|
}
|
|
}
|
|
}
|