Skip to content

Commit

Permalink
Allow specifying a drawable or mipmap to use on the default error act…
Browse files Browse the repository at this point in the history
…ivity with a method.
  • Loading branch information
Ereza committed Sep 21, 2015
1 parent 53448d3 commit da751b7
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 27 deletions.
35 changes: 23 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<? extends Activity>);
```
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
Expand All @@ -98,7 +105,7 @@ CustomActivityOnCrash.setErrorActivityClass(Class<? extends Activity>);
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:
Expand Down Expand Up @@ -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:*

Expand All @@ -140,6 +148,9 @@ You can provide new strings and translations for the default error activity stri
<string name="customactivityoncrash_error_activity_error_details">Error details</string>
<string name="customactivityoncrash_error_activity_error_details_title">Error details</string>
<string name="customactivityoncrash_error_activity_error_details_close">Close</string>
<string name="customactivityoncrash_error_activity_error_details_copy">Copy to clipboard</string>
<string name="customactivityoncrash_error_activity_error_details_copied">Copied to clipboard</string>
<string name="customactivityoncrash_error_activity_error_details_clipboard_label">Error information</string>
```

*There is a `sample` project module with examples of these overrides. If in doubt, check the code in that module.*
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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<? extends Activity> errorActivityClass = null;
private static Class<? extends Activity> restartActivityClass = null;

Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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));
Expand All @@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
android:paddingTop="@dimen/customactivityoncrash_activity_vertical_margin">

<ImageView
android:id="@+id/customactivityoncrash_error_activity_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
Expand Down
1 change: 1 addition & 0 deletions library/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@
<string name="customactivityoncrash_error_activity_error_details_close">Close</string>
<string name="customactivityoncrash_error_activity_error_details_copy">Copy to clipboard</string>
<string name="customactivityoncrash_error_activity_error_details_copied">Copied to clipboard</string>
<string name="customactivityoncrash_error_activity_error_details_clipboard_label">Error information</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -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:
// <action android:name="cat.ereza.customactivityoncrash.ERROR" />
Expand Down

0 comments on commit da751b7

Please sign in to comment.