#include <dali/internal/accessibility/bridge/bridge-collection.h>
#include <dali/internal/accessibility/bridge/bridge-component.h>
#include <dali/internal/accessibility/bridge/bridge-editable-text.h>
+#include <dali/internal/accessibility/bridge/bridge-hypertext.h>
+#include <dali/internal/accessibility/bridge/bridge-hyperlink.h>
#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-text.h>
#include <dali/internal/accessibility/bridge/bridge-value.h>
#include <dali/internal/accessibility/bridge/bridge-application.h>
using namespace Dali::Accessibility;
+namespace // unnamed namespace
+{
+
+const int RETRY_INTERVAL = 1000;
+
+} // unnamed namespace
+
/**
* @brief The BridgeImpl class is to implement some Bridge functions.
*/
public BridgeText,
public BridgeEditableText,
public BridgeSelection,
- public BridgeApplication
+ public BridgeApplication,
+ public BridgeHypertext,
+ public BridgeHyperlink,
+ public BridgeSocket
{
DBus::DBusClient mAccessibilityStatusClient;
DBus::DBusClient mRegistryClient;
Dali::Actor mHighlightedActor;
std::function<void(Dali::Actor)> mHighlightClearAction;
Dali::CallbackBase* mIdleCallback = NULL;
+ Dali::Timer mInitializeTimer;
+ Dali::Timer mReadIsEnabledTimer;
+ Dali::Timer mReadScreenReaderEnabledTimer;
+ Dali::Timer mForceUpTimer;
public:
BridgeImpl()
mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
if(!msg)
{
- LOG() << "Direct reading command failed (" << msg.getError().message << ")";
+ LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
}
},
true);
mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
if(!msg)
{
- LOG() << "Direct reading command failed (" << msg.getError().message << ")";
+ LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
}
},
false);
mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("StopReading").asyncCall([](DBus::ValueOrError<void> msg) {
if(!msg)
{
- LOG() << "Direct reading command failed (" << msg.getError().message << ")";
+ LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
}
},
alsoNonDiscardable);
mDirectReadingClient.method<DBus::ValueOrError<std::string, bool, int32_t>(std::string, bool)>("ReadCommand").asyncCall([=](DBus::ValueOrError<std::string, bool, int32_t> msg) {
if(!msg)
{
- LOG() << "Direct reading command failed (" << msg.getError().message << ")";
+ LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
}
else if(callback)
{
mData->mHighlightActor = {};
mDisabledSignal.Emit();
+ UnembedSocket(mApplication.GetAddress(), {AtspiDbusNameRegistry, "root"});
}
+
mHighlightedActor = {};
mHighlightClearAction = {};
BridgeAccessible::ForceDown();
mDirectReadingClient = {};
mDirectReadingCallbacks.clear();
mApplication.mChildren.clear();
- mApplication.mWindows.clear();
+ ClearTimer();
+ }
+
+ void ClearTimer()
+ {
+ if(mInitializeTimer)
+ {
+ mInitializeTimer.Stop();
+ mInitializeTimer.Reset();
+ }
+
+ if(mReadIsEnabledTimer)
+ {
+ mReadIsEnabledTimer.Stop();
+ mReadIsEnabledTimer.Reset();
+ }
+
+ if(mReadScreenReaderEnabledTimer)
+ {
+ mReadScreenReaderEnabledTimer.Stop();
+ mReadScreenReaderEnabledTimer.Reset();
+ }
+
+ if(mForceUpTimer)
+ {
+ mForceUpTimer.Stop();
+ mForceUpTimer.Reset();
+ }
}
/**
{
if(mData)
{
+ // The ~Window() after this point cannot emit DESTROY, because Bridge is not available. So emit DESTROY here.
+ for(auto windowAccessible : mApplication.mChildren)
+ {
+ BridgeObject::Emit(windowAccessible, WindowEvent::DESTROY);
+ }
mData->mCurrentlyHighlightedActor = {};
mData->mHighlightActor = {};
}
mConnectionPtr = {};
}
+ bool ForceUpTimerCallback()
+ {
+ if(ForceUp() != ForceUpResult::FAILED)
+ {
+ return false;
+ }
+ return true;
+ }
+
/**
* @copydoc Dali::Accessibility::Bridge::ForceUp()
*/
ForceUpResult ForceUp() override
{
- if(BridgeAccessible::ForceUp() == ForceUpResult::ALREADY_UP)
+ auto forceUpResult = BridgeAccessible::ForceUp();
+ if(forceUpResult == ForceUpResult::ALREADY_UP)
+ {
+ return forceUpResult;
+ }
+ else if(forceUpResult == ForceUpResult::FAILED)
{
- return ForceUpResult::ALREADY_UP;
+ if(!mForceUpTimer)
+ {
+ mForceUpTimer = Dali::Timer::New(RETRY_INTERVAL);
+ mForceUpTimer.TickSignal().Connect(this, &BridgeImpl::ForceUpTimerCallback);
+ mForceUpTimer.Start();
+ }
+ return forceUpResult;
}
BridgeObject::RegisterInterfaces();
BridgeEditableText::RegisterInterfaces();
BridgeSelection::RegisterInterfaces();
BridgeApplication::RegisterInterfaces();
+ BridgeHypertext::RegisterInterfaces();
+ BridgeHyperlink::RegisterInterfaces();
+ BridgeSocket::RegisterInterfaces();
RegisterOnBridge(&mApplication);
- mRegistryClient = {AtspiDbusNameRegistry, AtspiDbusPathDec, AtspiDbusInterfaceDec, mConnectionPtr};
+ mRegistryClient = {AtspiDbusNameRegistry, AtspiDbusPathDec, Accessible::GetInterfaceName(AtspiInterface::DEVICE_EVENT_CONTROLLER), mConnectionPtr};
mDirectReadingClient = DBus::DBusClient{DirectReadingDBusName, DirectReadingDBusPath, DirectReadingDBusInterface, mConnectionPtr};
mDirectReadingClient.addSignal<void(int32_t, std::string)>("ReadingStateChanged", [=](int32_t id, std::string readingState) {
}
});
- auto proxy = DBus::DBusClient{AtspiDbusNameRegistry, AtspiDbusPathRoot, AtspiDbusInterfaceSocket, mConnectionPtr};
- Address root{"", "root"};
- auto res = proxy.method<Address(Address)>("Embed").call(root);
- if(!res)
- {
- LOG() << "Call to Embed failed: " << res.getError().message;
- }
- assert(res);
-
- mApplication.mParent.SetAddress(std::move(std::get<0>(res)));
-
- if(mIsShown)
- {
- auto rootLayer = Dali::Stage::GetCurrent().GetRootLayer();
- auto window = Dali::DevelWindow::Get(rootLayer);
- EmitActivate(window); // Currently, sends a signal that the default window is activated here.
- }
-
+ auto parentAddress = EmbedSocket(mApplication.GetAddress(), {AtspiDbusNameRegistry, "root"});
+ mApplication.mParent.SetAddress(std::move(parentAddress));
mEnabledSignal.Emit();
return ForceUpResult::JUST_STARTED;
}
}
+ /**
+ * @copydoc Dali::Accessibility::Bridge::SuppressScreenReader()
+ */
+ void SuppressScreenReader(bool suppress) override
+ {
+ if(mIsScreenReaderSuppressed == suppress)
+ {
+ return;
+ }
+ mIsScreenReaderSuppressed = suppress;
+ ReadScreenReaderEnabledProperty();
+ }
+
+ void SwitchBridge()
+ {
+ if((!mIsScreenReaderSuppressed && mIsScreenReaderEnabled) || mIsEnabled)
+ {
+ ForceUp();
+ }
+ else
+ {
+ ForceDown();
+ }
+ }
+
+ bool ReadIsEnabledTimerCallback()
+ {
+ ReadIsEnabledProperty();
+ return false;
+ }
+
void ReadIsEnabledProperty()
{
mAccessibilityStatusClient.property<bool>("IsEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
DALI_LOG_ERROR("Get IsEnabled property error: %s\n", msg.getError().message.c_str());
if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
{
- ReadIsEnabledProperty();
+ if(!mReadIsEnabledTimer)
+ {
+ mReadIsEnabledTimer = Dali::Timer::New(RETRY_INTERVAL);
+ mReadIsEnabledTimer.TickSignal().Connect(this, &BridgeImpl::ReadIsEnabledTimerCallback);
+ }
+ mReadIsEnabledTimer.Start();
}
return;
}
- mIsEnabled = std::get<0>(msg);
- if(mIsEnabled)
+
+ if(mReadIsEnabledTimer)
{
- ForceUp();
+ mReadIsEnabledTimer.Stop();
+ mReadIsEnabledTimer.Reset();
}
+
+ mIsEnabled = std::get<0>(msg);
+ SwitchBridge();
});
}
{
mAccessibilityStatusClient.addPropertyChangedEvent<bool>("IsEnabled", [this](bool res) {
mIsEnabled = res;
- if(mIsScreenReaderEnabled || mIsEnabled)
- {
- ForceUp();
- }
- else
- {
- ForceDown();
- }
+ SwitchBridge();
});
}
+ bool ReadScreenReaderEnabledTimerCallback()
+ {
+ ReadScreenReaderEnabledProperty();
+ return false;
+ }
+
void ReadScreenReaderEnabledProperty()
{
+ // can be true because of SuppressScreenReader before init
+ if (!mAccessibilityStatusClient)
+ {
+ return;
+ }
+
mAccessibilityStatusClient.property<bool>("ScreenReaderEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
if(!msg)
{
DALI_LOG_ERROR("Get ScreenReaderEnabled property error: %s\n", msg.getError().message.c_str());
if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
{
- ReadScreenReaderEnabledProperty();
+ if(!mReadScreenReaderEnabledTimer)
+ {
+ mReadScreenReaderEnabledTimer = Dali::Timer::New(RETRY_INTERVAL);
+ mReadScreenReaderEnabledTimer.TickSignal().Connect(this, &BridgeImpl::ReadScreenReaderEnabledTimerCallback);
+ }
+ mReadScreenReaderEnabledTimer.Start();
}
return;
}
- mIsScreenReaderEnabled = std::get<0>(msg);
- if(mIsScreenReaderEnabled)
+
+ if(mReadScreenReaderEnabledTimer)
{
- ForceUp();
+ 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;
- if(mIsScreenReaderEnabled || mIsEnabled)
- {
- ForceUp();
- }
- else
- {
- ForceDown();
- }
+ EmitScreenReaderEnabledSignal();
+ SwitchBridge();
});
}
return true;
}
+ bool InitializeTimerCallback()
+ {
+ if ( InitializeAccessibilityStatusClient() )
+ {
+ ReadAndListenProperties();
+ return false;
+ }
+ return true;
+ }
+
bool OnIdleSignal()
{
if ( InitializeAccessibilityStatusClient() )
return false;
}
- return true;
+ if(!mInitializeTimer)
+ {
+ mInitializeTimer = Dali::Timer::New(RETRY_INTERVAL);
+ mInitializeTimer.TickSignal().Connect(this, &BridgeImpl::InitializeTimerCallback);
+ }
+ mInitializeTimer.Start();
+
+ mIdleCallback = NULL;
+ return false;
}
/**
{
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").call(ATSPI_PREFIX_PATH + plug.GetPath());
+ }
+
+ void UnembedSocket(const Address& plug, const Address& socket) override
+ {
+ auto client = CreateSocketClient(socket);
+
+ client.method<void(Address)>("Unembed").call(plug);
+ }
+
+private:
+ DBus::DBusClient CreateSocketClient(const Address& socket)
+ {
+ return {socket.GetBus(), ATSPI_PREFIX_PATH + socket.GetPath(), Accessible::GetInterfaceName(AtspiInterface::SOCKET), mConnectionPtr};
+ }
}; // BridgeImpl
namespace // unnamed namespace
* @return The BridgeImpl instance
* @note This method is to check environment variable first. If ATSPI is disable using env, it returns dummy bridge instance.
*/
-Bridge* CreateBridge()
+std::shared_ptr<Bridge> CreateBridge()
{
INITIALIZED_BRIDGE = true;
return Dali::Accessibility::DummyBridge::GetInstance();
}
- return new BridgeImpl;
+ return std::make_shared<BridgeImpl>();
}
catch(const std::exception&)
{
// Dali::Accessibility::Bridge class implementation
-Bridge* Bridge::GetCurrentBridge()
+std::shared_ptr<Bridge> Bridge::GetCurrentBridge()
{
- static Bridge* bridge;
+ static std::shared_ptr<Bridge> bridge;
if(bridge)
{
auto window = Dali::DevelWindow::Get(rootLayer);
auto applicationName = Dali::Internal::Adaptor::Adaptor::GetApplicationPackageName();
- auto accessible = Accessibility::Accessible::Get(rootLayer, true);
+ auto accessible = Accessibility::Accessible::Get(rootLayer);
- auto* bridge = Bridge::GetCurrentBridge();
+ auto bridge = Bridge::GetCurrentBridge();
bridge->AddTopLevelWindow(accessible);
bridge->SetApplicationName(applicationName);
bridge->Initialize();