// CLASS HEADER
// EXTERNAL INCLUDES
+#include <dali/devel-api/common/stage.h>
#include <dali/integration-api/debug.h>
+#include <dali/public-api/actors/layer.h>
#include <iostream>
#include <unordered_map>
// INTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/environment-variable.h>
+#include <dali/devel-api/adaptor-framework/window-devel.h>
#include <dali/internal/accessibility/bridge/bridge-accessible.h>
#include <dali/internal/accessibility/bridge/bridge-action.h>
#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-object.h>
+#include <dali/internal/accessibility/bridge/bridge-selection.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>
#include <dali/internal/accessibility/bridge/dummy-atspi.h>
+#include <dali/internal/adaptor/common/adaptor-impl.h>
#include <dali/internal/system/common/environment-variables.h>
-#include <dali/devel-api/adaptor-framework/environment-variable.h>
using namespace Dali::Accessibility;
+/**
+ * @brief The BridgeImpl class is to implement some Bridge functions.
+ */
class BridgeImpl : public virtual BridgeBase,
public BridgeAccessible,
public BridgeObject,
public BridgeAction,
public BridgeValue,
public BridgeText,
- public BridgeEditableText
+ public BridgeEditableText,
+ public BridgeSelection,
+ public BridgeApplication
{
- DBus::DBusClient listenOnAtspiEnabledSignalClient;
- DBus::DBusClient registryClient, directReadingClient;
- bool screenReaderEnabled = false;
- bool isEnabled = false;
- bool isShown = false;
- std::unordered_map<int32_t, std::function<void(std::string)>> directReadingCallbacks;
- Dali::Actor highlightedActor;
- std::function<void(Dali::Actor)> highlightClearAction;
+ DBus::DBusClient mAccessibilityStatusClient;
+ DBus::DBusClient mRegistryClient;
+ DBus::DBusClient mDirectReadingClient;
+ bool mIsScreenReaderEnabled = false;
+ bool mIsEnabled = false;
+ bool mIsShown = false;
+ std::unordered_map<int32_t, std::function<void(std::string)>> mDirectReadingCallbacks;
+ Dali::Actor mHighlightedActor;
+ std::function<void(Dali::Actor)> mHighlightClearAction;
+ Dali::CallbackBase* mIdleCallback = NULL;
public:
BridgeImpl()
{
- listenOnAtspiEnabledSignalClient = DBus::DBusClient{A11yDbusName, A11yDbusPath, A11yDbusStatusInterface, DBus::ConnectionType::SESSION};
-
- listenOnAtspiEnabledSignalClient.addPropertyChangedEvent<bool>("ScreenReaderEnabled", [this](bool res) {
- screenReaderEnabled = res;
- if(screenReaderEnabled || isEnabled)
- {
- ForceUp();
- }
- else
- {
- ForceDown();
- }
- });
-
- listenOnAtspiEnabledSignalClient.addPropertyChangedEvent<bool>("IsEnabled", [this](bool res) {
- isEnabled = res;
- if(screenReaderEnabled || isEnabled)
- {
- ForceUp();
- }
- else
- {
- ForceDown();
- }
- });
}
+ /**
+ * @copydoc Dali::Accessibility::Bridge::Emit()
+ */
Consumed Emit(KeyEventType type, unsigned int keyCode, const std::string& keyName, unsigned int timeStamp, bool isText) override
{
if(!IsUp())
return Consumed::NO;
}
- unsigned int evType = 0;
+ unsigned int keyType = 0;
switch(type)
{
case KeyEventType::KEY_PRESSED:
{
- evType = 0;
+ keyType = 0;
break;
}
case KeyEventType::KEY_RELEASED:
{
- evType = 1;
+ keyType = 1;
break;
}
default:
return Consumed::NO;
}
}
- auto m = registryClient.method<bool(std::tuple<uint32_t, int32_t, int32_t, int32_t, int32_t, std::string, bool>)>("NotifyListenersSync");
- auto result = m.call(std::tuple<uint32_t, int32_t, int32_t, int32_t, int32_t, std::string, bool>{evType, 0, static_cast<int32_t>(keyCode), 0, static_cast<int32_t>(timeStamp), keyName, isText ? 1 : 0});
+
+ 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 std::get<0>(result) ? Consumed::YES : Consumed::NO;
}
+ /**
+ * @copydoc Dali::Accessibility::Bridge::Pause()
+ */
void Pause() override
{
if(!IsUp())
return;
}
- directReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
+ mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
if(!msg)
{
LOG() << "Direct reading command failed (" << msg.getError().message << ")";
true);
}
+ /**
+ * @copydoc Dali::Accessibility::Bridge::Resume()
+ */
void Resume() override
{
if(!IsUp())
return;
}
- directReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
+ mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
if(!msg)
{
LOG() << "Direct reading command failed (" << msg.getError().message << ")";
false);
}
+ /**
+ * @copydoc Dali::Accessibility::Bridge::StopReading()
+ */
void StopReading(bool alsoNonDiscardable) override
{
if(!IsUp())
return;
}
- directReadingClient.method<DBus::ValueOrError<void>(bool)>("StopReading").asyncCall([](DBus::ValueOrError<void> msg) {
+ mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("StopReading").asyncCall([](DBus::ValueOrError<void> msg) {
if(!msg)
{
LOG() << "Direct reading command failed (" << msg.getError().message << ")";
alsoNonDiscardable);
}
+ /**
+ * @copydoc Dali::Accessibility::Bridge::Say()
+ */
void Say(const std::string& text, bool discardable, std::function<void(std::string)> callback) override
{
if(!IsUp())
return;
}
- directReadingClient.method<DBus::ValueOrError<std::string, bool, int32_t>(std::string, bool)>("ReadCommand").asyncCall([=](DBus::ValueOrError<std::string, bool, int32_t> msg) {
+ 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 << ")";
}
else if(callback)
{
- directReadingCallbacks.emplace(std::get<2>(msg), callback);
+ mDirectReadingCallbacks.emplace(std::get<2>(msg), callback);
}
},
text,
discardable);
}
+ /**
+ * @copydoc Dali::Accessibility::Bridge::ForceDown()
+ */
void ForceDown() override
{
- if(data)
+ if(mData)
{
- if(data->currentlyHighlightedActor && data->highlightActor)
+ if(mData->mCurrentlyHighlightedActor && mData->mHighlightActor)
{
- data->currentlyHighlightedActor.Remove(data->highlightActor);
+ mData->mCurrentlyHighlightedActor.Remove(mData->mHighlightActor);
}
- data->currentlyHighlightedActor = {};
- data->highlightActor = {};
+ mData->mCurrentlyHighlightedActor = {};
+ mData->mHighlightActor = {};
}
- highlightedActor = {};
- highlightClearAction = {};
+ mHighlightedActor = {};
+ mHighlightClearAction = {};
BridgeAccessible::ForceDown();
- registryClient = {};
- directReadingClient = {};
- directReadingCallbacks.clear();
+ mRegistryClient = {};
+ mDirectReadingClient = {};
+ mDirectReadingCallbacks.clear();
}
+ /**
+ * @copydoc Dali::Accessibility::Bridge::Terminate()
+ */
void Terminate() override
{
- if(data)
+ if(mData)
{
- data->currentlyHighlightedActor = {};
- data->highlightActor = {};
+ mData->mCurrentlyHighlightedActor = {};
+ mData->mHighlightActor = {};
}
ForceDown();
- listenOnAtspiEnabledSignalClient = {};
- dbusServer = {};
- con = {};
+ if((NULL != mIdleCallback) && Dali::Adaptor::IsAvailable())
+ {
+ Dali::Adaptor::Get().RemoveIdle(mIdleCallback);
+ }
+ mAccessibilityStatusClient = {};
+ mDbusServer = {};
+ mConnectionPtr = {};
}
+ /**
+ * @copydoc Dali::Accessibility::Bridge::ForceUp()
+ */
ForceUpResult ForceUp() override
{
if(BridgeAccessible::ForceUp() == ForceUpResult::ALREADY_UP)
BridgeValue::RegisterInterfaces();
BridgeText::RegisterInterfaces();
BridgeEditableText::RegisterInterfaces();
+ BridgeSelection::RegisterInterfaces();
+ BridgeApplication::RegisterInterfaces();
- RegisterOnBridge(&application);
+ RegisterOnBridge(&mApplication);
- registryClient = {AtspiDbusNameRegistry, AtspiDbusPathDec, AtspiDbusInterfaceDec, con};
- directReadingClient = DBus::DBusClient{DirectReadingDBusName, DirectReadingDBusPath, DirectReadingDBusInterface, con};
- directReadingClient.addSignal<void(int32_t, std::string)>("ReadingStateChanged", [=](int32_t id, std::string readingState) {
- auto it = directReadingCallbacks.find(id);
- if(it != directReadingCallbacks.end())
+ mRegistryClient = {AtspiDbusNameRegistry, AtspiDbusPathDec, AtspiDbusInterfaceDec, mConnectionPtr};
+ mDirectReadingClient = DBus::DBusClient{DirectReadingDBusName, DirectReadingDBusPath, DirectReadingDBusInterface, mConnectionPtr};
+
+ mDirectReadingClient.addSignal<void(int32_t, std::string)>("ReadingStateChanged", [=](int32_t id, std::string readingState) {
+ auto it = mDirectReadingCallbacks.find(id);
+ if(it != mDirectReadingCallbacks.end())
{
it->second(readingState);
if(readingState != "ReadingPaused" && readingState != "ReadingResumed" && readingState != "ReadingStarted")
- directReadingCallbacks.erase(it);
+ {
+ mDirectReadingCallbacks.erase(it);
+ }
}
});
- auto proxy = DBus::DBusClient{AtspiDbusNameRegistry, AtspiDbusPathRoot, AtspiDbusInterfaceSocket, con};
+ 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);
- application.parent.SetAddress(std::move(std::get<0>(res)));
- if(isShown)
+
+ mApplication.mParent.SetAddress(std::move(std::get<0>(res)));
+ if(mIsShown)
{
EmitActivate();
}
return ForceUpResult::JUST_STARTED;
}
+ /**
+ * @brief Sends a signal to dbus that the default window is activated.
+ *
+ * TODO : This is subject to change if/when we implement multi-window support.
+ * @see BridgeObject::Emit()
+ */
void EmitActivate()
{
- auto win = application.getActiveWindow();
+ auto win = mApplication.GetActiveWindow();
if(win)
{
win->Emit(WindowEvent::ACTIVATE, 0);
}
}
+ /**
+ * @brief Sends a signal to dbus that the default window is deactivated.
+ *
+ * TODO : This is subject to change if/when we implement multi-window support.
+ * @see BridgeObject::Emit()
+ */
void EmitDeactivate()
{
- auto win = application.getActiveWindow();
+ auto win = mApplication.GetActiveWindow();
if(win)
{
win->Emit(WindowEvent::DEACTIVATE, 0);
}
}
- void ApplicationHidden() override
+ /**
+ * @copydoc Dali::Accessibility::Bridge::WindowHidden()
+ */
+ void WindowHidden() override
{
- if(isShown && IsUp())
+ if(mIsShown && IsUp())
{
EmitDeactivate();
}
- isShown = false;
+ mIsShown = false;
}
- void ApplicationShown() override
+ /**
+ * @copydoc Dali::Accessibility::Bridge::WindowShown()
+ */
+ void WindowShown() override
{
- if(!isShown && IsUp())
+ if(!mIsShown && IsUp())
{
EmitActivate();
}
- isShown = true;
+ mIsShown = true;
}
- void Initialize() override
+ void ReadAndListenProperty()
{
- auto req = DBus::DBusClient{A11yDbusName, A11yDbusPath, A11yDbusStatusInterface, DBus::ConnectionType::SESSION};
- auto p = req.property<bool>("ScreenReaderEnabled").get();
- if(p)
+ // read property
+ auto enabled = mAccessibilityStatusClient.property<bool>("ScreenReaderEnabled").get();
+ if(enabled)
{
- screenReaderEnabled = std::get<0>(p);
+ mIsScreenReaderEnabled = std::get<0>(enabled);
}
- p = req.property<bool>("IsEnabled").get();
- if(p)
+
+ enabled = mAccessibilityStatusClient.property<bool>("IsEnabled").get();
+ if(enabled)
{
- isEnabled = std::get<0>(p);
+ mIsEnabled = std::get<0>(enabled);
}
- if(screenReaderEnabled || isEnabled)
+
+ if(mIsScreenReaderEnabled || mIsEnabled)
{
ForceUp();
}
+
+ // listen property change
+ mAccessibilityStatusClient.addPropertyChangedEvent<bool>("ScreenReaderEnabled", [this](bool res) {
+ mIsScreenReaderEnabled = res;
+ if(mIsScreenReaderEnabled || mIsEnabled)
+ {
+ ForceUp();
+ }
+ else
+ {
+ ForceDown();
+ }
+ });
+
+ mAccessibilityStatusClient.addPropertyChangedEvent<bool>("IsEnabled", [this](bool res) {
+ mIsEnabled = res;
+ if(mIsScreenReaderEnabled || mIsEnabled)
+ {
+ ForceUp();
+ }
+ else
+ {
+ ForceDown();
+ }
+ });
+ }
+
+ 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 GetScreenReaderEnabled()
+ bool OnIdleSignal()
{
- return screenReaderEnabled;
+ if ( InitializeAccessibilityStatusClient() )
+ {
+ ReadAndListenProperty();
+ mIdleCallback = NULL;
+ return false;
+ }
+
+ return true;
}
- bool GetIsEnabled()
+ /**
+ * @copydoc Dali::Accessibility::Bridge::Initialize()
+ */
+ void Initialize() override
{
- return isEnabled;
+ if ( InitializeAccessibilityStatusClient() )
+ {
+ ReadAndListenProperty();
+ 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;
}
-};
+}; // BridgeImpl
-static bool bridgeInitialized;
+namespace // unnamed namespace
+{
+
+bool INITIALIZED_BRIDGE = false;
-static Bridge* CreateBridge()
+/**
+ * @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.
+ */
+Bridge* CreateBridge()
{
- bridgeInitialized = true;
+ INITIALIZED_BRIDGE = true;
try
{
/* check environment variable first */
- const char *envAtspiDisabled = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_DISABLE_ATSPI);
- if (envAtspiDisabled && std::atoi(envAtspiDisabled) != 0)
+ const char* envAtspiDisabled = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_DISABLE_ATSPI);
+ if(envAtspiDisabled && std::atoi(envAtspiDisabled) != 0)
{
return Dali::Accessibility::DummyBridge::GetInstance();
}
return new BridgeImpl;
}
- catch (const std::exception&)
+ catch(const std::exception&)
{
DALI_LOG_ERROR("Failed to initialize AT-SPI bridge");
return Dali::Accessibility::DummyBridge::GetInstance();
}
}
+} // unnamed namespace
+
+// Dali::Accessibility::Bridge class implementation
+
Bridge* Bridge::GetCurrentBridge()
{
static Bridge* bridge;
- if (bridge)
+ if(bridge)
{
return bridge;
}
- else if (autoInitState == AutoInitState::ENABLED)
+ else if(mAutoInitState == AutoInitState::ENABLED)
{
bridge = CreateBridge();
/* check environment variable for suppressing screen-reader */
- const char *envSuppressScreenReader = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_SUPPRESS_SCREEN_READER);
- if (envSuppressScreenReader && std::atoi(envSuppressScreenReader) != 0)
+ const char* envSuppressScreenReader = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_SUPPRESS_SCREEN_READER);
+ if(envSuppressScreenReader && std::atoi(envSuppressScreenReader) != 0)
{
bridge->SuppressScreenReader(true);
}
void Bridge::DisableAutoInit()
{
- if (bridgeInitialized)
+ if(INITIALIZED_BRIDGE)
{
DALI_LOG_ERROR("Bridge::DisableAutoInit() called after bridge auto-initialization");
}
- autoInitState = AutoInitState::DISABLED;
+ mAutoInitState = AutoInitState::DISABLED;
}
-void Bridge::EnableAutoInit(Accessible* topLevelWindow, const std::string& applicationName)
+void Bridge::EnableAutoInit()
{
- autoInitState = AutoInitState::ENABLED;
+ mAutoInitState = AutoInitState::ENABLED;
- if (bridgeInitialized)
+ if(INITIALIZED_BRIDGE)
{
return;
}
+ auto rootLayer = Dali::Stage::GetCurrent().GetRootLayer();
+ auto window = Dali::DevelWindow::Get(rootLayer);
+ auto applicationName = Dali::Internal::Adaptor::Adaptor::GetApplicationPackageName();
+
auto* bridge = Bridge::GetCurrentBridge();
- bridge->AddTopLevelWindow(topLevelWindow);
+ bridge->AddTopLevelWindow(Dali::Accessibility::Accessible::Get(rootLayer, true));
bridge->SetApplicationName(applicationName);
bridge->Initialize();
+
+ if(window && window.IsVisible())
+ {
+ bridge->WindowShown();
+ }
}