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