Merge branch 'devel/master' into tizen
[platform/core/uifw/dali-adaptor.git] / dali / internal / accessibility / bridge / bridge-impl.cpp
1 /*
2  * Copyright (c) 2021 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/bridge-accessible.h>
31 #include <dali/internal/accessibility/bridge/bridge-action.h>
32 #include <dali/internal/accessibility/bridge/bridge-collection.h>
33 #include <dali/internal/accessibility/bridge/bridge-component.h>
34 #include <dali/internal/accessibility/bridge/bridge-editable-text.h>
35 #include <dali/internal/accessibility/bridge/bridge-hypertext.h>
36 #include <dali/internal/accessibility/bridge/bridge-hyperlink.h>
37 #include <dali/internal/accessibility/bridge/bridge-object.h>
38 #include <dali/internal/accessibility/bridge/bridge-selection.h>
39 #include <dali/internal/accessibility/bridge/bridge-socket.h>
40 #include <dali/internal/accessibility/bridge/bridge-text.h>
41 #include <dali/internal/accessibility/bridge/bridge-value.h>
42 #include <dali/internal/accessibility/bridge/bridge-application.h>
43 #include <dali/internal/accessibility/bridge/dummy-atspi.h>
44 #include <dali/internal/adaptor/common/adaptor-impl.h>
45 #include <dali/internal/system/common/environment-variables.h>
46
47 using namespace Dali::Accessibility;
48
49 namespace // unnamed namespace
50 {
51
52 const int RETRY_INTERVAL = 1000;
53
54 } // unnamed namespace
55
56 /**
57  * @brief The BridgeImpl class is to implement some Bridge functions.
58  */
59 class BridgeImpl : public virtual BridgeBase,
60                    public BridgeAccessible,
61                    public BridgeObject,
62                    public BridgeComponent,
63                    public BridgeCollection,
64                    public BridgeAction,
65                    public BridgeValue,
66                    public BridgeText,
67                    public BridgeEditableText,
68                    public BridgeSelection,
69                    public BridgeApplication,
70                    public BridgeHypertext,
71                    public BridgeHyperlink,
72                    public BridgeSocket
73 {
74   DBus::DBusClient                                              mAccessibilityStatusClient;
75   DBus::DBusClient                                              mRegistryClient;
76   DBus::DBusClient                                              mDirectReadingClient;
77   bool                                                          mIsScreenReaderEnabled = false;
78   bool                                                          mIsEnabled             = false;
79   bool                                                          mIsShown               = false;
80   std::unordered_map<int32_t, std::function<void(std::string)>> mDirectReadingCallbacks;
81   Dali::Actor                                                   mHighlightedActor;
82   std::function<void(Dali::Actor)>                              mHighlightClearAction;
83   Dali::CallbackBase*                                           mIdleCallback          = NULL;
84   Dali::Timer                                                   mInitializeTimer;
85   Dali::Timer                                                   mReadIsEnabledTimer;
86   Dali::Timer                                                   mReadScreenReaderEnabledTimer;
87   Dali::Timer                                                   mForceUpTimer;
88
89 public:
90   BridgeImpl()
91   {
92   }
93
94   /**
95    * @copydoc Dali::Accessibility::Bridge::Emit()
96    */
97   Consumed Emit(KeyEventType type, unsigned int keyCode, const std::string& keyName, unsigned int timeStamp, bool isText) override
98   {
99     if(!IsUp())
100     {
101       return Consumed::NO;
102     }
103
104     unsigned int keyType = 0;
105
106     switch(type)
107     {
108       case KeyEventType::KEY_PRESSED:
109       {
110         keyType = 0;
111         break;
112       }
113       case KeyEventType::KEY_RELEASED:
114       {
115         keyType = 1;
116         break;
117       }
118       default:
119       {
120         return Consumed::NO;
121       }
122     }
123
124     auto methodObject = mRegistryClient.method<bool(std::tuple<uint32_t, int32_t, int32_t, int32_t, int32_t, std::string, bool>)>("NotifyListenersSync");
125     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});
126     if(!result)
127     {
128       LOG() << result.getError().message;
129       return Consumed::NO;
130     }
131     return std::get<0>(result) ? Consumed::YES : Consumed::NO;
132   }
133
134   /**
135    * @copydoc Dali::Accessibility::Bridge::Pause()
136    */
137   void Pause() override
138   {
139     if(!IsUp())
140     {
141       return;
142     }
143
144     mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
145       if(!msg)
146       {
147         LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
148       }
149     },
150                                                                                         true);
151   }
152
153   /**
154    * @copydoc Dali::Accessibility::Bridge::Resume()
155    */
156   void Resume() override
157   {
158     if(!IsUp())
159     {
160       return;
161     }
162
163     mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
164       if(!msg)
165       {
166         LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
167       }
168     },
169                                                                                         false);
170   }
171
172   /**
173    * @copydoc Dali::Accessibility::Bridge::StopReading()
174    */
175   void StopReading(bool alsoNonDiscardable) override
176   {
177     if(!IsUp())
178     {
179       return;
180     }
181
182     mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("StopReading").asyncCall([](DBus::ValueOrError<void> msg) {
183       if(!msg)
184       {
185         LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
186       }
187     },
188                                                                                         alsoNonDiscardable);
189   }
190
191   /**
192    * @copydoc Dali::Accessibility::Bridge::Say()
193    */
194   void Say(const std::string& text, bool discardable, std::function<void(std::string)> callback) override
195   {
196     if(!IsUp())
197     {
198       return;
199     }
200
201     mDirectReadingClient.method<DBus::ValueOrError<std::string, bool, int32_t>(std::string, bool)>("ReadCommand").asyncCall([=](DBus::ValueOrError<std::string, bool, int32_t> msg) {
202       if(!msg)
203       {
204         LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
205       }
206       else if(callback)
207       {
208         mDirectReadingCallbacks.emplace(std::get<2>(msg), callback);
209       }
210     },
211                                                                                                                            text,
212                                                                                                                            discardable);
213   }
214
215   /**
216    * @copydoc Dali::Accessibility::Bridge::ForceDown()
217    */
218   void ForceDown() override
219   {
220     if(mData)
221     {
222       if(mData->mCurrentlyHighlightedActor && mData->mHighlightActor)
223       {
224         mData->mCurrentlyHighlightedActor.Remove(mData->mHighlightActor);
225       }
226       mData->mCurrentlyHighlightedActor = {};
227       mData->mHighlightActor            = {};
228
229       mDisabledSignal.Emit();
230       UnembedSocket(mApplication.GetAddress(), {AtspiDbusNameRegistry, "root"});
231     }
232
233     mHighlightedActor     = {};
234     mHighlightClearAction = {};
235     BridgeAccessible::ForceDown();
236     mRegistryClient       = {};
237     mDirectReadingClient  = {};
238     mDirectReadingCallbacks.clear();
239     mApplication.mChildren.clear();
240     ClearTimer();
241   }
242
243   void ClearTimer()
244   {
245     if(mInitializeTimer)
246     {
247       mInitializeTimer.Stop();
248       mInitializeTimer.Reset();
249     }
250
251     if(mReadIsEnabledTimer)
252     {
253       mReadIsEnabledTimer.Stop();
254       mReadIsEnabledTimer.Reset();
255     }
256
257     if(mReadScreenReaderEnabledTimer)
258     {
259       mReadScreenReaderEnabledTimer.Stop();
260       mReadScreenReaderEnabledTimer.Reset();
261     }
262
263     if(mForceUpTimer)
264     {
265       mForceUpTimer.Stop();
266       mForceUpTimer.Reset();
267     }
268   }
269
270   /**
271    * @copydoc Dali::Accessibility::Bridge::Terminate()
272    */
273   void Terminate() override
274   {
275     if(mData)
276     {
277       // The ~Window() after this point cannot emit DESTROY, because Bridge is not available. So emit DESTROY here.
278       for(auto windowAccessible : mApplication.mChildren)
279       {
280         BridgeObject::Emit(windowAccessible, WindowEvent::DESTROY);
281       }
282       mData->mCurrentlyHighlightedActor = {};
283       mData->mHighlightActor            = {};
284     }
285     ForceDown();
286     if((NULL != mIdleCallback) && Dali::Adaptor::IsAvailable())
287     {
288       Dali::Adaptor::Get().RemoveIdle(mIdleCallback);
289     }
290     mAccessibilityStatusClient        = {};
291     mDbusServer                       = {};
292     mConnectionPtr                    = {};
293   }
294
295   bool ForceUpTimerCallback()
296   {
297     if(ForceUp() != ForceUpResult::FAILED)
298     {
299       return false;
300     }
301     return true;
302   }
303
304   /**
305    * @copydoc Dali::Accessibility::Bridge::ForceUp()
306    */
307   ForceUpResult ForceUp() override
308   {
309     auto forceUpResult = BridgeAccessible::ForceUp();
310     if(forceUpResult == ForceUpResult::ALREADY_UP)
311     {
312       return forceUpResult;
313     }
314     else if(forceUpResult == ForceUpResult::FAILED)
315     {
316       if(!mForceUpTimer)
317       {
318         mForceUpTimer = Dali::Timer::New(RETRY_INTERVAL);
319         mForceUpTimer.TickSignal().Connect(this, &BridgeImpl::ForceUpTimerCallback);
320         mForceUpTimer.Start();
321       }
322       return forceUpResult;
323     }
324
325     BridgeObject::RegisterInterfaces();
326     BridgeAccessible::RegisterInterfaces();
327     BridgeComponent::RegisterInterfaces();
328     BridgeCollection::RegisterInterfaces();
329     BridgeAction::RegisterInterfaces();
330     BridgeValue::RegisterInterfaces();
331     BridgeText::RegisterInterfaces();
332     BridgeEditableText::RegisterInterfaces();
333     BridgeSelection::RegisterInterfaces();
334     BridgeApplication::RegisterInterfaces();
335     BridgeHypertext::RegisterInterfaces();
336     BridgeHyperlink::RegisterInterfaces();
337     BridgeSocket::RegisterInterfaces();
338
339     RegisterOnBridge(&mApplication);
340
341     mRegistryClient      = {AtspiDbusNameRegistry, AtspiDbusPathDec, Accessible::GetInterfaceName(AtspiInterface::DEVICE_EVENT_CONTROLLER), mConnectionPtr};
342     mDirectReadingClient = DBus::DBusClient{DirectReadingDBusName, DirectReadingDBusPath, DirectReadingDBusInterface, mConnectionPtr};
343
344     mDirectReadingClient.addSignal<void(int32_t, std::string)>("ReadingStateChanged", [=](int32_t id, std::string readingState) {
345       auto it = mDirectReadingCallbacks.find(id);
346       if(it != mDirectReadingCallbacks.end())
347       {
348         it->second(readingState);
349         if(readingState != "ReadingPaused" && readingState != "ReadingResumed" && readingState != "ReadingStarted")
350         {
351           mDirectReadingCallbacks.erase(it);
352         }
353       }
354     });
355
356     auto parentAddress = EmbedSocket(mApplication.GetAddress(), {AtspiDbusNameRegistry, "root"});
357     mApplication.mParent.SetAddress(std::move(parentAddress));
358     mEnabledSignal.Emit();
359
360     return ForceUpResult::JUST_STARTED;
361   }
362
363   /**
364    * @brief Sends a signal to dbus that the window is shown.
365    *
366    * @param[in] window The window to be shown
367    * @see Accessible::EmitShowing() and BridgeObject::EmitStateChanged()
368    */
369   void EmitShown(Dali::Window window)
370   {
371     auto windowAccessible = mApplication.GetWindowAccessible(window);
372     if(windowAccessible)
373     {
374       windowAccessible->EmitShowing(true);
375     }
376   }
377
378   /**
379    * @brief Sends a signal to dbus that the window is hidden.
380    *
381    * @param[in] window The window to be hidden
382    * @see Accessible::EmitShowing() and BridgeObject::EmitStateChanged()
383    */
384   void EmitHidden(Dali::Window window)
385   {
386     auto windowAccessible = mApplication.GetWindowAccessible(window);
387     if(windowAccessible)
388     {
389       windowAccessible->EmitShowing(false);
390     }
391   }
392
393   /**
394    * @brief Sends a signal to dbus that the window is activated.
395    *
396    * @param[in] window The window to be activated
397    * @see BridgeObject::Emit()
398    */
399   void EmitActivate(Dali::Window window)
400   {
401     auto windowAccessible = mApplication.GetWindowAccessible(window);
402     if(windowAccessible)
403     {
404       windowAccessible->Emit(WindowEvent::ACTIVATE, 0);
405     }
406   }
407
408   /**
409    * @brief Sends a signal to dbus that the window is deactivated.
410    *
411    * @param[in] window The window to be deactivated
412    * @see BridgeObject::Emit()
413    */
414   void EmitDeactivate(Dali::Window window)
415   {
416     auto windowAccessible = mApplication.GetWindowAccessible(window);
417     if(windowAccessible)
418     {
419       windowAccessible->Emit(WindowEvent::DEACTIVATE, 0);
420     }
421   }
422
423   /**
424    * @copydoc Dali::Accessibility::Bridge::WindowShown()
425    */
426   void WindowShown(Dali::Window window) override
427   {
428     if(!mIsShown && IsUp())
429     {
430       EmitShown(window);
431     }
432     mIsShown = true;
433   }
434
435   /**
436    * @copydoc Dali::Accessibility::Bridge::WindowHidden()
437    */
438   void WindowHidden(Dali::Window window) override
439   {
440     if(mIsShown && IsUp())
441     {
442       EmitHidden(window);
443     }
444     mIsShown = false;
445   }
446
447   /**
448    * @copydoc Dali::Accessibility::Bridge::WindowFocused()
449    */
450   void WindowFocused(Dali::Window window) override
451   {
452     if(mIsShown && IsUp())
453     {
454       EmitActivate(window);
455     }
456   }
457
458   /**
459    * @copydoc Dali::Accessibility::Bridge::WindowUnfocused()
460    */
461   void WindowUnfocused(Dali::Window window) override
462   {
463     if(mIsShown && IsUp())
464     {
465       EmitDeactivate(window);
466     }
467   }
468
469   /**
470    * @copydoc Dali::Accessibility::Bridge::SuppressScreenReader()
471    */
472   void SuppressScreenReader(bool suppress) override
473   {
474     if(mIsScreenReaderSuppressed == suppress)
475     {
476       return;
477     }
478     mIsScreenReaderSuppressed = suppress;
479     ReadScreenReaderEnabledProperty();
480   }
481
482   void SwitchBridge()
483   {
484     if((!mIsScreenReaderSuppressed && mIsScreenReaderEnabled) || mIsEnabled)
485     {
486       ForceUp();
487     }
488     else
489     {
490       ForceDown();
491     }
492   }
493
494   bool ReadIsEnabledTimerCallback()
495   {
496     ReadIsEnabledProperty();
497     return false;
498   }
499
500   void ReadIsEnabledProperty()
501   {
502     mAccessibilityStatusClient.property<bool>("IsEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
503       if(!msg)
504       {
505         DALI_LOG_ERROR("Get IsEnabled property error: %s\n", msg.getError().message.c_str());
506         if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
507         {
508           if(!mReadIsEnabledTimer)
509           {
510             mReadIsEnabledTimer = Dali::Timer::New(RETRY_INTERVAL);
511             mReadIsEnabledTimer.TickSignal().Connect(this, &BridgeImpl::ReadIsEnabledTimerCallback);
512           }
513           mReadIsEnabledTimer.Start();
514         }
515         return;
516       }
517
518       if(mReadIsEnabledTimer)
519       {
520         mReadIsEnabledTimer.Stop();
521         mReadIsEnabledTimer.Reset();
522       }
523
524       mIsEnabled = std::get<0>(msg);
525       SwitchBridge();
526     });
527   }
528
529   void ListenIsEnabledProperty()
530   {
531     mAccessibilityStatusClient.addPropertyChangedEvent<bool>("IsEnabled", [this](bool res) {
532       mIsEnabled = res;
533       SwitchBridge();
534     });
535   }
536
537   bool ReadScreenReaderEnabledTimerCallback()
538   {
539     ReadScreenReaderEnabledProperty();
540     return false;
541   }
542
543   void ReadScreenReaderEnabledProperty()
544   {
545     // can be true because of SuppressScreenReader before init
546     if (!mAccessibilityStatusClient)
547     {
548       return;
549     }
550
551     mAccessibilityStatusClient.property<bool>("ScreenReaderEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
552       if(!msg)
553       {
554         DALI_LOG_ERROR("Get ScreenReaderEnabled property error: %s\n", msg.getError().message.c_str());
555         if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
556         {
557           if(!mReadScreenReaderEnabledTimer)
558           {
559             mReadScreenReaderEnabledTimer = Dali::Timer::New(RETRY_INTERVAL);
560             mReadScreenReaderEnabledTimer.TickSignal().Connect(this, &BridgeImpl::ReadScreenReaderEnabledTimerCallback);
561           }
562           mReadScreenReaderEnabledTimer.Start();
563         }
564         return;
565       }
566
567       if(mReadScreenReaderEnabledTimer)
568       {
569         mReadScreenReaderEnabledTimer.Stop();
570         mReadScreenReaderEnabledTimer.Reset();
571       }
572
573       mIsScreenReaderEnabled = std::get<0>(msg);
574       SwitchBridge();
575     });
576   }
577
578   void EmitScreenReaderEnabledSignal()
579   {
580     if (mIsScreenReaderEnabled)
581     {
582       mScreenReaderEnabledSignal.Emit();
583     }
584     else
585     {
586       mScreenReaderDisabledSignal.Emit();
587     }
588   }
589
590   void ListenScreenReaderEnabledProperty()
591   {
592     mAccessibilityStatusClient.addPropertyChangedEvent<bool>("ScreenReaderEnabled", [this](bool res) {
593       mIsScreenReaderEnabled = res;
594       EmitScreenReaderEnabledSignal();
595       SwitchBridge();
596     });
597   }
598
599   void ReadAndListenProperties()
600   {
601     ReadIsEnabledProperty();
602     ListenIsEnabledProperty();
603
604     ReadScreenReaderEnabledProperty();
605     ListenScreenReaderEnabledProperty();
606   }
607
608   bool InitializeAccessibilityStatusClient()
609   {
610     mAccessibilityStatusClient = DBus::DBusClient{A11yDbusName, A11yDbusPath, A11yDbusStatusInterface, DBus::ConnectionType::SESSION};
611
612     if (!mAccessibilityStatusClient)
613     {
614       DALI_LOG_ERROR("Accessibility Status DbusClient is not ready\n");
615       return false;
616     }
617
618     return true;
619   }
620
621   bool InitializeTimerCallback()
622   {
623     if ( InitializeAccessibilityStatusClient() )
624     {
625       ReadAndListenProperties();
626       return false;
627     }
628     return true;
629   }
630
631   bool OnIdleSignal()
632   {
633     if ( InitializeAccessibilityStatusClient() )
634     {
635       ReadAndListenProperties();
636       mIdleCallback = NULL;
637       return false;
638     }
639
640     if(!mInitializeTimer)
641     {
642       mInitializeTimer = Dali::Timer::New(RETRY_INTERVAL);
643       mInitializeTimer.TickSignal().Connect(this, &BridgeImpl::InitializeTimerCallback);
644     }
645     mInitializeTimer.Start();
646
647     mIdleCallback = NULL;
648     return false;
649   }
650
651   /**
652    * @copydoc Dali::Accessibility::Bridge::Initialize()
653    */
654   void Initialize() override
655   {
656     if ( InitializeAccessibilityStatusClient() )
657     {
658       ReadAndListenProperties();
659       return;
660     }
661
662     // Initialize failed. Try it again on Idle
663     if( Dali::Adaptor::IsAvailable() )
664     {
665       Dali::Adaptor& adaptor = Dali::Adaptor::Get();
666       if( NULL == mIdleCallback )
667       {
668         mIdleCallback = MakeCallback( this, &BridgeImpl::OnIdleSignal );
669         adaptor.AddIdle( mIdleCallback, true );
670       }
671     }
672   }
673
674   /**
675    * @copydoc Dali::Accessibility::Bridge::GetScreenReaderEnabled()
676    */
677   bool GetScreenReaderEnabled() override
678   {
679     return mIsScreenReaderEnabled;
680   }
681
682   /**
683    * @copydoc Dali::Accessibility::Bridge::IsEnabled()
684    */
685   bool IsEnabled() override
686   {
687     return mIsEnabled;
688   }
689
690   Address EmbedSocket(const Address& plug, const Address& socket) override
691   {
692     auto client = CreateSocketClient(socket);
693     auto reply  = client.method<Address(Address)>("Embed").call(plug);
694
695     if(!reply)
696     {
697       DALI_LOG_ERROR("Failed to embed socket %s: %s", socket.ToString().c_str(), reply.getError().message.c_str());
698       return {};
699     }
700
701     return std::get<0>(reply.getValues());
702   }
703
704   void EmbedAtkSocket(const Address& plug, const Address& socket) override
705   {
706     auto client = CreateSocketClient(socket);
707
708     client.method<void(std::string)>("Embedded").call(ATSPI_PREFIX_PATH + plug.GetPath());
709   }
710
711   void UnembedSocket(const Address& plug, const Address& socket) override
712   {
713     auto client = CreateSocketClient(socket);
714
715     client.method<void(Address)>("Unembed").call(plug);
716   }
717
718 private:
719   DBus::DBusClient CreateSocketClient(const Address& socket)
720   {
721     return {socket.GetBus(), ATSPI_PREFIX_PATH + socket.GetPath(), Accessible::GetInterfaceName(AtspiInterface::SOCKET), mConnectionPtr};
722   }
723 }; // BridgeImpl
724
725 namespace // unnamed namespace
726 {
727
728 bool INITIALIZED_BRIDGE = false;
729
730 /**
731  * @brief Creates BridgeImpl instance.
732  *
733  * @return The BridgeImpl instance
734  * @note This method is to check environment variable first. If ATSPI is disable using env, it returns dummy bridge instance.
735  */
736 std::shared_ptr<Bridge> CreateBridge()
737 {
738   INITIALIZED_BRIDGE = true;
739
740   try
741   {
742     /* check environment variable first */
743     const char* envAtspiDisabled = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_DISABLE_ATSPI);
744     if(envAtspiDisabled && std::atoi(envAtspiDisabled) != 0)
745     {
746       return Dali::Accessibility::DummyBridge::GetInstance();
747     }
748
749     return std::make_shared<BridgeImpl>();
750   }
751   catch(const std::exception&)
752   {
753     DALI_LOG_ERROR("Failed to initialize AT-SPI bridge");
754     return Dali::Accessibility::DummyBridge::GetInstance();
755   }
756 }
757
758 } // unnamed namespace
759
760 // Dali::Accessibility::Bridge class implementation
761
762 std::shared_ptr<Bridge> Bridge::GetCurrentBridge()
763 {
764   static std::shared_ptr<Bridge> bridge;
765
766   if(bridge)
767   {
768     return bridge;
769   }
770   else if(mAutoInitState == AutoInitState::ENABLED)
771   {
772     bridge = CreateBridge();
773
774     /* check environment variable for suppressing screen-reader */
775     const char* envSuppressScreenReader = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_SUPPRESS_SCREEN_READER);
776     if(envSuppressScreenReader && std::atoi(envSuppressScreenReader) != 0)
777     {
778       bridge->SuppressScreenReader(true);
779     }
780
781     return bridge;
782   }
783
784   return Dali::Accessibility::DummyBridge::GetInstance();
785 }
786
787 void Bridge::DisableAutoInit()
788 {
789   if(INITIALIZED_BRIDGE)
790   {
791     DALI_LOG_ERROR("Bridge::DisableAutoInit() called after bridge auto-initialization");
792   }
793
794   mAutoInitState = AutoInitState::DISABLED;
795 }
796
797 void Bridge::EnableAutoInit()
798 {
799   mAutoInitState = AutoInitState::ENABLED;
800
801   if(INITIALIZED_BRIDGE)
802   {
803     return;
804   }
805
806   auto rootLayer       = Dali::Stage::GetCurrent().GetRootLayer(); // A root layer of the default window.
807   auto window          = Dali::DevelWindow::Get(rootLayer);
808   auto applicationName = Dali::Internal::Adaptor::Adaptor::GetApplicationPackageName();
809
810   auto accessible = Accessibility::Accessible::Get(rootLayer);
811
812   auto bridge = Bridge::GetCurrentBridge();
813   bridge->AddTopLevelWindow(accessible);
814   bridge->SetApplicationName(applicationName);
815   bridge->Initialize();
816
817   if(window && window.IsVisible())
818   {
819     bridge->WindowShown(window);
820   }
821 }