mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
249 lines
7.2 KiB
Dart
249 lines
7.2 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// ignore_for_file: avoid_dynamic_calls
|
|
|
|
import 'dart:convert';
|
|
|
|
import 'package:meta/meta.dart';
|
|
|
|
import 'constants.dart';
|
|
|
|
/// A semantics node created from Android accessibility information.
|
|
///
|
|
/// This object represents Android accessibility information derived from an
|
|
/// [AccessibilityNodeInfo](https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo)
|
|
/// object. The purpose is to verify in integration
|
|
/// tests that our semantics framework produces the correct accessibility info
|
|
/// on Android.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [AccessibilityNodeInfo](https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo)
|
|
class AndroidSemanticsNode {
|
|
AndroidSemanticsNode._(this._values);
|
|
|
|
/// Deserializes a new [AndroidSemanticsNode] from a json map.
|
|
///
|
|
/// The structure of the JSON:
|
|
///
|
|
/// {
|
|
/// "flags": {
|
|
/// "isChecked": bool,
|
|
/// "isCheckable": bool,
|
|
/// "isEditable": bool,
|
|
/// "isEnabled": bool,
|
|
/// "isFocusable": bool,
|
|
/// "isFocused": bool,
|
|
/// "isHeading": bool,
|
|
/// "isPassword": bool,
|
|
/// "isLongClickable": bool,
|
|
/// },
|
|
/// "text": String,
|
|
/// "contentDescription": String,
|
|
/// "className": String,
|
|
/// "id": int,
|
|
/// "rect": {
|
|
/// left: int,
|
|
/// top: int,
|
|
/// right: int,
|
|
/// bottom: int,
|
|
/// },
|
|
/// actions: [
|
|
/// int,
|
|
/// ]
|
|
/// }
|
|
factory AndroidSemanticsNode.deserialize(String value) {
|
|
return AndroidSemanticsNode._(json.decode(value));
|
|
}
|
|
|
|
final dynamic _values;
|
|
final List<AndroidSemanticsNode> _children = <AndroidSemanticsNode>[];
|
|
|
|
dynamic get _flags => _values['flags'];
|
|
|
|
/// The text value of the semantics node.
|
|
///
|
|
/// This is produced by combining the value, label, and hint fields from
|
|
/// the Flutter [SemanticsNode].
|
|
String? get text => _values['text'] as String?;
|
|
|
|
/// The contentDescription of the semantics node.
|
|
///
|
|
/// This field is used for the Switch, Radio, and Checkbox widgets
|
|
/// instead of [text]. If the text property is used for these, TalkBack
|
|
/// will not read out the "checked" or "not checked" label by default.
|
|
///
|
|
/// This is produced by combining the value, label, and hint fields from
|
|
/// the Flutter [SemanticsNode].
|
|
String? get contentDescription => _values['contentDescription'] as String?;
|
|
|
|
/// The className of the semantics node.
|
|
///
|
|
/// Certain kinds of Flutter semantics are mapped to Android classes to
|
|
/// use their default semantic behavior, such as checkboxes and images.
|
|
///
|
|
/// If a more specific value isn't provided, it defaults to
|
|
/// "android.view.View".
|
|
String? get className => _values['className'] as String?;
|
|
|
|
/// The identifier for this semantics node.
|
|
int? get id => _values['id'] as int?;
|
|
|
|
/// The children of this semantics node.
|
|
List<AndroidSemanticsNode> get children => _children;
|
|
|
|
/// Whether the node is currently in a checked state.
|
|
///
|
|
/// Equivalent to [SemanticsFlag.isChecked].
|
|
bool? get isChecked => _flags['isChecked'] as bool?;
|
|
|
|
/// Whether the node can be in a checked state.
|
|
///
|
|
/// Equivalent to [SemanticsFlag.hasCheckedState]
|
|
bool? get isCheckable => _flags['isCheckable'] as bool?;
|
|
|
|
/// Whether the node is editable.
|
|
///
|
|
/// This is usually only applied to text fields, which map
|
|
/// to "android.widget.EditText".
|
|
bool? get isEditable => _flags['isEditable'] as bool?;
|
|
|
|
/// Whether the node is enabled.
|
|
bool? get isEnabled => _flags['isEnabled'] as bool?;
|
|
|
|
/// Whether the node is focusable.
|
|
bool? get isFocusable => _flags['isFocusable'] as bool?;
|
|
|
|
/// Whether the node is focused.
|
|
bool? get isFocused => _flags['isFocused'] as bool?;
|
|
|
|
/// Whether the node is considered a heading.
|
|
bool? get isHeading => _flags['isHeading'] as bool?;
|
|
|
|
/// Whether the node represents a password field.
|
|
///
|
|
/// Equivalent to [SemanticsFlag.isObscured].
|
|
bool? get isPassword => _flags['isPassword'] as bool?;
|
|
|
|
/// Whether the node is long clickable.
|
|
///
|
|
/// Equivalent to having [SemanticsAction.longPress].
|
|
bool? get isLongClickable => _flags['isLongClickable'] as bool?;
|
|
|
|
/// Gets a [Rect] which defines the position and size of the semantics node.
|
|
Rect getRect() {
|
|
final dynamic rawRect = _values['rect'];
|
|
if (rawRect == null) {
|
|
return const Rect.fromLTRB(0.0, 0.0, 0.0, 0.0);
|
|
}
|
|
return Rect.fromLTRB(
|
|
(rawRect['left']! as int).toDouble(),
|
|
(rawRect['top']! as int).toDouble(),
|
|
(rawRect['right']! as int).toDouble(),
|
|
(rawRect['bottom']! as int).toDouble(),
|
|
);
|
|
}
|
|
|
|
/// Gets a [Size] which defines the size of the semantics node.
|
|
Size getSize() {
|
|
final Rect rect = getRect();
|
|
return Size(rect.bottom - rect.top, rect.right - rect.left);
|
|
}
|
|
|
|
/// Gets a list of [AndroidSemanticsActions] which are defined for the node.
|
|
List<AndroidSemanticsAction> getActions() {
|
|
final List<int>? actions = (_values['actions'] as List<dynamic>?)?.cast<int>();
|
|
if (actions == null) {
|
|
return const <AndroidSemanticsAction>[];
|
|
}
|
|
final List<AndroidSemanticsAction> convertedActions = <AndroidSemanticsAction>[];
|
|
for (final int id in actions) {
|
|
final AndroidSemanticsAction? action = AndroidSemanticsAction.deserialize(id);
|
|
if (action != null) {
|
|
convertedActions.add(action);
|
|
}
|
|
}
|
|
return convertedActions;
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
return _values.toString();
|
|
}
|
|
}
|
|
|
|
|
|
/// A Dart VM implementation of a rectangle.
|
|
///
|
|
/// Created to mirror the implementation of [ui.Rect].
|
|
@immutable
|
|
class Rect {
|
|
/// Creates a new rectangle.
|
|
///
|
|
/// All values are required.
|
|
const Rect.fromLTRB(this.left, this.top, this.right, this.bottom);
|
|
|
|
/// The top side of the rectangle.
|
|
final double top;
|
|
|
|
/// The left side of the rectangle.
|
|
final double left;
|
|
|
|
/// The right side of the rectangle.
|
|
final double right;
|
|
|
|
/// The bottom side of the rectangle.
|
|
final double bottom;
|
|
|
|
@override
|
|
int get hashCode => Object.hash(top, left, right, bottom);
|
|
|
|
@override
|
|
bool operator ==(Object other) {
|
|
if (other.runtimeType != runtimeType) {
|
|
return false;
|
|
}
|
|
return other is Rect
|
|
&& other.top == top
|
|
&& other.left == left
|
|
&& other.right == right
|
|
&& other.bottom == bottom;
|
|
}
|
|
|
|
@override
|
|
String toString() => 'Rect.fromLTRB($left, $top, $right, $bottom)';
|
|
}
|
|
|
|
/// A Dart VM implementation of a Size.
|
|
///
|
|
/// Created to mirror the implementation [ui.Size].
|
|
@immutable
|
|
class Size {
|
|
/// Creates a new [Size] object.
|
|
const Size(this.width, this.height);
|
|
|
|
/// The width of some object.
|
|
final double width;
|
|
|
|
/// The height of some object.
|
|
final double height;
|
|
|
|
@override
|
|
int get hashCode => Object.hash(width, height);
|
|
|
|
@override
|
|
bool operator ==(Object other) {
|
|
if (other.runtimeType != runtimeType) {
|
|
return false;
|
|
}
|
|
return other is Size
|
|
&& other.width == width
|
|
&& other.height == height;
|
|
}
|
|
|
|
@override
|
|
String toString() => 'Size{$width, $height}';
|
|
}
|