diff --git a/app/build.gradle b/app/build.gradle
index f7ccd64..c4bede4 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -41,6 +41,7 @@ dependencies {
implementation 'com.google.android.gms:play-services:10.0.1'
implementation 'com.github.wendykierp:JTransforms:3.0'
implementation 'com.google.android.gms:play-services-location:10.0.0'
+ //implementation 'com.github.RohitSurwase.UCE-Handler:uce_handler:1.3'
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 0672e07..bc37045 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -44,6 +44,9 @@
+
+ * This class is used to
+ * Created by Rohit.
+ */
+public final class UCEDefaultActivity extends Activity {
+ private File txtFile;
+ private String strCurrentErrorLog;
+ private String TAG = "UCEDefaultActivity";
+
+ @SuppressLint("PrivateResource")
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ setTheme(android.R.style.Theme_Holo_Light_DarkActionBar);
+ super.onCreate(savedInstanceState);
+ Log.i(TAG,"onCreate()");
+ setContentView(R.layout.default_error_activity);
+ findViewById(R.id.button_close_app).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ UCEHandler.closeApplication(UCEDefaultActivity.this);
+ }
+ });
+ findViewById(R.id.button_copy_error_log).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ copyErrorToClipboard();
+ }
+ });
+ findViewById(R.id.button_share_error_log).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ shareErrorLog();
+ }
+ });
+ findViewById(R.id.button_save_error_log).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ saveErrorLogToFile(true);
+ }
+ });
+ findViewById(R.id.button_email_error_log).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ emailErrorLog();
+ }
+ });
+ findViewById(R.id.button_view_error_log).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ AlertDialog dialog = new AlertDialog.Builder(UCEDefaultActivity.this)
+ .setTitle("Error Log")
+ .setMessage(getAllErrorDetailsFromIntent(UCEDefaultActivity.this, getIntent()))
+ .setPositiveButton("Copy Log & Close",
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ copyErrorToClipboard();
+ dialog.dismiss();
+ }
+ })
+ .setNeutralButton("Close",
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ })
+ .show();
+ TextView textView = dialog.findViewById(android.R.id.message);
+ if (textView != null) {
+ textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
+ }
+ }
+ });
+ }
+
+ public String getApplicationName(Context context) {
+ ApplicationInfo applicationInfo = context.getApplicationInfo();
+ int stringId = applicationInfo.labelRes;
+ return stringId == 0 ? applicationInfo.nonLocalizedLabel.toString() : context.getString(stringId);
+ }
+
+ private String getVersionName(Context context) {
+ try {
+ PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
+ return packageInfo.versionName;
+ } catch (Exception e) {
+ return "Unknown";
+ }
+ }
+
+ private String getActivityLogFromIntent(Intent intent) {
+ return intent.getStringExtra(UCEHandler.EXTRA_ACTIVITY_LOG);
+ }
+
+ private String getStackTraceFromIntent(Intent intent) {
+ return intent.getStringExtra(UCEHandler.EXTRA_STACK_TRACE);
+ }
+
+ private String getEmailAddressesFromIntent(Intent intent) {
+ Log.d(TAG,"getEmailFromIntent - "+intent.getStringExtra(UCEHandler.EXTRA_EMAIL_ADDRESSES)+".");
+ return intent.getStringExtra(UCEHandler.EXTRA_EMAIL_ADDRESSES);
+ }
+
+ private void emailErrorLog() {
+ saveErrorLogToFile(false);
+ String errorLog = getAllErrorDetailsFromIntent(UCEDefaultActivity.this, getIntent());
+ Log.d(TAG,"emailErrorLog() - addresses = "+UCEHandler.COMMA_SEPARATED_EMAIL_ADDRESSES+".");
+ //String[] emailAddressArray = UCEHandler.COMMA_SEPARATED_EMAIL_ADDRESSES.trim().split("\\s*,\\s*");
+ String emailStr = getEmailAddressesFromIntent(getIntent());
+ Log.d(TAG,"emailErrorLog() - EmailStr = "+emailStr);
+ String[] emailAddressArray = emailStr.split("\\s*,\\s*");
+ //String[] emailAddressArray = {"crashreports@openseizuredetector.org.uk"};
+ Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
+ emailIntent.setType("plain/text");
+ emailIntent.putExtra(Intent.EXTRA_EMAIL, emailAddressArray);
+ emailIntent.putExtra(Intent.EXTRA_SUBJECT, getApplicationName(UCEDefaultActivity.this) + " Application Crash Error Log");
+ emailIntent.putExtra(Intent.EXTRA_TEXT, getString(R.string.email_welcome_note) + errorLog);
+ //if (txtFile.exists()) {
+ // Uri filePath = Uri.fromFile(txtFile);
+ // emailIntent.putExtra(Intent.EXTRA_STREAM, filePath);
+ //}
+ startActivity(Intent.createChooser(emailIntent, "Email Error Log"));
+ }
+
+ private void saveErrorLogToFile(boolean isShowToast) {
+ Boolean isSDPresent = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
+ if (isSDPresent && isExternalStorageWritable()) {
+ Date currentDate = new Date();
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
+ String strCurrentDate = dateFormat.format(currentDate);
+ strCurrentDate = strCurrentDate.replace(" ", "_");
+ String errorLogFileName = getApplicationName(UCEDefaultActivity.this) + "_Error-Log_" + strCurrentDate;
+ String errorLog = getAllErrorDetailsFromIntent(UCEDefaultActivity.this, getIntent());
+ String fullPath = Environment.getExternalStorageDirectory() + "/AppErrorLogs_UCEH/";
+ FileOutputStream outputStream;
+ try {
+ File file = new File(fullPath);
+ file.mkdir();
+ txtFile = new File(fullPath + errorLogFileName + ".txt");
+ txtFile.createNewFile();
+ outputStream = new FileOutputStream(txtFile);
+ outputStream.write(errorLog.getBytes());
+ outputStream.close();
+ if (txtFile.exists() && isShowToast) {
+ Toast.makeText(this, "File Saved Successfully", Toast.LENGTH_SHORT).show();
+ }
+ } catch (IOException e) {
+ Log.e("REQUIRED", "This app does not have write storage permission to save log file.");
+ if (isShowToast) {
+ Toast.makeText(this, "Storage Permission Not Found", Toast.LENGTH_SHORT).show();
+ }
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private void shareErrorLog() {
+ String errorLog = getAllErrorDetailsFromIntent(UCEDefaultActivity.this, getIntent());
+ Intent share = new Intent(Intent.ACTION_SEND);
+ share.setType("text/plain");
+ share.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+ share.putExtra(Intent.EXTRA_SUBJECT, "Application Crash Error Log");
+ share.putExtra(Intent.EXTRA_TEXT, errorLog);
+ startActivity(Intent.createChooser(share, "Share Error Log"));
+ }
+
+ private void copyErrorToClipboard() {
+ String errorInformation = getAllErrorDetailsFromIntent(UCEDefaultActivity.this, getIntent());
+ ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
+ if (clipboard != null) {
+ ClipData clip = ClipData.newPlainText("View Error Log", errorInformation);
+ clipboard.setPrimaryClip(clip);
+ Toast.makeText(UCEDefaultActivity.this, "Error Log Copied", Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private String getAllErrorDetailsFromIntent(Context context, Intent intent) {
+ if (TextUtils.isEmpty(strCurrentErrorLog)) {
+ String LINE_SEPARATOR = "\n";
+ StringBuilder errorReport = new StringBuilder();
+ errorReport.append("***** UCE HANDLER Library ");
+ errorReport.append("\n***** by Rohit Surwase \n");
+ errorReport.append("\n***** DEVICE INFO \n");
+ errorReport.append("Brand: ");
+ errorReport.append(Build.BRAND);
+ errorReport.append(LINE_SEPARATOR);
+ errorReport.append("Device: ");
+ errorReport.append(Build.DEVICE);
+ errorReport.append(LINE_SEPARATOR);
+ errorReport.append("Model: ");
+ errorReport.append(Build.MODEL);
+ errorReport.append(LINE_SEPARATOR);
+ errorReport.append("Manufacturer: ");
+ errorReport.append(Build.MANUFACTURER);
+ errorReport.append(LINE_SEPARATOR);
+ errorReport.append("Product: ");
+ errorReport.append(Build.PRODUCT);
+ errorReport.append(LINE_SEPARATOR);
+ errorReport.append("SDK: ");
+ errorReport.append(Build.VERSION.SDK);
+ errorReport.append(LINE_SEPARATOR);
+ errorReport.append("Release: ");
+ errorReport.append(Build.VERSION.RELEASE);
+ errorReport.append(LINE_SEPARATOR);
+ errorReport.append("\n***** APP INFO \n");
+ String versionName = getVersionName(context);
+ errorReport.append("Version: ");
+ errorReport.append(versionName);
+ errorReport.append(LINE_SEPARATOR);
+ Date currentDate = new Date();
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
+ String firstInstallTime = getFirstInstallTimeAsString(context, dateFormat);
+ if (!TextUtils.isEmpty(firstInstallTime)) {
+ errorReport.append("Installed On: ");
+ errorReport.append(firstInstallTime);
+ errorReport.append(LINE_SEPARATOR);
+ }
+ String lastUpdateTime = getLastUpdateTimeAsString(context, dateFormat);
+ if (!TextUtils.isEmpty(lastUpdateTime)) {
+ errorReport.append("Updated On: ");
+ errorReport.append(lastUpdateTime);
+ errorReport.append(LINE_SEPARATOR);
+ }
+ errorReport.append("Current Date: ");
+ errorReport.append(dateFormat.format(currentDate));
+ errorReport.append(LINE_SEPARATOR);
+ errorReport.append("\n***** ERROR LOG \n");
+ errorReport.append(getStackTraceFromIntent(intent));
+ errorReport.append(LINE_SEPARATOR);
+ String activityLog = getActivityLogFromIntent(intent);
+ errorReport.append(LINE_SEPARATOR);
+ if (activityLog != null) {
+ errorReport.append("\n***** USER ACTIVITIES \n");
+ errorReport.append("User Activities: ");
+ errorReport.append(activityLog);
+ errorReport.append(LINE_SEPARATOR);
+ }
+ errorReport.append("\n***** END OF LOG *****\n");
+ strCurrentErrorLog = errorReport.toString();
+ return strCurrentErrorLog;
+ } else {
+ return strCurrentErrorLog;
+ }
+ }
+
+ private String getFirstInstallTimeAsString(Context context, DateFormat dateFormat) {
+ long firstInstallTime;
+ try {
+ firstInstallTime = context
+ .getPackageManager()
+ .getPackageInfo(context.getPackageName(), 0)
+ .firstInstallTime;
+ return dateFormat.format(new Date(firstInstallTime));
+ } catch (PackageManager.NameNotFoundException e) {
+ return "";
+ }
+ }
+
+ private String getLastUpdateTimeAsString(Context context, DateFormat dateFormat) {
+ long lastUpdateTime;
+ try {
+ lastUpdateTime = context
+ .getPackageManager()
+ .getPackageInfo(context.getPackageName(), 0)
+ .lastUpdateTime;
+ return dateFormat.format(new Date(lastUpdateTime));
+ } catch (PackageManager.NameNotFoundException e) {
+ return "";
+ }
+ }
+
+ public boolean isExternalStorageWritable() {
+ String state = Environment.getExternalStorageState();
+ if (Environment.MEDIA_MOUNTED.equals(state)) {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/app/src/main/java/com/rohitss/uceh/UCEHandler.java b/app/src/main/java/com/rohitss/uceh/UCEHandler.java
new file mode 100644
index 0000000..9970e55
--- /dev/null
+++ b/app/src/main/java/com/rohitss/uceh/UCEHandler.java
@@ -0,0 +1,271 @@
+/*
+ *
+ * * Copyright © 2018 Rohit Sahebrao Surwase.
+ * *
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ *
+ */
+package com.rohitss.uceh;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.Application;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.ref.WeakReference;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayDeque;
+import java.util.Date;
+import java.util.Deque;
+import java.util.Locale;
+
+/**
+ *
+ * This class is used to
+ * Created by Rohit.
+ */
+public final class UCEHandler {
+ static final String EXTRA_STACK_TRACE = "EXTRA_STACK_TRACE";
+ static final String EXTRA_ACTIVITY_LOG = "EXTRA_ACTIVITY_LOG";
+ static final String EXTRA_EMAIL_ADDRESSES = "EXTRA_EMAIL_ADDRESSES";
+ private final static String TAG = "UCEHandler";
+ private static final String UCE_HANDLER_PACKAGE_NAME = "com.rohitss.uceh";
+ private static final String DEFAULT_HANDLER_PACKAGE_NAME = "com.android.internal.os";
+ private static final int MAX_STACK_TRACE_SIZE = 131071; //128 KB - 1
+ private static final int MAX_ACTIVITIES_IN_LOG = 50;
+ private static final String SHARED_PREFERENCES_FILE = "uceh_preferences";
+ private static final String SHARED_PREFERENCES_FIELD_TIMESTAMP = "last_crash_timestamp";
+ private static final Deque activityLog = new ArrayDeque<>(MAX_ACTIVITIES_IN_LOG);
+ static String COMMA_SEPARATED_EMAIL_ADDRESSES;
+ @SuppressLint("StaticFieldLeak")
+ private static Application application;
+ private static boolean isInBackground = true;
+ private static boolean isBackgroundMode;
+ private static boolean isUCEHEnabled;
+ private static boolean isTrackActivitiesEnabled;
+ private static WeakReference lastActivityCreated = new WeakReference<>(null);
+
+ UCEHandler(Builder builder) {
+ isUCEHEnabled = builder.isUCEHEnabled;
+ isTrackActivitiesEnabled = builder.isTrackActivitiesEnabled;
+ isBackgroundMode = builder.isBackgroundModeEnabled;
+ COMMA_SEPARATED_EMAIL_ADDRESSES = builder.commaSeparatedEmailAddresses;
+ Log.d(TAG,"UCEHandler() - Email Addresses = "+COMMA_SEPARATED_EMAIL_ADDRESSES+".");
+ Log.d(TAG,"UCEHandler() - UCEHandler.Email Addresses = "+UCEHandler.COMMA_SEPARATED_EMAIL_ADDRESSES+".");
+ setUCEHandler(builder.context);
+ }
+
+ private static void setUCEHandler(final Context context) {
+ Log.i(TAG,"setUCEHandler() - email addresses = "+UCEHandler.COMMA_SEPARATED_EMAIL_ADDRESSES+".");
+ try {
+ if (context != null) {
+ final Thread.UncaughtExceptionHandler oldHandler = Thread.getDefaultUncaughtExceptionHandler();
+ if (oldHandler != null && oldHandler.getClass().getName().startsWith(UCE_HANDLER_PACKAGE_NAME)) {
+ Log.e(TAG, "UCEHandler was already installed, doing nothing!");
+ } else {
+ if (oldHandler != null && !oldHandler.getClass().getName().startsWith(DEFAULT_HANDLER_PACKAGE_NAME)) {
+ Log.e(TAG, "You already have an UncaughtExceptionHandler. If you use a custom UncaughtExceptionHandler, it should be initialized after UCEHandler! Installing anyway, but your original handler will not be called.");
+ }
+ application = (Application) context.getApplicationContext();
+ //Setup UCE Handler.
+ Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+ @Override
+ public void uncaughtException(Thread thread, final Throwable throwable) {
+ if (isUCEHEnabled) {
+ Log.e(TAG, "App crashed, executing UCEHandler's UncaughtExceptionHandler", throwable);
+ if (hasCrashedInTheLastSeconds(application)) {
+ Log.e(TAG, "App already crashed recently, not starting custom error activity because we could enter a restart loop. Are you sure that your app does not crash directly on init?", throwable);
+ if (oldHandler != null) {
+ oldHandler.uncaughtException(thread, throwable);
+ return;
+ }
+ } else {
+ setLastCrashTimestamp(application, new Date().getTime());
+ if (!isInBackground || isBackgroundMode) {
+ Log.d(TAG,"Preparing Intent");
+ final Intent intent = new Intent(application, UCEDefaultActivity.class);
+ intent.putExtra(EXTRA_EMAIL_ADDRESSES, COMMA_SEPARATED_EMAIL_ADDRESSES);
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ throwable.printStackTrace(pw);
+ String stackTraceString = sw.toString();
+ if (stackTraceString.length() > MAX_STACK_TRACE_SIZE) {
+ String disclaimer = " [stack trace too large]";
+ stackTraceString = stackTraceString.substring(0, MAX_STACK_TRACE_SIZE - disclaimer.length()) + disclaimer;
+ }
+ intent.putExtra(EXTRA_STACK_TRACE, stackTraceString);
+ if (isTrackActivitiesEnabled) {
+ StringBuilder activityLogStringBuilder = new StringBuilder();
+ while (!activityLog.isEmpty()) {
+ activityLogStringBuilder.append(activityLog.poll());
+ }
+ intent.putExtra(EXTRA_ACTIVITY_LOG, activityLogStringBuilder.toString());
+ }
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ Log.d(TAG,"calling startActivity()");
+ application.startActivity(intent);
+ } else {
+ Log.d(TAG,"using oldHandler");
+ if (oldHandler != null) {
+ oldHandler.uncaughtException(thread, throwable);
+ return;
+ }
+ //If it is null (should not be), we let it continue and kill the process or it will be stuck
+ }
+ }
+ final Activity lastActivity = lastActivityCreated.get();
+ if (lastActivity != null) {
+ lastActivity.finish();
+ lastActivityCreated.clear();
+ }
+ killCurrentProcess();
+ } else if (oldHandler != null) {
+ //Pass control to old uncaught exception handler
+ oldHandler.uncaughtException(thread, throwable);
+ }
+ }
+ });
+ application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
+ final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
+ int currentlyStartedActivities = 0;
+
+ @Override
+ public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+ if (activity.getClass() != UCEDefaultActivity.class) {
+ lastActivityCreated = new WeakReference<>(activity);
+ }
+ if (isTrackActivitiesEnabled) {
+ activityLog.add(dateFormat.format(new Date()) + ": " + activity.getClass().getSimpleName() + " created\n");
+ }
+ }
+
+ @Override
+ public void onActivityStarted(Activity activity) {
+ currentlyStartedActivities++;
+ isInBackground = (currentlyStartedActivities == 0);
+ }
+
+ @Override
+ public void onActivityResumed(Activity activity) {
+ if (isTrackActivitiesEnabled) {
+ activityLog.add(dateFormat.format(new Date()) + ": " + activity.getClass().getSimpleName() + " resumed\n");
+ }
+ }
+
+ @Override
+ public void onActivityPaused(Activity activity) {
+ if (isTrackActivitiesEnabled) {
+ activityLog.add(dateFormat.format(new Date()) + ": " + activity.getClass().getSimpleName() + " paused\n");
+ }
+ }
+
+ @Override
+ public void onActivityStopped(Activity activity) {
+ currentlyStartedActivities--;
+ isInBackground = (currentlyStartedActivities == 0);
+ }
+
+ @Override
+ public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
+ }
+
+ @Override
+ public void onActivityDestroyed(Activity activity) {
+ if (isTrackActivitiesEnabled) {
+ activityLog.add(dateFormat.format(new Date()) + ": " + activity.getClass().getSimpleName() + " destroyed\n");
+ }
+ }
+ });
+ }
+ Log.i(TAG, "UCEHandler has been installed.");
+ } else {
+ Log.e(TAG, "Context can not be null");
+ }
+ } catch (Throwable throwable) {
+ Log.e(TAG, "UCEHandler can not be initialized. Help making it better by reporting this as a bug.", throwable);
+ }
+ }
+
+ /**
+ * INTERNAL method that tells if the app has crashed in the last seconds.
+ * This is used to avoid restart loops.
+ *
+ * @return true if the app has crashed in the last seconds, false otherwise.
+ */
+ private static boolean hasCrashedInTheLastSeconds(Context context) {
+ long lastTimestamp = getLastCrashTimestamp(context);
+ long currentTimestamp = new Date().getTime();
+ return (lastTimestamp <= currentTimestamp && currentTimestamp - lastTimestamp < 3000);
+ }
+
+ @SuppressLint("ApplySharedPref")
+ private static void setLastCrashTimestamp(Context context, long timestamp) {
+ context.getSharedPreferences(SHARED_PREFERENCES_FILE, Context.MODE_PRIVATE).edit().putLong(SHARED_PREFERENCES_FIELD_TIMESTAMP, timestamp).commit();
+ }
+
+ private static void killCurrentProcess() {
+ android.os.Process.killProcess(android.os.Process.myPid());
+ System.exit(10);
+ }
+
+ private static long getLastCrashTimestamp(Context context) {
+ return context.getSharedPreferences(SHARED_PREFERENCES_FILE, Context.MODE_PRIVATE).getLong(SHARED_PREFERENCES_FIELD_TIMESTAMP, -1);
+ }
+
+ static void closeApplication(Activity activity) {
+ activity.finish();
+ killCurrentProcess();
+ }
+
+ public static class Builder {
+ private Context context;
+ private boolean isUCEHEnabled = true;
+ private String commaSeparatedEmailAddresses;
+ private boolean isTrackActivitiesEnabled = false;
+ private boolean isBackgroundModeEnabled = true;
+
+ public Builder(Context context) {
+ this.context = context;
+ }
+
+ public Builder setUCEHEnabled(boolean isUCEHEnabled) {
+ this.isUCEHEnabled = isUCEHEnabled;
+ return this;
+ }
+
+ public Builder setTrackActivitiesEnabled(boolean isTrackActivitiesEnabled) {
+ this.isTrackActivitiesEnabled = isTrackActivitiesEnabled;
+ return this;
+ }
+
+ public Builder setBackgroundModeEnabled(boolean isBackgroundModeEnabled) {
+ this.isBackgroundModeEnabled = isBackgroundModeEnabled;
+ return this;
+ }
+
+ public Builder addCommaSeparatedEmailAddresses(String commaSeparatedEmailAddresses) {
+ this.commaSeparatedEmailAddresses = (commaSeparatedEmailAddresses != null) ? commaSeparatedEmailAddresses : "";
+ return this;
+ }
+
+ public UCEHandler build() {
+ return new UCEHandler(this);
+ }
+ }
+}
diff --git a/app/src/main/java/uk/org/openseizuredetector/MainActivity.java b/app/src/main/java/uk/org/openseizuredetector/MainActivity.java
index 7f07f08..21a00ac 100644
--- a/app/src/main/java/uk/org/openseizuredetector/MainActivity.java
+++ b/app/src/main/java/uk/org/openseizuredetector/MainActivity.java
@@ -61,6 +61,7 @@ import com.github.mikephil.charting.data.BarData;
import com.github.mikephil.charting.data.BarDataSet;
import com.github.mikephil.charting.data.BarEntry;
import com.github.mikephil.charting.utils.ValueFormatter;
+import com.rohitss.uceh.UCEHandler;
public class MainActivity extends AppCompatActivity {
static final String TAG = "MainActivity";
@@ -89,7 +90,11 @@ public class MainActivity extends AppCompatActivity {
Log.i(TAG,"onCreate()");
// Set our custom uncaught exception handler to report issues.
- Thread.setDefaultUncaughtExceptionHandler(new OsdUncaughtExceptionHandler(MainActivity.this));
+ //Thread.setDefaultUncaughtExceptionHandler(new OsdUncaughtExceptionHandler(MainActivity.this));
+ new UCEHandler.Builder(this)
+ .addCommaSeparatedEmailAddresses("crashreports@openseizuredetector.org.uk,")
+ .build();
+
//int i = 5/0; // Force exception to test handler.
mUtil = new OsdUtil(this,serverStatusHandler);
mConnection = new SdServiceConnection(this);
diff --git a/app/src/main/java/uk/org/openseizuredetector/OsdUncaughtExceptionHandler.java b/app/src/main/java/uk/org/openseizuredetector/OsdUncaughtExceptionHandler.java
index 3834a92..1a5f866 100644
--- a/app/src/main/java/uk/org/openseizuredetector/OsdUncaughtExceptionHandler.java
+++ b/app/src/main/java/uk/org/openseizuredetector/OsdUncaughtExceptionHandler.java
@@ -163,8 +163,8 @@ public class OsdUncaughtExceptionHandler implements Thread.UncaughtExceptionHand
"You can review the information being sent in the next screen:"+
"\n"+errorContent.toString());
Dialog dialog = builder.create();
- dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
- //dialog.show();
+ //dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ dialog.show();
Looper.loop();
}
}.start();
diff --git a/app/src/main/java/uk/org/openseizuredetector/OsdUtil.java b/app/src/main/java/uk/org/openseizuredetector/OsdUtil.java
index ccfcf27..289ff6b 100644
--- a/app/src/main/java/uk/org/openseizuredetector/OsdUtil.java
+++ b/app/src/main/java/uk/org/openseizuredetector/OsdUtil.java
@@ -91,6 +91,9 @@ public class OsdUtil implements ActivityCompat.OnRequestPermissionsResultCallbac
private final String[] REQUIRED_PERMISSIONS = {
Manifest.permission.SEND_SMS,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ //Manifest.permission.SYSTEM_ALERT_WINDOW,
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.WAKE_LOCK,
};
/**
@@ -342,24 +345,30 @@ public class OsdUtil implements ActivityCompat.OnRequestPermissionsResultCallbac
fname = fname + "_" + dateStr + ".txt";
// Open output directory on SD Card.
- if (isExternalStorageWritable()) {
- try {
- FileWriter of = new FileWriter(getDataStorageDir().toString()
- + "/" + fname, true);
- if (msgStr != null) {
- String dateTimeStr = tnow.format("%Y-%m-%d %H:%M:%S");
- Log.v(TAG, "writing msgStr");
- of.append(dateTimeStr+", "
- +tnow.toMillis(true)+", "
- +msgStr+"
\n");
- }
- of.close();
- } catch (Exception ex) {
- Log.e(TAG, "writeToLogFile - error " + ex.toString());
- showToast("ERROR Writing to Log File");
- }
+ if (ContextCompat.checkSelfPermission(mContext,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE)
+ != PackageManager.PERMISSION_GRANTED) {
+ Log.e(TAG,"ERROR: We do not have permission to write to external storage");
} else {
- Log.e(TAG, "ERROR - Can not Write to External Folder");
+ if (isExternalStorageWritable()) {
+ try {
+ FileWriter of = new FileWriter(getDataStorageDir().toString()
+ + "/" + fname, true);
+ if (msgStr != null) {
+ String dateTimeStr = tnow.format("%Y-%m-%d %H:%M:%S");
+ Log.v(TAG, "writing msgStr");
+ of.append(dateTimeStr + ", "
+ + tnow.toMillis(true) + ", "
+ + msgStr + "
\n");
+ }
+ of.close();
+ } catch (Exception ex) {
+ Log.e(TAG, "writeToLogFile - error " + ex.toString());
+ showToast("ERROR Writing to Log File");
+ }
+ } else {
+ Log.e(TAG, "ERROR - Can not Write to External Folder");
+ }
}
}
diff --git a/app/src/main/java/uk/org/openseizuredetector/SdDataSourceGarmin.java b/app/src/main/java/uk/org/openseizuredetector/SdDataSourceGarmin.java
index 90e3eb4..f3fe8f3 100644
--- a/app/src/main/java/uk/org/openseizuredetector/SdDataSourceGarmin.java
+++ b/app/src/main/java/uk/org/openseizuredetector/SdDataSourceGarmin.java
@@ -40,6 +40,7 @@ import com.getpebble.android.kit.PebbleKit;
import com.getpebble.android.kit.util.PebbleDictionary;
import org.json.JSONArray;
+import org.json.JSONException;
import org.json.JSONObject;
import org.jtransforms.fft.DoubleFFT_1D;
@@ -373,7 +374,12 @@ public class SdDataSourceGarmin extends SdDataSource {
Log.v(TAG,"updateFromJSON - dataType="+dataTypeStr);
if (dataTypeStr.equals("raw")) {
Log.v(TAG,"updateFromJSON - processing raw data");
- mSdData.mHR = dataObject.getDouble("HR");
+ try {
+ mSdData.mHR = dataObject.getDouble("HR");
+ } catch (JSONException e) {
+ // if we get 'null' HR (For example if the heart rate is not working)
+ mSdData.mHR = -1;
+ }
JSONArray accelVals = dataObject.getJSONArray("data");
Log.v(TAG, "Received " + accelVals.length() + " acceleration values");
int i;
diff --git a/app/src/main/java/uk/org/openseizuredetector/SdServer.java b/app/src/main/java/uk/org/openseizuredetector/SdServer.java
index 0255b8d..82321b0 100644
--- a/app/src/main/java/uk/org/openseizuredetector/SdServer.java
+++ b/app/src/main/java/uk/org/openseizuredetector/SdServer.java
@@ -68,6 +68,8 @@ import java.util.*;
import android.text.format.Time;
+import com.rohitss.uceh.UCEHandler;
+
/**
* Based on example at:
@@ -169,15 +171,18 @@ public class SdServer extends Service implements SdDataReceiver, SdLocationRecei
mUtil.writeToSysLogFile("SdServer.onCreate()");
// Set our custom uncaught exception handler to report issues.
- Thread.setDefaultUncaughtExceptionHandler(
- new OsdUncaughtExceptionHandler(SdServer.this));
+ //Thread.setDefaultUncaughtExceptionHandler(
+ // new OsdUncaughtExceptionHandler(SdServer.this));
+ new UCEHandler.Builder(this)
+ .addCommaSeparatedEmailAddresses("crashreports@openseizuredetector.org.uk,")
+ .build();
//int i = 5/0; // Force exception to test handler.
// Create a wake lock, but don't use it until the service is started.
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- "MyWakelockTag");
+ "OSD:WakeLock");
}
/**
diff --git a/app/src/main/java/uk/org/openseizuredetector/StartupActivity.java b/app/src/main/java/uk/org/openseizuredetector/StartupActivity.java
index 5654667..44e6cbd 100644
--- a/app/src/main/java/uk/org/openseizuredetector/StartupActivity.java
+++ b/app/src/main/java/uk/org/openseizuredetector/StartupActivity.java
@@ -49,6 +49,8 @@ import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
+import com.rohitss.uceh.UCEHandler;
+
import java.util.Timer;
import java.util.TimerTask;
@@ -82,7 +84,11 @@ public class StartupActivity extends Activity {
Log.i(TAG,"onCreate()");
// Set our custom uncaught exception handler to report issues.
- Thread.setDefaultUncaughtExceptionHandler(new OsdUncaughtExceptionHandler(StartupActivity.this));
+ //Thread.setDefaultUncaughtExceptionHandler(new OsdUncaughtExceptionHandler(StartupActivity.this));
+ new UCEHandler.Builder(this)
+ .addCommaSeparatedEmailAddresses("crashreports@openseizuredetector.org.uk,")
+ .build();
+
mHandler = new Handler();
mUtil = new OsdUtil(this, mHandler);
@@ -103,6 +109,10 @@ public class StartupActivity extends Activity {
PreferenceManager.setDefaultValues(this, R.xml.camera_prefs, true);
PreferenceManager.setDefaultValues(this, R.xml.general_prefs, true);
PreferenceManager.setDefaultValues(this, R.xml.network_datasource_prefs, true);
+ PreferenceManager.setDefaultValues(this, R.xml.pebble_datasource_prefs, true);
+ PreferenceManager.setDefaultValues(this, R.xml.garmin_datasource_prefs, true);
+ PreferenceManager.setDefaultValues(this, R.xml.seizure_detector_prefs, true);
+ PreferenceManager.setDefaultValues(this, R.xml.network_passive_datasource_prefs, true);
Button b;
@@ -174,6 +184,14 @@ public class StartupActivity extends Activity {
mUtil.writeToSysLogFile("StartupActivity.onStart()");
TextView tv;
+ if (mUtil.arePermissionsOK()) {
+ Log.i(TAG,"onStart() - Permissions OK");
+ } else {
+ Log.i(TAG,"onStart() - Permissions Not OK - requesting them");
+ mUtil.requestPermissions(this);
+ }
+
+
String versionName = mUtil.getAppVersionName();
tv = (TextView) findViewById(R.id.appNameTv);
tv.setText("OpenSeizureDetector V" + versionName);
@@ -198,6 +216,8 @@ public class StartupActivity extends Activity {
mUsingPebbleDataSource = true;
}
+
+
if (mUtil.isServerRunning()) {
Log.i(TAG, "onStart() - server running - stopping it");
mUtil.writeToSysLogFile("StartupActivity.onStart() - server already running - stopping it.");
diff --git a/app/src/main/res/layout/default_error_activity.xml b/app/src/main/res/layout/default_error_activity.xml
new file mode 100644
index 0000000..be73d5d
--- /dev/null
+++ b/app/src/main/res/layout/default_error_activity.xml
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 6e4d4c4..5809b38 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,7 +1,7 @@
OpenSeizureDetector
-
-
- Hello blank fragment
+ Sorry, OpenSeizureDetector Has Crashed. Please Email this log file to us so we can work out what happened and fix it.\nThanks, Graham.
+ Dear OpenSeizureDetector,\n\nApplication is just crashed, please check following error log for more details.\n\n\n
+ OpenSeizureDetector (Using UCE Handler\nCopyright © 2018 Rohit Sahebrao Surwase.)
diff --git a/build.gradle b/build.gradle
index 1dcd6aa..5f5c70e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -19,6 +19,7 @@ allprojects {
url 'https://maven.google.com/'
name 'Google'
}
+ //maven { url 'https://jitpack.io' }
}
}