diff --git a/app/src/main/java/info/nightscout/androidaps/MainActivity.java b/app/src/main/java/info/nightscout/androidaps/MainActivity.java index b30fab68cd..fe66b95ab0 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/MainActivity.java @@ -399,24 +399,6 @@ public class MainActivity extends AppCompatActivity { return true; case R.id.nav_show_logcat: LogDialog.showLogcat(this); - return true; - case R.id.nav_export_log: - String recipient = "mmay@gmx.net"; - - String logDirectory = LoggerUtils.getLogDirectory(); - List logs = LoggerUtils.getLogfiles(logDirectory, 2); - - File zipDir = this.getExternalFilesDir("exports"); - File zipFile = new File(zipDir, LoggerUtils.constructName()); - - log.debug("zipFile: {}", zipFile.getAbsolutePath()); - File zip = LoggerUtils.zipLogs(zipFile, logs); - - Uri attachementUri = FileProvider.getUriForFile(this, "info.nightscout.androidaps.fileprovider", zip); - Intent emailIntent = LoggerUtils.sendMail(attachementUri, recipient, "Log Export"); - log.debug("sending emailIntent"); - startActivity(emailIntent); - return true; case R.id.nav_about: AlertDialog.Builder builder = new AlertDialog.Builder(this); diff --git a/app/src/main/java/info/nightscout/androidaps/MainApp.java b/app/src/main/java/info/nightscout/androidaps/MainApp.java index de800b8468..55dbb37447 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainApp.java +++ b/app/src/main/java/info/nightscout/androidaps/MainApp.java @@ -41,6 +41,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.LogShipper.LogShipperPlugin; import info.nightscout.androidaps.plugins.Loop.LoopPlugin; import info.nightscout.androidaps.plugins.NSClientInternal.NSClientPlugin; import info.nightscout.androidaps.plugins.NSClientInternal.receivers.AckAlarmReceiver; @@ -190,6 +191,7 @@ public class MainApp extends Application { pluginsList.add(StatuslinePlugin.initPlugin(this)); pluginsList.add(PersistentNotificationPlugin.getPlugin()); pluginsList.add(NSClientPlugin.getPlugin()); + pluginsList.add(LogShipperPlugin.initPlugin(this)); pluginsList.add(sConfigBuilder = ConfigBuilderPlugin.getPlugin()); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/LogShipper/LogShipperFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/LogShipper/LogShipperFragment.java new file mode 100644 index 0000000000..c893af0c4d --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/LogShipper/LogShipperFragment.java @@ -0,0 +1,38 @@ +package info.nightscout.androidaps.plugins.LogShipper; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import info.nightscout.androidaps.R; + +/** + * + */ +public class LogShipperFragment extends Fragment { + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.logshipper_fragment, container, false); + + view.findViewById(R.id.log_send).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + LogShipperPlugin.getPlugin().sendLogs(); + } + }); + + view.findViewById(R.id.log_delete).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + LogShipperPlugin.getPlugin().deleteLogs(); + } + }); + + return view; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/LogShipper/LogShipperPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/LogShipper/LogShipperPlugin.java new file mode 100644 index 0000000000..8553fbe720 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/LogShipper/LogShipperPlugin.java @@ -0,0 +1,276 @@ +package info.nightscout.androidaps.plugins.LogShipper; + +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 LogShipperPlugin extends PluginBase { + + private static final Logger LOG = LoggerFactory.getLogger(LogShipperPlugin.class); + + private final Context ctx; + + private static LogShipperPlugin logShipperPlugin; + + public static LogShipperPlugin getPlugin() { + return logShipperPlugin; + } + + public static LogShipperPlugin initPlugin(Context ctx) { + + if (logShipperPlugin == null) { + logShipperPlugin = new LogShipperPlugin(ctx); + } + + return logShipperPlugin; + } + + public LogShipperPlugin() { + // required for testing + super(null); + this.ctx = null; + } + + LogShipperPlugin(Context ctx) { + super(new PluginDescription() + .mainType(PluginType.GENERAL) + .fragmentClass(LogShipperFragment.class.getName()) + .alwayVisible(true) + .alwaysEnabled(true) + .pluginName(R.string.logshipper) + .shortName(R.string.logship_shortname) + .preferencesId(R.xml.pref_logshipper) + .description(R.string.description_logship) + ); + this.ctx = ctx; + } + + @Override + protected void onStart() { + super.onStart(); + } + + @Override + protected void onStop() { + + } + + public void sendLogs() { + String recipient = SP.getString("key_logshipper_email", "logs@androidaps.org"); + int amount = SP.getInt("key_logshipper_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); + } + + public void deleteLogs() { + String logDirectory = LoggerUtils.getLogDirectory(); + File logDir = new File(logDirectory); + + File[] files = logDir.listFiles(new FilenameFilter() { + @Override + public boolean accept(File file, String name) { + return name.startsWith("AndroidAPS") + && name.endsWith(".zip"); + } + }); + + Arrays.sort(files, new Comparator() { + public int compare(File f1, File f2) { + return 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(new FilenameFilter() { + @Override + public boolean accept(File file, String name) { + return name.startsWith("AndroidAPS") + && (name.endsWith(".log") + || (name.endsWith(".zip") && !name.endsWith(LoggerUtils.SUFFIX))); + } + }); + + Arrays.sort(files, new Comparator() { + public int compare(File f1, File f2) { + return 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); + } + + /** + * Zips the given files in a zipfile which is stored in the given zipDir using the givven + * name. + * + * @param zipFile + * @param files + * @return + */ + 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; + } + + /** + * This method stores all given files inside the given zipFile. + * + * @param zipFile + * @param files + * @throws IOException + */ + 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/LoggerUtils.java b/app/src/main/java/info/nightscout/utils/LoggerUtils.java index 8564c3300c..60d9251181 100644 --- a/app/src/main/java/info/nightscout/utils/LoggerUtils.java +++ b/app/src/main/java/info/nightscout/utils/LoggerUtils.java @@ -1,25 +1,8 @@ package info.nightscout.utils; -import android.content.Intent; -import android.net.Uri; - 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 ch.qos.logback.classic.LoggerContext; /** @@ -29,7 +12,7 @@ public class LoggerUtils { private static final Logger LOGGER = LoggerFactory.getLogger(LoggerUtils.class); - private static String SUFFIX = ".log.zip"; + public static String SUFFIX = ".log.zip"; /** * Returns the directory, in which the logs are stored on the system. This is configured in the @@ -42,136 +25,4 @@ public class LoggerUtils { return lc.getProperty("EXT_FILES_DIR"); } - - /** - * 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 static List getLogfiles(String directory, int amount) { - LOGGER.debug("getting {} logs from directory {}", amount, directory); - File logDir = new File(directory); - - File[] files = logDir.listFiles(new FilenameFilter() { - @Override - public boolean accept(File file, String name) { - return name.startsWith("AndroidAPS") - && (name.endsWith(".log") - || (name.endsWith(".zip") && !name.endsWith(SUFFIX))); - } - }); - - Arrays.sort(files, new Comparator() { - public int compare(File f1, File f2) { - return f2.getName().compareTo(f1.getName()); - } - }); - - List result = Arrays.asList(files); - int toIndex = amount++; - - if (toIndex > result.size()) { - toIndex = result.size(); - } - - LOGGER.debug("returning sublist 0 to {}", toIndex); - return result.subList(0, toIndex); - } - - /** - * Zips the given files in a zipfile which is stored in the given zipDir using the givven - * name. - * - * @param zipFile - * @param files - * @return - */ - public static File zipLogs(File zipFile, List files) { - LOGGER.debug("creating zip {}", zipFile.getAbsolutePath()); - - try { - zip(zipFile, files); - } catch (IOException e) { - LOGGER.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 static String constructName() { - return "AndroidAPS_LOG_" + String.valueOf(new Date().getTime()) + SUFFIX; - } - - /** - * This method stores all given files inside the given zipFile. - * - * @param zipFile - * @param files - * @throws IOException - */ - public static 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) { - LOGGER.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, ""); - - LOGGER.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/res/layout/logshipper_fragment.xml b/app/src/main/res/layout/logshipper_fragment.xml new file mode 100644 index 0000000000..d3cbad8181 --- /dev/null +++ b/app/src/main/res/layout/logshipper_fragment.xml @@ -0,0 +1,41 @@ + + + + +