mirror of https://github.com/readrops/Readrops.git
Display an error screen when the app crashes
This commit is contained in:
parent
4d9f71559f
commit
44b2858cb0
|
@ -24,7 +24,9 @@
|
|||
android:resource="@xml/file_paths" />
|
||||
</provider>
|
||||
|
||||
<receiver android:name=".sync.SyncBroadcastReceiver" android:exported="false" />
|
||||
<receiver
|
||||
android:name=".sync.SyncBroadcastReceiver"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
|
@ -38,7 +40,13 @@
|
|||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".util.CrashActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="false"
|
||||
android:finishOnTaskLaunch="true"
|
||||
android:launchMode="singleInstance"
|
||||
android:theme="@style/Theme.Readrops" />
|
||||
</application>
|
||||
|
||||
|
||||
</manifest>
|
|
@ -3,12 +3,14 @@ package com.readrops.app
|
|||
import android.app.Application
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import coil.ImageLoader
|
||||
import coil.ImageLoaderFactory
|
||||
import coil.disk.DiskCache
|
||||
import com.readrops.api.apiModule
|
||||
import com.readrops.app.util.CrashActivity
|
||||
import com.readrops.db.dbModule
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.android.ext.koin.androidLogger
|
||||
|
@ -16,12 +18,24 @@ import org.koin.core.component.KoinComponent
|
|||
import org.koin.core.component.get
|
||||
import org.koin.core.context.startKoin
|
||||
import org.koin.core.logger.Level
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
open class ReadropsApp : Application(), KoinComponent, ImageLoaderFactory {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
Thread.setDefaultUncaughtExceptionHandler { _, throwable ->
|
||||
val intent = Intent(this, CrashActivity::class.java).apply {
|
||||
putExtra(CrashActivity.THROWABLE_KEY, throwable)
|
||||
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||
}
|
||||
|
||||
startActivity(intent)
|
||||
exitProcess(0)
|
||||
}
|
||||
|
||||
startKoin {
|
||||
androidLogger(Level.ERROR)
|
||||
androidContext(this@ReadropsApp)
|
||||
|
|
|
@ -0,0 +1,169 @@
|
|||
package com.readrops.app.util
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.SystemBarStyle
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.readrops.app.R
|
||||
import com.readrops.app.util.theme.MediumSpacer
|
||||
import com.readrops.app.util.theme.ReadropsTheme
|
||||
import com.readrops.app.util.theme.ShortSpacer
|
||||
import com.readrops.app.util.theme.VeryLargeSpacer
|
||||
import com.readrops.app.util.theme.VeryShortSpacer
|
||||
import com.readrops.app.util.theme.spacing
|
||||
|
||||
class CrashActivity : ComponentActivity() {
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
enableEdgeToEdge(statusBarStyle = SystemBarStyle.auto(Color.TRANSPARENT, Color.TRANSPARENT))
|
||||
|
||||
val throwable = intent.getSerializableExtra(THROWABLE_KEY) as Throwable?
|
||||
|
||||
setContent {
|
||||
ReadropsTheme {
|
||||
CrashScreen(throwable?.stackTraceToString().orEmpty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val THROWABLE_KEY = "THROWABLE"
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CrashScreen(stackTrace: String) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
val clipboardManager = LocalClipboardManager.current
|
||||
val context = LocalContext.current
|
||||
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.statusBarsPadding()
|
||||
.padding(MaterialTheme.spacing.mediumSpacing)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
VeryLargeSpacer()
|
||||
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_bug),
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(64.dp)
|
||||
)
|
||||
|
||||
MediumSpacer()
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.readrops_crashed),
|
||||
style = MaterialTheme.typography.titleLarge
|
||||
)
|
||||
|
||||
ShortSpacer()
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.crash_message),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
|
||||
MediumSpacer()
|
||||
|
||||
Surface(
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
color = MaterialTheme.colorScheme.surfaceContainerLow,
|
||||
modifier = Modifier
|
||||
.weight(1f, fill = true)
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Text(
|
||||
text = stackTrace,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
textAlign = TextAlign.Justify,
|
||||
lineHeight = 20.sp,
|
||||
modifier = Modifier
|
||||
.padding(MaterialTheme.spacing.mediumSpacing)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.horizontalScroll(rememberScrollState())
|
||||
)
|
||||
}
|
||||
|
||||
MediumSpacer()
|
||||
|
||||
Column {
|
||||
Button(
|
||||
onClick = {
|
||||
uriHandler.openUri("https://github.com/readrops/Readrops/issues/new")
|
||||
clipboardManager.setText(AnnotatedString(stackTrace))
|
||||
displayToast(context)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(text = stringResource(R.string.report_error_github))
|
||||
}
|
||||
|
||||
VeryShortSpacer()
|
||||
|
||||
OutlinedButton(
|
||||
onClick = {
|
||||
clipboardManager.setText(AnnotatedString(stackTrace))
|
||||
displayToast(context)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(text = stringResource(R.string.copy_error_clipboard))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun displayToast(context: Context) {
|
||||
Toast.makeText(context, R.string.copied, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
@DefaultPreview
|
||||
@Composable
|
||||
private fun CrashScreenPreview() {
|
||||
ReadropsTheme {
|
||||
CrashScreen("")
|
||||
}
|
||||
}
|
|
@ -3,12 +3,12 @@ package com.readrops.app.util
|
|||
import android.content.res.Configuration
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
||||
@Preview(
|
||||
uiMode = Configuration.UI_MODE_NIGHT_YES,
|
||||
showBackground = true
|
||||
)
|
||||
@Preview(
|
||||
uiMode = Configuration.UI_MODE_NIGHT_NO,
|
||||
showBackground = true
|
||||
)
|
||||
@Preview(
|
||||
uiMode = Configuration.UI_MODE_NIGHT_YES,
|
||||
showBackground = true
|
||||
)
|
||||
annotation class DefaultPreview
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="512dp"
|
||||
android:height="512dp"
|
||||
android:viewportWidth="512"
|
||||
android:viewportHeight="512">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M256,0c53,0 96,43 96,96l0,3.6c0,15.7 -12.7,28.4 -28.4,28.4l-135.1,0c-15.7,0 -28.4,-12.7 -28.4,-28.4l0,-3.6c0,-53 43,-96 96,-96zM41.4,105.4c12.5,-12.5 32.8,-12.5 45.3,0l64,64c0.7,0.7 1.3,1.4 1.9,2.1c14.2,-7.3 30.4,-11.4 47.5,-11.4l112,0c17.1,0 33.2,4.1 47.5,11.4c0.6,-0.7 1.2,-1.4 1.9,-2.1l64,-64c12.5,-12.5 32.8,-12.5 45.3,0s12.5,32.8 0,45.3l-64,64c-0.7,0.7 -1.4,1.3 -2.1,1.9c6.2,12 10.1,25.3 11.1,39.5l64.3,0c17.7,0 32,14.3 32,32s-14.3,32 -32,32l-64,0c0,24.6 -5.5,47.8 -15.4,68.6c2.2,1.3 4.2,2.9 6,4.8l64,64c12.5,12.5 12.5,32.8 0,45.3s-32.8,12.5 -45.3,0l-63.1,-63.1c-24.5,21.8 -55.8,36.2 -90.3,39.6L272,240c0,-8.8 -7.2,-16 -16,-16s-16,7.2 -16,16l0,239.2c-34.5,-3.4 -65.8,-17.8 -90.3,-39.6L86.6,502.6c-12.5,12.5 -32.8,12.5 -45.3,0s-12.5,-32.8 0,-45.3l64,-64c1.9,-1.9 3.9,-3.4 6,-4.8C101.5,367.8 96,344.6 96,320l-64,0c-17.7,0 -32,-14.3 -32,-32s14.3,-32 32,-32l64.3,0c1.1,-14.1 5,-27.5 11.1,-39.5c-0.7,-0.6 -1.4,-1.2 -2.1,-1.9l-64,-64c-12.5,-12.5 -12.5,-32.8 0,-45.3z"/>
|
||||
</vector>
|
|
@ -208,4 +208,9 @@
|
|||
<string name="external">Externe</string>
|
||||
<string name="mark_all_articles_read">Marquer les articles comme lus</string>
|
||||
<string name="mark_all_articles_read_question">Voulez-vous vraiment marquer tous les articles comme lus ?</string>
|
||||
<string name="readrops_crashed">Readrops a planté.</string>
|
||||
<string name="crash_message">Si vous voyez ceci, cela signifie que l\'application a rencontré une erreur inattendue. Si cela est possible, merci de bien vouloir signaler cette erreur sur Github.</string>
|
||||
<string name="report_error_github">Signaler l\'erreur sur Github</string>
|
||||
<string name="copy_error_clipboard">Copier l\'erreur dans le presse-papier</string>
|
||||
<string name="copied">Copié !</string>
|
||||
</resources>
|
|
@ -217,4 +217,9 @@
|
|||
<string name="external">External</string>
|
||||
<string name="mark_all_articles_read">Mark all articles as read</string>
|
||||
<string name="mark_all_articles_read_question">Do you really want to mark all items as read?</string>
|
||||
<string name="readrops_crashed">Readrops crashed.</string>
|
||||
<string name="crash_message">I you see this, it means the app ran into an unexpected error. Please if you can, report the error on Github.</string>
|
||||
<string name="report_error_github">Report the error on Github</string>
|
||||
<string name="copy_error_clipboard">Copy error to clipboard</string>
|
||||
<string name="copied">Copied!</string>
|
||||
</resources>
|
Loading…
Reference in New Issue