diff --git a/README.md b/README.md index 4e092d5d4c..41a7e699e6 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ What is ACRA ? ============== -ACRA is a library enabling Android Application to automatically post their crash reports to a GoogleDoc form. It is targetted to android applications developers to help them get data from their applications when they crash or behave erroneously. +ACRA is a library enabling Android Application to automatically post their crash reports to a GoogleDoc form. It is targeted to android applications developers to help them get data from their applications when they crash or behave erroneously. ACRA is used in 2.68% ([See AppBrain/stats](http://www.appbrain.com/stats/libraries/details/acra/acra)) of all apps on Google Play as of Feb 2016. That's over 53K **apps** using ACRA. And since the average US user has 41 apps installed on their phone that means there is a 70% chance that ACRA is running on any phone. That means ACRA is running on over a **billion devices**. @@ -27,7 +27,7 @@ A crash reporting feature for android apps is native since Android 2.2 (FroYo) b ACRA's notification systems are clean. If a crash occurs, your application does not add user notifications over existing system's crash notifications or reporting features. If you use the Toast, Status bar notification or direct dialog modes, the "force close" dialog is not displayed anymore and devices where the system native reporting feature is enabled do not offer the user to send an additional report. -The user is notified of an error only once, and you might enhance the percieved quality of your application by defining your own texts in the notifications/dialogs. +The user is notified of an error only once, and you might enhance the perceived quality of your application by defining your own texts in the notifications/dialogs. Please do not hesitate to open defects/enhancements requests in [the issue tracker](http://github.com/ACRA/acra/issues). @@ -57,7 +57,7 @@ ACRA v4.8 ACRA v4.7 =============================== - Support for Android M (6.0) - - Using HtttpUrlConnection instead of Apache Http + - Using HttpUrlConnection instead of Apache Http - Using com.android.support:support-v4 to provide support fro removed Notification methods. - Minimum Android version is Froyo (2.2). ACRA will disable itself for anything prior to that. - Packaging as an AAR. diff --git a/build.gradle b/build.gradle index 592d037b8f..dfb6c0fffb 100644 --- a/build.gradle +++ b/build.gradle @@ -12,7 +12,7 @@ android { minSdkVersion 8 targetSdkVersion 23 versionCode 1 - versionName "1.0" + versionName "undefined" } buildTypes { @@ -28,7 +28,6 @@ android { } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:support-v4:23.1.1' - compile 'com.android.support:support-annotations:23.1.1' + compile 'com.android.support:support-v4:23.2.0' + compile 'com.android.support:support-annotations:23.2.0' } diff --git a/src/main/java/org/acra/ACRA.java b/src/main/java/org/acra/ACRA.java index c929dc67a4..9e1e413997 100644 --- a/src/main/java/org/acra/ACRA.java +++ b/src/main/java/org/acra/ACRA.java @@ -45,9 +45,10 @@ * @author Kevin Gaudin * */ -public class ACRA { +public final class ACRA { + private ACRA(){} - public static boolean DEV_LOGGING = false; // Should be false for release. + public static final boolean DEV_LOGGING = false; // Should be false for release. public static final String LOG_TAG = ACRA.class.getSimpleName(); diff --git a/src/main/java/org/acra/ACRAConstants.java b/src/main/java/org/acra/ACRAConstants.java index b99379eee7..eed196a723 100644 --- a/src/main/java/org/acra/ACRAConstants.java +++ b/src/main/java/org/acra/ACRAConstants.java @@ -27,6 +27,7 @@ * @since 4.3.0 */ public final class ACRAConstants { + private ACRAConstants(){} public static final String REPORTFILE_EXTENSION = ".stacktrace"; @@ -136,7 +137,7 @@ public final class ACRAConstants { * * @see org.acra.annotation.ReportsCrashes#mailTo() */ - public final static ReportField[] DEFAULT_MAIL_REPORT_FIELDS = { USER_COMMENT, ANDROID_VERSION, APP_VERSION_NAME, + public static final ReportField[] DEFAULT_MAIL_REPORT_FIELDS = { USER_COMMENT, ANDROID_VERSION, APP_VERSION_NAME, BRAND, PHONE_MODEL, CUSTOM_DATA, STACK_TRACE }; /** diff --git a/src/main/java/org/acra/ReportField.java b/src/main/java/org/acra/ReportField.java index 637d341f60..34bf13dead 100644 --- a/src/main/java/org/acra/ReportField.java +++ b/src/main/java/org/acra/ReportField.java @@ -15,10 +15,10 @@ */ package org.acra; -import org.acra.annotation.ReportsCrashes; - import android.content.res.Configuration; +import org.acra.annotation.ReportsCrashes; + /** * Specifies all the different fields available in a crash report. * @@ -293,7 +293,7 @@ public boolean containsKeyValuePairs() { * Whether this field is a collection of key/value pairs. * * @return true if the field contains a string with a key/value pair on each - * line, key and value separated by an equal sugn + * line, key and value separated by an equal sign * */ public boolean containsKeyValuePairs() { diff --git a/src/main/java/org/acra/annotation/ReportsCrashes.java b/src/main/java/org/acra/annotation/ReportsCrashes.java index e568ce185d..db2eca52ed 100644 --- a/src/main/java/org/acra/annotation/ReportsCrashes.java +++ b/src/main/java/org/acra/annotation/ReportsCrashes.java @@ -528,7 +528,7 @@ * {@link Context#getFilesDir()}. * * @return a String containing the path/name of your application log file. - * If the string does not containt any path separator, the file is + * If the string does not contain any path separator, the file is * assumed as being in {@link Context#getFilesDir()}. */ @NonNull String applicationLogFile() default ACRAConstants.DEFAULT_APPLICATION_LOGFILE; diff --git a/src/main/java/org/acra/builder/ReportExecutor.java b/src/main/java/org/acra/builder/ReportExecutor.java index 73622a2ab1..c38ef7c959 100644 --- a/src/main/java/org/acra/builder/ReportExecutor.java +++ b/src/main/java/org/acra/builder/ReportExecutor.java @@ -12,6 +12,7 @@ import android.support.annotation.Nullable; import android.support.v4.app.NotificationCompat; import android.widget.Toast; + import org.acra.ACRA; import org.acra.ACRAConstants; import org.acra.ReportingInteractionMode; @@ -39,6 +40,8 @@ */ public final class ReportExecutor { + private static final int THREAD_SLEEP_INTERVAL_MILLIS = 100; + private final Context context; private final ACRAConfiguration config; private final CrashReportDataFactory crashReportDataFactory; @@ -52,10 +55,6 @@ public final class ReportExecutor { private boolean enabled = false; - // This is used to wait for the crash toast to end it's display duration before killing the Application. - // TODO make this a local variable. Only here because it cannot be non-final and referenced within an anonymous class. - private boolean toastWaitEnded = true; - /** * Used to create a new (non-cached) PendingIntent each time a new crash occurs. */ @@ -196,68 +195,53 @@ public void run() { createNotification(reportFile, reportBuilder); } - // This is used to wait for the crash toast to end it's display duration before killing the Application. - toastWaitEnded = true; + final boolean showDirectDialog = (reportingInteractionMode == ReportingInteractionMode.DIALOG) + && !prefs.getBoolean(ACRA.PREF_ALWAYS_ACCEPT, false); if (shouldDisplayToast) { // A toast is being displayed, we have to wait for its end before doing anything else. - // The toastWaitEnded flag will be checked before any other operation. - toastWaitEnded = false; new Thread() { @Override public void run() { - if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "Waiting for " + ACRAConstants.TOAST_WAIT_DURATION - + " millis from " + sentToastTimeMillis.initialTimeMillis - + " currentMillis=" + System.currentTimeMillis()); + if (ACRA.DEV_LOGGING) + ACRA.log.d(LOG_TAG, "Waiting for " + ACRAConstants.TOAST_WAIT_DURATION + + " millis from " + sentToastTimeMillis.initialTimeMillis + + " currentMillis=" + System.currentTimeMillis()); while (sentToastTimeMillis.getElapsedTime() < ACRAConstants.TOAST_WAIT_DURATION) { try { // Wait a bit to let the user read the toast - Thread.sleep(100); + Thread.sleep(THREAD_SLEEP_INTERVAL_MILLIS); } catch (InterruptedException e1) { - if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "Interrupted while waiting for Toast to end.", e1); + if (ACRA.DEV_LOGGING) + ACRA.log.d(LOG_TAG, "Interrupted while waiting for Toast to end.", e1); } } - toastWaitEnded = true; + if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "Finished waiting for Toast"); + dialogAndEnd(reportBuilder, reportFile, showDirectDialog); } }.start(); + } else { + dialogAndEnd(reportBuilder, reportFile, showDirectDialog); } + } - final boolean showDirectDialog = (reportingInteractionMode == ReportingInteractionMode.DIALOG) - && !prefs.getBoolean(ACRA.PREF_ALWAYS_ACCEPT, false); - - new Thread() { - - @Override - public void run() { - // We have to wait for the toast display to be completed. - if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "Waiting for Toast"); - while (!toastWaitEnded) { - try { - Thread.sleep(100); - } catch (InterruptedException e1) { - if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "Interrupted waiting for Toast"); - } - } - if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "Finished waiting for Toast"); - - if (showDirectDialog) { - // Create a new activity task with the confirmation dialog. - // This new task will be persisted on application restart - // right after its death. - if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "Creating CrashReportDialog for " + reportFile); - final Intent dialogIntent = createCrashReportDialogIntent(reportFile, reportBuilder); - dialogIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(dialogIntent); - } + private void dialogAndEnd(@NonNull ReportBuilder reportBuilder, @NonNull File reportFile, boolean shouldShowDialog) { + if (shouldShowDialog) { + // Create a new activity task with the confirmation dialog. + // This new task will be persisted on application restart + // right after its death. + if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "Creating CrashReportDialog for " + reportFile); + final Intent dialogIntent = createCrashReportDialogIntent(reportFile, reportBuilder); + dialogIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(dialogIntent); + } - if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "Wait for Toast + worker ended. Kill Application ? " + reportBuilder.isEndApplication()); + if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "Wait for Toast + worker ended. Kill Application ? " + reportBuilder.isEndApplication()); - if (reportBuilder.isEndApplication()) { - endApplication(reportBuilder.getUncaughtExceptionThread(), reportBuilder.getException()); - } - } - }.start(); + if (reportBuilder.isEndApplication()) { + endApplication(reportBuilder.getUncaughtExceptionThread(), reportBuilder.getException()); + } } /** @@ -406,7 +390,7 @@ private void saveCrashReportFile(@NonNull File file, @NonNull CrashReportData cr * @param reportBuilder ReportBuilder containing the details of the crash. */ @NonNull - private Intent createCrashReportDialogIntent(File reportFile, @NonNull ReportBuilder reportBuilder) { + private Intent createCrashReportDialogIntent(@NonNull File reportFile, @NonNull ReportBuilder reportBuilder) { if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "Creating DialogIntent for " + reportFile + " exception=" + reportBuilder.getException()); final Intent dialogIntent = new Intent(context, config.reportDialogClass()); dialogIntent.putExtra(ACRAConstants.EXTRA_REPORT_FILE, reportFile); diff --git a/src/main/java/org/acra/collector/CollectorUtil.java b/src/main/java/org/acra/collector/CollectorUtil.java index 5767648edc..44a7067449 100644 --- a/src/main/java/org/acra/collector/CollectorUtil.java +++ b/src/main/java/org/acra/collector/CollectorUtil.java @@ -6,6 +6,7 @@ import java.io.Reader; public final class CollectorUtil { + private CollectorUtil(){} /** * Closes a Reader. diff --git a/src/main/java/org/acra/collector/CrashReportDataFactory.java b/src/main/java/org/acra/collector/CrashReportDataFactory.java index 3692143c76..613e0e984f 100644 --- a/src/main/java/org/acra/collector/CrashReportDataFactory.java +++ b/src/main/java/org/acra/collector/CrashReportDataFactory.java @@ -618,7 +618,7 @@ private String getStackTraceHash(Throwable th) { private Class getBuildConfigClass() throws ClassNotFoundException { final Class configuredBuildConfig = config.buildConfigClass(); if ((configuredBuildConfig != null) && !configuredBuildConfig.equals(Object.class)) { - // If set via annotations or programatically then it will have a real value, + // If set via annotations or programmatically then it will have a real value, // otherwise it will be Object.class (annotation default) or null (explicit programmatic). return configuredBuildConfig; } diff --git a/src/main/java/org/acra/collector/DeviceFeaturesCollector.java b/src/main/java/org/acra/collector/DeviceFeaturesCollector.java index bdccfd424f..fc6a2a9bee 100644 --- a/src/main/java/org/acra/collector/DeviceFeaturesCollector.java +++ b/src/main/java/org/acra/collector/DeviceFeaturesCollector.java @@ -30,6 +30,7 @@ * */ final class DeviceFeaturesCollector { + private DeviceFeaturesCollector(){} public static String getFeatures(@NonNull Context ctx) { diff --git a/src/main/java/org/acra/collector/DisplayManagerCollector.java b/src/main/java/org/acra/collector/DisplayManagerCollector.java index fec068b6d9..244cbec530 100644 --- a/src/main/java/org/acra/collector/DisplayManagerCollector.java +++ b/src/main/java/org/acra/collector/DisplayManagerCollector.java @@ -15,8 +15,9 @@ import java.lang.reflect.Field; final class DisplayManagerCollector { + private DisplayManagerCollector(){} - final static SparseArray mFlagsNames = new SparseArray(); + static final SparseArray mFlagsNames = new SparseArray(); public static String collectDisplays(@NonNull Context ctx) { final Display[] displays; diff --git a/src/main/java/org/acra/collector/DumpSysCollector.java b/src/main/java/org/acra/collector/DumpSysCollector.java index 65883f03c3..0f62320e0a 100644 --- a/src/main/java/org/acra/collector/DumpSysCollector.java +++ b/src/main/java/org/acra/collector/DumpSysCollector.java @@ -35,6 +35,7 @@ * */ final class DumpSysCollector { + private DumpSysCollector(){} /** * Collect results of the dumpsys meminfo command restricted to diff --git a/src/main/java/org/acra/collector/LogFileCollector.java b/src/main/java/org/acra/collector/LogFileCollector.java index 7a43849e72..84c7fc1d46 100644 --- a/src/main/java/org/acra/collector/LogFileCollector.java +++ b/src/main/java/org/acra/collector/LogFileCollector.java @@ -78,7 +78,7 @@ private static BufferedReader getReader(@NonNull Context context, @NonNull Strin // A file directly contained within the application files folder. inputStream = context.openFileInput(fileName); } - return new BufferedReader(new InputStreamReader(inputStream), 1024); + return new BufferedReader(new InputStreamReader(inputStream), 1024); //TODO: 1024 should be a constant. Use ACRAConstants.DEFAULT_BUFFER_SIZE_IN_BYTES ? } catch (FileNotFoundException e) { ACRA.log.e(LOG_TAG, "Cannot find application log file : '" + fileName + "'"); return new BufferedReader(new InputStreamReader(new ByteArrayInputStream(new byte[0]))); diff --git a/src/main/java/org/acra/collector/MediaCodecListCollector.java b/src/main/java/org/acra/collector/MediaCodecListCollector.java index 70f335ed6c..09f2611cd1 100644 --- a/src/main/java/org/acra/collector/MediaCodecListCollector.java +++ b/src/main/java/org/acra/collector/MediaCodecListCollector.java @@ -16,7 +16,6 @@ package org.acra.collector; -import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.media.MediaCodecInfo; import android.media.MediaCodecList; @@ -35,7 +34,8 @@ * * @author Kevin Gaudin */ -public class MediaCodecListCollector { +public final class MediaCodecListCollector { + private MediaCodecListCollector(){} private enum CodecType { AVC, H263, MPEG4, AAC @@ -110,7 +110,6 @@ private enum CodecType { * @return The media codecs information */ @NonNull - @SuppressLint("NewApi") //lint doesn't check complex NewApi blocks correctly public static String collectMediaCodecList() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { return ""; diff --git a/src/main/java/org/acra/collector/ReflectionCollector.java b/src/main/java/org/acra/collector/ReflectionCollector.java index b1cac3d617..638ab1098e 100644 --- a/src/main/java/org/acra/collector/ReflectionCollector.java +++ b/src/main/java/org/acra/collector/ReflectionCollector.java @@ -34,6 +34,7 @@ * */ final class ReflectionCollector { + private ReflectionCollector(){} /** * Retrieves key/value pairs from static fields of a class. diff --git a/src/main/java/org/acra/collector/SettingsCollector.java b/src/main/java/org/acra/collector/SettingsCollector.java index d2e6a095af..c9f90c1ac3 100644 --- a/src/main/java/org/acra/collector/SettingsCollector.java +++ b/src/main/java/org/acra/collector/SettingsCollector.java @@ -43,6 +43,8 @@ */ final class SettingsCollector { + private static final String ERROR = "Error: "; + private final Context context; private final ACRAConfiguration config; @@ -73,9 +75,9 @@ public String collectSystemSettings() { result.append(key.getName()).append("=").append(value).append("\n"); } } catch (@NonNull IllegalArgumentException e) { - ACRA.log.w(LOG_TAG, "Error : ", e); + ACRA.log.w(LOG_TAG, ERROR, e); } catch (@NonNull IllegalAccessException e) { - ACRA.log.w(LOG_TAG, "Error : ", e); + ACRA.log.w(LOG_TAG, ERROR, e); } } } @@ -102,9 +104,9 @@ public String collectSecureSettings() { result.append(key.getName()).append("=").append(value).append("\n"); } } catch (@NonNull IllegalArgumentException e) { - ACRA.log.w(LOG_TAG, "Error : ", e); + ACRA.log.w(LOG_TAG, ERROR, e); } catch (@NonNull IllegalAccessException e) { - ACRA.log.w(LOG_TAG, "Error : ", e); + ACRA.log.w(LOG_TAG, ERROR, e); } } } @@ -139,17 +141,17 @@ public String collectGlobalSettings() { } } } catch (@NonNull IllegalArgumentException e) { - ACRA.log.w(LOG_TAG, "Error : ", e); + ACRA.log.w(LOG_TAG, ERROR, e); } catch (@NonNull InvocationTargetException e) { - ACRA.log.w(LOG_TAG, "Error : ", e); + ACRA.log.w(LOG_TAG, ERROR, e); } catch (@NonNull NoSuchMethodException e) { - ACRA.log.w(LOG_TAG, "Error : ", e); + ACRA.log.w(LOG_TAG, ERROR, e); } catch (@NonNull SecurityException e) { - ACRA.log.w(LOG_TAG, "Error : ", e); + ACRA.log.w(LOG_TAG, ERROR, e); } catch (@NonNull ClassNotFoundException e) { - ACRA.log.w(LOG_TAG, "Error : ", e); + ACRA.log.w(LOG_TAG, ERROR, e); } catch (@NonNull IllegalAccessException e) { - ACRA.log.w(LOG_TAG, "Error : ", e); + ACRA.log.w(LOG_TAG, ERROR, e); } return result.toString(); diff --git a/src/main/java/org/acra/collector/ThreadCollector.java b/src/main/java/org/acra/collector/ThreadCollector.java index c9f4853c24..b3fbb66709 100644 --- a/src/main/java/org/acra/collector/ThreadCollector.java +++ b/src/main/java/org/acra/collector/ThreadCollector.java @@ -25,7 +25,8 @@ * @author Kevin Gaudin * */ -public class ThreadCollector { +public final class ThreadCollector { + private ThreadCollector(){} /** * Convenience method that collects some data identifying a Thread, usually the Thread which diff --git a/src/main/java/org/acra/config/ACRAConfiguration.java b/src/main/java/org/acra/config/ACRAConfiguration.java index 8bce294a53..b8bd8ae3ce 100644 --- a/src/main/java/org/acra/config/ACRAConfiguration.java +++ b/src/main/java/org/acra/config/ACRAConfiguration.java @@ -15,6 +15,7 @@ */ package org.acra.config; +import android.os.Build; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -34,6 +35,7 @@ import java.io.Serializable; import java.lang.annotation.Annotation; +import java.lang.reflect.Array; import java.security.KeyStore; import java.util.Arrays; import java.util.Collections; @@ -112,7 +114,8 @@ public final class ACRAConfiguration implements Serializable { /** * @param builder ConfigurationBuilder with which to initialise this {@link ACRAConfiguration}. */ - ACRAConfiguration(@Nullable ConfigurationBuilder builder) { + ACRAConfiguration(@NonNull ConfigurationBuilder builder) { + //noinspection ConstantConditions (don't rely on annotations alone) if (builder == null) { throw new NullPointerException("A ConfigurationBuilder must be supplied to ACRAConfiguration"); } @@ -237,7 +240,7 @@ public List getReportFields() { @NonNull @SuppressWarnings( "unused" ) public ACRAConfiguration setAdditionalDropboxTags(String[] additionalDropboxTags) { - this.additionalDropBoxTags = additionalDropboxTags; + this.additionalDropBoxTags = copyArray(additionalDropboxTags); return this; } @@ -251,7 +254,7 @@ public ACRAConfiguration setAdditionalDropboxTags(String[] additionalDropboxTags @NonNull @SuppressWarnings( "unused" ) public ACRAConfiguration setAdditionalSharedPreferences(String[] additionalSharedPreferences) { - this.additionalSharedPreferences = additionalSharedPreferences; + this.additionalSharedPreferences = copyArray(additionalSharedPreferences); return this; } @@ -279,7 +282,7 @@ public ACRAConfiguration setConnectionTimeout(Integer connectionTimeout) { @NonNull @SuppressWarnings( "unused" ) public ACRAConfiguration setCustomReportContent(ReportField[] customReportContent) { - this.customReportContent = customReportContent; + this.customReportContent = copyArray(customReportContent); return this; } @@ -405,7 +408,7 @@ public ACRAConfiguration setIncludeDropboxSystemTags(Boolean includeDropboxSyste @NonNull @SuppressWarnings( "unused" ) public ACRAConfiguration setLogcatArguments(String[] logcatArguments) { - this.logcatArguments = logcatArguments; + this.logcatArguments = copyArray(logcatArguments); return this; } @@ -772,7 +775,7 @@ public ACRAConfiguration setSendReportsAtShutdown(Boolean sendReportsAtShutdown) @NonNull @SuppressWarnings( "unused" ) public ACRAConfiguration setExcludeMatchingSharedPreferencesKeys(String[] excludeMatchingSharedPreferencesKeys) { - this.excludeMatchingSharedPreferencesKeys = excludeMatchingSharedPreferencesKeys; + this.excludeMatchingSharedPreferencesKeys = copyArray(excludeMatchingSharedPreferencesKeys); return this; } @@ -788,7 +791,7 @@ public ACRAConfiguration setExcludeMatchingSharedPreferencesKeys(String[] exclud @NonNull @SuppressWarnings( "unused" ) public ACRAConfiguration setExcludeMatchingSettingsKeys(String[] excludeMatchingSettingsKeys) { - this.excludeMatchingSettingsKeys = excludeMatchingSettingsKeys; + this.excludeMatchingSettingsKeys = copyArray(excludeMatchingSettingsKeys); return this; } @@ -874,17 +877,17 @@ public void setKeyStore(KeyStore keyStore) { */ @SuppressWarnings("unused") public void setReportSenderFactoryClasses(Class[] reportSenderFactoryClasses) { - this.reportSenderFactoryClasses = reportSenderFactoryClasses; + this.reportSenderFactoryClasses = copyArray(reportSenderFactoryClasses); } @SuppressWarnings("unused") public String[] additionalDropBoxTags() { - return additionalDropBoxTags; + return copyArray(additionalDropBoxTags); } @SuppressWarnings("unused") public String[] additionalSharedPreferences() { - return additionalSharedPreferences; + return copyArray(additionalSharedPreferences); } /** @@ -903,7 +906,7 @@ public int connectionTimeout() { @SuppressWarnings("unused") public ReportField[] customReportContent() { - return customReportContent; + return copyArray(customReportContent); } @SuppressWarnings("unused") @@ -948,7 +951,7 @@ public boolean includeDropBoxSystemTags() { @SuppressWarnings("unused") public String[] logcatArguments() { - return logcatArguments; + return copyArray(logcatArguments); } @SuppressWarnings("unused") @@ -1058,12 +1061,12 @@ public boolean sendReportsAtShutdown() { @SuppressWarnings("unused") public String[] excludeMatchingSharedPreferencesKeys() { - return excludeMatchingSharedPreferencesKeys; + return copyArray(excludeMatchingSharedPreferencesKeys); } @SuppressWarnings("unused") public String[] excludeMatchingSettingsKeys() { - return excludeMatchingSettingsKeys; + return copyArray(excludeMatchingSettingsKeys); } /** @@ -1108,7 +1111,7 @@ public Type reportType() { @SuppressWarnings("unused") public Class[] reportSenderFactoryClasses() { - return reportSenderFactoryClasses; + return copyArray(reportSenderFactoryClasses); } @SuppressWarnings("unused") @@ -1150,24 +1153,13 @@ public void checkCrashResources() throws ACRAConfigurationException { } } - @NonNull - private static String[] copyArray(@NonNull String[] source) { - final String[] target = new String[source.length]; - System.arraycopy(source, 0, target, 0, source.length); - return target; - } - - @NonNull - private static ReportField[] copyArray(@NonNull ReportField[] source) { - final ReportField[] target = new ReportField[source.length]; - System.arraycopy(source, 0, target, 0, source.length); - return target; - } - - @NonNull - private static Class[] copyArray(@NonNull Class[] source) { - final Class[] target = new Class[source.length]; - System.arraycopy(source, 0, target, 0, source.length); - return target; + private static T[] copyArray(@NonNull T[] source) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { + return Arrays.copyOf(source, source.length); + } + //noinspection unchecked + T[] result = (T[]) Array.newInstance(source.getClass().getComponentType(), source.length); + System.arraycopy(source, 0, result, 0, source.length); + return result; } } diff --git a/src/main/java/org/acra/dialog/CrashReportDialog.java b/src/main/java/org/acra/dialog/CrashReportDialog.java index 985f444fdf..03ca7591de 100644 --- a/src/main/java/org/acra/dialog/CrashReportDialog.java +++ b/src/main/java/org/acra/dialog/CrashReportDialog.java @@ -29,6 +29,7 @@ public class CrashReportDialog extends BaseCrashReportDialog implements DialogIn private static final String STATE_EMAIL = "email"; private static final String STATE_COMMENT = "comment"; + private static final int PADDING = 10; private LinearLayout scrollable; private EditText userCommentView; @@ -79,7 +80,7 @@ protected void buildAndShowDialog(@Nullable Bundle savedInstanceState){ protected View buildCustomView(@Nullable Bundle savedInstanceState) { final LinearLayout root = new LinearLayout(this); root.setOrientation(LinearLayout.VERTICAL); - root.setPadding(10, 10, 10, 10); + root.setPadding(PADDING, PADDING, PADDING, PADDING); root.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); root.setFocusable(true); root.setFocusableInTouchMode(true); @@ -151,7 +152,7 @@ protected EditText getCommentPrompt(CharSequence label, @Nullable CharSequence s final TextView labelView = new TextView(this); labelView.setText(label); - labelView.setPadding(labelView.getPaddingLeft(), 10, labelView.getPaddingRight(), labelView.getPaddingBottom()); + labelView.setPadding(labelView.getPaddingLeft(), PADDING, labelView.getPaddingRight(), labelView.getPaddingBottom()); scrollable.addView(labelView, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); EditText userCommentView = new EditText(this); @@ -174,7 +175,7 @@ protected EditText getEmailPrompt(CharSequence label, @Nullable CharSequence sav final TextView labelView = new TextView(this); labelView.setText(label); - labelView.setPadding(labelView.getPaddingLeft(), 10, labelView.getPaddingRight(), labelView.getPaddingBottom()); + labelView.setPadding(labelView.getPaddingLeft(), PADDING, labelView.getPaddingRight(), labelView.getPaddingBottom()); scrollable.addView(labelView); EditText userEmailView = new EditText(this); diff --git a/src/main/java/org/acra/file/CrashReportPersister.java b/src/main/java/org/acra/file/CrashReportPersister.java index 294d96571f..2a8c20bf58 100644 --- a/src/main/java/org/acra/file/CrashReportPersister.java +++ b/src/main/java/org/acra/file/CrashReportPersister.java @@ -122,7 +122,7 @@ public void store(@NonNull CrashReportData crashData, @NonNull File file) throws @NonNull private synchronized CrashReportData load(@NonNull Reader reader) throws IOException { int mode = NONE, unicode = 0, count = 0; - char nextChar, buf[] = new char[40]; + char nextChar, buf[] = new char[40]; //TODO: consider using a list instead of manually increasing the size when needed int offset = 0, keyLength = -1, intVal; boolean firstChar = true; diff --git a/src/main/java/org/acra/jraf/android/util/activitylifecyclecallbackscompat/MainLifecycleDispatcher.java b/src/main/java/org/acra/jraf/android/util/activitylifecyclecallbackscompat/MainLifecycleDispatcher.java index 9ad40d1451..ede3189c94 100644 --- a/src/main/java/org/acra/jraf/android/util/activitylifecyclecallbackscompat/MainLifecycleDispatcher.java +++ b/src/main/java/org/acra/jraf/android/util/activitylifecyclecallbackscompat/MainLifecycleDispatcher.java @@ -40,7 +40,7 @@ * {@link Application#registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks)} and * {@link Application#unregisterActivityLifecycleCallbacks(ActivityLifecycleCallbacks)}. */ -public class MainLifecycleDispatcher implements ActivityLifecycleCallbacksCompat { +public final class MainLifecycleDispatcher implements ActivityLifecycleCallbacksCompat { private static final MainLifecycleDispatcher INSTANCE = new MainLifecycleDispatcher(); @NonNull diff --git a/src/main/java/org/acra/prefs/PrefUtils.java b/src/main/java/org/acra/prefs/PrefUtils.java index 0ddcdf21a8..1359c6b585 100644 --- a/src/main/java/org/acra/prefs/PrefUtils.java +++ b/src/main/java/org/acra/prefs/PrefUtils.java @@ -5,6 +5,7 @@ import android.support.annotation.NonNull; public final class PrefUtils { + private PrefUtils(){} public static void save(@NonNull SharedPreferences.Editor editor) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { diff --git a/src/main/java/org/acra/sender/ReportSenderException.java b/src/main/java/org/acra/sender/ReportSenderException.java index b4c8cf4486..54a7b0004a 100644 --- a/src/main/java/org/acra/sender/ReportSenderException.java +++ b/src/main/java/org/acra/sender/ReportSenderException.java @@ -16,7 +16,7 @@ package org.acra.sender; /** - * This exception is thrown when an error ocurred while sending crash data in a + * This exception is thrown when an error occurred while sending crash data in a * {@link ReportSender} implementation. * * @author Kevin Gaudin diff --git a/src/main/java/org/acra/sender/SenderServiceStarter.java b/src/main/java/org/acra/sender/SenderServiceStarter.java index ac3421cb9a..158e732a3c 100644 --- a/src/main/java/org/acra/sender/SenderServiceStarter.java +++ b/src/main/java/org/acra/sender/SenderServiceStarter.java @@ -35,7 +35,7 @@ public void startService(boolean onlySendSilentReports, boolean approveReportsFi intent.putExtra(SenderService.EXTRA_ONLY_SEND_SILENT_REPORTS, onlySendSilentReports); intent.putExtra(SenderService.EXTRA_APPROVE_REPORTS_FIRST, approveReportsFirst); - intent.putExtra(SenderService.EXTRA_REPORT_SENDER_FACTORIES, new ArrayList(Arrays.asList(config.reportSenderFactoryClasses()))); + intent.putExtra(SenderService.EXTRA_REPORT_SENDER_FACTORIES, new ArrayList>(Arrays.asList(config.reportSenderFactoryClasses()))); intent.putExtra(SenderService.EXTRA_ACRA_CONFIG, config); context.startService(intent); diff --git a/src/main/java/org/acra/util/HttpRequest.java b/src/main/java/org/acra/util/HttpRequest.java index d2a4dc21dd..cd6cb46981 100644 --- a/src/main/java/org/acra/util/HttpRequest.java +++ b/src/main/java/org/acra/util/HttpRequest.java @@ -32,6 +32,13 @@ public final class HttpRequest { + private static final int HTTP_SUCCESS = 200; + private static final int HTTP_REDIRECT = 300; + private static final int HTTP_CLIENT_ERROR = 400; + private static final int HTTP_FORBIDDEN = 403; + private static final int HTTP_CONFLICT = 409; + private static final int MAX_HTTP_CODE = 600; + private final ACRAConfiguration config; private String login; private String password; @@ -140,16 +147,16 @@ public void send(@NonNull Context context, @NonNull URL url, @NonNull Method met final int responseCode = urlConnection.getResponseCode(); if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "Request response : " + responseCode + " : " + urlConnection.getResponseMessage()); - if ((responseCode >= 200) && (responseCode < 300)) { + if ((responseCode >= HTTP_SUCCESS) && (responseCode < HTTP_REDIRECT)) { // All is good ACRA.log.i(LOG_TAG, "Request received by server"); - } else if (responseCode == 403) { + } else if (responseCode == HTTP_FORBIDDEN) { // 403 is an explicit data validation refusal from the server. The request must not be repeated. Discard it. ACRA.log.w(LOG_TAG, "Data validation error on server - request will be discarded"); - } else if (responseCode == 409) { + } else if (responseCode == HTTP_CONFLICT) { // 409 means that the report has been received already. So we can discard it. ACRA.log.w(LOG_TAG, "Server has already received this post - request will be discarded"); - } else if ((responseCode >= 400) && (responseCode < 600)) { + } else if ((responseCode >= HTTP_CLIENT_ERROR) && (responseCode < MAX_HTTP_CODE)) { ACRA.log.w(LOG_TAG, "Could not send ACRA Post responseCode=" + responseCode + " message=" + urlConnection.getResponseMessage()); throw new IOException("Host returned error code " + responseCode); } else { diff --git a/src/main/java/org/acra/util/Installation.java b/src/main/java/org/acra/util/Installation.java index 38da1696bd..c72e2b5120 100644 --- a/src/main/java/org/acra/util/Installation.java +++ b/src/main/java/org/acra/util/Installation.java @@ -4,17 +4,17 @@ */ package org.acra.util; +import android.content.Context; +import android.support.annotation.NonNull; + +import org.acra.ACRA; + import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.util.UUID; -import org.acra.ACRA; - -import android.content.Context; -import android.support.annotation.NonNull; - import static org.acra.ACRA.LOG_TAG; /** @@ -28,12 +28,13 @@ * android developers blog. *

*/ -public class Installation { +public final class Installation { + private Installation(){} private static String sID; private static final String INSTALLATION = "ACRA-INSTALLATION"; - public synchronized static String id(@NonNull Context context) { + public static synchronized String id(@NonNull Context context) { if (sID == null) { final File installation = new File(context.getFilesDir(), INSTALLATION); try { diff --git a/src/main/java/org/acra/util/JSONReportBuilder.java b/src/main/java/org/acra/util/JSONReportBuilder.java index cbeac5f536..f59bcf2564 100644 --- a/src/main/java/org/acra/util/JSONReportBuilder.java +++ b/src/main/java/org/acra/util/JSONReportBuilder.java @@ -19,7 +19,8 @@ import static org.acra.ACRA.LOG_TAG; -public class JSONReportBuilder { +public final class JSONReportBuilder { + private JSONReportBuilder(){} /** *

* Create a JSONObject containing the whole report data with the most @@ -78,7 +79,7 @@ public static JSONObject buildJSONReport(@NonNull CrashReportData errorContent) if (key.containsKeyValuePairs()) { JSONObject subObject = new JSONObject(); String strContent = errorContent.getProperty(key); - reader = new BufferedReader(new StringReader(strContent), 1024); + reader = new BufferedReader(new StringReader(strContent), 1024); //TODO: 1024 should be a constant. Use ACRAConstants.DEFAULT_BUFFER_SIZE_IN_BYTES ? String line; try { while ((line = reader.readLine()) != null) { diff --git a/src/main/java/org/acra/util/ReportUtils.java b/src/main/java/org/acra/util/ReportUtils.java index 415bb21258..340438f07e 100644 --- a/src/main/java/org/acra/util/ReportUtils.java +++ b/src/main/java/org/acra/util/ReportUtils.java @@ -28,6 +28,7 @@ * @since 4.3.0 */ public final class ReportUtils { + private ReportUtils(){} /** * Calculates the free memory of the device. This is based on an inspection of the filesystem, which in android diff --git a/src/main/java/org/acra/util/ToastSender.java b/src/main/java/org/acra/util/ToastSender.java index 76295f2b11..be179d727f 100644 --- a/src/main/java/org/acra/util/ToastSender.java +++ b/src/main/java/org/acra/util/ToastSender.java @@ -1,10 +1,10 @@ package org.acra.util; -import org.acra.ACRA; - import android.content.Context; import android.widget.Toast; +import org.acra.ACRA; + import static org.acra.ACRA.LOG_TAG; /** @@ -14,6 +14,7 @@ * @since 4.3.0 */ public final class ToastSender { + private ToastSender(){} /** * Sends a Toast and ensures that any Exception thrown during sending is handled. diff --git a/src/test/java/org/acra/log/NonAndroidLog.java b/src/test/java/org/acra/log/NonAndroidLog.java index b176cfcbba..28f3f601a6 100644 --- a/src/test/java/org/acra/log/NonAndroidLog.java +++ b/src/test/java/org/acra/log/NonAndroidLog.java @@ -31,7 +31,7 @@ public final class NonAndroidLog implements ACRALog { /** * Any log that is output at level less that the supplied logLevel will be ignored. *

- * The deault log level is {@link NonAndroidLog#VERBOSE} + * The default log level is {@link NonAndroidLog#VERBOSE} *

* * @param logLevel LogLevel to use to filter log output.