1use accesskit::{
12 Action, ActionData, ActionHandler, ActionRequest, ActivationHandler, Node as NodeData,
13 NodeId as LocalNodeId, Orientation, Point, Role, ScrollUnit, TextSelection, Tree as TreeData,
14 TreeId, TreeUpdate,
15};
16use accesskit_consumer::{FilterResult, Node, TextPosition, Tree, TreeChangeHandler};
17use jni::{
18 objects::JObject,
19 sys::{jfloat, jint},
20 JNIEnv,
21};
22
23use crate::{
24 action::{PlatformAction, PlatformActionInner},
25 event::{QueuedEvent, QueuedEvents, ScrollDimension},
26 filters::filter,
27 node::{add_action, NodeWrapper},
28 util::*,
29};
30
31fn enqueue_window_content_changed(events: &mut Vec<QueuedEvent>) {
32 events.push(QueuedEvent::WindowContentChanged {
33 virtual_view_id: HOST_VIEW_ID,
34 });
35}
36
37fn enqueue_focus_event_if_applicable(
38 events: &mut Vec<QueuedEvent>,
39 node_id_map: &mut NodeIdMap,
40 node: &Node,
41) {
42 if node.is_root() && node.role() == Role::Window {
43 return;
44 }
45 let id = node_id_map.get_or_create_java_id(node);
46 events.push(QueuedEvent::Simple {
47 virtual_view_id: id,
48 event_type: EVENT_VIEW_FOCUSED,
49 });
50}
51
52struct AdapterChangeHandler<'a> {
53 events: &'a mut Vec<QueuedEvent>,
54 node_id_map: &'a mut NodeIdMap,
55 enqueued_window_content_changed: bool,
56}
57
58impl<'a> AdapterChangeHandler<'a> {
59 fn new(events: &'a mut Vec<QueuedEvent>, node_id_map: &'a mut NodeIdMap) -> Self {
60 Self {
61 events,
62 node_id_map,
63 enqueued_window_content_changed: false,
64 }
65 }
66}
67
68impl AdapterChangeHandler<'_> {
69 fn enqueue_window_content_changed_if_needed(&mut self) {
70 if self.enqueued_window_content_changed {
71 return;
72 }
73 enqueue_window_content_changed(self.events);
74 self.enqueued_window_content_changed = true;
75 }
76}
77
78impl TreeChangeHandler for AdapterChangeHandler<'_> {
79 fn node_added(&mut self, _node: &Node) {
80 self.enqueue_window_content_changed_if_needed();
81 }
83
84 fn node_updated(&mut self, old_node: &Node, new_node: &Node) {
85 self.enqueue_window_content_changed_if_needed();
86 if filter(new_node) != FilterResult::Include {
87 return;
88 }
89 let old_wrapper = NodeWrapper(old_node);
90 let new_wrapper = NodeWrapper(new_node);
91 let old_text = old_wrapper.text();
92 let new_text = new_wrapper.text();
93 if old_text != new_text {
94 let id = self.node_id_map.get_or_create_java_id(new_node);
95 self.events.push(QueuedEvent::TextChanged {
96 virtual_view_id: id,
97 old: old_text.unwrap_or_else(String::new),
98 new: new_text.clone().unwrap_or_else(String::new),
99 });
100 }
101 if old_node.raw_text_selection() != new_node.raw_text_selection()
102 || (new_node.raw_text_selection().is_some()
103 && old_node.is_focused() != new_node.is_focused())
104 {
105 if let Some((start, end)) = new_wrapper.text_selection() {
106 if let Some(text) = new_text {
107 let id = self.node_id_map.get_or_create_java_id(new_node);
108 self.events.push(QueuedEvent::TextSelectionChanged {
109 virtual_view_id: id,
110 text,
111 start: start as jint,
112 end: end as jint,
113 });
114 }
115 }
116 }
117 let scroll_x = old_wrapper
118 .scroll_x()
119 .zip(new_wrapper.scroll_x())
120 .and_then(|(old, new)| {
121 (new != old).then(|| ScrollDimension {
122 current: new,
123 delta: new - old,
124 max: new_wrapper.max_scroll_x(),
125 })
126 });
127 let scroll_y = old_wrapper
128 .scroll_y()
129 .zip(new_wrapper.scroll_y())
130 .and_then(|(old, new)| {
131 (new != old).then(|| ScrollDimension {
132 current: new,
133 delta: new - old,
134 max: new_wrapper.max_scroll_y(),
135 })
136 });
137 if scroll_x.is_some() || scroll_y.is_some() {
138 let id = self.node_id_map.get_or_create_java_id(new_node);
139 self.events.push(QueuedEvent::Scrolled {
140 virtual_view_id: id,
141 x: scroll_x,
142 y: scroll_y,
143 });
144 }
145 }
147
148 fn focus_moved(&mut self, _old_node: Option<&Node>, new_node: Option<&Node>) {
149 if let Some(new_node) = new_node {
150 enqueue_focus_event_if_applicable(self.events, self.node_id_map, new_node);
151 }
152 }
153
154 fn node_removed(&mut self, _node: &Node) {
155 self.enqueue_window_content_changed_if_needed();
156 }
158}
159
160const PLACEHOLDER_ROOT_ID: LocalNodeId = LocalNodeId(0);
161
162#[derive(Debug, Default)]
163enum State {
164 #[default]
165 Inactive,
166 Placeholder(Tree),
167 Active(Tree),
168}
169
170impl State {
171 fn get_or_init_tree<H: ActivationHandler + ?Sized>(
172 &mut self,
173 activation_handler: &mut H,
174 ) -> &Tree {
175 match self {
176 Self::Inactive => {
177 *self = match activation_handler.request_initial_tree() {
178 Some(initial_state) => Self::Active(Tree::new(initial_state, true)),
179 None => {
180 let placeholder_update = TreeUpdate {
181 nodes: vec![(PLACEHOLDER_ROOT_ID, NodeData::new(Role::Window))],
182 tree: Some(TreeData::new(PLACEHOLDER_ROOT_ID)),
183 tree_id: TreeId::ROOT,
184 focus: PLACEHOLDER_ROOT_ID,
185 };
186 Self::Placeholder(Tree::new(placeholder_update, true))
187 }
188 };
189 self.get_or_init_tree(activation_handler)
190 }
191 Self::Placeholder(tree) => tree,
192 Self::Active(tree) => tree,
193 }
194 }
195
196 fn get_full_tree(&mut self) -> Option<&mut Tree> {
197 match self {
198 Self::Inactive => None,
199 Self::Placeholder(_) => None,
200 Self::Active(tree) => Some(tree),
201 }
202 }
203}
204
205fn update_tree(
206 events: &mut Vec<QueuedEvent>,
207 node_id_map: &mut NodeIdMap,
208 tree: &mut Tree,
209 update: TreeUpdate,
210) {
211 let mut handler = AdapterChangeHandler::new(events, node_id_map);
212 tree.update_and_process_changes(update, &mut handler);
213}
214
215#[derive(Debug, Default)]
225pub struct Adapter {
226 node_id_map: NodeIdMap,
227 state: State,
228 accessibility_focus: Option<jint>,
229 hover_target: Option<jint>,
230}
231
232impl Adapter {
233 pub fn update_if_active(
246 &mut self,
247 update_factory: impl FnOnce() -> TreeUpdate,
248 ) -> Option<QueuedEvents> {
249 match &mut self.state {
250 State::Inactive => None,
251 State::Placeholder(_) => {
252 let tree = Tree::new(update_factory(), true);
253 let mut events = Vec::new();
254 enqueue_window_content_changed(&mut events);
255 let state = tree.state();
256 if let Some(focus) = state.focus() {
257 enqueue_focus_event_if_applicable(&mut events, &mut self.node_id_map, &focus);
258 }
259 self.state = State::Active(tree);
260 Some(QueuedEvents(events))
261 }
262 State::Active(tree) => {
263 let mut events = Vec::new();
264 update_tree(&mut events, &mut self.node_id_map, tree, update_factory());
265 Some(QueuedEvents(events))
266 }
267 }
268 }
269
270 pub fn create_accessibility_node_info<'local, H: ActivationHandler + ?Sized>(
277 &mut self,
278 activation_handler: &mut H,
279 env: &mut JNIEnv<'local>,
280 host: &JObject,
281 virtual_view_id: jint,
282 ) -> JObject<'local> {
283 let tree = self.state.get_or_init_tree(activation_handler);
284 let tree_state = tree.state();
285 let node = if virtual_view_id == HOST_VIEW_ID {
286 tree_state.root()
287 } else {
288 let Some(accesskit_id) = self.node_id_map.get_accesskit_id(virtual_view_id) else {
289 return JObject::null();
290 };
291 let Some(node) = tree_state.node_by_id(accesskit_id) else {
292 return JObject::null();
293 };
294 node
295 };
296
297 let node_info_class = env
298 .find_class("android/view/accessibility/AccessibilityNodeInfo")
299 .unwrap();
300 let node_info = env
301 .call_static_method(
302 &node_info_class,
303 "obtain",
304 "(Landroid/view/View;I)Landroid/view/accessibility/AccessibilityNodeInfo;",
305 &[host.into(), virtual_view_id.into()],
306 )
307 .unwrap()
308 .l()
309 .unwrap();
310
311 let package_name = get_package_name(env, host);
312 env.call_method(
313 &node_info,
314 "setPackageName",
315 "(Ljava/lang/CharSequence;)V",
316 &[(&package_name).into()],
317 )
318 .unwrap();
319
320 let wrapper = NodeWrapper(&node);
321 wrapper.populate_node_info(env, host, &mut self.node_id_map, &node_info);
322
323 let is_accessibility_focus = self.accessibility_focus == Some(virtual_view_id);
324 env.call_method(
325 &node_info,
326 "setAccessibilityFocused",
327 "(Z)V",
328 &[is_accessibility_focus.into()],
329 )
330 .unwrap();
331 add_action(
332 env,
333 &node_info,
334 if is_accessibility_focus {
335 ACTION_CLEAR_ACCESSIBILITY_FOCUS
336 } else {
337 ACTION_ACCESSIBILITY_FOCUS
338 },
339 );
340
341 node_info
342 }
343
344 pub fn find_focus<'local, H: ActivationHandler + ?Sized>(
350 &mut self,
351 activation_handler: &mut H,
352 env: &mut JNIEnv<'local>,
353 host: &JObject,
354 focus_type: jint,
355 ) -> JObject<'local> {
356 let virtual_view_id = match focus_type {
357 FOCUS_INPUT => {
358 let tree = self.state.get_or_init_tree(activation_handler);
359 let tree_state = tree.state();
360 let node = tree_state.focus_in_tree();
361 self.node_id_map.get_or_create_java_id(&node)
362 }
363 FOCUS_ACCESSIBILITY => {
364 let Some(id) = self.accessibility_focus else {
365 return JObject::null();
366 };
367 id
368 }
369 _ => return JObject::null(),
370 };
371 self.create_accessibility_node_info(activation_handler, env, host, virtual_view_id)
372 }
373
374 fn perform_simple_action<H: ActionHandler + ?Sized>(
375 &mut self,
376 action_handler: &mut H,
377 virtual_view_id: jint,
378 action: jint,
379 ) -> Option<QueuedEvents> {
380 let tree = self.state.get_full_tree()?;
381 let tree_state = tree.state();
382 let target = if virtual_view_id == HOST_VIEW_ID {
383 tree_state.root_id()
384 } else {
385 self.node_id_map.get_accesskit_id(virtual_view_id)?
386 };
387 let (target_node, target_tree) = tree.locate_node(target)?;
388 let mut events = Vec::new();
389 let request = match action {
390 ACTION_CLICK => ActionRequest {
391 action: {
392 let node = tree_state.node_by_id(target).unwrap();
393 if node.is_focusable(&filter)
394 && !node.is_focused()
395 && !node.is_clickable(&filter)
396 {
397 Action::Focus
398 } else {
399 Action::Click
400 }
401 },
402 target_tree,
403 target_node,
404 data: None,
405 },
406 ACTION_FOCUS => ActionRequest {
407 action: Action::Focus,
408 target_tree,
409 target_node,
410 data: None,
411 },
412 ACTION_SCROLL_BACKWARD | ACTION_SCROLL_FORWARD => ActionRequest {
413 action: {
414 let node = tree_state.node_by_id(target).unwrap();
415 if let Some(orientation) = node.orientation() {
416 match orientation {
417 Orientation::Horizontal => {
418 if action == ACTION_SCROLL_BACKWARD {
419 Action::ScrollLeft
420 } else {
421 Action::ScrollRight
422 }
423 }
424 Orientation::Vertical => {
425 if action == ACTION_SCROLL_BACKWARD {
426 Action::ScrollUp
427 } else {
428 Action::ScrollDown
429 }
430 }
431 }
432 } else if action == ACTION_SCROLL_BACKWARD {
433 if node.supports_action(Action::ScrollUp, &filter) {
434 Action::ScrollUp
435 } else {
436 Action::ScrollLeft
437 }
438 } else if node.supports_action(Action::ScrollDown, &filter) {
439 Action::ScrollDown
440 } else {
441 Action::ScrollRight
442 }
443 },
444 target_tree,
445 target_node,
446 data: Some(ActionData::ScrollUnit(ScrollUnit::Page)),
447 },
448 ACTION_ACCESSIBILITY_FOCUS => {
449 self.accessibility_focus = Some(virtual_view_id);
450 events.push(QueuedEvent::InvalidateHost);
451 events.push(QueuedEvent::Simple {
452 virtual_view_id,
453 event_type: EVENT_VIEW_ACCESSIBILITY_FOCUSED,
454 });
455 return Some(QueuedEvents(events));
456 }
457 ACTION_CLEAR_ACCESSIBILITY_FOCUS => {
458 if self.accessibility_focus == Some(virtual_view_id) {
459 self.accessibility_focus = None;
460 }
461 events.push(QueuedEvent::InvalidateHost);
462 events.push(QueuedEvent::Simple {
463 virtual_view_id,
464 event_type: EVENT_VIEW_ACCESSIBILITY_FOCUS_CLEARED,
465 });
466 return Some(QueuedEvents(events));
467 }
468 _ => {
469 return None;
470 }
471 };
472 action_handler.do_action(request);
473 if action == ACTION_CLICK {
474 events.push(QueuedEvent::Simple {
475 virtual_view_id,
476 event_type: EVENT_VIEW_CLICKED,
477 });
478 }
479 Some(QueuedEvents(events))
480 }
481
482 fn set_text_selection_common<H: ActionHandler + ?Sized, F, Extra>(
483 &mut self,
484 action_handler: &mut H,
485 events: &mut Vec<QueuedEvent>,
486 virtual_view_id: jint,
487 selection_factory: F,
488 ) -> Option<Extra>
489 where
490 for<'a> F: FnOnce(&'a Node<'a>) -> Option<(TextPosition<'a>, TextPosition<'a>, Extra)>,
491 {
492 let tree = self.state.get_full_tree()?;
493 let tree_state = tree.state();
494 let node = if virtual_view_id == HOST_VIEW_ID {
495 tree_state.root()
496 } else {
497 let id = self.node_id_map.get_accesskit_id(virtual_view_id)?;
498 tree_state.node_by_id(id).unwrap()
499 };
500 let (node_id, tree_id) = tree.locate_node(node.id())?;
501 let (focus_id, _) = tree.locate_node(tree_state.focus_id_in_tree())?;
502 if !node.supports_action(Action::SetTextSelection, &filter) {
506 return None;
507 }
508 let (anchor, focus, extra) = selection_factory(&node)?;
509 let selection = TextSelection {
510 anchor: anchor.to_raw(),
511 focus: focus.to_raw(),
512 };
513 let mut new_node = node.data().clone();
514 new_node.set_text_selection(selection);
515 let update = TreeUpdate {
516 nodes: vec![(node_id, new_node)],
517 tree: None,
518 tree_id,
519 focus: focus_id,
520 };
521 update_tree(events, &mut self.node_id_map, tree, update);
522 let request = ActionRequest {
523 action: Action::SetTextSelection,
524 target_tree: tree_id,
525 target_node: node_id,
526 data: Some(ActionData::SetTextSelection(selection)),
527 };
528 action_handler.do_action(request);
529 Some(extra)
530 }
531
532 fn set_text_selection<H: ActionHandler + ?Sized>(
533 &mut self,
534 action_handler: &mut H,
535 virtual_view_id: jint,
536 anchor: jint,
537 focus: jint,
538 ) -> Option<QueuedEvents> {
539 let mut events = Vec::new();
540 self.set_text_selection_common(action_handler, &mut events, virtual_view_id, |node| {
541 let anchor = usize::try_from(anchor).ok()?;
542 let anchor = node.text_position_from_global_utf16_index(anchor)?;
543 let focus = usize::try_from(focus).ok()?;
544 let focus = node.text_position_from_global_utf16_index(focus)?;
545 Some((anchor, focus, ()))
546 })?;
547 Some(QueuedEvents(events))
548 }
549
550 fn collapse_text_selection<H: ActionHandler + ?Sized>(
551 &mut self,
552 action_handler: &mut H,
553 virtual_view_id: jint,
554 ) -> Option<QueuedEvents> {
555 let mut events = Vec::new();
556 self.set_text_selection_common(action_handler, &mut events, virtual_view_id, |node| {
557 node.text_selection_focus().map(|pos| (pos, pos, ()))
558 })?;
559 Some(QueuedEvents(events))
560 }
561
562 fn traverse_text<H: ActionHandler + ?Sized>(
563 &mut self,
564 action_handler: &mut H,
565 virtual_view_id: jint,
566 granularity: jint,
567 forward: bool,
568 extend_selection: bool,
569 ) -> Option<QueuedEvents> {
570 let mut events = Vec::new();
571 let (segment_start, segment_end) =
572 self.set_text_selection_common(action_handler, &mut events, virtual_view_id, |node| {
573 let current = node.text_selection_focus().unwrap_or_else(|| {
574 let range = node.document_range();
575 if forward {
576 range.start()
577 } else {
578 range.end()
579 }
580 });
581 if (forward && current.is_document_end())
582 || (!forward && current.is_document_start())
583 {
584 return None;
585 }
586 let current = if forward {
587 current.biased_to_start()
588 } else {
589 current.biased_to_end()
590 };
591 let (segment_start, segment_end) = match granularity {
592 MOVEMENT_GRANULARITY_CHARACTER => {
593 if forward {
594 (current, current.forward_to_character_end())
595 } else {
596 (current.backward_to_character_start(), current)
597 }
598 }
599 MOVEMENT_GRANULARITY_WORD => {
600 if forward {
601 let start = if current.is_word_start() {
602 current
603 } else {
604 let start = current.forward_to_word_start();
605 if start.is_document_end() {
606 return None;
607 }
608 start
609 };
610 (start, start.forward_to_word_end())
611 } else {
612 let end = if current.is_line_end() || current.is_word_start() {
613 current
614 } else {
615 let end = current.backward_to_word_start().biased_to_end();
616 if end.is_document_start() {
617 return None;
618 }
619 end
620 };
621 (end.backward_to_word_start(), end)
622 }
623 }
624 MOVEMENT_GRANULARITY_LINE => {
625 if forward {
626 let start = if current.is_line_start() {
627 current
628 } else {
629 let start = current.forward_to_line_start();
630 if start.is_document_end() {
631 return None;
632 }
633 start
634 };
635 (start, start.forward_to_line_end())
636 } else {
637 let end = if current.is_line_end() {
638 current
639 } else {
640 let end = current.backward_to_line_start().biased_to_end();
641 if end.is_document_start() {
642 return None;
643 }
644 end
645 };
646 (end.backward_to_line_start(), end)
647 }
648 }
649 MOVEMENT_GRANULARITY_PARAGRAPH => {
650 if forward {
651 let mut start = current;
652 while start.is_paragraph_separator() {
653 start = start.forward_to_paragraph_start();
654 }
655 if start.is_document_end() {
656 return None;
657 }
658 let mut end = start.forward_to_paragraph_end();
659 let prev = end.backward_to_character_start();
660 if prev.is_paragraph_separator() {
661 end = prev;
662 }
663 (start, end)
664 } else {
665 let mut end = current;
666 while !end.is_document_start()
667 && end.backward_to_character_start().is_paragraph_separator()
668 {
669 end = end.backward_to_character_start();
670 }
671 if end.is_document_start() {
672 return None;
673 }
674 (end.backward_to_paragraph_start(), end)
675 }
676 }
677 _ => {
678 return None;
679 }
680 };
681 if segment_start == segment_end {
682 return None;
683 }
684 let focus = if forward { segment_end } else { segment_start };
685 let anchor = if extend_selection {
686 node.text_selection_anchor().unwrap_or({
687 if forward {
688 segment_start
689 } else {
690 segment_end
691 }
692 })
693 } else {
694 focus
695 };
696 Some((
697 anchor,
698 focus,
699 (
700 segment_start.to_global_utf16_index(),
701 segment_end.to_global_utf16_index(),
702 ),
703 ))
704 })?;
705 events.push(QueuedEvent::TextTraversed {
706 virtual_view_id,
707 granularity,
708 forward,
709 segment_start: segment_start as jint,
710 segment_end: segment_end as jint,
711 });
712 Some(QueuedEvents(events))
713 }
714
715 pub fn perform_action<H: ActionHandler + ?Sized>(
726 &mut self,
727 action_handler: &mut H,
728 virtual_view_id: jint,
729 action: &PlatformAction,
730 ) -> Option<QueuedEvents> {
731 match action.0 {
732 PlatformActionInner::Simple { action } => {
733 self.perform_simple_action(action_handler, virtual_view_id, action)
734 }
735 PlatformActionInner::SetTextSelection { anchor, focus } => {
736 self.set_text_selection(action_handler, virtual_view_id, anchor, focus)
737 }
738 PlatformActionInner::CollapseTextSelection => {
739 self.collapse_text_selection(action_handler, virtual_view_id)
740 }
741 PlatformActionInner::TraverseText {
742 granularity,
743 forward,
744 extend_selection,
745 } => self.traverse_text(
746 action_handler,
747 virtual_view_id,
748 granularity,
749 forward,
750 extend_selection,
751 ),
752 }
753 }
754
755 fn virtual_view_at_point<H: ActivationHandler + ?Sized>(
756 &mut self,
757 activation_handler: &mut H,
758 x: jfloat,
759 y: jfloat,
760 ) -> Option<jint> {
761 let tree = self.state.get_or_init_tree(activation_handler);
762 let tree_state = tree.state();
763 let root = tree_state.root();
764 let point = Point::new(x.into(), y.into());
765 let point = root.transform().inverse() * point;
766 let node = root.node_at_point(point, &filter)?;
767 Some(self.node_id_map.get_or_create_java_id(&node))
768 }
769
770 pub fn on_hover_event<H: ActivationHandler + ?Sized>(
786 &mut self,
787 activation_handler: &mut H,
788 action: jint,
789 x: jfloat,
790 y: jfloat,
791 ) -> Option<QueuedEvents> {
792 let mut events = Vec::new();
793 match action {
794 MOTION_ACTION_HOVER_ENTER | MOTION_ACTION_HOVER_MOVE => {
795 let new_id = self.virtual_view_at_point(activation_handler, x, y);
796 if self.hover_target != new_id {
797 if let Some(virtual_view_id) = new_id {
798 events.push(QueuedEvent::Simple {
799 virtual_view_id,
800 event_type: EVENT_VIEW_HOVER_ENTER,
801 });
802 }
803 if let Some(virtual_view_id) = self.hover_target {
804 events.push(QueuedEvent::Simple {
805 virtual_view_id,
806 event_type: EVENT_VIEW_HOVER_EXIT,
807 });
808 }
809 self.hover_target = new_id;
810 }
811 }
812 MOTION_ACTION_HOVER_EXIT => {
813 if let Some(virtual_view_id) = self.hover_target.take() {
814 events.push(QueuedEvent::Simple {
815 virtual_view_id,
816 event_type: EVENT_VIEW_HOVER_EXIT,
817 });
818 }
819 }
820 _ => return None,
821 }
822 Some(QueuedEvents(events))
823 }
824}