iris: AndroidAppState::platform_ready, a JavaVM+View handle for later JNI calls

Default no-op lifecycle hook, called once from new_peer right after new.
P0's bench build needs to call BatteryManager/ClipboardManager through the
view's own Context from a background thread as well as the UI thread, and
neither a JavaVM nor a GlobalRef to the view was reachable from
AndroidAppState::new before this. Existing implementors (Client,
TranscriptClient) are unaffected.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
irisandClaude Fable 5.1 committed 2026-09-05 21:35:33 -04:00
1 parent 88631f5e8b
commit 8d23a20792
1 file changed
+18 -2
+18 -2
View File
@@ -4,7 +4,7 @@ use accesskit_android::Adapter as AccessAdapter;
use android_view::{ use android_view::{
AccessibilityNodeInfo, AccessibilityNodeProvider, Bundle, CallbackCtx, Context, AccessibilityNodeInfo, AccessibilityNodeProvider, Bundle, CallbackCtx, Context,
InputConnection, KeyEvent, MotionEvent, Rect, View, ViewPeer, InputConnection, KeyEvent, MotionEvent, Rect, View, ViewPeer,
jni::{JNIEnv, sys::jint}, jni::{JNIEnv, JavaVM, objects::GlobalRef, sys::jint},
ndk::event::{Keycode, MotionAction}, ndk::event::{Keycode, MotionAction},
}; };
// `marker::Sized` explicitly: `crate::prelude::*` below also brings in the // `marker::Sized` explicitly: `crate::prelude::*` below also brings in the
@@ -107,6 +107,19 @@ pub trait AndroidAppState: HasAndroidUiState {
fn back_pressed(&mut self, rsc: &mut AndroidRsc<Self>, render: &mut UiRenderState) -> bool { fn back_pressed(&mut self, rsc: &mut AndroidRsc<Self>, render: &mut UiRenderState) -> bool {
false false
} }
/// Called once, right after `new`, with a fresh `JavaVM` handle and a
/// global reference to this app's own `View` -- for a caller that
/// needs to call into Java itself beyond what a [`RequestRedraw`]
/// handle already covers (P0's bench build calling
/// `BatteryManager`/`ClipboardManager` through the view's `Context`,
/// docs/RUST.md). Not folded into `new` itself: most implementors need
/// nothing here, and `new`'s job is building the widget tree, not
/// holding a platform handle -- the default does nothing. `vm`/`view`
/// are independent handles from the ones `new_peer` keeps for its own
/// `RequestRedraw` (a fresh `get_java_vm`/`new_global_ref` each), so
/// storing them has no effect on that mechanism.
#[allow(unused_variables)]
fn platform_ready(&mut self, rsc: &mut AndroidRsc<Self>, vm: JavaVM, view: GlobalRef) {}
} }
/// The android-view analogue of `default::DefaultRsc` -- identical in /// The android-view analogue of `default::DefaultRsc` -- identical in
@@ -561,7 +574,10 @@ pub fn new_peer<'local, State: AndroidAppState>(
}; };
let shared = Rc::new(RefCell::new(Shared::default())); let shared = Rc::new(RefCell::new(Shared::default()));
let ui_state = AndroidUiState::new(shared.clone()); let ui_state = AndroidUiState::new(shared.clone());
let state = State::new(ui_state, &mut rsc); let mut state = State::new(ui_state, &mut rsc);
let platform_vm = env.get_java_vm().unwrap();
let platform_view = env.new_global_ref(&view.0).unwrap();
state.platform_ready(&mut rsc, platform_vm, platform_view);
let peer = IrisViewPeer { let peer = IrisViewPeer {
rsc, rsc,
render: UiRenderState::new(), render: UiRenderState::new(),