[dali_2.3.43] Merge branch 'devel/master'
[platform/core/uifw/dali-adaptor.git] / dali / internal / accessibility / bridge / bridge-impl.cpp
index f76a0ea..2d1504a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 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.
@@ -25,6 +25,7 @@
 #include <unordered_map>
 
 // INTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/actor-accessible.h>
 #include <dali/devel-api/adaptor-framework/environment-variable.h>
 #include <dali/devel-api/adaptor-framework/window-devel.h>
 #include <dali/internal/accessibility/bridge/accessibility-common.h>
@@ -39,8 +40,8 @@
 #include <dali/internal/accessibility/bridge/bridge-object.h>
 #include <dali/internal/accessibility/bridge/bridge-selection.h>
 #include <dali/internal/accessibility/bridge/bridge-socket.h>
-#include <dali/internal/accessibility/bridge/bridge-table.h>
 #include <dali/internal/accessibility/bridge/bridge-table-cell.h>
+#include <dali/internal/accessibility/bridge/bridge-table.h>
 #include <dali/internal/accessibility/bridge/bridge-text.h>
 #include <dali/internal/accessibility/bridge/bridge-value.h>
 #include <dali/internal/accessibility/bridge/dummy/dummy-atspi.h>
@@ -80,6 +81,7 @@ class BridgeImpl : public virtual BridgeBase,
   DBus::DBusClient                                              mDirectReadingClient{};
   bool                                                          mIsScreenReaderEnabled{false};
   bool                                                          mIsEnabled{false};
+  bool                                                          mIsApplicationRunning{false};
   std::unordered_map<int32_t, std::function<void(std::string)>> mDirectReadingCallbacks{};
   Dali::Actor                                                   mHighlightedActor;
   std::function<void(Dali::Actor)>                              mHighlightClearAction{nullptr};
@@ -89,48 +91,99 @@ class BridgeImpl : public virtual BridgeBase,
   Dali::Timer                                                   mReadScreenReaderEnabledTimer;
   Dali::Timer                                                   mForceUpTimer;
   std::string                                                   mPreferredBusName;
+  std::map<uint32_t, std::shared_ptr<Accessible>>               mAccessibles; // Actor.ID to Accessible map
 
 public:
   BridgeImpl() = default;
 
   /**
-   * @copydoc Dali::Accessibility::Bridge::Emit()
+   * @copydoc Dali::Accessibility::Bridge::AddAccessible()
    */
-  Consumed Emit(KeyEventType type, unsigned int keyCode, const std::string& keyName, unsigned int timeStamp, bool isText) override
+  void AddAccessible(uint32_t actorId, std::shared_ptr<Accessible> accessible) override
   {
-    if(!IsUp())
-    {
-      return Consumed::NO;
-    }
+    mAccessibles[actorId] = std::move(accessible);
+  }
 
-    unsigned int keyType = 0;
+  /**
+   * @copydoc Dali::Accessibility::Bridge::RemoveAccessible()
+   */
+  void RemoveAccessible(uint32_t actorId) override
+  {
+    mAccessibles.erase(actorId);
+  }
 
-    switch(type)
+  /**
+   * @copydoc Dali::Accessibility::Bridge::GetAccessible()
+   */
+  std::shared_ptr<Accessible> GetAccessible(Dali::Actor actor) const override
+  {
+    uint32_t actorId = actor.GetProperty<int>(Dali::Actor::Property::ID);
+    auto     iter    = mAccessibles.find(actorId);
+    return iter != mAccessibles.end() ? iter->second : nullptr;
+  }
+
+  /**
+   * @copydoc Dali::Accessibility::Bridge::ShouldIncludeHidden()
+   */
+  bool ShouldIncludeHidden() const override
+  {
+    return mApplication.mShouldIncludeHidden;
+  }
+
+  void NotifyIncludeHiddenChanged() override
+  {
+    for(const auto& iter : mAccessibles)
     {
-      case KeyEventType::KEY_PRESSED:
+      const auto& accessible = iter.second;
+      if(accessible->IsHidden())
       {
-        keyType = 0;
-        break;
+        auto* parent = dynamic_cast<ActorAccessible*>(accessible->GetParent());
+        if(parent)
+        {
+          parent->OnChildrenChanged();
+        }
       }
-      case KeyEventType::KEY_RELEASED:
+    }
+  }
+
+  /**
+   * @copydoc Dali::Accessibility::Bridge::EmitKeyEvent()
+   */
+  bool EmitKeyEvent(Dali::KeyEvent keyEvent, std::function<void(Dali::KeyEvent, bool)> callback) override
+  {
+    using ArgumentTypes = std::tuple<uint32_t, int32_t, int32_t, int32_t, int32_t, std::string, bool>;
+
+    static const char* methodName = "NotifyListenersSync";
+
+    if(!IsUp())
+    {
+      return false;
+    }
+
+    uint32_t keyType   = (keyEvent.GetState() == Dali::KeyEvent::DOWN ? 0U : 1U);
+    auto     timeStamp = static_cast<std::int32_t>(keyEvent.GetTime());
+    bool     isText    = !keyEvent.GetKeyString().empty();
+
+    ArgumentTypes arguments(keyType, 0, keyEvent.GetKeyCode(), 0, timeStamp, keyEvent.GetKeyName(), isText);
+
+    auto functor = [keyEvent = std::move(keyEvent), callback = std::move(callback)](DBus::ValueOrError<bool> reply) {
+      bool consumed = false;
+
+      if(!reply)
       {
-        keyType = 1;
-        break;
+        DALI_LOG_ERROR("%s call failed: %s", methodName, reply.getError().message.c_str());
       }
-      default:
+      else
       {
-        return Consumed::NO;
+        consumed = std::get<0>(reply.getValues());
       }
-    }
 
-    auto methodObject = mRegistryClient.method<bool(std::tuple<uint32_t, int32_t, int32_t, int32_t, int32_t, std::string, bool>)>("NotifyListenersSync");
-    auto result       = methodObject.call(std::tuple<uint32_t, int32_t, int32_t, int32_t, int32_t, std::string, bool>{keyType, 0, static_cast<int32_t>(keyCode), 0, static_cast<int32_t>(timeStamp), keyName, isText ? 1 : 0});
-    if(!result)
-    {
-      LOG() << result.getError().message;
-      return Consumed::NO;
-    }
-    return std::get<0>(result) ? Consumed::YES : Consumed::NO;
+      callback(std::move(keyEvent), consumed);
+    };
+
+    mRegistryClient.method<bool(ArgumentTypes)>(methodName).asyncCall(std::move(functor), arguments);
+
+    return true;
   }
 
   /**
@@ -368,6 +421,21 @@ public:
   }
 
   /**
+   * @brief Sends a signal to dbus that the window is created.
+   *
+   * @param[in] window The window to be created
+   * @see BridgeObject::Emit()
+   */
+  void EmitCreated(Dali::Window window)
+  {
+    auto windowAccessible = mApplication.GetWindowAccessible(window);
+    if(windowAccessible)
+    {
+      windowAccessible->Emit(WindowEvent::CREATE, 0);
+    }
+  }
+
+  /**
    * @brief Sends a signal to dbus that the window is shown.
    *
    * @param[in] window The window to be shown
@@ -474,6 +542,17 @@ public:
   }
 
   /**
+   * @copydoc Dali::Accessibility::Bridge::WindowCreated()
+   */
+  void WindowCreated(Dali::Window window) override
+  {
+    if(IsUp())
+    {
+      EmitCreated(window);
+    }
+  }
+
+  /**
    * @copydoc Dali::Accessibility::Bridge::WindowShown()
    */
   void WindowShown(Dali::Window window) override
@@ -551,6 +630,24 @@ public:
   }
 
   /**
+   * @copydoc Dali::Accessibility::Bridge::ApplicationPaused()
+   */
+  void ApplicationPaused() override
+  {
+    mIsApplicationRunning = false;
+    SwitchBridge();
+  }
+
+  /**
+   * @copydoc Dali::Accessibility::Bridge::ApplicationResumed()
+   */
+  void ApplicationResumed() override
+  {
+    mIsApplicationRunning = true;
+    SwitchBridge();
+  }
+
+  /**
    * @copydoc Dali::Accessibility::Bridge::SuppressScreenReader()
    */
   void SuppressScreenReader(bool suppress) override
@@ -565,7 +662,9 @@ public:
 
   void SwitchBridge()
   {
-    if((!mIsScreenReaderSuppressed && mIsScreenReaderEnabled) || mIsEnabled)
+    bool isScreenReaderEnabled = mIsScreenReaderEnabled && !mIsScreenReaderSuppressed;
+
+    if((isScreenReaderEnabled || mIsEnabled) && mIsApplicationRunning)
     {
       ForceUp();
     }
@@ -750,7 +849,11 @@ public:
       if(NULL == mIdleCallback)
       {
         mIdleCallback = MakeCallback(this, &BridgeImpl::OnIdleSignal);
-        adaptor.AddIdle(mIdleCallback, true);
+        if(DALI_UNLIKELY(!adaptor.AddIdle(mIdleCallback, true)))
+        {
+          DALI_LOG_ERROR("Fail to add idle callback for bridge initialize. Call it synchronously.\n");
+          OnIdleSignal();
+        }
       }
     }
   }
@@ -785,13 +888,6 @@ public:
     return std::get<0>(reply.getValues());
   }
 
-  void EmbedAtkSocket(const Address& plug, const Address& socket) override
-  {
-    auto client = CreateSocketClient(socket);
-
-    client.method<void(std::string)>("Embedded").asyncCall([](DBus::ValueOrError<void>) {}, ATSPI_PREFIX_PATH + plug.GetPath());
-  }
-
   void UnembedSocket(const Address& plug, const Address& socket) override
   {
     auto client = CreateSocketClient(socket);
@@ -958,12 +1054,12 @@ void Bridge::EnableAutoInit()
   }
 }
 
-std::string Bridge::MakeBusNameForWidget(std::string_view widgetInstanceId)
+std::string Bridge::MakeBusNameForWidget(std::string_view widgetInstanceId, int widgetProcessId)
 {
   // The bus name should consist of dot-separated alphanumeric elements, e.g. "com.example.BusName123".
-  // Allowed characters in each element: "[A-Z][a-z][0-9]_", but no element may start with a digit.
+  // Allowed characters in each element: "[A-Z][a-z][0-9]_-", but no element may start with a digit.
 
-  static const char prefix[]   = "com.samsung.dali.widget_";
+  static const char prefix[]   = "elm.atspi.proxy.socket-";
   static const char underscore = '_';
 
   std::stringstream tmp;
@@ -972,8 +1068,10 @@ std::string Bridge::MakeBusNameForWidget(std::string_view widgetInstanceId)
 
   for(char ch : widgetInstanceId)
   {
-    tmp << (std::isalnum(ch) ? ch : underscore);
+    tmp << (!std::isalnum(ch) && ch != '_' && ch != '-' && ch != '.' ? underscore : ch);
   }
 
+  tmp << '-' << widgetProcessId;
+
   return tmp.str();
 }