3a232147b7e2fa601dcfad583bb73e812e12592f
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / accessibility-manager / accessibility-manager-impl.cpp
1 /*
2  * Copyright (c) 2020 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/devel-api/events/pan-gesture-devel.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.GetProperty< int >( Actor::Property::ID );
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.GetProperty< int >( Actor::Property::ID ));
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.GetProperty< int >( Actor::Property::ID )));
247     }
248
249     // Update the actor's focus order in its additional data
250     SynchronizeActorAdditionalInfo(actor.GetProperty< int >( Actor::Property::ID ), 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.GetProperty< int >( Actor::Property::ID ));
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.GetProperty< int >( Actor::Property::ID ));
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::EnableAccessibility(bool enabled)
562 {
563   AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
564   adaptor.EnableAccessibility(enabled);
565 }
566
567 bool AccessibilityManager::IsEnabled() const
568 {
569   AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
570   return adaptor.IsEnabled();
571 }
572
573 void AccessibilityManager::SetGroupMode(bool enabled)
574 {
575   mIsFocusWithinGroup = enabled;
576 }
577
578 bool AccessibilityManager::GetGroupMode() const
579 {
580   return mIsFocusWithinGroup;
581 }
582
583 void AccessibilityManager::SetWrapMode(bool wrapped)
584 {
585   mIsWrapped = wrapped;
586 }
587
588 bool AccessibilityManager::GetWrapMode() const
589 {
590   return mIsWrapped;
591 }
592
593 void AccessibilityManager::SetFocusIndicatorActor(Actor indicator)
594 {
595   if( mFocusIndicatorActor != indicator )
596   {
597     Actor currentFocusActor = GetCurrentFocusActor();
598     if( currentFocusActor )
599     {
600       // The new focus indicator should be added to the current focused actor immediately
601       if( mFocusIndicatorActor )
602       {
603         currentFocusActor.Remove( mFocusIndicatorActor );
604       }
605
606       if( indicator )
607       {
608         currentFocusActor.Add( indicator );
609       }
610     }
611
612     mFocusIndicatorActor = indicator;
613   }
614 }
615
616 Actor AccessibilityManager::GetFocusIndicatorActor()
617 {
618   if( ! mFocusIndicatorActor )
619   {
620     // Create the default if it hasn't been set and one that's shared by all the keyboard focusable actors
621     const std::string imageDirPath = AssetManager::GetDaliImagePath();
622     const std::string focusBorderImagePath = imageDirPath + FOCUS_BORDER_IMAGE_FILE_NAME;
623
624     mFocusIndicatorActor = Toolkit::ImageView::New(focusBorderImagePath);
625     mFocusIndicatorActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
626     mFocusIndicatorActor.SetProperty( Actor::Property::POSITION_Z,  1.0f );
627
628     // Apply size constraint to the focus indicator
629     mFocusIndicatorActor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
630   }
631
632   return mFocusIndicatorActor;
633 }
634
635 bool AccessibilityManager::DoMoveFocus(FocusIDIter focusIDIter, bool forward, bool wrapped)
636 {
637   DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] %d focusable actors\n", __FUNCTION__, __LINE__, mFocusIDContainer.size());
638   DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
639
640   if( (forward && ++focusIDIter == mFocusIDContainer.end())
641     || (!forward && focusIDIter-- == mFocusIDContainer.begin()) )
642   {
643     if(mIsEndcapFeedbackEnabled)
644     {
645       if(mIsEndcapFeedbackPlayed == false)
646       {
647         // play sound & skip moving once
648         Dali::SoundPlayer soundPlayer = Dali::SoundPlayer::Get();
649         if(soundPlayer)
650         {
651           if (!mIsFocusChainEndSoundFilePathSet)
652           {
653             const std::string soundDirPath = AssetManager::GetDaliSoundPath();
654             mFocusChainEndSoundFilePath = soundDirPath + FOCUS_CHAIN_END_SOUND_FILE_NAME;
655             mIsFocusChainEndSoundFilePathSet = true;
656           }
657           soundPlayer.PlaySound(mFocusChainEndSoundFilePath);
658         }
659
660         mIsEndcapFeedbackPlayed = true;
661         return true;
662       }
663       mIsEndcapFeedbackPlayed = false;
664     }
665
666     if(wrapped)
667     {
668       if(forward)
669       {
670         focusIDIter = mFocusIDContainer.begin();
671       }
672       else
673       {
674         focusIDIter = mFocusIDContainer.end();
675         --focusIDIter;//We want forward iterator to the last element here
676       }
677     }
678     else
679     {
680       DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] Overshot\n", __FUNCTION__, __LINE__);
681       // Send notification for handling overshooted situation
682       mFocusOvershotSignal.Emit(GetCurrentFocusActor(), forward ? Toolkit::AccessibilityManager::OVERSHOT_NEXT : Toolkit::AccessibilityManager::OVERSHOT_PREVIOUS);
683
684       return false; // Try to move the focus out of the scope
685     }
686   }
687
688   // Invalid focus.
689   if( focusIDIter == mFocusIDContainer.end() )
690   {
691     return false;
692   }
693
694   // Note: This function performs the focus change.
695   if( !DoSetCurrentFocusActor( (*focusIDIter).second ) )
696   {
697     mRecursiveFocusMoveCounter++;
698     if(mRecursiveFocusMoveCounter > mFocusIDContainer.size())
699     {
700       // We've attempted to focus all the actors in the whole focus chain and no actor
701       // can be focused successfully.
702       DALI_LOG_WARNING("[%s] There is no more focusable actor in %d focus chains\n", __FUNCTION__, mRecursiveFocusMoveCounter);
703
704       return false;
705     }
706     else
707     {
708       return DoMoveFocus(focusIDIter, forward, wrapped);
709     }
710   }
711
712   return true;
713 }
714
715 void AccessibilityManager::SetFocusable(Actor actor, bool focusable)
716 {
717   if(actor)
718   {
719     // Create/Set actor focusable property.
720     actor.RegisterProperty( ACTOR_FOCUSABLE, focusable, Property::READ_WRITE );
721   }
722 }
723
724 bool AccessibilityManager::ChangeAccessibilityStatus()
725 {
726   AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
727   mIsAccessibilityTtsEnabled = adaptor.IsEnabled();
728   Dali::Toolkit::AccessibilityManager handle( this );
729
730   if(mIsAccessibilityTtsEnabled)
731   {
732     // Show indicator when tts turned on if there is focused actor.
733     Actor actor = GetCurrentFocusActor();
734     if(actor)
735     {
736       actor.Add( GetFocusIndicatorActor() );
737     }
738     mIsFocusIndicatorEnabled = true;
739
740     // Connect a signal to the TTS player to implement continuous reading mode.
741     Dali::TtsPlayer player = Dali::TtsPlayer::Get( Dali::TtsPlayer::SCREEN_READER );
742     player.StateChangedSignal().Connect( this, &AccessibilityManager::TtsStateChanged );
743     mTtsCreated = true;
744   }
745   else
746   {
747     // Hide indicator when tts turned off
748     Actor actor = GetCurrentFocusActor();
749     if( actor && mFocusIndicatorActor )
750     {
751       actor.Remove( mFocusIndicatorActor );
752     }
753     mIsFocusIndicatorEnabled = false;
754
755     if( mTtsCreated )
756     {
757       // Disconnect the TTS state change signal.
758       Dali::TtsPlayer player = Dali::TtsPlayer::Get( Dali::TtsPlayer::SCREEN_READER );
759       player.StateChangedSignal().Disconnect( this, &AccessibilityManager::TtsStateChanged );
760       mTtsCreated = true;
761     }
762   }
763
764   mStatusChangedSignal.Emit( handle );
765
766   return true;
767 }
768
769 bool AccessibilityManager::AccessibilityActionNext(bool allowEndFeedback)
770 {
771   Dali::Toolkit::AccessibilityManager handle( this );
772   if( !mActionNextSignal.Empty() )
773   {
774     mActionNextSignal.Emit( handle );
775   }
776
777   if(mIsAccessibilityTtsEnabled)
778   {
779     mIsEndcapFeedbackEnabled = allowEndFeedback;
780     return MoveFocusForward();
781   }
782   else
783   {
784     return false;
785   }
786 }
787
788 bool AccessibilityManager::AccessibilityActionPrevious(bool allowEndFeedback)
789 {
790   Dali::Toolkit::AccessibilityManager handle( this );
791   if( !mActionPreviousSignal.Empty() )
792   {
793     mActionPreviousSignal.Emit( handle );
794   }
795
796   if(mIsAccessibilityTtsEnabled)
797   {
798     mIsEndcapFeedbackEnabled = allowEndFeedback;
799     return MoveFocusBackward();
800   }
801   else
802   {
803     return false;
804   }
805 }
806
807 bool AccessibilityManager::AccessibilityActionActivate()
808 {
809   Dali::Toolkit::AccessibilityManager handle( this );
810   if( !mActionActivateSignal.Empty() )
811   {
812     mActionActivateSignal.Emit( handle );
813   }
814
815   bool ret = false;
816
817   Actor actor = GetCurrentFocusActor();
818   if(actor)
819   {
820     DoActivate(actor);
821     ret = true;
822   }
823
824   return ret;
825 }
826
827 bool AccessibilityManager::AccessibilityActionRead(bool allowReadAgain)
828 {
829   Dali::Toolkit::AccessibilityManager handle( this );
830
831   if( allowReadAgain )
832   {
833     if ( !mActionReadSignal.Empty() )
834     {
835       mActionReadSignal.Emit( handle );
836     }
837   }
838   else
839   {
840     if ( !mActionOverSignal.Empty() )
841     {
842       mActionOverSignal.Emit( handle );
843     }
844   }
845
846   bool ret = false;
847
848   if(mIsAccessibilityTtsEnabled)
849   {
850     // Find the focusable actor at the read position
851     AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
852     Dali::HitTestAlgorithm::Results results;
853     Dali::HitTestAlgorithm::HitTest( Stage::GetCurrent(), adaptor.GetReadPosition(), results, IsActorFocusableFunction );
854
855     FocusIDIter focusIDIter = mFocusIDContainer.find(GetFocusOrder(results.actor));
856     if(focusIDIter != mFocusIDContainer.end())
857     {
858       if( allowReadAgain || (results.actor != GetCurrentFocusActor()) )
859       {
860         // Move the focus to the actor
861         ret = SetCurrentFocusActor(results.actor);
862         DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] SetCurrentFocusActor returns %s\n", __FUNCTION__, __LINE__, ret?"TRUE":"FALSE");
863       }
864     }
865   }
866
867   return ret;
868 }
869
870 bool AccessibilityManager::AccessibilityActionReadNext(bool allowEndFeedback)
871 {
872   Dali::Toolkit::AccessibilityManager handle( this );
873   if( !mActionReadNextSignal.Empty() )
874   {
875     mActionReadNextSignal.Emit( handle );
876   }
877
878   if(mIsAccessibilityTtsEnabled)
879   {
880     return MoveFocusForward();
881   }
882   else
883   {
884     return false;
885   }
886 }
887
888 bool AccessibilityManager::AccessibilityActionReadPrevious(bool allowEndFeedback)
889 {
890   Dali::Toolkit::AccessibilityManager handle( this );
891   if( !mActionReadPreviousSignal.Empty() )
892   {
893     mActionReadPreviousSignal.Emit( handle );
894   }
895
896   if(mIsAccessibilityTtsEnabled)
897   {
898     return MoveFocusBackward();
899   }
900   else
901   {
902     return false;
903   }
904 }
905
906 bool AccessibilityManager::AccessibilityActionUp()
907 {
908   Dali::Toolkit::AccessibilityManager handle( this );
909   if( !mActionUpSignal.Empty() )
910   {
911     mActionUpSignal.Emit( handle );
912   }
913
914   bool ret = false;
915
916   if(mIsAccessibilityTtsEnabled)
917   {
918     Actor actor = GetCurrentFocusActor();
919     if(actor)
920     {
921       Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
922       if(control)
923       {
924         // Notify the control that it is activated
925         ret = GetImplementation( control ).OnAccessibilityValueChange(true);
926       }
927     }
928   }
929
930   return ret;
931 }
932
933 bool AccessibilityManager::AccessibilityActionDown()
934 {
935   Dali::Toolkit::AccessibilityManager handle( this );
936   if( !mActionDownSignal.Empty() )
937   {
938     mActionDownSignal.Emit( handle );
939   }
940
941   bool ret = false;
942
943   if(mIsAccessibilityTtsEnabled)
944   {
945     Actor actor = GetCurrentFocusActor();
946     if(actor)
947     {
948       Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
949       if(control)
950       {
951         // Notify the control that it is activated
952         ret = GetImplementation( control ).OnAccessibilityValueChange(false);
953       }
954     }
955   }
956
957   return ret;
958 }
959
960 bool AccessibilityManager::ClearAccessibilityFocus()
961 {
962   Dali::Toolkit::AccessibilityManager handle( this );
963   if( !mActionClearFocusSignal.Empty() )
964   {
965     mActionClearFocusSignal.Emit( handle );
966   }
967
968   if(mIsAccessibilityTtsEnabled)
969   {
970     ClearFocus();
971     return true;
972   }
973   else
974   {
975     return false;
976   }
977 }
978
979 bool AccessibilityManager::AccessibilityActionScroll( Dali::TouchEvent& touch )
980 {
981   Dali::Toolkit::AccessibilityManager handle( this );
982   if( !mActionScrollSignal.Empty() )
983   {
984     mActionScrollSignal.Emit( handle, touch );
985   }
986
987   return true;
988 }
989
990 bool AccessibilityManager::AccessibilityActionBack()
991 {
992   Dali::Toolkit::AccessibilityManager handle( this );
993   if( !mActionBackSignal.Empty() )
994   {
995     mActionBackSignal.Emit( handle );
996   }
997
998   // TODO: Back to previous view
999
1000   return mIsAccessibilityTtsEnabled;
1001 }
1002
1003 bool AccessibilityManager::AccessibilityActionScrollUp()
1004 {
1005   Dali::Toolkit::AccessibilityManager handle( this );
1006   if( !mActionScrollUpSignal.Empty() )
1007   {
1008     mActionScrollUpSignal.Emit( handle );
1009   }
1010
1011   bool ret = false;
1012
1013   if(mIsAccessibilityTtsEnabled)
1014   {
1015     Actor actor = GetCurrentFocusActor();
1016     if(actor)
1017     {
1018       Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1019       if(control)
1020       {
1021         // TODO: Notify the control to scroll up. Should control handle this?
1022 //        ret = GetImplementation( control ).OnAccessibilityScroll(Direction::UP);
1023       }
1024     }
1025   }
1026
1027   return ret;
1028 }
1029
1030 bool AccessibilityManager::AccessibilityActionScrollDown()
1031 {
1032   Dali::Toolkit::AccessibilityManager handle( this );
1033   if( !mActionScrollDownSignal.Empty() )
1034   {
1035     mActionScrollDownSignal.Emit( handle );
1036   }
1037
1038   bool ret = false;
1039
1040   if(mIsAccessibilityTtsEnabled)
1041   {
1042     Actor actor = GetCurrentFocusActor();
1043     if(actor)
1044     {
1045       Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1046       if(control)
1047       {
1048         // TODO: Notify the control to scroll down. Should control handle this?
1049 //        ret = GetImplementation( control ).OnAccessibilityScrollDown(Direction::DOWN);
1050       }
1051     }
1052   }
1053
1054   return ret;
1055 }
1056
1057 bool AccessibilityManager::AccessibilityActionPageLeft()
1058 {
1059   Dali::Toolkit::AccessibilityManager handle( this );
1060   if( !mActionPageLeftSignal.Empty() )
1061   {
1062     mActionPageLeftSignal.Emit( handle );
1063   }
1064
1065   bool ret = false;
1066
1067   if(mIsAccessibilityTtsEnabled)
1068   {
1069     Actor actor = GetCurrentFocusActor();
1070     if(actor)
1071     {
1072       Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1073       if(control)
1074       {
1075         // TODO: Notify the control to scroll left to the previous page. Should control handle this?
1076 //        ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::LEFT);
1077       }
1078     }
1079   }
1080
1081   return ret;
1082 }
1083
1084 bool AccessibilityManager::AccessibilityActionPageRight()
1085 {
1086   Dali::Toolkit::AccessibilityManager handle( this );
1087   if( !mActionPageRightSignal.Empty() )
1088   {
1089     mActionPageRightSignal.Emit( handle );
1090   }
1091
1092   bool ret = false;
1093
1094   if(mIsAccessibilityTtsEnabled)
1095   {
1096     Actor actor = GetCurrentFocusActor();
1097     if(actor)
1098     {
1099       Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1100       if(control)
1101       {
1102         // TODO: Notify the control to scroll right to the next page. Should control handle this?
1103 //        ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::RIGHT);
1104       }
1105     }
1106   }
1107
1108   return ret;
1109 }
1110
1111 bool AccessibilityManager::AccessibilityActionPageUp()
1112 {
1113   Dali::Toolkit::AccessibilityManager handle( this );
1114   if( !mActionPageUpSignal.Empty() )
1115   {
1116     mActionPageUpSignal.Emit( handle );
1117   }
1118
1119   bool ret = false;
1120
1121   if(mIsAccessibilityTtsEnabled)
1122   {
1123     Actor actor = GetCurrentFocusActor();
1124     if(actor)
1125     {
1126       Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1127       if(control)
1128       {
1129         // TODO: Notify the control to scroll up to the previous page. Should control handle this?
1130 //        ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::UP);
1131       }
1132     }
1133   }
1134
1135   return ret;
1136 }
1137
1138 bool AccessibilityManager::AccessibilityActionPageDown()
1139 {
1140   Dali::Toolkit::AccessibilityManager handle( this );
1141   if( !mActionPageDownSignal.Empty() )
1142   {
1143     mActionPageDownSignal.Emit( handle );
1144   }
1145
1146   bool ret = false;
1147
1148   if(mIsAccessibilityTtsEnabled)
1149   {
1150     Actor actor = GetCurrentFocusActor();
1151     if(actor)
1152     {
1153       Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1154       if(control)
1155       {
1156         // TODO: Notify the control to scroll down to the next page. Should control handle this?
1157 //        ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::DOWN);
1158       }
1159     }
1160   }
1161
1162   return ret;
1163 }
1164
1165 bool AccessibilityManager::AccessibilityActionMoveToFirst()
1166 {
1167   Dali::Toolkit::AccessibilityManager handle( this );
1168   if( !mActionMoveToFirstSignal.Empty() )
1169   {
1170     mActionMoveToFirstSignal.Emit( handle );
1171   }
1172
1173   // TODO: Move to the first item on screen
1174
1175   return mIsAccessibilityTtsEnabled;
1176 }
1177
1178 bool AccessibilityManager::AccessibilityActionMoveToLast()
1179 {
1180   Dali::Toolkit::AccessibilityManager handle( this );
1181   if( !mActionMoveToLastSignal.Empty() )
1182   {
1183     mActionMoveToLastSignal.Emit( handle );
1184   }
1185
1186   // TODO: Move to the last item on screen
1187
1188   return mIsAccessibilityTtsEnabled;
1189 }
1190
1191 bool AccessibilityManager::AccessibilityActionReadFromTop()
1192 {
1193   Dali::Toolkit::AccessibilityManager handle( this );
1194   if( !mActionReadFromTopSignal.Empty() )
1195   {
1196     mActionReadFromTopSignal.Emit( handle );
1197   }
1198
1199   // TODO: Move to the top item on screen and read from the item continuously
1200
1201   return mIsAccessibilityTtsEnabled;
1202 }
1203
1204 bool AccessibilityManager::AccessibilityActionReadFromNext()
1205 {
1206   Dali::Toolkit::AccessibilityManager handle( this );
1207
1208   if( !mActionReadFromNextSignal.Empty() )
1209   {
1210     mActionReadFromNextSignal.Emit( handle );
1211   }
1212
1213   if( mIsAccessibilityTtsEnabled )
1214   {
1215     // Mark that we are in continuous play mode, so TTS signals can move focus.
1216     mContinuousPlayMode = true;
1217
1218     // Attempt to move to the next item and read from the item continuously.
1219     MoveFocusForward();
1220   }
1221
1222   return mIsAccessibilityTtsEnabled;
1223 }
1224
1225 void AccessibilityManager::TtsStateChanged( const Dali::TtsPlayer::State previousState, const Dali::TtsPlayer::State currentState )
1226 {
1227   if( mContinuousPlayMode )
1228   {
1229     // If we were playing and now we have stopped, attempt to play the next item.
1230     if( ( previousState == Dali::TtsPlayer::PLAYING ) && ( currentState == Dali::TtsPlayer::READY ) )
1231     {
1232       // Attempt to move the focus forward and play.
1233       // If we can't cancel continuous play mode.
1234       if( !MoveFocusForward() )
1235       {
1236         // We are done, exit continuous play mode.
1237         mContinuousPlayMode = false;
1238       }
1239     }
1240     else
1241     {
1242       // Unexpected play state change, exit continuous play mode.
1243       mContinuousPlayMode = false;
1244     }
1245   }
1246 }
1247
1248 bool AccessibilityManager::AccessibilityActionZoom()
1249 {
1250   Dali::Toolkit::AccessibilityManager handle( this );
1251   if( !mActionZoomSignal.Empty() )
1252   {
1253     mActionZoomSignal.Emit( handle );
1254   }
1255
1256   bool ret = false;
1257
1258   if(mIsAccessibilityTtsEnabled)
1259   {
1260     Actor actor = GetCurrentFocusActor();
1261     if(actor)
1262     {
1263       Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1264       if(control)
1265       {
1266         // Notify the control to zoom
1267         ret = GetImplementation( control ).OnAccessibilityZoom();
1268       }
1269     }
1270   }
1271
1272   return ret;
1273 }
1274
1275 bool AccessibilityManager::AccessibilityActionReadPauseResume()
1276 {
1277   Dali::Toolkit::AccessibilityManager handle( this );
1278   if( !mActionReadPauseResumeSignal.Empty() )
1279   {
1280     mActionReadPauseResumeSignal.Emit( handle );
1281   }
1282
1283   bool ret = false;
1284
1285   if(mIsAccessibilityTtsEnabled)
1286   {
1287     // Pause or resume the TTS player
1288     Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
1289     Dali::TtsPlayer::State state = player.GetState();
1290     if(state == Dali::TtsPlayer::PLAYING)
1291     {
1292       player.Pause();
1293       ret = true;
1294     }
1295     else if(state == Dali::TtsPlayer::PAUSED)
1296     {
1297       player.Resume();
1298       ret = true;
1299     }
1300   }
1301
1302   return ret;
1303 }
1304
1305 bool AccessibilityManager::AccessibilityActionStartStop()
1306 {
1307   Dali::Toolkit::AccessibilityManager handle( this );
1308   if( !mActionStartStopSignal.Empty() )
1309   {
1310     mActionStartStopSignal.Emit( handle );
1311   }
1312
1313   // TODO: Start/stop the current action
1314
1315   return mIsAccessibilityTtsEnabled;
1316 }
1317
1318 bool AccessibilityManager::HandlePanGesture(const AccessibilityGestureEvent& panEvent)
1319 {
1320   bool handled = false;
1321
1322   if( panEvent.state == AccessibilityGestureEvent::STARTED )
1323   {
1324     // Find the focusable actor at the event position
1325     Dali::HitTestAlgorithm::Results results;
1326     AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
1327
1328     Dali::HitTestAlgorithm::HitTest( Stage::GetCurrent(), panEvent.currentPosition, results, IsActorFocusableFunction );
1329     mCurrentGesturedActor = results.actor;
1330
1331     if(!mCurrentGesturedActor)
1332     {
1333       DALI_LOG_ERROR("Gesture detected, but no hit actor\n");
1334     }
1335   }
1336
1337   // GestureState::FINISHED (Up) events are delivered with previous (Motion) event position
1338   // Use the real previous position; otherwise we may incorrectly get a ZERO velocity
1339   if ( AccessibilityGestureEvent::FINISHED != panEvent.state )
1340   {
1341     // Store the previous position for next GestureState::FINISHED iteration.
1342     mPreviousPosition = panEvent.previousPosition;
1343   }
1344
1345   Actor rootActor = Stage::GetCurrent().GetRootLayer();
1346
1347   Dali::PanGesture pan = DevelPanGesture::New( static_cast<Dali::GestureState>(panEvent.state) );
1348   DevelPanGesture::SetTime( pan, panEvent.time );
1349   DevelPanGesture::SetNumberOfTouches( pan, panEvent.numberOfTouches  );
1350   DevelPanGesture::SetScreenPosition( pan, panEvent.currentPosition );
1351   DevelPanGesture::SetScreenDisplacement( pan, mPreviousPosition - panEvent.currentPosition );
1352   DevelPanGesture::SetScreenVelocity( pan, Vector2( pan.GetScreenDisplacement().x / panEvent.timeDelta, pan.GetScreenDisplacement().y / panEvent.timeDelta ) );
1353
1354   // Only handle the pan gesture when the current focused actor is scrollable or within a scrollable actor
1355   while(mCurrentGesturedActor && mCurrentGesturedActor != rootActor && !handled)
1356   {
1357     Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(mCurrentGesturedActor);
1358     if(control)
1359     {
1360       Vector2 localCurrent;
1361       control.ScreenToLocal( localCurrent.x, localCurrent.y, panEvent.currentPosition.x, panEvent.currentPosition.y );
1362       DevelPanGesture::SetPosition( pan, localCurrent );
1363
1364       Vector2 localPrevious;
1365       control.ScreenToLocal( localPrevious.x, localPrevious.y, mPreviousPosition.x, mPreviousPosition.y );
1366
1367       DevelPanGesture::SetDisplacement( pan, localCurrent - localPrevious );
1368       DevelPanGesture::SetVelocity( pan, Vector2( pan.GetDisplacement().x / panEvent.timeDelta, pan.GetDisplacement().y / panEvent.timeDelta ));
1369
1370       handled = GetImplementation( control ).OnAccessibilityPan(pan);
1371     }
1372
1373     // If the gesture is not handled by the control, check its parent
1374     if(!handled)
1375     {
1376       mCurrentGesturedActor = mCurrentGesturedActor.GetParent();
1377
1378       if(!mCurrentGesturedActor)
1379       {
1380         DALI_LOG_ERROR("no more gestured actor\n");
1381       }
1382     }
1383     else
1384     {
1385       // If handled, then update the pan gesture properties
1386       PanGestureDetector::SetPanGestureProperties( pan );
1387     }
1388   }
1389
1390   return handled;
1391 }
1392
1393 Toolkit::AccessibilityManager::FocusChangedSignalType& AccessibilityManager::FocusChangedSignal()
1394 {
1395   return mFocusChangedSignal;
1396 }
1397
1398 Toolkit::AccessibilityManager::FocusOvershotSignalType& AccessibilityManager::FocusOvershotSignal()
1399 {
1400   return mFocusOvershotSignal;
1401 }
1402
1403 Toolkit::AccessibilityManager::FocusedActorActivatedSignalType& AccessibilityManager::FocusedActorActivatedSignal()
1404 {
1405   return mFocusedActorActivatedSignal;
1406 }
1407
1408 } // namespace Internal
1409
1410 } // namespace Toolkit
1411
1412 } // namespace Dali