package fr.nicolaspomepuy.androidwearcrashreport.mobile; import android.annotation.TargetApi; import android.app.Activity; import android.app.ApplicationErrorReport; import android.content.Context; import android.content.Intent; import android.os.Build; import android.os.Bundle; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.wearable.DataApi; import com.google.android.gms.wearable.DataEvent; import com.google.android.gms.wearable.DataEventBuffer; import com.google.android.gms.wearable.DataMapItem; import com.google.android.gms.wearable.Wearable; /** * Created by nicolas on 16/08/14. */ public class CrashReport implements DataApi.DataListener, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { private Context context; private GoogleApiClient mGoogleApiClient = null; private IOnCrashListener onCrashListener; private static CrashReport INSTANCE; private boolean autoReport; private Throwable currentException; private CrashInfo currentCrashInfo; public static CrashReport getInstance(Context context) { if (null == INSTANCE) { INSTANCE = new CrashReport(context); } return INSTANCE; } private CrashReport(Context context) { this.context = context; //Init the Google API client ot listen to crashes mGoogleApiClient = new GoogleApiClient.Builder(context) .addApi(Wearable.API) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .build(); mGoogleApiClient.connect(); } private CrashReport() { } public CrashReport(Context context, IOnCrashListener listener) { onCrashListener = listener; } @Override public void onConnected(Bundle bundle) { //Add the listener to listen to data Wearable.DataApi.addListener(mGoogleApiClient, this); } @Override public void onConnectionSuspended(int i) { } @Override public void onConnectionFailed(ConnectionResult connectionResult) { } @Override public void onDataChanged(DataEventBuffer dataEvents) { for (DataEvent event : dataEvents) { if (event.getType() == DataEvent.TYPE_CHANGED && event.getDataItem().getUri().getPath().contains("/EXCEPTION")) { //A new Exception has been received DataMapItem dataMapItem = DataMapItem.fromDataItem(event.getDataItem()); //Get the exception byte[] serializedException = dataMapItem.getDataMap().getByteArray("ex"); Throwable throwable = (Throwable) Utils.deserializeObject(serializedException); //Send it with the listener if (throwable != null) { if (onCrashListener != null) { currentCrashInfo = new CrashInfo.Builder(throwable) .fingerprint(dataMapItem.getDataMap().getString("fingerprint")) .manufacturer(dataMapItem.getDataMap().getString("manufacturer")) .model(dataMapItem.getDataMap().getString("model")) .product(dataMapItem.getDataMap().getString("product")) .versionCode(dataMapItem.getDataMap().getInt("versionCode")) .versionName(dataMapItem.getDataMap().getString("versionName")) .build(); onCrashListener.onCrashReceived(currentCrashInfo); } this.currentException = throwable; } } } } @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) public void reportToPlayStore(Context c) { if (currentCrashInfo == null || currentException == null) { return; } ApplicationErrorReport applicationErrorReport = new ApplicationErrorReport(); applicationErrorReport.packageName = this.context.getPackageName(); applicationErrorReport.processName = this.context.getPackageName(); applicationErrorReport.time = System.currentTimeMillis(); applicationErrorReport.systemApp = false; /////////// // CRASH // /////////// applicationErrorReport.type = ApplicationErrorReport.TYPE_CRASH; ApplicationErrorReport.CrashInfo crashInfo = new ApplicationErrorReport.CrashInfo(); crashInfo.exceptionClassName = currentException.getClass().getSimpleName(); crashInfo.exceptionMessage = currentException.getMessage(); crashInfo.stackTrace = currentCrashInfo.toString() + " - " +Utils.getStackTrace(currentException); StackTraceElement stackTraceElement = currentException.getStackTrace()[0]; crashInfo.throwClassName = stackTraceElement.getClassName(); crashInfo.throwFileName = stackTraceElement.getFileName(); crashInfo.throwMethodName = stackTraceElement.getMethodName(); crashInfo.throwLineNumber = stackTraceElement.getLineNumber(); applicationErrorReport.crashInfo = crashInfo; Intent i = new Intent(Intent.ACTION_APP_ERROR); i.putExtra(Intent.EXTRA_BUG_REPORT, applicationErrorReport); if (!(c instanceof Activity)) { i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } // Force "Send feedback choice", but still needs user acknowledgement i.setClassName("com.google.android.feedback", "com.google.android.feedback.FeedbackActivity"); c.startActivity(i); currentCrashInfo = null; currentException = null; } public void setOnCrashListener(IOnCrashListener onCrashListener) { this.onCrashListener = onCrashListener; } //Listener interface public interface IOnCrashListener { void onCrashReceived(CrashInfo crashInfo); } }