From 5bdfb7df51ed2612da51c1003786474ac1f02ad5 Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Sun, 21 Mar 2021 22:50:23 +0900 Subject: [PATCH 1/2] Add sentry to github builds This commit adds github and fdroid product flavors. Sentry is added only to github builds. Sentry is opt-in. --- .gitignore | 1 + app/build.gradle | 21 +- app/sentry.gradle | 13 ++ .../fragments/settings/FlavorSettings.java | 40 ++++ .../java/awaisomereport/CrashHandler.java | 56 ++++++ app/src/github/AndroidManifest.xml | 10 + .../fragments/settings/FlavorSettings.java | 83 ++++++++ .../java/awaisomereport/CrashHandler.java | 59 ++++++ .../instagrabber/InstaGrabberApplication.java | 39 ++-- .../dialogs/ConfirmDialogFragment.java | 69 ++++--- .../DirectMessageSettingsFragment.java | 8 +- .../settings/GeneralPreferencesFragment.java | 10 + .../fragments/settings/IFlavorSettings.java | 14 ++ .../fragments/settings/PreferenceKeys.java | 1 + .../fragments/settings/SettingCategory.java | 6 + .../awais/instagrabber/utils/Constants.java | 2 + .../instagrabber/utils/SettingsHelper.java | 7 +- .../java/awaisomereport/CrashReporter.java | 179 ++---------------- .../awaisomereport/CrashReporterHelper.java | 134 +++++++++++++ .../awaisomereport/ErrorReporterActivity.java | 5 +- .../java/awaisomereport/ICrashHandler.java | 7 + app/src/main/res/values/strings.xml | 5 + 22 files changed, 553 insertions(+), 216 deletions(-) create mode 100644 app/sentry.gradle create mode 100644 app/src/fdroid/java/awais/instagrabber/fragments/settings/FlavorSettings.java create mode 100644 app/src/fdroid/java/awaisomereport/CrashHandler.java create mode 100644 app/src/github/AndroidManifest.xml create mode 100644 app/src/github/java/awais/instagrabber/fragments/settings/FlavorSettings.java create mode 100644 app/src/github/java/awaisomereport/CrashHandler.java create mode 100644 app/src/main/java/awais/instagrabber/fragments/settings/IFlavorSettings.java create mode 100644 app/src/main/java/awais/instagrabber/fragments/settings/SettingCategory.java create mode 100644 app/src/main/java/awaisomereport/CrashReporterHelper.java create mode 100644 app/src/main/java/awaisomereport/ICrashHandler.java diff --git a/.gitignore b/.gitignore index 4d79357f..d849b086 100755 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ .externalNativeBuild .cxx app/release +/sentry.properties \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 74bbf1dc..d945f017 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,6 @@ apply plugin: 'com.android.application' apply plugin: "androidx.navigation.safeargs" +apply from: 'sentry.gradle' android { compileSdkVersion 29 @@ -48,8 +49,22 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } -} + flavorDimensions "repo" + + productFlavors { + github { + dimension "repo" + versionNameSuffix "-github" + buildConfigField("String", "dsn", SENTRY_DSN) + } + + fdroid { + dimension "repo" + versionNameSuffix "-fdroid" + } + } +} configurations.all { resolutionStrategy.cacheChangingModulesFor 0, 'seconds' @@ -81,7 +96,7 @@ dependencies { implementation 'androidx.palette:palette:1.0.0' implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" - implementation 'com.google.guava:guava:27.1-jre' + implementation 'com.google.guava:guava:27.0.1-android' // Room def room_version = "2.2.6" @@ -116,5 +131,7 @@ dependencies { debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.6' + githubImplementation 'io.sentry:sentry-android:4.3.0' + testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1' } diff --git a/app/sentry.gradle b/app/sentry.gradle new file mode 100644 index 00000000..f2b24aa7 --- /dev/null +++ b/app/sentry.gradle @@ -0,0 +1,13 @@ +def dsnKey = 'DSN' +def defaultDsn = '\"\"' + +final Properties properties = new Properties() +File propertiesFile = rootProject.file('sentry.properties') +if (!propertiesFile.exists()) { + propertiesFile.createNewFile() +} +properties.load(new FileInputStream(propertiesFile)) + +ext{ + SENTRY_DSN = properties.getProperty(dsnKey, defaultDsn) +} \ No newline at end of file diff --git a/app/src/fdroid/java/awais/instagrabber/fragments/settings/FlavorSettings.java b/app/src/fdroid/java/awais/instagrabber/fragments/settings/FlavorSettings.java new file mode 100644 index 00000000..b30e263e --- /dev/null +++ b/app/src/fdroid/java/awais/instagrabber/fragments/settings/FlavorSettings.java @@ -0,0 +1,40 @@ +package awais.instagrabber.fragments.settings; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.fragment.app.FragmentManager; +import androidx.preference.Preference; + +import java.util.Collections; +import java.util.List; + +import awais.instagrabber.fragments.settings.IFlavorSettings; +import awais.instagrabber.fragments.settings.SettingCategory; + +public final class FlavorSettings implements IFlavorSettings { + + private static FlavorSettings instance; + + private FlavorSettings() { + } + + public static FlavorSettings getInstance() { + if (instance == null) { + instance = new FlavorSettings(); + } + return instance; + } + + @NonNull + @Override + public List getPreferences(@NonNull final Context context, + @NonNull final FragmentManager fragmentManager, + @NonNull final SettingCategory settingCategory) { + // switch (settingCategory) { + // default: + // break; + // } + return Collections.emptyList(); + } +} diff --git a/app/src/fdroid/java/awaisomereport/CrashHandler.java b/app/src/fdroid/java/awaisomereport/CrashHandler.java new file mode 100644 index 00000000..2f27d1c3 --- /dev/null +++ b/app/src/fdroid/java/awaisomereport/CrashHandler.java @@ -0,0 +1,56 @@ +package awaisomereport; + +import android.app.Application; + +import androidx.annotation.NonNull; + +public class CrashHandler implements ICrashHandler { + private static final String TAG = CrashHandler.class.getSimpleName(); + + private final Application application; + + public CrashHandler(@NonNull final Application application) { + this.application = application; + } + + @Override + public void uncaughtException(@NonNull final Thread t, + @NonNull final Throwable exception, + @NonNull final Thread.UncaughtExceptionHandler defaultEH) { + CrashReporterHelper.startErrorReporterActivity(application, exception); + // zipLogs(); + defaultEH.uncaughtException(t, exception); + } + + // public synchronized CrashReporter zipLogs() { + // final File logDir = Utils.logCollector != null ? Utils.logCollector.getLogDir() : + // new File(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? application.getDataDir() : application.getFilesDir(), "crashlogs"); + // + // try (final FileOutputStream fos = new FileOutputStream(crashLogsZip); + // final ZipOutputStream zos = new ZipOutputStream(fos)) { + // + // final File[] files = logDir.listFiles(); + // + // if (files != null) { + // zos.setLevel(5); + // byte[] buffer; + // for (final File file : files) { + // if (file != null && file.length() > 0) { + // buffer = new byte[1024]; + // try (final FileInputStream fis = new FileInputStream(file)) { + // zos.putNextEntry(new ZipEntry(file.getName())); + // int length; + // while ((length = fis.read(buffer)) > 0) zos.write(buffer, 0, length); + // zos.closeEntry(); + // } + // } + // } + // } + // + // } catch (final Exception e) { + // if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); + // } + // + // return this; + // } +} diff --git a/app/src/github/AndroidManifest.xml b/app/src/github/AndroidManifest.xml new file mode 100644 index 00000000..4c6a6fe0 --- /dev/null +++ b/app/src/github/AndroidManifest.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/github/java/awais/instagrabber/fragments/settings/FlavorSettings.java b/app/src/github/java/awais/instagrabber/fragments/settings/FlavorSettings.java new file mode 100644 index 00000000..85adb8a9 --- /dev/null +++ b/app/src/github/java/awais/instagrabber/fragments/settings/FlavorSettings.java @@ -0,0 +1,83 @@ +package awais.instagrabber.fragments.settings; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.fragment.app.FragmentManager; +import androidx.preference.Preference; + +import com.google.common.collect.ImmutableList; + +import java.util.Collections; +import java.util.List; + +import awais.instagrabber.R; +import awais.instagrabber.dialogs.ConfirmDialogFragment; + +import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_SENTRY; +import static awais.instagrabber.utils.Utils.settingsHelper; + +public final class FlavorSettings implements IFlavorSettings { + + private static FlavorSettings instance; + + private FlavorSettings() { + } + + public static FlavorSettings getInstance() { + if (instance == null) { + instance = new FlavorSettings(); + } + return instance; + } + + @NonNull + @Override + public List getPreferences(@NonNull final Context context, + @NonNull final FragmentManager fragmentManager, + @NonNull final SettingCategory settingCategory) { + switch (settingCategory) { + case GENERAL: + return getGeneralPrefs(context, fragmentManager); + default: + break; + } + return Collections.emptyList(); + } + + private List getGeneralPrefs(@NonNull final Context context, + @NonNull final FragmentManager fragmentManager) { + return ImmutableList.of( + getSentryPreference(context, fragmentManager) + ); + } + + private Preference getSentryPreference(@NonNull final Context context, + @NonNull final FragmentManager fragmentManager) { + if (!settingsHelper.hasPreference(PREF_ENABLE_SENTRY)) { + // disabled by default + settingsHelper.putBoolean(PREF_ENABLE_SENTRY, false); + } + return PreferenceHelper.getSwitchPreference( + context, + PREF_ENABLE_SENTRY, + R.string.enable_sentry, + R.string.sentry_summary, + false, + (preference, newValue) -> { + if (!(newValue instanceof Boolean)) return true; + final boolean enabled = (Boolean) newValue; + if (enabled) { + final ConfirmDialogFragment dialogFragment = ConfirmDialogFragment.newInstance( + 111, + 0, + R.string.sentry_start_next_launch, + R.string.ok, + 0, + 0); + dialogFragment.show(fragmentManager, "sentry_dialog"); + } + return true; + }); + } +} diff --git a/app/src/github/java/awaisomereport/CrashHandler.java b/app/src/github/java/awaisomereport/CrashHandler.java new file mode 100644 index 00000000..cd28eaa2 --- /dev/null +++ b/app/src/github/java/awaisomereport/CrashHandler.java @@ -0,0 +1,59 @@ +package awaisomereport; + +import android.app.Application; + +import androidx.annotation.NonNull; + +import awais.instagrabber.BuildConfig; +import awais.instagrabber.fragments.settings.PreferenceKeys; +import io.sentry.SentryLevel; +import io.sentry.android.core.SentryAndroid; +import io.sentry.protocol.Contexts; +import io.sentry.protocol.Device; + +import static awais.instagrabber.utils.Utils.settingsHelper; + +public class CrashHandler implements ICrashHandler { + private static final String TAG = CrashHandler.class.getSimpleName(); + + private final Application application; + private final boolean enabled; + + public CrashHandler(@NonNull final Application application) { + this.application = application; + if (!settingsHelper.hasPreference(PreferenceKeys.PREF_ENABLE_SENTRY)) { + // disabled by default (change to true if we need enabled by default) + enabled = false; + } else { + enabled = settingsHelper.getBoolean(PreferenceKeys.PREF_ENABLE_SENTRY); + } + if (!enabled) return; + SentryAndroid.init(application, options -> { + options.setDsn(BuildConfig.dsn); + options.setDiagnosticLevel(SentryLevel.ERROR); + options.setBeforeSend((event, hint) -> { + // Removing unneeded info from event + final Contexts contexts = event.getContexts(); + final Device device = contexts.getDevice(); + device.setName(null); + device.setTimezone(null); + device.setCharging(null); + device.setBootTime(null); + device.setFreeStorage(null); + device.setBatteryTemperature(null); + return event; + }); + }); + } + + @Override + public void uncaughtException(@NonNull final Thread t, + @NonNull final Throwable exception, + @NonNull final Thread.UncaughtExceptionHandler defaultEH) { + // When enabled, Sentry auto captures unhandled exceptions + if (!enabled) { + CrashReporterHelper.startErrorReporterActivity(application, exception); + } + defaultEH.uncaughtException(t, exception); + } +} diff --git a/app/src/main/java/awais/instagrabber/InstaGrabberApplication.java b/app/src/main/java/awais/instagrabber/InstaGrabberApplication.java index b90c8343..a033473d 100644 --- a/app/src/main/java/awais/instagrabber/InstaGrabberApplication.java +++ b/app/src/main/java/awais/instagrabber/InstaGrabberApplication.java @@ -18,14 +18,12 @@ import awais.instagrabber.utils.LocaleUtils; import awais.instagrabber.utils.SettingsHelper; import awais.instagrabber.utils.TextUtils; import awaisomereport.CrashReporter; -//import awaisomereport.LogCollector; import static awais.instagrabber.utils.CookieUtils.NET_COOKIE_MANAGER; import static awais.instagrabber.utils.Utils.applicationHandler; import static awais.instagrabber.utils.Utils.cacheDir; import static awais.instagrabber.utils.Utils.clipboardManager; import static awais.instagrabber.utils.Utils.datetimeParser; -//import static awais.instagrabber.utils.Utils.logCollector; import static awais.instagrabber.utils.Utils.settingsHelper; public final class InstaGrabberApplication extends Application { @@ -34,16 +32,16 @@ public final class InstaGrabberApplication extends Application { @Override public void onCreate() { super.onCreate(); - // final Set requestListeners = new HashSet<>(); - // requestListeners.add(new RequestLoggingListener()); - final ImagePipelineConfig imagePipelineConfig = ImagePipelineConfig - .newBuilder(this) - // .setMainDiskCacheConfig(diskCacheConfig) - // .setRequestListeners(requestListeners) - .setDownsampleEnabled(true) - .build(); - Fresco.initialize(this, imagePipelineConfig); - // FLog.setMinimumLoggingLevel(FLog.VERBOSE); + CookieHandler.setDefault(NET_COOKIE_MANAGER); + + if (settingsHelper == null) { + settingsHelper = new SettingsHelper(this); + } + + if (!BuildConfig.DEBUG) { + CrashReporter.get(this).start(); + } + // logCollector = new LogCollector(this); if (BuildConfig.DEBUG) { try { @@ -55,13 +53,16 @@ public final class InstaGrabberApplication extends Application { } } - if (!BuildConfig.DEBUG) CrashReporter.get(this).start(); -// logCollector = new LogCollector(this); - - CookieHandler.setDefault(NET_COOKIE_MANAGER); - - if (settingsHelper == null) - settingsHelper = new SettingsHelper(this); + // final Set requestListeners = new HashSet<>(); + // requestListeners.add(new RequestLoggingListener()); + final ImagePipelineConfig imagePipelineConfig = ImagePipelineConfig + .newBuilder(this) + // .setMainDiskCacheConfig(diskCacheConfig) + // .setRequestListeners(requestListeners) + .setDownsampleEnabled(true) + .build(); + Fresco.initialize(this, imagePipelineConfig); + // FLog.setMinimumLoggingLevel(FLog.VERBOSE); if (applicationHandler == null) { applicationHandler = new Handler(getApplicationContext().getMainLooper()); diff --git a/app/src/main/java/awais/instagrabber/dialogs/ConfirmDialogFragment.java b/app/src/main/java/awais/instagrabber/dialogs/ConfirmDialogFragment.java index caba0b38..5b8eb3c9 100644 --- a/app/src/main/java/awais/instagrabber/dialogs/ConfirmDialogFragment.java +++ b/app/src/main/java/awais/instagrabber/dialogs/ConfirmDialogFragment.java @@ -8,6 +8,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.fragment.app.DialogFragment; +import androidx.fragment.app.Fragment; import com.google.android.material.dialog.MaterialAlertDialogBuilder; @@ -17,6 +18,9 @@ public class ConfirmDialogFragment extends DialogFragment { private Context context; private ConfirmDialogFragmentCallback callback; + private final int defaultPositiveButtonText = R.string.ok; + // private final int defaultNegativeButtonText = R.string.cancel; + @NonNull public static ConfirmDialogFragment newInstance(final int requestCode, @StringRes final int title, @@ -26,11 +30,21 @@ public class ConfirmDialogFragment extends DialogFragment { @StringRes final int neutralText) { Bundle args = new Bundle(); args.putInt("requestCode", requestCode); - args.putInt("title", title); - args.putInt("message", message); - args.putInt("positive", positiveText); - args.putInt("negative", negativeText); - args.putInt("neutral", neutralText); + if (title != 0) { + args.putInt("title", title); + } + if (message != 0) { + args.putInt("message", message); + } + if (positiveText != 0) { + args.putInt("positive", positiveText); + } + if (negativeText != 0) { + args.putInt("negative", negativeText); + } + if (neutralText != 0) { + args.putInt("neutral", neutralText); + } ConfirmDialogFragment fragment = new ConfirmDialogFragment(); fragment.setArguments(args); return fragment; @@ -41,10 +55,9 @@ public class ConfirmDialogFragment extends DialogFragment { @Override public void onAttach(@NonNull final Context context) { super.onAttach(context); - try { - callback = (ConfirmDialogFragmentCallback) getParentFragment(); - } catch (ClassCastException e) { - throw new ClassCastException("Calling fragment must implement ConfirmDialogFragmentCallback interface"); + final Fragment parentFragment = getParentFragment(); + if (parentFragment instanceof ConfirmDialogFragmentCallback) { + callback = (ConfirmDialogFragmentCallback) parentFragment; } this.context = context; } @@ -53,38 +66,42 @@ public class ConfirmDialogFragment extends DialogFragment { @Override public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) { final Bundle arguments = getArguments(); - int title = -1; - int message = -1; - int positiveButtonText = R.string.ok; - int negativeButtonText = R.string.cancel; - int neutralButtonText = -1; + int title = 0; + int message = 0; + int neutralButtonText = 0; + int negativeButtonText = 0; + + final int positiveButtonText; final int requestCode; if (arguments != null) { - title = arguments.getInt("title", -1); - message = arguments.getInt("message", -1); - positiveButtonText = arguments.getInt("positive", R.string.ok); - negativeButtonText = arguments.getInt("negative", R.string.cancel); - neutralButtonText = arguments.getInt("neutral", -1); + title = arguments.getInt("title", 0); + message = arguments.getInt("message", 0); + positiveButtonText = arguments.getInt("positive", defaultPositiveButtonText); + negativeButtonText = arguments.getInt("negative", 0); + neutralButtonText = arguments.getInt("neutral", 0); requestCode = arguments.getInt("requestCode", 0); } else { requestCode = 0; + positiveButtonText = defaultPositiveButtonText; } final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context) .setPositiveButton(positiveButtonText, (d, w) -> { if (callback == null) return; callback.onPositiveButtonClicked(requestCode); - }) - .setNegativeButton(negativeButtonText, (dialog, which) -> { - if (callback == null) return; - callback.onNegativeButtonClicked(requestCode); }); - if (title > 0) { + if (title != 0) { builder.setTitle(title); } - if (message > 0) { + if (message != 0) { builder.setMessage(message); } - if (neutralButtonText > 0) { + if (negativeButtonText != 0) { + builder.setNegativeButton(negativeButtonText, (dialog, which) -> { + if (callback == null) return; + callback.onNegativeButtonClicked(requestCode); + }); + } + if (neutralButtonText != 0) { builder.setNeutralButton(neutralButtonText, (dialog, which) -> { if (callback == null) return; callback.onNeutralButtonClicked(requestCode); diff --git a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java index bbd174f2..64c7fbca 100644 --- a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java @@ -186,7 +186,7 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi R.string.admin_approval_required_description, R.string.ok, R.string.cancel, - -1 + 0 ); confirmDialogFragment.show(getChildFragmentManager(), "approval_required_dialog"); return; @@ -272,10 +272,10 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi final ConfirmDialogFragment confirmDialogFragment = ConfirmDialogFragment.newInstance( LEAVE_THREAD_REQUEST_CODE, R.string.dms_action_leave_question, - -1, + 0, R.string.yes, R.string.no, - -1 + 0 ); confirmDialogFragment.show(getChildFragmentManager(), "leave_thread_confirmation_dialog"); }); @@ -290,7 +290,7 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi R.string.dms_action_end_description, R.string.yes, R.string.no, - -1 + 0 ); confirmDialogFragment.show(getChildFragmentManager(), "end_thread_confirmation_dialog"); }); diff --git a/app/src/main/java/awais/instagrabber/fragments/settings/GeneralPreferencesFragment.java b/app/src/main/java/awais/instagrabber/fragments/settings/GeneralPreferencesFragment.java index 4606fb48..f63c4a7a 100644 --- a/app/src/main/java/awais/instagrabber/fragments/settings/GeneralPreferencesFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/settings/GeneralPreferencesFragment.java @@ -9,6 +9,8 @@ import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import androidx.preference.SwitchPreferenceCompat; +import java.util.List; + import awais.instagrabber.R; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; @@ -29,6 +31,14 @@ public class GeneralPreferencesFragment extends BasePreferencesFragment { } screen.addPreference(getUpdateCheckPreference(context)); screen.addPreference(getFlagSecurePreference(context)); + final List preferences = FlavorSettings.getInstance().getPreferences(context, + getChildFragmentManager(), + SettingCategory.GENERAL); + if (preferences != null) { + for (final Preference preference : preferences) { + screen.addPreference(preference); + } + } } private Preference getDefaultTabPreference(@NonNull final Context context) { diff --git a/app/src/main/java/awais/instagrabber/fragments/settings/IFlavorSettings.java b/app/src/main/java/awais/instagrabber/fragments/settings/IFlavorSettings.java new file mode 100644 index 00000000..4b0f1c5a --- /dev/null +++ b/app/src/main/java/awais/instagrabber/fragments/settings/IFlavorSettings.java @@ -0,0 +1,14 @@ +package awais.instagrabber.fragments.settings; + +import android.content.Context; + +import androidx.fragment.app.FragmentManager; +import androidx.preference.Preference; + +import java.util.List; + +public interface IFlavorSettings { + List getPreferences(Context context, + FragmentManager childFragmentManager, + SettingCategory settingCategory); +} diff --git a/app/src/main/java/awais/instagrabber/fragments/settings/PreferenceKeys.java b/app/src/main/java/awais/instagrabber/fragments/settings/PreferenceKeys.java index 3f481685..3482cf7f 100644 --- a/app/src/main/java/awais/instagrabber/fragments/settings/PreferenceKeys.java +++ b/app/src/main/java/awais/instagrabber/fragments/settings/PreferenceKeys.java @@ -5,4 +5,5 @@ public final class PreferenceKeys { public static final String PREF_ENABLE_DM_AUTO_REFRESH = "enable_dm_auto_refresh"; public static final String PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT = "enable_dm_auto_refresh_freq_unit"; public static final String PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER = "enable_dm_auto_refresh_freq_number"; + public static final String PREF_ENABLE_SENTRY = "enable_sentry"; } diff --git a/app/src/main/java/awais/instagrabber/fragments/settings/SettingCategory.java b/app/src/main/java/awais/instagrabber/fragments/settings/SettingCategory.java new file mode 100644 index 00000000..88ba5e6f --- /dev/null +++ b/app/src/main/java/awais/instagrabber/fragments/settings/SettingCategory.java @@ -0,0 +1,6 @@ +package awais.instagrabber.fragments.settings; + +public enum SettingCategory { + GENERAL, + // add more as and when required +} diff --git a/app/src/main/java/awais/instagrabber/utils/Constants.java b/app/src/main/java/awais/instagrabber/utils/Constants.java index cb56eba2..64666798 100644 --- a/app/src/main/java/awais/instagrabber/utils/Constants.java +++ b/app/src/main/java/awais/instagrabber/utils/Constants.java @@ -1,6 +1,8 @@ package awais.instagrabber.utils; public final class Constants { + public static final String CRASH_REPORT_EMAIL = "barinsta@austinhuang.me"; + // string prefs public static final String FOLDER_PATH = "custom_path"; public static final String DATE_TIME_FORMAT = "date_time_format"; diff --git a/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java b/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java index b6238a97..d51acd4a 100755 --- a/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java +++ b/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java @@ -12,6 +12,7 @@ import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_D import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER; import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT; import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_NOTIFICATIONS; +import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_SENTRY; import static awais.instagrabber.utils.Constants.APP_LANGUAGE; import static awais.instagrabber.utils.Constants.APP_THEME; import static awais.instagrabber.utils.Constants.APP_UA; @@ -130,6 +131,10 @@ public final class SettingsHelper { if (sharedPreferences != null) sharedPreferences.edit().putBoolean(key, val).apply(); } + public boolean hasPreference(final String key) { + return sharedPreferences != null && sharedPreferences.contains(key); + } + @StringDef( {APP_LANGUAGE, APP_THEME, APP_UA, BROWSER_UA, COOKIE, FOLDER_PATH, DATE_TIME_FORMAT, DATE_TIME_SELECTION, CUSTOM_DATE_TIME_FORMAT, DEVICE_UUID, SKIPPED_VERSION, DEFAULT_TAB, PREF_DARK_THEME, PREF_LIGHT_THEME, @@ -141,7 +146,7 @@ public final class SettingsHelper { @StringDef({DOWNLOAD_USER_FOLDER, FOLDER_SAVE_TO, AUTOPLAY_VIDEOS, SHOW_QUICK_ACCESS_DIALOG, MUTED_VIDEOS, SHOW_CAPTIONS, CUSTOM_DATE_TIME_FORMAT_ENABLED, MARK_AS_SEEN, DM_MARK_AS_SEEN, CHECK_ACTIVITY, CHECK_UPDATES, SWAP_DATE_TIME_FORMAT_ENABLED, PREF_ENABLE_DM_NOTIFICATIONS, PREF_ENABLE_DM_AUTO_REFRESH, - FLAG_SECURE}) + FLAG_SECURE, PREF_ENABLE_SENTRY}) public @interface BooleanSettings {} @StringDef({PREV_INSTALL_VERSION, BROWSER_UA_CODE, APP_UA_CODE, PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER}) diff --git a/app/src/main/java/awaisomereport/CrashReporter.java b/app/src/main/java/awaisomereport/CrashReporter.java index f5f22143..8b61e952 100755 --- a/app/src/main/java/awaisomereport/CrashReporter.java +++ b/app/src/main/java/awaisomereport/CrashReporter.java @@ -1,50 +1,35 @@ package awaisomereport; import android.app.Application; -import android.content.Context; -import android.content.Intent; -import android.os.Build; -import android.os.Process; -import android.util.Log; import androidx.annotation.NonNull; -import androidx.core.content.FileProvider; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.io.Writer; -import java.util.Date; -//import java.util.zip.ZipEntry; -//import java.util.zip.ZipOutputStream; - -import awais.instagrabber.BuildConfig; -import awais.instagrabber.utils.Utils; public final class CrashReporter implements Thread.UncaughtExceptionHandler { + private static final String TAG = CrashReporter.class.getSimpleName(); + private static CrashReporter reporterInstance; - private final Application application; - private final String email; -// private final File crashLogsZip; + + // private final File crashLogsZip; + private final CrashHandler crashHandler; + private boolean startAttempted = false; + private Thread.UncaughtExceptionHandler defaultEH; public static CrashReporter get(final Application application) { - if (reporterInstance == null) reporterInstance = new CrashReporter(application); + if (reporterInstance == null) { + reporterInstance = new CrashReporter(application); + } return reporterInstance; } private CrashReporter(@NonNull final Application application) { - this.application = application; - this.email = "barinsta@austinhuang.me"; -// this.crashLogsZip = new File(application.getExternalCacheDir(), "crash_logs.zip"); + crashHandler = new CrashHandler(application); + // this.crashLogsZip = new File(application.getExternalCacheDir(), "crash_logs.zip"); } public void start() { if (!startAttempted) { + defaultEH = Thread.getDefaultUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler(this); startAttempted = true; } @@ -52,140 +37,10 @@ public final class CrashReporter implements Thread.UncaughtExceptionHandler { @Override public void uncaughtException(@NonNull final Thread t, @NonNull final Throwable exception) { - final StringBuilder reportBuilder = new StringBuilder(); - reportBuilder.append("IMPORTANT: If sending by email, your email address and the entire content will be made public on GitHub issues."); - reportBuilder.append("\r\nIMPORTANT: When possible, please describe the steps leading to this crash. Thank you for your cooperation."); - reportBuilder.append("\r\n\r\nError report collected on: ").append(new Date().toString()); - - reportBuilder - .append("\r\n\r\nInformation:\r\n==============") - .append("\r\nVERSION : ").append(BuildConfig.VERSION_NAME) - .append("\r\nVERSION_CODE : ").append(BuildConfig.VERSION_CODE) - .append("\r\nPHONE-MODEL : ").append(Build.MODEL) - .append("\r\nANDROID_VERS : ").append(Build.VERSION.RELEASE) - .append("\r\nANDROID_REL : ").append(Build.VERSION.SDK_INT) - .append("\r\nBRAND : ").append(Build.BRAND) - .append("\r\nMANUFACTURER : ").append(Build.MANUFACTURER) - .append("\r\nBOARD : ").append(Build.BOARD) - .append("\r\nDEVICE : ").append(Build.DEVICE) - .append("\r\nPRODUCT : ").append(Build.PRODUCT) - .append("\r\nHOST : ").append(Build.HOST) - .append("\r\nTAGS : ").append(Build.TAGS); - - reportBuilder.append("\r\n\r\nStack:\r\n==============\r\n"); - final Writer result = new StringWriter(); - try (final PrintWriter printWriter = new PrintWriter(result)) { - exception.printStackTrace(printWriter); - reportBuilder.append(result.toString()); - - reportBuilder.append("\r\nCause:\r\n=============="); - - // for AsyncTask crashes - Throwable cause = exception.getCause(); - while (cause != null) { - cause.printStackTrace(printWriter); - reportBuilder.append(result.toString()); - cause = cause.getCause(); - } - } - reportBuilder.append("\r\n\r\n**** End of current Report ***"); - - final String errorContent = reportBuilder.toString(); - try (final FileOutputStream trace = application.openFileOutput("stack-" + System.currentTimeMillis() + ".stacktrace", Context.MODE_PRIVATE)) { - trace.write(errorContent.getBytes()); - } catch (final Exception ex) { - if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", ex); - } - - application.startActivity(new Intent(application, ErrorReporterActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); - -// zipLogs(); - - Process.killProcess(Process.myPid()); - System.exit(10); - } - -// public synchronized CrashReporter zipLogs() { -// final File logDir = Utils.logCollector != null ? Utils.logCollector.getLogDir() : -// new File(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? application.getDataDir() : application.getFilesDir(), "crashlogs"); -// -// try (final FileOutputStream fos = new FileOutputStream(crashLogsZip); -// final ZipOutputStream zos = new ZipOutputStream(fos)) { -// -// final File[] files = logDir.listFiles(); -// -// if (files != null) { -// zos.setLevel(5); -// byte[] buffer; -// for (final File file : files) { -// if (file != null && file.length() > 0) { -// buffer = new byte[1024]; -// try (final FileInputStream fis = new FileInputStream(file)) { -// zos.putNextEntry(new ZipEntry(file.getName())); -// int length; -// while ((length = fis.read(buffer)) > 0) zos.write(buffer, 0, length); -// zos.closeEntry(); -// } -// } -// } -// } -// -// } catch (final Exception e) { -// if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); -// } -// -// return this; -// } - - @SuppressWarnings("ResultOfMethodCallIgnored") - public void startCrashEmailIntent(final Context context) { - try { - final String filePath = context.getFilesDir().getAbsolutePath(); - - String[] errorFileList; - - try { - final File dir = new File(filePath); - if (dir.exists() && !dir.isDirectory()) dir.delete(); - dir.mkdir(); - errorFileList = dir.list((d, name) -> name.endsWith(".stacktrace")); - } catch (final Exception e) { - errorFileList = null; - } - - if (errorFileList != null && errorFileList.length > 0) { - final StringBuilder errorStringBuilder; - - errorStringBuilder = new StringBuilder("\r\n\r\n"); - final int maxSendMail = 5; - - int curIndex = 0; - for (final String curString : errorFileList) { - final File file = new File(filePath + '/' + curString); - - if (curIndex++ <= maxSendMail) { - errorStringBuilder.append("New Trace collected:\r\n=====================\r\n"); - try (final BufferedReader input = new BufferedReader(new FileReader(file))) { - String line; - while ((line = input.readLine()) != null) - errorStringBuilder.append(line).append("\r\n"); - } - } - - file.delete(); - } - - errorStringBuilder.append("\r\n\r\n"); - - context.startActivity(Intent.createChooser(new Intent(Intent.ACTION_SEND).setType("message/rfc822") - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION) - .putExtra(Intent.EXTRA_EMAIL, new String[]{email}) -// .putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(application, BuildConfig.APPLICATION_ID + ".provider", crashLogsZip)) - .putExtra(Intent.EXTRA_SUBJECT, "Barinsta Crash Report") - .putExtra(Intent.EXTRA_TEXT, errorStringBuilder.toString()), "Select an email app to send crash logs")); - } - } catch (final Exception e) { - if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); + if (crashHandler == null) { + defaultEH.uncaughtException(t, exception); + return; } + crashHandler.uncaughtException(t, exception, defaultEH); } } diff --git a/app/src/main/java/awaisomereport/CrashReporterHelper.java b/app/src/main/java/awaisomereport/CrashReporterHelper.java new file mode 100644 index 00000000..83f17f3e --- /dev/null +++ b/app/src/main/java/awaisomereport/CrashReporterHelper.java @@ -0,0 +1,134 @@ +package awaisomereport; + +import android.app.Application; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.os.Build; +import android.util.Log; + +import androidx.annotation.NonNull; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; +import java.util.Date; + +import awais.instagrabber.BuildConfig; +import awais.instagrabber.R; +import awais.instagrabber.utils.Constants; + +public final class CrashReporterHelper { + private static final String TAG = CrashReporterHelper.class.getSimpleName(); + + public static void startErrorReporterActivity(@NonNull final Application application, + @NonNull final Throwable exception) { + final StringBuilder reportBuilder = new StringBuilder(); + reportBuilder.append("IMPORTANT: If sending by email, your email address and the entire content will be made public on GitHub issues.") + .append("\r\nIMPORTANT: When possible, please describe the steps leading to this crash. Thank you for your cooperation.") + .append("\r\n\r\nError report collected on: ").append(new Date().toString()) + .append("\r\n\r\nInformation:\r\n==============") + .append("\r\nVERSION : ").append(BuildConfig.VERSION_NAME) + .append("\r\nVERSION_CODE : ").append(BuildConfig.VERSION_CODE) + .append("\r\nPHONE-MODEL : ").append(Build.MODEL) + .append("\r\nANDROID_VERS : ").append(Build.VERSION.RELEASE) + .append("\r\nANDROID_REL : ").append(Build.VERSION.SDK_INT) + .append("\r\nBRAND : ").append(Build.BRAND) + .append("\r\nMANUFACTURER : ").append(Build.MANUFACTURER) + .append("\r\nBOARD : ").append(Build.BOARD) + .append("\r\nDEVICE : ").append(Build.DEVICE) + .append("\r\nPRODUCT : ").append(Build.PRODUCT) + .append("\r\nHOST : ").append(Build.HOST) + .append("\r\nTAGS : ").append(Build.TAGS); + + reportBuilder.append("\r\n\r\nStack:\r\n==============\r\n"); + final Writer result = new StringWriter(); + try (final PrintWriter printWriter = new PrintWriter(result)) { + exception.printStackTrace(printWriter); + reportBuilder.append(result.toString()); + reportBuilder.append("\r\nCause:\r\n=============="); + // for AsyncTask crashes + Throwable cause = exception.getCause(); + while (cause != null) { + cause.printStackTrace(printWriter); + reportBuilder.append(result.toString()); + cause = cause.getCause(); + } + } + reportBuilder.append("\r\n\r\n**** End of current Report ***"); + + final String errorContent = reportBuilder.toString(); + try (final FileOutputStream trace = application.openFileOutput("stack-" + System.currentTimeMillis() + ".stacktrace", Context.MODE_PRIVATE)) { + trace.write(errorContent.getBytes()); + } catch (final Exception ex) { + if (BuildConfig.DEBUG) Log.e(TAG, "", ex); + } + + application.startActivity(new Intent(application, ErrorReporterActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + } + + public static void startCrashEmailIntent(final Context context) { + try { + final String filePath = context.getFilesDir().getAbsolutePath(); + + String[] errorFileList; + + try { + final File dir = new File(filePath); + if (dir.exists() && !dir.isDirectory()) { + //noinspection ResultOfMethodCallIgnored + dir.delete(); + } + //noinspection ResultOfMethodCallIgnored + dir.mkdirs(); + errorFileList = dir.list((d, name) -> name.endsWith(".stacktrace")); + } catch (final Exception e) { + errorFileList = null; + } + + if (errorFileList == null || errorFileList.length <= 0) { + return; + } + final StringBuilder errorStringBuilder; + + errorStringBuilder = new StringBuilder("\r\n\r\n"); + final int maxSendMail = 5; + int curIndex = 0; + for (final String curString : errorFileList) { + final File file = new File(filePath + '/' + curString); + + if (curIndex++ <= maxSendMail) { + errorStringBuilder.append("New Trace collected:\r\n=====================\r\n"); + try (final BufferedReader input = new BufferedReader(new FileReader(file))) { + String line; + while ((line = input.readLine()) != null) + errorStringBuilder.append(line).append("\r\n"); + } + } + //noinspection ResultOfMethodCallIgnored + file.delete(); + } + + errorStringBuilder.append("\r\n\r\n"); + final Resources resources = context.getResources(); + context.startActivity(Intent.createChooser( + new Intent(Intent.ACTION_SEND) + .setType("message/rfc822") + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION) + .putExtra(Intent.EXTRA_EMAIL, new String[]{Constants.CRASH_REPORT_EMAIL}) + // .putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(application, BuildConfig.APPLICATION_ID + ".provider", crashLogsZip)) + .putExtra(Intent.EXTRA_SUBJECT, resources.getString(R.string.crash_report_subject)) + .putExtra(Intent.EXTRA_TEXT, errorStringBuilder.toString()), + context.getResources().getString(R.string.crash_report_title)) + ); + } catch (final Exception e) { + Log.e(TAG, "", e); + } + } +} diff --git a/app/src/main/java/awaisomereport/ErrorReporterActivity.java b/app/src/main/java/awaisomereport/ErrorReporterActivity.java index 9fb1ce40..31dc3817 100755 --- a/app/src/main/java/awaisomereport/ErrorReporterActivity.java +++ b/app/src/main/java/awaisomereport/ErrorReporterActivity.java @@ -43,8 +43,9 @@ public final class ErrorReporterActivity extends Activity implements View.OnClic @Override public void onClick(@NonNull final View v) { - if (v == btnReport) - CrashReporter.get(getApplication()).startCrashEmailIntent(this); + if (v == btnReport) { + CrashReporterHelper.startCrashEmailIntent(this); + } finish(); System.exit(10); } diff --git a/app/src/main/java/awaisomereport/ICrashHandler.java b/app/src/main/java/awaisomereport/ICrashHandler.java new file mode 100644 index 00000000..103987d9 --- /dev/null +++ b/app/src/main/java/awaisomereport/ICrashHandler.java @@ -0,0 +1,7 @@ +package awaisomereport; + +public interface ICrashHandler { + void uncaughtException(Thread t, + Throwable exception, + Thread.UncaughtExceptionHandler defaultEH); +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b13df845..8dacbcb5 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -468,4 +468,9 @@ Request failed! Marked as seen Delete unsuccessful + Barinsta Crash Report + Select an email app to send crash logs + Enable Sentry + Sentry is a listener/handler for errors that asynchronously sends out the error/event to Sentry.io + Sentry will start on next launch From 39c472d6c2d1dcbec5e6227ed21ebe3924666997 Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Sun, 21 Mar 2021 23:30:38 +0900 Subject: [PATCH 2/2] Move sentry strings to github flavor strings.xml --- app/src/github/res/values/strings.xml | 6 ++++++ app/src/main/res/values/strings.xml | 3 --- 2 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 app/src/github/res/values/strings.xml diff --git a/app/src/github/res/values/strings.xml b/app/src/github/res/values/strings.xml new file mode 100644 index 00000000..6bc4c7a4 --- /dev/null +++ b/app/src/github/res/values/strings.xml @@ -0,0 +1,6 @@ + + + Enable Sentry + Sentry is a listener/handler for errors that asynchronously sends out the error/event to Sentry.io + Sentry will start on next launch + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8dacbcb5..cf7a695f 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -470,7 +470,4 @@ Delete unsuccessful Barinsta Crash Report Select an email app to send crash logs - Enable Sentry - Sentry is a listener/handler for errors that asynchronously sends out the error/event to Sentry.io - Sentry will start on next launch