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