2ebc0485bb90d3d7577d3be4f40b3ef617c217cb
[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 created.
372    *
373    * @param[in] window The window to be created
374    * @see BridgeObject::Emit()
375    */
376   void EmitCreated(Dali::Window window)
377   {
378     auto windowAccessible = mApplication.GetWindowAccessible(window);
379     if(windowAccessible)
380     {
381       windowAccessible->Emit(WindowEvent::CREATE, 0);
382     }
383   }
384
385   /**
386    * @brief Sends a signal to dbus that the window is shown.
387    *
388    * @param[in] window The window to be shown
389    * @see Accessible::EmitShowing() and BridgeObject::EmitStateChanged()
390    */
391   void EmitShown(Dali::Window window)
392   {
393     auto windowAccessible = mApplication.GetWindowAccessible(window);
394     if(windowAccessible)
395     {
396       windowAccessible->EmitShowing(true);
397     }
398   }
399
400   /**
401    * @brief Sends a signal to dbus that the window is hidden.
402    *
403    * @param[in] window The window to be hidden
404    * @see Accessible::EmitShowing() and BridgeObject::EmitStateChanged()
405    */
406   void EmitHidden(Dali::Window window)
407   {
408     auto windowAccessible = mApplication.GetWindowAccessible(window);
409     if(windowAccessible)
410     {
411       windowAccessible->EmitShowing(false);
412     }
413   }
414
415   /**
416    * @brief Sends a signal to dbus that the window is activated.
417    *
418    * @param[in] window The window to be activated
419    * @see BridgeObject::Emit()
420    */
421   void EmitActivate(Dali::Window window)
422   {
423     auto windowAccessible = mApplication.GetWindowAccessible(window);
424     if(windowAccessible)
425     {
426       windowAccessible->Emit(WindowEvent::ACTIVATE, 0);
427     }
428   }
429
430   /**
431    * @brief Sends a signal to dbus that the window is deactivated.
432    *
433    * @param[in] window The window to be deactivated
434    * @see BridgeObject::Emit()
435    */
436   void EmitDeactivate(Dali::Window window)
437   {
438     auto windowAccessible = mApplication.GetWindowAccessible(window);
439     if(windowAccessible)
440     {
441       windowAccessible->Emit(WindowEvent::DEACTIVATE, 0);
442     }
443   }
444
445   /**
446    * @brief Sends a signal to dbus that the window is minimized.
447    *
448    * @param[in] window The window to be minimized
449    * @see BridgeObject::Emit()
450    */
451   void EmitMinimize(Dali::Window window)
452   {
453     auto windowAccessible = mApplication.GetWindowAccessible(window);
454     if(windowAccessible)
455     {
456       windowAccessible->Emit(WindowEvent::MINIMIZE, 0);
457     }
458   }
459
460   /**
461    * @brief Sends a signal to dbus that the window is restored.
462    *
463    * @param[in] window The window to be restored
464    * @param[in] detail Restored window state
465    * @see BridgeObject::Emit()
466    */
467   void EmitRestore(Dali::Window window, Dali::Accessibility::WindowRestoreType detail)
468   {
469     auto windowAccessible = mApplication.GetWindowAccessible(window);
470     if(windowAccessible)
471     {
472       windowAccessible->Emit(WindowEvent::RESTORE, static_cast<unsigned int>(detail));
473     }
474   }
475
476   /**
477    * @brief Sends a signal to dbus that the window is maximized.
478    *
479    * @param[in] window The window to be maximized
480    * @see BridgeObject::Emit()
481    */
482   void EmitMaximize(Dali::Window window)
483   {
484     auto windowAccessible = mApplication.GetWindowAccessible(window);
485     if(windowAccessible)
486     {
487       windowAccessible->Emit(WindowEvent::MAXIMIZE, 0);
488     }
489   }
490
491   /**
492    * @copydoc Dali::Accessibility::Bridge::WindowCreated()
493    */
494   void WindowCreated(Dali::Window window) override
495   {
496     if(IsUp())
497     {
498       EmitCreated(window);
499     }
500   }
501
502   /**
503    * @copydoc Dali::Accessibility::Bridge::WindowShown()
504    */
505   void WindowShown(Dali::Window window) override
506   {
507     if(IsUp())
508     {
509       EmitShown(window);
510     }
511   }
512
513   /**
514    * @copydoc Dali::Accessibility::Bridge::WindowHidden()
515    */
516   void WindowHidden(Dali::Window window) override
517   {
518     if(IsUp())
519     {
520       EmitHidden(window);
521     }
522   }
523
524   /**
525    * @copydoc Dali::Accessibility::Bridge::WindowFocused()
526    */
527   void WindowFocused(Dali::Window window) override
528   {
529     if(IsUp())
530     {
531       EmitActivate(window);
532     }
533   }
534
535   /**
536    * @copydoc Dali::Accessibility::Bridge::WindowUnfocused()
537    */
538   void WindowUnfocused(Dali::Window window) override
539   {
540     if(IsUp())
541     {
542       EmitDeactivate(window);
543     }
544   }
545
546   /**
547    * @copydoc Dali::Accessibility::Bridge::WindowMinimized()
548    */
549   void WindowMinimized(Dali::Window window) override
550   {
551     if(IsUp())
552     {
553       EmitMinimize(window);
554     }
555   }
556
557   /**
558    * @copydoc Dali::Accessibility::Bridge::WindowRestored()
559    */
560   void WindowRestored(Dali::Window window, WindowRestoreType detail) override
561   {
562     if(IsUp())
563     {
564       EmitRestore(window, detail);
565     }
566   }
567
568   /**
569    * @copydoc Dali::Accessibility::Bridge::WindowMaximized()
570    */
571   void WindowMaximized(Dali::Window window) override
572   {
573     if(IsUp())
574     {
575       EmitMaximize(window);
576     }
577   }
578
579   /**
580    * @copydoc Dali::Accessibility::Bridge::SuppressScreenReader()
581    */
582   void SuppressScreenReader(bool suppress) override
583   {
584     if(mIsScreenReaderSuppressed == suppress)
585     {
586       return;
587     }
588     mIsScreenReaderSuppressed = suppress;
589     ReadScreenReaderEnabledProperty();
590   }
591
592   void SwitchBridge()
593   {
594     if((!mIsScreenReaderSuppressed && mIsScreenReaderEnabled) || mIsEnabled)
595     {
596       ForceUp();
597     }
598     else
599     {
600       ForceDown();
601     }
602   }
603
604   bool ReadIsEnabledTimerCallback()
605   {
606     ReadIsEnabledProperty();
607     return false;
608   }
609
610   void ReadIsEnabledProperty()
611   {
612     mAccessibilityStatusClient.property<bool>("IsEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
613       if(!msg)
614       {
615         DALI_LOG_ERROR("Get IsEnabled property error: %s\n", msg.getError().message.c_str());
616         if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
617         {
618           if(!mReadIsEnabledTimer)
619           {
620             mReadIsEnabledTimer = Dali::Timer::New(RETRY_INTERVAL);
621             mReadIsEnabledTimer.TickSignal().Connect(this, &BridgeImpl::ReadIsEnabledTimerCallback);
622           }
623           mReadIsEnabledTimer.Start();
624         }
625         return;
626       }
627
628       if(mReadIsEnabledTimer)
629       {
630         mReadIsEnabledTimer.Stop();
631         mReadIsEnabledTimer.Reset();
632       }
633
634       mIsEnabled = std::get<0>(msg);
635       SwitchBridge();
636     });
637   }
638
639   void ListenIsEnabledProperty()
640   {
641     mAccessibilityStatusClient.addPropertyChangedEvent<bool>("IsEnabled", [this](bool res) {
642       mIsEnabled = res;
643       SwitchBridge();
644     });
645   }
646
647   bool ReadScreenReaderEnabledTimerCallback()
648   {
649     ReadScreenReaderEnabledProperty();
650     return false;
651   }
652
653   void ReadScreenReaderEnabledProperty()
654   {
655     // can be true because of SuppressScreenReader before init
656     if(!mAccessibilityStatusClient)
657     {
658       return;
659     }
660
661     mAccessibilityStatusClient.property<bool>("ScreenReaderEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
662       if(!msg)
663       {
664         DALI_LOG_ERROR("Get ScreenReaderEnabled property error: %s\n", msg.getError().message.c_str());
665         if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
666         {
667           if(!mReadScreenReaderEnabledTimer)
668           {
669             mReadScreenReaderEnabledTimer = Dali::Timer::New(RETRY_INTERVAL);
670             mReadScreenReaderEnabledTimer.TickSignal().Connect(this, &BridgeImpl::ReadScreenReaderEnabledTimerCallback);
671           }
672           mReadScreenReaderEnabledTimer.Start();
673         }
674         return;
675       }
676
677       if(mReadScreenReaderEnabledTimer)
678       {
679         mReadScreenReaderEnabledTimer.Stop();
680         mReadScreenReaderEnabledTimer.Reset();
681       }
682
683       mIsScreenReaderEnabled = std::get<0>(msg);
684       SwitchBridge();
685     });
686   }
687
688   void EmitScreenReaderEnabledSignal()
689   {
690     if(mIsScreenReaderEnabled)
691     {
692       mScreenReaderEnabledSignal.Emit();
693     }
694     else
695     {
696       mScreenReaderDisabledSignal.Emit();
697     }
698   }
699
700   void ListenScreenReaderEnabledProperty()
701   {
702     mAccessibilityStatusClient.addPropertyChangedEvent<bool>("ScreenReaderEnabled", [this](bool res) {
703       mIsScreenReaderEnabled = res;
704       EmitScreenReaderEnabledSignal();
705       SwitchBridge();
706     });
707   }
708
709   void ReadAndListenProperties()
710   {
711     ReadIsEnabledProperty();
712     ListenIsEnabledProperty();
713
714     ReadScreenReaderEnabledProperty();
715     ListenScreenReaderEnabledProperty();
716   }
717
718   bool InitializeAccessibilityStatusClient()
719   {
720     mAccessibilityStatusClient = DBus::DBusClient{A11yDbusName, A11yDbusPath, A11yDbusStatusInterface, DBus::ConnectionType::SESSION};
721
722     if(!mAccessibilityStatusClient)
723     {
724       DALI_LOG_ERROR("Accessibility Status DbusClient is not ready\n");
725       return false;
726     }
727
728     return true;
729   }
730
731   bool InitializeTimerCallback()
732   {
733     if(InitializeAccessibilityStatusClient())
734     {
735       ReadAndListenProperties();
736       return false;
737     }
738     return true;
739   }
740
741   bool OnIdleSignal()
742   {
743     if(InitializeAccessibilityStatusClient())
744     {
745       ReadAndListenProperties();
746       mIdleCallback = NULL;
747       return false;
748     }
749
750     if(!mInitializeTimer)
751     {
752       mInitializeTimer = Dali::Timer::New(RETRY_INTERVAL);
753       mInitializeTimer.TickSignal().Connect(this, &BridgeImpl::InitializeTimerCallback);
754     }
755     mInitializeTimer.Start();
756
757     mIdleCallback = NULL;
758     return false;
759   }
760
761   /**
762    * @copydoc Dali::Accessibility::Bridge::Initialize()
763    */
764   void Initialize() override
765   {
766     if(InitializeAccessibilityStatusClient())
767     {
768       ReadAndListenProperties();
769       return;
770     }
771
772     // Initialize failed. Try it again on Idle
773     if(Dali::Adaptor::IsAvailable())
774     {
775       Dali::Adaptor& adaptor = Dali::Adaptor::Get();
776       if(NULL == mIdleCallback)
777       {
778         mIdleCallback = MakeCallback(this, &BridgeImpl::OnIdleSignal);
779         adaptor.AddIdle(mIdleCallback, true);
780       }
781     }
782   }
783
784   /**
785    * @copydoc Dali::Accessibility::Bridge::GetScreenReaderEnabled()
786    */
787   bool GetScreenReaderEnabled() override
788   {
789     return mIsScreenReaderEnabled;
790   }
791
792   /**
793    * @copydoc Dali::Accessibility::Bridge::IsEnabled()
794    */
795   bool IsEnabled() override
796   {
797     return mIsEnabled;
798   }
799
800   Address EmbedSocket(const Address& plug, const Address& socket) override
801   {
802     auto client = CreateSocketClient(socket);
803     auto reply  = client.method<Address(Address)>("Embed").call(plug);
804
805     if(!reply)
806     {
807       DALI_LOG_ERROR("Failed to embed socket %s: %s", socket.ToString().c_str(), reply.getError().message.c_str());
808       return {};
809     }
810
811     return std::get<0>(reply.getValues());
812   }
813
814   void EmbedAtkSocket(const Address& plug, const Address& socket) override
815   {
816     auto client = CreateSocketClient(socket);
817
818     client.method<void(std::string)>("Embedded").asyncCall([](DBus::ValueOrError<void>) {}, ATSPI_PREFIX_PATH + plug.GetPath());
819   }
820
821   void UnembedSocket(const Address& plug, const Address& socket) override
822   {
823     auto client = CreateSocketClient(socket);
824
825     client.method<void(Address)>("Unembed").asyncCall([](DBus::ValueOrError<void>) {}, plug);
826   }
827
828   void SetSocketOffset(ProxyAccessible* socket, std::int32_t x, std::int32_t y) override
829   {
830     AddCoalescableMessage(CoalescableMessages::SET_OFFSET, socket, 1.0f, [=]() {
831       auto client = CreateSocketClient(socket->GetAddress());
832
833       client.method<void(std::int32_t, std::int32_t)>("SetOffset").asyncCall([](DBus::ValueOrError<void>) {}, x, y);
834     });
835   }
836
837   void SetExtentsOffset(std::int32_t x, std::int32_t y) override
838   {
839     if(mData)
840     {
841       mData->mExtentsOffset = {x, y};
842     }
843   }
844
845   void SetPreferredBusName(std::string_view preferredBusName) override
846   {
847     if(preferredBusName == mPreferredBusName)
848     {
849       return;
850     }
851
852     std::string oldPreferredBusName = std::move(mPreferredBusName);
853     mPreferredBusName               = std::string{preferredBusName};
854
855     if(IsUp())
856     {
857       ReleaseBusName(oldPreferredBusName);
858       RequestBusName(mPreferredBusName);
859     }
860     // else: request/release will be handled by ForceUp/ForceDown, respectively
861   }
862
863 private:
864   DBus::DBusClient CreateSocketClient(const Address& socket)
865   {
866     return {socket.GetBus(), ATSPI_PREFIX_PATH + socket.GetPath(), Accessible::GetInterfaceName(AtspiInterface::SOCKET), mConnectionPtr};
867   }
868
869   void RequestBusName(const std::string& busName)
870   {
871     if(busName.empty())
872     {
873       return;
874     }
875
876     DBus::requestBusName(mConnectionPtr, busName);
877   }
878
879   void ReleaseBusName(const std::string& busName)
880   {
881     if(busName.empty())
882     {
883       return;
884     }
885
886     DBus::releaseBusName(mConnectionPtr, busName);
887   }
888 }; // BridgeImpl
889
890 namespace // unnamed namespace
891 {
892 bool INITIALIZED_BRIDGE = false;
893
894 /**
895  * @brief Creates BridgeImpl instance.
896  *
897  * @return The BridgeImpl instance
898  * @note This method is to check environment variable first. If ATSPI is disable using env, it returns dummy bridge instance.
899  */
900 std::shared_ptr<Bridge> CreateBridge()
901 {
902   INITIALIZED_BRIDGE = true;
903
904   try
905   {
906     /* check environment variable first */
907     const char* envAtspiDisabled = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_DISABLE_ATSPI);
908     if(envAtspiDisabled && std::atoi(envAtspiDisabled) != 0)
909     {
910       return Dali::Accessibility::DummyBridge::GetInstance();
911     }
912
913     return std::make_shared<BridgeImpl>();
914   }
915   catch(const std::exception&)
916   {
917     DALI_LOG_ERROR("Failed to initialize AT-SPI bridge");
918     return Dali::Accessibility::DummyBridge::GetInstance();
919   }
920 }
921
922 } // unnamed namespace
923
924 // Dali::Accessibility::Bridge class implementation
925
926 std::shared_ptr<Bridge> Bridge::GetCurrentBridge()
927 {
928   static std::shared_ptr<Bridge> bridge;
929
930   if(bridge)
931   {
932     return bridge;
933   }
934   else if(mAutoInitState == AutoInitState::ENABLED)
935   {
936     bridge = CreateBridge();
937
938     /* check environment variable for suppressing screen-reader */
939     const char* envSuppressScreenReader = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_SUPPRESS_SCREEN_READER);
940     if(envSuppressScreenReader && std::atoi(envSuppressScreenReader) != 0)
941     {
942       bridge->SuppressScreenReader(true);
943     }
944
945     return bridge;
946   }
947
948   return Dali::Accessibility::DummyBridge::GetInstance();
949 }
950
951 void Bridge::DisableAutoInit()
952 {
953   if(INITIALIZED_BRIDGE)
954   {
955     DALI_LOG_ERROR("Bridge::DisableAutoInit() called after bridge auto-initialization");
956   }
957
958   mAutoInitState = AutoInitState::DISABLED;
959 }
960
961 void Bridge::EnableAutoInit()
962 {
963   mAutoInitState = AutoInitState::ENABLED;
964
965   if(INITIALIZED_BRIDGE)
966   {
967     return;
968   }
969
970   auto rootLayer       = Dali::Stage::GetCurrent().GetRootLayer(); // A root layer of the default window.
971   auto window          = Dali::DevelWindow::Get(rootLayer);
972   auto applicationName = Dali::Internal::Adaptor::Adaptor::GetApplicationPackageName();
973
974   auto accessible = Accessibility::Accessible::Get(rootLayer);
975
976   auto bridge = Bridge::GetCurrentBridge();
977   bridge->AddTopLevelWindow(accessible);
978   bridge->SetApplicationName(applicationName);
979   bridge->Initialize();
980
981   if(window && window.IsVisible())
982   {
983     bridge->WindowShown(window);
984   }
985 }
986
987 std::string Bridge::MakeBusNameForWidget(std::string_view widgetInstanceId)
988 {
989   // The bus name should consist of dot-separated alphanumeric elements, e.g. "com.example.BusName123".
990   // Allowed characters in each element: "[A-Z][a-z][0-9]_", but no element may start with a digit.
991
992   static const char prefix[]   = "com.samsung.dali.widget_";
993   static const char underscore = '_';
994
995   std::stringstream tmp;
996
997   tmp << prefix;
998
999   for(char ch : widgetInstanceId)
1000   {
1001     tmp << (std::isalnum(ch) ? ch : underscore);
1002   }
1003
1004   return tmp.str();
1005 }