commit a373c8fceb559147ca0fd5c9941bef7b7d00017b
parent f57d880ec82b4898b45534836ab20865a88e2dee
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date: Sat, 30 Mar 2024 01:37:22 +0100
feat(core): crash overlay
Diffstat:
2 files changed, 78 insertions(+), 9 deletions(-)
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/SnapEnhance.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/SnapEnhance.kt
@@ -23,11 +23,11 @@ import me.rhunk.snapenhance.core.bridge.loadFromBridge
import me.rhunk.snapenhance.core.data.SnapClassCache
import me.rhunk.snapenhance.core.event.events.impl.NativeUnaryCallEvent
import me.rhunk.snapenhance.core.event.events.impl.SnapWidgetBroadcastReceiveEvent
+import me.rhunk.snapenhance.core.ui.InAppOverlay
import me.rhunk.snapenhance.core.util.LSPatchUpdater
import me.rhunk.snapenhance.core.util.hook.HookAdapter
import me.rhunk.snapenhance.core.util.hook.HookStage
import me.rhunk.snapenhance.core.util.hook.hook
-import java.lang.reflect.Modifier
import kotlin.system.measureTimeMillis
@@ -60,18 +60,23 @@ class SnapEnhance {
bridgeClient.apply {
connect(
onFailure = {
- crash("Snapchat can't connect to the SnapEnhance app. Please download stable version from https://github.com/rhunk/SnapEnhance/releases", it)
+ InAppOverlay.showCrashOverlay(
+ "Snapchat can't connect to the SnapEnhance app. Make sure you have the latest version installed on your device. You can download the latest stable version on github.com/rhunk/SnapEnhance",
+ throwable = it
+ )
}
) { bridgeResult ->
if (!bridgeResult) {
+ InAppOverlay.showCrashOverlay(
+ "Snapchat timed out while trying to connect to the SnapEnhance app. Make sure you have disabled any battery optimizations for SnapEnhance."
+ )
logCritical("Cannot connect to the SnapEnhance app")
- softRestartApp()
return@connect
}
runCatching {
LSPatchUpdater.onBridgeConnected(appContext, bridgeClient)
}.onFailure {
- logCritical("Failed to init LSPatchUpdater", it)
+ log.error("Failed to init LSPatchUpdater", it)
}
runCatching {
measureTimeMillis {
@@ -85,6 +90,7 @@ class SnapEnhance {
isBridgeInitialized = true
}.onFailure {
logCritical("Failed to initialize bridge", it)
+ InAppOverlay.showCrashOverlay("SnapEnhance failed to initialize. Please check logs for more details.")
}
}
}
@@ -315,7 +321,7 @@ class SnapEnhance {
}
val stringResources = material3RString.fields.filter {
- Modifier.isStatic(it.modifiers) && it.type == Int::class.javaPrimitiveType
+ java.lang.reflect.Modifier.isStatic(it.modifiers) && it.type == Int::class.javaPrimitiveType
}.associate { it.getInt(null) to it.name }
Resources::class.java.getMethod("getString", Int::class.javaPrimitiveType).hook(HookStage.BEFORE) { param ->
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/ui/InAppOverlay.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/ui/InAppOverlay.kt
@@ -1,6 +1,7 @@
package me.rhunk.snapenhance.core.ui
import android.app.Activity
+import android.view.View
import android.widget.FrameLayout
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.LinearEasing
@@ -15,10 +16,7 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Warning
-import androidx.compose.material3.Card
-import androidx.compose.material3.Icon
-import androidx.compose.material3.LinearProgressIndicator
-import androidx.compose.material3.Text
+import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -30,13 +28,78 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
import kotlinx.coroutines.delay
import me.rhunk.snapenhance.common.ui.AppMaterialTheme
import me.rhunk.snapenhance.common.ui.createComposeView
+import me.rhunk.snapenhance.common.util.ktx.copyToClipboard
+import me.rhunk.snapenhance.core.util.hook.HookStage
+import me.rhunk.snapenhance.core.util.hook.Hooker
import me.rhunk.snapenhance.core.util.ktx.isDarkTheme
import kotlin.math.roundToInt
class InAppOverlay {
+ companion object {
+ fun showCrashOverlay(content: String, throwable: Throwable? = null) {
+ Hooker.ephemeralHook(Activity::class.java, "onPostCreate", HookStage.AFTER) { param ->
+ val contentView = param.thisObject<Activity>().findViewById<FrameLayout>(android.R.id.content)
+ contentView.children().forEach { it.visibility = View.GONE }
+ lateinit var screenView: View
+ screenView = createComposeView(param.thisObject()) {
+ AppMaterialTheme(isDarkTheme = true) {
+ Surface(
+ color = MaterialTheme.colorScheme.surface
+ ) {
+ Box(
+ modifier = Modifier.fillMaxSize(),
+ contentAlignment = Alignment.Center
+ ) {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(16.dp),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Text(
+ text = "SnapEnhance",
+ fontSize = 28.sp
+ )
+ Spacer(modifier = Modifier.height(40.dp))
+ Text(
+ text = content,
+ fontSize = 16.sp
+ )
+ Spacer(modifier = Modifier.height(40.dp))
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceEvenly
+ ) {
+ throwable?.let {
+ Button(onClick = {
+ contentView.context.copyToClipboard(it.stackTraceToString())
+ }) {
+ Text("Copy error to clipboard")
+ }
+ }
+ Button(onClick = {
+ contentView.children().forEach { it.visibility = View.VISIBLE }
+ contentView.removeView(screenView)
+ }) {
+ Text("Ignore")
+ }
+ }
+ }
+ }
+ }
+ }
+ }.apply {
+ layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
+ }
+ contentView.addView(screenView)
+ }
+ }
+ }
+
inner class Toast(
val composable: @Composable Toast.() -> Unit,
val durationMs: Int