X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dali%2Finternal%2Faccessibility%2Fbridge%2Fbridge-impl.cpp;h=181b44015261383f189ec5d0cf84a094f2b6ba44;hb=93492c5bc9f16bc609650f7ec06b89aacafacd64;hp=91f68c82aff821b72238fbd532dcf136749e79cf;hpb=7a6df7ca09b96f9894473030ecb6819628b64081;p=platform%2Fcore%2Fuifw%2Fdali-adaptor.git diff --git a/dali/internal/accessibility/bridge/bridge-impl.cpp b/dali/internal/accessibility/bridge/bridge-impl.cpp index 91f68c8..181b440 100644 --- a/dali/internal/accessibility/bridge/bridge-impl.cpp +++ b/dali/internal/accessibility/bridge/bridge-impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * Copyright (c) 2021 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. @@ -18,22 +18,34 @@ // CLASS HEADER // EXTERNAL INCLUDES +#include #include +#include #include #include // INTERNAL INCLUDES +#include +#include #include #include #include #include +#include #include -#include +#include #include -#include +#include +#include +#include +#include +#include using namespace Dali::Accessibility; +/** + * @brief The BridgeImpl class is to implement some Bridge functions. + */ class BridgeImpl : public virtual BridgeBase, public BridgeAccessible, public BridgeObject, @@ -42,70 +54,48 @@ class BridgeImpl : public virtual BridgeBase, 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 > directReadingCallbacks; - Dali::Actor highlightedActor; - std::function highlightClearAction; + DBus::DBusClient mAccessibilityStatusClient; + DBus::DBusClient mRegistryClient; + DBus::DBusClient mDirectReadingClient; + bool mIsScreenReaderEnabled = false; + bool mIsEnabled = false; + bool mIsShown = false; + std::unordered_map> mDirectReadingCallbacks; + Dali::Actor mHighlightedActor; + std::function 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(); - } - } - ); } - Consumed Emit( KeyEventType type, unsigned int keyCode, const std::string& keyName, unsigned int timeStamp, bool isText ) override + /** + * @copydoc Dali::Accessibility::Bridge::Emit() + */ + Consumed Emit(KeyEventType type, unsigned int keyCode, const std::string& keyName, unsigned int timeStamp, bool isText) override { - if (!IsUp()) + if(!IsUp()) { return Consumed::NO; } - unsigned int evType = 0; + unsigned int keyType = 0; - switch( type ) + switch(type) { case KeyEventType::KEY_PRESSED: { - evType = 0; + keyType = 0; break; } case KeyEventType::KEY_RELEASED: { - evType = 1; + keyType = 1; break; } default: @@ -113,107 +103,148 @@ public: 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} ); - if( !result ) + + auto methodObject = mRegistryClient.method)>("NotifyListenersSync"); + auto result = methodObject.call(std::tuple{keyType, 0, static_cast(keyCode), 0, static_cast(timeStamp), keyName, isText ? 1 : 0}); + if(!result) { LOG() << result.getError().message; return Consumed::NO; } - return std::get< 0 >( result ) ? Consumed::YES : Consumed::NO; + return std::get<0>(result) ? Consumed::YES : Consumed::NO; } + /** + * @copydoc Dali::Accessibility::Bridge::Pause() + */ void Pause() override { - if (!IsUp()) + if(!IsUp()) { return; } - directReadingClient.method< DBus::ValueOrError< void >( bool ) > ( "PauseResume" ).asyncCall( - []( DBus::ValueOrError< void > msg ) { - if (!msg) - { - LOG() << "Direct reading command failed (" << msg.getError().message << ")"; - } - }, - true); + mDirectReadingClient.method(bool)>("PauseResume").asyncCall([](DBus::ValueOrError msg) { + if(!msg) + { + LOG() << "Direct reading command failed (" << msg.getError().message << ")"; + } + }, + true); } + /** + * @copydoc Dali::Accessibility::Bridge::Resume() + */ void Resume() override { - if (!IsUp()) + if(!IsUp()) { return; } - directReadingClient.method< DBus::ValueOrError< void >( bool ) > ( "PauseResume" ).asyncCall( - []( DBus::ValueOrError< void > msg) { - if (!msg) - { - LOG() << "Direct reading command failed (" << msg.getError().message << ")"; - } - }, - false); + mDirectReadingClient.method(bool)>("PauseResume").asyncCall([](DBus::ValueOrError msg) { + if(!msg) + { + LOG() << "Direct reading command failed (" << msg.getError().message << ")"; + } + }, + false); } - void Say( const std::string& text, bool discardable, std::function< void(std::string) > callback ) override + /** + * @copydoc Dali::Accessibility::Bridge::StopReading() + */ + void StopReading(bool alsoNonDiscardable) override { - if (!IsUp()) + if(!IsUp()) { return; } - directReadingClient.method< DBus::ValueOrError< std::string, bool, int32_t >( std::string, bool ) > ( "ReadCommand" ).asyncCall( - [=]( DBus::ValueOrError msg ) { - if ( !msg ) - { - LOG() << "Direct reading command failed (" << msg.getError().message << ")"; - } - else if( callback ) - { - directReadingCallbacks.emplace( std::get< 2 >( msg ), callback); - } - }, - text, - discardable); + mDirectReadingClient.method(bool)>("StopReading").asyncCall([](DBus::ValueOrError 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 callback) override + { + if(!IsUp()) + { + return; + } + + mDirectReadingClient.method(std::string, bool)>("ReadCommand").asyncCall([=](DBus::ValueOrError msg) { + if(!msg) + { + LOG() << "Direct reading command failed (" << msg.getError().message << ")"; + } + else if(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 = {}; + + mDisabledSignal.Emit(); } - 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 ) + if(BridgeAccessible::ForceUp() == ForceUpResult::ALREADY_UP) { return ForceUpResult::ALREADY_UP; } @@ -226,109 +257,303 @@ public: BridgeValue::RegisterInterfaces(); BridgeText::RegisterInterfaces(); BridgeEditableText::RegisterInterfaces(); + BridgeSelection::RegisterInterfaces(); + BridgeApplication::RegisterInterfaces(); + + RegisterOnBridge(&mApplication); - RegisterOnBridge( &application ); + mRegistryClient = {AtspiDbusNameRegistry, AtspiDbusPathDec, AtspiDbusInterfaceDec, mConnectionPtr}; + mDirectReadingClient = DBus::DBusClient{DirectReadingDBusName, DirectReadingDBusPath, DirectReadingDBusInterface, mConnectionPtr}; - 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 ) + mDirectReadingClient.addSignal("ReadingStateChanged", [=](int32_t id, std::string readingState) { + auto it = mDirectReadingCallbacks.find(id); + if(it != mDirectReadingCallbacks.end()) { - auto it = directReadingCallbacks.find( id ); - if (it != directReadingCallbacks.end()) + it->second(readingState); + if(readingState != "ReadingPaused" && readingState != "ReadingResumed" && readingState != "ReadingStarted") { - 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) + auto res = proxy.method("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) + assert(res); + + mApplication.mParent.SetAddress(std::move(std::get<0>(res))); + if(mIsShown) { EmitActivate(); } + + mEnabledSignal.Emit(); + 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(); - if (win) + auto win = mApplication.GetActiveWindow(); + if(win) { - win->Emit( WindowEvent::ACTIVATE, 0 ); + 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(); - if (win) + auto win = mApplication.GetActiveWindow(); + if(win) { - win->Emit( WindowEvent::DEACTIVATE, 0 ); + 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("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("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("ScreenReaderEnabled", [this](bool res) { + mIsScreenReaderEnabled = res; + if(mIsScreenReaderEnabled || mIsEnabled) + { + ForceUp(); + } + else + { + ForceDown(); + } + }); + + mAccessibilityStatusClient.addPropertyChangedEvent("IsEnabled", [this](bool res) { + mIsEnabled = res; + if(mIsScreenReaderEnabled || mIsEnabled) + { + ForceUp(); + } + else + { + ForceDown(); + } + }); } - bool GetScreenReaderEnabled() + bool InitializeAccessibilityStatusClient() { - return screenReaderEnabled; + 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 GetIsEnabled() + bool OnIdleSignal() + { + if ( InitializeAccessibilityStatusClient() ) + { + ReadAndListenProperty(); + mIdleCallback = NULL; + return false; + } + + return true; + } + + /** + * @copydoc Dali::Accessibility::Bridge::Initialize() + */ + void Initialize() override + { + 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 + +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. + */ +Bridge* CreateBridge() +{ + INITIALIZED_BRIDGE = true; + + try { - return isEnabled; + /* check environment variable first */ + 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&) + { + 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 BridgeImpl *bridge = new BridgeImpl; - return bridge; + static Bridge* bridge; + + if(bridge) + { + return bridge; + } + 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) + { + bridge->SuppressScreenReader(true); + } + + return bridge; + } + + return Dali::Accessibility::DummyBridge::GetInstance(); } +void Bridge::DisableAutoInit() +{ + if(INITIALIZED_BRIDGE) + { + DALI_LOG_ERROR("Bridge::DisableAutoInit() called after bridge auto-initialization"); + } + + mAutoInitState = AutoInitState::DISABLED; +} + +void Bridge::EnableAutoInit() +{ + mAutoInitState = AutoInitState::ENABLED; + + 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(Dali::Accessibility::Accessible::Get(rootLayer, true)); + bridge->SetApplicationName(applicationName); + bridge->Initialize(); + + if(window && window.IsVisible()) + { + bridge->WindowShown(); + } +}