diff --git a/shell/platform/ohos/BUILD.gn b/shell/platform/ohos/BUILD.gn index 62c781f18dd9cde37095e856a07f8103b34b060d..d4edca3eb4feb72c26d7a962d6f4198adb8e3178 100644 --- a/shell/platform/ohos/BUILD.gn +++ b/shell/platform/ohos/BUILD.gn @@ -87,7 +87,6 @@ source_set("flutter_ohos_sources") { "platform_view_ohos_delegate.h", "./accessibility/ohos_accessibility_bridge.h", "./accessibility/ohos_accessibility_features.h", - "./accessibility/ohos_accessibility_manager.h", "./accessibility/native_accessibility_channel.h", "./accessibility/ohos_accessibility_ddl.h", "./utils/ddl_utils.h", @@ -126,7 +125,6 @@ source_set("flutter_ohos_sources") { "platform_view_ohos_delegate.cpp", "./accessibility/ohos_accessibility_bridge.cpp", "./accessibility/ohos_accessibility_features.cpp", - "./accessibility/ohos_accessibility_manager.cpp", "./accessibility/native_accessibility_channel.cpp", "./accessibility/ohos_accessibility_ddl.cpp", "./utils/ohos_utils.cpp" diff --git a/shell/platform/ohos/accessibility/native_accessibility_channel.cpp b/shell/platform/ohos/accessibility/native_accessibility_channel.cpp index 7d571f3528bd1d3484c97909e4cbacc28fc304cc..aeb65841bacbaa4a30f9c031f5148bde3cf12cc8 100644 --- a/shell/platform/ohos/accessibility/native_accessibility_channel.cpp +++ b/shell/platform/ohos/accessibility/native_accessibility_channel.cpp @@ -83,7 +83,7 @@ namespace flutter { flutter::SemanticsNodeUpdates update, flutter::CustomAccessibilityActionUpdates actions) { - OhosAccessibilityBridge::GetInstance()->UpdateSemantics(update, actions); + OhosAccessibilityBridge::GetInstance()->UpdateSemantics(std::move(update), std::move(actions)); } /** diff --git a/shell/platform/ohos/accessibility/ohos_accessibility_bridge.cpp b/shell/platform/ohos/accessibility/ohos_accessibility_bridge.cpp index dbb1a076e9f5bad61c48d76558c3fe68aef5513d..e506c961d1a266a31c05467ac9cf7328d141ef80 100644 --- a/shell/platform/ohos/accessibility/ohos_accessibility_bridge.cpp +++ b/shell/platform/ohos/accessibility/ohos_accessibility_bridge.cpp @@ -16,13 +16,12 @@ #include #include #include +#include #include "flutter/fml/logging.h" #include "flutter/shell/platform/ohos/ohos_logging.h" #include "flutter/shell/common/platform_view.h" #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/ohos/ohos_shell_holder.h" -#include "third_party/skia/include/core/SkMatrix.h" -#include "third_party/skia/include/core/SkScalar.h" #include "flutter/shell/platform/ohos/ohos_xcomponent_adapter.h" namespace flutter { @@ -44,8 +43,40 @@ OhosAccessibilityBridge* OhosAccessibilityBridge::GetInstance() } OhosAccessibilityBridge::OhosAccessibilityBridge() - : isFlutterNavigated_(false), provider_(nullptr), - isAccessibilityEnabled_(false) {} +: xcomponentId_("oh_flutter_1"), + native_shell_holder_id_(0), + isFlutterNavigated_(false), + isTouchGuideOn_(false), + isAccessibilityEnabled_(false) {} + +/** + * flutter无障碍相关语义树、句柄指针provider等参数 + * 跟随xcomponentid显示切换,而加载对应xcomponent的语义树和provider指针 + * @NOTE: 当屏幕同时显示多个xcomponent时,无法通过聚焦事件而触发xcomponentid改变 + */ +void OhosAccessibilityBridge::AccessibiltiyChangesWithXComponentId() +{ + auto xcompMap = XComponentAdapter::GetInstance()->xcomponetMap_; + // 获取当前显示的xcomponetid + std::string currXcompId = XComponentAdapter::GetInstance()->currentXComponentId_; + if (xcomponentId_ == currXcompId) { + xcomponentId_ = currXcompId; + return; + } + + auto it = xcompMap.find(currXcompId); + if (!xcompMap.empty() && it != xcompMap.end()) { + // 更新xcompid,shellholderId,provider指针 + xcomponentId_ = currXcompId; + native_shell_holder_id_ = std::stoll(it->second->shellholderId_); + g_flutterSemanticsTree = g_flutterSemanticsTreeXComponents[xcomponentId_]; + FML_DLOG(INFO) << "AccessibiltiyChangesWithXComponentId -> xcomponentid:" << xcomponentId_; + } else { + xcomponentId_ = "oh_flutter_1"; + g_flutterSemanticsTree = g_flutterSemanticsTreeXComponents[xcomponentId_]; + FML_DLOG(INFO) << "AccessibiltiyChangesWithXComponentId -> xcomponentid:" << xcomponentId_; + } +} /** * 监听当前ohos平台是否开启无障碍屏幕朗读服务 @@ -54,7 +85,7 @@ void OhosAccessibilityBridge::OnOhosAccessibilityStateChange( bool ohosAccessibilityEnabled, int64_t shellholderId) { native_shell_holder_id_ = shellholderId; - provider_ = XComponentAdapter::GetInstance()->accessibilityProvider_; + AccessibiltiyChangesWithXComponentId(); nativeAccessibilityChannel_ = std::make_shared(); accessibilityFeatures_ = std::make_shared(); @@ -67,11 +98,6 @@ void OhosAccessibilityBridge::OnOhosAccessibilityStateChange( } } -void OhosAccessibilityBridge::SetNativeShellHolderId(int64_t id) -{ - this->native_shell_holder_id_ = id; -} - /** * 从dart侧传递到c++侧的flutter无障碍语义树节点更新过程, * 路由新页面、滑动页面等操作会自动触发该语义树的更新 @@ -81,14 +107,12 @@ void OhosAccessibilityBridge::UpdateSemantics( flutter::CustomAccessibilityActionUpdates actions) { FML_DLOG(INFO) << "OhosAccessibilityBridge::UpdateSemantics()"; - provider_ = XComponentAdapter::GetInstance()->accessibilityProvider_; - std::vector updatedFlutterNodes; + std::unordered_set updatedFlutterNodes; + AccessibiltiyChangesWithXComponentId(); - // 当flutter页面状态更新(路由新页面)时,自动请求root节点组件获焦(规避滑动组件更新干扰) + // request rootNode when routes a new flutter page + // on touch guide mode (screen reader is on) if (isFlutterNavigated_) { - Flutter_SendAccessibilityAsyncEvent(0, - ArkUI_AccessibilityEventType:: - ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_PAGE_STATE_UPDATE); RequestFocusWhenPageUpdate(0); isFlutterNavigated_ = false; } @@ -97,44 +121,42 @@ void OhosAccessibilityBridge::UpdateSemantics( for (const auto& item : update) { // 获取当前更新的节点node const auto& node = item.second; - // 更新扩展的SemanticsNode信息 - auto nodeEx = UpdatetSemanticsNodeExtent(node); - - //print semantics node and flags info for debugging - GetSemanticsNodeDebugInfo(nodeEx); - GetSemanticsFlagsDebugInfo(nodeEx); + auto nodeEx = UpdatetSemanticsNodeExtent(std::move(node)); // 构建flutter无障碍语义节点树 g_flutterSemanticsTree[nodeEx.id] = nodeEx; - // 若当前节点为获焦 - if (IsNodeFocused(nodeEx)) { - inputFocusedNode = nodeEx; - } + if (!IsNodeVisible(nodeEx)) { continue; } + // 若当前节点和更新前节点信息不同,则加入更新节点数组 if (nodeEx.hadPreviousConfig) { - updatedFlutterNodes.emplace_back(nodeEx); - } - - // 获取当前flutter节点的全部子节点数量,并构建父子节点id映射关系 - int32_t childNodeCount = nodeEx.childrenInTraversalOrder.size(); - for (int32_t i = 0; i < childNodeCount; i++) { - g_parentChildIdVec.emplace_back(std::make_pair(nodeEx.id, nodeEx.childrenInTraversalOrder[i])); + updatedFlutterNodes.emplace(nodeEx); + FML_DLOG(INFO) << "updatedFlutterNodes -> node.id=" << nodeEx.id; } } + // calculate the global tranfom matrix and parent id for each node + ComputeGlobalTransformAndParentId(); - // 页面内容更新事件 - Flutter_SendAccessibilityAsyncEvent(0, - ArkUI_AccessibilityEventType:: - ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_PAGE_CONTENT_UPDATE); - LOGD("Flutter_SendAccessibilityAsyncEvent -> PAGE_CONTENT_UPDATE"); + Flutter_SendAccessibilityAsyncEvent( + 0, ArkUI_AccessibilityEventType::ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_PAGE_CONTENT_UPDATE); + + // 将更新后的flutter语义树和父子节点id映射缓存,保存到相应的xcomponent里面 + g_flutterSemanticsTreeXComponents[xcomponentId_] = g_flutterSemanticsTree; + + // if the screen reader starts (touch guide state is true), + // sending the a11y event and draw the green rect + Flutter_SendAccessibilityAsyncEvent( + 0, ArkUI_AccessibilityEventType::ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_PAGE_CONTENT_UPDATE); + /* 针对更新后的节点进行事件处理 */ - for (const auto& nodeEx: updatedFlutterNodes) { + for (auto& nodeEx: updatedFlutterNodes) { + FML_DLOG(INFO) << "*#*#* updated node.id=" << nodeEx.id; + // 当滑动节点产生滑动,并执行滑动处理 if (HasScrolled(nodeEx)) { - LOGD("UpdateSemantics -> has scrolled"); + LOGD("UpdateSemantics -> nodeId = %{public}d has scrolled", nodeEx.id); auto OH_ArkUI_CreateAccessibilityElementInfo = OhosAccessibilityDDL::DLLoadCreateElemInfoFunc(ArkUIAccessibilityConstant::ARKUI_CREATE_NODE); CHECK_DLL_NULL_PTR(OH_ArkUI_CreateAccessibilityElementInfo); @@ -144,10 +166,12 @@ void OhosAccessibilityBridge::UpdateSemantics( // flutter滑动组件滑动处理逻辑 FlutterScrollExecution(nodeEx, _elementInfo); - // 屏幕朗读状态下双指滑动,获焦节点绿框实时跟随节点滑动 - Flutter_SendAccessibilityAsyncEvent( - static_cast(accessibilityFocusedNode.id), - ArkUI_AccessibilityEventType::ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_FOCUS_NODE_UPDATE); + // when screen reader is on and the touch guide is active + if (isTouchGuideOn_) { + Flutter_SendAccessibilityAsyncEvent( + static_cast(accessibilityFocusedNode.id), + ArkUI_AccessibilityEventType::ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_FOCUS_NODE_UPDATE); + } auto OH_ArkUI_DestoryAccessibilityElementInfo = OhosAccessibilityDDL::DLLoadDestroyElemFunc(ArkUIAccessibilityConstant::ARKUI_DESTORY_NODE); @@ -158,16 +182,12 @@ void OhosAccessibilityBridge::UpdateSemantics( // 判断是否触发liveRegion活动区,当前节点是否活跃 if (nodeEx.HasFlag(FLAGS_::kIsLiveRegion) && HasChangedLabel(nodeEx)) { - FML_DLOG(INFO) << "UpdateSemantics -> page content update, nodeEx.id=" << nodeEx.id; + FML_DLOG(INFO) << "liveRegion -> page content update, nodeEx.id=" << nodeEx.id; Flutter_SendAccessibilityAsyncEvent(static_cast(nodeEx.id), ArkUI_AccessibilityEventType:: ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_PAGE_CONTENT_UPDATE); - LOGD("Flutter_SendAccessibilityAsyncEvent -> PAGE_CONTENT_UPDATE"); } } - - // 输出flutter语义树相关重要语义信息debug日志 - GetSemanticsDebugInfo(); FML_DLOG(INFO) << "=== UpdateSemantics() is finished ==="; } @@ -202,13 +222,6 @@ void OhosAccessibilityBridge::FlutterScrollExecution( nodePosition -= node.scrollExtentMin; } - if (node.HasAction(ACTIONS_::kScrollUp) || - node.HasAction(ACTIONS_::kScrollDown)) { - } else if (node.HasAction(ACTIONS_::kScrollLeft) || - node.HasAction(ACTIONS_::kScrollRight)) { - LOGD("current flutterNode has scroll up/down/left/right"); - } - // 当可滑动组件存在滑动子节点 if (node.scrollChildren > 0) { // 配置当前滑动组件的子节点总数 @@ -270,7 +283,8 @@ void OhosAccessibilityBridge::FlutterScrollExecution( void OhosAccessibilityBridge::RequestFocusWhenPageUpdate(int32_t requestFocusId) { if (OHOS_API_VERSION < 13) { return; } - provider_ = XComponentAdapter::GetInstance()->accessibilityProvider_; + std::lock_guard lock(XComponentAdapter::GetInstance()->mutex_); + auto provider_ = XComponentAdapter::GetInstance()->GetAccessibilityProvider(xcomponentId_); CHECK_NULL_PTR_RET_VOID(provider_, RequestFocusWhenPageUpdate); auto OH_ArkUI_CreateAccessibilityEventInfo = @@ -378,15 +392,7 @@ void OhosAccessibilityBridge::OnTooltip(std::unique_ptr& message) //获取根节点 SemanticsNodeExtent OhosAccessibilityBridge::GetFlutterRootSemanticsNode() { - if (!g_flutterSemanticsTree.size()) { - LOGE("GetFlutterRootSemanticsNode: g_flutterSemanticsTree.size()=0"); - return {}; - } - if (g_flutterSemanticsTree.find(0) == g_flutterSemanticsTree.end()) { - LOGE("GetFlutterRootSemanticsNod: g_flutterSemanticsTree has no root id"); - return {}; - } - return g_flutterSemanticsTree.at(0); + return GetFlutterSemanticsNode(0); } /** @@ -395,12 +401,11 @@ SemanticsNodeExtent OhosAccessibilityBridge::GetFlutterRootSemanticsNode() SemanticsNodeExtent OhosAccessibilityBridge::GetFlutterSemanticsNode( int32_t id) { - if (g_flutterSemanticsTree.count(id) > 0) { - return g_flutterSemanticsTree.at(id); - } else { - LOGE("GetFlutterSemanticsNode g_flutterSemanticsTree = null"); - return {}; + auto it = g_flutterSemanticsTree.find(id); + if (it != g_flutterSemanticsTree.end()) { + return it->second; } + return {}; } /** @@ -408,20 +413,16 @@ SemanticsNodeExtent OhosAccessibilityBridge::GetFlutterSemanticsNode( */ int32_t OhosAccessibilityBridge::GetParentId(int64_t elementId) { - if (!g_parentChildIdVec.size()) { - FML_DLOG(WARNING) << "OhosAccessibilityBridge::GetParentId parentChildIdMap.size()=0"; - return ARKUI_ACCESSIBILITY_ROOT_PARENT_ID; - } if (elementId == -1 || elementId == 0) { return ARKUI_ACCESSIBILITY_ROOT_PARENT_ID; } - int32_t childElementId = static_cast(elementId); - for (const auto& item : g_parentChildIdVec) { - if (item.second == childElementId) { - return item.first; - } + int32_t id = static_cast(elementId); + auto node = GetFlutterSemanticsNode(id); + if (g_flutterSemanticsTree.find(id) == g_flutterSemanticsTree.end()) { + LOGE("GetParentId: %{public}d is null", id); + return -1; } - return RET_ERROR_STATE_CODE; + return node.parentId; } /** @@ -433,97 +434,78 @@ void OhosAccessibilityBridge::SetAbsoluteScreenRect(SemanticsNodeExtent& flutter float right, float bottom) { - g_screenRectMap[flutterNode.id] = AbsoluteRect{left, top, right, bottom}; + flutterNode.absoluteRect = {left, top, right, bottom}; FML_DLOG(INFO) << "SetAbsoluteScreenRect -> id=" << flutterNode.id << ", {" << left << ", " << top << ", " << right << ", "<< bottom << "> }"; } -AbsoluteRect OhosAccessibilityBridge::GetAbsoluteScreenRect(SemanticsNodeExtent& flutterNode) +AbsoluteRect OhosAccessibilityBridge::GetAbsoluteScreenRect(const SemanticsNodeExtent& flutterNode) { - if (!g_screenRectMap.empty() && g_screenRectMap.count(flutterNode.id) > 0) { - return g_screenRectMap.at(flutterNode.id); - } else { - FML_DLOG(ERROR) << "GetAbsoluteScreenRect -> flutterNodeId=" - << flutterNode.id << " is not found !"; - return {}; - } + return flutterNode.absoluteRect; } /** - * 获取flutter相对-绝对坐标映射的真实缩放系数 + * calculate the global transform matrix for each node */ -std::pair OhosAccessibilityBridge::GetRealScaleFactor() +void OhosAccessibilityBridge::ComputeGlobalTransformAndParentId() { - auto secondNode = GetFlutterSemanticsNode(1); - SkMatrix transform = secondNode.transform.asM33(); - auto scaleX = transform.get(SkMatrix::kMScaleX); - auto scaleY = transform.get(SkMatrix::kMScaleY); - return std::make_pair(scaleX, scaleY); + std::queue semanticsQue; + + auto rootNode = GetFlutterSemanticsNode(0); + rootNode.globalTransform = rootNode.transform; + rootNode.parentId = ARKUI_ACCESSIBILITY_ROOT_PARENT_ID; + g_flutterSemanticsTree[rootNode.id] = rootNode; + semanticsQue.push(rootNode); + + while (!semanticsQue.empty()) { + auto currNode = semanticsQue.front(); + semanticsQue.pop(); + + for (const auto& childId: currNode.childrenInTraversalOrder) { + auto childNode = GetFlutterSemanticsNode(childId); + childNode.parentId = currNode.id; + childNode.globalTransform = currNode.globalTransform * childNode.transform; + g_flutterSemanticsTree[childId] = childNode; + semanticsQue.push(childNode); + } + } +} + +SkPoint OhosAccessibilityBridge::ApplyTransform( + SkPoint& point, const SkM44& transform) { + SkV4 vector = transform.map(point.x(), point.y(), 0, 1); + return SkPoint::Make(vector.x / vector.w, vector.y / vector.w); } /** - * flutter语义树中相对坐标系转化为屏幕绝对坐标的映射算法实现 - * NOTE:目前暂未考虑旋转、透视场景,不影响屏幕朗读功能 - * (SkMatrix::kMSkewX, SkMatrix::kMSkewY, SkMatrix::kMPersp0, - * SkMatrix::kMPersp1, SkMatrix::kMPersp2) + * convert local(relative) rect to global(absolut) rect + * @param node flutter semantics node */ -void OhosAccessibilityBridge::FlutterRelativeRectToScreenRect( - SemanticsNodeExtent currNode) -{ - // 获取当前flutter节点的相对rect - auto [currLeft, currTop, currRight, currBottom] = currNode.rect; - - // 获取当前flutter节点的缩放、平移、透视等矩阵坐标转换以下矩阵坐标变换参数 - SkMatrix transform = currNode.transform.asM33(); - auto _kMScaleX = transform.get(SkMatrix::kMScaleX); - auto _kMTransX = transform.get(SkMatrix::kMTransX); - auto _kMScaleY = transform.get(SkMatrix::kMScaleY); - auto _kMTransY = transform.get(SkMatrix::kMTransY); - - // 获取当前flutter节点的父节点的相对rect - int32_t parentId = GetParentId(currNode.id); - auto parentNode = GetFlutterSemanticsNode(parentId); - if (g_flutterSemanticsTree.find(parentId) == g_flutterSemanticsTree.end()) { - LOGE("FlutterRelativeRectToScreenRect: GetFlutterSemanticsNode id=%{public}d null", parentId); - } - - // 获取当前flutter节点的父节点的绝对坐标 - auto [realParentLeft, realParentTop, realParentRight, realParentBottom] = GetAbsoluteScreenRect(parentNode); - - //获取root节点的绝对坐标 - auto rootNode = GetFlutterSemanticsNode(0); - auto rootRect = GetAbsoluteScreenRect(rootNode); - auto rootWidth = rootRect.right; - auto rootHeight = rootRect.bottom; - - // 获取真实缩放系数 - auto [realScaleXFactor, realScaleYFactor] = GetRealScaleFactor(); - - // 更新后的节点屏幕坐标,子节点的屏幕绝对坐标转换,包括offset偏移值计算、缩放系数变换 - bool isRectScaleChanged = _kMScaleX > 1 && _kMScaleY > 1; - float newLeft = isRectScaleChanged ? - currLeft + _kMTransX * _kMScaleX : (currLeft + _kMTransX) * realScaleXFactor + realParentLeft; - float newTop = isRectScaleChanged ? - currTop + _kMTransY * _kMScaleY : (currTop + _kMTransY) * realScaleYFactor + realParentTop; - float newRight = isRectScaleChanged ? - currRight * _kMScaleX : newLeft + currRight * realScaleXFactor; - float newBottom = isRectScaleChanged ? - currBottom * _kMScaleY : newTop + currBottom * realScaleYFactor; - - // 若子节点rect超过父节点则跳过显示(单个屏幕显示不下,滑动再重新显示) - const bool isExceedScreeArea = newLeft < realParentLeft || newTop < realParentTop || - newRight > realParentRight || newBottom > realParentBottom || - newLeft >= newRight || newTop >= newBottom || - newRight > rootWidth || newBottom > rootHeight; - if (isExceedScreeArea) { - FML_DLOG(WARNING) << "RelativeRectToScreenRect -> childRect exceeds parentRect {Id: " - << currNode.id << ", (" << newLeft << ", " << newTop - << ", " << newRight << ", " << newBottom << ")}"; - // 防止滑动场景下绿框坐标超出屏幕范围,进行正则化处理 - SetAbsoluteScreenRect(currNode, rootWidth, rootHeight, 0, 0); - } else { - SetAbsoluteScreenRect(currNode, newLeft, newTop, newRight, newBottom); +void OhosAccessibilityBridge::RelativeRectToScreenRect(SemanticsNodeExtent& node) +{ + auto [left, top, right, bottom] = node.rect; + SkM44 globalTransform = node.globalTransform; + + SkPoint points[4] = { + SkPoint::Make(left, top), // top-left point + SkPoint::Make(right, top), // top-right point + SkPoint::Make(right, bottom), // bottom-right point + SkPoint::Make(left, bottom) // bottom-left point + }; + + for (auto& point : points) { + point = ApplyTransform(point, globalTransform); + } + + SkRect globalRect; + bool checkResult = globalRect.setBoundsCheck(points, 4); + if (!checkResult) { + FML_DLOG(WARNING) << "RelativeRectToScreenRect -> Transformed points can't make a rect "; } + globalRect.setBounds(points, 4); + + SetAbsoluteScreenRect(node, globalRect.left(), globalRect.top(), + globalRect.right(), globalRect.bottom()); } /** @@ -531,95 +513,75 @@ void OhosAccessibilityBridge::FlutterRelativeRectToScreenRect( */ void OhosAccessibilityBridge::FlutterSetElementInfoOperationActions( ArkUI_AccessibilityElementInfo* elementInfoFromList, - std::string widget_type) + const SemanticsNodeExtent& node) { if (OHOS_API_VERSION < 13) { return; } auto OH_ArkUI_AccessibilityElementInfoSetOperationActions = OhosAccessibilityDDL::DLLoadSetElemOperActionsFunc(ArkUIAccessibilityConstant::ARKUI_SET_ACTIONS); CHECK_DLL_NULL_PTR(OH_ArkUI_AccessibilityElementInfoSetOperationActions); - if (OHOSUtils::Contains(widget_type, EDIT_TEXT_WIDGET_NAME) || - OHOSUtils::Contains(widget_type, EDIT_MULTILINE_TEXT_WIDGET_NAME)) { - // set elementinfo action types - int32_t actionTypeNum = 10; - ArkUI_AccessibleAction actions[actionTypeNum]; - int32_t idx = 0; - actions[idx].actionType = ArkUI_Accessibility_ActionType:: - ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_GAIN_ACCESSIBILITY_FOCUS; - actions[idx++].description = "获取焦点"; - actions[idx].actionType = ArkUI_Accessibility_ActionType:: - ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_CLEAR_ACCESSIBILITY_FOCUS; - actions[idx++].description = "清除焦点"; + + int32_t actionTypeNum = 30; // declare an unreachable array length + ArkUI_AccessibleAction actions[actionTypeNum]; + size_t idx = 0; // real length of array + if (node.HasAction(ACTIONS_::kTap)) { actions[idx].actionType = ArkUI_Accessibility_ActionType:: ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_CLICK; - actions[idx++].description = "点击操作"; + actions[idx++].description = "click action"; + } + if (node.HasAction(ACTIONS_::kLongPress)) { actions[idx].actionType = ArkUI_Accessibility_ActionType:: ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_LONG_CLICK; - actions[idx++].description = "长按操作"; - actions[idx].actionType = ArkUI_Accessibility_ActionType:: - ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_COPY; - actions[idx++].description = "文本复制"; - actions[idx].actionType = ArkUI_Accessibility_ActionType:: - ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_PASTE; - actions[idx++].description = "文本粘贴"; - actions[idx].actionType = ArkUI_Accessibility_ActionType:: - ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_CUT; - actions[idx++].description = "文本剪切"; - actions[idx].actionType = ArkUI_Accessibility_ActionType:: - ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SELECT_TEXT; - actions[idx++].description = "文本选择"; + actions[idx++].description = "longClick action"; + } + if (node.HasAction(ACTIONS_::kSetText)) { actions[idx].actionType = ArkUI_Accessibility_ActionType:: ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SET_TEXT; - actions[idx++].description = "文本内容设置"; + actions[idx++].description = "setText action"; + } + if (node.HasAction(ACTIONS_::kSetSelection)) { actions[idx].actionType = ArkUI_Accessibility_ActionType:: - ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SET_CURSOR_POSITION; - actions[idx].description = "光标位置设置"; - - ARKUI_ACCESSIBILITY_CALL_CHECK( - OH_ArkUI_AccessibilityElementInfoSetOperationActions(elementInfoFromList, actionTypeNum, actions) - ); - } else if (OHOSUtils::Contains(widget_type, SCROLL_WIDGET_NAME)) { - // if node is a scrollable component - int32_t actionTypeNum = 5; - ArkUI_AccessibleAction actions[actionTypeNum]; - int32_t idx = 0; + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SELECT_TEXT; + actions[idx++].description = "setSelection action"; + } + if (node.HasAction(ACTIONS_::kCopy)) { actions[idx].actionType = ArkUI_Accessibility_ActionType:: - ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_GAIN_ACCESSIBILITY_FOCUS; - actions[idx++].description = "获取焦点"; + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_COPY; + actions[idx++].description = "copy action"; + } + if (node.HasAction(ACTIONS_::kCut)) { actions[idx].actionType = ArkUI_Accessibility_ActionType:: - ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_CLEAR_ACCESSIBILITY_FOCUS; - actions[idx++].description = "清除焦点"; + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_CUT; + actions[idx++].description = "cut action"; + } + if (node.HasAction(ACTIONS_::kPaste)) { actions[idx].actionType = ArkUI_Accessibility_ActionType:: - ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_CLICK; - actions[idx++].description = "点击动作"; + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_PASTE; + actions[idx++].description = "paste action"; + } + if (node.HasAction(ACTIONS_::kScrollLeft) || + node.HasAction(ACTIONS_::kScrollUp) || + node.HasAction(ACTIONS_::kIncrease)) { actions[idx].actionType = ArkUI_Accessibility_ActionType:: ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SCROLL_FORWARD; - actions[idx++].description = "向上滑动"; + actions[idx++].description = "scrollForward action"; + } + if (node.HasAction(ACTIONS_::kScrollRight) || + node.HasAction(ACTIONS_::kScrollDown) || + node.HasAction(ACTIONS_::kDecrease)) { actions[idx].actionType = ArkUI_Accessibility_ActionType:: ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SCROLL_BACKWARD; - actions[idx].description = "向下滑动"; - - ARKUI_ACCESSIBILITY_CALL_CHECK( - OH_ArkUI_AccessibilityElementInfoSetOperationActions(elementInfoFromList, actionTypeNum, actions) - ); - } else { - // set common component action types - int32_t actionTypeNum = 3; - ArkUI_AccessibleAction actions[actionTypeNum]; - int32_t idx = 0; - actions[idx].actionType = ArkUI_Accessibility_ActionType:: - ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_GAIN_ACCESSIBILITY_FOCUS; - actions[idx++].description = "获取焦点"; - actions[idx].actionType = ArkUI_Accessibility_ActionType:: - ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_CLEAR_ACCESSIBILITY_FOCUS; - actions[idx++].description = "清除焦点"; - actions[idx].actionType = ArkUI_Accessibility_ActionType:: - ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_CLICK; - actions[idx].description = "点击动作"; - - ARKUI_ACCESSIBILITY_CALL_CHECK( - OH_ArkUI_AccessibilityElementInfoSetOperationActions(elementInfoFromList, actionTypeNum, actions) - ); - } + actions[idx++].description = "scrollBackward action"; + } + actions[idx].actionType = ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_GAIN_ACCESSIBILITY_FOCUS; + actions[idx++].description = "focus action"; + actions[idx].actionType = ArkUI_Accessibility_ActionType:: + ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_CLEAR_ACCESSIBILITY_FOCUS; + actions[idx++].description = "clearFocus action"; + + ARKUI_ACCESSIBILITY_CALL_CHECK( + OH_ArkUI_AccessibilityElementInfoSetOperationActions(elementInfoFromList, idx, actions) + ); } /** @@ -655,7 +617,7 @@ void OhosAccessibilityBridge::FlutterSetElementInfoProperties( rect = {static_cast(left), static_cast(top), static_cast(right), static_cast(bottom)}; } else { // 若当前节点为id >= 1的节点 - FlutterRelativeRectToScreenRect(flutterNode); + RelativeRectToScreenRect(flutterNode); auto [left, top, right, bottom] = GetAbsoluteScreenRect(flutterNode); rect = {static_cast(left), static_cast(top), static_cast(right), static_cast(bottom)}; @@ -671,9 +633,9 @@ void OhosAccessibilityBridge::FlutterSetElementInfoProperties( // 设置root节点的action类型 std::string widgeType = GetNodeComponentType(flutterNode); if (elementId < 1) { - FlutterSetElementInfoOperationActions(elementInfoFromList, OTHER_WIDGET_NAME); + FlutterSetElementInfoOperationActions(elementInfoFromList, flutterNode); } else { - FlutterSetElementInfoOperationActions(elementInfoFromList, widgeType); + FlutterSetElementInfoOperationActions(elementInfoFromList, flutterNode); } // 设置当前节点的父节点id @@ -716,7 +678,6 @@ void OhosAccessibilityBridge::FlutterSetElementInfoProperties( int32_t childCount = flutterNode.childrenInTraversalOrder.size(); if (childCount > 0) { auto childrenIdsVec = flutterNode.childrenInTraversalOrder; - std::sort(childrenIdsVec.begin(), childrenIdsVec.end()); int64_t childNodeIds[childCount]; for (int32_t i = 0; i < childCount; i++) { childNodeIds[i] = static_cast(childrenIdsVec[i]); @@ -889,7 +850,8 @@ void OhosAccessibilityBridge::FlutterSetElementInfoProperties( OhosAccessibilityDDL::DLLoadSetElemStringFunc(ArkUIAccessibilityConstant::ARKUI_SET_A11Y_LEVEL); CHECK_DLL_NULL_PTR(OH_ArkUI_AccessibilityElementInfoSetAccessibilityLevel); ARKUI_ACCESSIBILITY_CALL_CHECK( - OH_ArkUI_AccessibilityElementInfoSetAccessibilityLevel(elementInfoFromList, "yes"); + OH_ArkUI_AccessibilityElementInfoSetAccessibilityLevel( + elementInfoFromList, componentTypeName != OTHER_WIDGET_NAME ? "yes" : "no"); ); // 无障碍组,设置为true时表示该组件及其所有子组件为一整个可以选中的组件,无障碍服务将不再关注其子组件内容。默认值:false auto OH_ArkUI_AccessibilityElementInfoSetAccessibilityGroup = @@ -914,19 +876,14 @@ std::vector OhosAccessibilityBridge::GetLevelOrderTraversalTree(int32_t semanticsQue.push(root); while (!semanticsQue.empty()) { - uint32_t queSize = semanticsQue.size(); - for (uint32_t i=0; i(currNode.id)); - - std::sort(currNode.childrenInTraversalOrder.begin(), - currNode.childrenInTraversalOrder.end()); + for (const auto& childId: currNode.childrenInTraversalOrder) { auto childNode = GetFlutterSemanticsNode(childId); semanticsQue.push(childNode); } - } } return levelOrderTraversalTree; } @@ -978,6 +935,8 @@ int32_t OhosAccessibilityBridge::FindAccessibilityNodeInfosById( << elementId << " mode=" << mode; CHECK_NULL_PTR_WITH_RET(elementList, FindAccessibilityNodeInfosById); + AccessibiltiyChangesWithXComponentId(); + if (g_flutterSemanticsTree.size() == 0) { FML_DLOG(INFO) << "FindAccessibilityNodeInfosById g_flutterSemanticsTree is null"; @@ -1045,23 +1004,13 @@ void OhosAccessibilityBridge::DispatchSemanticsAction( std::move(args)); } -/** - * flutter按钮节点双击跳转新页面时,发送页面更新事件 - */ -void OhosAccessibilityBridge::DoubleClickRouteToNewPage(SemanticsNodeExtent node) -{ - if (node.HasFlag(FLAGS_::kIsButton)) { - RequestFocusWhenPageUpdate(0); - } -} - /** * perform click action in accessibility status */ void OhosAccessibilityBridge::PerformClickAction( int64_t elementId, ArkUI_Accessibility_ActionType action, - SemanticsNodeExtent flutterNode) + const SemanticsNodeExtent& flutterNode) { /** Click event, sent after the UI component responds. 1 */ auto clickEventType = ArkUI_AccessibilityEventType::ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_CLICKED; @@ -1070,8 +1019,6 @@ void OhosAccessibilityBridge::PerformClickAction( << ")" << " event: click(" << clickEventType << ")"; auto flutterTapAction = ArkuiActionsToFlutterActions(action); DispatchSemanticsAction(static_cast(elementId), flutterTapAction, {}); - // double click at button-like node for pushing page update - DoubleClickRouteToNewPage(flutterNode); } /** @@ -1080,7 +1027,7 @@ void OhosAccessibilityBridge::PerformClickAction( void OhosAccessibilityBridge::PerformLongClickAction( int64_t elementId, ArkUI_Accessibility_ActionType action, - SemanticsNodeExtent flutterNode) + const SemanticsNodeExtent& flutterNode) { /** Long click event, sent after the UI component responds. 2 */ auto longClickEventType = ArkUI_AccessibilityEventType::ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_LONG_CLICKED; @@ -1098,7 +1045,7 @@ void OhosAccessibilityBridge::PerformLongClickAction( void OhosAccessibilityBridge::PerformGainFocusnAction( int64_t elementId, ArkUI_Accessibility_ActionType action, - SemanticsNodeExtent flutterNode) + const SemanticsNodeExtent& flutterNode) { // 感知获焦flutter节点 accessibilityFocusedNode = flutterNode; @@ -1127,7 +1074,7 @@ void OhosAccessibilityBridge::PerformGainFocusnAction( void OhosAccessibilityBridge::PerformClearFocusAction( int64_t elementId, ArkUI_Accessibility_ActionType action, - SemanticsNodeExtent flutterNode) + const SemanticsNodeExtent& flutterNode) { // 解析arkui的失焦 -> flutter对应节点的失焦 auto flutterLoseFocusAction = ArkuiActionsToFlutterActions(action); @@ -1148,7 +1095,7 @@ void OhosAccessibilityBridge::PerformClearFocusAction( void OhosAccessibilityBridge::PerformScrollUpAction( int64_t elementId, ArkUI_Accessibility_ActionType action, - SemanticsNodeExtent flutterNode) + SemanticsNodeExtent& flutterNode) { // flutter scroll forward with different situations if (flutterNode.HasAction(ACTIONS_::kScrollUp)) { @@ -1164,16 +1111,6 @@ void OhosAccessibilityBridge::PerformScrollUpAction( elementId, ArkUI_AccessibilityEventType::ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_SELECTED); DispatchSemanticsAction(static_cast(elementId), ACTIONS_::kIncrease, {}); } - std::string currComponetType = GetNodeComponentType(flutterNode); - if (OHOSUtils::Contains(currComponetType, SCROLL_WIDGET_NAME)) { - /** Scrolled event, sent when a scrollable component experiences a scroll event. 4096 */ - ArkUI_AccessibilityEventType scrollEventType1 = - ArkUI_AccessibilityEventType::ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_SCROLLED; - Flutter_SendAccessibilityAsyncEvent(elementId, scrollEventType1); - FML_DLOG(INFO) - << "ExecuteAccessibilityAction -> action: scroll forward(" << action - << ")" << " event: scroll forward(" << scrollEventType1 << ")"; - } } /** @@ -1182,7 +1119,7 @@ void OhosAccessibilityBridge::PerformScrollUpAction( void OhosAccessibilityBridge::PerformScrollDownAction( int64_t elementId, ArkUI_Accessibility_ActionType action, - SemanticsNodeExtent flutterNode) + SemanticsNodeExtent& flutterNode) { // flutter scroll down with different situations if (flutterNode.HasAction(ACTIONS_::kScrollDown)) { @@ -1198,24 +1135,13 @@ void OhosAccessibilityBridge::PerformScrollDownAction( elementId, ArkUI_AccessibilityEventType::ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_SELECTED); DispatchSemanticsAction(static_cast(elementId), ACTIONS_::kDecrease, {}); } - std::string currComponetType = GetNodeComponentType(flutterNode); - if (OHOSUtils::Contains(currComponetType, SCROLL_WIDGET_NAME)) { - /** Scrolled event, sent when a scrollable component experiences a - * scroll event. 4096 */ - ArkUI_AccessibilityEventType scrollEventType1 = - ArkUI_AccessibilityEventType::ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_SCROLLED; - Flutter_SendAccessibilityAsyncEvent(elementId, scrollEventType1); - FML_DLOG(INFO) - << "ExecuteAccessibilityAction -> action: scroll forward(" << action - << ")" << " event: scroll forward(" << scrollEventType1 << ")"; - } } /** * perform invalid action in accessibility status */ void OhosAccessibilityBridge::PerformClipboardAction( int64_t elementId, - ArkUI_Accessibility_ActionType action) + const ArkUI_Accessibility_ActionType& action) { if (action == ArkUI_Accessibility_ActionType::ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_COPY) { FML_DLOG(INFO) << "ExecuteAccessibilityAction -> action: copy(" << action << ")"; @@ -1234,7 +1160,7 @@ void OhosAccessibilityBridge::PerformClipboardAction( void OhosAccessibilityBridge::PerformInvalidAction( int64_t elementId, ArkUI_Accessibility_ActionType action, - SemanticsNodeExtent flutterNode) + const SemanticsNodeExtent& flutterNode) { /** Invalid event. 0 */ ArkUI_AccessibilityEventType invalidEventType = @@ -1248,7 +1174,7 @@ void OhosAccessibilityBridge::PerformInvalidAction( * 设置输入框文本 */ void OhosAccessibilityBridge::PerformSetText( - SemanticsNodeExtent flutterNode, + SemanticsNodeExtent& flutterNode, ArkUI_Accessibility_ActionType action, ArkUI_AccessibilityActionArguments* actionArguments) { @@ -1275,7 +1201,7 @@ void OhosAccessibilityBridge::PerformSetText( * perform select text (from base to extent) in accessibility status */ void OhosAccessibilityBridge::PerformSelectText( - SemanticsNodeExtent flutterNode, + const SemanticsNodeExtent& flutterNode, ArkUI_Accessibility_ActionType action, ArkUI_AccessibilityActionArguments* actionArguments) { @@ -1338,7 +1264,7 @@ void OhosAccessibilityBridge::PerformSetCursorPosition( * perform custom action in accessibility status */ void OhosAccessibilityBridge::PerformCustomAction( - SemanticsNodeExtent flutterNode, + const SemanticsNodeExtent& flutterNode, ArkUI_Accessibility_ActionType action, ArkUI_AccessibilityActionArguments* actionArguments) { @@ -1350,7 +1276,8 @@ void OhosAccessibilityBridge::PerformCustomAction( /** * perform show on screen action in accessibility status */ -void OhosAccessibilityBridge::PerformShowOnScreenAction(SemanticsNodeExtent flutterNode) +void OhosAccessibilityBridge::PerformShowOnScreenAction( + const SemanticsNodeExtent& flutterNode) { if (!IsNodeShowOnScreen(flutterNode)) { DispatchSemanticsAction(flutterNode.id, ACTIONS_::kShowOnScreen, {}); @@ -1370,6 +1297,8 @@ int32_t OhosAccessibilityBridge::ExecuteAccessibilityAction( << elementId << " action=" << action; CHECK_NULL_PTR_WITH_RET(actionArguments, ExecuteAccessibilityAction); + AccessibiltiyChangesWithXComponentId(); + // 获取当前elementid对应的flutter语义节点 auto flutterNode = GetFlutterSemanticsNode(static_cast(elementId)); if (!g_flutterSemanticsTree.count(flutterNode.id)) { @@ -1565,7 +1494,9 @@ void OhosAccessibilityBridge::Flutter_SendAccessibilityAnnounceEvent( ArkUI_AccessibilityEventType eventType) { if (OHOS_API_VERSION < 13) { return; } - provider_ = XComponentAdapter::GetInstance()->accessibilityProvider_; + AccessibiltiyChangesWithXComponentId(); + std::lock_guard lock(XComponentAdapter::GetInstance()->mutex_); + auto provider_ = XComponentAdapter::GetInstance()->GetAccessibilityProvider(xcomponentId_); CHECK_NULL_PTR_RET_VOID(provider_, Flutter_SendAccessibilityAnnounceEvent); // 创建并设置屏幕朗读事件 @@ -1614,7 +1545,9 @@ void OhosAccessibilityBridge::Flutter_SendAccessibilityAsyncEvent( ArkUI_AccessibilityEventType eventType) { if (OHOS_API_VERSION < 13) { return; } - provider_ = XComponentAdapter::GetInstance()->accessibilityProvider_; + AccessibiltiyChangesWithXComponentId(); + std::lock_guard lock(XComponentAdapter::GetInstance()->mutex_); + auto provider_ = XComponentAdapter::GetInstance()->GetAccessibilityProvider(xcomponentId_); CHECK_NULL_PTR_RET_VOID(provider_, Flutter_SendAccessibilityAsyncEvent); // 创建eventInfo对象 @@ -1733,14 +1666,16 @@ std::string OhosAccessibilityBridge::GetNodeComponentType( /** * 判断当前节点是否为textfield文本框 */ -bool OhosAccessibilityBridge::IsTextField(SemanticsNodeExtent flutterNode) +bool OhosAccessibilityBridge::IsTextField( + const SemanticsNodeExtent& flutterNode) { return flutterNode.HasFlag(FLAGS_::kIsTextField); } /** * 判断当前节点是否为滑动条slider类型 */ -bool OhosAccessibilityBridge::IsSlider(SemanticsNodeExtent flutterNode) +bool OhosAccessibilityBridge::IsSlider( + const SemanticsNodeExtent& flutterNode) { return flutterNode.HasFlag(FLAGS_::kIsSlider); } @@ -1748,7 +1683,7 @@ bool OhosAccessibilityBridge::IsSlider(SemanticsNodeExtent flutterNode) * 判断当前flutter节点组件是否可点击 */ bool OhosAccessibilityBridge::IsNodeClickable( - SemanticsNodeExtent flutterNode) + const SemanticsNodeExtent& flutterNode) { return flutterNode.HasAction(ACTIONS_::kTap); } @@ -1756,7 +1691,7 @@ bool OhosAccessibilityBridge::IsNodeClickable( * 判断当前flutter节点组件是否可显示 */ bool OhosAccessibilityBridge::IsNodeVisible( - SemanticsNodeExtent flutterNode) + const SemanticsNodeExtent& flutterNode) { return !flutterNode.HasFlag(FLAGS_::kIsHidden); } @@ -1764,7 +1699,7 @@ bool OhosAccessibilityBridge::IsNodeVisible( * 判断当前flutter节点组件是否具备checkable属性 */ bool OhosAccessibilityBridge::IsNodeCheckable( - SemanticsNodeExtent flutterNode) + const SemanticsNodeExtent& flutterNode) { return flutterNode.HasFlag(FLAGS_::kHasCheckedState) || flutterNode.HasFlag(FLAGS_::kHasToggledState); @@ -1773,7 +1708,7 @@ bool OhosAccessibilityBridge::IsNodeCheckable( * 判断当前flutter节点组件是否checked/unchecked(checkbox、radio button) */ bool OhosAccessibilityBridge::IsNodeChecked( - SemanticsNodeExtent flutterNode) + const SemanticsNodeExtent& flutterNode) { return flutterNode.HasFlag(FLAGS_::kIsChecked) || flutterNode.HasFlag(FLAGS_::kIsToggled); @@ -1782,7 +1717,7 @@ bool OhosAccessibilityBridge::IsNodeChecked( * 判断当前flutter节点组件是否选中 */ bool OhosAccessibilityBridge::IsNodeSelected( - SemanticsNodeExtent flutterNode) + const SemanticsNodeExtent& flutterNode) { return flutterNode.HasFlag(FLAGS_::kIsSelected); } @@ -1790,7 +1725,7 @@ bool OhosAccessibilityBridge::IsNodeSelected( * 判断当前flutter节点组件是否为密码输入框 */ bool OhosAccessibilityBridge::IsNodePassword( - SemanticsNodeExtent flutterNode) + const SemanticsNodeExtent& flutterNode) { return flutterNode.HasFlag(FLAGS_::kIsTextField) && flutterNode.HasFlag(FLAGS_::kIsObscured); @@ -1799,7 +1734,7 @@ bool OhosAccessibilityBridge::IsNodePassword( * 判断当前flutter节点组件是否支持长按功能 */ bool OhosAccessibilityBridge::IsNodeHasLongPress( - SemanticsNodeExtent flutterNode) + const SemanticsNodeExtent& flutterNode) { return flutterNode.HasAction(ACTIONS_::kLongPress); } @@ -1807,7 +1742,7 @@ bool OhosAccessibilityBridge::IsNodeHasLongPress( * 判断当前flutter节点是否enabled */ bool OhosAccessibilityBridge::IsNodeEnabled( - SemanticsNodeExtent flutterNode) + const SemanticsNodeExtent& flutterNode) { return !flutterNode.HasFlag(FLAGS_::kHasEnabledState) || flutterNode.HasFlag(FLAGS_::kIsEnabled); @@ -1815,7 +1750,8 @@ bool OhosAccessibilityBridge::IsNodeEnabled( /** * 判断当前flutter节点是否在当前屏幕上显示 */ -bool OhosAccessibilityBridge::IsNodeShowOnScreen(SemanticsNodeExtent flutterNode) +bool OhosAccessibilityBridge::IsNodeShowOnScreen( + const SemanticsNodeExtent& flutterNode) { return flutterNode.HasAction(ACTIONS_::kShowOnScreen); } @@ -1857,10 +1793,6 @@ bool OhosAccessibilityBridge::IsNodeFocusable( if (node.IsPlatformViewNode()) { return true; } - // Always consider actionable nodes focusable. - if (node.actions != 0) { - return true; - } if ((node.flags & FOCUSABLE_FLAGS) != 0) { return true; } @@ -1881,7 +1813,7 @@ bool OhosAccessibilityBridge::IsNodeFocused(const SemanticsNodeExtent& flutterNo * 判断是否可滑动 */ bool OhosAccessibilityBridge::IsNodeScrollable( - SemanticsNodeExtent flutterNode) + const SemanticsNodeExtent& flutterNode) { return flutterNode.HasAction(ACTIONS_::kScrollLeft) || flutterNode.HasAction(ACTIONS_::kScrollRight) || @@ -1892,72 +1824,11 @@ bool OhosAccessibilityBridge::IsNodeScrollable( * 判断当前节点组件是否是滑动组件,如: listview, gridview等 */ bool OhosAccessibilityBridge::IsScrollableWidget( - SemanticsNodeExtent flutterNode) + const SemanticsNodeExtent& flutterNode) { return flutterNode.HasFlag(FLAGS_::kHasImplicitScrolling); } -void OhosAccessibilityBridge::AddRouteNodes( - std::vector edges, - SemanticsNodeExtent node) -{ - if (node.HasFlag(FLAGS_::kScopesRoute)) { - edges.emplace_back(node); - } - for (auto& childNodeId : node.childrenInTraversalOrder) { - auto childNode = GetFlutterSemanticsNode(childNodeId); - AddRouteNodes(edges, childNode); - } -} - -std::string OhosAccessibilityBridge::GetRouteName(SemanticsNodeExtent node) -{ - if (node.HasFlag(FLAGS_::kNamesRoute) && !node.label.empty()) { - return node.label; - } - for (auto& childNodeId : node.childrenInTraversalOrder) { - auto childNode = GetFlutterSemanticsNode(childNodeId); - std::string newName = GetRouteName(childNode); - if (!newName.empty()) { - return newName; - } - } - return ""; -} - -void OhosAccessibilityBridge::OnWindowNameChange(SemanticsNodeExtent route) -{ - std::string routeName = GetRouteName(route); - if (routeName.empty()) { - routeName = " "; - } - Flutter_SendAccessibilityAsyncEvent( - static_cast(route.id), - ArkUI_AccessibilityEventType:: - ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_PAGE_CONTENT_UPDATE); -} - -void OhosAccessibilityBridge::RemoveSemanticsNode( - SemanticsNodeExtent nodeToBeRemoved) -{ - if (!g_flutterSemanticsTree.size()) { - FML_DLOG(ERROR) << "OhosAccessibilityBridge::removeSemanticsNode -> " - "g_flutterSemanticsTree.szie()=0"; - return; - } - if (g_flutterSemanticsTree.find(nodeToBeRemoved.id) == - g_flutterSemanticsTree.end()) { - FML_DLOG(INFO) << "Attempted to remove a node that is not in the tree."; - } - int32_t nodeToBeRemovedParentId = GetParentId(nodeToBeRemoved.id); - for (auto it = g_parentChildIdVec.begin(); it != g_parentChildIdVec.end(); it++) { - if (it->first == nodeToBeRemovedParentId && - it->second == nodeToBeRemoved.id) { - g_parentChildIdVec.erase(it); - } - } -} - /** * when the system accessibility service is shut down, * clear all the flutter semantics-relevant caches like maps, vectors @@ -1965,8 +1836,10 @@ void OhosAccessibilityBridge::RemoveSemanticsNode( void OhosAccessibilityBridge::ClearFlutterSemanticsCaches() { g_flutterSemanticsTree.clear(); - g_parentChildIdVec.clear(); - g_screenRectMap.clear(); + Flutter_SendAccessibilityAsyncEvent( + accessibilityFocusedNode.id, + ArkUI_AccessibilityEventType::ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_ACCESSIBILITY_FOCUS_CLEARED); + accessibilityFocusedNode = {}; } /** @@ -1977,9 +1850,10 @@ SemanticsNodeExtent OhosAccessibilityBridge::UpdatetSemanticsNodeExtent( { SemanticsNodeExtent nodeEx = SemanticsNodeExtent(); // 获取更新前的flutter节点信息 - if (g_flutterSemanticsTree.size() > 0) { + if (!g_flutterSemanticsTree.size()) { auto prevNode = GetFlutterSemanticsNode(node.id); nodeEx.hadPreviousConfig = true; + nodeEx.parentId = prevNode.parentId; nodeEx.previousFlags = prevNode.flags; nodeEx.previousActions = prevNode.actions; nodeEx.previousTextSelectionBase = prevNode.textSelectionBase; @@ -1990,35 +1864,35 @@ SemanticsNodeExtent OhosAccessibilityBridge::UpdatetSemanticsNodeExtent( nodeEx.previousValue = prevNode.value; nodeEx.previousLabel = prevNode.label; } + // 更新当前flutter节点信息 - nodeEx.isNull = false; - nodeEx.id = std::move(node.id); - nodeEx.flags = std::move(node.flags); - nodeEx.actions = std::move(node.actions); - nodeEx.maxValueLength = std::move(node.maxValueLength); - nodeEx.currentValueLength = std::move(node.currentValueLength); - nodeEx.textSelectionBase = std::move(node.textSelectionBase); - nodeEx.textSelectionExtent = std::move(node.textSelectionExtent); - nodeEx.platformViewId = std::move(node.platformViewId); - nodeEx.scrollChildren = std::move(node.scrollChildren); - nodeEx.scrollIndex = std::move(node.scrollIndex); - nodeEx.scrollPosition = std::move(node.scrollPosition); - nodeEx.scrollExtentMax = std::move(node.scrollExtentMax); - nodeEx.scrollExtentMin = std::move(node.scrollExtentMin); - nodeEx.elevation = std::move(node.elevation); - nodeEx.thickness = std::move(node.thickness); - nodeEx.label = std::move(node.label); + nodeEx.id = node.id; + nodeEx.flags = node.flags; + nodeEx.actions = node.actions; + nodeEx.maxValueLength = node.maxValueLength; + nodeEx.currentValueLength = node.currentValueLength; + nodeEx.textSelectionBase = node.textSelectionBase; + nodeEx.textSelectionExtent = node.textSelectionExtent; + nodeEx.platformViewId = node.platformViewId; + nodeEx.scrollChildren = node.scrollChildren; + nodeEx.scrollIndex = node.scrollIndex; + nodeEx.scrollPosition = node.scrollPosition; + nodeEx.scrollExtentMax = node.scrollExtentMax; + nodeEx.scrollExtentMin = node.scrollExtentMin; + nodeEx.elevation = node.elevation; + nodeEx.thickness = node.thickness; + nodeEx.label = node.label; nodeEx.labelAttributes = std::move(node.labelAttributes); - nodeEx.hint = std::move(node.hint); + nodeEx.hint = node.hint; nodeEx.hintAttributes = std::move(node.hintAttributes); - nodeEx.value = std::move(node.value); + nodeEx.value = node.value; nodeEx.valueAttributes = std::move(node.valueAttributes); - nodeEx.increasedValue = std::move(node.increasedValue); + nodeEx.increasedValue = node.increasedValue; nodeEx.increasedValueAttributes = std::move(node.increasedValueAttributes); - nodeEx.decreasedValue = std::move(node.decreasedValue); + nodeEx.decreasedValue = node.decreasedValue; nodeEx.decreasedValueAttributes = std::move(node.decreasedValueAttributes); - nodeEx.tooltip = std::move(node.tooltip); - nodeEx.textDirection = std::move(node.textDirection); + nodeEx.tooltip = node.tooltip; + nodeEx.textDirection = node.textDirection; nodeEx.rect = std::move(node.rect); nodeEx.transform = std::move(node.transform); nodeEx.childrenInTraversalOrder = std::move(node.childrenInTraversalOrder); @@ -2028,11 +1902,12 @@ SemanticsNodeExtent OhosAccessibilityBridge::UpdatetSemanticsNodeExtent( } void OhosAccessibilityBridge::GetSemanticsNodeDebugInfo( - SemanticsNodeExtent node) + const SemanticsNodeExtent& node) { FML_DLOG(INFO) << "-------------------SemanticsNode------------------"; SkMatrix _transform = node.transform.asM33(); FML_DLOG(INFO) << "node.id=" << node.id; + FML_DLOG(INFO) << "node.parentId=" << node.parentId; FML_DLOG(INFO) << "node.label=" << node.label; FML_DLOG(INFO) << "node.previousLabel=" << node.previousLabel; FML_DLOG(INFO) << "node.tooltip=" << node.tooltip; @@ -2105,7 +1980,7 @@ void OhosAccessibilityBridge::GetSemanticsNodeDebugInfo( } void OhosAccessibilityBridge::GetSemanticsFlagsDebugInfo( - SemanticsNodeExtent node) + const SemanticsNodeExtent& node) { FML_DLOG(INFO) << "----------------SemanticsFlags-------------------------"; FML_DLOG(INFO) << "node.id=" << node.id; @@ -2146,7 +2021,7 @@ void OhosAccessibilityBridge::GetSemanticsFlagsDebugInfo( } void OhosAccessibilityBridge::GetCustomActionDebugInfo( - flutter::CustomAccessibilityAction customAccessibilityAction) + const flutter::CustomAccessibilityAction& customAccessibilityAction) { FML_DLOG(INFO) << "--------------CustomAccessibilityAction------------"; FML_DLOG(INFO) << "customAccessibilityAction.id=" @@ -2167,10 +2042,6 @@ void OhosAccessibilityBridge::GetSemanticsDebugInfo() FML_DLOG(INFO) << "g_flutterSemanticsTree -> {" << item.first << ", " << item.second.id << "}"; } - for (const auto& item : g_parentChildIdVec) { - FML_DLOG(INFO) << "g_parentChildIdVec -> (" << item.first << ", " - << item.second << ")"; - } //打印按层次遍历排序的flutter语义树节点id数组 std::vector levelOrderTraversalTree = GetLevelOrderTraversalTree(0); for (const auto& item: levelOrderTraversalTree) { diff --git a/shell/platform/ohos/accessibility/ohos_accessibility_bridge.h b/shell/platform/ohos/accessibility/ohos_accessibility_bridge.h index f4e03364ce919ed2d80743e5c48c986d4257616e..0dde97c7c1b83071e8c7f565f0a783a1467f21ca 100644 --- a/shell/platform/ohos/accessibility/ohos_accessibility_bridge.h +++ b/shell/platform/ohos/accessibility/ohos_accessibility_bridge.h @@ -30,6 +30,9 @@ #include "ohos_accessibility_ddl.h" #include "flutter/shell/platform/ohos/utils/ohos_utils.h" #include "flutter/shell/platform/ohos/utils/arkui_accessibility_constant.h" +#include "third_party/skia/include/core/SkMatrix.h" +#include "third_party/skia/include/core/SkScalar.h" +#include "third_party/skia/include/core/SkPoint.h" namespace flutter { typedef flutter::SemanticsFlags FLAGS_; @@ -43,9 +46,14 @@ struct AbsoluteRect { static constexpr AbsoluteRect MakeEmpty() { return AbsoluteRect{0.0, 0.0, 0.0, 0.0}; } + static constexpr AbsoluteRect MakeRect( + float left, float top, float right, float bottom) { + return AbsoluteRect{left, top, right, bottom}; + } + }; struct SemanticsNodeExtent : flutter::SemanticsNode { - bool isNull = false; + SkM44 globalTransform = SkM44{}; AbsoluteRect absoluteRect = AbsoluteRect::MakeEmpty(); int32_t parentId = -1; bool hadPreviousConfig = false; @@ -59,14 +67,20 @@ struct SemanticsNodeExtent : flutter::SemanticsNode { double previousScrollExtentMin = std::nan(""); std::string previousValue; std::string previousLabel; - bool HasPrevAction(SemanticsAction action) const - { + bool HasPrevAction(SemanticsAction action) const { return (previousActions & this->actions) != 0; } - bool HasPrevFlag(SemanticsFlags flag) const - { + bool HasPrevFlag(SemanticsFlags flag) const { return (previousFlags & this->flags) != 0; } + bool operator==(const SemanticsNodeExtent& other) const { + return id == other.id; + } + struct Hash { + std::size_t operator()(const SemanticsNodeExtent& obj) const { + return std::hash()(obj.id); + } + }; }; /** @@ -78,13 +92,15 @@ public: OhosAccessibilityBridge(const OhosAccessibilityBridge&) = delete; OhosAccessibilityBridge& operator=(const OhosAccessibilityBridge&) = delete; - bool isFlutterNavigated_; + std::string xcomponentId_; int64_t native_shell_holder_id_; - ArkUI_AccessibilityProvider* provider_; + bool isFlutterNavigated_; + bool isTouchGuideOn_; - void OnOhosAccessibilityStateChange(bool ohosAccessibilityEnabled, int64_t shellholderId); + std::unordered_map g_flutterSemanticsTree; + std::unordered_map> g_flutterSemanticsTreeXComponents; - void SetNativeShellHolderId(int64_t id); + void OnOhosAccessibilityStateChange(bool ohosAccessibilityEnabled, int64_t shellholderId); void UpdateSemantics(flutter::SemanticsNodeUpdates update, flutter::CustomAccessibilityActionUpdates actions); @@ -138,8 +154,8 @@ public: std::unique_ptr& message, ArkUI_AccessibilityEventType eventType); - void FlutterRelativeRectToScreenRect(SemanticsNodeExtent node); - AbsoluteRect GetAbsoluteScreenRect(SemanticsNodeExtent& flutterNode); + void RelativeRectToScreenRect(SemanticsNodeExtent& node); + AbsoluteRect GetAbsoluteScreenRect(const SemanticsNodeExtent& flutterNode); void SetAbsoluteScreenRect(SemanticsNodeExtent& flutterNode, float left, float top, @@ -161,10 +177,6 @@ private: std::shared_ptr nativeAccessibilityChannel_; std::shared_ptr accessibilityFeatures_; - std::unordered_map g_flutterSemanticsTree; - std::vector> g_parentChildIdVec; - std::unordered_map g_screenRectMap; - SemanticsNodeExtent inputFocusedNode; SemanticsNodeExtent lastInputFocusedNode; SemanticsNodeExtent accessibilityFocusedNode; @@ -195,23 +207,6 @@ private: const std::string SWITCH_WIDGET_NAME = "Toggle"; const std::string SEEKBAR_WIDGET_NAME = "SeekBar"; - const std::map - ArkUI_ACTION_TYPE_MAP_ = { - {"invalid", ArkUI_Accessibility_ActionType::ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_INVALID}, - {"click", ArkUI_Accessibility_ActionType::ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_CLICK}, - {"long press", ArkUI_Accessibility_ActionType::ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_LONG_CLICK}, - {"focus acquisition", ArkUI_Accessibility_ActionType::ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_GAIN_ACCESSIBILITY_FOCUS}, - {"focus clearance", ArkUI_Accessibility_ActionType::ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_CLEAR_ACCESSIBILITY_FOCUS}, - {"forward scroll", ArkUI_Accessibility_ActionType::ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SCROLL_FORWARD}, - {"backward scroll", ArkUI_Accessibility_ActionType::ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SCROLL_BACKWARD}, - {"copy text", ArkUI_Accessibility_ActionType::ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_COPY}, - {"paste text", ArkUI_Accessibility_ActionType::ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_PASTE}, - {"cut text", ArkUI_Accessibility_ActionType::ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_CUT}, - {"text selection", ArkUI_Accessibility_ActionType::ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SELECT_TEXT}, - {"set text", ArkUI_Accessibility_ActionType::ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SET_TEXT}, - {"text cursor position setting", ArkUI_Accessibility_ActionType::ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SET_CURSOR_POSITION}, - }; - static const int32_t FOCUSABLE_FLAGS = static_cast(FLAGS_::kHasCheckedState) | static_cast(FLAGS_::kIsChecked) | @@ -238,7 +233,7 @@ private: int64_t elementId); void FlutterSetElementInfoOperationActions( ArkUI_AccessibilityElementInfo* elementInfoFromList, - std::string widget_type); + const SemanticsNodeExtent& node); void BuildArkUISemanticsTree( int64_t elementId, ArkUI_AccessibilityElementInfo* elementInfoFromList, @@ -250,134 +245,75 @@ private: flutter::SemanticsAction ArkuiActionsToFlutterActions( ArkUI_Accessibility_ActionType arkui_action); + void ComputeGlobalTransformAndParentId(); + void ConvertRectToGlobal(SemanticsNodeExtent& node); + SkPoint ApplyTransform(SkPoint& point, const SkM44& transform); + bool HasScrolled(const SemanticsNodeExtent& flutterNode); bool HasChangedLabel(const SemanticsNodeExtent& flutterNode); bool IsNodeFocusable(const SemanticsNodeExtent& flutterNode); bool IsNodeFocused(const SemanticsNodeExtent& flutterNode); - bool IsNodeCheckable(SemanticsNodeExtent flutterNode); - bool IsNodeChecked(SemanticsNodeExtent flutterNode); - bool IsNodeSelected(SemanticsNodeExtent flutterNode); - bool IsNodeClickable(SemanticsNodeExtent flutterNode); - bool IsNodeScrollable(SemanticsNodeExtent flutterNode); - bool IsNodePassword(SemanticsNodeExtent flutterNode); - bool IsNodeVisible(SemanticsNodeExtent flutterNode); - bool IsNodeEnabled(SemanticsNodeExtent flutterNode); - bool IsNodeHasLongPress(SemanticsNodeExtent flutterNode); - bool IsNodeShowOnScreen(SemanticsNodeExtent flutterNode); - - bool IsTextField(SemanticsNodeExtent flutterNode); - bool IsSlider(SemanticsNodeExtent flutterNode); - bool IsScrollableWidget(SemanticsNodeExtent flutterNode); + bool IsNodeCheckable(const SemanticsNodeExtent& flutterNode); + bool IsNodeChecked(const SemanticsNodeExtent& flutterNode); + bool IsNodeSelected(const SemanticsNodeExtent& flutterNode); + bool IsNodeClickable(const SemanticsNodeExtent& flutterNode); + bool IsNodeScrollable(const SemanticsNodeExtent& flutterNode); + bool IsNodePassword(const SemanticsNodeExtent& flutterNode); + bool IsNodeVisible(const SemanticsNodeExtent& flutterNode); + bool IsNodeEnabled(const SemanticsNodeExtent& flutterNode); + bool IsNodeHasLongPress(const SemanticsNodeExtent& flutterNode); + bool IsNodeShowOnScreen(const SemanticsNodeExtent& flutterNode); + bool IsTextField(const SemanticsNodeExtent& flutterNode); + bool IsSlider(const SemanticsNodeExtent& flutterNode); + bool IsScrollableWidget(const SemanticsNodeExtent& flutterNode); void PerformClickAction(int64_t elementId, ArkUI_Accessibility_ActionType action, - SemanticsNodeExtent flutterNode); + const SemanticsNodeExtent& flutterNode); void PerformLongClickAction(int64_t elementId, ArkUI_Accessibility_ActionType action, - SemanticsNodeExtent flutterNode); + const SemanticsNodeExtent& flutterNode); void PerformGainFocusnAction(int64_t elementId, ArkUI_Accessibility_ActionType action, - SemanticsNodeExtent flutterNode); + const SemanticsNodeExtent& flutterNode); void PerformClearFocusAction(int64_t elementId, ArkUI_Accessibility_ActionType action, - SemanticsNodeExtent flutterNode); + const SemanticsNodeExtent& flutterNode); void PerformScrollUpAction(int64_t elementId, ArkUI_Accessibility_ActionType action, - SemanticsNodeExtent flutterNode); + SemanticsNodeExtent& flutterNode); void PerformScrollDownAction(int64_t elementId, ArkUI_Accessibility_ActionType action, - SemanticsNodeExtent flutterNode); + SemanticsNodeExtent& flutterNode); void PerformClipboardAction(int64_t elementId, - ArkUI_Accessibility_ActionType action); + const ArkUI_Accessibility_ActionType& action); void PerformInvalidAction(int64_t elementId, ArkUI_Accessibility_ActionType action, - SemanticsNodeExtent flutterNode); - void PerformSetText(SemanticsNodeExtent flutterNode, + const SemanticsNodeExtent& flutterNode); + void PerformSetText(SemanticsNodeExtent& flutterNode, ArkUI_Accessibility_ActionType action, ArkUI_AccessibilityActionArguments* actionArguments); - void PerformSelectText(SemanticsNodeExtent flutterNode, + void PerformSelectText(const SemanticsNodeExtent& flutterNode, ArkUI_Accessibility_ActionType action, ArkUI_AccessibilityActionArguments* actionArguments); void PerformSetCursorPosition(SemanticsNodeExtent flutterNode, ArkUI_Accessibility_ActionType action, ArkUI_AccessibilityActionArguments* actionArguments); - void PerformCustomAction(SemanticsNodeExtent flutterNode, + void PerformCustomAction(const SemanticsNodeExtent& flutterNode, ArkUI_Accessibility_ActionType action, ArkUI_AccessibilityActionArguments* actionArguments); - void PerformShowOnScreenAction(SemanticsNodeExtent flutterNode); + void PerformShowOnScreenAction(const SemanticsNodeExtent& flutterNode); - void AddRouteNodes(std::vector edges, - SemanticsNodeExtent node); - std::string GetRouteName(SemanticsNodeExtent node); - void OnWindowNameChange(SemanticsNodeExtent route); - void RemoveSemanticsNode(SemanticsNodeExtent nodeToBeRemoved); - - void GetSemanticsNodeDebugInfo(SemanticsNodeExtent node); - void GetSemanticsFlagsDebugInfo(SemanticsNodeExtent node); + void GetSemanticsNodeDebugInfo(const SemanticsNodeExtent& node); + void GetSemanticsFlagsDebugInfo(const SemanticsNodeExtent& node); void GetCustomActionDebugInfo( - flutter::CustomAccessibilityAction customAccessibilityAction); + const flutter::CustomAccessibilityAction& customAccessibilityAction); void RequestFocusWhenPageUpdate(int32_t requestFocusId); - bool Contains(const std::string source, const std::string target); - std::pair GetRealScaleFactor(); - void DoubleClickRouteToNewPage(SemanticsNodeExtent node); void GetSemanticsDebugInfo(); void AccessibiltiyChangesWithXComponentId(); }; -enum class AccessibilityAction : int32_t { - kTap = 1 << 0, - kLongPress = 1 << 1, - kScrollLeft = 1 << 2, - kScrollRight = 1 << 3, - kScrollUp = 1 << 4, - kScrollDown = 1 << 5, - kIncrease = 1 << 6, - kDecrease = 1 << 7, - kShowOnScreen = 1 << 8, - kMoveCursorForwardByCharacter = 1 << 9, - kMoveCursorBackwardByCharacter = 1 << 10, - kSetSelection = 1 << 11, - kCopy = 1 << 12, - kCut = 1 << 13, - kPaste = 1 << 14, - kDidGainAccessibilityFocus = 1 << 15, - kDidLoseAccessibilityFocus = 1 << 16, - kCustomAction = 1 << 17, - kDismiss = 1 << 18, - kMoveCursorForwardByWord = 1 << 19, - kMoveCursorBackwardByWord = 1 << 20, - kSetText = 1 << 21, -}; -enum class AccessibilityFlags : int32_t { - kHasCheckedState = 1 << 0, - kIsChecked = 1 << 1, - kIsSelected = 1 << 2, - kIsButton = 1 << 3, - kIsTextField = 1 << 4, - kIsFocused = 1 << 5, - kHasEnabledState = 1 << 6, - kIsEnabled = 1 << 7, - kIsInMutuallyExclusiveGroup = 1 << 8, - kIsHeader = 1 << 9, - kIsObscured = 1 << 10, - kScopesRoute = 1 << 11, - kNamesRoute = 1 << 12, - kIsHidden = 1 << 13, - kIsImage = 1 << 14, - kIsLiveRegion = 1 << 15, - kHasToggledState = 1 << 16, - kIsToggled = 1 << 17, - kHasImplicitScrolling = 1 << 18, - kIsMultiline = 1 << 19, - kIsReadOnly = 1 << 20, - kIsFocusable = 1 << 21, - kIsLink = 1 << 22, - kIsSlider = 1 << 23, - kIsKeyboardKey = 1 << 24, - kIsCheckStateMixed = 1 << 25, -}; - } // namespace flutter #endif // OHOS_ACCESSIBILITY_BRIDGE_H diff --git a/shell/platform/ohos/accessibility/ohos_accessibility_manager.cpp b/shell/platform/ohos/accessibility/ohos_accessibility_manager.cpp deleted file mode 100644 index e9b1499f702b7dd1a5adcb535340ca0d9b280b2c..0000000000000000000000000000000000000000 --- a/shell/platform/ohos/accessibility/ohos_accessibility_manager.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2024 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "ohos_accessibility_manager.h" - -namespace flutter { - -OhosAccessibilityManager::OhosAccessibilityManager() {} - -OhosAccessibilityManager::~OhosAccessibilityManager() {} - -/** - * 监听ohos平台是否开启无障碍屏幕朗读功能 - */ -void OhosAccessibilityManager::OnAccessibilityStateChanged( - bool ohosAccessibilityEnabled) {} - -void OhosAccessibilityManager::SetOhosAccessibilityEnabled(bool isEnabled) -{ - this->isOhosAccessibilityEnabled_ = isEnabled; -} - -bool OhosAccessibilityManager::GetOhosAccessibilityEnabled() -{ - return this->isOhosAccessibilityEnabled_; -} - -} // namespace flutter diff --git a/shell/platform/ohos/accessibility/ohos_accessibility_manager.h b/shell/platform/ohos/accessibility/ohos_accessibility_manager.h deleted file mode 100644 index e3c873fe8c3113875d1ae2a6028c5507b899c36c..0000000000000000000000000000000000000000 --- a/shell/platform/ohos/accessibility/ohos_accessibility_manager.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2024 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef OHOS_ACCESSIBILITY_MANAGER_H -#define OHOS_ACCESSIBILITY_MANAGER_H -#include - -namespace flutter { -/** - * 无障碍辅助管理类 - */ -class OhosAccessibilityManager { - public: - OhosAccessibilityManager(); - ~OhosAccessibilityManager(); - - void OnAccessibilityStateChanged(bool ohosAccessibilityEnabled); - - bool GetOhosAccessibilityEnabled(); - - void SetOhosAccessibilityEnabled(bool isEnabled); - - private: - bool isOhosAccessibilityEnabled_; -}; - -} // namespace flutter - -#endif \ No newline at end of file diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/cpp/types/libflutter/index.d.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/cpp/types/libflutter/index.d.ets index 279c97baa1ad512f3fa9f31e28a4050901c05468..4185733a255cc574c70471fc81a75cf6194b5426 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/cpp/types/libflutter/index.d.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/cpp/types/libflutter/index.d.ets @@ -175,4 +175,10 @@ export const nativeUnicodeIsEmojiModifierBase: (code: number) => number; export const nativeUnicodeIsVariationSelector: (code: number) => number; -export const nativeUnicodeIsRegionalIndicatorSymbol: (code: number) => number; \ No newline at end of file +export const nativeUnicodeIsRegionalIndicatorSymbol: (code: number) => number; + +export const nativeGetXComponentId: (id: string) => number; + +export const nativeSetDVsyncSwitch: (nativeShellHolderId: number, isEnable: boolean) => void; + +export const nativeTouchGuideStateChange: (state: Boolean) => void; diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterNapi.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterNapi.ets index e36a5666822d8dfde8b31ffe32e10d7ac7b3729a..b9f5cb3398827f9f425a0c52771abb4c0f359f61 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterNapi.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/engine/FlutterNapi.ets @@ -391,6 +391,11 @@ export default class FlutterNapi { flutter.nativeAccessibilityStateChange(this.nativeShellHolderId!, state); } + touchGuideStateChange(state: Boolean): void { + this.ensureRunningOnMainThread(); + flutter.nativeTouchGuideStateChange(state); + } + setLocalizationPlugin(localizationPlugin: LocalizationPlugin | null): void { this.localizationPlugin = localizationPlugin; } diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterPage.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterPage.ets index 533e1e6a510b7b6b002088b4121dfa5f5a2d09b1..ead59a58d1b0ae5136012a6d24cec0f92e62e3db 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterPage.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/embedding/ohos/FlutterPage.ets @@ -17,7 +17,7 @@ import Log from '../../util/Log'; import { FlutterView } from '../../view/FlutterView'; import FlutterManager from './FlutterManager'; import { DVModel, DVModelChildren, DynamicView } from '../../view/DynamicView/dynamicView'; - +import flutter from 'libflutter.so'; const TAG = "FlutterPage"; export const OHOS_FLUTTER_PAGE_UPDATE = "ohos_flutter_page_update"; @@ -56,7 +56,20 @@ export struct FlutterPage { .onLoad((context) => { this.flutterView?.onSurfaceCreated() Log.d(TAG, "XComponent onLoad "); - this.flutterView?.onAccessibilityIsOpen() + // 当xcomponent窗口部分显示或完全隐藏时触发回调 + this.getUIContext()?.getAttachedFrameNodeById(this.viewId)?.commonEvent.setOnVisibleAreaApproximateChange( + { ratios: [0.0, 1.0], expectedUpdateInterval: 0 }, + (ratioInc: boolean, ratio: number) => { + if (ratioInc) { + Log.i(TAG, "setOnVisibleAreaApproximateChange -> xcomponentId: " + this.viewId + + " ratioInc: " + ratioInc + " ratio: " + ratio); + flutter.nativeGetXComponentId(this.viewId); + // 保证获取xcomponentid之后再使用无障碍 + this.flutterView?.onTouchGuideIsOpen(); + this.flutterView?.onAccessibilityIsOpen(); + } + } + ) }) .onDestroy(() => { Log.d(TAG, "XComponent onDestroy "); @@ -136,7 +149,20 @@ export struct FlutterPage { .onLoad((context) => { this.flutterView?.onSurfaceCreated() Log.d(TAG, "XComponent onLoad "); - this.flutterView?.onAccessibilityIsOpen() + // 当xcomponent窗口部分显示或完全隐藏时触发回调 + this.getUIContext()?.getAttachedFrameNodeById(this.viewId)?.commonEvent.setOnVisibleAreaApproximateChange( + { ratios: [0.0, 1.0], expectedUpdateInterval: 0 }, + (ratioInc: boolean, ratio: number) => { + if (ratioInc) { + Log.i(TAG, "setOnVisibleAreaApproximateChange -> xcomponentId: " + this.viewId + + " ratioInc: " + ratioInc + " ratio: " + ratio); + flutter.nativeGetXComponentId(this.viewId); + // 保证获取xcomponentid之后再使用无障碍 + this.flutterView?.onTouchGuideIsOpen(); + this.flutterView?.onAccessibilityIsOpen(); + } + } + ) }) .onDestroy(() => { Log.d(TAG, "XComponent onDestroy "); diff --git a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/FlutterView.ets b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/FlutterView.ets index c38327f4fbf6261bcf4236c81a9c3d3c7d6d3a5c..11d7a008336e0fd1272be89e91e4c0aa6aa6a5bc 100644 --- a/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/FlutterView.ets +++ b/shell/platform/ohos/flutter_embedding/flutter/src/main/ets/view/FlutterView.ets @@ -214,6 +214,12 @@ export class FlutterView { this.mainWindow?.on('avoidAreaChange', this.avoidAreaChangeCallback); this.mainWindow?.on('windowStatusChange', this.windowStatusChangeCallback); + // listen the touchGuideStateChange, when screen reader is on that + // the callback data of this lister event is true + accessibility.on('touchGuideStateChange', (data: boolean) => { + this.flutterEngine?.getFlutterNapi()?.touchGuideStateChange(data); + console.info(`subscribe touch guide state change, result: ${JSON.stringify(data)}`); + }); //监听系统无障碍服务状态改变 accessibility.on('accessibilityStateChange', (data: boolean) => { Log.i(TAG, `subscribe accessibility state change, result: ${JSON.stringify(data)}`); @@ -377,6 +383,9 @@ export class FlutterView { this.mainWindow?.off('windowSizeChange', this.windowSizeChangeCallback); this.mainWindow?.off('avoidAreaChange', this.avoidAreaChangeCallback); this.mainWindow?.off('windowStatusChange', this.windowStatusChangeCallback); + accessibility.off('touchGuideStateChange', (data: boolean) => { + Log.i(TAG, `subscribe touch guide state change, result: ${JSON.stringify(data)}`); + }); accessibility.off('accessibilityStateChange', (data: boolean) => { Log.i(TAG, `unsubscribe accessibility state change, result: ${JSON.stringify(data)}`); }); @@ -500,6 +509,14 @@ export class FlutterView { Log.i(TAG, `accessibility isOpen state -> ${JSON.stringify(accessibilityState)}`); } + onTouchGuideIsOpen() { + let touchGuideState: boolean = accessibility.isOpenTouchGuideSync(); + if(touchGuideState) { + this.flutterEngine?.getFlutterNapi()?.touchGuideStateChange(touchGuideState) + } + Log.i(TAG, `Touch Guide isOpen state -> ${JSON.stringify(touchGuideState)}`); + } + onAreaChange(newArea: Area | null, setFullScreen: boolean = false) { const originalMetrics = this.viewportMetrics.clone(); diff --git a/shell/platform/ohos/library_loader.cpp b/shell/platform/ohos/library_loader.cpp index 7b2c7da1c04a7232d100042ab23a770b07400797..ac5020abfa81982a6975969b3cd7c64a232d1866 100644 --- a/shell/platform/ohos/library_loader.cpp +++ b/shell/platform/ohos/library_loader.cpp @@ -190,6 +190,12 @@ static napi_value Init(napi_env env, napi_value exports) { DECLARE_NAPI_FUNCTION( "nativeUnicodeIsRegionalIndicatorSymbol", flutter::PlatformViewOHOSNapi::nativeUnicodeIsRegionalIndicatorSymbol), + DECLARE_NAPI_FUNCTION( + "nativeGetXComponentId", + flutter::PlatformViewOHOSNapi::nativeGetXComponentId), + DECLARE_NAPI_FUNCTION( + "nativeTouchGuideStateChange", + flutter::PlatformViewOHOSNapi::NativeTouchGuideStateChange), }; FML_DLOG(INFO) << "Init NAPI size=" << sizeof(desc) / sizeof(desc[0]); diff --git a/shell/platform/ohos/napi/platform_view_ohos_napi.cpp b/shell/platform/ohos/napi/platform_view_ohos_napi.cpp index d1bf9ed4725e9298687873967201de3e48c2efde..ceec4e68939af73701c96a0c900040f47caadefc 100644 --- a/shell/platform/ohos/napi/platform_view_ohos_napi.cpp +++ b/shell/platform/ohos/napi/platform_view_ohos_napi.cpp @@ -2277,4 +2277,40 @@ napi_value PlatformViewOHOSNapi::nativeUnicodeIsRegionalIndicatorSymbol(napi_env napi_create_int32(env, (int)is_emoji, &result); return result; } + +napi_value PlatformViewOHOSNapi::nativeGetXComponentId(napi_env env, napi_callback_info info) +{ + size_t argc = 1; + napi_value args[1] = {nullptr}; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + std::string xcomponentId; + bool ret = fml::napi::GetString(env, args[0], xcomponentId); + if (ret != napi_ok) { + FML_DLOG(ERROR) << "nativeGetXComponentId xcomponentId: " << xcomponentId; + return nullptr; + } + // obtain the current visible xcomponent id from the ets callback event + XComponentAdapter::GetInstance()->currentXComponentId_ = xcomponentId; + FML_DLOG(INFO) << "nativeGetXComponentId -> xcomponentId: " << xcomponentId; + return nullptr; +} + +napi_value PlatformViewOHOSNapi::NativeTouchGuideStateChange(napi_env env, napi_callback_info info) +{ + size_t argc = 1; + napi_value args[1] = {nullptr}; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + bool touchGuideState = false; + bool ret = napi_get_value_bool(env, args[0], &touchGuideState); + if (ret != napi_ok) { + FML_DLOG(ERROR) << "NativeTouchGuideStateChange touchGuideState: " << touchGuideState; + return nullptr; + } + + OhosAccessibilityBridge::GetInstance()->isTouchGuideOn_ = touchGuideState; + FML_DLOG(INFO) << "NativeTouchGuideStateChange -> touchGuideState: " << touchGuideState; + return nullptr; +} } // namespace flutter \ No newline at end of file diff --git a/shell/platform/ohos/napi/platform_view_ohos_napi.h b/shell/platform/ohos/napi/platform_view_ohos_napi.h index 013557aeae1f5e6755b59a19826fd91da5cf22ea..9fd5394e80b8efb29f45ff3207269a87ed212bb9 100644 --- a/shell/platform/ohos/napi/platform_view_ohos_napi.h +++ b/shell/platform/ohos/napi/platform_view_ohos_napi.h @@ -269,6 +269,14 @@ class PlatformViewOHOSNapi { napi_env env, napi_callback_info info); + static napi_value nativeGetXComponentId( + napi_env env, + napi_callback_info info); + + static napi_value NativeTouchGuideStateChange( + napi_env env, + napi_callback_info info); + private: static napi_env env_; napi_ref ref_napi_obj_; diff --git a/shell/platform/ohos/ohos_xcomponent_adapter.cpp b/shell/platform/ohos/ohos_xcomponent_adapter.cpp index 424123a0c64ccc522e4b29259ebd392b54460644..6193b2df6f09a2e392abcb7033211a123933f92e 100644 --- a/shell/platform/ohos/ohos_xcomponent_adapter.cpp +++ b/shell/platform/ohos/ohos_xcomponent_adapter.cpp @@ -195,13 +195,18 @@ void OnSurfaceChangedCB(OH_NativeXComponent* component, void* window) { } void OnSurfaceDestroyedCB(OH_NativeXComponent* component, void* window) { + std::lock_guard lock(XComponentAdapter::GetInstance()->mutex_); for(auto it = XComponentAdapter::GetInstance()->xcomponetMap_.begin(); it != XComponentAdapter::GetInstance()->xcomponetMap_.end();) { if(it->second->nativeXComponent_ == component) { it->second->OnSurfaceDestroyed(component, window); delete it->second; + // 将当前要销毁的xcomponent对应的无障碍provider指针置nullptr + it->second->accessibilityProvider_ = nullptr; it = XComponentAdapter::GetInstance()->xcomponetMap_.erase(it); + // delete the semantics tree of the destroyed xcomponent + OhosAccessibilityBridge::GetInstance()->g_flutterSemanticsTreeXComponents.erase(it->first); } else { ++it; } @@ -364,6 +369,16 @@ void XComponentBase::DetachFlutterEngine() { isEngineAttached_ = false; } +ArkUI_AccessibilityProvider* XComponentAdapter::GetAccessibilityProvider(const std::string& xcompId) +{ + auto it = xcomponetMap_.find(xcompId); + if (it != xcomponetMap_.end()) { + return it->second->accessibilityProvider_; + } else { + return nullptr; + } +} + void XComponentBase::RegisterArkUIAccessibilityService(OH_NativeXComponent* nativeXComponent) { BindAccessibilityProviderCallback(); @@ -385,8 +400,11 @@ void XComponentBase::RegisterArkUIAccessibilityService(OH_NativeXComponent* nati OH_ArkUI_AccessibilityProviderRegisterCallback(a11yProvider, &accessibilityProviderCallback_) ); - XComponentAdapter::GetInstance()->accessibilityProvider_ = a11yProvider; - + std::lock_guard lock(XComponentAdapter::GetInstance()->mutex_); + auto* base = XComponentAdapter::GetInstance()->xcomponetMap_[id_]; + base->accessibilityProvider_ = a11yProvider; + base->nativeXComponent_ = nativeXComponent; + FML_DLOG(INFO) << "RegisterArkUIAccessibilityService is finished"; } @@ -397,9 +415,8 @@ void XComponentBase::SetNativeXComponent(OH_NativeXComponent* nativeXComponent){ OH_NativeXComponent_RegisterCallback(nativeXComponent_, &callback_); OH_NativeXComponent_RegisterMouseEventCallback(nativeXComponent_, &mouseCallback_); // register the OH_ArkUI accessibility callbacks - if (OH_GetSdkApiVersion() >= 13) { - RegisterArkUIAccessibilityService(nativeXComponent_); - } + if (OH_GetSdkApiVersion() < 13) { return; } + RegisterArkUIAccessibilityService(nativeXComponent_); } } @@ -456,7 +473,6 @@ void XComponentBase::OnSurfaceChanged(OH_NativeXComponent* component, void* wind void XComponentBase::OnSurfaceDestroyed(OH_NativeXComponent* component, void* window) { window_ = nullptr; - XComponentAdapter::GetInstance()->accessibilityProvider_ = nullptr; LOGD("XComponentManger::OnSurfaceDestroyed"); if (isEngineAttached_) { PlatformViewOHOSNapi::SurfaceDestroyed(std::stoll(shellholderId_)); diff --git a/shell/platform/ohos/ohos_xcomponent_adapter.h b/shell/platform/ohos/ohos_xcomponent_adapter.h index 7698e3a8f2cb44c9be00d65370badaad71ae55e3..632e9d216d8b3118a563793d352b03f0ac8177fd 100644 --- a/shell/platform/ohos/ohos_xcomponent_adapter.h +++ b/shell/platform/ohos/ohos_xcomponent_adapter.h @@ -20,7 +20,7 @@ #include #include #include - +#include #include "flutter/shell/platform/ohos/ohos_touch_processor.h" #include "flutter/shell/platform/ohos/napi/platform_view_ohos_napi.h" #include "napi/native_api.h" @@ -51,7 +51,8 @@ public: void OnDispatchMouseEvent(OH_NativeXComponent* component, void* window); void OnDispatchMouseWheelEvent(mouseWheelEvent event); - void RegisterArkUIAccessibilityService(OH_NativeXComponent* nativeXComponent); + void RegisterArkUIAccessibilityService( + OH_NativeXComponent* nativeXComponent); OH_NativeXComponent_TouchEvent touchEvent_; OH_NativeXComponent_Callback callback_; @@ -68,7 +69,6 @@ public: uint64_t height_; OhosTouchProcessor ohosTouchProcessor_; ArkUI_AccessibilityProvider* accessibilityProvider_; - }; class XComponentAdapter { @@ -83,9 +83,12 @@ class XComponentAdapter { void DetachFlutterEngine(std::string& id); void OnMouseWheel(std::string& id, mouseWheelEvent event); + ArkUI_AccessibilityProvider* GetAccessibilityProvider(const std::string& xcompId); + public: std::map xcomponetMap_; - ArkUI_AccessibilityProvider* accessibilityProvider_; + std::string currentXComponentId_; + std::mutex mutex_; private: static XComponentAdapter mXComponentAdapter; diff --git a/shell/platform/ohos/platform_view_ohos.cpp b/shell/platform/ohos/platform_view_ohos.cpp index 6d860f66aa1bd0d2d71161f8ed44301f1093a5e7..242a8a78e9ef06f5f64f06725e96d64e0309df1a 100644 --- a/shell/platform/ohos/platform_view_ohos.cpp +++ b/shell/platform/ohos/platform_view_ohos.cpp @@ -298,12 +298,9 @@ void PlatformViewOHOS::UpdateAssetResolverByType( void PlatformViewOHOS::UpdateSemantics( flutter::SemanticsNodeUpdates update, flutter::CustomAccessibilityActionUpdates actions) { - task_runners_.GetPlatformTaskRunner()->PostTask( - [update = std::move(update), actions = std::move(actions)]() { - auto nativeAccessibilityChannel_ = std::make_shared(); - nativeAccessibilityChannel_->UpdateSemantics(update, actions); - FML_DLOG(INFO) << "PlatformViewOHOS::UpdateSemantics is called"; - }); + FML_DLOG(INFO) << "PlatformViewOHOS::UpdateSemantics()"; + auto nativeAccessibilityChannel_ = std::make_shared(); + nativeAccessibilityChannel_->UpdateSemantics(std::move(update), std::move(actions)); } // |PlatformView|