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