/** * This file is part of CrashCatcher library. * Copyright (c) 2014, Sibext Ltd. (http://www.sibext.com), * All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3.0 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License * for more details (http://www.gnu.org/licenses/lgpl-3.0.txt). * * You should have received a copy of the GNU Lesser General Public * License along with this library. */ package com.sibext.android.tools; import android.app.Activity; import android.os.AsyncTask; import android.os.Bundle; import android.os.Environment; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.*; import com.sibext.android.manager.CrashCatcherManager; import com.sibext.crashcatcher.R; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.Writer; /** * The Simple crash catcher activity for automatic send email report. * <p/> * <p> * </p> * * @author Nikolay Moskvin <moskvin@sibext.com> * @author Mike Osipov <osipov@sibext.com> */ public abstract class CatchActivity extends Activity { private static final String TAG = "[CCL] CrashCatcherActivity"; private static final String DEFAULT_CRASH_SUBJECT = "Crash report"; private static final String DEFAULT_SUBJECT = "MANUAL Report"; public static final String TRACE_INFO = "TRACE_INFO"; public static final String RESULT_EXTRA_TEXT = "RESULT_EXTRA_TEXT"; private final static String STORAGE_DIRECTORY = Environment.getExternalStorageDirectory() .toString(); private final static String SETTINGS_DIR_PROJECT = STORAGE_DIRECTORY + "/.settings"; final static String SETTINGS_DIR_LOG = STORAGE_DIRECTORY + "/.logcat"; private final static String PATH_TO_LOG = SETTINGS_DIR_LOG + "/logcat.txt"; private final static String PATH_TO_RESULT = SETTINGS_DIR_PROJECT + "/result.jpg"; private ProgressBar progressBar; private TextView statusText; private Button yes; private Button no; private EditText note; protected String currentReportId; private boolean isManual; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG, "onCreate: start"); setTitle(getString(R.string.crash_catcher)); setContentView(R.layout.com_sibext_crashcatcher_activity_crash_with_form); progressBar = (ProgressBar)findViewById(R.id.com_sibext_crashcatcher_crash_progress); statusText = (TextView)findViewById(R.id.com_sibext_crashcatcher_crash_status); final TextView titleText = (TextView)findViewById(R.id.com_sibext_crashcatcher_crash_error); yes = (Button)findViewById(R.id.com_sibext_crashcatcher_yes); no = (Button)findViewById(R.id.com_sibext_crashcatcher_no); note = (EditText)findViewById(R.id.com_sibext_crashcatcher_note); isManual = getIntent().getBooleanExtra(CrashCatcherManager.MANUAL_FLAG_KEY, false); titleText.setText(isManual ? R.string.manual_report_message : R.string.crash_message); note.setHint(isManual ? R.string.com_sibext_crashcatcher_manual_message_hint : R.string.com_sibext_crashcatcher_message_hint); no.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { KeyboardHelper.hide(v.getContext()); finish(); } }); yes.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { KeyboardHelper.hide(v.getContext()); yes.setClickable(false); no.setClickable(false); note.setEnabled(false); progressBar.setVisibility(View.VISIBLE); new CrashSendTask(isManual).execute(); } }); Log.d(TAG, "onCreate: finish"); } @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "onDestroy:"); no.setOnClickListener(null); yes.setOnClickListener(null); } abstract protected boolean onReportReadyForSend(String title, StringBuilder body, String resultPath, boolean isManual); protected void onReportSent() { onReportSent(getString(R.string.com_sibext_crashcatcher_status, currentReportId)); } protected void onReportSent(final String status) { progressBar.post(new Runnable() { @Override public void run() { progressBar.setVisibility(View.INVISIBLE); statusText.setVisibility(View.VISIBLE); note.setVisibility(View.INVISIBLE); statusText.setText(status); no.setVisibility(View.INVISIBLE); yes.setText(R.string.com_sibext_crashcatcher_exit); yes.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { finish(); } }); yes.setClickable(true); } }); } protected void onReportUnSent() { onReportUnSent(getString(R.string.com_sibext_crashcatcher_error)); } protected void onReportUnSent(final String reason) { progressBar.post(new Runnable() { @Override public void run() { progressBar.setVisibility(View.INVISIBLE); yes.setClickable(true); no.setClickable(true); note.setEnabled(true); Toast.makeText(CatchActivity.this, reason, Toast.LENGTH_LONG).show(); } }); } protected String getNotes() { return note.getText().toString(); } protected boolean hasNotes() { return getNotes().trim().length() != 0; } private String getPathResult() { return PATH_TO_RESULT; } private String getSubject() { return DEFAULT_SUBJECT; } private String getCrashSubject() { return DEFAULT_CRASH_SUBJECT; } private String getPathLog() { return PATH_TO_LOG; } private String getPathDirLog() { return SETTINGS_DIR_LOG; } private void captureLog() { final StringBuilder log = ReportHelper.getLog(); saveLogToFile(log); } private void saveLogToFile(StringBuilder builder) { File outputFile = new File(getPathLog()); if ( outputFile.exists() ) { outputFile.delete(); } Writer writer = null; try { writer = new BufferedWriter(new FileWriter(outputFile)); writer.write(builder.toString()); } catch (Exception e) { Log.e(TAG, "saveLogToFile failed", e); throw new CrashCatcherError("Error writing file on device"); } finally { try { writer.close(); } catch (Exception e) { } } } private String getFinalSubject(boolean isManuallyMode) { currentReportId = ReportHelper.generateReportID(); try { String versionName = getPackageManager() .getPackageInfo(getPackageName(), 0).versionName; return "[" + getPackageName() + " v" + versionName + "] " + ( isManuallyMode ? getSubject() : getCrashSubject() ) + " " + currentReportId; } catch (Exception e) { return "[" + getPackageName() + " NO VERSION] " + getSubject() + " " + currentReportId; } } private String getExtraText() { return getIntent().getStringExtra(RESULT_EXTRA_TEXT); } private boolean hasExtraText() { return getIntent().getStringExtra(RESULT_EXTRA_TEXT) != null; } private class CrashSendTask extends AsyncTask<Void, Void, Boolean> { private final boolean isManuallyMode; private StringBuilder body = new StringBuilder(""); public CrashSendTask(boolean isManuallyMode) { this.isManuallyMode = isManuallyMode; } @Override protected Boolean doInBackground(Void... params) { try { Log.i(TAG, "Capture log..."); captureLog(); } catch (CrashCatcherError e) { Log.e(TAG, "ERROR when capture log!"); body.append(e.getMessage()).append("\n"); return false; } return true; } @Override protected void onPostExecute(Boolean success) { if ( isCancelled() ) { return; } if ( hasExtraText() ) { body.append(getExtraText()); } body.append("\n"); if ( !isManuallyMode ) { body.append(" Error: ").append(getIntent().getStringExtra(TRACE_INFO)); } else { body.append("Note: Manually sending"); } final String title = getFinalSubject(isManuallyMode); if ( onReportReadyForSend(title, body, getPathLog(), isManuallyMode) ) { finish(); } } } }