diff --git a/lib/ui/semantics.dart b/lib/ui/semantics.dart index bbcec353d4e..75df7db4f21 100644 --- a/lib/ui/semantics.dart +++ b/lib/ui/semantics.dart @@ -618,9 +618,16 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { /// string describes what result an action performed on this node has. The /// reading direction of all these strings is given by `textDirection`. /// - /// The fields 'textSelectionBase' and 'textSelectionExtent' describe the + /// The fields `textSelectionBase` and `textSelectionExtent` describe the /// currently selected text within `value`. /// + /// The field `maxValueLength` is used to indicate that an editable text field + /// has a limit on the number of characters entered. If it is -1 there is + /// no limit on the number of characters entered. The field + /// `currentValueLength` indicates how much of that limit has already been + /// used up. When `maxValueLength` is set, `currentValueLength` must also be + /// set. + /// /// The field `platformViewId` references the platform view, whose semantics /// nodes will be added as children to this node. If a platform view is /// specified, `childrenInHitTestOrder` and `childrenInTraversalOrder` must be @@ -652,6 +659,8 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { int id, int flags, int actions, + int maxValueLength, + int currentValueLength, int textSelectionBase, int textSelectionExtent, int platformViewId, @@ -683,6 +692,8 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { id, flags, actions, + maxValueLength, + currentValueLength, textSelectionBase, textSelectionExtent, platformViewId, @@ -713,6 +724,8 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { int id, int flags, int actions, + int maxValueLength, + int currentValueLength, int textSelectionBase, int textSelectionExtent, int platformViewId, diff --git a/lib/ui/semantics/semantics_node.h b/lib/ui/semantics/semantics_node.h index 81f98f1417b..bf3d3beaf2a 100644 --- a/lib/ui/semantics/semantics_node.h +++ b/lib/ui/semantics/semantics_node.h @@ -93,6 +93,8 @@ struct SemanticsNode { int32_t id = 0; int32_t flags = 0; int32_t actions = 0; + int32_t maxValueLength = -1; + int32_t currentValueLength = -1; int32_t textSelectionBase = -1; int32_t textSelectionExtent = -1; int32_t platformViewId = -1; diff --git a/lib/ui/semantics/semantics_update_builder.cc b/lib/ui/semantics/semantics_update_builder.cc index a5b94199c23..7048f6a45f4 100644 --- a/lib/ui/semantics/semantics_update_builder.cc +++ b/lib/ui/semantics/semantics_update_builder.cc @@ -40,6 +40,8 @@ void SemanticsUpdateBuilder::updateNode( int id, int flags, int actions, + int maxValueLength, + int currentValueLength, int textSelectionBase, int textSelectionExtent, int platformViewId, @@ -74,6 +76,8 @@ void SemanticsUpdateBuilder::updateNode( node.id = id; node.flags = flags; node.actions = actions; + node.maxValueLength = maxValueLength; + node.currentValueLength = currentValueLength; node.textSelectionBase = textSelectionBase; node.textSelectionExtent = textSelectionExtent; node.platformViewId = platformViewId; diff --git a/lib/ui/semantics/semantics_update_builder.h b/lib/ui/semantics/semantics_update_builder.h index 867fbef13f6..bbf595e6318 100644 --- a/lib/ui/semantics/semantics_update_builder.h +++ b/lib/ui/semantics/semantics_update_builder.h @@ -26,6 +26,8 @@ class SemanticsUpdateBuilder void updateNode(int id, int flags, int actions, + int maxValueLength, + int currentValueLength, int textSelectionBase, int textSelectionExtent, int platformViewId, diff --git a/lib/web_ui/lib/src/engine/semantics/semantics.dart b/lib/web_ui/lib/src/engine/semantics/semantics.dart index 36f9b2f1887..6005d288ea8 100644 --- a/lib/web_ui/lib/src/engine/semantics/semantics.dart +++ b/lib/web_ui/lib/src/engine/semantics/semantics.dart @@ -35,6 +35,8 @@ class SemanticsNodeUpdate { this.id, this.flags, this.actions, + this.maxValueLength, + this.currentValueLength, this.textSelectionBase, this.textSelectionExtent, this.platformViewId, @@ -67,6 +69,12 @@ class SemanticsNodeUpdate { /// See [ui.SemanticsUpdateBuilder.updateNode]. final int actions; + /// See [ui.SemanticsUpdateBuilder.updateNode]. + final int maxValueLength; + + /// See [ui.SemanticsUpdateBuilder.updateNode]. + final int currentValueLength; + /// See [ui.SemanticsUpdateBuilder.updateNode]. final int textSelectionBase; diff --git a/lib/web_ui/lib/src/ui/semantics.dart b/lib/web_ui/lib/src/ui/semantics.dart index 8884bb12921..3a9a1632914 100644 --- a/lib/web_ui/lib/src/ui/semantics.dart +++ b/lib/web_ui/lib/src/ui/semantics.dart @@ -648,6 +648,8 @@ class SemanticsUpdateBuilder { int id, int flags, int actions, + int maxValueLength, + int currentValueLength, int textSelectionBase, int textSelectionExtent, int platformViewId, @@ -676,6 +678,8 @@ class SemanticsUpdateBuilder { id: id, flags: flags, actions: actions, + maxValueLength: maxValueLength, + currentValueLength: currentValueLength, textSelectionBase: textSelectionBase, textSelectionExtent: textSelectionExtent, scrollChildren: scrollChildren, diff --git a/shell/platform/android/io/flutter/view/AccessibilityBridge.java b/shell/platform/android/io/flutter/view/AccessibilityBridge.java index e4af91446ad..6732c9b4455 100644 --- a/shell/platform/android/io/flutter/view/AccessibilityBridge.java +++ b/shell/platform/android/io/flutter/view/AccessibilityBridge.java @@ -564,6 +564,14 @@ public class AccessibilityBridge extends AccessibilityNodeProvider { granularities |= AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD; } result.setMovementGranularities(granularities); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && semanticsNode.maxValueLength >= 0) { + // Account for the fact that Flutter is counting Unicode scalar values and Android + // is counting UTF16 words. + final int length = semanticsNode.value == null ? 0 : semanticsNode.value.length(); + int a = length - semanticsNode.currentValueLength + semanticsNode.maxValueLength; + result.setMaxTextLength(length - semanticsNode.currentValueLength + semanticsNode.maxValueLength); + } + } // These are non-ops on older devices. Attempting to interact with the text will cause Talkback to read the @@ -1719,6 +1727,8 @@ public class AccessibilityBridge extends AccessibilityNodeProvider { private int flags; private int actions; + private int maxValueLength; + private int currentValueLength; private int textSelectionBase; private int textSelectionExtent; private int platformViewId; @@ -1858,6 +1868,8 @@ public class AccessibilityBridge extends AccessibilityNodeProvider { flags = buffer.getInt(); actions = buffer.getInt(); + maxValueLength = buffer.getInt(); + currentValueLength = buffer.getInt(); textSelectionBase = buffer.getInt(); textSelectionExtent = buffer.getInt(); platformViewId = buffer.getInt(); diff --git a/shell/platform/android/platform_view_android.cc b/shell/platform/android/platform_view_android.cc index 449a7e4571f..856fd9748ce 100644 --- a/shell/platform/android/platform_view_android.cc +++ b/shell/platform/android/platform_view_android.cc @@ -227,7 +227,7 @@ void PlatformViewAndroid::DispatchSemanticsAction(JNIEnv* env, void PlatformViewAndroid::UpdateSemantics( flutter::SemanticsNodeUpdates update, flutter::CustomAccessibilityActionUpdates actions) { - constexpr size_t kBytesPerNode = 39 * sizeof(int32_t); + constexpr size_t kBytesPerNode = 41 * sizeof(int32_t); constexpr size_t kBytesPerChild = sizeof(int32_t); constexpr size_t kBytesPerAction = 4 * sizeof(int32_t); @@ -261,6 +261,8 @@ void PlatformViewAndroid::UpdateSemantics( buffer_int32[position++] = node.id; buffer_int32[position++] = node.flags; buffer_int32[position++] = node.actions; + buffer_int32[position++] = node.maxValueLength; + buffer_int32[position++] = node.currentValueLength; buffer_int32[position++] = node.textSelectionBase; buffer_int32[position++] = node.textSelectionExtent; buffer_int32[position++] = node.platformViewId;