181b44015261383f189ec5d0cf84a094f2b6ba44
[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   }
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     if(mIsShown)
291     {
292       EmitActivate();
293     }
294
295     mEnabledSignal.Emit();
296
297     return ForceUpResult::JUST_STARTED;
298   }
299
300   /**
301    * @brief Sends a signal to dbus that the default window is activated.
302    *
303    * TODO : This is subject to change if/when we implement multi-window support.
304    * @see BridgeObject::Emit()
305    */
306   void EmitActivate()
307   {
308     auto win = mApplication.GetActiveWindow();
309     if(win)
310     {
311       win->Emit(WindowEvent::ACTIVATE, 0);
312     }
313   }
314
315   /**
316    * @brief Sends a signal to dbus that the default window is deactivated.
317    *
318    * TODO : This is subject to change if/when we implement multi-window support.
319    * @see BridgeObject::Emit()
320    */
321   void EmitDeactivate()
322   {
323     auto win = mApplication.GetActiveWindow();
324     if(win)
325     {
326       win->Emit(WindowEvent::DEACTIVATE, 0);
327     }
328   }
329
330   /**
331    * @copydoc Dali::Accessibility::Bridge::WindowHidden()
332    */
333   void WindowHidden() override
334   {
335     if(mIsShown && IsUp())
336     {
337       EmitDeactivate();
338     }
339     mIsShown = false;
340   }
341
342   /**
343    * @copydoc Dali::Accessibility::Bridge::WindowShown()
344    */
345   void WindowShown() override
346   {
347     if(!mIsShown && IsUp())
348     {
349       EmitActivate();
350     }
351     mIsShown = true;
352   }
353
354   void ReadAndListenProperty()
355   {
356     // read property
357     auto enabled = mAccessibilityStatusClient.property<bool>("ScreenReaderEnabled").get();
358     if(enabled)
359     {
360       mIsScreenReaderEnabled = std::get<0>(enabled);
361     }
362
363     enabled = mAccessibilityStatusClient.property<bool>("IsEnabled").get();
364     if(enabled)
365     {
366       mIsEnabled = std::get<0>(enabled);
367     }
368
369     if(mIsScreenReaderEnabled || mIsEnabled)
370     {
371       ForceUp();
372     }
373
374     // listen property change
375     mAccessibilityStatusClient.addPropertyChangedEvent<bool>("ScreenReaderEnabled", [this](bool res) {
376       mIsScreenReaderEnabled = res;
377       if(mIsScreenReaderEnabled || mIsEnabled)
378       {
379         ForceUp();
380       }
381       else
382       {
383         ForceDown();
384       }
385     });
386
387     mAccessibilityStatusClient.addPropertyChangedEvent<bool>("IsEnabled", [this](bool res) {
388       mIsEnabled = res;
389       if(mIsScreenReaderEnabled || mIsEnabled)
390       {
391         ForceUp();
392       }
393       else
394       {
395         ForceDown();
396       }
397     });
398   }
399
400   bool InitializeAccessibilityStatusClient()
401   {
402     mAccessibilityStatusClient = DBus::DBusClient{A11yDbusName, A11yDbusPath, A11yDbusStatusInterface, DBus::ConnectionType::SESSION};
403
404     if (!mAccessibilityStatusClient)
405     {
406       DALI_LOG_ERROR("Accessibility Status DbusClient is not ready\n");
407       return false;
408     }
409
410     return true;
411   }
412
413   bool OnIdleSignal()
414   {
415     if ( InitializeAccessibilityStatusClient() )
416     {
417       ReadAndListenProperty();
418       mIdleCallback = NULL;
419       return false;
420     }
421
422     return true;
423   }
424
425   /**
426    * @copydoc Dali::Accessibility::Bridge::Initialize()
427    */
428   void Initialize() override
429   {
430     if ( InitializeAccessibilityStatusClient() )
431     {
432       ReadAndListenProperty();
433       return;
434     }
435
436     // Initialize failed. Try it again on Idle
437     if( Dali::Adaptor::IsAvailable() )
438     {
439       Dali::Adaptor& adaptor = Dali::Adaptor::Get();
440       if( NULL == mIdleCallback )
441       {
442         mIdleCallback = MakeCallback( this, &BridgeImpl::OnIdleSignal );
443         adaptor.AddIdle( mIdleCallback, true );
444       }
445     }
446   }
447
448   /**
449    * @copydoc Dali::Accessibility::Bridge::GetScreenReaderEnabled()
450    */
451   bool GetScreenReaderEnabled() override
452   {
453     return mIsScreenReaderEnabled;
454   }
455
456   /**
457    * @copydoc Dali::Accessibility::Bridge::IsEnabled()
458    */
459   bool IsEnabled() override
460   {
461     return mIsEnabled;
462   }
463 }; // BridgeImpl
464
465 namespace // unnamed namespace
466 {
467
468 bool INITIALIZED_BRIDGE = false;
469
470 /**
471  * @brief Creates BridgeImpl instance.
472  *
473  * @return The BridgeImpl instance
474  * @note This method is to check environment variable first. If ATSPI is disable using env, it returns dummy bridge instance.
475  */
476 Bridge* CreateBridge()
477 {
478   INITIALIZED_BRIDGE = true;
479
480   try
481   {
482     /* check environment variable first */
483     const char* envAtspiDisabled = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_DISABLE_ATSPI);
484     if(envAtspiDisabled && std::atoi(envAtspiDisabled) != 0)
485     {
486       return Dali::Accessibility::DummyBridge::GetInstance();
487     }
488
489     return new BridgeImpl;
490   }
491   catch(const std::exception&)
492   {
493     DALI_LOG_ERROR("Failed to initialize AT-SPI bridge");
494     return Dali::Accessibility::DummyBridge::GetInstance();
495   }
496 }
497
498 } // unnamed namespace
499
500 // Dali::Accessibility::Bridge class implementation
501
502 Bridge* Bridge::GetCurrentBridge()
503 {
504   static Bridge* bridge;
505
506   if(bridge)
507   {
508     return bridge;
509   }
510   else if(mAutoInitState == AutoInitState::ENABLED)
511   {
512     bridge = CreateBridge();
513
514     /* check environment variable for suppressing screen-reader */
515     const char* envSuppressScreenReader = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_SUPPRESS_SCREEN_READER);
516     if(envSuppressScreenReader && std::atoi(envSuppressScreenReader) != 0)
517     {
518       bridge->SuppressScreenReader(true);
519     }
520
521     return bridge;
522   }
523
524   return Dali::Accessibility::DummyBridge::GetInstance();
525 }
526
527 void Bridge::DisableAutoInit()
528 {
529   if(INITIALIZED_BRIDGE)
530   {
531     DALI_LOG_ERROR("Bridge::DisableAutoInit() called after bridge auto-initialization");
532   }
533
534   mAutoInitState = AutoInitState::DISABLED;
535 }
536
537 void Bridge::EnableAutoInit()
538 {
539   mAutoInitState = AutoInitState::ENABLED;
540
541   if(INITIALIZED_BRIDGE)
542   {
543     return;
544   }
545
546   auto rootLayer       = Dali::Stage::GetCurrent().GetRootLayer();
547   auto window          = Dali::DevelWindow::Get(rootLayer);
548   auto applicationName = Dali::Internal::Adaptor::Adaptor::GetApplicationPackageName();
549
550   auto* bridge = Bridge::GetCurrentBridge();
551   bridge->AddTopLevelWindow(Dali::Accessibility::Accessible::Get(rootLayer, true));
552   bridge->SetApplicationName(applicationName);
553   bridge->Initialize();
554
555   if(window && window.IsVisible())
556   {
557     bridge->WindowShown();
558   }
559 }