Re-implement Crashlytics

This commit is contained in:
William Brawner 2020-09-21 12:36:07 -07:00
parent f830657668
commit 0ff5ccdbd6
14 changed files with 104 additions and 5 deletions

1
.gitignore vendored
View file

@ -12,4 +12,3 @@
*.log *.log
keystore.properties keystore.properties
*.jks *.jks
sentry.properties

View file

@ -0,0 +1,3 @@
url=ACRA_URL
user=ACRA_USER
pass=ACRA_PASS

View file

@ -3,6 +3,7 @@ apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
apply plugin: 'jacoco' apply plugin: 'jacoco'
apply plugin: 'com.google.firebase.crashlytics'
def keystoreProperties = new Properties() def keystoreProperties = new Properties()
try { try {
@ -110,8 +111,9 @@ dependencies {
implementation 'androidx.legacy:legacy-support-v13:1.0.0' implementation 'androidx.legacy:legacy-support-v13:1.0.0'
implementation 'com.commonsware.cwac:anddown:0.3.0' implementation 'com.commonsware.cwac:anddown:0.3.0'
playImplementation 'com.android.billingclient:billing:3.0.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.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.core:core-ktx:1.3.1"
implementation 'androidx.browser:browser:1.2.0' implementation 'androidx.browser:browser:1.2.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
@ -132,7 +134,6 @@ android.productFlavors.each { flavor ->
} }
repositories { repositories {
mavenCentral() mavenCentral()
jcenter()
} }
jacoco { jacoco {

View file

@ -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)
}
}
}
}

View file

@ -43,6 +43,7 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".view.activity.MainActivity" /> <activity android:name=".view.activity.MainActivity" />
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider" android:authorities="${applicationId}.fileprovider"
@ -52,6 +53,9 @@
android:name="android.support.FILE_PROVIDER_PATHS" android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" /> android:resource="@xml/file_paths" />
</provider> </provider>
<meta-data
android:name="firebase_crashlytics_collection_enabled"
android:value="false" />
</application> </application>
</manifest> </manifest>

View file

@ -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.

View file

@ -0,0 +1,6 @@
package com.wbrawner.simplemarkdown.utility
interface ErrorHandler {
fun enable(enable: Boolean)
fun reportException(t: Throwable, message: String? = null)
}

View file

@ -24,6 +24,8 @@ import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupWithNavController import androidx.navigation.ui.setupWithNavController
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.wbrawner.simplemarkdown.R 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.view.adapter.EditPagerAdapter
import com.wbrawner.simplemarkdown.viewmodel.MarkdownViewModel import com.wbrawner.simplemarkdown.viewmodel.MarkdownViewModel
import kotlinx.android.synthetic.main.fragment_main.* import kotlinx.android.synthetic.main.fragment_main.*
@ -37,6 +39,7 @@ class MainFragment : Fragment(), ActivityCompat.OnRequestPermissionsResultCallba
override val coroutineContext: CoroutineContext = Dispatchers.Main override val coroutineContext: CoroutineContext = Dispatchers.Main
private val viewModel: MarkdownViewModel by viewModels() private val viewModel: MarkdownViewModel by viewModels()
private var appBarConfiguration: AppBarConfiguration? = null private var appBarConfiguration: AppBarConfiguration? = null
private val errorHandler: ErrorHandler by errorHandlerImpl()
override fun onAttach(context: Context) { override fun onAttach(context: Context) {
super.onAttach(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() { override fun onPause() {
super.onPause() super.onPause()
launch { launch {

View file

@ -12,6 +12,8 @@ import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.setupWithNavController import androidx.navigation.ui.setupWithNavController
import com.wbrawner.simplemarkdown.R 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.readAssetToString
import com.wbrawner.simplemarkdown.utility.toHtml import com.wbrawner.simplemarkdown.utility.toHtml
import kotlinx.android.synthetic.main.fragment_markdown_info.* import kotlinx.android.synthetic.main.fragment_markdown_info.*
@ -23,6 +25,7 @@ import kotlin.coroutines.CoroutineContext
class MarkdownInfoFragment : Fragment(), CoroutineScope { class MarkdownInfoFragment : Fragment(), CoroutineScope {
override val coroutineContext: CoroutineContext = Dispatchers.Main override val coroutineContext: CoroutineContext = Dispatchers.Main
private val errorHandler: ErrorHandler by errorHandlerImpl()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -60,6 +63,7 @@ class MarkdownInfoFragment : Fragment(), CoroutineScope {
"UTF-8", null "UTF-8", null
) )
} catch (e: Exception) { } catch (e: Exception) {
errorHandler.reportException(e)
Toast.makeText(view.context, R.string.file_load_error, Toast.LENGTH_SHORT).show() Toast.makeText(view.context, R.string.file_load_error, Toast.LENGTH_SHORT).show()
findNavController().navigateUp() findNavController().navigateUp()
} }

View file

@ -34,7 +34,7 @@
<string name="action_lock_swipe">Lock Swiping</string> <string name="action_lock_swipe">Lock Swiping</string>
<string name="action_select">Select</string> <string name="action_select">Select</string>
<string name="action_privacy">Privacy</string> <string name="action_privacy">Privacy</string>
<string name="pref_key_error_reports_enabled">acra.enable</string> <string name="pref_key_error_reports_enabled">crashlytics.enable</string>
<string name="pref_title_error_reports">Enable automated error reports</string> <string name="pref_title_error_reports">Enable automated error reports</string>
<string name="pref_error_reports_off">Error reports will not be sent</string> <string name="pref_error_reports_off">Error reports will not be sent</string>
<string name="pref_error_reports_on">Error reports will be sent</string> <string name="pref_error_reports_on">Error reports will be sent</string>

View file

@ -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()
}
}

View file

@ -27,6 +27,7 @@ object ReviewHelper : Application.ActivityLifecycleCallbacks {
private lateinit var application: Application private lateinit var application: Application
private lateinit var reviewManager: ReviewManager private lateinit var reviewManager: ReviewManager
private lateinit var sharedPreferences: SharedPreferences private lateinit var sharedPreferences: SharedPreferences
private val errorHandler: ErrorHandler by errorHandlerImpl()
private var currentActivity: Activity? = null private var currentActivity: Activity? = null
private var activityCount = 0 private var activityCount = 0
set(value) { set(value) {

View file

@ -9,6 +9,7 @@ buildscript {
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:4.0.1' classpath 'com.android.tools.build:gradle:4.0.1'
classpath 'com.google.gms:google-services:4.3.3' 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" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
} }
} }

View file

@ -1,4 +1,4 @@
#Wed Jun 10 07:53:53 MST 2020 #Mon Sep 21 10:36:14 MST 2020
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME