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) 2024 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-cell.h>
43 #include <dali/internal/accessibility/bridge/bridge-table.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         if(DALI_UNLIKELY(!adaptor.AddIdle(mIdleCallback, true)))
780         {
781           DALI_LOG_ERROR("Fail to add idle callback for bridge initialize. Call it synchronously.\n");
782           OnIdleSignal();
783         }
784       }
785     }
786   }
787
788   /**
789    * @copydoc Dali::Accessibility::Bridge::GetScreenReaderEnabled()
790    */
791   bool GetScreenReaderEnabled() override
792   {
793     return mIsScreenReaderEnabled;
794   }
795
796   /**
797    * @copydoc Dali::Accessibility::Bridge::IsEnabled()
798    */
799   bool IsEnabled() override
800   {
801     return mIsEnabled;
802   }
803
804   Address EmbedSocket(const Address& plug, const Address& socket) override
805   {
806     auto client = CreateSocketClient(socket);
807     auto reply  = client.method<Address(Address)>("Embed").call(plug);
808
809     if(!reply)
810     {
811       DALI_LOG_ERROR("Failed to embed socket %s: %s", socket.ToString().c_str(), reply.getError().message.c_str());
812       return {};
813     }
814
815     return std::get<0>(reply.getValues());
816   }
817
818   void EmbedAtkSocket(const Address& plug, const Address& socket) override
819   {
820     auto client = CreateSocketClient(socket);
821
822     client.method<void(std::string)>("Embedded").asyncCall([](DBus::ValueOrError<void>) {}, ATSPI_PREFIX_PATH + plug.GetPath());
823   }
824
825   void UnembedSocket(const Address& plug, const Address& socket) override
826   {
827     auto client = CreateSocketClient(socket);
828
829     client.method<void(Address)>("Unembed").asyncCall([](DBus::ValueOrError<void>) {}, plug);
830   }
831
832   void SetSocketOffset(ProxyAccessible* socket, std::int32_t x, std::int32_t y) override
833   {
834     AddCoalescableMessage(CoalescableMessages::SET_OFFSET, socket, 1.0f, [=]() {
835       auto client = CreateSocketClient(socket->GetAddress());
836
837       client.method<void(std::int32_t, std::int32_t)>("SetOffset").asyncCall([](DBus::ValueOrError<void>) {}, x, y);
838     });
839   }
840
841   void SetExtentsOffset(std::int32_t x, std::int32_t y) override
842   {
843     if(mData)
844     {
845       mData->mExtentsOffset = {x, y};
846     }
847   }
848
849   void SetPreferredBusName(std::string_view preferredBusName) override
850   {
851     if(preferredBusName == mPreferredBusName)
852     {
853       return;
854     }
855
856     std::string oldPreferredBusName = std::move(mPreferredBusName);
857     mPreferredBusName               = std::string{preferredBusName};
858
859     if(IsUp())
860     {
861       ReleaseBusName(oldPreferredBusName);
862       RequestBusName(mPreferredBusName);
863     }
864     // else: request/release will be handled by ForceUp/ForceDown, respectively
865   }
866
867 private:
868   DBus::DBusClient CreateSocketClient(const Address& socket)
869   {
870     return {socket.GetBus(), ATSPI_PREFIX_PATH + socket.GetPath(), Accessible::GetInterfaceName(AtspiInterface::SOCKET), mConnectionPtr};
871   }
872
873   void RequestBusName(const std::string& busName)
874   {
875     if(busName.empty())
876     {
877       return;
878     }
879
880     DBus::requestBusName(mConnectionPtr, busName);
881   }
882
883   void ReleaseBusName(const std::string& busName)
884   {
885     if(busName.empty())
886     {
887       return;
888     }
889
890     DBus::releaseBusName(mConnectionPtr, busName);
891   }
892 }; // BridgeImpl
893
894 namespace // unnamed namespace
895 {
896 bool INITIALIZED_BRIDGE = false;
897
898 /**
899  * @brief Creates BridgeImpl instance.
900  *
901  * @return The BridgeImpl instance
902  * @note This method is to check environment variable first. If ATSPI is disable using env, it returns dummy bridge instance.
903  */
904 std::shared_ptr<Bridge> CreateBridge()
905 {
906   INITIALIZED_BRIDGE = true;
907
908   try
909   {
910     /* check environment variable first */
911     const char* envAtspiDisabled = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_DISABLE_ATSPI);
912     if(envAtspiDisabled && std::atoi(envAtspiDisabled) != 0)
913     {
914       return Dali::Accessibility::DummyBridge::GetInstance();
915     }
916
917     return std::make_shared<BridgeImpl>();
918   }
919   catch(const std::exception&)
920   {
921     DALI_LOG_ERROR("Failed to initialize AT-SPI bridge");
922     return Dali::Accessibility::DummyBridge::GetInstance();
923   }
924 }
925
926 } // unnamed namespace
927
928 // Dali::Accessibility::Bridge class implementation
929
930 std::shared_ptr<Bridge> Bridge::GetCurrentBridge()
931 {
932   static std::shared_ptr<Bridge> bridge;
933
934   if(bridge)
935   {
936     return bridge;
937   }
938   else if(mAutoInitState == AutoInitState::ENABLED)
939   {
940     bridge = CreateBridge();
941
942     /* check environment variable for suppressing screen-reader */
943     const char* envSuppressScreenReader = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_SUPPRESS_SCREEN_READER);
944     if(envSuppressScreenReader && std::atoi(envSuppressScreenReader) != 0)
945     {
946       bridge->SuppressScreenReader(true);
947     }
948
949     return bridge;
950   }
951
952   return Dali::Accessibility::DummyBridge::GetInstance();
953 }
954
955 void Bridge::DisableAutoInit()
956 {
957   if(INITIALIZED_BRIDGE)
958   {
959     DALI_LOG_ERROR("Bridge::DisableAutoInit() called after bridge auto-initialization");
960   }
961
962   mAutoInitState = AutoInitState::DISABLED;
963 }
964
965 void Bridge::EnableAutoInit()
966 {
967   mAutoInitState = AutoInitState::ENABLED;
968
969   if(INITIALIZED_BRIDGE)
970   {
971     return;
972   }
973
974   auto rootLayer       = Dali::Stage::GetCurrent().GetRootLayer(); // A root layer of the default window.
975   auto window          = Dali::DevelWindow::Get(rootLayer);
976   auto applicationName = Dali::Internal::Adaptor::Adaptor::GetApplicationPackageName();
977
978   auto accessible = Accessibility::Accessible::Get(rootLayer);
979
980   auto bridge = Bridge::GetCurrentBridge();
981   bridge->AddTopLevelWindow(accessible);
982   bridge->SetApplicationName(applicationName);
983   bridge->Initialize();
984
985   if(window && window.IsVisible())
986   {
987     bridge->WindowShown(window);
988   }
989 }
990
991 std::string Bridge::MakeBusNameForWidget(std::string_view widgetInstanceId)
992 {
993   // The bus name should consist of dot-separated alphanumeric elements, e.g. "com.example.BusName123".
994   // Allowed characters in each element: "[A-Z][a-z][0-9]_", but no element may start with a digit.
995
996   static const char prefix[]   = "com.samsung.dali.widget_";
997   static const char underscore = '_';
998
999   std::stringstream tmp;
1000
1001   tmp << prefix;
1002
1003   for(char ch : widgetInstanceId)
1004   {
1005     tmp << (std::isalnum(ch) ? ch : underscore);
1006   }
1007
1008   return tmp.str();
1009 }