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