Further Setter/Getter public API removal from Dali::Actor
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / accessibility-manager / accessibility-manager-impl.cpp
1 /*
2  * Copyright (c) 2019 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 #include "accessibility-manager-impl.h"
20
21 // EXTERNAL INCLUDES
22 #include <cstring> // for strcmp
23 #include <dali/public-api/actors/layer.h>
24 #include <dali/devel-api/adaptor-framework/accessibility-adaptor.h>
25 #include <dali/devel-api/adaptor-framework/sound-player.h>
26 #include <dali/public-api/animation/constraints.h>
27 #include <dali/devel-api/events/hit-test-algorithm.h>
28 #include <dali/public-api/images/resource-image.h>
29 #include <dali/integration-api/debug.h>
30
31 // INTERNAL INCLUDES
32 #include <dali-toolkit/devel-api/asset-manager/asset-manager.h>
33 #include <dali-toolkit/public-api/controls/control.h>
34 #include <dali-toolkit/public-api/controls/control-impl.h>
35 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
36
37 namespace Dali
38 {
39
40 namespace Toolkit
41 {
42
43 namespace Internal
44 {
45
46 namespace // unnamed namespace
47 {
48
49 // Signals
50
51 const char* const SIGNAL_FOCUS_CHANGED =           "focusChanged";
52 const char* const SIGNAL_FOCUS_OVERSHOT =          "focusOvershot";
53 const char* const SIGNAL_FOCUSED_ACTOR_ACTIVATED = "focusedActorActivated";
54
55 #if defined(DEBUG_ENABLED)
56 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_FOCUS_MANAGER");
57 #endif
58
59 const char* const ACTOR_FOCUSABLE("focusable");
60 const char* const IS_FOCUS_GROUP("isFocusGroup");
61
62 const char* FOCUS_BORDER_IMAGE_FILE_NAME = "B16-8_TTS_focus.9.png";
63
64 const char* FOCUS_SOUND_FILE_NAME = "Focus.ogg";
65 const char* FOCUS_CHAIN_END_SOUND_FILE_NAME = "End_of_List.ogg";
66
67 /**
68  * The function to be used in the hit-test algorithm to check whether the actor is hittable.
69  */
70 bool IsActorFocusableFunction(Actor actor, Dali::HitTestAlgorithm::TraverseType type)
71 {
72   bool hittable = false;
73
74   switch (type)
75   {
76     case Dali::HitTestAlgorithm::CHECK_ACTOR:
77     {
78       // Check whether the actor is visible and not fully transparent.
79       if( actor.GetCurrentProperty< bool >( Actor::Property::VISIBLE )
80        && actor.GetCurrentProperty< Vector4 >( Actor::Property::WORLD_COLOR ).a > 0.01f) // not FULLY_TRANSPARENT
81       {
82         // Check whether the actor is focusable
83         Property::Index propertyActorFocusable = actor.GetPropertyIndex(ACTOR_FOCUSABLE);
84         if(propertyActorFocusable != Property::INVALID_INDEX)
85         {
86           hittable = actor.GetProperty<bool>(propertyActorFocusable);
87         }
88       }
89       break;
90     }
91     case Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE:
92     {
93       if( actor.GetCurrentProperty< bool >( Actor::Property::VISIBLE ) ) // Actor is visible, if not visible then none of its children are visible.
94       {
95         hittable = true;
96       }
97       break;
98     }
99     default:
100     {
101       break;
102     }
103   }
104
105   return hittable;
106 };
107
108 }
109
110 AccessibilityManager::AccessibilityManager()
111 : mCurrentFocusActor(FocusIDPair(0, 0)),
112   mCurrentGesturedActor(),
113   mFocusIndicatorActor(),
114   mPreviousPosition( 0.0f, 0.0f ),
115   mRecursiveFocusMoveCounter(0),
116   mFocusSoundFilePath(),
117   mFocusChainEndSoundFilePath(),
118   mIsWrapped(false),
119   mIsFocusWithinGroup(false),
120   mIsEndcapFeedbackEnabled(false),
121   mIsEndcapFeedbackPlayed(false),
122   mIsAccessibilityTtsEnabled(false),
123   mTtsCreated(false),
124   mIsFocusIndicatorEnabled(false),
125   mContinuousPlayMode(false),
126   mIsFocusSoundFilePathSet(false),
127   mIsFocusChainEndSoundFilePathSet(false)
128 {
129 }
130
131 AccessibilityManager::~AccessibilityManager()
132 {
133 }
134
135 void AccessibilityManager::Initialise()
136 {
137   AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
138   adaptor.SetActionHandler(*this);
139   adaptor.SetGestureHandler(*this);
140
141   ChangeAccessibilityStatus();
142 }
143
144 AccessibilityManager::ActorAdditionalInfo AccessibilityManager::GetActorAdditionalInfo(const unsigned int actorID) const
145 {
146   ActorAdditionalInfo data;
147   IDAdditionalInfoConstIter iter = mIDAdditionalInfoContainer.find(actorID);
148   if(iter != mIDAdditionalInfoContainer.end())
149   {
150     data = (*iter).second;
151   }
152
153   return data;
154 }
155
156 void AccessibilityManager::SynchronizeActorAdditionalInfo(const unsigned int actorID, const unsigned int order)
157 {
158   ActorAdditionalInfo actorInfo = GetActorAdditionalInfo(actorID);
159   actorInfo.mFocusOrder = order;
160   mIDAdditionalInfoContainer.erase(actorID);
161   mIDAdditionalInfoContainer.insert(IDAdditionalInfoPair(actorID, actorInfo));
162 }
163
164 void AccessibilityManager::SetAccessibilityAttribute(Actor actor, Toolkit::AccessibilityManager::AccessibilityAttribute type, const std::string& text)
165 {
166   if(actor)
167   {
168     unsigned int actorID = actor.GetId();
169
170     ActorAdditionalInfo info = GetActorAdditionalInfo(actorID);
171     info.mAccessibilityAttributes[type] = text;
172
173     mIDAdditionalInfoContainer.erase(actorID);
174     mIDAdditionalInfoContainer.insert(IDAdditionalInfoPair(actorID, info));
175   }
176 }
177
178 std::string AccessibilityManager::GetAccessibilityAttribute(Actor actor, Toolkit::AccessibilityManager::AccessibilityAttribute type) const
179 {
180   std::string text;
181
182   if(actor)
183   {
184     ActorAdditionalInfo data = GetActorAdditionalInfo(actor.GetId());
185     text = data.mAccessibilityAttributes[type];
186   }
187
188   return text;
189 }
190
191 void AccessibilityManager::SetFocusOrder(Actor actor, const unsigned int order)
192 {
193   // Do nothing if the focus order of the actor is not changed.
194   if(actor && GetFocusOrder(actor) != order)
195   {
196     // Firstly delete the actor from the focus chain if it's already there with a different focus order.
197     mFocusIDContainer.erase(GetFocusOrder(actor));
198
199     // Create/retrieve actor focusable property
200     Property::Index propertyActorFocusable = actor.RegisterProperty( ACTOR_FOCUSABLE, true, Property::READ_WRITE );
201
202     if(order == 0)
203     {
204       // The actor is not focusable without a defined focus order.
205       actor.SetProperty(propertyActorFocusable, false);
206
207       // If the actor is currently being focused, it should clear the focus
208       if(actor == GetCurrentFocusActor())
209       {
210         ClearFocus();
211       }
212     }
213     else // Insert the actor to the focus chain
214     {
215       // Check whether there is another actor in the focus chain with the same focus order already.
216       FocusIDIter focusIDIter = mFocusIDContainer.find(order);
217       if(focusIDIter != mFocusIDContainer.end())
218       {
219         // We need to increase the focus order of that actor and all the actors followed it
220         // in the focus chain.
221         FocusIDIter lastIter = mFocusIDContainer.end();
222         --lastIter;//We want forward iterator to the last element here
223         mFocusIDContainer.insert(FocusIDPair((*lastIter).first + 1, (*lastIter).second));
224
225         // Update the actor's focus order in its additional data
226         SynchronizeActorAdditionalInfo((*lastIter).second, (*lastIter).first + 1);
227
228         for(FocusIDIter iter = lastIter; iter != focusIDIter; iter--)
229         {
230           FocusIDIter previousIter = iter;
231           --previousIter;//We want forward iterator to the previous element here
232           unsigned int actorID = (*previousIter).second;
233           (*iter).second = actorID;
234
235           // Update the actor's focus order in its additional data
236           SynchronizeActorAdditionalInfo(actorID, (*iter).first);
237         }
238
239         mFocusIDContainer.erase(order);
240       }
241
242       // The actor is focusable
243       actor.SetProperty(propertyActorFocusable, true);
244
245       // Now we insert the actor into the focus chain with the specified focus order
246       mFocusIDContainer.insert(FocusIDPair(order, actor.GetId()));
247     }
248
249     // Update the actor's focus order in its additional data
250     SynchronizeActorAdditionalInfo(actor.GetId(), order);
251   }
252 }
253
254 unsigned int AccessibilityManager::GetFocusOrder(Actor actor) const
255 {
256   unsigned int focusOrder = 0;
257
258   if(actor)
259   {
260     ActorAdditionalInfo data = GetActorAdditionalInfo(actor.GetId());
261     focusOrder = data.mFocusOrder;
262   }
263
264   return focusOrder;
265 }
266
267 unsigned int AccessibilityManager::GenerateNewFocusOrder() const
268 {
269   unsigned int order = 1;
270   FocusIDContainer::const_reverse_iterator iter = mFocusIDContainer.rbegin();
271
272   if(iter != mFocusIDContainer.rend())
273   {
274     order = (*iter).first + 1;
275   }
276
277   return order;
278 }
279
280 Actor AccessibilityManager::GetActorByFocusOrder(const unsigned int order)
281 {
282   Actor actor = Actor();
283
284   FocusIDIter focusIDIter = mFocusIDContainer.find(order);
285   if(focusIDIter != mFocusIDContainer.end())
286   {
287     Actor rootActor = Stage::GetCurrent().GetRootLayer();
288     actor = rootActor.FindChildById(mFocusIDContainer[order]);
289   }
290
291   return actor;
292 }
293
294 bool AccessibilityManager::SetCurrentFocusActor(Actor actor)
295 {
296   if(actor)
297   {
298     return DoSetCurrentFocusActor(actor.GetId());
299   }
300
301   return false;
302 }
303
304 bool AccessibilityManager::DoSetCurrentFocusActor(const unsigned int actorID)
305 {
306   Actor rootActor = Stage::GetCurrent().GetRootLayer();
307
308   // If the group mode is enabled, check which focus group the current focused actor belongs to
309   Actor focusGroup;
310   if(mIsFocusWithinGroup)
311   {
312     focusGroup = GetFocusGroup(GetCurrentFocusActor());
313   }
314
315   if(!focusGroup)
316   {
317     focusGroup = rootActor;
318   }
319
320   Actor actor = focusGroup.FindChildById(actorID);
321
322   // Check whether the actor is in the stage
323   if(actor)
324   {
325     // Check whether the actor is focusable
326     bool actorFocusable = false;
327     Property::Index propertyActorFocusable = actor.GetPropertyIndex(ACTOR_FOCUSABLE);
328     if(propertyActorFocusable != Property::INVALID_INDEX)
329     {
330       actorFocusable = actor.GetProperty<bool>(propertyActorFocusable);
331     }
332
333     // Go through the actor's hierarchy to check whether the actor is visible
334     bool actorVisible = actor.GetCurrentProperty< bool >( Actor::Property::VISIBLE );
335     Actor parent = actor.GetParent();
336     while (actorVisible && parent && parent != rootActor)
337     {
338       actorVisible = parent.GetCurrentProperty< bool >( Actor::Property::VISIBLE );
339       parent = parent.GetParent();
340     }
341
342     // Check whether the actor is fully transparent
343     bool actorOpaque = actor.GetCurrentProperty< Vector4 >( Actor::Property::WORLD_COLOR ).a > 0.01f;
344
345     // Set the focus only when the actor is focusable and visible and not fully transparent
346     if(actorVisible && actorFocusable && actorOpaque)
347     {
348       // Draw the focus indicator upon the focused actor
349       if( mIsFocusIndicatorEnabled )
350       {
351         actor.Add( GetFocusIndicatorActor() );
352       }
353
354       // Send notification for the change of focus actor
355       mFocusChangedSignal.Emit( GetCurrentFocusActor(), actor );
356
357       // Save the current focused actor
358       mCurrentFocusActor = FocusIDPair(GetFocusOrder(actor), actorID);
359
360       if(mIsAccessibilityTtsEnabled)
361       {
362         Dali::SoundPlayer soundPlayer = Dali::SoundPlayer::Get();
363         if(soundPlayer)
364         {
365           if (!mIsFocusSoundFilePathSet)
366           {
367             const std::string soundDirPath = AssetManager::GetDaliSoundPath();
368             mFocusSoundFilePath = soundDirPath + FOCUS_SOUND_FILE_NAME;
369             mIsFocusSoundFilePathSet = true;
370           }
371           soundPlayer.PlaySound(mFocusSoundFilePath);
372         }
373
374         // Play the accessibility attributes with the TTS player.
375         Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
376
377         // Combine attribute texts to one text
378         std::string informationText;
379         for(int i = 0; i < Toolkit::AccessibilityManager::ACCESSIBILITY_ATTRIBUTE_NUM; i++)
380         {
381           if(!GetActorAdditionalInfo(actorID).mAccessibilityAttributes[i].empty())
382           {
383             if( i > 0 )
384             {
385               informationText += ", "; // for space time between each information
386             }
387             informationText += GetActorAdditionalInfo(actorID).mAccessibilityAttributes[i];
388           }
389         }
390         player.Play(informationText);
391       }
392
393       return true;
394     }
395   }
396
397   DALI_LOG_WARNING("[%s:%d] FAILED\n", __FUNCTION__, __LINE__);
398   return false;
399 }
400
401 Actor AccessibilityManager::GetCurrentFocusActor()
402 {
403   Actor rootActor = Stage::GetCurrent().GetRootLayer();
404   return rootActor.FindChildById(mCurrentFocusActor.second);
405 }
406
407 Actor AccessibilityManager::GetCurrentFocusGroup()
408 {
409   return GetFocusGroup(GetCurrentFocusActor());
410 }
411
412 unsigned int AccessibilityManager::GetCurrentFocusOrder()
413 {
414   return mCurrentFocusActor.first;
415 }
416
417 bool AccessibilityManager::MoveFocusForward()
418 {
419   bool ret = false;
420   mRecursiveFocusMoveCounter = 0;
421
422   FocusIDIter focusIDIter = mFocusIDContainer.find(mCurrentFocusActor.first);
423   if(focusIDIter != mFocusIDContainer.end())
424   {
425     DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
426     ret = DoMoveFocus(focusIDIter, true, mIsWrapped);
427   }
428   else
429   {
430     // TODO: if there is not focused actor, move first actor
431     if(!mFocusIDContainer.empty())
432     {
433       //if there is not focused actor, move 1st actor
434       focusIDIter = mFocusIDContainer.begin(); // TODO: I'm not sure it was sorted.
435       DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
436       ret = DoSetCurrentFocusActor((*focusIDIter).second);
437     }
438   }
439
440   DALI_LOG_INFO( gLogFilter, Debug::General, "[%s] %s\n", __FUNCTION__, ret?"SUCCEED!!!":"FAILED!!!");
441
442   return ret;
443 }
444
445 bool AccessibilityManager::MoveFocusBackward()
446 {
447   bool ret = false;
448   mRecursiveFocusMoveCounter = 0;
449
450   FocusIDIter focusIDIter = mFocusIDContainer.find(mCurrentFocusActor.first);
451   if(focusIDIter != mFocusIDContainer.end())
452   {
453     DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
454     ret = DoMoveFocus(focusIDIter, false, mIsWrapped);
455   }
456   else
457   {
458     // TODO: if there is not focused actor, move last actor
459     if(!mFocusIDContainer.empty())
460     {
461       //if there is not focused actor, move last actor
462       focusIDIter = mFocusIDContainer.end();
463       --focusIDIter;//We want forward iterator to the last element here
464       DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
465       ret = DoSetCurrentFocusActor((*focusIDIter).second);
466     }
467   }
468
469   DALI_LOG_INFO( gLogFilter, Debug::General, "[%s] %s\n", __FUNCTION__, ret?"SUCCEED!!!":"FAILED!!!");
470
471   return ret;
472 }
473
474 void AccessibilityManager::DoActivate(Actor actor)
475 {
476   if(actor)
477   {
478     Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
479     if(control)
480     {
481       // Notify the control that it is activated
482       GetImplementation( control ).AccessibilityActivate();
483     }
484
485     // Send notification for the activation of focused actor
486     mFocusedActorActivatedSignal.Emit(actor);
487   }
488 }
489
490 void AccessibilityManager::ClearFocus()
491 {
492   Actor actor = GetCurrentFocusActor();
493   if( actor && mFocusIndicatorActor )
494   {
495     actor.Remove( mFocusIndicatorActor );
496   }
497
498   mCurrentFocusActor = FocusIDPair(0, 0);
499
500   // Send notification for the change of focus actor
501   mFocusChangedSignal.Emit(actor, Actor());
502
503   if(mIsAccessibilityTtsEnabled)
504   {
505     // Stop the TTS playing if any
506     Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
507     player.Stop();
508   }
509 }
510
511 void AccessibilityManager::Reset()
512 {
513   ClearFocus();
514   mFocusIDContainer.clear();
515   mIDAdditionalInfoContainer.clear();
516 }
517
518 void AccessibilityManager::SetFocusGroup(Actor actor, bool isFocusGroup)
519 {
520   if(actor)
521   {
522     // Create/Set focus group property.
523     actor.RegisterProperty( IS_FOCUS_GROUP, isFocusGroup, Property::READ_WRITE );
524   }
525 }
526
527 bool AccessibilityManager::IsFocusGroup(Actor actor) const
528 {
529   // Check whether the actor is a focus group
530   bool isFocusGroup = false;
531
532   if(actor)
533   {
534     Property::Index propertyIsFocusGroup = actor.GetPropertyIndex(IS_FOCUS_GROUP);
535     if(propertyIsFocusGroup != Property::INVALID_INDEX)
536     {
537       isFocusGroup = actor.GetProperty<bool>(propertyIsFocusGroup);
538     }
539   }
540
541   return isFocusGroup;
542 }
543
544 Actor AccessibilityManager::GetFocusGroup(Actor actor)
545 {
546   // Go through the actor's hierarchy to check which focus group the actor belongs to
547   while (actor && !IsFocusGroup(actor))
548   {
549     actor = actor.GetParent();
550   }
551
552   return actor;
553 }
554
555 Vector2 AccessibilityManager::GetReadPosition() const
556 {
557   AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
558   return adaptor.GetReadPosition();
559 }
560
561 void AccessibilityManager::SetGroupMode(bool enabled)
562 {
563   mIsFocusWithinGroup = enabled;
564 }
565
566 bool AccessibilityManager::GetGroupMode() const
567 {
568   return mIsFocusWithinGroup;
569 }
570
571 void AccessibilityManager::SetWrapMode(bool wrapped)
572 {
573   mIsWrapped = wrapped;
574 }
575
576 bool AccessibilityManager::GetWrapMode() const
577 {
578   return mIsWrapped;
579 }
580
581 void AccessibilityManager::SetFocusIndicatorActor(Actor indicator)
582 {
583   if( mFocusIndicatorActor != indicator )
584   {
585     Actor currentFocusActor = GetCurrentFocusActor();
586     if( currentFocusActor )
587     {
588       // The new focus indicator should be added to the current focused actor immediately
589       if( mFocusIndicatorActor )
590       {
591         currentFocusActor.Remove( mFocusIndicatorActor );
592       }
593
594       if( indicator )
595       {
596         currentFocusActor.Add( indicator );
597       }
598     }
599
600     mFocusIndicatorActor = indicator;
601   }
602 }
603
604 Actor AccessibilityManager::GetFocusIndicatorActor()
605 {
606   if( ! mFocusIndicatorActor )
607   {
608     // Create the default if it hasn't been set and one that's shared by all the keyboard focusable actors
609     const std::string imageDirPath = AssetManager::GetDaliImagePath();
610     const std::string focusBorderImagePath = imageDirPath + FOCUS_BORDER_IMAGE_FILE_NAME;
611
612     mFocusIndicatorActor = Toolkit::ImageView::New(focusBorderImagePath);
613     mFocusIndicatorActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
614     mFocusIndicatorActor.SetProperty( Actor::Property::POSITION_Z,  1.0f );
615
616     // Apply size constraint to the focus indicator
617     mFocusIndicatorActor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
618   }
619
620   return mFocusIndicatorActor;
621 }
622
623 bool AccessibilityManager::DoMoveFocus(FocusIDIter focusIDIter, bool forward, bool wrapped)
624 {
625   DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] %d focusable actors\n", __FUNCTION__, __LINE__, mFocusIDContainer.size());
626   DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
627
628   if( (forward && ++focusIDIter == mFocusIDContainer.end())
629     || (!forward && focusIDIter-- == mFocusIDContainer.begin()) )
630   {
631     if(mIsEndcapFeedbackEnabled)
632     {
633       if(mIsEndcapFeedbackPlayed == false)
634       {
635         // play sound & skip moving once
636         Dali::SoundPlayer soundPlayer = Dali::SoundPlayer::Get();
637         if(soundPlayer)
638         {
639           if (!mIsFocusChainEndSoundFilePathSet)
640           {
641             const std::string soundDirPath = AssetManager::GetDaliSoundPath();
642             mFocusChainEndSoundFilePath = soundDirPath + FOCUS_CHAIN_END_SOUND_FILE_NAME;
643             mIsFocusChainEndSoundFilePathSet = true;
644           }
645           soundPlayer.PlaySound(mFocusChainEndSoundFilePath);
646         }
647
648         mIsEndcapFeedbackPlayed = true;
649         return true;
650       }
651       mIsEndcapFeedbackPlayed = false;
652     }
653
654     if(wrapped)
655     {
656       if(forward)
657       {
658         focusIDIter = mFocusIDContainer.begin();
659       }
660       else
661       {
662         focusIDIter = mFocusIDContainer.end();
663         --focusIDIter;//We want forward iterator to the last element here
664       }
665     }
666     else
667     {
668       DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] Overshot\n", __FUNCTION__, __LINE__);
669       // Send notification for handling overshooted situation
670       mFocusOvershotSignal.Emit(GetCurrentFocusActor(), forward ? Toolkit::AccessibilityManager::OVERSHOT_NEXT : Toolkit::AccessibilityManager::OVERSHOT_PREVIOUS);
671
672       return false; // Try to move the focus out of the scope
673     }
674   }
675
676   // Invalid focus.
677   if( focusIDIter == mFocusIDContainer.end() )
678   {
679     return false;
680   }
681
682   // Note: This function performs the focus change.
683   if( !DoSetCurrentFocusActor( (*focusIDIter).second ) )
684   {
685     mRecursiveFocusMoveCounter++;
686     if(mRecursiveFocusMoveCounter > mFocusIDContainer.size())
687     {
688       // We've attempted to focus all the actors in the whole focus chain and no actor
689       // can be focused successfully.
690       DALI_LOG_WARNING("[%s] There is no more focusable actor in %d focus chains\n", __FUNCTION__, mRecursiveFocusMoveCounter);
691
692       return false;
693     }
694     else
695     {
696       return DoMoveFocus(focusIDIter, forward, wrapped);
697     }
698   }
699
700   return true;
701 }
702
703 void AccessibilityManager::SetFocusable(Actor actor, bool focusable)
704 {
705   if(actor)
706   {
707     // Create/Set actor focusable property.
708     actor.RegisterProperty( ACTOR_FOCUSABLE, focusable, Property::READ_WRITE );
709   }
710 }
711
712 bool AccessibilityManager::ChangeAccessibilityStatus()
713 {
714   AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
715   mIsAccessibilityTtsEnabled = adaptor.IsEnabled();
716   Dali::Toolkit::AccessibilityManager handle( this );
717
718   if(mIsAccessibilityTtsEnabled)
719   {
720     // Show indicator when tts turned on if there is focused actor.
721     Actor actor = GetCurrentFocusActor();
722     if(actor)
723     {
724       actor.Add( GetFocusIndicatorActor() );
725     }
726     mIsFocusIndicatorEnabled = true;
727
728     // Connect a signal to the TTS player to implement continuous reading mode.
729     Dali::TtsPlayer player = Dali::TtsPlayer::Get( Dali::TtsPlayer::SCREEN_READER );
730     player.StateChangedSignal().Connect( this, &AccessibilityManager::TtsStateChanged );
731     mTtsCreated = true;
732   }
733   else
734   {
735     // Hide indicator when tts turned off
736     Actor actor = GetCurrentFocusActor();
737     if( actor && mFocusIndicatorActor )
738     {
739       actor.Remove( mFocusIndicatorActor );
740     }
741     mIsFocusIndicatorEnabled = false;
742
743     if( mTtsCreated )
744     {
745       // Disconnect the TTS state change signal.
746       Dali::TtsPlayer player = Dali::TtsPlayer::Get( Dali::TtsPlayer::SCREEN_READER );
747       player.StateChangedSignal().Disconnect( this, &AccessibilityManager::TtsStateChanged );
748       mTtsCreated = true;
749     }
750   }
751
752   mStatusChangedSignal.Emit( handle );
753
754   return true;
755 }
756
757 bool AccessibilityManager::AccessibilityActionNext(bool allowEndFeedback)
758 {
759   Dali::Toolkit::AccessibilityManager handle( this );
760   if( !mActionNextSignal.Empty() )
761   {
762     mActionNextSignal.Emit( handle );
763   }
764
765   if(mIsAccessibilityTtsEnabled)
766   {
767     mIsEndcapFeedbackEnabled = allowEndFeedback;
768     return MoveFocusForward();
769   }
770   else
771   {
772     return false;
773   }
774 }
775
776 bool AccessibilityManager::AccessibilityActionPrevious(bool allowEndFeedback)
777 {
778   Dali::Toolkit::AccessibilityManager handle( this );
779   if( !mActionPreviousSignal.Empty() )
780   {
781     mActionPreviousSignal.Emit( handle );
782   }
783
784   if(mIsAccessibilityTtsEnabled)
785   {
786     mIsEndcapFeedbackEnabled = allowEndFeedback;
787     return MoveFocusBackward();
788   }
789   else
790   {
791     return false;
792   }
793 }
794
795 bool AccessibilityManager::AccessibilityActionActivate()
796 {
797   Dali::Toolkit::AccessibilityManager handle( this );
798   if( !mActionActivateSignal.Empty() )
799   {
800     mActionActivateSignal.Emit( handle );
801   }
802
803   bool ret = false;
804
805   Actor actor = GetCurrentFocusActor();
806   if(actor)
807   {
808     DoActivate(actor);
809     ret = true;
810   }
811
812   return ret;
813 }
814
815 bool AccessibilityManager::AccessibilityActionRead(bool allowReadAgain)
816 {
817   Dali::Toolkit::AccessibilityManager handle( this );
818
819   if( allowReadAgain )
820   {
821     if ( !mActionReadSignal.Empty() )
822     {
823       mActionReadSignal.Emit( handle );
824     }
825   }
826   else
827   {
828     if ( !mActionOverSignal.Empty() )
829     {
830       mActionOverSignal.Emit( handle );
831     }
832   }
833
834   bool ret = false;
835
836   if(mIsAccessibilityTtsEnabled)
837   {
838     // Find the focusable actor at the read position
839     AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
840     Dali::HitTestAlgorithm::Results results;
841     Dali::HitTestAlgorithm::HitTest( Stage::GetCurrent(), adaptor.GetReadPosition(), results, IsActorFocusableFunction );
842
843     FocusIDIter focusIDIter = mFocusIDContainer.find(GetFocusOrder(results.actor));
844     if(focusIDIter != mFocusIDContainer.end())
845     {
846       if( allowReadAgain || (results.actor != GetCurrentFocusActor()) )
847       {
848         // Move the focus to the actor
849         ret = SetCurrentFocusActor(results.actor);
850         DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] SetCurrentFocusActor returns %s\n", __FUNCTION__, __LINE__, ret?"TRUE":"FALSE");
851       }
852     }
853   }
854
855   return ret;
856 }
857
858 bool AccessibilityManager::AccessibilityActionReadNext(bool allowEndFeedback)
859 {
860   Dali::Toolkit::AccessibilityManager handle( this );
861   if( !mActionReadNextSignal.Empty() )
862   {
863     mActionReadNextSignal.Emit( handle );
864   }
865
866   if(mIsAccessibilityTtsEnabled)
867   {
868     return MoveFocusForward();
869   }
870   else
871   {
872     return false;
873   }
874 }
875
876 bool AccessibilityManager::AccessibilityActionReadPrevious(bool allowEndFeedback)
877 {
878   Dali::Toolkit::AccessibilityManager handle( this );
879   if( !mActionReadPreviousSignal.Empty() )
880   {
881     mActionReadPreviousSignal.Emit( handle );
882   }
883
884   if(mIsAccessibilityTtsEnabled)
885   {
886     return MoveFocusBackward();
887   }
888   else
889   {
890     return false;
891   }
892 }
893
894 bool AccessibilityManager::AccessibilityActionUp()
895 {
896   Dali::Toolkit::AccessibilityManager handle( this );
897   if( !mActionUpSignal.Empty() )
898   {
899     mActionUpSignal.Emit( handle );
900   }
901
902   bool ret = false;
903
904   if(mIsAccessibilityTtsEnabled)
905   {
906     Actor actor = GetCurrentFocusActor();
907     if(actor)
908     {
909       Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
910       if(control)
911       {
912         // Notify the control that it is activated
913         ret = GetImplementation( control ).OnAccessibilityValueChange(true);
914       }
915     }
916   }
917
918   return ret;
919 }
920
921 bool AccessibilityManager::AccessibilityActionDown()
922 {
923   Dali::Toolkit::AccessibilityManager handle( this );
924   if( !mActionDownSignal.Empty() )
925   {
926     mActionDownSignal.Emit( handle );
927   }
928
929   bool ret = false;
930
931   if(mIsAccessibilityTtsEnabled)
932   {
933     Actor actor = GetCurrentFocusActor();
934     if(actor)
935     {
936       Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
937       if(control)
938       {
939         // Notify the control that it is activated
940         ret = GetImplementation( control ).OnAccessibilityValueChange(false);
941       }
942     }
943   }
944
945   return ret;
946 }
947
948 bool AccessibilityManager::ClearAccessibilityFocus()
949 {
950   Dali::Toolkit::AccessibilityManager handle( this );
951   if( !mActionClearFocusSignal.Empty() )
952   {
953     mActionClearFocusSignal.Emit( handle );
954   }
955
956   if(mIsAccessibilityTtsEnabled)
957   {
958     ClearFocus();
959     return true;
960   }
961   else
962   {
963     return false;
964   }
965 }
966
967 bool AccessibilityManager::AccessibilityActionScroll( Dali::TouchEvent& touchEvent )
968 {
969   Dali::Toolkit::AccessibilityManager handle( this );
970   if( !mActionScrollSignal.Empty() )
971   {
972     mActionScrollSignal.Emit( handle, touchEvent );
973   }
974
975   return true;
976 }
977
978 bool AccessibilityManager::AccessibilityActionBack()
979 {
980   Dali::Toolkit::AccessibilityManager handle( this );
981   if( !mActionBackSignal.Empty() )
982   {
983     mActionBackSignal.Emit( handle );
984   }
985
986   // TODO: Back to previous view
987
988   return mIsAccessibilityTtsEnabled;
989 }
990
991 bool AccessibilityManager::AccessibilityActionScrollUp()
992 {
993   Dali::Toolkit::AccessibilityManager handle( this );
994   if( !mActionScrollUpSignal.Empty() )
995   {
996     mActionScrollUpSignal.Emit( handle );
997   }
998
999   bool ret = false;
1000
1001   if(mIsAccessibilityTtsEnabled)
1002   {
1003     Actor actor = GetCurrentFocusActor();
1004     if(actor)
1005     {
1006       Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1007       if(control)
1008       {
1009         // TODO: Notify the control to scroll up. Should control handle this?
1010 //        ret = GetImplementation( control ).OnAccessibilityScroll(Direction::UP);
1011       }
1012     }
1013   }
1014
1015   return ret;
1016 }
1017
1018 bool AccessibilityManager::AccessibilityActionScrollDown()
1019 {
1020   Dali::Toolkit::AccessibilityManager handle( this );
1021   if( !mActionScrollDownSignal.Empty() )
1022   {
1023     mActionScrollDownSignal.Emit( handle );
1024   }
1025
1026   bool ret = false;
1027
1028   if(mIsAccessibilityTtsEnabled)
1029   {
1030     Actor actor = GetCurrentFocusActor();
1031     if(actor)
1032     {
1033       Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1034       if(control)
1035       {
1036         // TODO: Notify the control to scroll down. Should control handle this?
1037 //        ret = GetImplementation( control ).OnAccessibilityScrollDown(Direction::DOWN);
1038       }
1039     }
1040   }
1041
1042   return ret;
1043 }
1044
1045 bool AccessibilityManager::AccessibilityActionPageLeft()
1046 {
1047   Dali::Toolkit::AccessibilityManager handle( this );
1048   if( !mActionPageLeftSignal.Empty() )
1049   {
1050     mActionPageLeftSignal.Emit( handle );
1051   }
1052
1053   bool ret = false;
1054
1055   if(mIsAccessibilityTtsEnabled)
1056   {
1057     Actor actor = GetCurrentFocusActor();
1058     if(actor)
1059     {
1060       Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1061       if(control)
1062       {
1063         // TODO: Notify the control to scroll left to the previous page. Should control handle this?
1064 //        ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::LEFT);
1065       }
1066     }
1067   }
1068
1069   return ret;
1070 }
1071
1072 bool AccessibilityManager::AccessibilityActionPageRight()
1073 {
1074   Dali::Toolkit::AccessibilityManager handle( this );
1075   if( !mActionPageRightSignal.Empty() )
1076   {
1077     mActionPageRightSignal.Emit( handle );
1078   }
1079
1080   bool ret = false;
1081
1082   if(mIsAccessibilityTtsEnabled)
1083   {
1084     Actor actor = GetCurrentFocusActor();
1085     if(actor)
1086     {
1087       Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1088       if(control)
1089       {
1090         // TODO: Notify the control to scroll right to the next page. Should control handle this?
1091 //        ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::RIGHT);
1092       }
1093     }
1094   }
1095
1096   return ret;
1097 }
1098
1099 bool AccessibilityManager::AccessibilityActionPageUp()
1100 {
1101   Dali::Toolkit::AccessibilityManager handle( this );
1102   if( !mActionPageUpSignal.Empty() )
1103   {
1104     mActionPageUpSignal.Emit( handle );
1105   }
1106
1107   bool ret = false;
1108
1109   if(mIsAccessibilityTtsEnabled)
1110   {
1111     Actor actor = GetCurrentFocusActor();
1112     if(actor)
1113     {
1114       Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1115       if(control)
1116       {
1117         // TODO: Notify the control to scroll up to the previous page. Should control handle this?
1118 //        ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::UP);
1119       }
1120     }
1121   }
1122
1123   return ret;
1124 }
1125
1126 bool AccessibilityManager::AccessibilityActionPageDown()
1127 {
1128   Dali::Toolkit::AccessibilityManager handle( this );
1129   if( !mActionPageDownSignal.Empty() )
1130   {
1131     mActionPageDownSignal.Emit( handle );
1132   }
1133
1134   bool ret = false;
1135
1136   if(mIsAccessibilityTtsEnabled)
1137   {
1138     Actor actor = GetCurrentFocusActor();
1139     if(actor)
1140     {
1141       Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1142       if(control)
1143       {
1144         // TODO: Notify the control to scroll down to the next page. Should control handle this?
1145 //        ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::DOWN);
1146       }
1147     }
1148   }
1149
1150   return ret;
1151 }
1152
1153 bool AccessibilityManager::AccessibilityActionMoveToFirst()
1154 {
1155   Dali::Toolkit::AccessibilityManager handle( this );
1156   if( !mActionMoveToFirstSignal.Empty() )
1157   {
1158     mActionMoveToFirstSignal.Emit( handle );
1159   }
1160
1161   // TODO: Move to the first item on screen
1162
1163   return mIsAccessibilityTtsEnabled;
1164 }
1165
1166 bool AccessibilityManager::AccessibilityActionMoveToLast()
1167 {
1168   Dali::Toolkit::AccessibilityManager handle( this );
1169   if( !mActionMoveToLastSignal.Empty() )
1170   {
1171     mActionMoveToLastSignal.Emit( handle );
1172   }
1173
1174   // TODO: Move to the last item on screen
1175
1176   return mIsAccessibilityTtsEnabled;
1177 }
1178
1179 bool AccessibilityManager::AccessibilityActionReadFromTop()
1180 {
1181   Dali::Toolkit::AccessibilityManager handle( this );
1182   if( !mActionReadFromTopSignal.Empty() )
1183   {
1184     mActionReadFromTopSignal.Emit( handle );
1185   }
1186
1187   // TODO: Move to the top item on screen and read from the item continuously
1188
1189   return mIsAccessibilityTtsEnabled;
1190 }
1191
1192 bool AccessibilityManager::AccessibilityActionReadFromNext()
1193 {
1194   Dali::Toolkit::AccessibilityManager handle( this );
1195
1196   if( !mActionReadFromNextSignal.Empty() )
1197   {
1198     mActionReadFromNextSignal.Emit( handle );
1199   }
1200
1201   if( mIsAccessibilityTtsEnabled )
1202   {
1203     // Mark that we are in continuous play mode, so TTS signals can move focus.
1204     mContinuousPlayMode = true;
1205
1206     // Attempt to move to the next item and read from the item continuously.
1207     MoveFocusForward();
1208   }
1209
1210   return mIsAccessibilityTtsEnabled;
1211 }
1212
1213 void AccessibilityManager::TtsStateChanged( const Dali::TtsPlayer::State previousState, const Dali::TtsPlayer::State currentState )
1214 {
1215   if( mContinuousPlayMode )
1216   {
1217     // If we were playing and now we have stopped, attempt to play the next item.
1218     if( ( previousState == Dali::TtsPlayer::PLAYING ) && ( currentState == Dali::TtsPlayer::READY ) )
1219     {
1220       // Attempt to move the focus forward and play.
1221       // If we can't cancel continuous play mode.
1222       if( !MoveFocusForward() )
1223       {
1224         // We are done, exit continuous play mode.
1225         mContinuousPlayMode = false;
1226       }
1227     }
1228     else
1229     {
1230       // Unexpected play state change, exit continuous play mode.
1231       mContinuousPlayMode = false;
1232     }
1233   }
1234 }
1235
1236 bool AccessibilityManager::AccessibilityActionZoom()
1237 {
1238   Dali::Toolkit::AccessibilityManager handle( this );
1239   if( !mActionZoomSignal.Empty() )
1240   {
1241     mActionZoomSignal.Emit( handle );
1242   }
1243
1244   bool ret = false;
1245
1246   if(mIsAccessibilityTtsEnabled)
1247   {
1248     Actor actor = GetCurrentFocusActor();
1249     if(actor)
1250     {
1251       Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1252       if(control)
1253       {
1254         // Notify the control to zoom
1255         ret = GetImplementation( control ).OnAccessibilityZoom();
1256       }
1257     }
1258   }
1259
1260   return ret;
1261 }
1262
1263 bool AccessibilityManager::AccessibilityActionReadPauseResume()
1264 {
1265   Dali::Toolkit::AccessibilityManager handle( this );
1266   if( !mActionReadPauseResumeSignal.Empty() )
1267   {
1268     mActionReadPauseResumeSignal.Emit( handle );
1269   }
1270
1271   bool ret = false;
1272
1273   if(mIsAccessibilityTtsEnabled)
1274   {
1275     // Pause or resume the TTS player
1276     Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
1277     Dali::TtsPlayer::State state = player.GetState();
1278     if(state == Dali::TtsPlayer::PLAYING)
1279     {
1280       player.Pause();
1281       ret = true;
1282     }
1283     else if(state == Dali::TtsPlayer::PAUSED)
1284     {
1285       player.Resume();
1286       ret = true;
1287     }
1288   }
1289
1290   return ret;
1291 }
1292
1293 bool AccessibilityManager::AccessibilityActionStartStop()
1294 {
1295   Dali::Toolkit::AccessibilityManager handle( this );
1296   if( !mActionStartStopSignal.Empty() )
1297   {
1298     mActionStartStopSignal.Emit( handle );
1299   }
1300
1301   // TODO: Start/stop the current action
1302
1303   return mIsAccessibilityTtsEnabled;
1304 }
1305
1306 bool AccessibilityManager::AccessibilityActionTouch(const TouchEvent& touchEvent)
1307 {
1308   bool handled = false;
1309
1310   // TODO: Need to convert the touchevent for the focused actor?
1311
1312   Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(GetCurrentFocusActor());
1313   if(control)
1314   {
1315     handled = GetImplementation( control ).OnAccessibilityTouch(touchEvent);
1316   }
1317
1318   return handled;
1319 }
1320
1321 bool AccessibilityManager::HandlePanGesture(const AccessibilityGestureEvent& panEvent)
1322 {
1323   bool handled = false;
1324
1325   if( panEvent.state == AccessibilityGestureEvent::Started )
1326   {
1327     // Find the focusable actor at the event position
1328     Dali::HitTestAlgorithm::Results results;
1329     AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
1330
1331     Dali::HitTestAlgorithm::HitTest( Stage::GetCurrent(), panEvent.currentPosition, results, IsActorFocusableFunction );
1332     mCurrentGesturedActor = results.actor;
1333
1334     if(!mCurrentGesturedActor)
1335     {
1336       DALI_LOG_ERROR("Gesture detected, but no hit actor\n");
1337     }
1338   }
1339
1340   // Gesture::Finished (Up) events are delivered with previous (Motion) event position
1341   // Use the real previous position; otherwise we may incorrectly get a ZERO velocity
1342   if ( AccessibilityGestureEvent::Finished != panEvent.state )
1343   {
1344     // Store the previous position for next Gesture::Finished iteration.
1345     mPreviousPosition = panEvent.previousPosition;
1346   }
1347
1348   Actor rootActor = Stage::GetCurrent().GetRootLayer();
1349
1350   Dali::PanGesture pan( static_cast<Dali::Gesture::State>(panEvent.state) );
1351
1352   pan.time = panEvent.time;
1353   pan.numberOfTouches = panEvent.numberOfTouches;
1354   pan.screenPosition = panEvent.currentPosition;
1355   pan.screenDisplacement = mPreviousPosition - panEvent.currentPosition;
1356   pan.screenVelocity.x = pan.screenDisplacement.x / panEvent.timeDelta;
1357   pan.screenVelocity.y = pan.screenDisplacement.y / panEvent.timeDelta;
1358
1359   // Only handle the pan gesture when the current focused actor is scrollable or within a scrollable actor
1360   while(mCurrentGesturedActor && mCurrentGesturedActor != rootActor && !handled)
1361   {
1362     Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(mCurrentGesturedActor);
1363     if(control)
1364     {
1365       Vector2 localCurrent;
1366       control.ScreenToLocal( localCurrent.x, localCurrent.y, panEvent.currentPosition.x, panEvent.currentPosition.y );
1367       pan.position = localCurrent;
1368
1369       Vector2 localPrevious;
1370       control.ScreenToLocal( localPrevious.x, localPrevious.y, mPreviousPosition.x, mPreviousPosition.y );
1371
1372       pan.displacement = localCurrent - localPrevious;
1373       pan.velocity.x = pan.displacement.x / panEvent.timeDelta;
1374       pan.velocity.y = pan.displacement.y / panEvent.timeDelta;
1375
1376       handled = GetImplementation( control ).OnAccessibilityPan(pan);
1377     }
1378
1379     // If the gesture is not handled by the control, check its parent
1380     if(!handled)
1381     {
1382       mCurrentGesturedActor = mCurrentGesturedActor.GetParent();
1383
1384       if(!mCurrentGesturedActor)
1385       {
1386         DALI_LOG_ERROR("no more gestured actor\n");
1387       }
1388     }
1389     else
1390     {
1391       // If handled, then update the pan gesture properties
1392       PanGestureDetector::SetPanGestureProperties( pan );
1393     }
1394   }
1395
1396   return handled;
1397 }
1398
1399 Toolkit::AccessibilityManager::FocusChangedSignalType& AccessibilityManager::FocusChangedSignal()
1400 {
1401   return mFocusChangedSignal;
1402 }
1403
1404 Toolkit::AccessibilityManager::FocusOvershotSignalType& AccessibilityManager::FocusOvershotSignal()
1405 {
1406   return mFocusOvershotSignal;
1407 }
1408
1409 Toolkit::AccessibilityManager::FocusedActorActivatedSignalType& AccessibilityManager::FocusedActorActivatedSignal()
1410 {
1411   return mFocusedActorActivatedSignal;
1412 }
1413
1414 } // namespace Internal
1415
1416 } // namespace Toolkit
1417
1418 } // namespace Dali