未验证 提交 7c34dfaf 编写于 作者: M Michael Goderbauer 提交者: GitHub

Adds a11y action for selecting text (and moving cursor) (#4589)

See https://github.com/flutter/flutter/pull/14275 for framework side change.

Also includes some minor clean-ups for consistency. 

Required for https://github.com/flutter/flutter/issues/13469.
上级 4c82c566
...@@ -17,9 +17,10 @@ class SemanticsAction { ...@@ -17,9 +17,10 @@ class SemanticsAction {
static const int _kScrollDownIndex = 1 << 5; static const int _kScrollDownIndex = 1 << 5;
static const int _kIncreaseIndex = 1 << 6; static const int _kIncreaseIndex = 1 << 6;
static const int _kDecreaseIndex = 1 << 7; static const int _kDecreaseIndex = 1 << 7;
static const int _kShowOnScreen = 1 << 8; static const int _kShowOnScreenIndex = 1 << 8;
static const int _kMoveCursorForwardByCharacter = 1 << 9; static const int _kMoveCursorForwardByCharacterIndex = 1 << 9;
static const int _kMoveCursorBackwardByCharacter = 1 << 10; static const int _kMoveCursorBackwardByCharacterIndex = 1 << 10;
static const int _kSetSelectionIndex = 1 << 11;
/// The numerical value for this action. /// The numerical value for this action.
/// ///
...@@ -76,18 +77,35 @@ class SemanticsAction { ...@@ -76,18 +77,35 @@ class SemanticsAction {
/// ///
/// For example, this action might be send to a node in a scrollable list that /// For example, this action might be send to a node in a scrollable list that
/// is partially off screen to bring it on screen. /// is partially off screen to bring it on screen.
static const SemanticsAction showOnScreen = const SemanticsAction._(_kShowOnScreen); static const SemanticsAction showOnScreen = const SemanticsAction._(_kShowOnScreenIndex);
/// Move the cursor forward by one character. /// Move the cursor forward by one character.
/// ///
/// This is for example used by the cursor control in text fields. /// This is for example used by the cursor control in text fields.
static const SemanticsAction moveCursorForwardByCharacter = const SemanticsAction._(_kMoveCursorForwardByCharacter); ///
/// The action includes a boolean argument, which indicates whether the cursor
/// movement should extend (or start) a selection.
static const SemanticsAction moveCursorForwardByCharacter = const SemanticsAction._(_kMoveCursorForwardByCharacterIndex);
/// Move the cursor backward by one character. /// Move the cursor backward by one character.
/// ///
/// This is for example used by the cursor control in text fields. /// This is for example used by the cursor control in text fields.
static const SemanticsAction moveCursorBackwardByCharacter = const SemanticsAction._(_kMoveCursorBackwardByCharacter); ///
/// The action includes a boolean argument, which indicates whether the cursor
/// movement should extend (or start) a selection.
static const SemanticsAction moveCursorBackwardByCharacter = const SemanticsAction._(_kMoveCursorBackwardByCharacterIndex);
/// Set the text selection to the given range.
///
/// The provided argument is a Map<String, int> which includes the keys `base`
/// and `extent` indicating where the selection within the `value` of the
/// semantics node should start and where it should end. Values for both
/// keys can range from 0 to length of `value` (inclusive).
///
/// Setting `base` and `extent` to the same value will move the cursor to
/// that position (without selecting anything).
static const SemanticsAction setSelection = const SemanticsAction._(_kSetSelectionIndex);
/// The possible semantics actions. /// The possible semantics actions.
/// ///
...@@ -102,9 +120,10 @@ class SemanticsAction { ...@@ -102,9 +120,10 @@ class SemanticsAction {
_kScrollDownIndex: scrollDown, _kScrollDownIndex: scrollDown,
_kIncreaseIndex: increase, _kIncreaseIndex: increase,
_kDecreaseIndex: decrease, _kDecreaseIndex: decrease,
_kShowOnScreen: showOnScreen, _kShowOnScreenIndex: showOnScreen,
_kMoveCursorForwardByCharacter: moveCursorForwardByCharacter, _kMoveCursorForwardByCharacterIndex: moveCursorForwardByCharacter,
_kMoveCursorBackwardByCharacter: moveCursorBackwardByCharacter, _kMoveCursorBackwardByCharacterIndex: moveCursorBackwardByCharacter,
_kSetSelectionIndex: setSelection,
}; };
@override @override
...@@ -126,12 +145,14 @@ class SemanticsAction { ...@@ -126,12 +145,14 @@ class SemanticsAction {
return 'SemanticsAction.increase'; return 'SemanticsAction.increase';
case _kDecreaseIndex: case _kDecreaseIndex:
return 'SemanticsAction.decrease'; return 'SemanticsAction.decrease';
case _kShowOnScreen: case _kShowOnScreenIndex:
return 'SemanticsAction.showOnScreen'; return 'SemanticsAction.showOnScreen';
case _kMoveCursorForwardByCharacter: case _kMoveCursorForwardByCharacterIndex:
return 'SemanticsAction.moveCursorForwardByCharacter'; return 'SemanticsAction.moveCursorForwardByCharacter';
case _kMoveCursorBackwardByCharacter: case _kMoveCursorBackwardByCharacterIndex:
return 'SemanticsAction.moveCursorBackwardByCharacter'; return 'SemanticsAction.moveCursorBackwardByCharacter';
case _kSetSelectionIndex:
return 'SemanticsAction.setSelection';
} }
return null; return null;
} }
......
...@@ -31,6 +31,11 @@ import java.util.Set; ...@@ -31,6 +31,11 @@ import java.util.Set;
class AccessibilityBridge extends AccessibilityNodeProvider implements BasicMessageChannel.MessageHandler<Object> { class AccessibilityBridge extends AccessibilityNodeProvider implements BasicMessageChannel.MessageHandler<Object> {
private static final String TAG = "FlutterView"; private static final String TAG = "FlutterView";
// Constants from higher API levels.
// TODO(goderbauer): Get these from Android Support Library when
// https://github.com/flutter/flutter/issues/11099 is resolved.
public static final int ACTION_SHOW_ON_SCREEN = 16908342; // API level 23
private Map<Integer, SemanticsObject> mObjects; private Map<Integer, SemanticsObject> mObjects;
private final FlutterView mOwner; private final FlutterView mOwner;
private boolean mAccessibilityEnabled = false; private boolean mAccessibilityEnabled = false;
...@@ -51,7 +56,8 @@ class AccessibilityBridge extends AccessibilityNodeProvider implements BasicMess ...@@ -51,7 +56,8 @@ class AccessibilityBridge extends AccessibilityNodeProvider implements BasicMess
DECREASE(1 << 7), DECREASE(1 << 7),
SHOW_ON_SCREEN(1 << 8), SHOW_ON_SCREEN(1 << 8),
MOVE_CURSOR_FORWARD_BY_CHARACTER(1 << 9), MOVE_CURSOR_FORWARD_BY_CHARACTER(1 << 9),
MOVE_CURSOR_BACKWARD_BY_CHARACTER(1 << 10); MOVE_CURSOR_BACKWARD_BY_CHARACTER(1 << 10),
SET_SELECTION(1 << 11);
Action(int value) { Action(int value) {
this.value = value; this.value = value;
...@@ -142,6 +148,9 @@ class AccessibilityBridge extends AccessibilityNodeProvider implements BasicMess ...@@ -142,6 +148,9 @@ class AccessibilityBridge extends AccessibilityNodeProvider implements BasicMess
} }
result.setMovementGranularities(granularities); result.setMovementGranularities(granularities);
} }
if (object.hasAction(Action.SET_SELECTION)) {
result.addAction(AccessibilityNodeInfo.ACTION_SET_SELECTION);
}
if (object.hasFlag(Flag.IS_BUTTON)) { if (object.hasFlag(Flag.IS_BUTTON)) {
result.setClassName("android.widget.Button"); result.setClassName("android.widget.Button");
...@@ -317,12 +326,30 @@ class AccessibilityBridge extends AccessibilityNodeProvider implements BasicMess ...@@ -317,12 +326,30 @@ class AccessibilityBridge extends AccessibilityNodeProvider implements BasicMess
return true; return true;
} }
// TODO(goderbauer): Use ACTION_SHOW_ON_SCREEN from Android Support Library after case ACTION_SHOW_ON_SCREEN: {
// https://github.com/flutter/flutter/issues/11099 is resolved.
case 16908342: { // ACTION_SHOW_ON_SCREEN, added in API level 23
mOwner.dispatchSemanticsAction(virtualViewId, Action.SHOW_ON_SCREEN); mOwner.dispatchSemanticsAction(virtualViewId, Action.SHOW_ON_SCREEN);
return true; return true;
} }
case AccessibilityNodeInfo.ACTION_SET_SELECTION: {
final Map<String, Integer> selection = new HashMap<String, Integer>();
final boolean hasSelection = arguments != null
&& arguments.containsKey(
AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT)
&& arguments.containsKey(
AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT);
if (hasSelection) {
selection.put("base", arguments.getInt(
AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT));
selection.put("extent", arguments.getInt(
AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT));
} else {
// Clear the selection
selection.put("base", object.textSelectionExtent);
selection.put("extent", object.textSelectionExtent);
}
mOwner.dispatchSemanticsAction(virtualViewId, Action.SET_SELECTION, selection);
return true;
}
} }
return false; return false;
} }
...@@ -467,7 +494,7 @@ class AccessibilityBridge extends AccessibilityNodeProvider implements BasicMess ...@@ -467,7 +494,7 @@ class AccessibilityBridge extends AccessibilityNodeProvider implements BasicMess
object.id, AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED); object.id, AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED);
selectionEvent.getText().add(newValue); selectionEvent.getText().add(newValue);
selectionEvent.setFromIndex(object.textSelectionBase); selectionEvent.setFromIndex(object.textSelectionBase);
selectionEvent.setToIndex(object.previousTextSelectionExtent); selectionEvent.setToIndex(object.textSelectionExtent);
selectionEvent.setItemCount(newValue.length()); selectionEvent.setItemCount(newValue.length());
sendAccessibilityEvent(selectionEvent); sendAccessibilityEvent(selectionEvent);
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册