972436836f8d2984aceae5388b3cd414c5c1716f
[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 class BridgeImpl : public virtual BridgeBase,
47                    public BridgeAccessible,
48                    public BridgeObject,
49                    public BridgeComponent,
50                    public BridgeCollection,
51                    public BridgeAction,
52                    public BridgeValue,
53                    public BridgeText,
54                    public BridgeEditableText,
55                    public BridgeSelection,
56                    public BridgeApplication
57 {
58   DBus::DBusClient                                              accessibilityStatusClient;
59   DBus::DBusClient                                              registryClient, directReadingClient;
60   bool                                                          screenReaderEnabled = false;
61   bool                                                          isEnabled           = false;
62   bool                                                          isShown             = false;
63   std::unordered_map<int32_t, std::function<void(std::string)>> directReadingCallbacks;
64   Dali::Actor                                                   highlightedActor;
65   std::function<void(Dali::Actor)>                              highlightClearAction;
66   Dali::CallbackBase* mIdleCallback = NULL;
67
68 public:
69   BridgeImpl()
70   {
71   }
72
73   Consumed Emit(KeyEventType type, unsigned int keyCode, const std::string& keyName, unsigned int timeStamp, bool isText) override
74   {
75     if(!IsUp())
76     {
77       return Consumed::NO;
78     }
79
80     unsigned int keyType = 0;
81
82     switch(type)
83     {
84       case KeyEventType::KEY_PRESSED:
85       {
86         keyType = 0;
87         break;
88       }
89       case KeyEventType::KEY_RELEASED:
90       {
91         keyType = 1;
92         break;
93       }
94       default:
95       {
96         return Consumed::NO;
97       }
98     }
99     auto m      = registryClient.method<bool(std::tuple<uint32_t, int32_t, int32_t, int32_t, int32_t, std::string, bool>)>("NotifyListenersSync");
100     auto result = m.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});
101     if(!result)
102     {
103       LOG() << result.getError().message;
104       return Consumed::NO;
105     }
106     return std::get<0>(result) ? Consumed::YES : Consumed::NO;
107   }
108
109   void Pause() override
110   {
111     if(!IsUp())
112     {
113       return;
114     }
115
116     directReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
117       if(!msg)
118       {
119         LOG() << "Direct reading command failed (" << msg.getError().message << ")";
120       }
121     },
122                                                                                         true);
123   }
124
125   void Resume() override
126   {
127     if(!IsUp())
128     {
129       return;
130     }
131
132     directReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
133       if(!msg)
134       {
135         LOG() << "Direct reading command failed (" << msg.getError().message << ")";
136       }
137     },
138                                                                                         false);
139   }
140
141   void StopReading(bool alsoNonDiscardable) override
142   {
143     if(!IsUp())
144     {
145       return;
146     }
147
148     directReadingClient.method<DBus::ValueOrError<void>(bool)>("StopReading").asyncCall([](DBus::ValueOrError<void> msg) {
149       if(!msg)
150       {
151         LOG() << "Direct reading command failed (" << msg.getError().message << ")";
152       }
153     },
154                                                                                         alsoNonDiscardable);
155   }
156
157   void Say(const std::string& text, bool discardable, std::function<void(std::string)> callback) override
158   {
159     if(!IsUp())
160     {
161       return;
162     }
163
164     directReadingClient.method<DBus::ValueOrError<std::string, bool, int32_t>(std::string, bool)>("ReadCommand").asyncCall([=](DBus::ValueOrError<std::string, bool, int32_t> msg) {
165       if(!msg)
166       {
167         LOG() << "Direct reading command failed (" << msg.getError().message << ")";
168       }
169       else if(callback)
170       {
171         directReadingCallbacks.emplace(std::get<2>(msg), callback);
172       }
173     },
174                                                                                                                            text,
175                                                                                                                            discardable);
176   }
177
178   void ForceDown() override
179   {
180     if(mData)
181     {
182       if(mData->mCurrentlyHighlightedActor && mData->mHighlightActor)
183       {
184         mData->mCurrentlyHighlightedActor.Remove(mData->mHighlightActor);
185       }
186       mData->mCurrentlyHighlightedActor = {};
187       mData->mHighlightActor            = {};
188     }
189     highlightedActor     = {};
190     highlightClearAction = {};
191     BridgeAccessible::ForceDown();
192     registryClient      = {};
193     directReadingClient = {};
194     directReadingCallbacks.clear();
195   }
196
197   void Terminate() override
198   {
199     if(mData)
200     {
201       mData->mCurrentlyHighlightedActor = {};
202       mData->mHighlightActor            = {};
203     }
204     ForceDown();
205     if((NULL != mIdleCallback) && Dali::Adaptor::IsAvailable())
206     {
207       Dali::Adaptor::Get().RemoveIdle(mIdleCallback);
208     }
209     accessibilityStatusClient        = {};
210     dbusServer                       = {};
211     con                              = {};
212   }
213
214   ForceUpResult ForceUp() override
215   {
216     if(BridgeAccessible::ForceUp() == ForceUpResult::ALREADY_UP)
217     {
218       return ForceUpResult::ALREADY_UP;
219     }
220
221     BridgeObject::RegisterInterfaces();
222     BridgeAccessible::RegisterInterfaces();
223     BridgeComponent::RegisterInterfaces();
224     BridgeCollection::RegisterInterfaces();
225     BridgeAction::RegisterInterfaces();
226     BridgeValue::RegisterInterfaces();
227     BridgeText::RegisterInterfaces();
228     BridgeEditableText::RegisterInterfaces();
229     BridgeSelection::RegisterInterfaces();
230     BridgeApplication::RegisterInterfaces();
231
232     RegisterOnBridge(&application);
233
234     registryClient      = {AtspiDbusNameRegistry, AtspiDbusPathDec, AtspiDbusInterfaceDec, con};
235     directReadingClient = DBus::DBusClient{DirectReadingDBusName, DirectReadingDBusPath, DirectReadingDBusInterface, con};
236     directReadingClient.addSignal<void(int32_t, std::string)>("ReadingStateChanged", [=](int32_t id, std::string readingState) {
237       auto it = directReadingCallbacks.find(id);
238       if(it != directReadingCallbacks.end())
239       {
240         it->second(readingState);
241         if(readingState != "ReadingPaused" && readingState != "ReadingResumed" && readingState != "ReadingStarted")
242           directReadingCallbacks.erase(it);
243       }
244     });
245
246     auto    proxy = DBus::DBusClient{AtspiDbusNameRegistry, AtspiDbusPathRoot, AtspiDbusInterfaceSocket, con};
247     Address root{"", "root"};
248     auto    res = proxy.method<Address(Address)>("Embed").call(root);
249     if(!res)
250     {
251       LOG() << "Call to Embed failed: " << res.getError().message;
252     }
253     assert(res);
254     application.parent.SetAddress(std::move(std::get<0>(res)));
255     if(isShown)
256     {
257       EmitActivate();
258     }
259     return ForceUpResult::JUST_STARTED;
260   }
261
262   void EmitActivate()
263   {
264     auto win = application.getActiveWindow();
265     if(win)
266     {
267       win->Emit(WindowEvent::ACTIVATE, 0);
268     }
269   }
270
271   void EmitDeactivate()
272   {
273     auto win = application.getActiveWindow();
274     if(win)
275     {
276       win->Emit(WindowEvent::DEACTIVATE, 0);
277     }
278   }
279
280   void WindowHidden() override
281   {
282     if(isShown && IsUp())
283     {
284       EmitDeactivate();
285     }
286     isShown = false;
287   }
288
289   void WindowShown() override
290   {
291     if(!isShown && IsUp())
292     {
293       EmitActivate();
294     }
295     isShown = true;
296   }
297
298   void ReadAndListenProperty()
299   {
300     // read property
301     auto enabled = accessibilityStatusClient.property<bool>("ScreenReaderEnabled").get();
302     if(enabled)
303     {
304       screenReaderEnabled = std::get<0>(enabled);
305     }
306     enabled = accessibilityStatusClient.property<bool>("IsEnabled").get();
307     if(enabled)
308     {
309       isEnabled = std::get<0>(enabled);
310     }
311     if(screenReaderEnabled || isEnabled)
312     {
313       ForceUp();
314     }
315
316     // listen property change
317     accessibilityStatusClient.addPropertyChangedEvent<bool>("ScreenReaderEnabled", [this](bool res) {
318       screenReaderEnabled = res;
319       if(screenReaderEnabled || isEnabled)
320       {
321         ForceUp();
322       }
323       else
324       {
325         ForceDown();
326       }
327     });
328
329     accessibilityStatusClient.addPropertyChangedEvent<bool>("IsEnabled", [this](bool res) {
330       isEnabled = res;
331       if(screenReaderEnabled || isEnabled)
332       {
333         ForceUp();
334       }
335       else
336       {
337         ForceDown();
338       }
339     });
340   }
341
342   bool InitializeAccessibilityStatusClient()
343   {
344     accessibilityStatusClient = DBus::DBusClient{A11yDbusName, A11yDbusPath, A11yDbusStatusInterface, DBus::ConnectionType::SESSION};
345
346     if (!accessibilityStatusClient)
347     {
348       DALI_LOG_ERROR("Accessibility Status DbusClient is not ready\n");
349       return false;
350     }
351
352     return true;
353   }
354
355   bool OnIdleSignal()
356   {
357     if ( InitializeAccessibilityStatusClient() )
358     {
359       ReadAndListenProperty();
360       mIdleCallback = NULL;
361       return false;
362     }
363
364     return true;
365   }
366
367   void Initialize() override
368   {
369     if ( InitializeAccessibilityStatusClient() )
370     {
371       ReadAndListenProperty();
372       return;
373     }
374
375     // Initialize failed. Try it again on Idle
376     if( Dali::Adaptor::IsAvailable() )
377     {
378       Dali::Adaptor& adaptor = Dali::Adaptor::Get();
379       if( NULL == mIdleCallback )
380       {
381         mIdleCallback = MakeCallback( this, &BridgeImpl::OnIdleSignal );
382         adaptor.AddIdle( mIdleCallback, true );
383       }
384     }
385   }
386
387   bool GetScreenReaderEnabled()
388   {
389     return screenReaderEnabled;
390   }
391
392   bool IsEnabled()
393   {
394     return isEnabled;
395   }
396 };
397
398 static bool bridgeInitialized;
399
400 static Bridge* CreateBridge()
401 {
402   bridgeInitialized = true;
403
404   try
405   {
406     /* check environment variable first */
407     const char* envAtspiDisabled = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_DISABLE_ATSPI);
408     if(envAtspiDisabled && std::atoi(envAtspiDisabled) != 0)
409     {
410       return Dali::Accessibility::DummyBridge::GetInstance();
411     }
412
413     return new BridgeImpl;
414   }
415   catch(const std::exception&)
416   {
417     DALI_LOG_ERROR("Failed to initialize AT-SPI bridge");
418     return Dali::Accessibility::DummyBridge::GetInstance();
419   }
420 }
421
422 Bridge* Bridge::GetCurrentBridge()
423 {
424   static Bridge* bridge;
425
426   if(bridge)
427   {
428     return bridge;
429   }
430   else if(autoInitState == AutoInitState::ENABLED)
431   {
432     bridge = CreateBridge();
433
434     /* check environment variable for suppressing screen-reader */
435     const char* envSuppressScreenReader = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_SUPPRESS_SCREEN_READER);
436     if(envSuppressScreenReader && std::atoi(envSuppressScreenReader) != 0)
437     {
438       bridge->SuppressScreenReader(true);
439     }
440
441     return bridge;
442   }
443
444   return Dali::Accessibility::DummyBridge::GetInstance();
445 }
446
447 void Bridge::DisableAutoInit()
448 {
449   if(bridgeInitialized)
450   {
451     DALI_LOG_ERROR("Bridge::DisableAutoInit() called after bridge auto-initialization");
452   }
453
454   autoInitState = AutoInitState::DISABLED;
455 }
456
457 void Bridge::EnableAutoInit()
458 {
459   autoInitState = AutoInitState::ENABLED;
460
461   if(bridgeInitialized)
462   {
463     return;
464   }
465
466   auto rootLayer       = Dali::Stage::GetCurrent().GetRootLayer();
467   auto window          = Dali::DevelWindow::Get(rootLayer);
468   auto applicationName = Dali::Internal::Adaptor::Adaptor::GetApplicationPackageName();
469
470   auto* bridge = Bridge::GetCurrentBridge();
471   bridge->AddTopLevelWindow(Dali::Accessibility::Accessible::Get(rootLayer, true));
472   bridge->SetApplicationName(applicationName);
473   bridge->Initialize();
474
475   if(window && window.IsVisible())
476   {
477     bridge->WindowShown();
478   }
479 }