Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion adapters/android/src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
// the LICENSE-APACHE file) or the MIT license (found in
// the LICENSE-MIT file), at your option.

use jni::{JNIEnv, objects::JObject, sys::jint};
use jni::{
JNIEnv,
objects::JObject,
sys::{jfloat, jint},
};

use crate::util::*;

Expand All @@ -21,6 +25,9 @@ pub(crate) enum PlatformActionInner {
forward: bool,
extend_selection: bool,
},
SetProgress {
value: jfloat,
},
}

pub struct PlatformAction(pub(crate) PlatformActionInner);
Expand Down Expand Up @@ -63,6 +70,15 @@ impl PlatformAction {
extend_selection,
}))
}
ACTION_SET_PROGRESS => {
if arguments.is_null()
|| !bundle_contains_key(env, arguments, ACTION_ARGUMENT_PROGRESS_VALUE)
{
return None;
}
let value = bundle_get_float(env, arguments, ACTION_ARGUMENT_PROGRESS_VALUE);
Some(Self(PlatformActionInner::SetProgress { value }))
}
_ => Some(Self(PlatformActionInner::Simple { action })),
}
}
Expand Down
43 changes: 43 additions & 0 deletions adapters/android/src/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,23 @@ impl Adapter {
target_node,
data: None,
},
ACTION_SCROLL_BACKWARD | ACTION_SCROLL_FORWARD
if {
let node = tree_state.node_by_id(target).unwrap();
node.supports_increment(&filter) || node.supports_decrement(&filter)
} =>
{
ActionRequest {
action: if action == ACTION_SCROLL_BACKWARD {
Action::Decrement
} else {
Action::Increment
},
target_tree,
target_node,
data: None,
}
}
ACTION_SCROLL_BACKWARD | ACTION_SCROLL_FORWARD => ActionRequest {
action: {
let node = tree_state.node_by_id(target).unwrap();
Expand Down Expand Up @@ -532,6 +549,29 @@ impl Adapter {
Some(QueuedEvents(events))
}

fn set_progress<H: ActionHandler + ?Sized>(
&mut self,
action_handler: &mut H,
virtual_view_id: jint,
value: jfloat,
) -> Option<QueuedEvents> {
let tree = self.state.get_full_tree()?;
let tree_state = tree.state();
let target = self.node_id_map.get_accesskit_id(virtual_view_id)?;
let (target_node, target_tree) = tree_state.locate_node(target)?;
let node = tree_state.node_by_id(target)?;
if !node.supports_action(Action::SetValue, &filter) || node.numeric_value().is_none() {
return None;
}
action_handler.do_action(ActionRequest {
action: Action::SetValue,
target_tree,
target_node,
data: Some(ActionData::NumericValue(value.into())),
});
Some(QueuedEvents(Vec::new()))
}

fn set_text_selection_common<H: ActionHandler + ?Sized, F, Extra>(
&mut self,
action_handler: &mut H,
Expand Down Expand Up @@ -802,6 +842,9 @@ impl Adapter {
forward,
extend_selection,
),
PlatformActionInner::SetProgress { value } => {
self.set_progress(action_handler, virtual_view_id, value)
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions adapters/android/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,9 @@ impl QueuedEvents {
/// any locks required by the host view's implementations of Android
/// framework callbacks.
pub fn raise(self, env: &mut JNIEnv, host: &JObject) {
if self.0.is_empty() {
return;
}
// The framework throws an exception if events are sent from the
// UI thread while accessibility is disabled.
if !is_accessibility_enabled(env, host) {
Expand Down
42 changes: 41 additions & 1 deletion adapters/android/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ impl NodeWrapper<'_> {
}
}

fn supports_set_progress(&self) -> bool {
self.range().is_some() && self.0.supports_action(Action::SetValue, &filter)
}

fn is_range_control(&self) -> bool {
self.0.supports_increment(&filter) || self.0.supports_decrement(&filter)
}

pub(crate) fn content_description(&self) -> Option<String> {
if self.0.label_comes_from_value() {
self.0.value()
Expand Down Expand Up @@ -377,7 +385,11 @@ impl NodeWrapper<'_> {
.unwrap();

let can_focus = self.is_focusable() && !self.0.is_focused();
if self.0.is_clickable(&filter) || can_focus {
// Like the framework's `SeekBar`, a range control doesn't advertise
// a click action unless it's really clickable. TalkBack then handles
// a double-tap by synthesizing a tap at the center of the control,
// which is how users expect to jump to the middle of the range.
if self.0.is_clickable(&filter) || (can_focus && !self.is_range_control()) {
add_action(env, node_info, ACTION_CLICK);
}
if can_focus {
Expand All @@ -399,13 +411,18 @@ impl NodeWrapper<'_> {
)
.unwrap();
}
// Like the framework's own `SeekBar`, a control with a numeric
// value is adjusted through the scroll actions; TalkBack's
// "adjust slider" reading control performs nothing else.
if self.0.supports_action(Action::ScrollLeft, &filter)
|| self.0.supports_action(Action::ScrollUp, &filter)
|| self.0.supports_decrement(&filter)
{
add_action(env, node_info, ACTION_SCROLL_BACKWARD);
}
if self.0.supports_action(Action::ScrollRight, &filter)
|| self.0.supports_action(Action::ScrollDown, &filter)
|| self.0.supports_increment(&filter)
{
add_action(env, node_info, ACTION_SCROLL_FORWARD);
}
Expand Down Expand Up @@ -437,6 +454,29 @@ impl NodeWrapper<'_> {
)
.unwrap();
}
if self.supports_set_progress() {
// Unlike the legacy actions, this one isn't a bitmask
// and must be added as an `AccessibilityAction` object.
let action_class = env
.find_class("android/view/accessibility/AccessibilityNodeInfo$AccessibilityAction")
.unwrap();
let action = env
.get_static_field(
&action_class,
"ACTION_SET_PROGRESS",
"Landroid/view/accessibility/AccessibilityNodeInfo$AccessibilityAction;",
)
.unwrap()
.l()
.unwrap();
env.call_method(
node_info,
"addAction",
"(Landroid/view/accessibility/AccessibilityNodeInfo$AccessibilityAction;)V",
&[(&action).into()],
)
.unwrap();
}

let live = match self.0.live() {
Live::Off => LIVE_REGION_NONE,
Expand Down
20 changes: 19 additions & 1 deletion adapters/android/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use accesskit_consumer::{FullNodeId, NodeRef};
use jni::{
JNIEnv,
objects::{GlobalRef, JObject},
sys::jint,
sys::{jfloat, jint},
};
use std::{collections::HashMap, sync::OnceLock};

Expand All @@ -20,13 +20,18 @@ pub(crate) const ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: jint = 1 << 9;
pub(crate) const ACTION_SCROLL_FORWARD: jint = 1 << 12;
pub(crate) const ACTION_SCROLL_BACKWARD: jint = 1 << 13;
pub(crate) const ACTION_SET_SELECTION: jint = 1 << 17;
// This is the value of `android.R.id.accessibilityActionSetProgress`,
// a stable public resource ID rather than a legacy bitmask action.
pub(crate) const ACTION_SET_PROGRESS: jint = 0x0102003d;

pub(crate) const ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT: &str =
"ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
pub(crate) const ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN: &str =
"ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
pub(crate) const ACTION_ARGUMENT_SELECTION_START_INT: &str = "ACTION_ARGUMENT_SELECTION_START_INT";
pub(crate) const ACTION_ARGUMENT_SELECTION_END_INT: &str = "ACTION_ARGUMENT_SELECTION_END_INT";
pub(crate) const ACTION_ARGUMENT_PROGRESS_VALUE: &str =
"android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";

pub(crate) const CONTENT_CHANGE_TYPE_SUBTREE: jint = 1 << 0;

Expand Down Expand Up @@ -113,6 +118,19 @@ pub(crate) fn bundle_get_int(env: &mut JNIEnv, bundle: &JObject, key: &str) -> j
.unwrap()
}

pub(crate) fn bundle_get_float(env: &mut JNIEnv, bundle: &JObject, key: &str) -> jfloat {
let key = env.new_string(key).unwrap();
env.call_method(
bundle,
"getFloat",
"(Ljava/lang/String;)F",
&[(&key).into()],
)
.unwrap()
.f()
.unwrap()
}

pub(crate) fn bundle_get_bool(env: &mut JNIEnv, bundle: &JObject, key: &str) -> bool {
let key = env.new_string(key).unwrap();
env.call_method(
Expand Down
Loading