Enable and disable CrashHandler

Going to "Preferences" the user will be able to enable and disable the
CrashHanlder of Husky.
This commit is contained in:
Adolfo Santiago 2022-06-25 10:00:18 +02:00
parent c4a4b26320
commit 5ff66f5ac9
No known key found for this signature in database
GPG Key ID: 244D6F9A317B4A65
8 changed files with 122 additions and 66 deletions

View File

@ -49,9 +49,9 @@
<string name="pref_title_composing">Composing</string>
<string name="pref_title_composing_title">Composing using zero-width space characters in emojis</string>
<string name="pref_acra_category">ACRA Settings</string>
<string name="pref_acra_body">Enable ACRA reporting</string>
<string name="key_enable_acra">acra.enable</string>
<string name="pref_crashhandler_category">CrashHanlder Settings</string>
<string name="pref_crashhandler_body">Enable CrashHanlder email reporting</string>
<string name="pref_crashhandler_key">enableCrashHanlder</string>
<string name="pref_title_anonymize_upload_filenames">Anonymize uploaded file names</string>
<string name="pref_title_hide_live_notification_description">Hide Live Notification username</string>

View File

@ -113,7 +113,7 @@ class LoginActivity : BaseActivity(), Injectable {
} else {
binding.toolbar.visibility = View.GONE
}
val a = 1/0
}
override fun requiresLogin(): Boolean {

View File

@ -56,12 +56,19 @@ class TuskyApplication : Application(), HasAndroidInjector {
@Inject
lateinit var notificationWorkerFactory: NotificationWorkerFactory
@Inject
protected lateinit var crashHandler: CrashHandler
override fun onCreate() {
super.onCreate()
AppInjector.init(this)
val preferences = PreferenceManager.getDefaultSharedPreferences(this)
CrashHandler.setAsDefaultHandler(this)
if(preferences.getBoolean(PrefKeys.CRASH_HANDLER_ENABLE, false)) {
crashHandler.setAsDefaultHandler()
}
if(ApplicationUtils.isDebug()) {
Timber.plant(HyperlinkDebugTree())
@ -71,9 +78,6 @@ class TuskyApplication : Application(), HasAndroidInjector {
AutoDisposePlugins.setHideProxies(false) // a small performance optimization
AppInjector.init(this)
// init the custom emoji fonts
val emojiSelection = preferences.getInt(PrefKeys.EMOJI, 0)
val emojiConfig = EmojiCompatFont.byId(emojiSelection)

View File

@ -27,11 +27,10 @@ import androidx.preference.PreferenceFragmentCompat
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.appstore.EventHub
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent
import com.keylesspalace.tusky.components.notifications.NotificationHelper
import com.keylesspalace.tusky.core.logging.CrashHandler
import com.keylesspalace.tusky.db.AccountManager
import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.entity.Notification
import com.keylesspalace.tusky.service.StreamingService
import com.keylesspalace.tusky.settings.AppTheme
import com.keylesspalace.tusky.settings.PrefKeys
import com.keylesspalace.tusky.settings.emojiPreference
@ -62,6 +61,9 @@ class PreferencesFragment : PreferenceFragmentCompat(), Injectable {
@Inject
lateinit var eventHub: EventHub
@Inject
lateinit var crashHandler: CrashHandler
private val iconSize by lazy { resources.getDimensionPixelSize(R.dimen.preference_icon_size) }
private var httpProxyPref: Preference? = null
@ -362,15 +364,25 @@ class PreferencesFragment : PreferenceFragmentCompat(), Injectable {
}
}
/*
preferenceCategory(R.string.pref_acra_category) {
preferenceCategory(R.string.pref_crashhandler_category) {
switchPreference {
setDefaultValue(false)
key = PREF_ENABLE_ACRA
setTitle(R.string.pref_acra_body)
key = PrefKeys.CRASH_HANDLER_ENABLE
setTitle(R.string.pref_crashhandler_body)
isSingleLineTitle = false
setOnPreferenceChangeListener { _, value ->
with(value as Boolean) {
if(this) {
crashHandler.setAsDefaultHandler()
} else {
crashHandler.removeDefaultHandler()
}
}
true
}
}
}*/
}
}
}

View File

@ -32,44 +32,33 @@ import com.keylesspalace.tusky.core.ui.callbacks.ActivityCallback
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.lang.Thread.UncaughtExceptionHandler
import javax.inject.Inject
import timber.log.Timber
class CrashHandler(
private val defaultHandler: Thread.UncaughtExceptionHandler,
class CrashHandler @Inject constructor(
private val huskyApp: Application
) : Thread.UncaughtExceptionHandler {
) : UncaughtExceptionHandler {
private var lastActivity: Activity? = null
private val activityCallbacks = object : ActivityCallback() {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
lastActivity = activity
Timber.d("onActivityCreated[${activity::class.simpleName}]")
}
companion object {
fun setAsDefaultHandler(application: Application) {
val handler = Thread.getDefaultUncaughtExceptionHandler()?.let {
CrashHandler(it, application)
}
Thread.setDefaultUncaughtExceptionHandler(handler)
override fun onActivityResumed(activity: Activity) {
lastActivity = activity
Timber.d("onActivityResumed[${activity::class.simpleName}]")
}
override fun onActivityStopped(activity: Activity) {
lastActivity = null
Timber.d("onActivityStopped[${activity::class.simpleName}]")
}
}
init {
huskyApp.registerActivityLifecycleCallbacks(
object : ActivityCallback() {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
lastActivity = activity
Timber.d("onActivityCreated[${activity::class.simpleName}]")
}
override fun onActivityResumed(activity: Activity) {
lastActivity = activity
Timber.d("onActivityResumed[${activity::class.simpleName}]")
}
override fun onActivityStopped(activity: Activity) {
lastActivity = null
Timber.d("onActivityStopped[${activity::class.simpleName}]")
}
}
)
}
private val defaultHandler = Thread.getDefaultUncaughtExceptionHandler()
private var lastActivity: Activity? = null
override fun uncaughtException(thread: Thread, throwable: Throwable) {
try {
@ -77,7 +66,7 @@ class CrashHandler(
} catch(e: IOException) {
Timber.e("CrashHandler Exception[${e.message}]")
} finally {
lastActivity?.finish()
lastActivity?.finish() ?: defaultHandler?.uncaughtException(thread, throwable)
}
}
@ -104,14 +93,17 @@ class CrashHandler(
.appendLine()
.appendLine("## Device details")
.appendLine(getDeviceInfo())
//.appendLine("## Crash details")
//.appendLine(stacktrace)
//.appendLine("## Crash details")
//.appendLine(stacktrace)
}.toString()
Timber.d(formattedLog)
val intent = Intent(Intent.ACTION_SEND).apply {
type = "message/rfc822"
putExtra(Intent.EXTRA_EMAIL, arrayOf(activity.getString(R.string.crashhandler_email)))
putExtra(
Intent.EXTRA_EMAIL,
arrayOf(activity.getString(R.string.crashhandler_email))
)
putExtra(Intent.EXTRA_SUBJECT, "Husky ${BuildConfig.VERSION_NAME} crash")
putExtra(Intent.EXTRA_TEXT, formattedLog)
putExtra(Intent.EXTRA_STREAM, getCrashFileUri(activity, stacktrace))
@ -129,7 +121,10 @@ class CrashHandler(
}
private fun getCrashFileUri(activity: Activity, stacktrace: String): Uri {
val file = File("${huskyApp.cacheDir}/crashes", activity.getString(R.string.crashhandler_email_report_filename))
val file = File(
"${huskyApp.cacheDir}/crashes",
activity.getString(R.string.crashhandler_email_report_filename)
)
FileOutputStream(file).apply {
write(stacktrace.toByteArray())
}.also {
@ -141,4 +136,21 @@ class CrashHandler(
file
)
}
fun setAsDefaultHandler() {
val handler = defaultHandler?.let {
this@CrashHandler
}
Thread.setDefaultUncaughtExceptionHandler(handler)
huskyApp.registerActivityLifecycleCallbacks(activityCallbacks)
Timber.d("Set default handler[${handler}]")
}
fun removeDefaultHandler() {
Thread.setDefaultUncaughtExceptionHandler(defaultHandler)
huskyApp.unregisterActivityLifecycleCallbacks(activityCallbacks)
Timber.d("Remove default handler")
}
}

View File

@ -27,6 +27,7 @@ import com.keylesspalace.tusky.appstore.EventHub
import com.keylesspalace.tusky.appstore.EventHubImpl
import com.keylesspalace.tusky.components.notifications.Notifier
import com.keylesspalace.tusky.components.notifications.SystemNotifier
import com.keylesspalace.tusky.core.logging.CrashHandler
import com.keylesspalace.tusky.db.AppDatabase
import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.network.TimelineCases
@ -59,8 +60,10 @@ class AppModule {
}
@Provides
fun providesTimelineUseCases(api: MastodonApi,
eventHub: EventHub): TimelineCases {
fun providesTimelineUseCases(
api: MastodonApi,
eventHub: EventHub
): TimelineCases {
return TimelineCasesImpl(api, eventHub)
}
@ -72,20 +75,43 @@ class AppModule {
@Singleton
fun providesDatabase(appContext: Context): AppDatabase {
return Room.databaseBuilder(appContext, AppDatabase::class.java, "tuskyDB")
.allowMainThreadQueries()
.addMigrations(AppDatabase.MIGRATION_2_3, AppDatabase.MIGRATION_3_4, AppDatabase.MIGRATION_4_5,
AppDatabase.MIGRATION_5_6, AppDatabase.MIGRATION_6_7, AppDatabase.MIGRATION_7_8,
AppDatabase.MIGRATION_8_9, AppDatabase.MIGRATION_9_10, AppDatabase.MIGRATION_10_11,
AppDatabase.MIGRATION_11_12, AppDatabase.MIGRATION_12_13, AppDatabase.MIGRATION_10_13,
AppDatabase.MIGRATION_13_14, AppDatabase.MIGRATION_14_15, AppDatabase.MIGRATION_15_16,
AppDatabase.MIGRATION_16_17, AppDatabase.MIGRATION_17_18, AppDatabase.MIGRATION_18_19,
AppDatabase.MIGRATION_19_20, AppDatabase.MIGRATION_20_21, AppDatabase.MIGRATION_21_22,
AppDatabase.MIGRATION_22_23, AppDatabase.MIGRATION_23_24, AppDatabase.MIGRATION_24_25,
AppDatabase.MIGRATION_25_26, AppDatabase.MIGRATION_26_27)
.build()
.allowMainThreadQueries()
.addMigrations(
AppDatabase.MIGRATION_2_3,
AppDatabase.MIGRATION_3_4,
AppDatabase.MIGRATION_4_5,
AppDatabase.MIGRATION_5_6,
AppDatabase.MIGRATION_6_7,
AppDatabase.MIGRATION_7_8,
AppDatabase.MIGRATION_8_9,
AppDatabase.MIGRATION_9_10,
AppDatabase.MIGRATION_10_11,
AppDatabase.MIGRATION_11_12,
AppDatabase.MIGRATION_12_13,
AppDatabase.MIGRATION_10_13,
AppDatabase.MIGRATION_13_14,
AppDatabase.MIGRATION_14_15,
AppDatabase.MIGRATION_15_16,
AppDatabase.MIGRATION_16_17,
AppDatabase.MIGRATION_17_18,
AppDatabase.MIGRATION_18_19,
AppDatabase.MIGRATION_19_20,
AppDatabase.MIGRATION_20_21,
AppDatabase.MIGRATION_21_22,
AppDatabase.MIGRATION_22_23,
AppDatabase.MIGRATION_23_24,
AppDatabase.MIGRATION_24_25,
AppDatabase.MIGRATION_25_26,
AppDatabase.MIGRATION_26_27
)
.build()
}
@Provides
@Singleton
fun notifier(context: Context): Notifier = SystemNotifier(context)
@Provides
@Singleton
fun crashHandler(app: Application) = CrashHandler(app)
}

View File

@ -93,4 +93,6 @@ object PrefKeys {
const val TAB_FILTER_HOME_REPLIES = "tabFilterHomeReplies"
const val TAB_FILTER_HOME_BOOSTS = "tabFilterHomeBoosts"
const val CRASH_HANDLER_ENABLE = "enableCrashHanlder"
}

View File

@ -10,12 +10,12 @@
</PreferenceCategory>
<PreferenceCategory android:title="@string/pref_acra_category">
<PreferenceCategory android:title="@string/pref_crashhandler_category">
<CheckBoxPreference
android:defaultValue="false"
android:key="@string/key_enable_acra"
android:title="@string/pref_acra_body" />
android:key="@string/pref_crashhandler_key"
android:title="@string/pref_crashhandler_body" />
</PreferenceCategory>