f46f5ea21815256a2cc486cee60ad9368df306d5
[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 ReadAndListenProperty()
407   {
408     // read property
409     auto enabled = mAccessibilityStatusClient.property<bool>("ScreenReaderEnabled").get();
410     if(enabled)
411     {
412       mIsScreenReaderEnabled = std::get<0>(enabled);
413     }
414
415     enabled = mAccessibilityStatusClient.property<bool>("IsEnabled").get();
416     if(enabled)
417     {
418       mIsEnabled = std::get<0>(enabled);
419     }
420
421     if(mIsScreenReaderEnabled || mIsEnabled)
422     {
423       ForceUp();
424     }
425
426     // listen property change
427     mAccessibilityStatusClient.addPropertyChangedEvent<bool>("ScreenReaderEnabled", [this](bool res) {
428       mIsScreenReaderEnabled = res;
429       if(mIsScreenReaderEnabled || mIsEnabled)
430       {
431         ForceUp();
432       }
433       else
434       {
435         ForceDown();
436       }
437     });
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   bool InitializeAccessibilityStatusClient()
453   {
454     mAccessibilityStatusClient = DBus::DBusClient{A11yDbusName, A11yDbusPath, A11yDbusStatusInterface, DBus::ConnectionType::SESSION};
455
456     if (!mAccessibilityStatusClient)
457     {
458       DALI_LOG_ERROR("Accessibility Status DbusClient is not ready\n");
459       return false;
460     }
461
462     return true;
463   }
464
465   bool OnIdleSignal()
466   {
467     if ( InitializeAccessibilityStatusClient() )
468     {
469       ReadAndListenProperty();
470       mIdleCallback = NULL;
471       return false;
472     }
473
474     return true;
475   }
476
477   /**
478    * @copydoc Dali::Accessibility::Bridge::Initialize()
479    */
480   void Initialize() override
481   {
482     if ( InitializeAccessibilityStatusClient() )
483     {
484       ReadAndListenProperty();
485       return;
486     }
487
488     // Initialize failed. Try it again on Idle
489     if( Dali::Adaptor::IsAvailable() )
490     {
491       Dali::Adaptor& adaptor = Dali::Adaptor::Get();
492       if( NULL == mIdleCallback )
493       {
494         mIdleCallback = MakeCallback( this, &BridgeImpl::OnIdleSignal );
495         adaptor.AddIdle( mIdleCallback, true );
496       }
497     }
498   }
499
500   /**
501    * @copydoc Dali::Accessibility::Bridge::GetScreenReaderEnabled()
502    */
503   bool GetScreenReaderEnabled() override
504   {
505     return mIsScreenReaderEnabled;
506   }
507
508   /**
509    * @copydoc Dali::Accessibility::Bridge::IsEnabled()
510    */
511   bool IsEnabled() override
512   {
513     return mIsEnabled;
514   }
515 }; // BridgeImpl
516
517 namespace // unnamed namespace
518 {
519
520 bool INITIALIZED_BRIDGE = false;
521
522 /**
523  * @brief Creates BridgeImpl instance.
524  *
525  * @return The BridgeImpl instance
526  * @note This method is to check environment variable first. If ATSPI is disable using env, it returns dummy bridge instance.
527  */
528 Bridge* CreateBridge()
529 {
530   INITIALIZED_BRIDGE = true;
531
532   try
533   {
534     /* check environment variable first */
535     const char* envAtspiDisabled = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_DISABLE_ATSPI);
536     if(envAtspiDisabled && std::atoi(envAtspiDisabled) != 0)
537     {
538       return Dali::Accessibility::DummyBridge::GetInstance();
539     }
540
541     return new BridgeImpl;
542   }
543   catch(const std::exception&)
544   {
545     DALI_LOG_ERROR("Failed to initialize AT-SPI bridge");
546     return Dali::Accessibility::DummyBridge::GetInstance();
547   }
548 }
549
550 } // unnamed namespace
551
552 // Dali::Accessibility::Bridge class implementation
553
554 Bridge* Bridge::GetCurrentBridge()
555 {
556   static Bridge* bridge;
557
558   if(bridge)
559   {
560     return bridge;
561   }
562   else if(mAutoInitState == AutoInitState::ENABLED)
563   {
564     bridge = CreateBridge();
565
566     /* check environment variable for suppressing screen-reader */
567     const char* envSuppressScreenReader = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_SUPPRESS_SCREEN_READER);
568     if(envSuppressScreenReader && std::atoi(envSuppressScreenReader) != 0)
569     {
570       bridge->SuppressScreenReader(true);
571     }
572
573     return bridge;
574   }
575
576   return Dali::Accessibility::DummyBridge::GetInstance();
577 }
578
579 void Bridge::DisableAutoInit()
580 {
581   if(INITIALIZED_BRIDGE)
582   {
583     DALI_LOG_ERROR("Bridge::DisableAutoInit() called after bridge auto-initialization");
584   }
585
586   mAutoInitState = AutoInitState::DISABLED;
587 }
588
589 void Bridge::EnableAutoInit()
590 {
591   mAutoInitState = AutoInitState::ENABLED;
592
593   if(INITIALIZED_BRIDGE)
594   {
595     return;
596   }
597
598   auto rootLayer       = Dali::Stage::GetCurrent().GetRootLayer(); // A root layer of the default window.
599   auto window          = Dali::DevelWindow::Get(rootLayer);
600   auto applicationName = Dali::Internal::Adaptor::Adaptor::GetApplicationPackageName();
601
602   auto accessible = Accessibility::Accessible::Get(rootLayer, true);
603
604   auto* bridge = Bridge::GetCurrentBridge();
605   bridge->AddTopLevelWindow(accessible);
606   bridge->SetApplicationName(applicationName);
607   bridge->Initialize();
608
609   if(window && window.IsVisible())
610   {
611     bridge->WindowShown(window);
612   }
613 }