[Tizen][ATSPI] Introduce GetNodeInfo interface
[platform/core/uifw/dali-adaptor.git] / dali / internal / accessibility / bridge / bridge-accessible.cpp
index 2b3d24a..c57a887 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include <dali/internal/accessibility/bridge/bridge-accessible.h>
 
 // EXTERNAL INCLUDES
+#include <dali/public-api/math/math-utils.h>
+
 #include <algorithm>
 #include <iostream>
 
+// INTERNAL INCLUDES
+#include <dali/devel-api/atspi-interfaces/accessible.h>
+#include <dali/devel-api/atspi-interfaces/component.h>
+#include <dali/devel-api/atspi-interfaces/selection.h>
+#include <dali/devel-api/atspi-interfaces/text.h>
+#include <dali/devel-api/atspi-interfaces/value.h>
+
 //comment out 2 lines below to get more logs
 #undef LOG
 #define LOG() _LoggerEmpty()
@@ -32,7 +41,6 @@ using namespace Dali::Accessibility;
 
 namespace
 {
-
 bool SortVertically(Component* lhs, Component* rhs)
 {
   auto leftRect  = lhs->GetExtents(CoordinateType::WINDOW);
@@ -54,7 +62,7 @@ std::vector<std::vector<Component*>> SplitLines(const std::vector<Component*>& c
   // Find first with non-zero area
   auto first = std::find_if(children.begin(), children.end(), [](Component* child) -> bool {
     auto extents = child->GetExtents(CoordinateType::WINDOW);
-    return extents.height != 0.0f && extents.width != 0.0f;
+    return !Dali::EqualsZero(extents.height) && !Dali::EqualsZero(extents.width);
   });
 
   if(first == children.end())
@@ -63,8 +71,8 @@ std::vector<std::vector<Component*>> SplitLines(const std::vector<Component*>& c
   }
 
   std::vector<std::vector<Component*>> lines(1);
-  Dali::Rect<> lineRect = (*first)->GetExtents(CoordinateType::WINDOW);
-  Dali::Rect<> rect;
+  Dali::Rect<>                         lineRect = (*first)->GetExtents(CoordinateType::WINDOW);
+  Dali::Rect<>                         rect;
 
   // Split into lines
   for(auto it = first; it != children.end(); ++it)
@@ -72,7 +80,7 @@ std::vector<std::vector<Component*>> SplitLines(const std::vector<Component*>& c
     auto child = *it;
 
     rect = child->GetExtents(CoordinateType::WINDOW);
-    if(rect.height == 0.0f || rect.width == 0.0f)
+    if(Dali::EqualsZero(rect.height) || Dali::EqualsZero(rect.width))
     {
       // Zero area, ignore
       continue;
@@ -101,7 +109,7 @@ static bool AcceptObjectCheckRelations(Component* obj)
 
   for(const auto& it : relations)
   {
-    if(it.relationType == RelationType::CONTROLLED_BY)
+    if(it.mRelationType == RelationType::CONTROLLED_BY)
     {
       return false;
     }
@@ -150,7 +158,7 @@ static bool IsObjectZeroSize(Component* obj)
     return false;
   }
   auto extents = obj->GetExtents(CoordinateType::WINDOW);
-  return extents.height == 0 || extents.width == 0;
+  return Dali::EqualsZero(extents.height) || Dali::EqualsZero(extents.width);
 }
 
 static bool IsObjectAcceptable(Component* obj)
@@ -251,7 +259,7 @@ static std::string GetComponentInfo(Component* obj)
   std::ostringstream object;
   auto               extent = obj->GetExtents(CoordinateType::SCREEN);
   object << "name: " << obj->GetName() << " extent: (" << extent.x << ", "
-    << extent.y << "), [" << extent.width << ", " << extent.height << "]";
+         << extent.y << "), [" << extent.width << ", " << extent.height << "]";
   return object.str();
 }
 
@@ -260,30 +268,6 @@ static std::string MakeIndent(unsigned int maxRecursionDepth)
   return std::string(GET_NAVIGABLE_AT_POINT_MAX_RECURSION_DEPTH - maxRecursionDepth, ' ');
 }
 
-static bool IsDeputy(Accessible* obj)
-{
-  //TODO: add deputy
-  return false;
-}
-
-static Accessible* GetProxyInParent(Accessible* obj)
-{
-  if(!obj)
-  {
-    return nullptr;
-  }
-
-  auto children = obj->GetChildren();
-  for(auto& child : children)
-  {
-    if(child->IsProxy())
-    {
-      return child;
-    }
-  }
-  return nullptr;
-}
-
 static bool IsRoleAcceptableWhenNavigatingNextPrev(Accessible* obj)
 {
   if(!obj)
@@ -349,18 +333,13 @@ static bool CheckChainEndWithAttribute(Accessible* obj, unsigned char forward)
   return false;
 }
 
-static Accessible* GetDeputyOfProxyInParent(Accessible* obj)
-{
-  return nullptr;
-}
-
-static std::vector<Component*> GetScrollableParents(Accessible *accessible)
+static std::vector<Component*> GetScrollableParents(Accessible* accessible)
 {
   std::vector<Component*> scrollableParents;
 
   while(accessible)
   {
-    accessible = accessible->GetParent();
+    accessible     = accessible->GetParent();
     auto component = dynamic_cast<Component*>(accessible);
     if(component && component->IsScrollable())
     {
@@ -370,7 +349,7 @@ static std::vector<Component*> GetScrollableParents(Accessible *accessible)
   return scrollableParents;
 }
 
-static std::vector<Component*> GetNonDuplicatedScrollableParents(Accessible *child, Accessible *start)
+static std::vector<Component*> GetNonDuplicatedScrollableParents(Accessible* child, Accessible* start)
 {
   auto scrollableParentsOfChild = GetScrollableParents(child);
   auto scrollableParentsOfStart = GetScrollableParents(start);
@@ -381,21 +360,20 @@ static std::vector<Component*> GetNonDuplicatedScrollableParents(Accessible *chi
   {
     scrollableParentsOfChild.pop_back();
     scrollableParentsOfStart.pop_back();
-   }
+  }
 
   return scrollableParentsOfChild;
 }
 
 } // anonymous namespace
 
-
 BridgeAccessible::BridgeAccessible()
 {
 }
 
 void BridgeAccessible::RegisterInterfaces()
 {
-  DBus::DBusInterfaceDescription desc{AtspiDbusInterfaceAccessible};
+  DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::ACCESSIBLE)};
   AddGetPropertyToInterface(desc, "ChildCount", &BridgeAccessible::GetChildCount);
   AddGetPropertyToInterface(desc, "Name", &BridgeAccessible::GetName);
   AddGetPropertyToInterface(desc, "Description", &BridgeAccessible::GetDescription);
@@ -405,7 +383,7 @@ void BridgeAccessible::RegisterInterfaces()
   AddFunctionToInterface(desc, "GetLocalizedRoleName", &BridgeAccessible::GetLocalizedRoleName);
   AddFunctionToInterface(desc, "GetState", &BridgeAccessible::GetStates);
   AddFunctionToInterface(desc, "GetAttributes", &BridgeAccessible::GetAttributes);
-  AddFunctionToInterface(desc, "GetInterfaces", &BridgeAccessible::GetInterfaces);
+  AddFunctionToInterface(desc, "GetInterfaces", &BridgeAccessible::GetInterfacesAsStrings);
   AddFunctionToInterface(desc, "GetChildAtIndex", &BridgeAccessible::GetChildAtIndex);
   AddFunctionToInterface(desc, "GetChildren", &BridgeAccessible::GetChildren);
   AddFunctionToInterface(desc, "GetIndexInParent", &BridgeAccessible::GetIndexInParent);
@@ -415,9 +393,16 @@ void BridgeAccessible::RegisterInterfaces()
   AddFunctionToInterface(desc, "DoGesture", &BridgeAccessible::DoGesture);
   AddFunctionToInterface(desc, "GetReadingMaterial", &BridgeAccessible::GetReadingMaterial);
   AddFunctionToInterface(desc, "GetRelationSet", &BridgeAccessible::GetRelationSet);
+  AddFunctionToInterface(desc, "SetListenPostRender", &BridgeAccessible::SetListenPostRender);
+  AddFunctionToInterface(desc, "GetNodeInfo", &BridgeAccessible::GetNodeInfo);
   mDbusServer.addInterface("/", desc, true);
 }
 
+Accessible* BridgeAccessible::FindSelf() const
+{
+  return FindCurrentObject();
+}
+
 Component* BridgeAccessible::GetObjectInRelation(Accessible* obj, RelationType relationType)
 {
   if(!obj)
@@ -427,11 +412,11 @@ Component* BridgeAccessible::GetObjectInRelation(Accessible* obj, RelationType r
 
   for(auto& relation : obj->GetRelationSet())
   {
-    if(relation.relationType == relationType)
+    if(relation.mRelationType == relationType)
     {
-      for(auto& address : relation.targets)
+      for(auto& target : relation.mTargets)
       {
-        auto component = dynamic_cast<Component*>(Find(address));
+        auto component = dynamic_cast<Component*>(target);
         if(component)
         {
           return component;
@@ -494,9 +479,9 @@ BridgeAccessible::ReadingMaterialType BridgeAccessible::GetReadingMaterial()
     auto relation  = std::find_if(relations.begin(),
                                  relations.end(),
                                  [relationType](const Dali::Accessibility::Relation& relation) -> bool {
-                                   return relation.relationType == relationType;
+                                   return relation.mRelationType == relationType;
                                  });
-    return relations.end() != relation && !relation->targets.empty() ? Find(relation->targets.back()) : nullptr;
+    return relations.end() != relation && !relation->mTargets.empty() ? relation->mTargets.back() : nullptr;
   };
 
   auto        labellingObject = findObjectByRelationType(RelationType::LABELLED_BY);
@@ -504,14 +489,16 @@ BridgeAccessible::ReadingMaterialType BridgeAccessible::GetReadingMaterial()
 
   auto describedByObject = findObjectByRelationType(RelationType::DESCRIBED_BY);
 
-  double currentValue     = 0.0;
-  double minimumIncrement = 0.0;
-  double maximumValue     = 0.0;
-  double minimumValue     = 0.0;
-  auto*  valueInterface   = dynamic_cast<Dali::Accessibility::Value*>(self);
+  double      currentValue     = 0.0;
+  std::string currentValueText;
+  double      minimumIncrement = 0.0;
+  double      maximumValue     = 0.0;
+  double      minimumValue     = 0.0;
+  auto*       valueInterface   = Value::DownCast(self);
   if(valueInterface)
   {
     currentValue     = valueInterface->GetCurrent();
+    currentValueText = valueInterface->GetValueText();
     minimumIncrement = valueInterface->GetMinimumIncrement();
     maximumValue     = valueInterface->GetMaximum();
     minimumValue     = valueInterface->GetMinimum();
@@ -519,7 +506,7 @@ BridgeAccessible::ReadingMaterialType BridgeAccessible::GetReadingMaterial()
 
   int32_t firstSelectedChildIndex = -1;
   int32_t selectedChildCount      = 0;
-  auto*   selfSelectionInterface  = dynamic_cast<Dali::Accessibility::Selection*>(self);
+  auto*   selfSelectionInterface  = Selection::DownCast(self);
   if(selfSelectionInterface)
   {
     selectedChildCount      = selfSelectionInterface->GetSelectedChildrenCount();
@@ -542,9 +529,16 @@ BridgeAccessible::ReadingMaterialType BridgeAccessible::GetReadingMaterial()
     }
   }
 
+  auto    attributes        = self->GetAttributes();
+  auto    itemCount         = attributes.find("item_count");
   auto    atspiRole         = self->GetRole();
   int32_t listChildrenCount = 0;
-  if(atspiRole == Role::DIALOG)
+  if(itemCount != attributes.end())
+  {
+    // "item_count" gives manual control to the application, so it has priority
+    listChildrenCount = std::atoi(itemCount->second.c_str());
+  }
+  else if(atspiRole == Role::DIALOG)
   {
     listChildrenCount = GetItemCountOfFirstDescendantContainer(self, Role::LIST, Role::LIST_ITEM, true);
   }
@@ -553,14 +547,13 @@ BridgeAccessible::ReadingMaterialType BridgeAccessible::GetReadingMaterial()
     listChildrenCount = GetItemCountOfFirstDescendantContainer(self, Role::POPUP_MENU, Role::MENU_ITEM, false);
   }
 
-  auto*       textInterface         = dynamic_cast<Dali::Accessibility::Text*>(self);
+  auto*       textInterface         = Text::DownCast(self);
   std::string nameFromTextInterface = "";
   if(textInterface)
   {
     nameFromTextInterface = textInterface->GetText(0, textInterface->GetCharacterCount());
   }
 
-  auto attributes        = self->GetAttributes();
   auto name              = self->GetName();
   auto role              = static_cast<uint32_t>(atspiRole);
   auto states            = self->GetStates();
@@ -573,7 +566,7 @@ BridgeAccessible::ReadingMaterialType BridgeAccessible::GetReadingMaterial()
   auto  parentChildCount         = parent ? static_cast<int32_t>(parent->GetChildCount()) : 0;
   auto  parentStateSet           = parent ? parent->GetStates() : States{};
   bool  isSelectedInParent       = false;
-  auto* parentSelectionInterface = dynamic_cast<Dali::Accessibility::Selection*>(parent);
+  auto* parentSelectionInterface = Selection::DownCast(parent);
   if(parentSelectionInterface)
   {
     isSelectedInParent = parentSelectionInterface->IsChildSelected(indexInParent);
@@ -589,6 +582,7 @@ BridgeAccessible::ReadingMaterialType BridgeAccessible::GetReadingMaterial()
     localizedRoleName,
     childCount,
     currentValue,
+    currentValueText,
     minimumIncrement,
     maximumValue,
     minimumValue,
@@ -606,9 +600,49 @@ BridgeAccessible::ReadingMaterialType BridgeAccessible::GetReadingMaterial()
     describedByObject};
 }
 
-void BridgeAccessible::SuppressScreenReader(bool suppress)
+BridgeAccessible::NodeInfoType BridgeAccessible::GetNodeInfo()
 {
-  mIsScreenReaderSuppressed = suppress;
+  auto self         = FindSelf();
+  auto roleName     = self->GetRoleName();
+  auto name         = self->GetName();
+  auto toolkitName  = "dali";
+  auto attributes   = self->GetAttributes();
+  auto states       = self->GetStates();
+
+  auto* component   = Component::DownCast(self);
+  Dali::Rect<> screenExtents = {0, 0, 0, 0};
+  Dali::Rect<> windowExtents = {0, 0, 0, 0};
+  if (component)
+  {
+    screenExtents = component->GetExtents(CoordinateType::SCREEN);
+    windowExtents = component->GetExtents(CoordinateType::WINDOW);
+  }
+
+  auto* valueInterface    = Value::DownCast(self);
+  double currentValue     = 0.0;
+  double minimumIncrement = 0.0;
+  double maximumValue     = 0.0;
+  double minimumValue     = 0.0;
+  if(valueInterface)
+  {
+    currentValue     = valueInterface->GetCurrent();
+    minimumIncrement = valueInterface->GetMinimumIncrement();
+    maximumValue     = valueInterface->GetMaximum();
+    minimumValue     = valueInterface->GetMinimum();
+  }
+
+  return {
+    roleName,
+    name,
+    toolkitName,
+    attributes,
+    states,
+    {screenExtents.x, screenExtents.y, screenExtents.width, screenExtents.height},
+    {windowExtents.x, windowExtents.y, windowExtents.width, windowExtents.height},
+    currentValue,
+    minimumIncrement,
+    maximumValue,
+    minimumValue};
 }
 
 DBus::ValueOrError<bool> BridgeAccessible::DoGesture(Dali::Accessibility::Gesture type, int32_t startPositionX, int32_t startPositionY, int32_t endPositionX, int32_t endPositionY, Dali::Accessibility::GestureState state, uint32_t eventTime)
@@ -622,6 +656,10 @@ DBus::ValueOrError<Accessible*, uint8_t, Accessible*> BridgeAccessible::GetNavig
   Accessible* deputy     = nullptr;
   auto        accessible = FindSelf();
   auto        cType      = static_cast<CoordinateType>(coordinateType);
+
+  x -= mData->mExtentsOffset.first;
+  y -= mData->mExtentsOffset.second;
+
   LOG() << "GetNavigableAtPoint: " << x << ", " << y << " type: " << coordinateType;
   auto component = CalculateNavigableAccessibleAtPoint(accessible, {x, y}, cType, GET_NAVIGABLE_AT_POINT_MAX_RECURSION_DEPTH);
   bool recurse   = false;
@@ -649,8 +687,8 @@ std::vector<Component*> BridgeAccessible::GetValidChildren(const std::vector<Acc
   std::vector<Component*> vec;
 
   Dali::Rect<> scrollableParentExtents;
-  auto nonDuplicatedScrollableParents = GetNonDuplicatedScrollableParents(children.front(), start);
-  if (!nonDuplicatedScrollableParents.empty())
+  auto         nonDuplicatedScrollableParents = GetNonDuplicatedScrollableParents(children.front(), start);
+  if(!nonDuplicatedScrollableParents.empty())
   {
     scrollableParentExtents = nonDuplicatedScrollableParents.front()->GetExtents(CoordinateType::WINDOW);
   }
@@ -690,7 +728,6 @@ void BridgeAccessible::SortChildrenFromTopLeft(std::vector<Dali::Accessibility::
   children = sortedChildren;
 }
 
-
 template<class T>
 struct CycleDetection
 {
@@ -738,6 +775,10 @@ Accessible* BridgeAccessible::GetNextNonDefunctSibling(Accessible* obj, Accessib
   {
     return nullptr;
   }
+  else if(parent->IsProxy())
+  {
+    return parent;
+  }
 
   auto children = GetValidChildren(parent->GetChildren(), start);
   SortChildrenFromTopLeft(children);
@@ -809,9 +850,7 @@ Accessible* BridgeAccessible::CalculateNeighbor(Accessible* root, Accessible* st
 
   if(searchMode == BridgeAccessible::NeighborSearchMode::RECURSE_TO_OUTSIDE)
   {
-    // This only works if we navigate backward, and it is not possible to
-    // find in embedded process. In this case the deputy should be used */
-    return GetDeputyOfProxyInParent(start);
+    searchMode = BridgeAccessible::NeighborSearchMode::CONTINUE_AFTER_FAILED_RECURSION;
   }
 
   Accessible* node = start ? start : root;
@@ -861,7 +900,7 @@ Accessible* BridgeAccessible::CalculateNeighbor(Accessible* root, Accessible* st
     // 2. parent after all children in backward traversing
     // 3. Nodes with roles: ATSPI_ROLE_PAGE_TAB, ATSPI_ROLE_POPUP_MENU and ATSPI_ROLE_DIALOG, only when looking for first or last element.
     //    Objects with those roles shouldnt be reachable, when navigating next / prev.
-    bool areAllChildrenVisitedOrMovingForward= (children.size() == 0 || forward || areAllChildrenVisited);
+    bool areAllChildrenVisitedOrMovingForward = (children.size() == 0 || forward || areAllChildrenVisited);
 
     if(!forceNext && node != start && areAllChildrenVisitedOrMovingForward && IsObjectAcceptable(node))
     {
@@ -872,17 +911,6 @@ Accessible* BridgeAccessible::CalculateNeighbor(Accessible* root, Accessible* st
     }
 
     Accessible* nextRelatedInDirection = !forceNext ? GetObjectInRelation(node, forward ? RelationType::FLOWS_TO : RelationType::FLOWS_FROM) : nullptr;
-    // forceNext means that the searchMode is NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING
-    // in this case the node is elm_layout which is parent of proxy object.
-    // There is an access object working for the proxy object, and the access
-    // object could have relation information. This relation information should
-    // be checked first before using the elm_layout as a node.
-    if(forceNext && forward)
-    {
-      auto deputy = GetDeputyOfProxyInParent(node);
-      nextRelatedInDirection = GetObjectInRelation(deputy, RelationType::FLOWS_TO);
-    }
-
     if(nextRelatedInDirection && start && start->GetStates()[State::DEFUNCT])
     {
       nextRelatedInDirection = NULL;
@@ -891,27 +919,6 @@ Accessible* BridgeAccessible::CalculateNeighbor(Accessible* root, Accessible* st
     unsigned char wantCycleDetection = 0;
     if(nextRelatedInDirection)
     {
-      // Check whether nextRelatedInDirection is deputy object or not
-      Accessible* parent;
-      if(!forward)
-      {
-        // If the prev object is deputy, then go to inside of its proxy first
-        if(IsDeputy(nextRelatedInDirection))
-        {
-          parent                 = nextRelatedInDirection->GetParent();
-          nextRelatedInDirection = GetProxyInParent(parent);
-        }
-      }
-      else
-      {
-        // If current object is deputy, and it has relation next object,
-        // then do not use the relation next object, and use proxy first
-        if(IsDeputy(node))
-        {
-          parent                 = node->GetParent();
-          nextRelatedInDirection = GetProxyInParent(parent);
-        }
-      }
       node               = nextRelatedInDirection;
       wantCycleDetection = 1;
     }
@@ -945,9 +952,8 @@ Accessible* BridgeAccessible::CalculateNeighbor(Accessible* root, Accessible* st
 
 DBus::ValueOrError<Accessible*, uint8_t> BridgeAccessible::GetNeighbor(std::string rootPath, int32_t direction, int32_t searchMode)
 {
-  auto start               = FindSelf();
-  rootPath                 = StripPrefix(rootPath);
-  auto          root       = !rootPath.empty() ? Find(rootPath) : nullptr;
+  auto          start      = FindSelf();
+  auto          root       = !rootPath.empty() ? Find(StripPrefix(rootPath)) : nullptr;
   auto          accessible = CalculateNeighbor(root, start, direction == 1, static_cast<NeighborSearchMode>(searchMode));
   unsigned char recurse    = 0;
   if(accessible)
@@ -1015,9 +1021,9 @@ DBus::ValueOrError<std::unordered_map<std::string, std::string>> BridgeAccessibl
   return attributes;
 }
 
-DBus::ValueOrError<std::vector<std::string>> BridgeAccessible::GetInterfaces()
+DBus::ValueOrError<std::vector<std::string>> BridgeAccessible::GetInterfacesAsStrings()
 {
-  return FindSelf()->GetInterfaces();
+  return FindSelf()->GetInterfacesAsStrings();
 }
 
 int BridgeAccessible::GetChildCount()
@@ -1041,7 +1047,9 @@ std::string BridgeAccessible::GetName()
 
 DBus::ValueOrError<Accessible*, uint32_t, std::unordered_map<std::string, std::string>> BridgeAccessible::GetDefaultLabelInfo()
 {
-  auto defaultLabel = FindSelf()->GetDefaultLabel();
+  auto* defaultLabel = GetDefaultLabel(FindSelf());
+  DALI_ASSERT_DEBUG(defaultLabel);
+
   // By default, the text is taken from navigation context root's accessibility properties name and description.
   return {defaultLabel, static_cast<uint32_t>(defaultLabel->GetRole()), defaultLabel->GetAttributes()};
 }
@@ -1053,8 +1061,14 @@ DBus::ValueOrError<std::vector<BridgeAccessible::Relation>> BridgeAccessible::Ge
 
   for(auto& it : relations)
   {
-    ret.emplace_back(Relation{static_cast<uint32_t>(it.relationType), it.targets});
+    ret.emplace_back(Relation{static_cast<uint32_t>(it.mRelationType), it.mTargets});
   }
 
   return ret;
 }
+
+DBus::ValueOrError<void> BridgeAccessible::SetListenPostRender(bool enabled)
+{
+  FindSelf()->SetListenPostRender(enabled);
+  return {};
+}