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