Revert "[Tizen] Remove to call key consumed event in ATSPI bridge"
[platform/core/uifw/dali-adaptor.git] / dali / internal / accessibility / bridge / bridge-impl.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19
20 // EXTERNAL INCLUDES
21 #include <dali/devel-api/common/stage.h>
22 #include <dali/integration-api/debug.h>
23 #include <dali/public-api/actors/layer.h>
24 #include <iostream>
25 #include <unordered_map>
26
27 // INTERNAL INCLUDES
28 #include <dali/devel-api/adaptor-framework/environment-variable.h>
29 #include <dali/devel-api/adaptor-framework/window-devel.h>
30 #include <dali/internal/accessibility/bridge/accessibility-common.h>
31 #include <dali/internal/accessibility/bridge/bridge-accessible.h>
32 #include <dali/internal/accessibility/bridge/bridge-action.h>
33 #include <dali/internal/accessibility/bridge/bridge-application.h>
34 #include <dali/internal/accessibility/bridge/bridge-collection.h>
35 #include <dali/internal/accessibility/bridge/bridge-component.h>
36 #include <dali/internal/accessibility/bridge/bridge-editable-text.h>
37 #include <dali/internal/accessibility/bridge/bridge-hyperlink.h>
38 #include <dali/internal/accessibility/bridge/bridge-hypertext.h>
39 #include <dali/internal/accessibility/bridge/bridge-object.h>
40 #include <dali/internal/accessibility/bridge/bridge-selection.h>
41 #include <dali/internal/accessibility/bridge/bridge-socket.h>
42 #include <dali/internal/accessibility/bridge/bridge-table.h>
43 #include <dali/internal/accessibility/bridge/bridge-table-cell.h>
44 #include <dali/internal/accessibility/bridge/bridge-text.h>
45 #include <dali/internal/accessibility/bridge/bridge-value.h>
46 #include <dali/internal/accessibility/bridge/dummy/dummy-atspi.h>
47 #include <dali/internal/adaptor/common/adaptor-impl.h>
48 #include <dali/internal/system/common/environment-variables.h>
49
50 using namespace Dali::Accessibility;
51
52 namespace // unnamed namespace
53 {
54 const int RETRY_INTERVAL = 1000;
55
56 } // unnamed namespace
57
58 /**
59  * @brief The BridgeImpl class is to implement some Bridge functions.
60  */
61 class BridgeImpl : public virtual BridgeBase,
62                    public BridgeAccessible,
63                    public BridgeObject,
64                    public BridgeComponent,
65                    public BridgeCollection,
66                    public BridgeAction,
67                    public BridgeValue,
68                    public BridgeText,
69                    public BridgeEditableText,
70                    public BridgeSelection,
71                    public BridgeApplication,
72                    public BridgeHypertext,
73                    public BridgeHyperlink,
74                    public BridgeSocket,
75                    public BridgeTable,
76                    public BridgeTableCell
77 {
78   DBus::DBusClient                                              mAccessibilityStatusClient{};
79   DBus::DBusClient                                              mRegistryClient{};
80   DBus::DBusClient                                              mDirectReadingClient{};
81   bool                                                          mIsScreenReaderEnabled{false};
82   bool                                                          mIsEnabled{false};
83   std::unordered_map<int32_t, std::function<void(std::string)>> mDirectReadingCallbacks{};
84   Dali::Actor                                                   mHighlightedActor;
85   std::function<void(Dali::Actor)>                              mHighlightClearAction{nullptr};
86   Dali::CallbackBase*                                           mIdleCallback{};
87   Dali::Timer                                                   mInitializeTimer;
88   Dali::Timer                                                   mReadIsEnabledTimer;
89   Dali::Timer                                                   mReadScreenReaderEnabledTimer;
90   Dali::Timer                                                   mForceUpTimer;
91   std::string                                                   mPreferredBusName;
92
93 public:
94   BridgeImpl() = default;
95
96   /**
97    * @copydoc Dali::Accessibility::Bridge::Emit()
98    */
99   Consumed Emit(KeyEventType type, unsigned int keyCode, const std::string& keyName, unsigned int timeStamp, bool isText) override
100   {
101     if(!IsUp())
102     {
103       return Consumed::NO;
104     }
105
106     unsigned int keyType = 0;
107
108     switch(type)
109     {
110       case KeyEventType::KEY_PRESSED:
111       {
112         keyType = 0;
113         break;
114       }
115       case KeyEventType::KEY_RELEASED:
116       {
117         keyType = 1;
118         break;
119       }
120       default:
121       {
122         return Consumed::NO;
123       }
124     }
125
126     auto methodObject = mRegistryClient.method<bool(std::tuple<uint32_t, int32_t, int32_t, int32_t, int32_t, std::string, bool>)>("NotifyListenersSync");
127     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});
128     if(!result)
129     {
130       LOG() << result.getError().message;
131       return Consumed::NO;
132     }
133     return std::get<0>(result) ? Consumed::YES : Consumed::NO;
134   }
135
136   /**
137    * @copydoc Dali::Accessibility::Bridge::Pause()
138    */
139   void Pause() override
140   {
141     if(!IsUp())
142     {
143       return;
144     }
145
146     mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
147       if(!msg)
148       {
149         LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
150       }
151     },
152                                                                                          true);
153   }
154
155   /**
156    * @copydoc Dali::Accessibility::Bridge::Resume()
157    */
158   void Resume() override
159   {
160     if(!IsUp())
161     {
162       return;
163     }
164
165     mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
166       if(!msg)
167       {
168         LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
169       }
170     },
171                                                                                          false);
172   }
173
174   /**
175    * @copydoc Dali::Accessibility::Bridge::StopReading()
176    */
177   void StopReading(bool alsoNonDiscardable) override
178   {
179     if(!IsUp())
180     {
181       return;
182     }
183
184     mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("StopReading").asyncCall([](DBus::ValueOrError<void> msg) {
185       if(!msg)
186       {
187         LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
188       }
189     },
190                                                                                          alsoNonDiscardable);
191   }
192
193   /**
194    * @copydoc Dali::Accessibility::Bridge::Say()
195    */
196   void Say(const std::string& text, bool discardable, std::function<void(std::string)> callback) override
197   {
198     if(!IsUp())
199     {
200       return;
201     }
202
203     mDirectReadingClient.method<DBus::ValueOrError<std::string, bool, int32_t>(std::string, bool)>("ReadCommand").asyncCall([=](DBus::ValueOrError<std::string, bool, int32_t> msg) {
204       if(!msg)
205       {
206         LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
207       }
208       else if(callback)
209       {
210         mDirectReadingCallbacks.emplace(std::get<2>(msg), callback);
211       }
212     },
213                                                                                                                             text,
214                                                                                                                             discardable);
215   }
216
217   /**
218    * @copydoc Dali::Accessibility::Bridge::ForceDown()
219    */
220   void ForceDown() override
221   {
222     if(mData)
223     {
224       if(mData->mCurrentlyHighlightedActor && mData->mHighlightActor)
225       {
226         mData->mCurrentlyHighlightedActor.Remove(mData->mHighlightActor);
227       }
228       mData->mCurrentlyHighlightedActor = {};
229       mData->mHighlightActor            = {};
230
231       mDisabledSignal.Emit();
232       UnembedSocket(mApplication.GetAddress(), {AtspiDbusNameRegistry, "root"});
233       ReleaseBusName(mPreferredBusName);
234     }
235
236     mHighlightedActor     = {};
237     mHighlightClearAction = {};
238     BridgeAccessible::ForceDown();
239     mRegistryClient      = {};
240     mDirectReadingClient = {};
241     mDirectReadingCallbacks.clear();
242     mApplication.mChildren.clear();
243     ClearTimer();
244   }
245
246   void ClearTimer()
247   {
248     if(mInitializeTimer)
249     {
250       mInitializeTimer.Stop();
251       mInitializeTimer.Reset();
252     }
253
254     if(mReadIsEnabledTimer)
255     {
256       mReadIsEnabledTimer.Stop();
257       mReadIsEnabledTimer.Reset();
258     }
259
260     if(mReadScreenReaderEnabledTimer)
261     {
262       mReadScreenReaderEnabledTimer.Stop();
263       mReadScreenReaderEnabledTimer.Reset();
264     }
265
266     if(mForceUpTimer)
267     {
268       mForceUpTimer.Stop();
269       mForceUpTimer.Reset();
270     }
271   }
272
273   /**
274    * @copydoc Dali::Accessibility::Bridge::Terminate()
275    */
276   void Terminate() override
277   {
278     if(mData)
279     {
280       // The ~Window() after this point cannot emit DESTROY, because Bridge is not available. So emit DESTROY here.
281       for(auto windowAccessible : mApplication.mChildren)
282       {
283         BridgeObject::Emit(windowAccessible, WindowEvent::DESTROY);
284       }
285       mData->mCurrentlyHighlightedActor = {};
286       mData->mHighlightActor            = {};
287     }
288     ForceDown();
289     if((NULL != mIdleCallback) && Dali::Adaptor::IsAvailable())
290     {
291       Dali::Adaptor::Get().RemoveIdle(mIdleCallback);
292     }
293     mAccessibilityStatusClient = {};
294     mDbusServer                = {};
295     mConnectionPtr             = {};
296   }
297
298   bool ForceUpTimerCallback()
299   {
300     if(ForceUp() != ForceUpResult::FAILED)
301     {
302       return false;
303     }
304     return true;
305   }
306
307   /**
308    * @copydoc Dali::Accessibility::Bridge::ForceUp()
309    */
310   ForceUpResult ForceUp() override
311   {
312     auto forceUpResult = BridgeAccessible::ForceUp();
313     if(forceUpResult == ForceUpResult::ALREADY_UP)
314     {
315       return forceUpResult;
316     }
317     else if(forceUpResult == ForceUpResult::FAILED)
318     {
319       if(!mForceUpTimer)
320       {
321         mForceUpTimer = Dali::Timer::New(RETRY_INTERVAL);
322         mForceUpTimer.TickSignal().Connect(this, &BridgeImpl::ForceUpTimerCallback);
323         mForceUpTimer.Start();
324       }
325       return forceUpResult;
326     }
327
328     BridgeObject::RegisterInterfaces();
329     BridgeAccessible::RegisterInterfaces();
330     BridgeComponent::RegisterInterfaces();
331     BridgeCollection::RegisterInterfaces();
332     BridgeAction::RegisterInterfaces();
333     BridgeValue::RegisterInterfaces();
334     BridgeText::RegisterInterfaces();
335     BridgeEditableText::RegisterInterfaces();
336     BridgeSelection::RegisterInterfaces();
337     BridgeApplication::RegisterInterfaces();
338     BridgeHypertext::RegisterInterfaces();
339     BridgeHyperlink::RegisterInterfaces();
340     BridgeSocket::RegisterInterfaces();
341     BridgeTable::RegisterInterfaces();
342     BridgeTableCell::RegisterInterfaces();
343
344     RegisterOnBridge(&mApplication);
345
346     mRegistryClient      = {AtspiDbusNameRegistry, AtspiDbusPathDec, Accessible::GetInterfaceName(AtspiInterface::DEVICE_EVENT_CONTROLLER), mConnectionPtr};
347     mDirectReadingClient = DBus::DBusClient{DirectReadingDBusName, DirectReadingDBusPath, DirectReadingDBusInterface, mConnectionPtr};
348
349     mDirectReadingClient.addSignal<void(int32_t, std::string)>("ReadingStateChanged", [=](int32_t id, std::string readingState) {
350       auto it = mDirectReadingCallbacks.find(id);
351       if(it != mDirectReadingCallbacks.end())
352       {
353         it->second(readingState);
354         if(readingState != "ReadingPaused" && readingState != "ReadingResumed" && readingState != "ReadingStarted")
355         {
356           mDirectReadingCallbacks.erase(it);
357         }
358       }
359     });
360
361     RequestBusName(mPreferredBusName);
362
363     auto parentAddress = EmbedSocket(mApplication.GetAddress(), {AtspiDbusNameRegistry, "root"});
364     mApplication.mParent.SetAddress(std::move(parentAddress));
365     mEnabledSignal.Emit();
366
367     return ForceUpResult::JUST_STARTED;
368   }
369
370   /**
371    * @brief Sends a signal to dbus that the window is shown.
372    *
373    * @param[in] window The window to be shown
374    * @see Accessible::EmitShowing() and BridgeObject::EmitStateChanged()
375    */
376   void EmitShown(Dali::Window window)
377   {
378     auto windowAccessible = mApplication.GetWindowAccessible(window);
379     if(windowAccessible)
380     {
381       windowAccessible->EmitShowing(true);
382     }
383   }
384
385   /**
386    * @brief Sends a signal to dbus that the window is hidden.
387    *
388    * @param[in] window The window to be hidden
389    * @see Accessible::EmitShowing() and BridgeObject::EmitStateChanged()
390    */
391   void EmitHidden(Dali::Window window)
392   {
393     auto windowAccessible = mApplication.GetWindowAccessible(window);
394     if(windowAccessible)
395     {
396       windowAccessible->EmitShowing(false);
397     }
398   }
399
400   /**
401    * @brief Sends a signal to dbus that the window is activated.
402    *
403    * @param[in] window The window to be activated
404    * @see BridgeObject::Emit()
405    */
406   void EmitActivate(Dali::Window window)
407   {
408     auto windowAccessible = mApplication.GetWindowAccessible(window);
409     if(windowAccessible)
410     {
411       windowAccessible->Emit(WindowEvent::ACTIVATE, 0);
412     }
413   }
414
415   /**
416    * @brief Sends a signal to dbus that the window is deactivated.
417    *
418    * @param[in] window The window to be deactivated
419    * @see BridgeObject::Emit()
420    */
421   void EmitDeactivate(Dali::Window window)
422   {
423     auto windowAccessible = mApplication.GetWindowAccessible(window);
424     if(windowAccessible)
425     {
426       windowAccessible->Emit(WindowEvent::DEACTIVATE, 0);
427     }
428   }
429
430   /**
431    * @brief Sends a signal to dbus that the window is minimized.
432    *
433    * @param[in] window The window to be minimized
434    * @see BridgeObject::Emit()
435    */
436   void EmitMinimize(Dali::Window window)
437   {
438     auto windowAccessible = mApplication.GetWindowAccessible(window);
439     if(windowAccessible)
440     {
441       windowAccessible->Emit(WindowEvent::MINIMIZE, 0);
442     }
443   }
444
445   /**
446    * @brief Sends a signal to dbus that the window is restored.
447    *
448    * @param[in] window The window to be restored
449    * @param[in] detail Restored window state
450    * @see BridgeObject::Emit()
451    */
452   void EmitRestore(Dali::Window window, Dali::Accessibility::WindowRestoreType detail)
453   {
454     auto windowAccessible = mApplication.GetWindowAccessible(window);
455     if(windowAccessible)
456     {
457       windowAccessible->Emit(WindowEvent::RESTORE, static_cast<unsigned int>(detail));
458     }
459   }
460
461   /**
462    * @brief Sends a signal to dbus that the window is maximized.
463    *
464    * @param[in] window The window to be maximized
465    * @see BridgeObject::Emit()
466    */
467   void EmitMaximize(Dali::Window window)
468   {
469     auto windowAccessible = mApplication.GetWindowAccessible(window);
470     if(windowAccessible)
471     {
472       windowAccessible->Emit(WindowEvent::MAXIMIZE, 0);
473     }
474   }
475
476   /**
477    * @copydoc Dali::Accessibility::Bridge::WindowShown()
478    */
479   void WindowShown(Dali::Window window) override
480   {
481     if(IsUp())
482     {
483       EmitShown(window);
484     }
485   }
486
487   /**
488    * @copydoc Dali::Accessibility::Bridge::WindowHidden()
489    */
490   void WindowHidden(Dali::Window window) override
491   {
492     if(IsUp())
493     {
494       EmitHidden(window);
495     }
496   }
497
498   /**
499    * @copydoc Dali::Accessibility::Bridge::WindowFocused()
500    */
501   void WindowFocused(Dali::Window window) override
502   {
503     if(IsUp())
504     {
505       EmitActivate(window);
506     }
507   }
508
509   /**
510    * @copydoc Dali::Accessibility::Bridge::WindowUnfocused()
511    */
512   void WindowUnfocused(Dali::Window window) override
513   {
514     if(IsUp())
515     {
516       EmitDeactivate(window);
517     }
518   }
519
520   /**
521    * @copydoc Dali::Accessibility::Bridge::WindowMinimized()
522    */
523   void WindowMinimized(Dali::Window window) override
524   {
525     if(IsUp())
526     {
527       EmitMinimize(window);
528     }
529   }
530
531   /**
532    * @copydoc Dali::Accessibility::Bridge::WindowRestored()
533    */
534   void WindowRestored(Dali::Window window, WindowRestoreType detail) override
535   {
536     if(IsUp())
537     {
538       EmitRestore(window, detail);
539     }
540   }
541
542   /**
543    * @copydoc Dali::Accessibility::Bridge::WindowMaximized()
544    */
545   void WindowMaximized(Dali::Window window) override
546   {
547     if(IsUp())
548     {
549       EmitMaximize(window);
550     }
551   }
552
553   /**
554    * @copydoc Dali::Accessibility::Bridge::SuppressScreenReader()
555    */
556   void SuppressScreenReader(bool suppress) override
557   {
558     if(mIsScreenReaderSuppressed == suppress)
559     {
560       return;
561     }
562     mIsScreenReaderSuppressed = suppress;
563     ReadScreenReaderEnabledProperty();
564   }
565
566   void SwitchBridge()
567   {
568     if((!mIsScreenReaderSuppressed && mIsScreenReaderEnabled) || mIsEnabled)
569     {
570       ForceUp();
571     }
572     else
573     {
574       ForceDown();
575     }
576   }
577
578   bool ReadIsEnabledTimerCallback()
579   {
580     ReadIsEnabledProperty();
581     return false;
582   }
583
584   void ReadIsEnabledProperty()
585   {
586     mAccessibilityStatusClient.property<bool>("IsEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
587       if(!msg)
588       {
589         DALI_LOG_ERROR("Get IsEnabled property error: %s\n", msg.getError().message.c_str());
590         if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
591         {
592           if(!mReadIsEnabledTimer)
593           {
594             mReadIsEnabledTimer = Dali::Timer::New(RETRY_INTERVAL);
595             mReadIsEnabledTimer.TickSignal().Connect(this, &BridgeImpl::ReadIsEnabledTimerCallback);
596           }
597           mReadIsEnabledTimer.Start();
598         }
599         return;
600       }
601
602       if(mReadIsEnabledTimer)
603       {
604         mReadIsEnabledTimer.Stop();
605         mReadIsEnabledTimer.Reset();
606       }
607
608       mIsEnabled = std::get<0>(msg);
609       SwitchBridge();
610     });
611   }
612
613   void ListenIsEnabledProperty()
614   {
615     mAccessibilityStatusClient.addPropertyChangedEvent<bool>("IsEnabled", [this](bool res) {
616       mIsEnabled = res;
617       SwitchBridge();
618     });
619   }
620
621   bool ReadScreenReaderEnabledTimerCallback()
622   {
623     ReadScreenReaderEnabledProperty();
624     return false;
625   }
626
627   void ReadScreenReaderEnabledProperty()
628   {
629     // can be true because of SuppressScreenReader before init
630     if(!mAccessibilityStatusClient)
631     {
632       return;
633     }
634
635     mAccessibilityStatusClient.property<bool>("ScreenReaderEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
636       if(!msg)
637       {
638         DALI_LOG_ERROR("Get ScreenReaderEnabled property error: %s\n", msg.getError().message.c_str());
639         if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
640         {
641           if(!mReadScreenReaderEnabledTimer)
642           {
643             mReadScreenReaderEnabledTimer = Dali::Timer::New(RETRY_INTERVAL);
644             mReadScreenReaderEnabledTimer.TickSignal().Connect(this, &BridgeImpl::ReadScreenReaderEnabledTimerCallback);
645           }
646           mReadScreenReaderEnabledTimer.Start();
647         }
648         return;
649       }
650
651       if(mReadScreenReaderEnabledTimer)
652       {
653         mReadScreenReaderEnabledTimer.Stop();
654         mReadScreenReaderEnabledTimer.Reset();
655       }
656
657       mIsScreenReaderEnabled = std::get<0>(msg);
658       SwitchBridge();
659     });
660   }
661
662   void EmitScreenReaderEnabledSignal()
663   {
664     if(mIsScreenReaderEnabled)
665     {
666       mScreenReaderEnabledSignal.Emit();
667     }
668     else
669     {
670       mScreenReaderDisabledSignal.Emit();
671     }
672   }
673
674   void ListenScreenReaderEnabledProperty()
675   {
676     mAccessibilityStatusClient.addPropertyChangedEvent<bool>("ScreenReaderEnabled", [this](bool res) {
677       mIsScreenReaderEnabled = res;
678       EmitScreenReaderEnabledSignal();
679       SwitchBridge();
680     });
681   }
682
683   void ReadAndListenProperties()
684   {
685     ReadIsEnabledProperty();
686     ListenIsEnabledProperty();
687
688     ReadScreenReaderEnabledProperty();
689     ListenScreenReaderEnabledProperty();
690   }
691
692   bool InitializeAccessibilityStatusClient()
693   {
694     mAccessibilityStatusClient = DBus::DBusClient{A11yDbusName, A11yDbusPath, A11yDbusStatusInterface, DBus::ConnectionType::SESSION};
695
696     if(!mAccessibilityStatusClient)
697     {
698       DALI_LOG_ERROR("Accessibility Status DbusClient is not ready\n");
699       return false;
700     }
701
702     return true;
703   }
704
705   bool InitializeTimerCallback()
706   {
707     if(InitializeAccessibilityStatusClient())
708     {
709       ReadAndListenProperties();
710       return false;
711     }
712     return true;
713   }
714
715   bool OnIdleSignal()
716   {
717     if(InitializeAccessibilityStatusClient())
718     {
719       ReadAndListenProperties();
720       mIdleCallback = NULL;
721       return false;
722     }
723
724     if(!mInitializeTimer)
725     {
726       mInitializeTimer = Dali::Timer::New(RETRY_INTERVAL);
727       mInitializeTimer.TickSignal().Connect(this, &BridgeImpl::InitializeTimerCallback);
728     }
729     mInitializeTimer.Start();
730
731     mIdleCallback = NULL;
732     return false;
733   }
734
735   /**
736    * @copydoc Dali::Accessibility::Bridge::Initialize()
737    */
738   void Initialize() override
739   {
740     if(InitializeAccessibilityStatusClient())
741     {
742       ReadAndListenProperties();
743       return;
744     }
745
746     // Initialize failed. Try it again on Idle
747     if(Dali::Adaptor::IsAvailable())
748     {
749       Dali::Adaptor& adaptor = Dali::Adaptor::Get();
750       if(NULL == mIdleCallback)
751       {
752         mIdleCallback = MakeCallback(this, &BridgeImpl::OnIdleSignal);
753         adaptor.AddIdle(mIdleCallback, true);
754       }
755     }
756   }
757
758   /**
759    * @copydoc Dali::Accessibility::Bridge::GetScreenReaderEnabled()
760    */
761   bool GetScreenReaderEnabled() override
762   {
763     return mIsScreenReaderEnabled;
764   }
765
766   /**
767    * @copydoc Dali::Accessibility::Bridge::IsEnabled()
768    */
769   bool IsEnabled() override
770   {
771     return mIsEnabled;
772   }
773
774   Address EmbedSocket(const Address& plug, const Address& socket) override
775   {
776     auto client = CreateSocketClient(socket);
777     auto reply  = client.method<Address(Address)>("Embed").call(plug);
778
779     if(!reply)
780     {
781       DALI_LOG_ERROR("Failed to embed socket %s: %s", socket.ToString().c_str(), reply.getError().message.c_str());
782       return {};
783     }
784
785     return std::get<0>(reply.getValues());
786   }
787
788   void EmbedAtkSocket(const Address& plug, const Address& socket) override
789   {
790     auto client = CreateSocketClient(socket);
791
792     client.method<void(std::string)>("Embedded").asyncCall([](DBus::ValueOrError<void>) {}, ATSPI_PREFIX_PATH + plug.GetPath());
793   }
794
795   void UnembedSocket(const Address& plug, const Address& socket) override
796   {
797     auto client = CreateSocketClient(socket);
798
799     client.method<void(Address)>("Unembed").asyncCall([](DBus::ValueOrError<void>) {}, plug);
800   }
801
802   void SetSocketOffset(ProxyAccessible* socket, std::int32_t x, std::int32_t y) override
803   {
804     AddCoalescableMessage(CoalescableMessages::SET_OFFSET, socket, 1.0f, [=]() {
805       auto client = CreateSocketClient(socket->GetAddress());
806
807       client.method<void(std::int32_t, std::int32_t)>("SetOffset").asyncCall([](DBus::ValueOrError<void>) {}, x, y);
808     });
809   }
810
811   void SetExtentsOffset(std::int32_t x, std::int32_t y) override
812   {
813     if(mData)
814     {
815       mData->mExtentsOffset = {x, y};
816     }
817   }
818
819   void SetPreferredBusName(std::string_view preferredBusName) override
820   {
821     if(preferredBusName == mPreferredBusName)
822     {
823       return;
824     }
825
826     std::string oldPreferredBusName = std::move(mPreferredBusName);
827     mPreferredBusName               = std::string{preferredBusName};
828
829     if(IsUp())
830     {
831       ReleaseBusName(oldPreferredBusName);
832       RequestBusName(mPreferredBusName);
833     }
834     // else: request/release will be handled by ForceUp/ForceDown, respectively
835   }
836
837 private:
838   DBus::DBusClient CreateSocketClient(const Address& socket)
839   {
840     return {socket.GetBus(), ATSPI_PREFIX_PATH + socket.GetPath(), Accessible::GetInterfaceName(AtspiInterface::SOCKET), mConnectionPtr};
841   }
842
843   void RequestBusName(const std::string& busName)
844   {
845     if(busName.empty())
846     {
847       return;
848     }
849
850     DBus::requestBusName(mConnectionPtr, busName);
851   }
852
853   void ReleaseBusName(const std::string& busName)
854   {
855     if(busName.empty())
856     {
857       return;
858     }
859
860     DBus::releaseBusName(mConnectionPtr, busName);
861   }
862 }; // BridgeImpl
863
864 namespace // unnamed namespace
865 {
866 bool INITIALIZED_BRIDGE = false;
867
868 /**
869  * @brief Creates BridgeImpl instance.
870  *
871  * @return The BridgeImpl instance
872  * @note This method is to check environment variable first. If ATSPI is disable using env, it returns dummy bridge instance.
873  */
874 std::shared_ptr<Bridge> CreateBridge()
875 {
876   INITIALIZED_BRIDGE = true;
877
878   try
879   {
880     /* check environment variable first */
881     const char* envAtspiDisabled = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_DISABLE_ATSPI);
882     if(envAtspiDisabled && std::atoi(envAtspiDisabled) != 0)
883     {
884       return Dali::Accessibility::DummyBridge::GetInstance();
885     }
886
887     return std::make_shared<BridgeImpl>();
888   }
889   catch(const std::exception&)
890   {
891     DALI_LOG_ERROR("Failed to initialize AT-SPI bridge");
892     return Dali::Accessibility::DummyBridge::GetInstance();
893   }
894 }
895
896 } // unnamed namespace
897
898 // Dali::Accessibility::Bridge class implementation
899
900 std::shared_ptr<Bridge> Bridge::GetCurrentBridge()
901 {
902   static std::shared_ptr<Bridge> bridge;
903
904   if(bridge)
905   {
906     return bridge;
907   }
908   else if(mAutoInitState == AutoInitState::ENABLED)
909   {
910     bridge = CreateBridge();
911
912     /* check environment variable for suppressing screen-reader */
913     const char* envSuppressScreenReader = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_SUPPRESS_SCREEN_READER);
914     if(envSuppressScreenReader && std::atoi(envSuppressScreenReader) != 0)
915     {
916       bridge->SuppressScreenReader(true);
917     }
918
919     return bridge;
920   }
921
922   return Dali::Accessibility::DummyBridge::GetInstance();
923 }
924
925 void Bridge::DisableAutoInit()
926 {
927   if(INITIALIZED_BRIDGE)
928   {
929     DALI_LOG_ERROR("Bridge::DisableAutoInit() called after bridge auto-initialization");
930   }
931
932   mAutoInitState = AutoInitState::DISABLED;
933 }
934
935 void Bridge::EnableAutoInit()
936 {
937   mAutoInitState = AutoInitState::ENABLED;
938
939   if(INITIALIZED_BRIDGE)
940   {
941     return;
942   }
943
944   auto rootLayer       = Dali::Stage::GetCurrent().GetRootLayer(); // A root layer of the default window.
945   auto window          = Dali::DevelWindow::Get(rootLayer);
946   auto applicationName = Dali::Internal::Adaptor::Adaptor::GetApplicationPackageName();
947
948   auto accessible = Accessibility::Accessible::Get(rootLayer);
949
950   auto bridge = Bridge::GetCurrentBridge();
951   bridge->AddTopLevelWindow(accessible);
952   bridge->SetApplicationName(applicationName);
953   bridge->Initialize();
954
955   if(window && window.IsVisible())
956   {
957     bridge->WindowShown(window);
958   }
959 }
960
961 std::string Bridge::MakeBusNameForWidget(std::string_view widgetInstanceId)
962 {
963   // The bus name should consist of dot-separated alphanumeric elements, e.g. "com.example.BusName123".
964   // Allowed characters in each element: "[A-Z][a-z][0-9]_", but no element may start with a digit.
965
966   static const char prefix[]   = "com.samsung.dali.widget_";
967   static const char underscore = '_';
968
969   std::stringstream tmp;
970
971   tmp << prefix;
972
973   for(char ch : widgetInstanceId)
974   {
975     tmp << (std::isalnum(ch) ? ch : underscore);
976   }
977
978   return tmp.str();
979 }