Merge pull request #1262 from triplem/logshipper

Logshipper
This commit is contained in:
Milos Kozak 2018-08-02 14:40:54 +02:00 committed by GitHub
commit ddff05da75
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 578 additions and 56 deletions

View file

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

View file

@ -127,6 +127,16 @@
</intent-filter>
</receiver>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="info.nightscout.androidaps.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
<!-- Service processing incomming data -->
<service
android:name=".services.DataService"

View file

@ -51,13 +51,10 @@ import info.nightscout.androidaps.interfaces.PluginBase;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.logging.LogSettingActivity;
import info.nightscout.androidaps.plugins.ConfigBuilder.ProfileFunctions;
import info.nightscout.androidaps.plugins.Food.FoodPlugin;
import info.nightscout.androidaps.plugins.NSClientInternal.data.NSSettingsStatus;
import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin;
import info.nightscout.androidaps.setupwizard.SetupWizardActivity;
import info.nightscout.androidaps.tabs.TabPageAdapter;
import info.nightscout.utils.AndroidPermission;
import info.nightscout.utils.ImportExportPrefs;
import info.nightscout.utils.LocaleHelper;
import info.nightscout.utils.LogDialog;
import info.nightscout.utils.OKDialog;
@ -379,29 +376,6 @@ public class MainActivity extends AppCompatActivity {
case R.id.nav_setupwizard:
startActivity(new Intent(this, SetupWizardActivity.class));
return true;
case R.id.nav_resetdb:
new AlertDialog.Builder(this)
.setTitle(R.string.nav_resetdb)
.setMessage(R.string.reset_db_confirm)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
MainApp.getDbHelper().resetDatabases();
// should be handled by Plugin-Interface and
// additional service interface and plugin registry
FoodPlugin.getPlugin().getService().resetFood();
TreatmentsPlugin.getPlugin().getService().resetTreatments();
})
.create()
.show();
return true;
case R.id.nav_export:
ImportExportPrefs.verifyStoragePermissions(this);
ImportExportPrefs.exportSharedPreferences(this);
return true;
case R.id.nav_import:
ImportExportPrefs.verifyStoragePermissions(this);
ImportExportPrefs.importSharedPreferences(this);
return true;
case R.id.nav_show_logcat:
LogDialog.showLogcat(this);
return true;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,80 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="2dp"
tools:context="info.nightscout.androidaps.plugins.Maintenance.MaintenanceFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:id="@+id/log_send"
style="?android:attr/buttonStyle"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="3dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="3dp"
android:layout_weight="0.5"
android:text="@string/send_all_logs"
android:textColor="@color/colorTreatmentButton" />
<Button
android:id="@+id/log_delete"
style="?android:attr/buttonStyle"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="3dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="3dp"
android:layout_weight="0.5"
android:text="@string/delete_logs"
android:textColor="@color/colorTreatmentButton" />
<Button
android:id="@+id/nav_export"
style="?android:attr/buttonStyle"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="3dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="3dp"
android:layout_weight="0.5"
android:text="@string/nav_export"
android:textColor="@color/colorTreatmentButton" />
<Button
android:id="@+id/nav_import"
style="?android:attr/buttonStyle"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="3dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="3dp"
android:layout_weight="0.5"
android:text="@string/nav_import"
android:textColor="@color/colorTreatmentButton" />
<Button
android:id="@+id/nav_resetdb"
style="?android:attr/buttonStyle"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="3dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="3dp"
android:layout_weight="0.5"
android:text="@string/nav_resetdb"
android:textColor="@color/colorTreatmentButton" />
</LinearLayout>
</FrameLayout>

View file

@ -18,18 +18,6 @@
android:id="@+id/nav_setupwizard"
app:showAsAction="never"
android:title="@string/nav_setupwizard" />
<item
android:id="@+id/nav_resetdb"
app:showAsAction="never"
android:title="@string/nav_resetdb" />
<item
android:id="@+id/nav_export"
app:showAsAction="never"
android:title="@string/nav_export" />
<item
android:id="@+id/nav_import"
app:showAsAction="never"
android:title="@string/nav_import" />
<item
android:id="@+id/nav_show_logcat"
app:showAsAction="never"

View file

@ -500,6 +500,7 @@
<string name="copy_to_clipboard">Copy To Clipboard</string>
<string name="copied_to_clipboard">Copied to clipboard</string>
<string name="nav_show_logcat">Show log</string>
<string name="nav_export_log">Export Logs</string>
<string name="overview_calibration">Calibration</string>
<string name="send_calibration" formatted="false">Send calibration %.1f to xDrip?</string>
<string name="xdripnotinstalled">xDrip+ not installed</string>
@ -1164,6 +1165,18 @@
<string name="storedsettingsfound">Stored settings found</string>
<string name="allow_hardware_pump_text">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.</string>
<string name="error_adding_treatment_title">Treatment data incomplete</string>
<string name="maintenance_settings">Maintenance Settings</string>
<string name="maintenance_email">Email</string>
<string name="key_maintenance_logs_email" translatable="false">email</string>
<string name="invalid_email_message">Invalid Email</string>
<string name="key_maintenance_logs_amount" translatable="false">amount of logs to send</string>
<string name="maintenance_amount">No of Logs to send</string>
<string name="maintenance">Wartung</string>
<string name="maintenance_shortname">MAINT</string>
<string name="description_maintenance">Provides several functions for maintenance (eg. log sending, log deletion).</string>
<string name="send_all_logs">Send Logs by Email</string>
<string name="delete_logs">Delete Logs</string>
<string name="error_adding_treatment_message">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.</string>
<string name="generated_ecarbs_note">eCarbs: %1$d g (%2$d h), delay: %3$d m</string>
<string name="key_plugin_stats_report_timestamp" translatable="false">key_plugin_stats_report_timestamp</string>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="exports" path="exports/" />
</paths>

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:validatingEditText="http://schemas.android.com/apk/res/info.nightscout.androidaps">
<PreferenceCategory
android:key="maintenance"
android:title="@string/maintenance_settings">
<com.andreabaccega.widget.ValidatingEditTextPreference
android:defaultValue="logs@androidaps.org"
android:dialogMessage="@string/maintenance_email"
android:inputType="textEmailAddress"
android:key="@string/key_maintenance_logs_email"
android:selectAllOnFocus="true"
android:title="@string/maintenance_email"
validatingEditText:emptyAllowed="false"
validatingEditText:testErrorString="@string/invalid_email_message"
validatingEditText:testType="email"/>
<EditTextPreference
android:defaultValue="2"
android:inputType="number"
android:key="@string/key_maintenance_logs_amount"
android:title="@string/maintenance_amount">
</EditTextPreference>
</PreferenceCategory>
</PreferenceScreen>

View file

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

View file

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

View file