2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "accessibility-manager-impl.h"
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>
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>
46 namespace // unnamed namespace
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";
55 #if defined(DEBUG_ENABLED)
56 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_FOCUS_MANAGER");
59 const char* const ACTOR_FOCUSABLE("focusable");
60 const char* const IS_FOCUS_GROUP("isFocusGroup");
62 const char* FOCUS_BORDER_IMAGE_FILE_NAME = "B16-8_TTS_focus.9.png";
64 const char* FOCUS_SOUND_FILE_NAME = "Focus.ogg";
65 const char* FOCUS_CHAIN_END_SOUND_FILE_NAME = "End_of_List.ogg";
68 * The function to be used in the hit-test algorithm to check whether the actor is hittable.
70 bool IsActorFocusableFunction(Actor actor, Dali::HitTestAlgorithm::TraverseType type)
72 bool hittable = false;
76 case Dali::HitTestAlgorithm::CHECK_ACTOR:
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
82 // Check whether the actor is focusable
83 Property::Index propertyActorFocusable = actor.GetPropertyIndex(ACTOR_FOCUSABLE);
84 if(propertyActorFocusable != Property::INVALID_INDEX)
86 hittable = actor.GetProperty<bool>(propertyActorFocusable);
91 case Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE:
93 if( actor.GetCurrentProperty< bool >( Actor::Property::VISIBLE ) ) // Actor is visible, if not visible then none of its children are visible.
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(),
119 mIsFocusWithinGroup(false),
120 mIsEndcapFeedbackEnabled(false),
121 mIsEndcapFeedbackPlayed(false),
122 mIsAccessibilityTtsEnabled(false),
124 mIsFocusIndicatorEnabled(false),
125 mContinuousPlayMode(false),
126 mIsFocusSoundFilePathSet(false),
127 mIsFocusChainEndSoundFilePathSet(false)
131 AccessibilityManager::~AccessibilityManager()
135 void AccessibilityManager::Initialise()
137 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
138 adaptor.SetActionHandler(*this);
139 adaptor.SetGestureHandler(*this);
141 ChangeAccessibilityStatus();
144 AccessibilityManager::ActorAdditionalInfo AccessibilityManager::GetActorAdditionalInfo(const unsigned int actorID) const
146 ActorAdditionalInfo data;
147 IDAdditionalInfoConstIter iter = mIDAdditionalInfoContainer.find(actorID);
148 if(iter != mIDAdditionalInfoContainer.end())
150 data = (*iter).second;
156 void AccessibilityManager::SynchronizeActorAdditionalInfo(const unsigned int actorID, const unsigned int order)
158 ActorAdditionalInfo actorInfo = GetActorAdditionalInfo(actorID);
159 actorInfo.mFocusOrder = order;
160 mIDAdditionalInfoContainer.erase(actorID);
161 mIDAdditionalInfoContainer.insert(IDAdditionalInfoPair(actorID, actorInfo));
164 void AccessibilityManager::SetAccessibilityAttribute(Actor actor, Toolkit::AccessibilityManager::AccessibilityAttribute type, const std::string& text)
168 unsigned int actorID = actor.GetProperty< int >( Actor::Property::ID );
170 ActorAdditionalInfo info = GetActorAdditionalInfo(actorID);
171 info.mAccessibilityAttributes[type] = text;
173 mIDAdditionalInfoContainer.erase(actorID);
174 mIDAdditionalInfoContainer.insert(IDAdditionalInfoPair(actorID, info));
178 void AccessibilityManager::DeleteAccessibilityAttribute(Actor actor)
182 unsigned int actorID = actor.GetProperty< int >( Actor::Property::ID );
183 mIDAdditionalInfoContainer.erase(actorID);
187 std::string AccessibilityManager::GetAccessibilityAttribute(Actor actor, Toolkit::AccessibilityManager::AccessibilityAttribute type) const
193 ActorAdditionalInfo data = GetActorAdditionalInfo(actor.GetProperty< int >( Actor::Property::ID ));
194 text = data.mAccessibilityAttributes[type];
200 void AccessibilityManager::SetFocusOrder(Actor actor, const unsigned int order)
202 // Do nothing if the focus order of the actor is not changed.
203 if(actor && GetFocusOrder(actor) != order)
205 // Firstly delete the actor from the focus chain if it's already there with a different focus order.
206 mFocusIDContainer.erase(GetFocusOrder(actor));
208 // Create/retrieve actor focusable property
209 Property::Index propertyActorFocusable = actor.RegisterProperty( ACTOR_FOCUSABLE, true, Property::READ_WRITE );
213 // The actor is not focusable without a defined focus order.
214 actor.SetProperty(propertyActorFocusable, false);
216 // If the actor is currently being focused, it should clear the focus
217 if(actor == GetCurrentFocusActor())
222 else // Insert the actor to the focus chain
224 // Check whether there is another actor in the focus chain with the same focus order already.
225 FocusIDIter focusIDIter = mFocusIDContainer.find(order);
226 if(focusIDIter != mFocusIDContainer.end())
228 // We need to increase the focus order of that actor and all the actors followed it
229 // in the focus chain.
230 FocusIDIter lastIter = mFocusIDContainer.end();
231 --lastIter;//We want forward iterator to the last element here
232 mFocusIDContainer.insert(FocusIDPair((*lastIter).first + 1, (*lastIter).second));
234 // Update the actor's focus order in its additional data
235 SynchronizeActorAdditionalInfo((*lastIter).second, (*lastIter).first + 1);
237 for(FocusIDIter iter = lastIter; iter != focusIDIter; iter--)
239 FocusIDIter previousIter = iter;
240 --previousIter;//We want forward iterator to the previous element here
241 unsigned int actorID = (*previousIter).second;
242 (*iter).second = actorID;
244 // Update the actor's focus order in its additional data
245 SynchronizeActorAdditionalInfo(actorID, (*iter).first);
248 mFocusIDContainer.erase(order);
251 // The actor is focusable
252 actor.SetProperty(propertyActorFocusable, true);
254 // Now we insert the actor into the focus chain with the specified focus order
255 mFocusIDContainer.insert(FocusIDPair(order, actor.GetProperty< int >( Actor::Property::ID )));
258 // Update the actor's focus order in its additional data
259 SynchronizeActorAdditionalInfo(actor.GetProperty< int >( Actor::Property::ID ), order);
263 unsigned int AccessibilityManager::GetFocusOrder(Actor actor) const
265 unsigned int focusOrder = 0;
269 ActorAdditionalInfo data = GetActorAdditionalInfo(actor.GetProperty< int >( Actor::Property::ID ));
270 focusOrder = data.mFocusOrder;
276 unsigned int AccessibilityManager::GenerateNewFocusOrder() const
278 unsigned int order = 1;
279 FocusIDContainer::const_reverse_iterator iter = mFocusIDContainer.rbegin();
281 if(iter != mFocusIDContainer.rend())
283 order = (*iter).first + 1;
289 Actor AccessibilityManager::GetActorByFocusOrder(const unsigned int order)
291 Actor actor = Actor();
293 FocusIDIter focusIDIter = mFocusIDContainer.find(order);
294 if(focusIDIter != mFocusIDContainer.end())
296 Actor rootActor = Stage::GetCurrent().GetRootLayer();
297 actor = rootActor.FindChildById(mFocusIDContainer[order]);
303 bool AccessibilityManager::SetCurrentFocusActor(Actor actor)
307 return DoSetCurrentFocusActor(actor.GetProperty< int >( Actor::Property::ID ));
313 bool AccessibilityManager::DoSetCurrentFocusActor(const unsigned int actorID)
315 Actor rootActor = Stage::GetCurrent().GetRootLayer();
317 // If the group mode is enabled, check which focus group the current focused actor belongs to
319 if(mIsFocusWithinGroup)
321 focusGroup = GetFocusGroup(GetCurrentFocusActor());
326 focusGroup = rootActor;
329 Actor actor = focusGroup.FindChildById(actorID);
331 // Check whether the actor is in the stage
334 // Check whether the actor is focusable
335 bool actorFocusable = false;
336 Property::Index propertyActorFocusable = actor.GetPropertyIndex(ACTOR_FOCUSABLE);
337 if(propertyActorFocusable != Property::INVALID_INDEX)
339 actorFocusable = actor.GetProperty<bool>(propertyActorFocusable);
342 // Go through the actor's hierarchy to check whether the actor is visible
343 bool actorVisible = actor.GetCurrentProperty< bool >( Actor::Property::VISIBLE );
344 Actor parent = actor.GetParent();
345 while (actorVisible && parent && parent != rootActor)
347 actorVisible = parent.GetCurrentProperty< bool >( Actor::Property::VISIBLE );
348 parent = parent.GetParent();
351 // Check whether the actor is fully transparent
352 bool actorOpaque = actor.GetCurrentProperty< Vector4 >( Actor::Property::WORLD_COLOR ).a > 0.01f;
354 // Set the focus only when the actor is focusable and visible and not fully transparent
355 if(actorVisible && actorFocusable && actorOpaque)
357 // Draw the focus indicator upon the focused actor
358 if( mIsFocusIndicatorEnabled )
360 actor.Add( GetFocusIndicatorActor() );
363 auto adaptor = AccessibilityAdaptor::Get();
364 Vector2 readPosition = adaptor.GetReadPosition();
365 auto actorPosition = actor.GetCurrentProperty<Vector2>(Actor::Property::SCREEN_POSITION);
366 auto actorSize = actor.GetCurrentProperty<Vector2>(Actor::Property::SIZE);
367 Rect<> actorRect(actorPosition.x, actorPosition.y, actorSize.width, actorSize.height);
369 if(actorRect.Contains(Rect<>(readPosition.x, readPosition.y, 0, 0)))
371 // If the last touched position is within the extents of the actor,
372 // then use that position. (The center may be covered by some other actor).
373 adaptor.SetFocusedActorPosition(readPosition);
377 // Otherwise, use the center point.
378 adaptor.SetFocusedActorPosition(actorPosition + actorSize / 2);
381 // Send notification for the change of focus actor
382 mFocusChangedSignal.Emit( GetCurrentFocusActor(), actor );
384 // Save the current focused actor
385 mCurrentFocusActor = FocusIDPair(GetFocusOrder(actor), actorID);
387 if(mIsAccessibilityTtsEnabled)
389 Dali::SoundPlayer soundPlayer = Dali::SoundPlayer::Get();
392 if (!mIsFocusSoundFilePathSet)
394 const std::string soundDirPath = AssetManager::GetDaliSoundPath();
395 mFocusSoundFilePath = soundDirPath + FOCUS_SOUND_FILE_NAME;
396 mIsFocusSoundFilePathSet = true;
398 soundPlayer.PlaySound(mFocusSoundFilePath);
401 // Play the accessibility attributes with the TTS player.
402 Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
404 // Combine attribute texts to one text
405 std::string informationText;
406 for(int i = 0; i < Toolkit::AccessibilityManager::ACCESSIBILITY_ATTRIBUTE_NUM; i++)
408 if(!GetActorAdditionalInfo(actorID).mAccessibilityAttributes[i].empty())
412 informationText += ", "; // for space time between each information
414 informationText += GetActorAdditionalInfo(actorID).mAccessibilityAttributes[i];
417 player.Play(informationText);
424 DALI_LOG_WARNING("[%s:%d] FAILED\n", __FUNCTION__, __LINE__);
428 Actor AccessibilityManager::GetCurrentFocusActor()
430 Actor rootActor = Stage::GetCurrent().GetRootLayer();
431 return rootActor.FindChildById(mCurrentFocusActor.second);
434 Actor AccessibilityManager::GetCurrentFocusGroup()
436 return GetFocusGroup(GetCurrentFocusActor());
439 unsigned int AccessibilityManager::GetCurrentFocusOrder()
441 return mCurrentFocusActor.first;
444 bool AccessibilityManager::MoveFocusForward()
447 mRecursiveFocusMoveCounter = 0;
449 FocusIDIter focusIDIter = mFocusIDContainer.find(mCurrentFocusActor.first);
450 if (focusIDIter != mFocusIDContainer.end())
452 DALI_LOG_ERROR("[%s:%d] focus order : %d \n", __FUNCTION__, __LINE__, (*focusIDIter).first);
453 ret = DoMoveFocus(focusIDIter, true, mIsWrapped);
457 // TODO: if there is not focused actor, move first actor
458 if (!mFocusIDContainer.empty())
460 //if there is not focused actor, move 1st actor
461 focusIDIter = mFocusIDContainer.begin(); // TODO: I'm not sure it was sorted.
462 DALI_LOG_ERROR("[%s:%d] focus order : %d \n", __FUNCTION__, __LINE__, (*focusIDIter).first);
463 ret = DoSetCurrentFocusActor((*focusIDIter).second);
467 DALI_LOG_ERROR("[%s] %s\n", __FUNCTION__, ret?"SUCCEED!!!":"FAILED!!!");
472 bool AccessibilityManager::MoveFocusBackward()
475 mRecursiveFocusMoveCounter = 0;
477 FocusIDIter focusIDIter = mFocusIDContainer.find(mCurrentFocusActor.first);
478 if (focusIDIter != mFocusIDContainer.end())
480 DALI_LOG_ERROR("[%s:%d] focus order : %d \n", __FUNCTION__, __LINE__, (*focusIDIter).first);
481 ret = DoMoveFocus(focusIDIter, false, mIsWrapped);
485 // TODO: if there is not focused actor, move last actor
486 if (!mFocusIDContainer.empty())
488 //if there is not focused actor, move last actor
489 focusIDIter = mFocusIDContainer.end();
490 --focusIDIter;//We want forward iterator to the last element here
491 DALI_LOG_ERROR("[%s:%d] focus order : %d \n", __FUNCTION__, __LINE__, (*focusIDIter).first);
492 ret = DoSetCurrentFocusActor((*focusIDIter).second);
496 DALI_LOG_ERROR("[%s] %s\n", __FUNCTION__, ret?"SUCCEED!!!":"FAILED!!!");
501 void AccessibilityManager::DoActivate(Actor actor)
505 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
508 // Notify the control that it is activated
509 GetImplementation( control ).AccessibilityActivate();
512 // Send notification for the activation of focused actor
513 mFocusedActorActivatedSignal.Emit(actor);
517 void AccessibilityManager::ClearFocus()
519 Actor actor = GetCurrentFocusActor();
520 if( actor && mFocusIndicatorActor )
522 actor.Remove( mFocusIndicatorActor );
525 mCurrentFocusActor = FocusIDPair(0, 0);
527 // Send notification for the change of focus actor
528 mFocusChangedSignal.Emit(actor, Actor());
530 if(mIsAccessibilityTtsEnabled)
532 // Stop the TTS playing if any
533 Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
538 void AccessibilityManager::Reset()
541 mFocusIDContainer.clear();
542 mIDAdditionalInfoContainer.clear();
545 void AccessibilityManager::SetFocusGroup(Actor actor, bool isFocusGroup)
549 // Create/Set focus group property.
550 actor.RegisterProperty( IS_FOCUS_GROUP, isFocusGroup, Property::READ_WRITE );
554 bool AccessibilityManager::IsFocusGroup(Actor actor) const
556 // Check whether the actor is a focus group
557 bool isFocusGroup = false;
561 Property::Index propertyIsFocusGroup = actor.GetPropertyIndex(IS_FOCUS_GROUP);
562 if(propertyIsFocusGroup != Property::INVALID_INDEX)
564 isFocusGroup = actor.GetProperty<bool>(propertyIsFocusGroup);
571 Actor AccessibilityManager::GetFocusGroup(Actor actor)
573 // Go through the actor's hierarchy to check which focus group the actor belongs to
574 while (actor && !IsFocusGroup(actor))
576 actor = actor.GetParent();
582 Vector2 AccessibilityManager::GetReadPosition() const
584 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
585 return adaptor.GetReadPosition();
588 void AccessibilityManager::EnableAccessibility(bool enabled)
590 DALI_LOG_ERROR("[%s:%d] Set Enabled Forcibly : %d\n", __FUNCTION__, __LINE__, enabled);
591 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
592 adaptor.EnableAccessibility(enabled);
595 bool AccessibilityManager::IsEnabled() const
597 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
598 return adaptor.IsEnabled();
601 void AccessibilityManager::SetGroupMode(bool enabled)
603 mIsFocusWithinGroup = enabled;
606 bool AccessibilityManager::GetGroupMode() const
608 return mIsFocusWithinGroup;
611 void AccessibilityManager::SetWrapMode(bool wrapped)
613 mIsWrapped = wrapped;
616 bool AccessibilityManager::GetWrapMode() const
621 void AccessibilityManager::SetFocusIndicatorActor(Actor indicator)
623 if( mFocusIndicatorActor != indicator )
625 Actor currentFocusActor = GetCurrentFocusActor();
626 if( currentFocusActor )
628 // The new focus indicator should be added to the current focused actor immediately
629 if( mFocusIndicatorActor )
631 currentFocusActor.Remove( mFocusIndicatorActor );
636 currentFocusActor.Add( indicator );
640 mFocusIndicatorActor = indicator;
644 Actor AccessibilityManager::GetFocusIndicatorActor()
646 if( ! mFocusIndicatorActor )
648 // Create the default if it hasn't been set and one that's shared by all the keyboard focusable actors
649 const std::string imageDirPath = AssetManager::GetDaliImagePath();
650 const std::string focusBorderImagePath = imageDirPath + FOCUS_BORDER_IMAGE_FILE_NAME;
652 mFocusIndicatorActor = Toolkit::ImageView::New(focusBorderImagePath);
653 mFocusIndicatorActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
654 mFocusIndicatorActor.SetProperty( Actor::Property::POSITION_Z, 1.0f );
656 // Apply size constraint to the focus indicator
657 mFocusIndicatorActor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
660 return mFocusIndicatorActor;
663 bool AccessibilityManager::DoMoveFocus(FocusIDIter focusIDIter, bool forward, bool wrapped)
665 DALI_LOG_ERROR("[%s:%d] %d focusable actors\n", __FUNCTION__, __LINE__, mFocusIDContainer.size());
666 DALI_LOG_ERROR("[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
668 if( (forward && ++focusIDIter == mFocusIDContainer.end())
669 || (!forward && focusIDIter-- == mFocusIDContainer.begin()) )
671 if(mIsEndcapFeedbackEnabled)
673 if(mIsEndcapFeedbackPlayed == false)
675 // play sound & skip moving once
676 Dali::SoundPlayer soundPlayer = Dali::SoundPlayer::Get();
679 if (!mIsFocusChainEndSoundFilePathSet)
681 const std::string soundDirPath = AssetManager::GetDaliSoundPath();
682 mFocusChainEndSoundFilePath = soundDirPath + FOCUS_CHAIN_END_SOUND_FILE_NAME;
683 mIsFocusChainEndSoundFilePathSet = true;
685 soundPlayer.PlaySound(mFocusChainEndSoundFilePath);
688 mIsEndcapFeedbackPlayed = true;
691 mIsEndcapFeedbackPlayed = false;
698 focusIDIter = mFocusIDContainer.begin();
702 focusIDIter = mFocusIDContainer.end();
703 --focusIDIter;//We want forward iterator to the last element here
708 DALI_LOG_ERROR("[%s:%d] Overshot\n", __FUNCTION__, __LINE__);
709 // Send notification for handling overshooted situation
710 mFocusOvershotSignal.Emit(GetCurrentFocusActor(), forward ? Toolkit::AccessibilityManager::OVERSHOT_NEXT : Toolkit::AccessibilityManager::OVERSHOT_PREVIOUS);
712 return false; // Try to move the focus out of the scope
717 if( focusIDIter == mFocusIDContainer.end() )
722 // Note: This function performs the focus change.
723 if( !DoSetCurrentFocusActor( (*focusIDIter).second ) )
725 mRecursiveFocusMoveCounter++;
726 if(mRecursiveFocusMoveCounter > mFocusIDContainer.size())
728 // We've attempted to focus all the actors in the whole focus chain and no actor
729 // can be focused successfully.
730 DALI_LOG_WARNING("[%s] There is no more focusable actor in %d focus chains\n", __FUNCTION__, mRecursiveFocusMoveCounter);
736 return DoMoveFocus(focusIDIter, forward, wrapped);
743 void AccessibilityManager::SetFocusable(Actor actor, bool focusable)
747 // Create/Set actor focusable property.
748 actor.RegisterProperty( ACTOR_FOCUSABLE, focusable, Property::READ_WRITE );
752 bool AccessibilityManager::ChangeAccessibilityStatus()
754 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
755 mIsAccessibilityTtsEnabled = adaptor.IsEnabled();
756 Dali::Toolkit::AccessibilityManager handle( this );
758 DALI_LOG_ERROR("[%s:%d] TtsEnabled : %d \n", __FUNCTION__, __LINE__, mIsAccessibilityTtsEnabled);
760 if(mIsAccessibilityTtsEnabled)
762 // Show indicator when tts turned on if there is focused actor.
763 Actor actor = GetCurrentFocusActor();
766 actor.Add( GetFocusIndicatorActor() );
768 mIsFocusIndicatorEnabled = true;
770 // Connect a signal to the TTS player to implement continuous reading mode.
771 Dali::TtsPlayer player = Dali::TtsPlayer::Get( Dali::TtsPlayer::SCREEN_READER );
772 player.StateChangedSignal().Connect( this, &AccessibilityManager::TtsStateChanged );
777 // Hide indicator when tts turned off
778 Actor actor = GetCurrentFocusActor();
779 if( actor && mFocusIndicatorActor )
781 actor.Remove( mFocusIndicatorActor );
783 mIsFocusIndicatorEnabled = false;
787 // Disconnect the TTS state change signal.
788 Dali::TtsPlayer player = Dali::TtsPlayer::Get( Dali::TtsPlayer::SCREEN_READER );
789 player.StateChangedSignal().Disconnect( this, &AccessibilityManager::TtsStateChanged );
794 mStatusChangedSignal.Emit( handle );
799 bool AccessibilityManager::AccessibilityActionNext(bool allowEndFeedback)
801 Dali::Toolkit::AccessibilityManager handle( this );
802 if( !mActionNextSignal.Empty() )
804 mActionNextSignal.Emit( handle );
807 if(mIsAccessibilityTtsEnabled)
809 mIsEndcapFeedbackEnabled = allowEndFeedback;
810 return MoveFocusForward();
818 bool AccessibilityManager::AccessibilityActionPrevious(bool allowEndFeedback)
820 Dali::Toolkit::AccessibilityManager handle( this );
821 if( !mActionPreviousSignal.Empty() )
823 mActionPreviousSignal.Emit( handle );
826 if(mIsAccessibilityTtsEnabled)
828 mIsEndcapFeedbackEnabled = allowEndFeedback;
829 return MoveFocusBackward();
837 bool AccessibilityManager::AccessibilityActionActivate()
839 Dali::Toolkit::AccessibilityManager handle( this );
840 if( !mActionActivateSignal.Empty() )
842 mActionActivateSignal.Emit( handle );
847 Actor actor = GetCurrentFocusActor();
857 bool AccessibilityManager::AccessibilityActionRead(bool allowReadAgain)
859 Dali::Toolkit::AccessibilityManager handle(this);
863 if (!mActionReadSignal.Empty())
865 mActionReadSignal.Emit(handle);
870 if (!mActionOverSignal.Empty())
872 mActionOverSignal.Emit(handle);
878 if (mIsAccessibilityTtsEnabled)
880 // Find the focusable actor at the read position
881 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
882 Dali::HitTestAlgorithm::Results results;
883 Dali::HitTestAlgorithm::HitTest(Stage::GetCurrent(), adaptor.GetReadPosition(), results, IsActorFocusableFunction);
885 FocusIDIter focusIDIter = mFocusIDContainer.find(GetFocusOrder(results.actor));
886 if (focusIDIter != mFocusIDContainer.end())
888 if (allowReadAgain || (results.actor != GetCurrentFocusActor()))
890 // Move the focus to the actor
891 ret = SetCurrentFocusActor(results.actor);
892 DALI_LOG_ERROR("[%s:%d] SetCurrentFocusActor returns %s\n", __FUNCTION__, __LINE__, ret?"TRUE":"FALSE");
900 bool AccessibilityManager::AccessibilityActionReadNext(bool allowEndFeedback)
902 Dali::Toolkit::AccessibilityManager handle( this );
903 if( !mActionReadNextSignal.Empty() )
905 mActionReadNextSignal.Emit( handle );
908 if(mIsAccessibilityTtsEnabled)
910 return MoveFocusForward();
918 bool AccessibilityManager::AccessibilityActionReadPrevious(bool allowEndFeedback)
920 Dali::Toolkit::AccessibilityManager handle( this );
921 if( !mActionReadPreviousSignal.Empty() )
923 mActionReadPreviousSignal.Emit( handle );
926 if(mIsAccessibilityTtsEnabled)
928 return MoveFocusBackward();
936 bool AccessibilityManager::AccessibilityActionUp()
938 Dali::Toolkit::AccessibilityManager handle( this );
939 if( !mActionUpSignal.Empty() )
941 mActionUpSignal.Emit( handle );
946 if(mIsAccessibilityTtsEnabled)
948 Actor actor = GetCurrentFocusActor();
951 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
954 // Notify the control that it is activated
955 ret = GetImplementation( control ).OnAccessibilityValueChange(true);
963 bool AccessibilityManager::AccessibilityActionDown()
965 Dali::Toolkit::AccessibilityManager handle( this );
966 if( !mActionDownSignal.Empty() )
968 mActionDownSignal.Emit( handle );
973 if(mIsAccessibilityTtsEnabled)
975 Actor actor = GetCurrentFocusActor();
978 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
981 // Notify the control that it is activated
982 ret = GetImplementation( control ).OnAccessibilityValueChange(false);
990 bool AccessibilityManager::ClearAccessibilityFocus()
992 Dali::Toolkit::AccessibilityManager handle( this );
993 if( !mActionClearFocusSignal.Empty() )
995 mActionClearFocusSignal.Emit( handle );
998 if(mIsAccessibilityTtsEnabled)
1009 bool AccessibilityManager::AccessibilityActionScroll( Dali::TouchEvent& touch )
1011 Dali::Toolkit::AccessibilityManager handle( this );
1012 if( !mActionScrollSignal.Empty() )
1014 mActionScrollSignal.Emit( handle, touch );
1020 bool AccessibilityManager::AccessibilityActionBack()
1022 Dali::Toolkit::AccessibilityManager handle( this );
1023 if( !mActionBackSignal.Empty() )
1025 mActionBackSignal.Emit( handle );
1028 // TODO: Back to previous view
1030 return mIsAccessibilityTtsEnabled;
1033 bool AccessibilityManager::AccessibilityActionScrollUp()
1035 Dali::Toolkit::AccessibilityManager handle( this );
1036 if( !mActionScrollUpSignal.Empty() )
1038 mActionScrollUpSignal.Emit( handle );
1043 if(mIsAccessibilityTtsEnabled)
1045 Actor actor = GetCurrentFocusActor();
1048 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1051 // TODO: Notify the control to scroll up. Should control handle this?
1052 // ret = GetImplementation( control ).OnAccessibilityScroll(Direction::UP);
1060 bool AccessibilityManager::AccessibilityActionScrollDown()
1062 Dali::Toolkit::AccessibilityManager handle( this );
1063 if( !mActionScrollDownSignal.Empty() )
1065 mActionScrollDownSignal.Emit( handle );
1070 if(mIsAccessibilityTtsEnabled)
1072 Actor actor = GetCurrentFocusActor();
1075 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1078 // TODO: Notify the control to scroll down. Should control handle this?
1079 // ret = GetImplementation( control ).OnAccessibilityScrollDown(Direction::DOWN);
1087 bool AccessibilityManager::AccessibilityActionPageLeft()
1089 Dali::Toolkit::AccessibilityManager handle( this );
1090 if( !mActionPageLeftSignal.Empty() )
1092 mActionPageLeftSignal.Emit( handle );
1097 if(mIsAccessibilityTtsEnabled)
1099 Actor actor = GetCurrentFocusActor();
1102 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1105 // TODO: Notify the control to scroll left to the previous page. Should control handle this?
1106 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::LEFT);
1114 bool AccessibilityManager::AccessibilityActionPageRight()
1116 Dali::Toolkit::AccessibilityManager handle( this );
1117 if( !mActionPageRightSignal.Empty() )
1119 mActionPageRightSignal.Emit( handle );
1124 if(mIsAccessibilityTtsEnabled)
1126 Actor actor = GetCurrentFocusActor();
1129 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1132 // TODO: Notify the control to scroll right to the next page. Should control handle this?
1133 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::RIGHT);
1141 bool AccessibilityManager::AccessibilityActionPageUp()
1143 Dali::Toolkit::AccessibilityManager handle( this );
1144 if( !mActionPageUpSignal.Empty() )
1146 mActionPageUpSignal.Emit( handle );
1151 if(mIsAccessibilityTtsEnabled)
1153 Actor actor = GetCurrentFocusActor();
1156 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1159 // TODO: Notify the control to scroll up to the previous page. Should control handle this?
1160 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::UP);
1168 bool AccessibilityManager::AccessibilityActionPageDown()
1170 Dali::Toolkit::AccessibilityManager handle( this );
1171 if( !mActionPageDownSignal.Empty() )
1173 mActionPageDownSignal.Emit( handle );
1178 if(mIsAccessibilityTtsEnabled)
1180 Actor actor = GetCurrentFocusActor();
1183 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1186 // TODO: Notify the control to scroll down to the next page. Should control handle this?
1187 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::DOWN);
1195 bool AccessibilityManager::AccessibilityActionMoveToFirst()
1197 Dali::Toolkit::AccessibilityManager handle( this );
1198 if( !mActionMoveToFirstSignal.Empty() )
1200 mActionMoveToFirstSignal.Emit( handle );
1203 // TODO: Move to the first item on screen
1205 return mIsAccessibilityTtsEnabled;
1208 bool AccessibilityManager::AccessibilityActionMoveToLast()
1210 Dali::Toolkit::AccessibilityManager handle( this );
1211 if( !mActionMoveToLastSignal.Empty() )
1213 mActionMoveToLastSignal.Emit( handle );
1216 // TODO: Move to the last item on screen
1218 return mIsAccessibilityTtsEnabled;
1221 bool AccessibilityManager::AccessibilityActionReadFromTop()
1223 Dali::Toolkit::AccessibilityManager handle( this );
1224 if( !mActionReadFromTopSignal.Empty() )
1226 mActionReadFromTopSignal.Emit( handle );
1229 // TODO: Move to the top item on screen and read from the item continuously
1231 return mIsAccessibilityTtsEnabled;
1234 bool AccessibilityManager::AccessibilityActionReadFromNext()
1236 Dali::Toolkit::AccessibilityManager handle( this );
1238 if( !mActionReadFromNextSignal.Empty() )
1240 mActionReadFromNextSignal.Emit( handle );
1243 if( mIsAccessibilityTtsEnabled )
1245 // Mark that we are in continuous play mode, so TTS signals can move focus.
1246 mContinuousPlayMode = true;
1248 // Attempt to move to the next item and read from the item continuously.
1252 return mIsAccessibilityTtsEnabled;
1255 void AccessibilityManager::TtsStateChanged( const Dali::TtsPlayer::State previousState, const Dali::TtsPlayer::State currentState )
1257 if( mContinuousPlayMode )
1259 // If we were playing and now we have stopped, attempt to play the next item.
1260 if( ( previousState == Dali::TtsPlayer::PLAYING ) && ( currentState == Dali::TtsPlayer::READY ) )
1262 // Attempt to move the focus forward and play.
1263 // If we can't cancel continuous play mode.
1264 if( !MoveFocusForward() )
1266 // We are done, exit continuous play mode.
1267 mContinuousPlayMode = false;
1272 // Unexpected play state change, exit continuous play mode.
1273 mContinuousPlayMode = false;
1278 bool AccessibilityManager::AccessibilityActionZoom()
1280 Dali::Toolkit::AccessibilityManager handle( this );
1281 if( !mActionZoomSignal.Empty() )
1283 mActionZoomSignal.Emit( handle );
1288 if(mIsAccessibilityTtsEnabled)
1290 Actor actor = GetCurrentFocusActor();
1293 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1296 // Notify the control to zoom
1297 ret = GetImplementation( control ).OnAccessibilityZoom();
1305 bool AccessibilityManager::AccessibilityActionReadPauseResume()
1307 Dali::Toolkit::AccessibilityManager handle( this );
1308 if( !mActionReadPauseResumeSignal.Empty() )
1310 mActionReadPauseResumeSignal.Emit( handle );
1315 if(mIsAccessibilityTtsEnabled)
1317 // Pause or resume the TTS player
1318 Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
1319 Dali::TtsPlayer::State state = player.GetState();
1320 if(state == Dali::TtsPlayer::PLAYING)
1325 else if(state == Dali::TtsPlayer::PAUSED)
1335 bool AccessibilityManager::AccessibilityActionStartStop()
1337 Dali::Toolkit::AccessibilityManager handle( this );
1338 if( !mActionStartStopSignal.Empty() )
1340 mActionStartStopSignal.Emit( handle );
1343 // TODO: Start/stop the current action
1345 return mIsAccessibilityTtsEnabled;
1348 bool AccessibilityManager::AccessibilityActionForwardToApp()
1350 Dali::Toolkit::AccessibilityManager handle( this );
1351 if( !mActionForwardSignal.Empty() )
1353 mActionForwardSignal.Emit( handle );
1356 return mIsAccessibilityTtsEnabled;
1359 bool AccessibilityManager::HandlePanGesture(const AccessibilityGestureEvent& panEvent)
1361 bool handled = false;
1363 if( panEvent.state == AccessibilityGestureEvent::STARTED )
1365 // Find the focusable actor at the event position
1366 Dali::HitTestAlgorithm::Results results;
1367 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
1369 Dali::HitTestAlgorithm::HitTest( Stage::GetCurrent(), panEvent.currentPosition, results, IsActorFocusableFunction );
1370 mCurrentGesturedActor = results.actor;
1372 if(!mCurrentGesturedActor)
1374 DALI_LOG_ERROR("Gesture detected, but no hit actor\n");
1378 // GestureState::FINISHED (Up) events are delivered with previous (Motion) event position
1379 // Use the real previous position; otherwise we may incorrectly get a ZERO velocity
1380 if ( AccessibilityGestureEvent::FINISHED != panEvent.state )
1382 // Store the previous position for next GestureState::FINISHED iteration.
1383 mPreviousPosition = panEvent.previousPosition;
1386 Actor rootActor = Stage::GetCurrent().GetRootLayer();
1388 Dali::PanGesture pan = DevelPanGesture::New( static_cast<Dali::GestureState>(panEvent.state) );
1389 DevelPanGesture::SetTime( pan, panEvent.time );
1390 DevelPanGesture::SetNumberOfTouches( pan, panEvent.numberOfTouches );
1391 DevelPanGesture::SetScreenPosition( pan, panEvent.currentPosition );
1392 DevelPanGesture::SetScreenDisplacement( pan, mPreviousPosition - panEvent.currentPosition );
1393 // Avoid dividing by 0
1394 if(panEvent.timeDelta > 0)
1396 DevelPanGesture::SetScreenVelocity( pan, Vector2( pan.GetScreenDisplacement().x / panEvent.timeDelta, pan.GetScreenDisplacement().y / panEvent.timeDelta ) );
1399 // Only handle the pan gesture when the current focused actor is scrollable or within a scrollable actor
1400 while(mCurrentGesturedActor && mCurrentGesturedActor != rootActor && !handled)
1402 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(mCurrentGesturedActor);
1405 Vector2 localCurrent;
1406 control.ScreenToLocal( localCurrent.x, localCurrent.y, panEvent.currentPosition.x, panEvent.currentPosition.y );
1407 DevelPanGesture::SetPosition( pan, localCurrent );
1409 Vector2 localPrevious;
1410 control.ScreenToLocal( localPrevious.x, localPrevious.y, mPreviousPosition.x, mPreviousPosition.y );
1412 DevelPanGesture::SetDisplacement( pan, localCurrent - localPrevious );
1413 // Avoid dividing by 0
1414 if(panEvent.timeDelta > 0)
1416 DevelPanGesture::SetVelocity( pan, Vector2( pan.GetDisplacement().x / panEvent.timeDelta, pan.GetDisplacement().y / panEvent.timeDelta ));
1419 handled = GetImplementation( control ).OnAccessibilityPan(pan);
1422 // If the gesture is not handled by the control, check its parent
1425 mCurrentGesturedActor = mCurrentGesturedActor.GetParent();
1427 if(!mCurrentGesturedActor)
1429 DALI_LOG_ERROR("no more gestured actor\n");
1434 // If handled, then update the pan gesture properties
1435 PanGestureDetector::SetPanGestureProperties( pan );
1442 Toolkit::AccessibilityManager::FocusChangedSignalType& AccessibilityManager::FocusChangedSignal()
1444 return mFocusChangedSignal;
1447 Toolkit::AccessibilityManager::FocusOvershotSignalType& AccessibilityManager::FocusOvershotSignal()
1449 return mFocusOvershotSignal;
1452 Toolkit::AccessibilityManager::FocusedActorActivatedSignalType& AccessibilityManager::FocusedActorActivatedSignal()
1454 return mFocusedActorActivatedSignal;
1457 } // namespace Internal
1459 } // namespace Toolkit