diff --git a/.gitignore b/.gitignore index 6a955c3..07544b8 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,3 @@ *.log keystore.properties *.jks -sentry.properties diff --git a/app/acra.properties.sample b/app/acra.properties.sample new file mode 100644 index 0000000..c0c912f --- /dev/null +++ b/app/acra.properties.sample @@ -0,0 +1,3 @@ +url=ACRA_URL +user=ACRA_USER +pass=ACRA_PASS diff --git a/app/build.gradle b/app/build.gradle index f6c35c1..e04aba7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -3,6 +3,7 @@ apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' apply plugin: 'jacoco' +apply plugin: 'com.google.firebase.crashlytics' def keystoreProperties = new Properties() try { @@ -110,8 +111,9 @@ dependencies { implementation 'androidx.legacy:legacy-support-v13:1.0.0' implementation 'com.commonsware.cwac:anddown:0.3.0' playImplementation 'com.android.billingclient:billing:3.0.0' - playImplementation 'com.google.firebase:firebase-core:17.5.0' playImplementation 'com.google.android.play:core-ktx:1.8.1' + playImplementation 'com.google.firebase:firebase-crashlytics:17.2.1' + playImplementation 'com.google.firebase:firebase-analytics:17.5.0' implementation "androidx.core:core-ktx:1.3.1" implementation 'androidx.browser:browser:1.2.0' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" @@ -132,7 +134,6 @@ android.productFlavors.each { flavor -> } repositories { mavenCentral() - jcenter() } jacoco { diff --git a/app/src/free/java/com/wbrawner/simplemarkdown/utility/ErrorHandler.kt b/app/src/free/java/com/wbrawner/simplemarkdown/utility/ErrorHandler.kt new file mode 100644 index 0000000..7bc5eda --- /dev/null +++ b/app/src/free/java/com/wbrawner/simplemarkdown/utility/ErrorHandler.kt @@ -0,0 +1,22 @@ +package com.wbrawner.simplemarkdown.utility + +import kotlin.reflect.KProperty + +class errorHandlerImpl { + operator fun getValue(thisRef: Any, property: KProperty<*>): ErrorHandler { + return implementation + } + + companion object { + // Default no-op error handler for debugging + val implementation: ErrorHandler = object : ErrorHandler { + override fun enable(enable: Boolean) { + // no-op + } + + override fun reportException(t: Throwable, message: String?) { + Log.e("ErrorHandler", "Caught non-fatal exception. Message: $message", t) + } + } + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 70f1d5d..ea4d80f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -43,6 +43,7 @@ + + \ No newline at end of file diff --git a/app/src/main/assets/Privacy Policy.md b/app/src/main/assets/Privacy Policy.md new file mode 100644 index 0000000..74416b3 --- /dev/null +++ b/app/src/main/assets/Privacy Policy.md @@ -0,0 +1,12 @@ +## Privacy Policy + +The internet access permission is requested primarily for retrieving images from the internet in + case you embed them in your markdown, but it also allows me to send automated error and crash + reports to myself whenever the app runs into an issue. These error reports are opt-out, and are + powered by [Firebase Crashlytics] (https://firebase.google.com/docs/crashlytics/), which is a + free error reporting solution provided by Google. These error reports are used exclusively for + fixing problems that occur while you're using the app, along with some analytics info like how + long you use the app for, how often, and which features of the app you use. This helps me to + determine how to spend my very limited time on building out new features. I'll have to defer to + [Google's Privacy Policy](https://policies.google.com/privacy) to explain how they handle the + data. As for me, I don't knowingly or willingly sell or share your data. diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/utility/ErrorHandler.kt b/app/src/main/java/com/wbrawner/simplemarkdown/utility/ErrorHandler.kt new file mode 100644 index 0000000..a961b4b --- /dev/null +++ b/app/src/main/java/com/wbrawner/simplemarkdown/utility/ErrorHandler.kt @@ -0,0 +1,6 @@ +package com.wbrawner.simplemarkdown.utility + +interface ErrorHandler { + fun enable(enable: Boolean) + fun reportException(t: Throwable, message: String? = null) +} diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/MainFragment.kt b/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/MainFragment.kt index 1eb789f..b0ea4cf 100644 --- a/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/MainFragment.kt +++ b/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/MainFragment.kt @@ -24,6 +24,8 @@ import androidx.navigation.ui.AppBarConfiguration import androidx.navigation.ui.setupWithNavController import androidx.preference.PreferenceManager import com.wbrawner.simplemarkdown.R +import com.wbrawner.simplemarkdown.utility.ErrorHandler +import com.wbrawner.simplemarkdown.utility.errorHandlerImpl import com.wbrawner.simplemarkdown.view.adapter.EditPagerAdapter import com.wbrawner.simplemarkdown.viewmodel.MarkdownViewModel import kotlinx.android.synthetic.main.fragment_main.* @@ -37,6 +39,7 @@ class MainFragment : Fragment(), ActivityCompat.OnRequestPermissionsResultCallba override val coroutineContext: CoroutineContext = Dispatchers.Main private val viewModel: MarkdownViewModel by viewModels() private var appBarConfiguration: AppBarConfiguration? = null + private val errorHandler: ErrorHandler by errorHandlerImpl() override fun onAttach(context: Context) { super.onAttach(context) @@ -137,6 +140,17 @@ class MainFragment : Fragment(), ActivityCompat.OnRequestPermissionsResultCallba } } + override fun onStart() { + super.onStart() + launch { + withContext(Dispatchers.IO) { + val enableErrorReports = PreferenceManager.getDefaultSharedPreferences(requireContext()) + .getBoolean(getString(R.string.pref_key_error_reports_enabled), true) + errorHandler.enable(enableErrorReports) + } + } + } + override fun onPause() { super.onPause() launch { diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/MarkdownInfoFragment.kt b/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/MarkdownInfoFragment.kt index ef68e66..96a205e 100644 --- a/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/MarkdownInfoFragment.kt +++ b/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/MarkdownInfoFragment.kt @@ -12,6 +12,8 @@ import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController import androidx.navigation.ui.setupWithNavController import com.wbrawner.simplemarkdown.R +import com.wbrawner.simplemarkdown.utility.ErrorHandler +import com.wbrawner.simplemarkdown.utility.errorHandlerImpl import com.wbrawner.simplemarkdown.utility.readAssetToString import com.wbrawner.simplemarkdown.utility.toHtml import kotlinx.android.synthetic.main.fragment_markdown_info.* @@ -23,6 +25,7 @@ import kotlin.coroutines.CoroutineContext class MarkdownInfoFragment : Fragment(), CoroutineScope { override val coroutineContext: CoroutineContext = Dispatchers.Main + private val errorHandler: ErrorHandler by errorHandlerImpl() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -60,6 +63,7 @@ class MarkdownInfoFragment : Fragment(), CoroutineScope { "UTF-8", null ) } catch (e: Exception) { + errorHandler.reportException(e) Toast.makeText(view.context, R.string.file_load_error, Toast.LENGTH_SHORT).show() findNavController().navigateUp() } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e32611c..d5cee08 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -34,7 +34,7 @@ Lock Swiping Select Privacy - acra.enable + crashlytics.enable Enable automated error reports Error reports will not be sent Error reports will be sent diff --git a/app/src/play/java/com/wbrawner/simplemarkdown/utility/CrashlyticsErrorHandler.kt b/app/src/play/java/com/wbrawner/simplemarkdown/utility/CrashlyticsErrorHandler.kt new file mode 100644 index 0000000..cfc8d81 --- /dev/null +++ b/app/src/play/java/com/wbrawner/simplemarkdown/utility/CrashlyticsErrorHandler.kt @@ -0,0 +1,32 @@ +package com.wbrawner.simplemarkdown.utility + +import android.util.Log +import com.google.firebase.crashlytics.FirebaseCrashlytics +import com.wbrawner.simplemarkdown.BuildConfig +import kotlin.reflect.KProperty + +class CrashlyticsErrorHandler : ErrorHandler { + private val crashlytics = FirebaseCrashlytics.getInstance() + + override fun enable(enable: Boolean) { + crashlytics.setCrashlyticsCollectionEnabled(enable) + } + + override fun reportException(t: Throwable, message: String?) { + @Suppress("ConstantConditionIf") + if (BuildConfig.DEBUG) { + Log.e("CrashlyticsErrorHandler", "Caught exception: $message", t) + } + crashlytics.recordException(t) + } +} + +class errorHandlerImpl { + operator fun getValue(thisRef: Any, property: KProperty<*>): ErrorHandler { + return impl + } + + companion object { + val impl = CrashlyticsErrorHandler() + } +} diff --git a/app/src/play/java/com/wbrawner/simplemarkdown/utility/ReviewHelper.kt b/app/src/play/java/com/wbrawner/simplemarkdown/utility/ReviewHelper.kt index fae79e7..3b515c5 100644 --- a/app/src/play/java/com/wbrawner/simplemarkdown/utility/ReviewHelper.kt +++ b/app/src/play/java/com/wbrawner/simplemarkdown/utility/ReviewHelper.kt @@ -27,6 +27,7 @@ object ReviewHelper : Application.ActivityLifecycleCallbacks { private lateinit var application: Application private lateinit var reviewManager: ReviewManager private lateinit var sharedPreferences: SharedPreferences + private val errorHandler: ErrorHandler by errorHandlerImpl() private var currentActivity: Activity? = null private var activityCount = 0 set(value) { diff --git a/build.gradle b/build.gradle index 1da8f69..a5a151f 100644 --- a/build.gradle +++ b/build.gradle @@ -9,6 +9,7 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:4.0.1' classpath 'com.google.gms:google-services:4.3.3' + classpath 'com.google.firebase:firebase-crashlytics-gradle:2.3.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7730d7d..d5089a9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,4 @@ -#Wed Jun 10 07:53:53 MST 2020 +#Mon Sep 21 10:36:14 MST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME