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