diff --git a/app/build.gradle b/app/build.gradle
index d2d6dfecbb..54aa94ccc0 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -190,6 +190,7 @@ dependencies {
libs "MilosKozak:danars-support-lib:master@zip"
implementation "com.android.support:appcompat-v7:${supportLibraryVersion}"
+ implementation "com.android.support:support-v13:${supportLibraryVersion}"
implementation "com.android.support:support-v4:${supportLibraryVersion}"
implementation "com.android.support:cardview-v7:${supportLibraryVersion}"
implementation "com.android.support:recyclerview-v7:${supportLibraryVersion}"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 213bde1916..bbe042213a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -127,6 +127,16 @@
+
+
+
+
{
- MainApp.getDbHelper().resetDatabases();
- // should be handled by Plugin-Interface and
- // additional service interface and plugin registry
- FoodPlugin.getPlugin().getService().resetFood();
- TreatmentsPlugin.getPlugin().getService().resetTreatments();
- })
- .create()
- .show();
- return true;
- case R.id.nav_export:
- ImportExportPrefs.verifyStoragePermissions(this);
- ImportExportPrefs.exportSharedPreferences(this);
- return true;
- case R.id.nav_import:
- ImportExportPrefs.verifyStoragePermissions(this);
- ImportExportPrefs.importSharedPreferences(this);
- return true;
case R.id.nav_show_logcat:
LogDialog.showLogcat(this);
return true;
diff --git a/app/src/main/java/info/nightscout/androidaps/MainApp.java b/app/src/main/java/info/nightscout/androidaps/MainApp.java
index e1cc242e7d..b16f3bc570 100644
--- a/app/src/main/java/info/nightscout/androidaps/MainApp.java
+++ b/app/src/main/java/info/nightscout/androidaps/MainApp.java
@@ -23,7 +23,6 @@ import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.ArrayList;
-import ch.qos.logback.classic.LoggerContext;
import info.nightscout.androidaps.data.ConstraintChecker;
import info.nightscout.androidaps.db.DatabaseHelper;
import info.nightscout.androidaps.interfaces.PluginBase;
@@ -40,6 +39,7 @@ 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.IobCobCalculator.IobCobCalculatorPlugin;
+import info.nightscout.androidaps.plugins.Maintenance.MaintenancePlugin;
import info.nightscout.androidaps.plugins.Loop.LoopPlugin;
import info.nightscout.androidaps.plugins.NSClientInternal.NSClientPlugin;
import info.nightscout.androidaps.plugins.NSClientInternal.NSUpload;
@@ -79,6 +79,7 @@ import info.nightscout.androidaps.receivers.KeepAliveReceiver;
import info.nightscout.androidaps.receivers.NSAlarmReceiver;
import info.nightscout.androidaps.services.Intents;
import info.nightscout.utils.FabricPrivacy;
+import info.nightscout.utils.LoggerUtils;
import io.fabric.sdk.android.Fabric;
@@ -128,7 +129,7 @@ public class MainApp extends Application {
log.info("Version: " + BuildConfig.VERSION_NAME);
log.info("BuildVersion: " + BuildConfig.BUILDVERSION);
- String extFilesDir = this.getLogDirectory();
+ String extFilesDir = LoggerUtils.getLogDirectory();
File engineeringModeSemaphore = new File(extFilesDir, "engineering_mode");
engineeringMode = engineeringModeSemaphore.exists() && engineeringModeSemaphore.isFile();
@@ -190,6 +191,7 @@ public class MainApp extends Application {
pluginsList.add(StatuslinePlugin.initPlugin(this));
pluginsList.add(PersistentNotificationPlugin.getPlugin());
pluginsList.add(NSClientPlugin.getPlugin());
+ pluginsList.add(MaintenancePlugin.initPlugin(this));
pluginsList.add(sConfigBuilder = ConfigBuilderPlugin.getPlugin());
@@ -389,11 +391,6 @@ public class MainApp extends Application {
return devBranch;
}
- public String getLogDirectory() {
- LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
- return lc.getProperty("EXT_FILES_DIR");
- }
-
@Override
public void onTerminate() {
if (L.isEnabled(L.CORE))
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Maintenance/MaintenanceFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Maintenance/MaintenanceFragment.java
new file mode 100644
index 0000000000..cda9e3d861
--- /dev/null
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/Maintenance/MaintenanceFragment.java
@@ -0,0 +1,83 @@
+package info.nightscout.androidaps.plugins.Maintenance;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v7.app.AlertDialog;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import info.nightscout.androidaps.MainApp;
+import info.nightscout.androidaps.R;
+import info.nightscout.androidaps.plugins.Food.FoodPlugin;
+import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin;
+import info.nightscout.utils.ImportExportPrefs;
+
+/**
+ *
+ */
+public class MaintenanceFragment extends Fragment {
+
+ private Fragment f;
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ this.f = this;
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ this.f = null;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.maintenance_fragment, container, false);
+
+ final Fragment f = this;
+
+ view.findViewById(R.id.log_send).setOnClickListener(view1 -> MaintenancePlugin.getPlugin().sendLogs());
+
+ view.findViewById(R.id.log_delete).setOnClickListener(view12 -> MaintenancePlugin.getPlugin().deleteLogs());
+
+ view.findViewById(R.id.nav_resetdb).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ new AlertDialog.Builder(f.getContext())
+ .setTitle(R.string.nav_resetdb)
+ .setMessage(R.string.reset_db_confirm)
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok, (dialog, which) -> {
+ MainApp.getDbHelper().resetDatabases();
+ // should be handled by Plugin-Interface and
+ // additional service interface and plugin registry
+ FoodPlugin.getPlugin().getService().resetFood();
+ TreatmentsPlugin.getPlugin().getService().resetTreatments();
+ })
+ .create()
+ .show();
+ }
+ });
+
+ view.findViewById(R.id.nav_export).setOnClickListener(view13 -> {
+ // start activity for checking permissions...
+ ImportExportPrefs.verifyStoragePermissions(f);
+ ImportExportPrefs.exportSharedPreferences(f);
+ });
+
+ view.findViewById(R.id.nav_import).setOnClickListener(view14 -> {
+ // start activity for checking permissions...
+ ImportExportPrefs.verifyStoragePermissions(f);
+ ImportExportPrefs.importSharedPreferences(f);
+ });
+
+
+ return view;
+ }
+
+}
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Maintenance/MaintenancePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Maintenance/MaintenancePlugin.java
new file mode 100644
index 0000000000..481a65b262
--- /dev/null
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/Maintenance/MaintenancePlugin.java
@@ -0,0 +1,237 @@
+package info.nightscout.androidaps.plugins.Maintenance;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.support.v4.content.FileProvider;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import info.nightscout.androidaps.R;
+import info.nightscout.androidaps.interfaces.PluginBase;
+import info.nightscout.androidaps.interfaces.PluginDescription;
+import info.nightscout.androidaps.interfaces.PluginType;
+import info.nightscout.utils.LoggerUtils;
+import info.nightscout.utils.SP;
+
+public class MaintenancePlugin extends PluginBase {
+
+ private static final Logger LOG = LoggerFactory.getLogger(MaintenancePlugin.class);
+
+ private final Context ctx;
+
+ private static MaintenancePlugin maintenancePlugin;
+
+ public static MaintenancePlugin getPlugin() {
+ return maintenancePlugin;
+ }
+
+ public static MaintenancePlugin initPlugin(Context ctx) {
+
+ if (maintenancePlugin == null) {
+ maintenancePlugin = new MaintenancePlugin(ctx);
+ }
+
+ return maintenancePlugin;
+ }
+
+ public MaintenancePlugin() {
+ // required for testing
+ super(null);
+ this.ctx = null;
+ }
+
+ MaintenancePlugin(Context ctx) {
+ super(new PluginDescription()
+ .mainType(PluginType.GENERAL)
+ .fragmentClass(MaintenanceFragment.class.getName())
+ .alwayVisible(true)
+ .alwaysEnabled(true)
+ .pluginName(R.string.maintenance)
+ .shortName(R.string.maintenance_shortname)
+ .preferencesId(R.xml.pref_maintenance)
+ .description(R.string.description_maintenance)
+ );
+ this.ctx = ctx;
+ }
+
+ public void sendLogs() {
+ String recipient = SP.getString("key_maintenance_logs_email", "logs@androidaps.org");
+ int amount = SP.getInt("key_maintenance_logs_amount", 2);
+
+ String logDirectory = LoggerUtils.getLogDirectory();
+ List logs = this.getLogfiles(logDirectory, amount);
+
+ File zipDir = this.ctx.getExternalFilesDir("exports");
+ File zipFile = new File(zipDir, this.constructName());
+
+ LOG.debug("zipFile: {}", zipFile.getAbsolutePath());
+ File zip = this.zipLogs(zipFile, logs);
+
+ Uri attachementUri = FileProvider.getUriForFile(this.ctx, "info.nightscout.androidaps.fileprovider", zip);
+ Intent emailIntent = this.sendMail(attachementUri, recipient, "Log Export");
+ LOG.debug("sending emailIntent");
+ ctx.startActivity(emailIntent);
+ }
+
+ //todo replace this with a call on startup of the application, specifically to remove
+ // unnecessary garbage from the log exports
+ public void deleteLogs() {
+ String logDirectory = LoggerUtils.getLogDirectory();
+ File logDir = new File(logDirectory);
+
+ File[] files = logDir.listFiles((file, name) -> name.startsWith("AndroidAPS")
+ && name.endsWith(".zip"));
+
+ Arrays.sort(files, (f1, f2) -> f1.getName().compareTo(f2.getName()));
+
+ List delFiles = Arrays.asList(files);
+ int amount = SP.getInt("key_logshipper_amount", 2);
+ int keepIndex = amount - 1;
+
+ if (keepIndex < delFiles.size()) {
+ delFiles = delFiles.subList(keepIndex, delFiles.size());
+
+ for (File file : delFiles) {
+ file.delete();
+ }
+ }
+
+ File exportDir = new File(logDirectory, "exports");
+
+ if (exportDir.exists()) {
+ File[] expFiles = exportDir.listFiles();
+
+ for (File file : expFiles) {
+ file.delete();
+ }
+ exportDir.delete();
+ }
+ }
+
+ /**
+ * returns a list of log files. The number of returned logs is given via the amount
+ * parameter.
+ *
+ * The log files are sorted by the name descending.
+ *
+ * @param directory
+ * @param amount
+ * @return
+ */
+ public List getLogfiles(String directory, int amount) {
+ LOG.debug("getting {} logs from directory {}", amount, directory);
+ File logDir = new File(directory);
+
+ File[] files = logDir.listFiles((file, name) -> name.startsWith("AndroidAPS")
+ && (name.endsWith(".log")
+ || (name.endsWith(".zip") && !name.endsWith(LoggerUtils.SUFFIX))));
+
+ Arrays.sort(files, (f1, f2) -> f2.getName().compareTo(f1.getName()));
+
+ List result = Arrays.asList(files);
+ int toIndex = amount++;
+
+ if (toIndex > result.size()) {
+ toIndex = result.size();
+ }
+
+ LOG.debug("returning sublist 0 to {}", toIndex);
+ return result.subList(0, toIndex);
+ }
+
+ public File zipLogs(File zipFile, List files) {
+ LOG.debug("creating zip {}", zipFile.getAbsolutePath());
+
+ try {
+ zip(zipFile, files);
+ } catch (IOException e) {
+ LOG.error("Cannot retrieve zip", e);
+ }
+
+ return zipFile;
+ }
+
+ /**
+ * construct the name of zip file which is used to export logs.
+ *
+ * The name is constructed using the following scheme:
+ * AndroidAPS_LOG_ + Long Time + .log.zip
+ *
+ * @return
+ */
+ public String constructName() {
+ return "AndroidAPS_LOG_" + String.valueOf(new Date().getTime()) + LoggerUtils.SUFFIX;
+ }
+
+ public void zip(File zipFile, List files) throws IOException {
+ final int BUFFER_SIZE = 2048;
+
+ ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile)));
+
+ for (File file : files) {
+ byte data[] = new byte[BUFFER_SIZE];
+
+ try(FileInputStream fileInputStream = new FileInputStream( file )) {
+
+ try(BufferedInputStream origin = new BufferedInputStream(fileInputStream, BUFFER_SIZE)) {
+ ZipEntry entry = new ZipEntry(file.getName());
+
+ out.putNextEntry(entry);
+ int count;
+ while ((count = origin.read(data, 0, BUFFER_SIZE)) != -1) {
+ out.write(data, 0, count);
+ }
+
+ }
+ }
+ }
+
+ out.close();
+ }
+
+ /**
+ * send a mail with the given file to the recipients with the given subject.
+ *
+ * the returned intent should be used to really send the mail using
+ *
+ * startActivity(Intent.createChooser(emailIntent , "Send email..."));
+ *
+ * @param attachementUri
+ * @param recipient
+ * @param subject
+ * @return
+ */
+ public static Intent sendMail(Uri attachementUri, String recipient, String subject) {
+ LOG.debug("sending email to {} with subject {}", recipient, subject);
+ Intent emailIntent = new Intent(Intent.ACTION_SEND);
+
+ emailIntent.setType("text/plain");
+ emailIntent.putExtra(Intent.EXTRA_EMAIL , new String[]{recipient});
+ emailIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
+ emailIntent.putExtra(Intent.EXTRA_TEXT, "");
+
+ LOG.debug("put path {}", attachementUri.toString());
+ emailIntent.putExtra(Intent.EXTRA_STREAM, attachementUri);
+ emailIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ return emailIntent;
+ }
+
+
+}
diff --git a/app/src/main/java/info/nightscout/utils/ImportExportPrefs.java b/app/src/main/java/info/nightscout/utils/ImportExportPrefs.java
index 22175e0281..0cef5817a9 100644
--- a/app/src/main/java/info/nightscout/utils/ImportExportPrefs.java
+++ b/app/src/main/java/info/nightscout/utils/ImportExportPrefs.java
@@ -3,12 +3,15 @@ package info.nightscout.utils;
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
+import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.support.v4.app.ActivityCompat;
+import android.support.v4.app.Fragment;
+import android.support.v4.content.ContextCompat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -45,7 +48,6 @@ public class ImportExportPrefs {
// Check if we have write permission
int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
-
if (permission != PackageManager.PERMISSION_GRANTED) {
// We don't have permission so prompt the user
ActivityCompat.requestPermissions(
@@ -56,7 +58,22 @@ public class ImportExportPrefs {
}
}
- public static void exportSharedPreferences(final Activity c) {
+ public 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);
+ }
+
+ }
+
+ public static void exportSharedPreferences(final Fragment f) {
+ exportSharedPreferences(f.getContext());
+ }
+
+ public static void exportSharedPreferences(final Context c) {
new AlertDialog.Builder(c)
.setMessage(MainApp.gs(R.string.export_to) + " " + file + " ?")
@@ -86,13 +103,17 @@ public class ImportExportPrefs {
.show();
}
- public static void importSharedPreferences(final Activity c) {
- new AlertDialog.Builder(c)
+ public static void importSharedPreferences(final Fragment fragment) {
+ importSharedPreferences(fragment.getContext());
+ }
+
+ public static void importSharedPreferences(final Context context) {
+ new AlertDialog.Builder(context)
.setMessage(MainApp.gs(R.string.import_from) + " " + file + " ?")
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(c);
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = prefs.edit();
String line;
String[] lineParts;
@@ -113,20 +134,20 @@ public class ImportExportPrefs {
}
reader.close();
editor.commit();
- OKDialog.show(c, MainApp.gs(R.string.setting_imported), MainApp.gs(R.string.restartingapp), new Runnable() {
+ OKDialog.show(context, MainApp.gs(R.string.setting_imported), MainApp.gs(R.string.restartingapp), new Runnable() {
@Override
public void run() {
log.debug("Exiting");
MainApp.instance().stopKeepAliveService();
MainApp.bus().post(new EventAppExit());
MainApp.closeDbHelper();
- c.finish();
+// context.finish();
System.runFinalization();
System.exit(0);
}
});
} catch (FileNotFoundException e) {
- ToastUtils.showToastInUiThread(c, MainApp.gs(R.string.filenotfound) + " " + file);
+ ToastUtils.showToastInUiThread(context, MainApp.gs(R.string.filenotfound) + " " + file);
log.error("Unhandled exception", e);
} catch (IOException e) {
log.error("Unhandled exception", e);
diff --git a/app/src/main/java/info/nightscout/utils/LoggerUtils.java b/app/src/main/java/info/nightscout/utils/LoggerUtils.java
new file mode 100644
index 0000000000..60d9251181
--- /dev/null
+++ b/app/src/main/java/info/nightscout/utils/LoggerUtils.java
@@ -0,0 +1,28 @@
+package info.nightscout.utils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import ch.qos.logback.classic.LoggerContext;
+
+/**
+ * This class provides serveral methods for log-handling (eg. sending logs as emails).
+ */
+public class LoggerUtils {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(LoggerUtils.class);
+
+ public static String SUFFIX = ".log.zip";
+
+ /**
+ * Returns the directory, in which the logs are stored on the system. This is configured in the
+ * logback.xml file.
+ *
+ * @return
+ */
+ public static String getLogDirectory() {
+ LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
+ return lc.getProperty("EXT_FILES_DIR");
+ }
+
+}
diff --git a/app/src/main/java/info/nightscout/utils/OKDialog.java b/app/src/main/java/info/nightscout/utils/OKDialog.java
index 5185049678..a432cf27fc 100644
--- a/app/src/main/java/info/nightscout/utils/OKDialog.java
+++ b/app/src/main/java/info/nightscout/utils/OKDialog.java
@@ -1,7 +1,9 @@
package info.nightscout.utils;
import android.app.Activity;
+import android.content.Context;
import android.content.DialogInterface;
+import android.os.Handler;
import android.os.SystemClock;
import android.support.v7.app.AlertDialog;
import android.support.v7.view.ContextThemeWrapper;
@@ -21,8 +23,12 @@ public class OKDialog {
private static Logger log = LoggerFactory.getLogger(OKDialog.class);
public static void show(final Activity activity, String title, String message, final Runnable runnable) {
+ show(activity, title, message, runnable);
+ }
+
+ public static void show(final Context context, String title, String message, final Runnable runnable) {
try {
- AlertDialog.Builder builder = new AlertDialog.Builder(new ContextThemeWrapper(activity, R.style.AppTheme));
+ AlertDialog.Builder builder = new AlertDialog.Builder(new ContextThemeWrapper(context, R.style.AppTheme));
builder.setTitle(title);
builder.setMessage(message);
builder.setPositiveButton(MainApp.gs(R.string.ok), new DialogInterface.OnClickListener() {
@@ -30,7 +36,7 @@ public class OKDialog {
dialog.dismiss();
if (runnable != null) {
SystemClock.sleep(100);
- activity.runOnUiThread(runnable);
+ runOnUiThread(runnable);
}
}
});
@@ -41,6 +47,11 @@ public class OKDialog {
}
}
+ public static boolean runOnUiThread(Runnable theRunnable) {
+ final Handler mainHandler = new Handler(MainApp.instance().getApplicationContext().getMainLooper());
+ return mainHandler.post(theRunnable);
+ }
+
public static void show(final Activity activity, String title, Spanned message, final Runnable runnable) {
try {
AlertDialog.Builder builder = new AlertDialog.Builder(new ContextThemeWrapper(activity, R.style.AppTheme));
diff --git a/app/src/main/res/layout/maintenance_fragment.xml b/app/src/main/res/layout/maintenance_fragment.xml
new file mode 100644
index 0000000000..9a9a9650f5
--- /dev/null
+++ b/app/src/main/res/layout/maintenance_fragment.xml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml
index b92d3d83c8..98d3019476 100644
--- a/app/src/main/res/menu/menu_main.xml
+++ b/app/src/main/res/menu/menu_main.xml
@@ -18,18 +18,6 @@
android:id="@+id/nav_setupwizard"
app:showAsAction="never"
android:title="@string/nav_setupwizard" />
-
-
-
- Copy To Clipboard
Copied to clipboard
Show log
+ Export Logs
Calibration
Send calibration %.1f to xDrip?
xDrip+ not installed
@@ -1164,6 +1165,18 @@
Stored settings found
Attention: If you activate and connect to a hardware pump, AndroidAPS will copy the basal settings from the profile to the pump, overwriting the existing basal rate stored on the pump. Make sure you have the correct basal setting in AndroidAPS. If you are not sure or don\'t want to overwrite the basal settings on your pump, press cancel and repeat switching to the pump at a later time.
Treatment data incomplete
+ Maintenance Settings
+ Email
+ email
+ Invalid Email
+ amount of logs to send
+ No of Logs to send
+ Wartung
+ MAINT
+ Provides several functions for maintenance (eg. log sending, log deletion).
+ Send Logs by Email
+ Delete Logs
+
A treatment (insulin: %1$.2f, carbs: %2$d, at: %3$s) could not be added to treatments. Please check and manually add a record as appropriate.
eCarbs: %1$d g (%2$d h), delay: %3$d m
key_plugin_stats_report_timestamp
diff --git a/app/src/main/res/xml/filepaths.xml b/app/src/main/res/xml/filepaths.xml
new file mode 100644
index 0000000000..7490f459d4
--- /dev/null
+++ b/app/src/main/res/xml/filepaths.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/pref_maintenance.xml b/app/src/main/res/xml/pref_maintenance.xml
new file mode 100644
index 0000000000..75b555439d
--- /dev/null
+++ b/app/src/main/res/xml/pref_maintenance.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/test/java/info/nightscout/MainAppTest.java b/app/src/test/java/info/nightscout/MainAppTest.java
index 7bb58797be..569d5ded21 100644
--- a/app/src/test/java/info/nightscout/MainAppTest.java
+++ b/app/src/test/java/info/nightscout/MainAppTest.java
@@ -12,6 +12,7 @@ import info.nightscout.androidaps.R;
import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.plugins.Overview.OverviewPlugin;
+import info.nightscout.utils.LoggerUtils;
/**
@@ -127,7 +128,7 @@ public class MainAppTest {
@Test
public void getLogDirectoryTest() {
// logger not initialized in Roboelectric
- Assert.assertNull(mainApp.getLogDirectory());
+ Assert.assertNull(LoggerUtils.getLogDirectory());
}
}
diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/Maintenance/MaintenancePluginTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/Maintenance/MaintenancePluginTest.java
new file mode 100644
index 0000000000..3eb3d2d661
--- /dev/null
+++ b/app/src/test/java/info/nightscout/androidaps/plugins/Maintenance/MaintenancePluginTest.java
@@ -0,0 +1,43 @@
+package info.nightscout.androidaps.plugins.Maintenance;
+
+import org.junit.Test;
+
+import java.io.File;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class MaintenancePluginTest {
+
+ private MaintenancePlugin sut = new MaintenancePlugin();
+
+ @Test
+ public void getLogfilesTest() {
+ String logDirectory = "src/test/res/logger";
+
+ List logs = sut.getLogfiles(logDirectory, 2);
+ assertEquals(2, logs.size());
+ assertEquals("AndroidAPS.log", logs.get(0).getName());
+ assertEquals("AndroidAPS.2018-01-03_01-01-00.1.zip", logs.get(1).getName());
+
+ logs = sut.getLogfiles(logDirectory, 10);
+ assertEquals(4, logs.size());
+ }
+
+
+ @Test
+ public void zipLogsTest() {
+ String logDirectory = "src/test/res/logger";
+ List logs = sut.getLogfiles(logDirectory, 2);
+
+ String name = "AndroidAPS.log.zip";
+
+ File zipFile = new File("build/" + name);
+ zipFile = sut.zipLogs(zipFile, logs);
+
+ assertTrue(zipFile.exists());
+ assertTrue(zipFile.isFile());
+ }
+
+}
diff --git a/app/src/test/res/logger/AndroidAPS.2018-01-01_01-01-00.1.zip b/app/src/test/res/logger/AndroidAPS.2018-01-01_01-01-00.1.zip
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/app/src/test/res/logger/AndroidAPS.2018-01-02_01-01-00.1.zip b/app/src/test/res/logger/AndroidAPS.2018-01-02_01-01-00.1.zip
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/app/src/test/res/logger/AndroidAPS.2018-01-03_01-01-00.1.zip b/app/src/test/res/logger/AndroidAPS.2018-01-03_01-01-00.1.zip
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/app/src/test/res/logger/AndroidAPS.log b/app/src/test/res/logger/AndroidAPS.log
new file mode 100644
index 0000000000..e69de29bb2