From cdf67c3296b9a7aadf3528ed3eed29cdac752dde Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 11 Sep 2015 03:08:53 +0200 Subject: share rather than save log files (OKC-01-015) --- .../operations/results/OperationResult.java | 80 +++++++-- .../keychain/ui/LogDisplayFragment.java | 189 ++++----------------- 2 files changed, 94 insertions(+), 175 deletions(-) (limited to 'OpenKeychain/src/main/java') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java index 6e9aca30d..41691933e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java @@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.operations.results; import android.app.Activity; import android.content.Intent; +import android.content.res.Resources; import android.os.Parcel; import android.os.Parcelable; import android.support.annotation.NonNull; @@ -52,6 +53,8 @@ import java.util.List; */ public abstract class OperationResult implements Parcelable { + final static String INDENTATION_WHITESPACE = " "; + public static final String EXTRA_RESULT = "operation_result"; /** @@ -166,6 +169,27 @@ public abstract class OperationResult implements Parcelable { ", mIndent=" + mIndent + '}'; } + + StringBuilder getPrintableLogEntry(Resources resources, int indent) { + + StringBuilder result = new StringBuilder(); + int padding = mIndent +indent; + if (padding > INDENTATION_WHITESPACE.length()) { + padding = INDENTATION_WHITESPACE.length(); + } + result.append(INDENTATION_WHITESPACE, 0, padding); + result.append(LOG_LEVEL_NAME[mType.mLevel.ordinal()]).append(' '); + + // special case: first parameter may be a quantity + if (mParameters != null && mParameters.length > 0 && mParameters[0] instanceof Integer) { + result.append(resources.getQuantityString(mType.getMsgId(), (Integer) mParameters[0], mParameters)); + } else { + result.append(resources.getString(mType.getMsgId(), mParameters)); + } + + return result; + } + } public static class SubLogEntryParcel extends LogEntryParcel { @@ -202,6 +226,17 @@ public abstract class OperationResult implements Parcelable { dest.writeParcelable(mSubResult, 0); } + @Override + StringBuilder getPrintableLogEntry(Resources resources, int indent) { + + LogEntryParcel subEntry = mSubResult.getLog().getLast(); + if (subEntry != null) { + return subEntry.getPrintableLogEntry(resources, mIndent +indent); + } else { + return super.getPrintableLogEntry(resources, indent); + } + } + } public Showable createNotify(final Activity activity) { @@ -245,15 +280,15 @@ public abstract class OperationResult implements Parcelable { } return Notify.create(activity, logText, Notify.LENGTH_LONG, style, - new ActionListener() { - @Override - public void onAction() { - Intent intent = new Intent( - activity, LogDisplayActivity.class); - intent.putExtra(LogDisplayFragment.EXTRA_RESULT, OperationResult.this); - activity.startActivity(intent); - } - }, R.string.snackbar_details); + new ActionListener() { + @Override + public void onAction() { + Intent intent = new Intent( + activity, LogDisplayActivity.class); + intent.putExtra(LogDisplayFragment.EXTRA_RESULT, OperationResult.this); + activity.startActivity(intent); + } + }, R.string.snackbar_details); } @@ -803,14 +838,7 @@ public abstract class OperationResult implements Parcelable { MSG_LV_FETCH_ERROR_URL (LogLevel.ERROR, R.string.msg_lv_fetch_error_url), MSG_LV_FETCH_ERROR_IO (LogLevel.ERROR, R.string.msg_lv_fetch_error_io), MSG_LV_FETCH_ERROR_FORMAT(LogLevel.ERROR, R.string.msg_lv_fetch_error_format), - MSG_LV_FETCH_ERROR_NOTHING (LogLevel.ERROR, R.string.msg_lv_fetch_error_nothing), - - //export log - MSG_EXPORT_LOG(LogLevel.START,R.string.msg_export_log_start), - MSG_EXPORT_LOG_EXPORT_ERROR_NO_FILE(LogLevel.ERROR,R.string.msg_export_log_error_no_file), - MSG_EXPORT_LOG_EXPORT_ERROR_FOPEN(LogLevel.ERROR,R.string.msg_export_log_error_fopen), - MSG_EXPORT_LOG_EXPORT_ERROR_WRITING(LogLevel.ERROR,R.string.msg_export_log_error_writing), - MSG_EXPORT_LOG_EXPORT_SUCCESS (LogLevel.OK, R.string.msg_export_log_success); + MSG_LV_FETCH_ERROR_NOTHING (LogLevel.ERROR, R.string.msg_lv_fetch_error_nothing); public final int mMsgId; public final LogLevel mLevel; @@ -833,6 +861,10 @@ public abstract class OperationResult implements Parcelable { OK, // should occur once at the end of a successful operation CANCELLED, // should occur once at the end of a cancelled operation } + // for print of debug log. keep those in sync with above! + static final String[] LOG_LEVEL_NAME = new String[] { + "[DEBUG]", "[INFO]", "[WARN]", "[ERROR]", "[START]", "[OK]", "[CANCEL]" + }; @Override public int describeContents() { @@ -931,6 +963,20 @@ public abstract class OperationResult implements Parcelable { public Iterator iterator() { return mParcels.iterator(); } + + /** + * returns an indented String of an entire OperationLog + * @param indent padding to add at the start of all log entries, made for use with SubLogs + * @return printable, indented version of passed operationLog + */ + public String getPrintableOperationLog(Resources resources, int indent) { + StringBuilder log = new StringBuilder(); + for (LogEntryParcel entry : this) { + log.append(entry.getPrintableLogEntry(resources, indent)).append("\n"); + } + return log.toString().substring(0, log.length() -1); // get rid of extra new line + } + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java index 4de83337e..43c8d2643 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -18,11 +18,12 @@ package org.sufficientlysecure.keychain.ui; +import android.app.Activity; import android.content.Context; import android.content.Intent; import android.graphics.Color; +import android.net.Uri; import android.os.Bundle; -import android.os.Parcel; import android.support.v4.app.ListFragment; import android.util.TypedValue; import android.view.LayoutInflater; @@ -37,19 +38,19 @@ import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.TextView; -import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEntryParcel; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogLevel; import org.sufficientlysecure.keychain.operations.results.OperationResult.SubLogEntryParcel; +import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider; import org.sufficientlysecure.keychain.ui.util.FormattingUtils; -import org.sufficientlysecure.keychain.util.FileHelper; -import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.ui.util.Notify; +import org.sufficientlysecure.keychain.ui.util.Notify.Style; + +import java.io.IOException; +import java.io.OutputStream; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.PrintWriter; public class LogDisplayFragment extends ListFragment implements OnItemClickListener { @@ -60,6 +61,8 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe public static final String EXTRA_RESULT = "log"; protected int mTextColor; + private Uri mLogTempFile; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -118,170 +121,40 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_log_display_export_log: - exportLog(); + shareLog(); break; } return super.onOptionsItemSelected(item); } - private void exportLog() { - showExportLogDialog(new File(Constants.Path.APP_DIR, "export.log")); - } - - private void writeToLogFile(final OperationResult.OperationLog operationLog, final File f) { - OperationResult.OperationLog currLog = new OperationResult.OperationLog(); - currLog.add(OperationResult.LogType.MSG_EXPORT_LOG, 0); - - boolean error = false; - - PrintWriter pw = null; - try { - pw = new PrintWriter(f); - pw.print(getPrintableOperationLog(operationLog, "")); - if (pw.checkError()) {//IOException - Log.e(Constants.TAG, "Log Export I/O Exception " + f.getAbsolutePath()); - currLog.add(OperationResult.LogType.MSG_EXPORT_LOG_EXPORT_ERROR_WRITING, 1); - error = true; - } - } catch (FileNotFoundException e) { - Log.e(Constants.TAG, "File not found for exporting log " + f.getAbsolutePath()); - currLog.add(OperationResult.LogType.MSG_EXPORT_LOG_EXPORT_ERROR_FOPEN, 1); - error = true; - } - if (pw != null) { - pw.close(); - if (!error && pw.checkError()) {//check if it is only pw.close() which generated error - currLog.add(OperationResult.LogType.MSG_EXPORT_LOG_EXPORT_ERROR_WRITING, 1); - error = true; - } - } - - if (!error) { - currLog.add(OperationResult.LogType.MSG_EXPORT_LOG_EXPORT_SUCCESS, 1); - } - - int opResultCode = error ? OperationResult.RESULT_ERROR : OperationResult.RESULT_OK; - OperationResult opResult = new LogExportResult(opResultCode, currLog); - opResult.createNotify(getActivity()).show(); - } - - /** - * returns an indented String of an entire OperationLog - * - * @param opLog log to be converted to indented, printable format - * @param basePadding padding to add at the start of all log entries, made for use with SubLogs - * @return printable, indented version of passed operationLog - */ - private String getPrintableOperationLog(OperationResult.OperationLog opLog, String basePadding) { - String log = ""; - for (LogEntryParcel anOpLog : opLog) { - log += getPrintableLogEntry(anOpLog, basePadding) + "\n"; - } - log = log.substring(0, log.length() - 1);//gets rid of extra new line - return log; - } - - /** - * returns an indented String of a LogEntryParcel including any sub-logs it may contain - * - * @param entryParcel log entryParcel whose String representation is to be obtained - * @return indented version of passed log entryParcel in a readable format - */ - private String getPrintableLogEntry(OperationResult.LogEntryParcel entryParcel, - String basePadding) { - - final String indent = " ";//4 spaces = 1 Indent level - - String padding = basePadding; - for (int i = 0; i < entryParcel.mIndent; i++) { - padding += indent; - } - String logText = padding; - - switch (entryParcel.mType.mLevel) { - case DEBUG: - logText += "[DEBUG]"; - break; - case INFO: - logText += "[INFO]"; - break; - case WARN: - logText += "[WARN]"; - break; - case ERROR: - logText += "[ERROR]"; - break; - case START: - logText += "[START]"; - break; - case OK: - logText += "[OK]"; - break; - case CANCELLED: - logText += "[CANCELLED]"; - break; - } + private void shareLog() { - // special case: first parameter may be a quantity - if (entryParcel.mParameters != null && entryParcel.mParameters.length > 0 - && entryParcel.mParameters[0] instanceof Integer) { - logText += getResources().getQuantityString(entryParcel.mType.getMsgId(), - (Integer) entryParcel.mParameters[0], - entryParcel.mParameters); - } else { - logText += getResources().getString(entryParcel.mType.getMsgId(), - entryParcel.mParameters); + Activity activity = getActivity(); + if (activity == null) { + return; } - if (entryParcel instanceof SubLogEntryParcel) { - OperationResult subResult = ((SubLogEntryParcel) entryParcel).getSubResult(); - LogEntryParcel subEntry = subResult.getLog().getLast(); - if (subEntry != null) { - //the first line of log of subResult is same as entryParcel, so replace logText - logText = getPrintableOperationLog(subResult.getLog(), padding); + String log = mResult.getLog().getPrintableOperationLog(getResources(), 0); + + // if there is no log temp file yet, create one + if (mLogTempFile == null) { + mLogTempFile = TemporaryStorageProvider.createFile(getActivity(), "openkeychain_log.txt", "text/plain"); + try { + OutputStream outputStream = activity.getContentResolver().openOutputStream(mLogTempFile); + outputStream.write(log.getBytes()); + } catch (IOException e) { + Notify.create(activity, R.string.error_log_share_internal, Style.ERROR).show(); + return; } } - return logText; - } - - private void showExportLogDialog(final File exportFile) { - - String title = this.getString(R.string.title_export_log); - - String message = this.getString(R.string.specify_file_to_export_log_to); - - FileHelper.saveDocumentDialog(new FileHelper.FileDialogCallback() { - @Override - public void onFileSelected(File file, boolean checked) { - writeToLogFile(mResult.getLog(), file); - } - }, this.getActivity().getSupportFragmentManager(), title, message, exportFile, null); - } - - private static class LogExportResult extends OperationResult { - - public static Creator CREATOR = new Creator() { - public LogExportResult createFromParcel(final Parcel source) { - return new LogExportResult(source); - } - - public LogExportResult[] newArray(final int size) { - return new LogExportResult[size]; - } - }; - - public LogExportResult(int result, OperationLog log) { - super(result, log); - } + Intent intent = new Intent(Intent.ACTION_SEND); + intent.putExtra(Intent.EXTRA_STREAM, mLogTempFile); + intent.setType("text/plain"); + intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + startActivity(intent); - /** - * trivial but necessary to implement the Parcelable protocol. - */ - public LogExportResult(Parcel source) { - super(source); - } } @Override -- cgit v1.2.3