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