From da751b7c9c1940a070f3a3c684039c468a74f0d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduard=20Ereza=20Mart=C3=ADnez?= Date: Mon, 21 Sep 2015 18:19:38 +0200 Subject: [PATCH] Allow specifying a drawable or mipmap to use on the default error activity with a method. --- README.md | 35 ++++++++++------ .../CustomActivityOnCrash.java | 31 ++++++++++++++ .../activity/DefaultErrorActivity.java | 40 ++++++++++++------- ...activityoncrash_default_error_activity.xml | 1 + library/src/main/res/values/strings.xml | 1 + .../SampleCrashingApplication.java | 4 ++ 6 files changed, 85 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 27dc08d..23d099b 100644 --- a/README.md +++ b/README.md @@ -56,33 +56,40 @@ CustomActivityOnCrash.setLaunchErrorActivityWhenInBackground(boolean); ``` This method defines if the error activity should be launched when the app crashes while on background. By default, this is true. On API<14, it's always true since there is no way to detect if the app is in foreground. -If you set it to false, a crash while in background won't launch the error activity nor the system dialog, so it will be a silent crash. -The default is true. +If you set it to `false`, a crash while in background won't launch the error activity nor the system dialog, so it will be a silent crash. +The default is `true`. ```java CustomActivityOnCrash.setShowErrorDetails(boolean); ``` This method defines if the error activity must show a button with error details. -If you set it to false, the button on the default error activity will disappear, thus disabling the user from seeing the stack trace. -The default is true. +If you set it to `false`, the button on the default error activity will disappear, thus disabling the user from seeing the stack trace. +The default is `true`. + +```java +CustomActivityOnCrash.setDefaultErrorActivityDrawable(int); +``` +This method allows changing the default upside-down bug image with an image of your choice. +You may pass a resource id for a drawable or a mipmap. +The default is `R.drawable.customactivityoncrash_error_image`. ```java CustomActivityOnCrash.setEnableAppRestart(boolean); ``` This method defines if the error activity must show a "Restart app" button or a "Close app" button. -If you set it to false, the button on the default error activity will close the app instead of restarting. -Warning! If you set it to true, there is the possibility of it still displaying the "Close app" button, +If you set it to `false`, the button on the default error activity will close the app instead of restarting. +Warning! If you set it to `true`, there is the possibility of it still displaying the "Close app" button, if no restart activity is specified or found! -The default is true. +The default is `true`. ```java CustomActivityOnCrash.setRestartActivityClass(Class); ``` This method sets the activity that must be launched by the error activity when the user presses the button to restart the app. If you don't set it (or set it to null), the library will use the first activity on your manifest that has an intent-filter with action -cat.ereza.customactivityoncrash.RESTART, and if there is none, the default launchable activity on your app. +`cat.ereza.customactivityoncrash.RESTART`, and if there is none, the default launchable activity on your app. If no launchable activity can be found and you didn't specify any, the "restart app" button will become a "close app" button, -even if setEnableAppRestart is set to true. +even if `setEnableAppRestart` is set to `true`. As noted, you can also use the following intent-filter to specify the restart activity: ```xml @@ -98,7 +105,7 @@ CustomActivityOnCrash.setErrorActivityClass(Class); This method allows you to set a custom error activity to be launched, instead of the default one. Use it if you need further customization that is not just strings, colors or themes (see below). If you don't set it (or set it to null), the library will use first activity on your manifest that has an intent-filter with action -cat.ereza.customactivityoncrash.ERROR, and if there is none, a default error activity from the library. +`cat.ereza.customactivityoncrash.ERROR`, and if there is none, a default error activity from the library. If you use this, the activity **must** be declared in your `AndroidManifest.xml`, with `process` set to `:error_activity`. Example: @@ -128,6 +135,7 @@ You can override the default error activity theme by defining a theme in your ap *Image:* By default, an image of a bug is displayed. You can change it to any image by creating a `customactivityoncrash_error_image` drawable on all density buckets (mdpi, hdpi, xhdpi, xxhdpi and xxxhdpi). +You can also use the provided `CustomActivityOnCrash.setDefaultErrorActivityDrawable(int)` method. *Strings:* @@ -140,6 +148,9 @@ You can provide new strings and translations for the default error activity stri Error details Error details Close + Copy to clipboard + Copied to clipboard + Error information ``` *There is a `sample` project module with examples of these overrides. If in doubt, check the code in that module.* @@ -161,13 +172,13 @@ Returns several error details including the stack trace that caused the error, a ```java CustomActivityOnCrash.getRestartActivityClassFromIntent(getIntent()); ``` -Returns the class of the activity you have to launch to restart the app, or null if not set. +Returns the class of the activity you have to launch to restart the app, or `null` if not set. ```java CustomActivityOnCrash.restartApplicationWithIntent(activity, intent); ``` Kills the current process and restarts the app again with an `startActivity()` to the passed intent. -You **MUST** call this to restart the app, or you will end up having several Application class instances and experience multiprocess issues in API<17. +You **MUST** call this to restart the app, or you will end up having several `Application` class instances and experience multiprocess issues in API<17. ```java CustomActivityOnCrash.closeApplication(activity); diff --git a/library/src/main/java/cat/ereza/customactivityoncrash/CustomActivityOnCrash.java b/library/src/main/java/cat/ereza/customactivityoncrash/CustomActivityOnCrash.java index accc82e..1521eb3 100644 --- a/library/src/main/java/cat/ereza/customactivityoncrash/CustomActivityOnCrash.java +++ b/library/src/main/java/cat/ereza/customactivityoncrash/CustomActivityOnCrash.java @@ -50,6 +50,7 @@ public final class CustomActivityOnCrash { private static final String EXTRA_RESTART_ACTIVITY_CLASS = "cat.ereza.customactivityoncrash.EXTRA_RESTART_ACTIVITY_CLASS"; private static final String EXTRA_SHOW_ERROR_DETAILS = "cat.ereza.customactivityoncrash.EXTRA_SHOW_ERROR_DETAILS"; private static final String EXTRA_STACK_TRACE = "cat.ereza.customactivityoncrash.EXTRA_STACK_TRACE"; + private static final String EXTRA_IMAGE_DRAWABLE_ID = "cat.ereza.customactivityoncrash.EXTRA_IMAGE_DRAWABLE_ID"; //General constants private final static String TAG = "CustomActivityOnCrash"; @@ -68,6 +69,7 @@ public final class CustomActivityOnCrash { private static boolean launchErrorActivityWhenInBackground = true; private static boolean showErrorDetails = true; private static boolean enableAppRestart = true; + private static int defaultErrorActivityDrawableId = R.drawable.customactivityoncrash_error_image; private static Class errorActivityClass = null; private static Class restartActivityClass = null; @@ -138,6 +140,7 @@ public void uncaughtException(Thread thread, final Throwable throwable) { intent.putExtra(EXTRA_STACK_TRACE, stackTraceString); intent.putExtra(EXTRA_RESTART_ACTIVITY_CLASS, restartActivityClass); intent.putExtra(EXTRA_SHOW_ERROR_DETAILS, showErrorDetails); + intent.putExtra(EXTRA_IMAGE_DRAWABLE_ID, defaultErrorActivityDrawableId); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); application.startActivity(intent); } @@ -222,6 +225,16 @@ public static boolean isShowErrorDetailsFromIntent(Intent intent) { return intent.getBooleanExtra(CustomActivityOnCrash.EXTRA_SHOW_ERROR_DETAILS, true); } + /** + * Given an Intent, returns the drawable id of the image to show on the default error activity. + * + * @param intent The Intent. Must not be null. + * @return The id of the drawable to use. + */ + public static int getDefaultErrorActivityDrawableIdFromIntent(Intent intent) { + return intent.getIntExtra(CustomActivityOnCrash.EXTRA_IMAGE_DRAWABLE_ID, R.drawable.customactivityoncrash_error_image); + } + /** * Given an Intent, returns the stack trace extra from it. * @@ -347,6 +360,24 @@ public static void setShowErrorDetails(boolean showErrorDetails) { CustomActivityOnCrash.showErrorDetails = showErrorDetails; } + /** + * Returns the default error activity drawable identifier. + * + * @return the default error activity drawable identifier + */ + public static int getDefaultErrorActivityDrawable() { + return defaultErrorActivityDrawableId; + } + + /** + * Defines which drawable to use in the default error activity image. + * Set this if you want to use an image other than the default one. + * The default is R.drawable.customactivityoncrash_error_image (a cute upside-down bug). + */ + public static void setDefaultErrorActivityDrawable(int defaultErrorActivityDrawableId) { + CustomActivityOnCrash.defaultErrorActivityDrawableId = defaultErrorActivityDrawableId; + } + /** * Returns if the error activity should show a restart button. * Note that even if restart is enabled, a valid restart activity could not be found. diff --git a/library/src/main/java/cat/ereza/customactivityoncrash/activity/DefaultErrorActivity.java b/library/src/main/java/cat/ereza/customactivityoncrash/activity/DefaultErrorActivity.java index 0a68cf3..0218cea 100644 --- a/library/src/main/java/cat/ereza/customactivityoncrash/activity/DefaultErrorActivity.java +++ b/library/src/main/java/cat/ereza/customactivityoncrash/activity/DefaultErrorActivity.java @@ -16,7 +16,6 @@ package cat.ereza.customactivityoncrash.activity; -import android.annotation.SuppressLint; import android.app.Activity; import android.app.AlertDialog; import android.content.ClipData; @@ -28,9 +27,10 @@ import android.util.TypedValue; import android.view.View; import android.widget.Button; +import android.widget.ImageView; import android.widget.TextView; - import android.widget.Toast; + import cat.ereza.customactivityoncrash.CustomActivityOnCrash; import cat.ereza.customactivityoncrash.R; @@ -82,12 +82,13 @@ public void onClick(View v) { .setMessage(CustomActivityOnCrash.getAllErrorDetailsFromIntent(DefaultErrorActivity.this, getIntent())) .setPositiveButton(R.string.customactivityoncrash_error_activity_error_details_close, null) .setNeutralButton(R.string.customactivityoncrash_error_activity_error_details_copy, - new DialogInterface.OnClickListener() { - @Override public void onClick(DialogInterface dialog, int which) { - copyErrorToClipboard(); - Toast.makeText(DefaultErrorActivity.this, R.string.customactivityoncrash_error_activity_error_details_copied, Toast.LENGTH_SHORT).show(); - } - }) + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + copyErrorToClipboard(); + Toast.makeText(DefaultErrorActivity.this, R.string.customactivityoncrash_error_activity_error_details_copied, Toast.LENGTH_SHORT).show(); + } + }) .show(); TextView textView = (TextView) dialog.findViewById(android.R.id.message); textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimension(R.dimen.customactivityoncrash_error_activity_error_details_text_size)); @@ -96,20 +97,29 @@ public void onClick(View v) { } else { moreInfoButton.setVisibility(View.GONE); } + + int defaultErrorActivityDrawableId = CustomActivityOnCrash.getDefaultErrorActivityDrawableIdFromIntent(getIntent()); + ImageView errorImageView = ((ImageView) findViewById(R.id.customactivityoncrash_error_activity_image)); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + errorImageView.setImageDrawable(getResources().getDrawable(defaultErrorActivityDrawableId, getTheme())); + } else { + //noinspection deprecation + errorImageView.setImageDrawable(getResources().getDrawable(defaultErrorActivityDrawableId)); + } } - @SuppressLint("NewApi") private void copyErrorToClipboard() { String errorInformation = - CustomActivityOnCrash.getAllErrorDetailsFromIntent(DefaultErrorActivity.this, getIntent()); + CustomActivityOnCrash.getAllErrorDetailsFromIntent(DefaultErrorActivity.this, getIntent()); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { - android.text.ClipboardManager clipboard = (android.text.ClipboardManager) getSystemService(CLIPBOARD_SERVICE); - clipboard.setText(errorInformation); - } else { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText("Error information", errorInformation); + ClipData clip = ClipData.newPlainText(getString(R.string.customactivityoncrash_error_activity_error_details_clipboard_label), errorInformation); clipboard.setPrimaryClip(clip); + } else { + //noinspection deprecation + android.text.ClipboardManager clipboard = (android.text.ClipboardManager) getSystemService(CLIPBOARD_SERVICE); + clipboard.setText(errorInformation); } } } diff --git a/library/src/main/res/layout/customactivityoncrash_default_error_activity.xml b/library/src/main/res/layout/customactivityoncrash_default_error_activity.xml index 8110136..61eeb53 100644 --- a/library/src/main/res/layout/customactivityoncrash_default_error_activity.xml +++ b/library/src/main/res/layout/customactivityoncrash_default_error_activity.xml @@ -36,6 +36,7 @@ android:paddingTop="@dimen/customactivityoncrash_activity_vertical_margin"> Close Copy to clipboard Copied to clipboard + Error information diff --git a/sample/src/main/java/cat/ereza/sample/customactivityoncrash/SampleCrashingApplication.java b/sample/src/main/java/cat/ereza/sample/customactivityoncrash/SampleCrashingApplication.java index b69a7bc..aa91059 100644 --- a/sample/src/main/java/cat/ereza/sample/customactivityoncrash/SampleCrashingApplication.java +++ b/sample/src/main/java/cat/ereza/sample/customactivityoncrash/SampleCrashingApplication.java @@ -46,6 +46,10 @@ public void onCreate() { //restart activity is found! // CustomActivityOnCrash.setEnableAppRestart(false); + //This shows a different image on the error activity, instead of the default upside-down bug. + //You may use a drawable or a mipmap. +// CustomActivityOnCrash.setDefaultErrorActivityDrawable(R.mipmap.ic_launcher); + //This sets a custom error activity class instead of the default one. //If you set this, this will be used. However, you can also set it with an intent-filter: //