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