From f022269b6f9278700306ebe6b23a0c789acda050 Mon Sep 17 00:00:00 2001 From: Floens Date: Sun, 14 Jan 2018 17:29:37 +0100 Subject: [PATCH] add crash reporting reports crashes with acra to an instance of acralyzer. a new setting controls the usage of it. required some workarounds for the application class. include the default proguard file, includes some other proguard rules acra requires (and is cleaner anyway). closes #390 --- Clover/app/build.gradle | 7 +- Clover/app/proguard.cfg | 13 +- .../java/org/floens/chan/ChanApplication.java | 11 ++ .../src/main/java/org/floens/chan/Chan.java | 13 +- .../chan/core/settings/ChanSettings.java | 4 + .../ui/controller/MainSettingsController.java | 16 +++ .../org/floens/chan/utils/AndroidUtils.java | 18 ++- Clover/app/src/main/res/values/strings.xml | 4 + .../java/org/floens/chan/ChanApplication.java | 121 ++++++++++++++++++ 9 files changed, 195 insertions(+), 12 deletions(-) rename Clover/app/src/{main => debug}/java/org/floens/chan/ChanApplication.java (75%) create mode 100644 Clover/app/src/release/java/org/floens/chan/ChanApplication.java diff --git a/Clover/app/build.gradle b/Clover/app/build.gradle index cf837499..708a8b34 100644 --- a/Clover/app/build.gradle +++ b/Clover/app/build.gradle @@ -78,6 +78,7 @@ android { resValue "string", "app_name", "Clover" resValue "string", "app_flavor_name", "" buildConfigField "String", "UPDATE_API_ENDPOINT", "\"https://floens.github.io/Clover/api/update\"" + buildConfigField "String", "CRASH_REPORT_ENDPOINT", "\"https://acra.floens.org/clover/report\"" } dev { @@ -86,6 +87,7 @@ android { resValue "string", "app_name", "Clover dev" resValue "string", "app_flavor_name", "" buildConfigField "String", "UPDATE_API_ENDPOINT", "\"\"" + buildConfigField "String", "CRASH_REPORT_ENDPOINT", "\"\"" } fdroid { @@ -94,6 +96,7 @@ android { resValue "string", "app_name", "Clover" resValue "string", "app_flavor_name", "F-Droid" buildConfigField "String", "UPDATE_API_ENDPOINT", "\"https://floens.github.io/Clover/api/update\"" + buildConfigField "String", "CRASH_REPORT_ENDPOINT", "\"https://acra.floens.org/clover/report\"" } } @@ -103,7 +106,7 @@ android { signingConfig signingConfigs.release } minifyEnabled true - proguardFiles 'proguard.cfg' + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard.cfg' } debug { @@ -141,4 +144,6 @@ dependencies { implementation 'com.google.code.gson:gson:2.8.1' implementation 'me.xdrop:fuzzywuzzy:1.1.9' implementation 'org.codejargon.feather:feather:1.0' + + releaseImplementation 'ch.acra:acra-http:5.0.1' } diff --git a/Clover/app/proguard.cfg b/Clover/app/proguard.cfg index 5bc091ac..eb1eabd6 100644 --- a/Clover/app/proguard.cfg +++ b/Clover/app/proguard.cfg @@ -110,4 +110,15 @@ -keep public class android.support.v7.widget.RecyclerView -keep public class android.support.v4.widget.SlidingPaneLayout --dontwarn dagger.internal.** +# Keep Feather inject working. +-keepclassmembers,allowobfuscation class * { + @javax.inject.* *; + (); +} + +# ACRA stuff +# ACRA loads Plugins using reflection, so we need to keep all Plugin classes +-keep class * extends @android.support.annotation.Keep org.acra.** {*;} + +# ACRA uses enum fields in annotations, so we have to keep those +-keep enum org.acra.** {*;} diff --git a/Clover/app/src/main/java/org/floens/chan/ChanApplication.java b/Clover/app/src/debug/java/org/floens/chan/ChanApplication.java similarity index 75% rename from Clover/app/src/main/java/org/floens/chan/ChanApplication.java rename to Clover/app/src/debug/java/org/floens/chan/ChanApplication.java index 4001cd19..ebad39ed 100644 --- a/Clover/app/src/main/java/org/floens/chan/ChanApplication.java +++ b/Clover/app/src/debug/java/org/floens/chan/ChanApplication.java @@ -17,5 +17,16 @@ */ package org.floens.chan; +/** + * The ChanApplication belonging to the debug configuration. + * + * It does not have acra enabled, unlike the release version, and immediately calls initialize. + */ public class ChanApplication extends Chan { + @Override + public void onCreate() { + super.onCreate(); + + initialize(); + } } diff --git a/Clover/app/src/main/java/org/floens/chan/Chan.java b/Clover/app/src/main/java/org/floens/chan/Chan.java index 8b4d4994..15d8d89f 100644 --- a/Clover/app/src/main/java/org/floens/chan/Chan.java +++ b/Clover/app/src/main/java/org/floens/chan/Chan.java @@ -20,6 +20,7 @@ package org.floens.chan; import android.annotation.SuppressLint; import android.app.Activity; import android.app.Application; +import android.content.Context; import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; @@ -41,6 +42,7 @@ import javax.inject.Inject; import de.greenrobot.event.EventBus; +@SuppressLint("Registered") // extended by ChanApplication, which is registered in the manifest. public class Chan extends Application implements UserAgentProvider, Application.ActivityLifecycleCallbacks { private static final String TAG = "ChanApplication"; @@ -55,6 +57,7 @@ public class Chan extends Application implements UserAgentProvider, Application. @Inject DatabaseManager databaseManager; + private Feather feather; public Chan() { @@ -75,15 +78,17 @@ public class Chan extends Application implements UserAgentProvider, Application. } @Override - public void onCreate() { - super.onCreate(); + protected void attachBaseContext(Context base) { + super.attachBaseContext(base); + + AndroidUtils.init(this); + } + public void initialize() { final long startTime = Time.startTiming(); registerActivityLifecycleCallbacks(this); - AndroidUtils.init(this); - userAgent = createUserAgent(); initializeGraph(); diff --git a/Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java b/Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java index 79924098..c78d3f8c 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java +++ b/Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java @@ -155,6 +155,8 @@ public class ChanSettings { public static final LongSetting updateCheckTime; public static final LongSetting updateCheckInterval; + public static final BooleanSetting crashReporting; + static { SharedPreferences p = AndroidUtils.getPreferences(); @@ -238,6 +240,8 @@ public class ChanSettings { updateCheckTime = new LongSetting(p, "update_check_time", 0L); updateCheckInterval = new LongSetting(p, "update_check_interval", UpdateManager.DEFAULT_UPDATE_CHECK_INTERVAL_MS); + crashReporting = new BooleanSetting(p, "preference_crash_reporting", true); + // Old (but possibly still in some users phone) // preference_board_view_mode default "list" // preference_board_editor_filler default false diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/MainSettingsController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/MainSettingsController.java index e2bc0dda..65652bfa 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/MainSettingsController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/MainSettingsController.java @@ -29,6 +29,7 @@ import org.floens.chan.R; import org.floens.chan.core.presenter.SettingsPresenter; import org.floens.chan.core.settings.ChanSettings; import org.floens.chan.ui.activity.StartActivity; +import org.floens.chan.ui.settings.BooleanSettingView; import org.floens.chan.ui.settings.LinkSettingView; import org.floens.chan.ui.settings.SettingView; import org.floens.chan.ui.settings.SettingsController; @@ -49,6 +50,7 @@ public class MainSettingsController extends SettingsController implements Settin private SettingView developerView; private LinkSettingView sitesSetting; private LinkSettingView filtersSetting; + private SettingView crashReportSetting; public MainSettingsController(Context context) { super(context); @@ -106,6 +108,15 @@ public class MainSettingsController extends SettingsController implements Settin R.string.setting_watch_summary_enabled : R.string.setting_watch_summary_disabled); } + @Override + public void onPreferenceChange(SettingView item) { + super.onPreferenceChange(item); + if (item == crashReportSetting) { + Toast.makeText(context, R.string.settings_crash_reporting_toggle_notice, + Toast.LENGTH_LONG).show(); + } + } + private void populatePreferences() { // General group { @@ -153,6 +164,11 @@ public class MainSettingsController extends SettingsController implements Settin setupUpdateSetting(about); + crashReportSetting = about.add(new BooleanSettingView(this, + ChanSettings.crashReporting, + R.string.settings_crash_reporting, + R.string.settings_crash_reporting_description)); + setupExtraAboutSettings(about, version); about.add(new LinkSettingView(this, diff --git a/Clover/app/src/main/java/org/floens/chan/utils/AndroidUtils.java b/Clover/app/src/main/java/org/floens/chan/utils/AndroidUtils.java index e72420a6..dad104d8 100644 --- a/Clover/app/src/main/java/org/floens/chan/utils/AndroidUtils.java +++ b/Clover/app/src/main/java/org/floens/chan/utils/AndroidUtils.java @@ -75,14 +75,16 @@ public class AndroidUtils { private static final Handler mainHandler = new Handler(Looper.getMainLooper()); public static void init(Application application) { - AndroidUtils.application = application; + if (AndroidUtils.application == null) { + AndroidUtils.application = application; - ROBOTO_MEDIUM = getTypeface("Roboto-Medium.ttf"); - ROBOTO_MEDIUM_ITALIC = getTypeface("Roboto-MediumItalic.ttf"); - ROBOTO_CONDENSED_REGULAR = getTypeface("RobotoCondensed-Regular.ttf"); + ROBOTO_MEDIUM = getTypeface("Roboto-Medium.ttf"); + ROBOTO_MEDIUM_ITALIC = getTypeface("Roboto-MediumItalic.ttf"); + ROBOTO_CONDENSED_REGULAR = getTypeface("RobotoCondensed-Regular.ttf"); - connectivityManager = (ConnectivityManager) - application.getSystemService(Context.CONNECTIVITY_SERVICE); + connectivityManager = (ConnectivityManager) + application.getSystemService(Context.CONNECTIVITY_SERVICE); + } } public static Resources getRes() { @@ -101,6 +103,10 @@ public class AndroidUtils { return PreferenceManager.getDefaultSharedPreferences(application); } + public static SharedPreferences getPreferences(Application application) { + return PreferenceManager.getDefaultSharedPreferences(application); + } + public static SharedPreferences getPreferences(String name) { return application.getSharedPreferences(name, Context.MODE_PRIVATE); } diff --git a/Clover/app/src/main/res/values/strings.xml b/Clover/app/src/main/res/values/strings.xml index a3b6c567..7adc1b35 100644 --- a/Clover/app/src/main/res/values/strings.xml +++ b/Clover/app/src/main/res/values/strings.xml @@ -421,6 +421,10 @@ Re-enable this permission in the app settings if you permanently disabled it." About Check for updates + Report crashes + Crash reporting creates reports of errors in Clover. + Crash reports do not collect any personally identifiable information. + Setting will be applied on next app start. Released under the GNU GPLv3 license Tap to see license Open Source Licenses diff --git a/Clover/app/src/release/java/org/floens/chan/ChanApplication.java b/Clover/app/src/release/java/org/floens/chan/ChanApplication.java new file mode 100644 index 00000000..97f675e5 --- /dev/null +++ b/Clover/app/src/release/java/org/floens/chan/ChanApplication.java @@ -0,0 +1,121 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.floens.chan; + +import android.content.Context; + +import org.acra.ACRA; +import org.acra.annotation.AcraCore; +import org.acra.annotation.AcraHttpSender; +import org.acra.data.StringFormat; +import org.acra.sender.HttpSender; +import org.floens.chan.core.settings.ChanSettings; + +import static org.acra.ReportField.ANDROID_VERSION; +import static org.acra.ReportField.APP_VERSION_CODE; +import static org.acra.ReportField.APP_VERSION_NAME; +import static org.acra.ReportField.AVAILABLE_MEM_SIZE; +import static org.acra.ReportField.BRAND; +import static org.acra.ReportField.BUILD; +import static org.acra.ReportField.BUILD_CONFIG; +import static org.acra.ReportField.LOGCAT; +import static org.acra.ReportField.MEDIA_CODEC_LIST; +import static org.acra.ReportField.PACKAGE_NAME; +import static org.acra.ReportField.PHONE_MODEL; +import static org.acra.ReportField.PRODUCT; +import static org.acra.ReportField.REPORT_ID; +import static org.acra.ReportField.STACK_TRACE; +import static org.acra.ReportField.TOTAL_MEM_SIZE; +import static org.acra.ReportField.USER_APP_START_DATE; +import static org.acra.ReportField.USER_CRASH_DATE; + +/** + * The ChanApplication belonging to the release configuration. + *

+ * It has acra enabled. Acra unfortunately believes it needs to have annotations on our + * application class, which I find really intrusive. We already had ChanApplication pointing to + * Chan, so we now have a debug and release variant of ChanApplication. The real initialization + * is done with the {@link Chan#initialize()} method, which does not get called from acra + * processes. + */ +@AcraCore( + sharedPreferencesName = "acra_preferences", + alsoReportToAndroidFramework = true, + buildConfigClass = BuildConfig.class, + reportFormat = StringFormat.JSON, + reportContent = { + // Required + REPORT_ID, + + // What app version + APP_VERSION_CODE, + APP_VERSION_NAME, + PACKAGE_NAME, + BUILD_CONFIG, + + // What phone + PHONE_MODEL, + BRAND, + PRODUCT, + + // What Android version + ANDROID_VERSION, + BUILD, + + // Memory details + TOTAL_MEM_SIZE, + AVAILABLE_MEM_SIZE, + + // Useful for webm debugging + MEDIA_CODEC_LIST, + + // The error + STACK_TRACE, + LOGCAT, + USER_APP_START_DATE, + USER_CRASH_DATE + } +) +@AcraHttpSender( + httpMethod = HttpSender.Method.PUT, + uri = BuildConfig.CRASH_REPORT_ENDPOINT +) +public class ChanApplication extends Chan { + @Override + public void onCreate() { + super.onCreate(); + + // Do not initialize again if running from the crash reporting process. + if (!ACRA.isACRASenderServiceProcess()) { + initialize(); + } + } + + @Override + protected void attachBaseContext(Context base) { + super.attachBaseContext(base); + + if (enableAcra()) { + ACRA.init(this); + } + } + + private boolean enableAcra() { + return !BuildConfig.CRASH_REPORT_ENDPOINT.isEmpty() && ChanSettings.crashReporting.get(); + } +}