/* * Copyright (C) 2010 Mark Wyszomierski * * Portions Copyright (C) 2009 Xtralogic, Inc. */ package com.joelapenna.foursquared; import android.app.Activity; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.DialogInterface; import android.content.Intent; import android.content.res.Resources; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.util.Log; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; /** * This is taken from the android-log-collector project here: * * http://code.google.com/p/android-log-collector/ * * so as we can dump the last set of system logs from the user's device at the * bottom of their feedback email. If they are reporting a crash, the logs * might show exceptions etc. Android 2.2+ reports this directly to the marketplace * for us so this will be phased out eventually. * * @date July 8, 2010 * @author Mark Wyszomierski (markww@gmail.com) * */ public class SendLogActivity extends Activity { public final static String TAG = "com.xtralogic.android.logcollector";//$NON-NLS-1$ private static final String FEEDBACK_EMAIL_ADDRESS = "crashreport-android@foursquare.com"; public static final String ACTION_SEND_LOG = "com.xtralogic.logcollector.intent.action.SEND_LOG";//$NON-NLS-1$ public static final String EXTRA_SEND_INTENT_ACTION = "com.xtralogic.logcollector.intent.extra.SEND_INTENT_ACTION";//$NON-NLS-1$ public static final String EXTRA_DATA = "com.xtralogic.logcollector.intent.extra.DATA";//$NON-NLS-1$ public static final String EXTRA_ADDITIONAL_INFO = "com.xtralogic.logcollector.intent.extra.ADDITIONAL_INFO";//$NON-NLS-1$ public static final String EXTRA_SHOW_UI = "com.xtralogic.logcollector.intent.extra.SHOW_UI";//$NON-NLS-1$ public static final String EXTRA_FILTER_SPECS = "com.xtralogic.logcollector.intent.extra.FILTER_SPECS";//$NON-NLS-1$ public static final String EXTRA_FORMAT = "com.xtralogic.logcollector.intent.extra.FORMAT";//$NON-NLS-1$ public static final String EXTRA_BUFFER = "com.xtralogic.logcollector.intent.extra.BUFFER";//$NON-NLS-1$ private static final String LINE_SEPARATOR = System.getProperty("line.separator"); final int MAX_LOG_MESSAGE_LENGTH = 100000; private AlertDialog mMainDialog; private Intent mSendIntent; private CollectLogTask mCollectLogTask; private ProgressDialog mProgressDialog; private String mAdditonalInfo; private String[] mFilterSpecs; private String mFormat; private String mBuffer; @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); mSendIntent = new Intent(Intent.ACTION_SEND); mSendIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.send_log_message_subject)); mSendIntent.setType("text/plain"); Foursquared foursquared = (Foursquared)getApplication(); StringBuilder body = new StringBuilder(); Resources res = getResources(); body.append(res.getString(R.string.feedback_more)); body.append(LINE_SEPARATOR); body.append(res.getString(R.string.feedback_question_how_to_reproduce)); body.append(LINE_SEPARATOR); body.append(LINE_SEPARATOR); body.append(res.getString(R.string.feedback_question_expected_output)); body.append(LINE_SEPARATOR); body.append(LINE_SEPARATOR); body.append(res.getString(R.string.feedback_question_additional_information)); body.append(LINE_SEPARATOR); body.append(LINE_SEPARATOR); body.append("--------------------------------------"); body.append(LINE_SEPARATOR); body.append("ver: "); body.append(foursquared.getVersion()); body.append(LINE_SEPARATOR); body.append("user: "); body.append(foursquared.getUserId()); body.append(LINE_SEPARATOR); body.append("p: "); body.append(Build.MODEL); body.append(LINE_SEPARATOR); body.append("os: "); body.append(Build.VERSION.RELEASE); body.append(LINE_SEPARATOR); body.append("build#: "); body.append(Build.DISPLAY); body.append(LINE_SEPARATOR); body.append(LINE_SEPARATOR); mSendIntent.putExtra(Intent.EXTRA_SUBJECT, res.getString(R.string.feedback_subject)); mSendIntent.putExtra(Intent.EXTRA_EMAIL, new String[] { FEEDBACK_EMAIL_ADDRESS }); mSendIntent.setType("message/rfc822"); mAdditonalInfo = body.toString(); mFormat = "process"; collectAndSendLog(); } @SuppressWarnings("unchecked") void collectAndSendLog(){ /*Usage: logcat [options] [filterspecs] options include: -s Set default filter to silent. Like specifying filterspec '*:s' -f <filename> Log to file. Default to stdout -r [<kbytes>] Rotate log every kbytes. (16 if unspecified). Requires -f -n <count> Sets max number of rotated logs to <count>, default 4 -v <format> Sets the log print format, where <format> is one of: brief process tag thread raw time threadtime long -c clear (flush) the entire log and exit -d dump the log and then exit (don't block) -g get the size of the log's ring buffer and exit -b <buffer> request alternate ring buffer ('main' (default), 'radio', 'events') -B output the log in binary filterspecs are a series of <tag>[:priority] where <tag> is a log component tag (or * for all) and priority is: V Verbose D Debug I Info W Warn E Error F Fatal S Silent (supress all output) '*' means '*:d' and <tag> by itself means <tag>:v If not specified on the commandline, filterspec is set from ANDROID_LOG_TAGS. If no filterspec is found, filter defaults to '*:I' If not specified with -v, format is set from ANDROID_PRINTF_LOG or defaults to "brief"*/ ArrayList<String> list = new ArrayList<String>(); if (mFormat != null){ list.add("-v"); list.add(mFormat); } if (mBuffer != null){ list.add("-b"); list.add(mBuffer); } if (mFilterSpecs != null){ for (String filterSpec : mFilterSpecs){ list.add(filterSpec); } } mCollectLogTask = (CollectLogTask) new CollectLogTask().execute(list); } private class CollectLogTask extends AsyncTask<ArrayList<String>, Void, StringBuilder>{ @Override protected void onPreExecute(){ showProgressDialog(getString(R.string.send_log_acquiring_log_progress_dialog_message)); } @Override protected StringBuilder doInBackground(ArrayList<String>... params){ final StringBuilder log = new StringBuilder(); try{ ArrayList<String> commandLine = new ArrayList<String>(); commandLine.add("logcat");//$NON-NLS-1$ commandLine.add("-d");//$NON-NLS-1$ ArrayList<String> arguments = ((params != null) && (params.length > 0)) ? params[0] : null; if (null != arguments){ commandLine.addAll(arguments); } Process process = Runtime.getRuntime().exec(commandLine.toArray(new String[0])); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream())); String line; while ((line = bufferedReader.readLine()) != null){ log.append(line); log.append(LINE_SEPARATOR); } } catch (IOException e){ Log.e(TAG, "CollectLogTask.doInBackground failed", e);//$NON-NLS-1$ } return log; } @Override protected void onPostExecute(StringBuilder log){ if (null != log){ //truncate if necessary int keepOffset = Math.max(log.length() - MAX_LOG_MESSAGE_LENGTH, 0); if (keepOffset > 0){ log.delete(0, keepOffset); } if (mAdditonalInfo != null){ log.insert(0, mAdditonalInfo); } mSendIntent.putExtra(Intent.EXTRA_TEXT, log.toString()); startActivity(Intent.createChooser(mSendIntent, getString(R.string.send_log_chooser_title))); dismissProgressDialog(); dismissMainDialog(); finish(); } else{ dismissProgressDialog(); showErrorDialog(getString(R.string.send_log_failed_to_get_log_message)); } } } void showErrorDialog(String errorMessage){ new AlertDialog.Builder(this) .setTitle(getString(R.string.app_name)) .setMessage(errorMessage) .setIcon(android.R.drawable.ic_dialog_alert) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener(){ public void onClick(DialogInterface dialog, int whichButton){ finish(); } }) .show(); } void dismissMainDialog(){ if (null != mMainDialog && mMainDialog.isShowing()){ mMainDialog.dismiss(); mMainDialog = null; } } void showProgressDialog(String message){ mProgressDialog = new ProgressDialog(this); mProgressDialog.setIndeterminate(true); mProgressDialog.setMessage(message); mProgressDialog.setCancelable(true); mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener(){ public void onCancel(DialogInterface dialog){ cancellCollectTask(); finish(); } }); mProgressDialog.show(); } private void dismissProgressDialog(){ if (null != mProgressDialog && mProgressDialog.isShowing()) { mProgressDialog.dismiss(); mProgressDialog = null; } } void cancellCollectTask(){ if (mCollectLogTask != null && mCollectLogTask.getStatus() == AsyncTask.Status.RUNNING) { mCollectLogTask.cancel(true); mCollectLogTask = null; } } @Override protected void onPause(){ cancellCollectTask(); dismissProgressDialog(); dismissMainDialog(); super.onPause(); } }