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