Add ACRA for crash reports
This commit is contained in:
@@ -112,6 +112,9 @@ dependencies {
|
||||
implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0'
|
||||
implementation 'com.github.solkin:disk-lru-cache:1.4'
|
||||
|
||||
implementation 'ch.acra:acra-mail:5.9.3'
|
||||
implementation 'ch.acra:acra-dialog:5.9.3'
|
||||
|
||||
debugImplementation 'org.jsoup:jsoup:1.15.1'
|
||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1'
|
||||
|
||||
|
||||
@@ -68,11 +68,6 @@
|
||||
android:name="org.koitharu.kotatsu.settings.sources.auth.SourceAuthActivity"
|
||||
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
<activity
|
||||
android:name="org.koitharu.kotatsu.core.ui.CrashActivity"
|
||||
android:label="@string/error_occurred"
|
||||
android:theme="@android:style/Theme.DeviceDefault"
|
||||
android:windowSoftInputMode="stateAlwaysHidden" />
|
||||
<activity
|
||||
android:name="org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity"
|
||||
android:label="@string/favourites_categories"
|
||||
@@ -86,9 +81,6 @@
|
||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="org.koitharu.kotatsu.search.ui.global.GlobalSearchActivity"
|
||||
android:label="@string/search" />
|
||||
<activity
|
||||
android:name="org.koitharu.kotatsu.search.ui.multi.MultiSearchActivity"
|
||||
android:label="@string/search" />
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
package org.koitharu.kotatsu
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.os.StrictMode
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.fragment.app.strictmode.FragmentStrictMode
|
||||
import org.acra.ReportField
|
||||
import org.acra.config.dialog
|
||||
import org.acra.config.mailSender
|
||||
import org.acra.data.StringFormat
|
||||
import org.acra.ktx.initAcra
|
||||
import org.koin.android.ext.android.get
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.core.context.startKoin
|
||||
@@ -13,7 +19,6 @@ import org.koitharu.kotatsu.core.db.databaseModule
|
||||
import org.koitharu.kotatsu.core.github.githubModule
|
||||
import org.koitharu.kotatsu.core.network.networkModule
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.ui.AppCrashHandler
|
||||
import org.koitharu.kotatsu.core.ui.uiModule
|
||||
import org.koitharu.kotatsu.details.detailsModule
|
||||
import org.koitharu.kotatsu.favourites.favouritesModule
|
||||
@@ -41,7 +46,6 @@ class KotatsuApp : Application() {
|
||||
enableStrictMode()
|
||||
}
|
||||
initKoin()
|
||||
Thread.setDefaultUncaughtExceptionHandler(AppCrashHandler(applicationContext))
|
||||
AppCompatDelegate.setDefaultNightMode(get<AppSettings>().theme)
|
||||
registerActivityLifecycleCallbacks(get<AppProtectHelper>())
|
||||
registerActivityLifecycleCallbacks(get<ActivityRecreationHandle>())
|
||||
@@ -75,6 +79,36 @@ class KotatsuApp : Application() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun attachBaseContext(base: Context?) {
|
||||
super.attachBaseContext(base)
|
||||
initAcra {
|
||||
buildConfigClass = BuildConfig::class.java
|
||||
reportFormat = StringFormat.KEY_VALUE_LIST
|
||||
reportContent = listOf(
|
||||
ReportField.PACKAGE_NAME,
|
||||
ReportField.APP_VERSION_CODE,
|
||||
ReportField.APP_VERSION_NAME,
|
||||
ReportField.ANDROID_VERSION,
|
||||
ReportField.PHONE_MODEL,
|
||||
ReportField.CRASH_CONFIGURATION,
|
||||
ReportField.STACK_TRACE,
|
||||
ReportField.SHARED_PREFERENCES,
|
||||
)
|
||||
dialog {
|
||||
text = getString(R.string.crash_text)
|
||||
title = getString(R.string.error_occurred)
|
||||
positiveButtonText = getString(R.string.send)
|
||||
resIcon = R.drawable.ic_alert_outline
|
||||
resTheme = android.R.style.Theme_Material_Light_Dialog_Alert
|
||||
}
|
||||
mailSender {
|
||||
mailTo = getString(R.string.email_error_report)
|
||||
reportAsFile = true
|
||||
reportFileName = "stacktrace.txt"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun enableStrictMode() {
|
||||
StrictMode.setThreadPolicy(
|
||||
StrictMode.ThreadPolicy.Builder()
|
||||
|
||||
@@ -12,7 +12,6 @@ import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.appcompat.widget.ActionBarContextView
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
@@ -83,8 +82,9 @@ abstract class BaseActivity<B : ViewBinding> :
|
||||
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
if (BuildConfig.DEBUG && keyCode == KeyEvent.KEYCODE_VOLUME_UP) { // TODO remove
|
||||
ActivityCompat.recreate(this)
|
||||
return true
|
||||
// ActivityCompat.recreate(this)
|
||||
throw RuntimeException("Test crash")
|
||||
// return true
|
||||
}
|
||||
return super.onKeyDown(keyCode, event)
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
package org.koitharu.kotatsu.core.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import kotlin.system.exitProcess
|
||||
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
|
||||
|
||||
class AppCrashHandler(private val applicationContext: Context) : Thread.UncaughtExceptionHandler {
|
||||
|
||||
override fun uncaughtException(t: Thread, e: Throwable) {
|
||||
val intent = CrashActivity.newIntent(applicationContext, e)
|
||||
intent.flags = (Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
try {
|
||||
applicationContext.startActivity(intent)
|
||||
} catch (t: Throwable) {
|
||||
t.printStackTraceDebug()
|
||||
}
|
||||
Log.e("CRASH", e.message, e)
|
||||
exitProcess(1)
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
package org.koitharu.kotatsu.core.ui
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.databinding.ActivityCrashBinding
|
||||
import org.koitharu.kotatsu.main.ui.MainActivity
|
||||
import org.koitharu.kotatsu.parsers.util.ellipsize
|
||||
import org.koitharu.kotatsu.utils.ShareHelper
|
||||
|
||||
class CrashActivity : Activity(), View.OnClickListener {
|
||||
|
||||
private lateinit var binding: ActivityCrashBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityCrashBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
binding.textView.text = intent.getStringExtra(Intent.EXTRA_TEXT)
|
||||
binding.buttonClose.setOnClickListener(this)
|
||||
binding.buttonRestart.setOnClickListener(this)
|
||||
binding.buttonReport.setOnClickListener(this)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||
menuInflater.inflate(R.menu.opt_crash, menu)
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_share -> {
|
||||
ShareHelper(this).shareText(binding.textView.text.toString())
|
||||
}
|
||||
else -> return super.onOptionsItemSelected(item)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.button_close -> {
|
||||
finish()
|
||||
}
|
||||
R.id.button_restart -> {
|
||||
val intent = Intent(applicationContext, MainActivity::class.java)
|
||||
intent.flags = (Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
R.id.button_report -> {
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.data = Uri.parse("https://github.com/nv95/Kotatsu/issues")
|
||||
try {
|
||||
startActivity(Intent.createChooser(intent, getString(R.string.report_github)))
|
||||
} catch (_: ActivityNotFoundException) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val MAX_TRACE_SIZE = 131071
|
||||
|
||||
fun newIntent(context: Context, error: Throwable): Intent {
|
||||
val crashInfo = error
|
||||
.stackTraceToString()
|
||||
.trimIndent()
|
||||
.ellipsize(MAX_TRACE_SIZE)
|
||||
val intent = Intent(context, CrashActivity::class.java)
|
||||
intent.putExtra(Intent.EXTRA_TEXT, crashInfo)
|
||||
return intent
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,8 @@ import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import com.google.android.material.R as materialR
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ui.CrashActivity
|
||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||
import org.koitharu.kotatsu.download.domain.DownloadState
|
||||
import org.koitharu.kotatsu.download.ui.DownloadsActivity
|
||||
@@ -20,7 +20,6 @@ import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.util.format
|
||||
import org.koitharu.kotatsu.utils.PendingIntentCompat
|
||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||
import com.google.android.material.R as materialR
|
||||
|
||||
class DownloadNotification(private val context: Context, startId: Int) {
|
||||
|
||||
@@ -92,14 +91,6 @@ class DownloadNotification(private val context: Context, startId: Int) {
|
||||
builder.setContentText(message)
|
||||
builder.setAutoCancel(true)
|
||||
builder.setOngoing(false)
|
||||
builder.setContentIntent(
|
||||
PendingIntent.getActivity(
|
||||
context,
|
||||
state.manga.hashCode(),
|
||||
CrashActivity.newIntent(context, state.error),
|
||||
PendingIntent.FLAG_CANCEL_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
|
||||
)
|
||||
)
|
||||
builder.setCategory(NotificationCompat.CATEGORY_ERROR)
|
||||
builder.setStyle(NotificationCompat.BigTextStyle().bigText(message))
|
||||
}
|
||||
|
||||
@@ -216,10 +216,5 @@ class TrackWorker(context: Context, workerParams: WorkerParameters) :
|
||||
works.any { x -> x.state == WorkInfo.State.RUNNING }
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getInfo(context: Context): List<WorkInfo> {
|
||||
val query = WorkQuery.Builder.fromTags(listOf(TAG, TAG_ONESHOT)).build()
|
||||
return WorkManager.getInstance(context).getWorkInfos(query).await().orEmpty()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context=".core.ui.CrashActivity">
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="4dp"
|
||||
android:textIsSelectable="true" />
|
||||
</ScrollView>
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_report"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:drawableEnd="@android:drawable/ic_menu_set_as"
|
||||
android:text="@string/report_github" />
|
||||
|
||||
<LinearLayout
|
||||
style="?buttonBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:weightSum="2">
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_close"
|
||||
style="?buttonBarButtonStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="2dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/close" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_restart"
|
||||
style="?buttonBarButtonStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="2dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/restart" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -6,6 +6,7 @@
|
||||
<string name="url_twitter">https://twitter.com/kotatsuapp</string>
|
||||
<string name="url_reddit">https://reddit.com/user/kotatsuapp</string>
|
||||
<string name="url_weblate">https://hosted.weblate.org/engage/kotatsu</string>
|
||||
<string name="email_error_report">kotatsu@waifu.club</string>
|
||||
<string-array name="values_theme" translatable="false">
|
||||
<item>-1</item>
|
||||
<item>1</item>
|
||||
|
||||
@@ -298,4 +298,6 @@
|
||||
<string name="detect_reader_mode_summary">Automatically detect if manga is webtoon</string>
|
||||
<string name="disable_battery_optimization">Disable battery optimization</string>
|
||||
<string name="disable_battery_optimization_summary">Helps with background updates checks</string>
|
||||
<string name="crash_text">Something went wrong. Please submit a bug report to the developers to help us fix it.</string>
|
||||
<string name="send">Send</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user