The problem: the Android adapter puts a node's live region on the node info, but nothing in the event path consults live(). It is read in exactly one place:
|
let live = match self.0.live() { |
|
Live::Off => LIVE_REGION_NONE, |
|
Live::Polite => LIVE_REGION_POLITE, |
|
Live::Assertive => LIVE_REGION_ASSERTIVE, |
|
}; |
|
env.call_method(node_info, "setLiveRegion", "(I)V", &[live.into()]) |
|
.unwrap(); |
What gets emitted instead. node_added enqueues only a host-level content-changed event; the gap is already marked (// TODO: live regions?):
|
impl TreeChangeHandler for AdapterChangeHandler<'_> { |
|
fn node_added(&mut self, _node: &NodeRef) { |
|
self.enqueue_window_content_changed_if_needed(); |
|
// TODO: live regions? |
|
} |
That event is sourced at HOST_VIEW_ID (-1), never at the changed node, and send_window_content_changed stamps it CONTENT_CHANGE_TYPE_SUBTREE, so setLiveRegion never rides on an event a screen reader could key off:
|
fn enqueue_window_content_changed(events: &mut Vec<QueuedEvent>) { |
|
events.push(QueuedEvent::WindowContentChanged { |
|
virtual_view_id: HOST_VIEW_ID, |
|
}); |
|
} |
The one per-node event is the wrong one. TYPE_VIEW_TEXT_CHANGED is pushed from a single site, in node_updated, gated on text():
|
let old_text = old_wrapper.text(); |
|
let new_text = new_wrapper.text(); |
|
if old_text != new_text { |
|
let id = self.node_id_map.get_or_create_java_id(new_node); |
|
self.events.push(QueuedEvent::TextChanged { |
|
virtual_view_id: id, |
|
old: old_text.unwrap_or_else(String::new), |
|
new: new_text.clone().unwrap_or_else(String::new), |
|
}); |
|
} |
and text() is value() or the text-range document, so labels never reach it:
|
pub(crate) fn text(&self) -> Option<String> { |
|
self.0.value().or_else(|| { |
|
self.0 |
|
.supports_text_ranges() |
|
.then(|| self.0.document_range().text()) |
|
}) |
|
} |
It is also an editing event (before-text, added/removed counts), not an announcement, and node_added cannot reach it. TYPE_ANNOUNCEMENT appears nowhere under adapters/android/.
Expected: a Live::Polite/Live::Assertive node whose label or value changes, or that is added already carrying it, produces something TalkBack speaks.
Actual: only the host-sourced TYPE_WINDOW_CONTENT_CHANGED/SUBTREE; neither a label nor a value change produces any node-sourced event. Worse than iOS, which does announce value changes (adapters/ios/src/event.rs gates announcements on value() — #765 is the label gap there).
Triggering sequence: the announcement node in winit's simple.rs — Role::Label + set_value + set_live(Live::Polite), pushed as a new child, so it lands in node_added.
Tested/untested: every Android claim is from the code at 2dfdd7b, not from a run — I have no Android device, and the repo ships no Android harness (no android_main, no gradle project or manifest). The iOS side of the comparison I have exercised on a physical iPhone. Filing as a report.
(Drafted with AI assistance; the code reading is mine.)
The problem: the Android adapter puts a node's live region on the node info, but nothing in the event path consults
live(). It is read in exactly one place:accesskit/adapters/android/src/node.rs
Lines 417 to 423 in 2dfdd7b
What gets emitted instead.
node_addedenqueues only a host-level content-changed event; the gap is already marked (// TODO: live regions?):accesskit/adapters/android/src/adapter.rs
Lines 83 to 87 in 2dfdd7b
That event is sourced at
HOST_VIEW_ID(-1), never at the changed node, andsend_window_content_changedstamps itCONTENT_CHANGE_TYPE_SUBTREE, sosetLiveRegionnever rides on an event a screen reader could key off:accesskit/adapters/android/src/adapter.rs
Lines 30 to 34 in 2dfdd7b
The one per-node event is the wrong one.
TYPE_VIEW_TEXT_CHANGEDis pushed from a single site, innode_updated, gated ontext():accesskit/adapters/android/src/adapter.rs
Lines 96 to 105 in 2dfdd7b
and
text()isvalue()or the text-range document, so labels never reach it:accesskit/adapters/android/src/node.rs
Lines 85 to 91 in 2dfdd7b
It is also an editing event (before-text, added/removed counts), not an announcement, and
node_addedcannot reach it.TYPE_ANNOUNCEMENTappears nowhere underadapters/android/.Expected: a
Live::Polite/Live::Assertivenode whose label or value changes, or that is added already carrying it, produces something TalkBack speaks.Actual: only the host-sourced
TYPE_WINDOW_CONTENT_CHANGED/SUBTREE; neither a label nor a value change produces any node-sourced event. Worse than iOS, which does announce value changes (adapters/ios/src/event.rsgates announcements onvalue()— #765 is the label gap there).Triggering sequence: the announcement node in winit's
simple.rs—Role::Label+set_value+set_live(Live::Polite), pushed as a new child, so it lands innode_added.Tested/untested: every Android claim is from the code at
2dfdd7b, not from a run — I have no Android device, and the repo ships no Android harness (noandroid_main, no gradle project or manifest). The iOS side of the comparison I have exercised on a physical iPhone. Filing as a report.(Drafted with AI assistance; the code reading is mine.)