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:
parent
c4a4b26320
commit
5ff66f5ac9
8 changed files with 122 additions and 66 deletions
|
@ -49,9 +49,9 @@
|
||||||
<string name="pref_title_composing">Composing</string>
|
<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_title_composing_title">Composing using zero-width space characters in emojis</string>
|
||||||
|
|
||||||
<string name="pref_acra_category">ACRA Settings</string>
|
<string name="pref_crashhandler_category">CrashHanlder Settings</string>
|
||||||
<string name="pref_acra_body">Enable ACRA reporting</string>
|
<string name="pref_crashhandler_body">Enable CrashHanlder email reporting</string>
|
||||||
<string name="key_enable_acra">acra.enable</string>
|
<string name="pref_crashhandler_key">enableCrashHanlder</string>
|
||||||
|
|
||||||
<string name="pref_title_anonymize_upload_filenames">Anonymize uploaded file names</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>
|
<string name="pref_title_hide_live_notification_description">Hide Live Notification username</string>
|
||||||
|
|
|
@ -113,7 +113,7 @@ class LoginActivity : BaseActivity(), Injectable {
|
||||||
} else {
|
} else {
|
||||||
binding.toolbar.visibility = View.GONE
|
binding.toolbar.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
val a = 1/0
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun requiresLogin(): Boolean {
|
override fun requiresLogin(): Boolean {
|
||||||
|
|
|
@ -56,12 +56,19 @@ class TuskyApplication : Application(), HasAndroidInjector {
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var notificationWorkerFactory: NotificationWorkerFactory
|
lateinit var notificationWorkerFactory: NotificationWorkerFactory
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
protected lateinit var crashHandler: CrashHandler
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
|
AppInjector.init(this)
|
||||||
|
|
||||||
val preferences = PreferenceManager.getDefaultSharedPreferences(this)
|
val preferences = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
|
||||||
CrashHandler.setAsDefaultHandler(this)
|
if(preferences.getBoolean(PrefKeys.CRASH_HANDLER_ENABLE, false)) {
|
||||||
|
crashHandler.setAsDefaultHandler()
|
||||||
|
}
|
||||||
|
|
||||||
if(ApplicationUtils.isDebug()) {
|
if(ApplicationUtils.isDebug()) {
|
||||||
Timber.plant(HyperlinkDebugTree())
|
Timber.plant(HyperlinkDebugTree())
|
||||||
|
@ -71,9 +78,6 @@ class TuskyApplication : Application(), HasAndroidInjector {
|
||||||
|
|
||||||
AutoDisposePlugins.setHideProxies(false) // a small performance optimization
|
AutoDisposePlugins.setHideProxies(false) // a small performance optimization
|
||||||
|
|
||||||
AppInjector.init(this)
|
|
||||||
|
|
||||||
|
|
||||||
// init the custom emoji fonts
|
// init the custom emoji fonts
|
||||||
val emojiSelection = preferences.getInt(PrefKeys.EMOJI, 0)
|
val emojiSelection = preferences.getInt(PrefKeys.EMOJI, 0)
|
||||||
val emojiConfig = EmojiCompatFont.byId(emojiSelection)
|
val emojiConfig = EmojiCompatFont.byId(emojiSelection)
|
||||||
|
|
|
@ -27,11 +27,10 @@ import androidx.preference.PreferenceFragmentCompat
|
||||||
import com.keylesspalace.tusky.R
|
import com.keylesspalace.tusky.R
|
||||||
import com.keylesspalace.tusky.appstore.EventHub
|
import com.keylesspalace.tusky.appstore.EventHub
|
||||||
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent
|
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.db.AccountManager
|
||||||
import com.keylesspalace.tusky.di.Injectable
|
import com.keylesspalace.tusky.di.Injectable
|
||||||
import com.keylesspalace.tusky.entity.Notification
|
import com.keylesspalace.tusky.entity.Notification
|
||||||
import com.keylesspalace.tusky.service.StreamingService
|
|
||||||
import com.keylesspalace.tusky.settings.AppTheme
|
import com.keylesspalace.tusky.settings.AppTheme
|
||||||
import com.keylesspalace.tusky.settings.PrefKeys
|
import com.keylesspalace.tusky.settings.PrefKeys
|
||||||
import com.keylesspalace.tusky.settings.emojiPreference
|
import com.keylesspalace.tusky.settings.emojiPreference
|
||||||
|
@ -62,6 +61,9 @@ class PreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var eventHub: EventHub
|
lateinit var eventHub: EventHub
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var crashHandler: CrashHandler
|
||||||
|
|
||||||
private val iconSize by lazy { resources.getDimensionPixelSize(R.dimen.preference_icon_size) }
|
private val iconSize by lazy { resources.getDimensionPixelSize(R.dimen.preference_icon_size) }
|
||||||
private var httpProxyPref: Preference? = null
|
private var httpProxyPref: Preference? = null
|
||||||
|
|
||||||
|
@ -362,15 +364,25 @@ class PreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
preferenceCategory(R.string.pref_crashhandler_category) {
|
||||||
preferenceCategory(R.string.pref_acra_category) {
|
|
||||||
switchPreference {
|
switchPreference {
|
||||||
setDefaultValue(false)
|
setDefaultValue(false)
|
||||||
key = PREF_ENABLE_ACRA
|
key = PrefKeys.CRASH_HANDLER_ENABLE
|
||||||
setTitle(R.string.pref_acra_body)
|
setTitle(R.string.pref_crashhandler_body)
|
||||||
isSingleLineTitle = false
|
isSingleLineTitle = false
|
||||||
|
setOnPreferenceChangeListener { _, value ->
|
||||||
|
with(value as Boolean) {
|
||||||
|
if(this) {
|
||||||
|
crashHandler.setAsDefaultHandler()
|
||||||
|
} else {
|
||||||
|
crashHandler.removeDefaultHandler()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,27 +32,15 @@ import com.keylesspalace.tusky.core.ui.callbacks.ActivityCallback
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import java.lang.Thread.UncaughtExceptionHandler
|
||||||
|
import javax.inject.Inject
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
class CrashHandler(
|
class CrashHandler @Inject constructor(
|
||||||
private val defaultHandler: Thread.UncaughtExceptionHandler,
|
|
||||||
private val huskyApp: Application
|
private val huskyApp: Application
|
||||||
) : Thread.UncaughtExceptionHandler {
|
) : UncaughtExceptionHandler {
|
||||||
|
|
||||||
private var lastActivity: Activity? = null
|
private val activityCallbacks = object : ActivityCallback() {
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun setAsDefaultHandler(application: Application) {
|
|
||||||
val handler = Thread.getDefaultUncaughtExceptionHandler()?.let {
|
|
||||||
CrashHandler(it, application)
|
|
||||||
}
|
|
||||||
Thread.setDefaultUncaughtExceptionHandler(handler)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
huskyApp.registerActivityLifecycleCallbacks(
|
|
||||||
object : ActivityCallback() {
|
|
||||||
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
|
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
|
||||||
lastActivity = activity
|
lastActivity = activity
|
||||||
Timber.d("onActivityCreated[${activity::class.simpleName}]")
|
Timber.d("onActivityCreated[${activity::class.simpleName}]")
|
||||||
|
@ -68,8 +56,9 @@ class CrashHandler(
|
||||||
Timber.d("onActivityStopped[${activity::class.simpleName}]")
|
Timber.d("onActivityStopped[${activity::class.simpleName}]")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
|
||||||
}
|
private val defaultHandler = Thread.getDefaultUncaughtExceptionHandler()
|
||||||
|
private var lastActivity: Activity? = null
|
||||||
|
|
||||||
override fun uncaughtException(thread: Thread, throwable: Throwable) {
|
override fun uncaughtException(thread: Thread, throwable: Throwable) {
|
||||||
try {
|
try {
|
||||||
|
@ -77,7 +66,7 @@ class CrashHandler(
|
||||||
} catch(e: IOException) {
|
} catch(e: IOException) {
|
||||||
Timber.e("CrashHandler Exception[${e.message}]")
|
Timber.e("CrashHandler Exception[${e.message}]")
|
||||||
} finally {
|
} finally {
|
||||||
lastActivity?.finish()
|
lastActivity?.finish() ?: defaultHandler?.uncaughtException(thread, throwable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,7 +100,10 @@ class CrashHandler(
|
||||||
|
|
||||||
val intent = Intent(Intent.ACTION_SEND).apply {
|
val intent = Intent(Intent.ACTION_SEND).apply {
|
||||||
type = "message/rfc822"
|
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_SUBJECT, "Husky ${BuildConfig.VERSION_NAME} crash")
|
||||||
putExtra(Intent.EXTRA_TEXT, formattedLog)
|
putExtra(Intent.EXTRA_TEXT, formattedLog)
|
||||||
putExtra(Intent.EXTRA_STREAM, getCrashFileUri(activity, stacktrace))
|
putExtra(Intent.EXTRA_STREAM, getCrashFileUri(activity, stacktrace))
|
||||||
|
@ -129,7 +121,10 @@ class CrashHandler(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getCrashFileUri(activity: Activity, stacktrace: String): Uri {
|
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 {
|
FileOutputStream(file).apply {
|
||||||
write(stacktrace.toByteArray())
|
write(stacktrace.toByteArray())
|
||||||
}.also {
|
}.also {
|
||||||
|
@ -141,4 +136,21 @@ class CrashHandler(
|
||||||
file
|
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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import com.keylesspalace.tusky.appstore.EventHub
|
||||||
import com.keylesspalace.tusky.appstore.EventHubImpl
|
import com.keylesspalace.tusky.appstore.EventHubImpl
|
||||||
import com.keylesspalace.tusky.components.notifications.Notifier
|
import com.keylesspalace.tusky.components.notifications.Notifier
|
||||||
import com.keylesspalace.tusky.components.notifications.SystemNotifier
|
import com.keylesspalace.tusky.components.notifications.SystemNotifier
|
||||||
|
import com.keylesspalace.tusky.core.logging.CrashHandler
|
||||||
import com.keylesspalace.tusky.db.AppDatabase
|
import com.keylesspalace.tusky.db.AppDatabase
|
||||||
import com.keylesspalace.tusky.network.MastodonApi
|
import com.keylesspalace.tusky.network.MastodonApi
|
||||||
import com.keylesspalace.tusky.network.TimelineCases
|
import com.keylesspalace.tusky.network.TimelineCases
|
||||||
|
@ -59,8 +60,10 @@ class AppModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
fun providesTimelineUseCases(api: MastodonApi,
|
fun providesTimelineUseCases(
|
||||||
eventHub: EventHub): TimelineCases {
|
api: MastodonApi,
|
||||||
|
eventHub: EventHub
|
||||||
|
): TimelineCases {
|
||||||
return TimelineCasesImpl(api, eventHub)
|
return TimelineCasesImpl(api, eventHub)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,19 +76,42 @@ class AppModule {
|
||||||
fun providesDatabase(appContext: Context): AppDatabase {
|
fun providesDatabase(appContext: Context): AppDatabase {
|
||||||
return Room.databaseBuilder(appContext, AppDatabase::class.java, "tuskyDB")
|
return Room.databaseBuilder(appContext, AppDatabase::class.java, "tuskyDB")
|
||||||
.allowMainThreadQueries()
|
.allowMainThreadQueries()
|
||||||
.addMigrations(AppDatabase.MIGRATION_2_3, AppDatabase.MIGRATION_3_4, AppDatabase.MIGRATION_4_5,
|
.addMigrations(
|
||||||
AppDatabase.MIGRATION_5_6, AppDatabase.MIGRATION_6_7, AppDatabase.MIGRATION_7_8,
|
AppDatabase.MIGRATION_2_3,
|
||||||
AppDatabase.MIGRATION_8_9, AppDatabase.MIGRATION_9_10, AppDatabase.MIGRATION_10_11,
|
AppDatabase.MIGRATION_3_4,
|
||||||
AppDatabase.MIGRATION_11_12, AppDatabase.MIGRATION_12_13, AppDatabase.MIGRATION_10_13,
|
AppDatabase.MIGRATION_4_5,
|
||||||
AppDatabase.MIGRATION_13_14, AppDatabase.MIGRATION_14_15, AppDatabase.MIGRATION_15_16,
|
AppDatabase.MIGRATION_5_6,
|
||||||
AppDatabase.MIGRATION_16_17, AppDatabase.MIGRATION_17_18, AppDatabase.MIGRATION_18_19,
|
AppDatabase.MIGRATION_6_7,
|
||||||
AppDatabase.MIGRATION_19_20, AppDatabase.MIGRATION_20_21, AppDatabase.MIGRATION_21_22,
|
AppDatabase.MIGRATION_7_8,
|
||||||
AppDatabase.MIGRATION_22_23, AppDatabase.MIGRATION_23_24, AppDatabase.MIGRATION_24_25,
|
AppDatabase.MIGRATION_8_9,
|
||||||
AppDatabase.MIGRATION_25_26, AppDatabase.MIGRATION_26_27)
|
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()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun notifier(context: Context): Notifier = SystemNotifier(context)
|
fun notifier(context: Context): Notifier = SystemNotifier(context)
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun crashHandler(app: Application) = CrashHandler(app)
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,4 +93,6 @@ object PrefKeys {
|
||||||
|
|
||||||
const val TAB_FILTER_HOME_REPLIES = "tabFilterHomeReplies"
|
const val TAB_FILTER_HOME_REPLIES = "tabFilterHomeReplies"
|
||||||
const val TAB_FILTER_HOME_BOOSTS = "tabFilterHomeBoosts"
|
const val TAB_FILTER_HOME_BOOSTS = "tabFilterHomeBoosts"
|
||||||
|
|
||||||
|
const val CRASH_HANDLER_ENABLE = "enableCrashHanlder"
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,12 +10,12 @@
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/pref_acra_category">
|
<PreferenceCategory android:title="@string/pref_crashhandler_category">
|
||||||
|
|
||||||
<CheckBoxPreference
|
<CheckBoxPreference
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:key="@string/key_enable_acra"
|
android:key="@string/pref_crashhandler_key"
|
||||||
android:title="@string/pref_acra_body" />
|
android:title="@string/pref_crashhandler_body" />
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue