Crash screen tweak
This commit is contained in:
@@ -4,8 +4,10 @@ import android.app.Application
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.PackageInfo
|
import android.content.pm.PackageInfo
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
|
import android.net.ConnectivityManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.StrictMode
|
import android.os.StrictMode
|
||||||
|
import androidx.core.content.getSystemService
|
||||||
import androidx.hilt.work.HiltWorkerFactory
|
import androidx.hilt.work.HiltWorkerFactory
|
||||||
import androidx.work.Configuration
|
import androidx.work.Configuration
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
@@ -21,9 +23,10 @@ import org.acra.ktx.initAcra
|
|||||||
import org.acra.sender.HttpSender
|
import org.acra.sender.HttpSender
|
||||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||||
import org.xtimms.shirizu.core.database.ShirizuDatabase
|
import org.xtimms.shirizu.core.database.ShirizuDatabase
|
||||||
import org.xtimms.shirizu.core.prefs.AppSettings
|
|
||||||
import org.xtimms.shirizu.core.prefs.KotatsuAppSettings
|
import org.xtimms.shirizu.core.prefs.KotatsuAppSettings
|
||||||
import org.xtimms.shirizu.core.updates.Updater
|
import org.xtimms.shirizu.core.updates.Updater
|
||||||
|
import org.xtimms.shirizu.crash.CrashActivity
|
||||||
|
import org.xtimms.shirizu.crash.GlobalExceptionHandler
|
||||||
import org.xtimms.shirizu.utils.lang.processLifecycleScope
|
import org.xtimms.shirizu.utils.lang.processLifecycleScope
|
||||||
import org.xtimms.shirizu.work.WorkScheduleManager
|
import org.xtimms.shirizu.work.WorkScheduleManager
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -61,6 +64,7 @@ class App : Application(), Configuration.Provider {
|
|||||||
) else getPackageInfo(packageName, 0)
|
) else getPackageInfo(packageName, 0)
|
||||||
}
|
}
|
||||||
DynamicColors.applyToActivitiesIfAvailable(this)
|
DynamicColors.applyToActivitiesIfAvailable(this)
|
||||||
|
connectivityManager = getSystemService()!!
|
||||||
|
|
||||||
processLifecycleScope.launch(Dispatchers.IO) {
|
processLifecycleScope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
@@ -70,30 +74,29 @@ class App : Application(), Configuration.Provider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GlobalExceptionHandler.initialize(applicationContext, CrashActivity::class.java)
|
initAcra {
|
||||||
if (AppSettings.isACRAEnabled()) {
|
buildConfigClass = BuildConfig::class.java
|
||||||
initAcra {
|
reportFormat = StringFormat.JSON
|
||||||
buildConfigClass = BuildConfig::class.java
|
httpSender {
|
||||||
reportFormat = StringFormat.JSON
|
uri = BuildConfig.ACRA_URI
|
||||||
httpSender {
|
basicAuthLogin = BuildConfig.ACRA_AUTH_LOGIN
|
||||||
uri = BuildConfig.ACRA_URI
|
basicAuthPassword = BuildConfig.ACRA_AUTH_PASSWORD
|
||||||
basicAuthLogin = BuildConfig.ACRA_AUTH_LOGIN
|
httpMethod = HttpSender.Method.POST
|
||||||
basicAuthPassword = BuildConfig.ACRA_AUTH_PASSWORD
|
|
||||||
httpMethod = HttpSender.Method.POST
|
|
||||||
}
|
|
||||||
reportContent = listOf(
|
|
||||||
ReportField.PACKAGE_NAME,
|
|
||||||
ReportField.INSTALLATION_ID,
|
|
||||||
ReportField.APP_VERSION_CODE,
|
|
||||||
ReportField.APP_VERSION_NAME,
|
|
||||||
ReportField.ANDROID_VERSION,
|
|
||||||
ReportField.PHONE_MODEL,
|
|
||||||
ReportField.STACK_TRACE,
|
|
||||||
ReportField.CRASH_CONFIGURATION,
|
|
||||||
ReportField.CUSTOM_DATA,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
reportContent = listOf(
|
||||||
|
ReportField.PACKAGE_NAME,
|
||||||
|
ReportField.INSTALLATION_ID,
|
||||||
|
ReportField.APP_VERSION_CODE,
|
||||||
|
ReportField.APP_VERSION_NAME,
|
||||||
|
ReportField.ANDROID_VERSION,
|
||||||
|
ReportField.PHONE_MODEL,
|
||||||
|
ReportField.STACK_TRACE,
|
||||||
|
ReportField.CRASH_CONFIGURATION,
|
||||||
|
ReportField.CUSTOM_DATA,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GlobalExceptionHandler.initialize(applicationContext, CrashActivity::class.java)
|
||||||
workScheduleManager.init()
|
workScheduleManager.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,8 +126,8 @@ class App : Application(), Configuration.Provider {
|
|||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
lateinit var packageInfo: PackageInfo
|
lateinit var packageInfo: PackageInfo
|
||||||
|
lateinit var connectivityManager: ConnectivityManager
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
fun getVersionReport(): String {
|
fun getVersionReport(): String {
|
||||||
val versionName = packageInfo.versionName
|
val versionName = packageInfo.versionName
|
||||||
val versionCode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
val versionCode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
|
|||||||
@@ -1,12 +1,21 @@
|
|||||||
package org.xtimms.shirizu.core.screens
|
package org.xtimms.shirizu.core.screens
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedContent
|
||||||
|
import androidx.compose.animation.core.RepeatMode
|
||||||
|
import androidx.compose.animation.core.VectorConverter
|
||||||
|
import androidx.compose.animation.core.animateFloat
|
||||||
|
import androidx.compose.animation.core.animateValue
|
||||||
|
import androidx.compose.animation.core.infiniteRepeatable
|
||||||
|
import androidx.compose.animation.core.rememberInfiniteTransition
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.ColumnScope
|
import androidx.compose.foundation.layout.ColumnScope
|
||||||
|
import androidx.compose.foundation.layout.absoluteOffset
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.requiredSize
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
@@ -21,20 +30,34 @@ import androidx.compose.material3.OutlinedButton
|
|||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.drawBehind
|
import androidx.compose.ui.draw.drawBehind
|
||||||
|
import androidx.compose.ui.draw.rotate
|
||||||
import androidx.compose.ui.geometry.Offset
|
import androidx.compose.ui.geometry.Offset
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.layout.onGloballyPositioned
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.zIndex
|
import androidx.compose.ui.zIndex
|
||||||
|
import org.xtimms.shirizu.R
|
||||||
|
import org.xtimms.shirizu.sections.stats.Size
|
||||||
|
import org.xtimms.shirizu.ui.theme.ShirizuTheme
|
||||||
import org.xtimms.shirizu.utils.composable.secondaryItemAlpha
|
import org.xtimms.shirizu.utils.composable.secondaryItemAlpha
|
||||||
|
import org.xtimms.shirizu.utils.material.combineColors
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun InfoScreen(
|
fun InfoScreen(
|
||||||
icon: ImageVector,
|
icon: ImageVector,
|
||||||
headingText: String,
|
headingText1: String,
|
||||||
|
headingText2: String,
|
||||||
subtitleText: String,
|
subtitleText: String,
|
||||||
acceptText: String,
|
acceptText: String,
|
||||||
onAcceptClick: () -> Unit,
|
onAcceptClick: () -> Unit,
|
||||||
@@ -43,6 +66,9 @@ fun InfoScreen(
|
|||||||
onRejectClick: (() -> Unit)? = null,
|
onRejectClick: (() -> Unit)? = null,
|
||||||
content: @Composable ColumnScope.() -> Unit,
|
content: @Composable ColumnScope.() -> Unit,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
val localDensity = LocalDensity.current
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
bottomBar = {
|
bottomBar = {
|
||||||
val strokeWidth = Dp.Hairline
|
val strokeWidth = Dp.Hairline
|
||||||
@@ -82,15 +108,50 @@ fun InfoScreen(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
// Status bar scrim
|
|
||||||
|
var headerSize by remember { mutableStateOf(Size(0.dp, 0.dp)) }
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.zIndex(2f)
|
|
||||||
.secondaryItemAlpha()
|
|
||||||
.background(MaterialTheme.colorScheme.background)
|
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.height(paddingValues.calculateTopPadding()),
|
.onGloballyPositioned {
|
||||||
)
|
headerSize = Size(
|
||||||
|
width = with(localDensity) { it.size.width.toDp() },
|
||||||
|
height = with(localDensity) { it.size.height.toDp() }
|
||||||
|
)
|
||||||
|
},
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
val halfWidth = headerSize.width / 2
|
||||||
|
val halfHeight = headerSize.height / 2
|
||||||
|
|
||||||
|
val starColor1 = combineColors(
|
||||||
|
MaterialTheme.colorScheme.primaryContainer,
|
||||||
|
MaterialTheme.colorScheme.surface,
|
||||||
|
0.5f,
|
||||||
|
)
|
||||||
|
|
||||||
|
val angleStar1 by rememberInfiniteTransition("angleStar1").animateFloat(
|
||||||
|
label = "angleStar1",
|
||||||
|
initialValue = -20f,
|
||||||
|
targetValue = 20f,
|
||||||
|
animationSpec = infiniteRepeatable(tween(5000), RepeatMode.Reverse)
|
||||||
|
)
|
||||||
|
|
||||||
|
Icon(
|
||||||
|
modifier = Modifier
|
||||||
|
.requiredSize(256.dp)
|
||||||
|
.absoluteOffset(
|
||||||
|
x = halfWidth * 0.7f,
|
||||||
|
y = -halfHeight * 0.6f
|
||||||
|
)
|
||||||
|
.rotate(angleStar1)
|
||||||
|
.zIndex(-1f),
|
||||||
|
painter = painterResource(R.drawable.shape_soft_star_1),
|
||||||
|
tint = starColor1,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -108,10 +169,24 @@ fun InfoScreen(
|
|||||||
.size(48.dp),
|
.size(48.dp),
|
||||||
tint = MaterialTheme.colorScheme.primary,
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
)
|
)
|
||||||
Text(
|
val infiniteTransition by rememberInfiniteTransition("infiniteTransition").animateValue(
|
||||||
text = headingText,
|
label = "infiniteTransition",
|
||||||
style = MaterialTheme.typography.headlineLarge,
|
initialValue = 0,
|
||||||
|
targetValue = 2,
|
||||||
|
typeConverter = Int.VectorConverter,
|
||||||
|
animationSpec = infiniteRepeatable(tween(15000), RepeatMode.Restart)
|
||||||
)
|
)
|
||||||
|
val heading = when (infiniteTransition) {
|
||||||
|
0 -> headingText1
|
||||||
|
1 -> headingText2
|
||||||
|
else -> headingText2
|
||||||
|
}
|
||||||
|
AnimatedContent(targetState = heading, label = "heading animation") {
|
||||||
|
Text(
|
||||||
|
text = it,
|
||||||
|
style = MaterialTheme.typography.headlineLarge,
|
||||||
|
)
|
||||||
|
}
|
||||||
Text(
|
Text(
|
||||||
text = subtitleText,
|
text = subtitleText,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -128,15 +203,18 @@ fun InfoScreen(
|
|||||||
@PreviewLightDark
|
@PreviewLightDark
|
||||||
@Composable
|
@Composable
|
||||||
private fun InfoScaffoldPreview() {
|
private fun InfoScaffoldPreview() {
|
||||||
InfoScreen(
|
ShirizuTheme {
|
||||||
icon = Icons.Outlined.Newspaper,
|
InfoScreen(
|
||||||
headingText = "Heading",
|
icon = Icons.Outlined.Newspaper,
|
||||||
subtitleText = "Subtitle",
|
headingText1 = "Heading 1",
|
||||||
acceptText = "Accept",
|
headingText2 = "Heading 2",
|
||||||
onAcceptClick = {},
|
subtitleText = "Subtitle",
|
||||||
rejectText = "Reject",
|
acceptText = "Accept",
|
||||||
onRejectClick = {},
|
onAcceptClick = {},
|
||||||
) {
|
rejectText = "Reject",
|
||||||
Text("Hello world")
|
onRejectClick = {},
|
||||||
|
) {
|
||||||
|
Text("Hello world")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,7 @@ import androidx.activity.enableEdgeToEdge
|
|||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import org.xtimms.shirizu.LocalDarkTheme
|
import org.xtimms.shirizu.LocalDarkTheme
|
||||||
import org.xtimms.shirizu.LocalDynamicColorSwitch
|
import org.xtimms.shirizu.LocalDynamicColorSwitch
|
||||||
|
import org.xtimms.shirizu.LocalWindowWidthState
|
||||||
import org.xtimms.shirizu.MainActivity
|
import org.xtimms.shirizu.MainActivity
|
||||||
import org.xtimms.shirizu.SettingsProvider
|
import org.xtimms.shirizu.SettingsProvider
|
||||||
import org.xtimms.shirizu.ui.theme.ShirizuTheme
|
import org.xtimms.shirizu.ui.theme.ShirizuTheme
|
||||||
@@ -21,7 +22,7 @@ class CrashActivity : ComponentActivity() {
|
|||||||
|
|
||||||
val exception = GlobalExceptionHandler.getThrowableFromIntent(intent)
|
val exception = GlobalExceptionHandler.getThrowableFromIntent(intent)
|
||||||
setContent {
|
setContent {
|
||||||
SettingsProvider {
|
SettingsProvider(LocalWindowWidthState.current) {
|
||||||
ShirizuTheme(
|
ShirizuTheme(
|
||||||
darkTheme = LocalDarkTheme.current.isDarkTheme(),
|
darkTheme = LocalDarkTheme.current.isDarkTheme(),
|
||||||
isDynamicColorEnabled = LocalDynamicColorSwitch.current,
|
isDynamicColorEnabled = LocalDynamicColorSwitch.current,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.xtimms.shirizu.crash
|
package org.xtimms.shirizu.crash
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
@@ -9,7 +10,6 @@ import androidx.compose.material.icons.outlined.BugReport
|
|||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
@@ -17,29 +17,30 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.text.font.FontFamily
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import kotlinx.coroutines.launch
|
import org.acra.ktx.sendWithAcra
|
||||||
import org.xtimms.shirizu.R
|
import org.xtimms.shirizu.R
|
||||||
import org.xtimms.shirizu.core.screens.InfoScreen
|
import org.xtimms.shirizu.core.screens.InfoScreen
|
||||||
import org.xtimms.shirizu.ui.theme.ShirizuTheme
|
import org.xtimms.shirizu.ui.theme.ShirizuTheme
|
||||||
import org.xtimms.shirizu.utils.CrashLogUtil
|
import org.xtimms.shirizu.utils.system.toast
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CrashScreen(
|
fun CrashScreen(
|
||||||
exception: Throwable?,
|
exception: Throwable?,
|
||||||
onRestartClick: () -> Unit,
|
onRestartClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
val activity = (context as? Activity)
|
||||||
|
|
||||||
InfoScreen(
|
InfoScreen(
|
||||||
icon = Icons.Outlined.BugReport,
|
icon = Icons.Outlined.BugReport,
|
||||||
headingText = stringResource(R.string.crash_screen_title),
|
headingText1 = stringResource(R.string.crash_screen_title),
|
||||||
|
headingText2 = stringResource(id = R.string.crash_screen_something_went_wrong),
|
||||||
subtitleText = stringResource(R.string.crash_screen_description, stringResource(R.string.app_name)),
|
subtitleText = stringResource(R.string.crash_screen_description, stringResource(R.string.app_name)),
|
||||||
acceptText = stringResource(R.string.pref_dump_crash_logs),
|
acceptText = stringResource(R.string.pref_send_crash_logs),
|
||||||
onAcceptClick = {
|
onAcceptClick = {
|
||||||
scope.launch {
|
exception?.sendWithAcra()
|
||||||
CrashLogUtil(context).dumpLogs()
|
context.toast(context.resources.getString(R.string.crash_screen_toast))
|
||||||
}
|
activity?.finishAndRemoveTask()
|
||||||
},
|
},
|
||||||
rejectText = stringResource(R.string.crash_screen_restart_application),
|
rejectText = stringResource(R.string.crash_screen_restart_application),
|
||||||
onRejectClick = onRestartClick,
|
onRejectClick = onRestartClick,
|
||||||
|
|||||||
Reference in New Issue
Block a user