diff --git a/adapters/android/src/adapter.rs b/adapters/android/src/adapter.rs index 34caf7b72..ea90f068e 100644 --- a/adapters/android/src/adapter.rs +++ b/adapters/android/src/adapter.rs @@ -168,13 +168,8 @@ impl TreeChangeHandler for AdapterChangeHandler<'_> { y: scroll_y, }); } - if old_node.numeric_value() != new_node.numeric_value() && new_node.data().value().is_none() - { - if let (Some(current), Some(min), Some(max)) = ( - new_node.numeric_value(), - new_node.min_numeric_value(), - new_node.max_numeric_value(), - ) { + if let Some(range) = new_wrapper.range() { + if old_node.numeric_value() != Some(range.current) { let id = self.node_id_map.get_or_create_java_id(new_node); let event_type = if self.accessibility_focus == Some(id) { EVENT_VIEW_SELECTED @@ -184,9 +179,7 @@ impl TreeChangeHandler for AdapterChangeHandler<'_> { self.events.push(QueuedEvent::RangeValueChanged { virtual_view_id: id, event_type, - current, - min, - max, + range, }); } } diff --git a/adapters/android/src/event.rs b/adapters/android/src/event.rs index 933409339..4c771c74c 100644 --- a/adapters/android/src/event.rs +++ b/adapters/android/src/event.rs @@ -243,20 +243,18 @@ fn send_range_value_changed( host: &JObject, virtual_view_id: jint, event_type: jint, - current: f64, - min: f64, - max: f64, + range: Range, ) { let event = new_event(env, host, virtual_view_id, event_type); - let item_index = if max > min && current >= min && current <= max { - ((current - min) * 100.0 / (max - min)) as jint - } else { - 0 - }; env.call_method(&event, "setItemCount", "(I)V", &[100i32.into()]) .unwrap(); - env.call_method(&event, "setCurrentItemIndex", "(I)V", &[item_index.into()]) - .unwrap(); + env.call_method( + &event, + "setCurrentItemIndex", + "(I)V", + &[range.current_item_index().into()], + ) + .unwrap(); send_completed_event(env, host, event); } @@ -327,9 +325,7 @@ pub(crate) enum QueuedEvent { RangeValueChanged { virtual_view_id: jint, event_type: jint, - current: f64, - min: f64, - max: f64, + range: Range, }, InvalidateHost, } @@ -407,19 +403,9 @@ impl QueuedEvents { QueuedEvent::RangeValueChanged { virtual_view_id, event_type, - current, - min, - max, + range, } => { - send_range_value_changed( - env, - host, - virtual_view_id, - event_type, - current, - min, - max, - ); + send_range_value_changed(env, host, virtual_view_id, event_type, range); } QueuedEvent::InvalidateHost => { env.call_method(host, "invalidate", "()V", &[]).unwrap(); diff --git a/adapters/android/src/node.rs b/adapters/android/src/node.rs index d2e480843..07c907fb9 100644 --- a/adapters/android/src/node.rs +++ b/adapters/android/src/node.rs @@ -70,6 +70,10 @@ impl NodeWrapper<'_> { self.0.is_selected().unwrap_or(false) } + pub(crate) fn range(&self) -> Option { + Range::from_node(self.0) + } + pub(crate) fn content_description(&self) -> Option { if self.0.label_comes_from_value() { self.0.value() @@ -387,38 +391,15 @@ impl NodeWrapper<'_> { add_action(env, node_info, ACTION_SCROLL_FORWARD); } - if self.0.data().value().is_none() { - if let (Some(current), Some(min), Some(max)) = ( - self.0.numeric_value(), - self.0.min_numeric_value(), - self.0.max_numeric_value(), - ) { - let range_info_class = env - .find_class("android/view/accessibility/AccessibilityNodeInfo$RangeInfo") - .unwrap(); - let range_info = env - .call_static_method( - &range_info_class, - "obtain", - "(IFFF)Landroid/view/accessibility/AccessibilityNodeInfo$RangeInfo;", - &[ - RANGE_TYPE_FLOAT.into(), - (min as f32).into(), - (max as f32).into(), - (current as f32).into(), - ], - ) - .unwrap() - .l() - .unwrap(); - env.call_method( - node_info, - "setRangeInfo", - "(Landroid/view/accessibility/AccessibilityNodeInfo$RangeInfo;)V", - &[(&range_info).into()], - ) - .unwrap(); - } + if let Some(range) = self.range() { + let range_info = range.to_java(env); + env.call_method( + node_info, + "setRangeInfo", + "(Landroid/view/accessibility/AccessibilityNodeInfo$RangeInfo;)V", + &[(&range_info).into()], + ) + .unwrap(); } let live = match self.0.live() { diff --git a/adapters/android/src/util.rs b/adapters/android/src/util.rs index 61b0022ec..8868e6e3d 100644 --- a/adapters/android/src/util.rs +++ b/adapters/android/src/util.rs @@ -61,8 +61,72 @@ pub(crate) const MOVEMENT_GRANULARITY_WORD: jint = 1 << 1; pub(crate) const MOVEMENT_GRANULARITY_LINE: jint = 1 << 2; pub(crate) const MOVEMENT_GRANULARITY_PARAGRAPH: jint = 1 << 3; +pub(crate) const RANGE_TYPE_INT: jint = 0; pub(crate) const RANGE_TYPE_FLOAT: jint = 1; +#[derive(Clone, Copy, Debug, PartialEq)] +pub(crate) struct Range { + pub(crate) current: f64, + pub(crate) min: f64, + pub(crate) max: f64, + step: Option, +} + +impl Range { + pub(crate) fn from_node(node: &NodeRef) -> Option { + if node.has_value() { + return None; + } + Some(Self { + current: node.numeric_value()?, + min: node.min_numeric_value()?, + max: node.max_numeric_value()?, + step: node.numeric_value_step(), + }) + } + + fn range_type(&self) -> jint { + let is_integral = |value: f64| value.fract() == 0.0; + if is_integral(self.current) + && is_integral(self.min) + && is_integral(self.max) + && self.step.is_none_or(is_integral) + { + RANGE_TYPE_INT + } else { + RANGE_TYPE_FLOAT + } + } + + pub(crate) fn current_item_index(&self) -> jint { + if self.max > self.min && self.current >= self.min && self.current <= self.max { + ((self.current - self.min) * 100.0 / (self.max - self.min)) as jint + } else { + 0 + } + } + + pub(crate) fn to_java<'local>(self, env: &mut JNIEnv<'local>) -> JObject<'local> { + let range_info_class = env + .find_class("android/view/accessibility/AccessibilityNodeInfo$RangeInfo") + .unwrap(); + env.call_static_method( + &range_info_class, + "obtain", + "(IFFF)Landroid/view/accessibility/AccessibilityNodeInfo$RangeInfo;", + &[ + self.range_type().into(), + (self.min as f32).into(), + (self.max as f32).into(), + (self.current as f32).into(), + ], + ) + .unwrap() + .l() + .unwrap() + } +} + #[derive(Debug, Default)] pub(crate) struct NodeIdMap { java_to_accesskit: HashMap,