+ if(mReadScreenReaderEnabledTimer)
+ {
+ mReadScreenReaderEnabledTimer.Stop();
+ mReadScreenReaderEnabledTimer.Reset();
+ }
+
+ mIsScreenReaderEnabled = std::get<0>(msg);
+ SwitchBridge();
+ });
+ }
+
+ void EmitScreenReaderEnabledSignal()
+ {
+ if(mIsScreenReaderEnabled)
+ {
+ mScreenReaderEnabledSignal.Emit();
+ }
+ else
+ {
+ mScreenReaderDisabledSignal.Emit();
+ }
+ }
+
+ void ListenScreenReaderEnabledProperty()
+ {
+ mAccessibilityStatusClient.addPropertyChangedEvent<bool>("ScreenReaderEnabled", [this](bool res) {
+ mIsScreenReaderEnabled = res;
+ EmitScreenReaderEnabledSignal();
+ SwitchBridge();
+ });
+ }
+
+ void ReadAndListenProperties()
+ {
+ ReadIsEnabledProperty();
+ ListenIsEnabledProperty();
+
+ ReadScreenReaderEnabledProperty();
+ ListenScreenReaderEnabledProperty();
+ }
+
+ bool InitializeAccessibilityStatusClient()
+ {
+ mAccessibilityStatusClient = DBus::DBusClient{A11yDbusName, A11yDbusPath, A11yDbusStatusInterface, DBus::ConnectionType::SESSION};
+
+ if(!mAccessibilityStatusClient)
+ {
+ DALI_LOG_ERROR("Accessibility Status DbusClient is not ready\n");
+ return false;
+ }
+
+ return true;
+ }
+
+ bool InitializeTimerCallback()
+ {
+ if(InitializeAccessibilityStatusClient())
+ {
+ ReadAndListenProperties();
+ return false;
+ }
+ return true;
+ }
+
+ bool OnIdleSignal()
+ {
+ if(InitializeAccessibilityStatusClient())
+ {
+ ReadAndListenProperties();
+ mIdleCallback = NULL;
+ return false;
+ }
+
+ if(!mInitializeTimer)
+ {
+ mInitializeTimer = Dali::Timer::New(RETRY_INTERVAL);
+ mInitializeTimer.TickSignal().Connect(this, &BridgeImpl::InitializeTimerCallback);
+ }
+ mInitializeTimer.Start();
+
+ mIdleCallback = NULL;
+ return false;
+ }
+
+ /**
+ * @copydoc Dali::Accessibility::Bridge::Initialize()
+ */
+ void Initialize() override
+ {
+ if(InitializeAccessibilityStatusClient())
+ {
+ ReadAndListenProperties();
+ return;
+ }
+
+ // Initialize failed. Try it again on Idle
+ if(Dali::Adaptor::IsAvailable())
+ {
+ Dali::Adaptor& adaptor = Dali::Adaptor::Get();
+ if(NULL == mIdleCallback)
+ {
+ mIdleCallback = MakeCallback(this, &BridgeImpl::OnIdleSignal);
+ adaptor.AddIdle(mIdleCallback, true);
+ }
+ }
+ }
+
+ /**
+ * @copydoc Dali::Accessibility::Bridge::GetScreenReaderEnabled()
+ */
+ bool GetScreenReaderEnabled() override
+ {
+ return mIsScreenReaderEnabled;
+ }
+
+ /**
+ * @copydoc Dali::Accessibility::Bridge::IsEnabled()
+ */
+ bool IsEnabled() override
+ {
+ return mIsEnabled;
+ }
+
+ Address EmbedSocket(const Address& plug, const Address& socket) override
+ {
+ auto client = CreateSocketClient(socket);
+ auto reply = client.method<Address(Address)>("Embed").call(plug);
+
+ if(!reply)
+ {
+ DALI_LOG_ERROR("Failed to embed socket %s: %s", socket.ToString().c_str(), reply.getError().message.c_str());
+ return {};
+ }
+
+ 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);
+
+ client.method<void(Address)>("Unembed").asyncCall([](DBus::ValueOrError<void>) {}, plug);
+ }
+
+ void SetSocketOffset(ProxyAccessible* socket, std::int32_t x, std::int32_t y) override
+ {
+ AddCoalescableMessage(CoalescableMessages::SET_OFFSET, socket, 1.0f, [=]() {
+ auto client = CreateSocketClient(socket->GetAddress());
+
+ client.method<void(std::int32_t, std::int32_t)>("SetOffset").asyncCall([](DBus::ValueOrError<void>) {}, x, y);
+ });
+ }
+
+ void SetExtentsOffset(std::int32_t x, std::int32_t y) override
+ {
+ if(mData)
+ {
+ mData->mExtentsOffset = {x, y};
+ }
+ }
+
+ void SetPreferredBusName(std::string_view preferredBusName) override
+ {
+ if(preferredBusName == mPreferredBusName)
+ {
+ return;
+ }
+
+ std::string oldPreferredBusName = std::move(mPreferredBusName);
+ mPreferredBusName = std::string{preferredBusName};
+
+ if(IsUp())
+ {
+ ReleaseBusName(oldPreferredBusName);
+ RequestBusName(mPreferredBusName);
+ }
+ // else: request/release will be handled by ForceUp/ForceDown, respectively
+ }
+
+private:
+ DBus::DBusClient CreateSocketClient(const Address& socket)
+ {
+ return {socket.GetBus(), ATSPI_PREFIX_PATH + socket.GetPath(), Accessible::GetInterfaceName(AtspiInterface::SOCKET), mConnectionPtr};
+ }
+
+ void RequestBusName(const std::string& busName)
+ {
+ if(busName.empty())
+ {
+ return;
+ }
+
+ DBus::requestBusName(mConnectionPtr, busName);
+ }
+
+ void ReleaseBusName(const std::string& busName)
+ {
+ if(busName.empty())
+ {
+ return;
+ }
+
+ DBus::releaseBusName(mConnectionPtr, busName);
+ }
+}; // BridgeImpl
+
+namespace // unnamed namespace
+{
+bool INITIALIZED_BRIDGE = false;
+
+/**
+ * @brief Creates BridgeImpl instance.
+ *
+ * @return The BridgeImpl instance
+ * @note This method is to check environment variable first. If ATSPI is disable using env, it returns dummy bridge instance.
+ */
+std::shared_ptr<Bridge> CreateBridge()