[ATSPI] Read property in Async way
[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     mHighlightedActor     = {};
213     mHighlightClearAction = {};
214     BridgeAccessible::ForceDown();
215     mRegistryClient       = {};
216     mDirectReadingClient  = {};
217     mDirectReadingCallbacks.clear();
218     mApplication.mChildren.clear();
219     mApplication.mWindows.clear();
220   }
221
222   /**
223    * @copydoc Dali::Accessibility::Bridge::Terminate()
224    */
225   void Terminate() override
226   {
227     if(mData)
228     {
229       mData->mCurrentlyHighlightedActor = {};
230       mData->mHighlightActor            = {};
231     }
232     ForceDown();
233     if((NULL != mIdleCallback) && Dali::Adaptor::IsAvailable())
234     {
235       Dali::Adaptor::Get().RemoveIdle(mIdleCallback);
236     }
237     mAccessibilityStatusClient        = {};
238     mDbusServer                       = {};
239     mConnectionPtr                    = {};
240   }
241
242   /**
243    * @copydoc Dali::Accessibility::Bridge::ForceUp()
244    */
245   ForceUpResult ForceUp() override
246   {
247     if(BridgeAccessible::ForceUp() == ForceUpResult::ALREADY_UP)
248     {
249       return ForceUpResult::ALREADY_UP;
250     }
251
252     BridgeObject::RegisterInterfaces();
253     BridgeAccessible::RegisterInterfaces();
254     BridgeComponent::RegisterInterfaces();
255     BridgeCollection::RegisterInterfaces();
256     BridgeAction::RegisterInterfaces();
257     BridgeValue::RegisterInterfaces();
258     BridgeText::RegisterInterfaces();
259     BridgeEditableText::RegisterInterfaces();
260     BridgeSelection::RegisterInterfaces();
261     BridgeApplication::RegisterInterfaces();
262
263     RegisterOnBridge(&mApplication);
264
265     mRegistryClient      = {AtspiDbusNameRegistry, AtspiDbusPathDec, AtspiDbusInterfaceDec, mConnectionPtr};
266     mDirectReadingClient = DBus::DBusClient{DirectReadingDBusName, DirectReadingDBusPath, DirectReadingDBusInterface, mConnectionPtr};
267
268     mDirectReadingClient.addSignal<void(int32_t, std::string)>("ReadingStateChanged", [=](int32_t id, std::string readingState) {
269       auto it = mDirectReadingCallbacks.find(id);
270       if(it != mDirectReadingCallbacks.end())
271       {
272         it->second(readingState);
273         if(readingState != "ReadingPaused" && readingState != "ReadingResumed" && readingState != "ReadingStarted")
274         {
275           mDirectReadingCallbacks.erase(it);
276         }
277       }
278     });
279
280     auto    proxy = DBus::DBusClient{AtspiDbusNameRegistry, AtspiDbusPathRoot, AtspiDbusInterfaceSocket, mConnectionPtr};
281     Address root{"", "root"};
282     auto    res = proxy.method<Address(Address)>("Embed").call(root);
283     if(!res)
284     {
285       LOG() << "Call to Embed failed: " << res.getError().message;
286     }
287     assert(res);
288
289     mApplication.mParent.SetAddress(std::move(std::get<0>(res)));
290
291     if(mIsShown)
292     {
293       auto rootLayer = Dali::Stage::GetCurrent().GetRootLayer();
294       auto window    = Dali::DevelWindow::Get(rootLayer);
295       EmitActivate(window); // Currently, sends a signal that the default window is activated here.
296     }
297     return ForceUpResult::JUST_STARTED;
298   }
299
300   /**
301    * @brief Sends a signal to dbus that the window is shown.
302    *
303    * @param[in] window The window to be shown
304    * @see Accessible::EmitShowing() and BridgeObject::EmitStateChanged()
305    */
306   void EmitShown(Dali::Window window)
307   {
308     auto windowAccessible = mApplication.GetWindowAccessible(window);
309     if(windowAccessible)
310     {
311       windowAccessible->EmitShowing(true);
312     }
313   }
314
315   /**
316    * @brief Sends a signal to dbus that the window is hidden.
317    *
318    * @param[in] window The window to be hidden
319    * @see Accessible::EmitShowing() and BridgeObject::EmitStateChanged()
320    */
321   void EmitHidden(Dali::Window window)
322   {
323     auto windowAccessible = mApplication.GetWindowAccessible(window);
324     if(windowAccessible)
325     {
326       windowAccessible->EmitShowing(false);
327     }
328   }
329
330   /**
331    * @brief Sends a signal to dbus that the window is activated.
332    *
333    * @param[in] window The window to be activated
334    * @see BridgeObject::Emit()
335    */
336   void EmitActivate(Dali::Window window)
337   {
338     auto windowAccessible = mApplication.GetWindowAccessible(window);
339     if(windowAccessible)
340     {
341       windowAccessible->Emit(WindowEvent::ACTIVATE, 0);
342     }
343   }
344
345   /**
346    * @brief Sends a signal to dbus that the window is deactivated.
347    *
348    * @param[in] window The window to be deactivated
349    * @see BridgeObject::Emit()
350    */
351   void EmitDeactivate(Dali::Window window)
352   {
353     auto windowAccessible = mApplication.GetWindowAccessible(window);
354     if(windowAccessible)
355     {
356       windowAccessible->Emit(WindowEvent::DEACTIVATE, 0);
357     }
358   }
359
360   /**
361    * @copydoc Dali::Accessibility::Bridge::WindowShown()
362    */
363   void WindowShown(Dali::Window window) override
364   {
365     if(!mIsShown && IsUp())
366     {
367       EmitShown(window);
368     }
369     mIsShown = true;
370   }
371
372   /**
373    * @copydoc Dali::Accessibility::Bridge::WindowHidden()
374    */
375   void WindowHidden(Dali::Window window) override
376   {
377     if(mIsShown && IsUp())
378     {
379       EmitHidden(window);
380     }
381     mIsShown = false;
382   }
383
384   /**
385    * @copydoc Dali::Accessibility::Bridge::WindowFocused()
386    */
387   void WindowFocused(Dali::Window window) override
388   {
389     if(mIsShown && IsUp())
390     {
391       EmitActivate(window);
392     }
393   }
394
395   /**
396    * @copydoc Dali::Accessibility::Bridge::WindowUnfocused()
397    */
398   void WindowUnfocused(Dali::Window window) override
399   {
400     if(mIsShown && IsUp())
401     {
402       EmitDeactivate(window);
403     }
404   }
405
406   void ReadIsEnabledProperty()
407   {
408     mAccessibilityStatusClient.property<bool>("IsEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
409       if(!msg)
410       {
411         DALI_LOG_ERROR("Get IsEnabled property error: %s\n", msg.getError().message.c_str());
412         if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
413         {
414           ReadIsEnabledProperty();
415         }
416         return;
417       }
418       mIsEnabled = std::get<0>(msg);
419       if(mIsEnabled)
420       {
421         ForceUp();
422       }
423     });
424   }
425
426   void ListenIsEnabledProperty()
427   {
428     mAccessibilityStatusClient.addPropertyChangedEvent<bool>("IsEnabled", [this](bool res) {
429       mIsEnabled = res;
430       if(mIsScreenReaderEnabled || mIsEnabled)
431       {
432         ForceUp();
433       }
434       else
435       {
436         ForceDown();
437       }
438     });
439   }
440
441   void ReadScreenReaderEnabledProperty()
442   {
443     mAccessibilityStatusClient.property<bool>("ScreenReaderEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
444       if(!msg)
445       {
446         DALI_LOG_ERROR("Get ScreenReaderEnabled property error: %s\n", msg.getError().message.c_str());
447         if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
448         {
449           ReadScreenReaderEnabledProperty();
450         }
451         return;
452       }
453       mIsScreenReaderEnabled = std::get<0>(msg);
454       if(mIsScreenReaderEnabled)
455       {
456         ForceUp();
457       }
458     });
459   }
460
461   void ListenScreenReaderEnabledProperty()
462   {
463     mAccessibilityStatusClient.addPropertyChangedEvent<bool>("ScreenReaderEnabled", [this](bool res) {
464       mIsScreenReaderEnabled = res;
465       if(mIsScreenReaderEnabled || mIsEnabled)
466       {
467         ForceUp();
468       }
469       else
470       {
471         ForceDown();
472       }
473     });
474   }
475
476   void ReadAndListenProperties()
477   {
478     ReadIsEnabledProperty();
479     ListenIsEnabledProperty();
480
481     ReadScreenReaderEnabledProperty();
482     ListenScreenReaderEnabledProperty();
483   }
484
485   bool InitializeAccessibilityStatusClient()
486   {
487     mAccessibilityStatusClient = DBus::DBusClient{A11yDbusName, A11yDbusPath, A11yDbusStatusInterface, DBus::ConnectionType::SESSION};
488
489     if (!mAccessibilityStatusClient)
490     {
491       DALI_LOG_ERROR("Accessibility Status DbusClient is not ready\n");
492       return false;
493     }
494
495     return true;
496   }
497
498   bool OnIdleSignal()
499   {
500     if ( InitializeAccessibilityStatusClient() )
501     {
502       ReadAndListenProperties();
503       mIdleCallback = NULL;
504       return false;
505     }
506
507     return true;
508   }
509
510   /**
511    * @copydoc Dali::Accessibility::Bridge::Initialize()
512    */
513   void Initialize() override
514   {
515     if ( InitializeAccessibilityStatusClient() )
516     {
517       ReadAndListenProperties();
518       return;
519     }
520
521     // Initialize failed. Try it again on Idle
522     if( Dali::Adaptor::IsAvailable() )
523     {
524       Dali::Adaptor& adaptor = Dali::Adaptor::Get();
525       if( NULL == mIdleCallback )
526       {
527         mIdleCallback = MakeCallback( this, &BridgeImpl::OnIdleSignal );
528         adaptor.AddIdle( mIdleCallback, true );
529       }
530     }
531   }
532
533   /**
534    * @copydoc Dali::Accessibility::Bridge::GetScreenReaderEnabled()
535    */
536   bool GetScreenReaderEnabled() override
537   {
538     return mIsScreenReaderEnabled;
539   }
540
541   /**
542    * @copydoc Dali::Accessibility::Bridge::IsEnabled()
543    */
544   bool IsEnabled() override
545   {
546     return mIsEnabled;
547   }
548 }; // BridgeImpl
549
550 namespace // unnamed namespace
551 {
552
553 bool INITIALIZED_BRIDGE = false;
554
555 /**
556  * @brief Creates BridgeImpl instance.
557  *
558  * @return The BridgeImpl instance
559  * @note This method is to check environment variable first. If ATSPI is disable using env, it returns dummy bridge instance.
560  */
561 Bridge* CreateBridge()
562 {
563   INITIALIZED_BRIDGE = true;
564
565   try
566   {
567     /* check environment variable first */
568     const char* envAtspiDisabled = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_DISABLE_ATSPI);
569     if(envAtspiDisabled && std::atoi(envAtspiDisabled) != 0)
570     {
571       return Dali::Accessibility::DummyBridge::GetInstance();
572     }
573
574     return new BridgeImpl;
575   }
576   catch(const std::exception&)
577   {
578     DALI_LOG_ERROR("Failed to initialize AT-SPI bridge");
579     return Dali::Accessibility::DummyBridge::GetInstance();
580   }
581 }
582
583 } // unnamed namespace
584
585 // Dali::Accessibility::Bridge class implementation
586
587 Bridge* Bridge::GetCurrentBridge()
588 {
589   static Bridge* bridge;
590
591   if(bridge)
592   {
593     return bridge;
594   }
595   else if(mAutoInitState == AutoInitState::ENABLED)
596   {
597     bridge = CreateBridge();
598
599     /* check environment variable for suppressing screen-reader */
600     const char* envSuppressScreenReader = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_SUPPRESS_SCREEN_READER);
601     if(envSuppressScreenReader && std::atoi(envSuppressScreenReader) != 0)
602     {
603       bridge->SuppressScreenReader(true);
604     }
605
606     return bridge;
607   }
608
609   return Dali::Accessibility::DummyBridge::GetInstance();
610 }
611
612 void Bridge::DisableAutoInit()
613 {
614   if(INITIALIZED_BRIDGE)
615   {
616     DALI_LOG_ERROR("Bridge::DisableAutoInit() called after bridge auto-initialization");
617   }
618
619   mAutoInitState = AutoInitState::DISABLED;
620 }
621
622 void Bridge::EnableAutoInit()
623 {
624   mAutoInitState = AutoInitState::ENABLED;
625
626   if(INITIALIZED_BRIDGE)
627   {
628     return;
629   }
630
631   auto rootLayer       = Dali::Stage::GetCurrent().GetRootLayer(); // A root layer of the default window.
632   auto window          = Dali::DevelWindow::Get(rootLayer);
633   auto applicationName = Dali::Internal::Adaptor::Adaptor::GetApplicationPackageName();
634
635   auto accessible = Accessibility::Accessible::Get(rootLayer, true);
636
637   auto* bridge = Bridge::GetCurrentBridge();
638   bridge->AddTopLevelWindow(accessible);
639   bridge->SetApplicationName(applicationName);
640   bridge->Initialize();
641
642   if(window && window.IsVisible())
643   {
644     bridge->WindowShown(window);
645   }
646 }