iris is the framework alone; the app is one crate in app-rust/
Iris: "the organization of the rust rewrite is a mess right now... there shouldn't be anything related to the app inside of iris. Iris is supposed to be the UI framework alone." And, on the crate count: "I'm confused why the app only code needs more than one crate though." Nine cargo workspaces become three, and the port's project code -- which sat in five places, four of them inside the framework -- becomes one crate, `ai-app`, in `app-rust/`: client-core -> app-rust/src/client iris/transcript-ui -> app-rust/src/ui iris/transcript-fixture -> app-rust/src/ui/fixture.rs + tests/ + touch/ iris/desktop-app -> app-rust/src/desktop + src/bin_desktop.rs iris/android-app -> app-rust/src/android + android-project/ android-shell -> app-rust/src/shell iris/ keeps core, macro, the iris crate, tabs-ui and rig-input, and now mentions no session, transcript, setup or server anywhere. Only two of the old splits had a reason that survived reading. event-model stays a crate at the repo root because server/ depends on it too, so a crate is what makes the backend and the app agree by construction. The two Android .so names looked like a hard constraint -- a package produces one library artifact -- until P2 turned out to already plan merging those two Android apps into one; both faces now come out of libai_app.so, picked apart by features so `--no-default-features --features shell` keeps wgpu, parley and iris out of the Compose app's APK. docs/RUST.md's "One app crate" has the rest, including what each remaining feature is for. DECISIONS.md and SUBAGENTS.md move into docs/ with everything else. Verified: ./run-tests.sh and `cd iris && cargo test` green, clippy and fmt clean in all five workspaces, `cargo ndk -t x86_64` links libai_app.so, build-apk.sh produces an APK that installs and launches on this checkout's emulator (Gl ... virgl, as expected), and the phone-sized headless screenshot renders the transcript unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7b54aaf3c4
commit
a9312e9431
113 files changed
+23221
-2992
No files matched your search
@@ -0,0 +1,86 @@
|
||||
use crate::platform::OpenUrl;
|
||||
use android_view::{
|
||||
View,
|
||||
jni::{JNIEnv, objects::JValue},
|
||||
};
|
||||
|
||||
use super::view::HasAndroidUiState;
|
||||
|
||||
/// Android's URL opener. Like `FocusHost::focus_gained`'s keyboard, the
|
||||
/// real work is a JNI call and this runs deep inside the sensor dispatch
|
||||
/// with no `CallbackCtx` in reach -- so it raises a flag that
|
||||
/// `IrisViewPeer::after_input` consumes, exactly as
|
||||
/// `pending_show_keyboard` does.
|
||||
///
|
||||
/// Last request wins: two links cannot be tapped in one frame, and a URL
|
||||
/// left queued from a frame that somehow never reached `after_input`
|
||||
/// would open at some unrelated later tap, which is worse than dropping
|
||||
/// it.
|
||||
impl<T: HasAndroidUiState> OpenUrl for T {
|
||||
fn open_url(&mut self, url: &str) {
|
||||
self.android_state_mut().pending_open_url = Some(url.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
/// `startActivity(new Intent(ACTION_VIEW, Uri.parse(url)))` on the view's
|
||||
/// own context.
|
||||
///
|
||||
/// `FLAG_ACTIVITY_NEW_TASK` because the context here is the view's, which
|
||||
/// may be an application context rather than the activity's -- Android
|
||||
/// throws `AndroidRuntimeException` for a non-activity context without it,
|
||||
/// and it is harmless when the context *is* an activity's.
|
||||
///
|
||||
/// Every failure is logged with the URL and returns; there is nothing to
|
||||
/// fall back to, and the reader will see that nothing happened.
|
||||
pub(super) fn open_url<'local>(env: &mut JNIEnv<'local>, view: &View<'local>, url: &str) {
|
||||
match try_open_url(env, view, url) {
|
||||
Ok(()) => {}
|
||||
Err(e) => {
|
||||
// A pending Java exception makes every later JNI call fail in
|
||||
// ways nowhere near here, so it is cleared at the boundary.
|
||||
let _ = env.exception_clear();
|
||||
log::warn!("could not open {url}: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_open_url<'local>(
|
||||
env: &mut JNIEnv<'local>,
|
||||
view: &View<'local>,
|
||||
url: &str,
|
||||
) -> Result<(), android_view::jni::errors::Error> {
|
||||
let context = env
|
||||
.call_method(&view.0, "getContext", "()Landroid/content/Context;", &[])?
|
||||
.l()?;
|
||||
let jurl = env.new_string(url)?;
|
||||
let uri = env.call_static_method(
|
||||
"android/net/Uri",
|
||||
"parse",
|
||||
"(Ljava/lang/String;)Landroid/net/Uri;",
|
||||
&[JValue::Object(jurl.as_ref())],
|
||||
)?;
|
||||
let action = env.new_string("android.intent.action.VIEW")?;
|
||||
let intent = env.new_object(
|
||||
"android/content/Intent",
|
||||
"(Ljava/lang/String;Landroid/net/Uri;)V",
|
||||
&[JValue::Object(action.as_ref()), JValue::Object(&uri.l()?)],
|
||||
)?;
|
||||
env.call_method(
|
||||
&intent,
|
||||
"addFlags",
|
||||
"(I)Landroid/content/Intent;",
|
||||
&[JValue::Int(FLAG_ACTIVITY_NEW_TASK)],
|
||||
)?;
|
||||
env.call_method(
|
||||
&context,
|
||||
"startActivity",
|
||||
"(Landroid/content/Intent;)V",
|
||||
&[JValue::Object(&intent)],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// `android.content.Intent.FLAG_ACTIVITY_NEW_TASK`. A constant rather than
|
||||
/// a static-field read: it is part of the platform's stable ABI and
|
||||
/// reading it costs two more JNI calls that can each fail.
|
||||
const FLAG_ACTIVITY_NEW_TASK: i32 = 0x1000_0000;
|
||||
Reference in new issue
Block a user