mirror of
https://github.com/ruffle-rs/ruffle-android.git
synced 2024-11-23 05:39:38 +00:00
Add a (super ugly) panic screen
Some checks failed
Build / build-native-libs (arm64-v8a, aarch64-linux-android) (push) Failing after 1s
Build / build-native-libs (armeabi-v7a, armv7-linux-androideabi) (push) Failing after 1s
Build / build-native-libs (x86, i686-linux-android) (push) Failing after 1s
Build / build-native-libs (x86_64, x86_64-linux-android) (push) Failing after 2s
Build / build-apks (push) Has been skipped
Build / Android Tests (26) (push) Has been skipped
Build / Android Tests (34) (push) Has been skipped
Lint & Format / rust (push) Failing after 2s
Lint & Format / android (push) Failing after 1s
Some checks failed
Build / build-native-libs (arm64-v8a, aarch64-linux-android) (push) Failing after 1s
Build / build-native-libs (armeabi-v7a, armv7-linux-androideabi) (push) Failing after 1s
Build / build-native-libs (x86, i686-linux-android) (push) Failing after 1s
Build / build-native-libs (x86_64, x86_64-linux-android) (push) Failing after 2s
Build / build-apks (push) Has been skipped
Build / Android Tests (26) (push) Has been skipped
Build / Android Tests (34) (push) Has been skipped
Lint & Format / rust (push) Failing after 2s
Lint & Format / android (push) Failing after 1s
This commit is contained in:
parent
fd9c1b656e
commit
0319ac9dad
12
Cargo.lock
generated
12
Cargo.lock
generated
@ -1926,16 +1926,6 @@ version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "log-panics"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68f9dd8546191c1850ecf67d22f5ff00a935b890d0e84713159a55495cc2ac5f"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lru"
|
||||
version = "0.12.4"
|
||||
@ -2769,9 +2759,9 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"android-activity",
|
||||
"android_logger",
|
||||
"backtrace",
|
||||
"jni",
|
||||
"log",
|
||||
"log-panics",
|
||||
"ndk",
|
||||
"ndk-context",
|
||||
"ruffle_core",
|
||||
|
@ -40,10 +40,10 @@ ruffle_video_software = { git = "https://github.com/ruffle-rs/ruffle.git", branc
|
||||
ruffle_frontend_utils = { git = "https://github.com/ruffle-rs/ruffle.git", branch = "master" }
|
||||
|
||||
log = "0.4.22"
|
||||
log-panics = { version = "2.1.0", features = ["with-backtrace"]}
|
||||
|
||||
# Redirect tracing to log
|
||||
tracing = {version = "0.1.40", features = ["log", "log-always"]}
|
||||
backtrace = "0.3.74"
|
||||
|
||||
url = "2.5.2"
|
||||
webbrowser = "1.0.1"
|
||||
|
@ -24,6 +24,11 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".PanicActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:exported="false">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".PlayerActivity"
|
||||
android:exported="true"
|
||||
|
20
app/src/main/java/rs/ruffle/PanicActivity.kt
Normal file
20
app/src/main/java/rs/ruffle/PanicActivity.kt
Normal file
@ -0,0 +1,20 @@
|
||||
package rs.ruffle
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import rs.ruffle.ui.theme.RuffleTheme
|
||||
|
||||
class PanicActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
enableEdgeToEdge()
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContent {
|
||||
RuffleTheme {
|
||||
PanicScreen(message = intent.getStringExtra("message") ?: "Unknown")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
77
app/src/main/java/rs/ruffle/PanicScreen.kt
Normal file
77
app/src/main/java/rs/ruffle/PanicScreen.kt
Normal file
@ -0,0 +1,77 @@
|
||||
package rs.ruffle
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
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.wrapContentSize
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import rs.ruffle.ui.theme.RuffleTheme
|
||||
|
||||
@Composable
|
||||
fun PanicScreen(message: String) {
|
||||
Scaffold { innerPadding ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentSize(align = Alignment.Center),
|
||||
style = MaterialTheme.typography.headlineLarge,
|
||||
text = "Ruffle Panicked :("
|
||||
)
|
||||
SelectionContainer {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.wrapContentSize(align = Alignment.Center)
|
||||
.padding(horizontal = 8.dp, vertical = 20.dp)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.horizontalScroll(rememberScrollState()),
|
||||
text = message,
|
||||
softWrap = false
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(name = "Panic - Light", uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||
@Preview(name = "Panic - Dark", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
fun PanicScreenPreview() {
|
||||
RuffleTheme {
|
||||
PanicScreen(
|
||||
message = """Error: panicked at core/src/display_object/movie_clip.rs:477:9:
|
||||
assertion `left == right` failed: Called replace_movie on a clip with LoaderInfo set
|
||||
left: Some(LoaderInfoObject(LoaderInfoObject { ptr: 0x31b30a8 }))
|
||||
right: None
|
||||
at n.wbg.__wbg_new_796382978dfd4fb0 (https://unpkg.com/@ruffle-rs/ruffle/core.ruffle.90db0a0ab193ed0c601b.js:1:83857)
|
||||
at ruffle_web.wasm.js_sys::Error::new::hfb561c222a4e70eb (wasm://wasm/ruffle_web.wasm-0321683a:wasm-function[12733]:0x98671a)
|
||||
at ruffle_web.wasm.core::ops::function::FnOnce::call_once{{vtable.shim}}::h8a2a563fa204b611 (wasm://wasm/ruffle_web.wasm-0321683a:wasm-function[9789]:0x9164aa)
|
||||
at ruffle_web.wasm.std::panicking::rust_panic_with_hook::h33fe77d38d305ca3 (wasm://wasm/ruffle_web.wasm-0321683a:wasm-function[6355]:0x8070ed)
|
||||
at ruffle_web.wasm.core::panicking::panic_fmt::hde8b7aa66e2831e1 (wasm://wasm/ruffle_web.wasm-0321683a:wasm-function[9511]:0x9071fd)
|
||||
at ruffle_web.wasm.core::panicking::assert_failed_inner::hc95b7725cb4077cb (wasm://wasm/ruffle_web.wasm-0321683a:wasm-function[4402]:0x73cb5e)
|
||||
at ruffle_web.wasm.ruffle_core::display_object::movie_clip::MovieClip::replace_with_movie::haf940b0718ed269c (wasm://wasm/ruffle_web.wasm-0321683a:wasm-function[2052]:0x50a035)
|
||||
at ruffle_web.wasm.ruffle_core::loader::Loader::movie_loader::{{closure}}::h566c935379317178 (wasm://wasm/ruffle_web.wasm-0321683a:wasm-function[1053]:0x2bc268)
|
||||
at ruffle_web.wasm.<ruffle_web::navigator::WebNavigatorBackend as ruffle_core::backend::navigator::NavigatorBackend>::spawn_future::{{closure}}::h13f3540dbe40e875 (wasm://wasm/ruffle_web.wasm-0321683a:wasm-function[1520]:0x419980)
|
||||
at ruffle_web.wasm.wasm_bindgen_futures::queue::Queue::new::{{closure}}::hf37247571cf9bbf7 (wasm://wasm/ruffle_web.wasm-0321683a:wasm-function[3648]:0x6ba342)"""
|
||||
)
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Build.VERSION_CODES
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.MotionEvent
|
||||
@ -230,6 +231,14 @@ class PlayerActivity : GameActivity() {
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
nativeInit { message ->
|
||||
Log.e("ruffle", "Handling panic: $message")
|
||||
startActivity(
|
||||
Intent(this, PanicActivity::class.java).apply {
|
||||
putExtra("message", message)
|
||||
}
|
||||
)
|
||||
}
|
||||
// When true, the app will fit inside any system UI windows.
|
||||
// When false, we render behind any system UI windows.
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
@ -264,11 +273,10 @@ class PlayerActivity : GameActivity() {
|
||||
init {
|
||||
// load the native activity
|
||||
System.loadLibrary("ruffle_android")
|
||||
nativeInit()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
private external fun nativeInit()
|
||||
private external fun nativeInit(crashCallback: CrashCallback)
|
||||
|
||||
private fun <T> gatherAllDescendantsOfType(v: View, t: Class<*>): List<T> {
|
||||
val result: MutableList<T> = ArrayList()
|
||||
@ -282,4 +290,8 @@ class PlayerActivity : GameActivity() {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
fun interface CrashCallback {
|
||||
fun onCrash(message: String)
|
||||
}
|
||||
}
|
||||
|
@ -172,8 +172,7 @@ impl JavaInterface {
|
||||
}
|
||||
|
||||
pub fn init(env: &mut JNIEnv, class: &JClass) {
|
||||
JAVA_INTERFACE
|
||||
.set(JavaInterface {
|
||||
let _ = JAVA_INTERFACE.set(JavaInterface {
|
||||
get_surface_width: env
|
||||
.get_method_id(class, "getSurfaceWidth", "()I")
|
||||
.expect("getSurfaceWidth must exist"),
|
||||
@ -198,7 +197,6 @@ impl JavaInterface {
|
||||
get_android_data_storage_dir: env
|
||||
.get_method_id(class, "getAndroidDataStorageDir", "()Ljava/lang/String;")
|
||||
.expect("getAndroidDataStorageDir must exist"),
|
||||
})
|
||||
.unwrap_or_else(|_| panic!("Init cannot be called more than once!"))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
80
src/lib.rs
80
src/lib.rs
@ -18,13 +18,16 @@ use std::sync::mpsc::Sender;
|
||||
use std::sync::{mpsc, MutexGuard};
|
||||
use std::time::Duration;
|
||||
use std::{
|
||||
panic,
|
||||
sync::{Arc, Mutex},
|
||||
thread,
|
||||
time::Instant,
|
||||
};
|
||||
use wgpu::rwh::{AndroidDisplayHandle, HasWindowHandle, RawDisplayHandle};
|
||||
|
||||
use android_activity::input::{InputEvent, KeyAction, MotionAction};
|
||||
use android_activity::{AndroidApp, AndroidAppWaker, InputStatus, MainEvent, PollEvent};
|
||||
use backtrace::Backtrace;
|
||||
use jni::objects::JClass;
|
||||
|
||||
use audio::AAudioAudioBackend;
|
||||
@ -565,7 +568,69 @@ pub unsafe extern "C" fn Java_rs_ruffle_PlayerActivity_clearContextMenu(
|
||||
|
||||
#[no_mangle]
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
pub unsafe extern "C" fn Java_rs_ruffle_PlayerActivity_nativeInit(mut env: JNIEnv, class: JClass) {
|
||||
pub unsafe extern "C" fn Java_rs_ruffle_PlayerActivity_nativeInit(
|
||||
mut env: JNIEnv,
|
||||
class: JClass,
|
||||
crash_callback: JObject,
|
||||
) {
|
||||
let crash_callback = env.new_global_ref(crash_callback).unwrap();
|
||||
let jvm = env.get_java_vm().unwrap();
|
||||
|
||||
android_logger::init_once(
|
||||
android_logger::Config::default()
|
||||
.with_max_level(log::LevelFilter::Info)
|
||||
.with_tag("ruffle")
|
||||
.with_filter(
|
||||
android_logger::FilterBuilder::new()
|
||||
.parse("warn,ruffle=info")
|
||||
.build(),
|
||||
),
|
||||
);
|
||||
|
||||
panic::set_hook(Box::new(move |info| {
|
||||
let backtrace = Backtrace::new();
|
||||
let thread = thread::current();
|
||||
let thread = thread.name().unwrap_or("<unnamed>");
|
||||
let message = match info.payload().downcast_ref::<&'static str>() {
|
||||
Some(s) => *s,
|
||||
None => match info.payload().downcast_ref::<String>() {
|
||||
Some(s) => &**s,
|
||||
None => "Box<Any>",
|
||||
},
|
||||
};
|
||||
|
||||
let full = match info.location() {
|
||||
Some(location) => format!(
|
||||
"thread '{}' panicked at '{}': {}:{}\n{:?}",
|
||||
thread,
|
||||
message,
|
||||
location.file(),
|
||||
location.line(),
|
||||
backtrace
|
||||
),
|
||||
None => format!(
|
||||
"thread '{}' panicked at '{}'\n{:?}",
|
||||
thread, message, backtrace
|
||||
),
|
||||
};
|
||||
log::error!(target: "panic","{}", full);
|
||||
|
||||
let mut env = jvm.attach_current_thread().unwrap();
|
||||
if env.exception_check().unwrap() {
|
||||
// There's a pending exception, java will discover this on their own
|
||||
} else {
|
||||
let java_message = env.new_string(full).unwrap();
|
||||
let crash_callback = env.new_global_ref(&crash_callback).unwrap();
|
||||
env.call_method(
|
||||
crash_callback,
|
||||
"onCrash",
|
||||
"(Ljava/lang/String;)V",
|
||||
&[(&java_message).into()],
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}));
|
||||
|
||||
JavaInterface::init(&mut env, &class)
|
||||
}
|
||||
|
||||
@ -591,19 +656,6 @@ fn get_view_size() -> Result<(i32, i32), Box<dyn std::error::Error>> {
|
||||
|
||||
#[no_mangle]
|
||||
fn android_main(app: AndroidApp) {
|
||||
android_logger::init_once(
|
||||
android_logger::Config::default()
|
||||
.with_max_level(log::LevelFilter::Info)
|
||||
.with_tag("ruffle")
|
||||
.with_filter(
|
||||
android_logger::FilterBuilder::new()
|
||||
.parse("warn,ruffle=info")
|
||||
.build(),
|
||||
),
|
||||
);
|
||||
|
||||
log_panics::init();
|
||||
|
||||
log::info!("Starting android_main...");
|
||||
run(app);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user