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 std::string AccessibilityManager::GetAccessibilityAttribute(Actor actor, Toolkit::AccessibilityManager::AccessibilityAttribute type) const
184 ActorAdditionalInfo data = GetActorAdditionalInfo(actor.GetProperty< int >( Actor::Property::ID ));
185 text = data.mAccessibilityAttributes[type];
191 void AccessibilityManager::SetFocusOrder(Actor actor, const unsigned int order)
193 // Do nothing if the focus order of the actor is not changed.
194 if(actor && GetFocusOrder(actor) != order)
196 // Firstly delete the actor from the focus chain if it's already there with a different focus order.
197 mFocusIDContainer.erase(GetFocusOrder(actor));
199 // Create/retrieve actor focusable property
200 Property::Index propertyActorFocusable = actor.RegisterProperty( ACTOR_FOCUSABLE, true, Property::READ_WRITE );
204 // The actor is not focusable without a defined focus order.
205 actor.SetProperty(propertyActorFocusable, false);
207 // If the actor is currently being focused, it should clear the focus
208 if(actor == GetCurrentFocusActor())
213 else // Insert the actor to the focus chain
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())
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));
225 // Update the actor's focus order in its additional data
226 SynchronizeActorAdditionalInfo((*lastIter).second, (*lastIter).first + 1);
228 for(FocusIDIter iter = lastIter; iter != focusIDIter; iter--)
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;
235 // Update the actor's focus order in its additional data
236 SynchronizeActorAdditionalInfo(actorID, (*iter).first);
239 mFocusIDContainer.erase(order);
242 // The actor is focusable
243 actor.SetProperty(propertyActorFocusable, true);
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 )));
249 // Update the actor's focus order in its additional data
250 SynchronizeActorAdditionalInfo(actor.GetProperty< int >( Actor::Property::ID ), order);
254 unsigned int AccessibilityManager::GetFocusOrder(Actor actor) const
256 unsigned int focusOrder = 0;
260 ActorAdditionalInfo data = GetActorAdditionalInfo(actor.GetProperty< int >( Actor::Property::ID ));
261 focusOrder = data.mFocusOrder;
267 unsigned int AccessibilityManager::GenerateNewFocusOrder() const
269 unsigned int order = 1;
270 FocusIDContainer::const_reverse_iterator iter = mFocusIDContainer.rbegin();
272 if(iter != mFocusIDContainer.rend())
274 order = (*iter).first + 1;
280 Actor AccessibilityManager::GetActorByFocusOrder(const unsigned int order)
282 Actor actor = Actor();
284 FocusIDIter focusIDIter = mFocusIDContainer.find(order);
285 if(focusIDIter != mFocusIDContainer.end())
287 Actor rootActor = Stage::GetCurrent().GetRootLayer();
288 actor = rootActor.FindChildById(mFocusIDContainer[order]);
294 bool AccessibilityManager::SetCurrentFocusActor(Actor actor)
298 return DoSetCurrentFocusActor(actor.GetProperty< int >( Actor::Property::ID ));
304 bool AccessibilityManager::DoSetCurrentFocusActor(const unsigned int actorID)
306 Actor rootActor = Stage::GetCurrent().GetRootLayer();
308 // If the group mode is enabled, check which focus group the current focused actor belongs to
310 if(mIsFocusWithinGroup)
312 focusGroup = GetFocusGroup(GetCurrentFocusActor());
317 focusGroup = rootActor;
320 Actor actor = focusGroup.FindChildById(actorID);
322 // Check whether the actor is in the stage
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)
330 actorFocusable = actor.GetProperty<bool>(propertyActorFocusable);
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)
338 actorVisible = parent.GetCurrentProperty< bool >( Actor::Property::VISIBLE );
339 parent = parent.GetParent();
342 // Check whether the actor is fully transparent
343 bool actorOpaque = actor.GetCurrentProperty< Vector4 >( Actor::Property::WORLD_COLOR ).a > 0.01f;
345 // Set the focus only when the actor is focusable and visible and not fully transparent
346 if(actorVisible && actorFocusable && actorOpaque)
348 // Draw the focus indicator upon the focused actor
349 if( mIsFocusIndicatorEnabled )
351 actor.Add( GetFocusIndicatorActor() );
354 // Send notification for the change of focus actor
355 mFocusChangedSignal.Emit( GetCurrentFocusActor(), actor );
357 // Save the current focused actor
358 mCurrentFocusActor = FocusIDPair(GetFocusOrder(actor), actorID);
360 if(mIsAccessibilityTtsEnabled)
362 Dali::SoundPlayer soundPlayer = Dali::SoundPlayer::Get();
365 if (!mIsFocusSoundFilePathSet)
367 const std::string soundDirPath = AssetManager::GetDaliSoundPath();
368 mFocusSoundFilePath = soundDirPath + FOCUS_SOUND_FILE_NAME;
369 mIsFocusSoundFilePathSet = true;
371 soundPlayer.PlaySound(mFocusSoundFilePath);
374 // Play the accessibility attributes with the TTS player.
375 Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
377 // Combine attribute texts to one text
378 std::string informationText;
379 for(int i = 0; i < Toolkit::AccessibilityManager::ACCESSIBILITY_ATTRIBUTE_NUM; i++)
381 if(!GetActorAdditionalInfo(actorID).mAccessibilityAttributes[i].empty())
385 informationText += ", "; // for space time between each information
387 informationText += GetActorAdditionalInfo(actorID).mAccessibilityAttributes[i];
390 player.Play(informationText);
397 DALI_LOG_WARNING("[%s:%d] FAILED\n", __FUNCTION__, __LINE__);
401 Actor AccessibilityManager::GetCurrentFocusActor()
403 Actor rootActor = Stage::GetCurrent().GetRootLayer();
404 return rootActor.FindChildById(mCurrentFocusActor.second);
407 Actor AccessibilityManager::GetCurrentFocusGroup()
409 return GetFocusGroup(GetCurrentFocusActor());
412 unsigned int AccessibilityManager::GetCurrentFocusOrder()
414 return mCurrentFocusActor.first;
417 bool AccessibilityManager::MoveFocusForward()
420 mRecursiveFocusMoveCounter = 0;
422 FocusIDIter focusIDIter = mFocusIDContainer.find(mCurrentFocusActor.first);
423 if(focusIDIter != mFocusIDContainer.end())
425 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
426 ret = DoMoveFocus(focusIDIter, true, mIsWrapped);
430 // TODO: if there is not focused actor, move first actor
431 if(!mFocusIDContainer.empty())
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);
440 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s] %s\n", __FUNCTION__, ret?"SUCCEED!!!":"FAILED!!!");
445 bool AccessibilityManager::MoveFocusBackward()
448 mRecursiveFocusMoveCounter = 0;
450 FocusIDIter focusIDIter = mFocusIDContainer.find(mCurrentFocusActor.first);
451 if(focusIDIter != mFocusIDContainer.end())
453 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
454 ret = DoMoveFocus(focusIDIter, false, mIsWrapped);
458 // TODO: if there is not focused actor, move last actor
459 if(!mFocusIDContainer.empty())
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);
469 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s] %s\n", __FUNCTION__, ret?"SUCCEED!!!":"FAILED!!!");
474 void AccessibilityManager::DoActivate(Actor actor)
478 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
481 // Notify the control that it is activated
482 GetImplementation( control ).AccessibilityActivate();
485 // Send notification for the activation of focused actor
486 mFocusedActorActivatedSignal.Emit(actor);
490 void AccessibilityManager::ClearFocus()
492 Actor actor = GetCurrentFocusActor();
493 if( actor && mFocusIndicatorActor )
495 actor.Remove( mFocusIndicatorActor );
498 mCurrentFocusActor = FocusIDPair(0, 0);
500 // Send notification for the change of focus actor
501 mFocusChangedSignal.Emit(actor, Actor());
503 if(mIsAccessibilityTtsEnabled)
505 // Stop the TTS playing if any
506 Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
511 void AccessibilityManager::Reset()
514 mFocusIDContainer.clear();
515 mIDAdditionalInfoContainer.clear();
518 void AccessibilityManager::SetFocusGroup(Actor actor, bool isFocusGroup)
522 // Create/Set focus group property.
523 actor.RegisterProperty( IS_FOCUS_GROUP, isFocusGroup, Property::READ_WRITE );
527 bool AccessibilityManager::IsFocusGroup(Actor actor) const
529 // Check whether the actor is a focus group
530 bool isFocusGroup = false;
534 Property::Index propertyIsFocusGroup = actor.GetPropertyIndex(IS_FOCUS_GROUP);
535 if(propertyIsFocusGroup != Property::INVALID_INDEX)
537 isFocusGroup = actor.GetProperty<bool>(propertyIsFocusGroup);
544 Actor AccessibilityManager::GetFocusGroup(Actor actor)
546 // Go through the actor's hierarchy to check which focus group the actor belongs to
547 while (actor && !IsFocusGroup(actor))
549 actor = actor.GetParent();
555 Vector2 AccessibilityManager::GetReadPosition() const
557 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
558 return adaptor.GetReadPosition();
561 void AccessibilityManager::EnableAccessibility(bool enabled)
563 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] Set Enabled Forcibly : %d \n", __FUNCTION__, __LINE__, enabled );
564 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
565 adaptor.EnableAccessibility(enabled);
568 bool AccessibilityManager::IsEnabled() const
570 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
571 return adaptor.IsEnabled();
574 void AccessibilityManager::SetGroupMode(bool enabled)
576 mIsFocusWithinGroup = enabled;
579 bool AccessibilityManager::GetGroupMode() const
581 return mIsFocusWithinGroup;
584 void AccessibilityManager::SetWrapMode(bool wrapped)
586 mIsWrapped = wrapped;
589 bool AccessibilityManager::GetWrapMode() const
594 void AccessibilityManager::SetFocusIndicatorActor(Actor indicator)
596 if( mFocusIndicatorActor != indicator )
598 Actor currentFocusActor = GetCurrentFocusActor();
599 if( currentFocusActor )
601 // The new focus indicator should be added to the current focused actor immediately
602 if( mFocusIndicatorActor )
604 currentFocusActor.Remove( mFocusIndicatorActor );
609 currentFocusActor.Add( indicator );
613 mFocusIndicatorActor = indicator;
617 Actor AccessibilityManager::GetFocusIndicatorActor()
619 if( ! mFocusIndicatorActor )
621 // Create the default if it hasn't been set and one that's shared by all the keyboard focusable actors
622 const std::string imageDirPath = AssetManager::GetDaliImagePath();
623 const std::string focusBorderImagePath = imageDirPath + FOCUS_BORDER_IMAGE_FILE_NAME;
625 mFocusIndicatorActor = Toolkit::ImageView::New(focusBorderImagePath);
626 mFocusIndicatorActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
627 mFocusIndicatorActor.SetProperty( Actor::Property::POSITION_Z, 1.0f );
629 // Apply size constraint to the focus indicator
630 mFocusIndicatorActor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
633 return mFocusIndicatorActor;
636 bool AccessibilityManager::DoMoveFocus(FocusIDIter focusIDIter, bool forward, bool wrapped)
638 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] %d focusable actors\n", __FUNCTION__, __LINE__, mFocusIDContainer.size());
639 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
641 if( (forward && ++focusIDIter == mFocusIDContainer.end())
642 || (!forward && focusIDIter-- == mFocusIDContainer.begin()) )
644 if(mIsEndcapFeedbackEnabled)
646 if(mIsEndcapFeedbackPlayed == false)
648 // play sound & skip moving once
649 Dali::SoundPlayer soundPlayer = Dali::SoundPlayer::Get();
652 if (!mIsFocusChainEndSoundFilePathSet)
654 const std::string soundDirPath = AssetManager::GetDaliSoundPath();
655 mFocusChainEndSoundFilePath = soundDirPath + FOCUS_CHAIN_END_SOUND_FILE_NAME;
656 mIsFocusChainEndSoundFilePathSet = true;
658 soundPlayer.PlaySound(mFocusChainEndSoundFilePath);
661 mIsEndcapFeedbackPlayed = true;
664 mIsEndcapFeedbackPlayed = false;
671 focusIDIter = mFocusIDContainer.begin();
675 focusIDIter = mFocusIDContainer.end();
676 --focusIDIter;//We want forward iterator to the last element here
681 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] Overshot\n", __FUNCTION__, __LINE__);
682 // Send notification for handling overshooted situation
683 mFocusOvershotSignal.Emit(GetCurrentFocusActor(), forward ? Toolkit::AccessibilityManager::OVERSHOT_NEXT : Toolkit::AccessibilityManager::OVERSHOT_PREVIOUS);
685 return false; // Try to move the focus out of the scope
690 if( focusIDIter == mFocusIDContainer.end() )
695 // Note: This function performs the focus change.
696 if( !DoSetCurrentFocusActor( (*focusIDIter).second ) )
698 mRecursiveFocusMoveCounter++;
699 if(mRecursiveFocusMoveCounter > mFocusIDContainer.size())
701 // We've attempted to focus all the actors in the whole focus chain and no actor
702 // can be focused successfully.
703 DALI_LOG_WARNING("[%s] There is no more focusable actor in %d focus chains\n", __FUNCTION__, mRecursiveFocusMoveCounter);
709 return DoMoveFocus(focusIDIter, forward, wrapped);
716 void AccessibilityManager::SetFocusable(Actor actor, bool focusable)
720 // Create/Set actor focusable property.
721 actor.RegisterProperty( ACTOR_FOCUSABLE, focusable, Property::READ_WRITE );
725 bool AccessibilityManager::ChangeAccessibilityStatus()
727 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
728 mIsAccessibilityTtsEnabled = adaptor.IsEnabled();
729 Dali::Toolkit::AccessibilityManager handle( this );
731 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] TtsEnabled : %d \n", __FUNCTION__, __LINE__, mIsAccessibilityTtsEnabled );
733 if(mIsAccessibilityTtsEnabled)
735 // Show indicator when tts turned on if there is focused actor.
736 Actor actor = GetCurrentFocusActor();
739 actor.Add( GetFocusIndicatorActor() );
741 mIsFocusIndicatorEnabled = true;
743 // Connect a signal to the TTS player to implement continuous reading mode.
744 Dali::TtsPlayer player = Dali::TtsPlayer::Get( Dali::TtsPlayer::SCREEN_READER );
745 player.StateChangedSignal().Connect( this, &AccessibilityManager::TtsStateChanged );
750 // Hide indicator when tts turned off
751 Actor actor = GetCurrentFocusActor();
752 if( actor && mFocusIndicatorActor )
754 actor.Remove( mFocusIndicatorActor );
756 mIsFocusIndicatorEnabled = false;
760 // Disconnect the TTS state change signal.
761 Dali::TtsPlayer player = Dali::TtsPlayer::Get( Dali::TtsPlayer::SCREEN_READER );
762 player.StateChangedSignal().Disconnect( this, &AccessibilityManager::TtsStateChanged );
767 mStatusChangedSignal.Emit( handle );
772 bool AccessibilityManager::AccessibilityActionNext(bool allowEndFeedback)
774 Dali::Toolkit::AccessibilityManager handle( this );
775 if( !mActionNextSignal.Empty() )
777 mActionNextSignal.Emit( handle );
780 if(mIsAccessibilityTtsEnabled)
782 mIsEndcapFeedbackEnabled = allowEndFeedback;
783 return MoveFocusForward();
791 bool AccessibilityManager::AccessibilityActionPrevious(bool allowEndFeedback)
793 Dali::Toolkit::AccessibilityManager handle( this );
794 if( !mActionPreviousSignal.Empty() )
796 mActionPreviousSignal.Emit( handle );
799 if(mIsAccessibilityTtsEnabled)
801 mIsEndcapFeedbackEnabled = allowEndFeedback;
802 return MoveFocusBackward();
810 bool AccessibilityManager::AccessibilityActionActivate()
812 Dali::Toolkit::AccessibilityManager handle( this );
813 if( !mActionActivateSignal.Empty() )
815 mActionActivateSignal.Emit( handle );
820 Actor actor = GetCurrentFocusActor();
830 bool AccessibilityManager::AccessibilityActionRead(bool allowReadAgain)
832 Dali::Toolkit::AccessibilityManager handle( this );
836 if ( !mActionReadSignal.Empty() )
838 mActionReadSignal.Emit( handle );
843 if ( !mActionOverSignal.Empty() )
845 mActionOverSignal.Emit( handle );
851 if(mIsAccessibilityTtsEnabled)
853 // Find the focusable actor at the read position
854 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
855 Dali::HitTestAlgorithm::Results results;
856 Dali::HitTestAlgorithm::HitTest( Stage::GetCurrent(), adaptor.GetReadPosition(), results, IsActorFocusableFunction );
858 FocusIDIter focusIDIter = mFocusIDContainer.find(GetFocusOrder(results.actor));
859 if(focusIDIter != mFocusIDContainer.end())
861 if( allowReadAgain || (results.actor != GetCurrentFocusActor()) )
863 // Move the focus to the actor
864 ret = SetCurrentFocusActor(results.actor);
865 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] SetCurrentFocusActor returns %s\n", __FUNCTION__, __LINE__, ret?"TRUE":"FALSE");
873 bool AccessibilityManager::AccessibilityActionReadNext(bool allowEndFeedback)
875 Dali::Toolkit::AccessibilityManager handle( this );
876 if( !mActionReadNextSignal.Empty() )
878 mActionReadNextSignal.Emit( handle );
881 if(mIsAccessibilityTtsEnabled)
883 return MoveFocusForward();
891 bool AccessibilityManager::AccessibilityActionReadPrevious(bool allowEndFeedback)
893 Dali::Toolkit::AccessibilityManager handle( this );
894 if( !mActionReadPreviousSignal.Empty() )
896 mActionReadPreviousSignal.Emit( handle );
899 if(mIsAccessibilityTtsEnabled)
901 return MoveFocusBackward();
909 bool AccessibilityManager::AccessibilityActionUp()
911 Dali::Toolkit::AccessibilityManager handle( this );
912 if( !mActionUpSignal.Empty() )
914 mActionUpSignal.Emit( handle );
919 if(mIsAccessibilityTtsEnabled)
921 Actor actor = GetCurrentFocusActor();
924 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
927 // Notify the control that it is activated
928 ret = GetImplementation( control ).OnAccessibilityValueChange(true);
936 bool AccessibilityManager::AccessibilityActionDown()
938 Dali::Toolkit::AccessibilityManager handle( this );
939 if( !mActionDownSignal.Empty() )
941 mActionDownSignal.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(false);
963 bool AccessibilityManager::ClearAccessibilityFocus()
965 Dali::Toolkit::AccessibilityManager handle( this );
966 if( !mActionClearFocusSignal.Empty() )
968 mActionClearFocusSignal.Emit( handle );
971 if(mIsAccessibilityTtsEnabled)
982 bool AccessibilityManager::AccessibilityActionScroll( Dali::TouchEvent& touch )
984 Dali::Toolkit::AccessibilityManager handle( this );
985 if( !mActionScrollSignal.Empty() )
987 mActionScrollSignal.Emit( handle, touch );
993 bool AccessibilityManager::AccessibilityActionBack()
995 Dali::Toolkit::AccessibilityManager handle( this );
996 if( !mActionBackSignal.Empty() )
998 mActionBackSignal.Emit( handle );
1001 // TODO: Back to previous view
1003 return mIsAccessibilityTtsEnabled;
1006 bool AccessibilityManager::AccessibilityActionScrollUp()
1008 Dali::Toolkit::AccessibilityManager handle( this );
1009 if( !mActionScrollUpSignal.Empty() )
1011 mActionScrollUpSignal.Emit( handle );
1016 if(mIsAccessibilityTtsEnabled)
1018 Actor actor = GetCurrentFocusActor();
1021 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1024 // TODO: Notify the control to scroll up. Should control handle this?
1025 // ret = GetImplementation( control ).OnAccessibilityScroll(Direction::UP);
1033 bool AccessibilityManager::AccessibilityActionScrollDown()
1035 Dali::Toolkit::AccessibilityManager handle( this );
1036 if( !mActionScrollDownSignal.Empty() )
1038 mActionScrollDownSignal.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 down. Should control handle this?
1052 // ret = GetImplementation( control ).OnAccessibilityScrollDown(Direction::DOWN);
1060 bool AccessibilityManager::AccessibilityActionPageLeft()
1062 Dali::Toolkit::AccessibilityManager handle( this );
1063 if( !mActionPageLeftSignal.Empty() )
1065 mActionPageLeftSignal.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 left to the previous page. Should control handle this?
1079 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::LEFT);
1087 bool AccessibilityManager::AccessibilityActionPageRight()
1089 Dali::Toolkit::AccessibilityManager handle( this );
1090 if( !mActionPageRightSignal.Empty() )
1092 mActionPageRightSignal.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 right to the next page. Should control handle this?
1106 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::RIGHT);
1114 bool AccessibilityManager::AccessibilityActionPageUp()
1116 Dali::Toolkit::AccessibilityManager handle( this );
1117 if( !mActionPageUpSignal.Empty() )
1119 mActionPageUpSignal.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 up to the previous page. Should control handle this?
1133 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::UP);
1141 bool AccessibilityManager::AccessibilityActionPageDown()
1143 Dali::Toolkit::AccessibilityManager handle( this );
1144 if( !mActionPageDownSignal.Empty() )
1146 mActionPageDownSignal.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 down to the next page. Should control handle this?
1160 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::DOWN);
1168 bool AccessibilityManager::AccessibilityActionMoveToFirst()
1170 Dali::Toolkit::AccessibilityManager handle( this );
1171 if( !mActionMoveToFirstSignal.Empty() )
1173 mActionMoveToFirstSignal.Emit( handle );
1176 // TODO: Move to the first item on screen
1178 return mIsAccessibilityTtsEnabled;
1181 bool AccessibilityManager::AccessibilityActionMoveToLast()
1183 Dali::Toolkit::AccessibilityManager handle( this );
1184 if( !mActionMoveToLastSignal.Empty() )
1186 mActionMoveToLastSignal.Emit( handle );
1189 // TODO: Move to the last item on screen
1191 return mIsAccessibilityTtsEnabled;
1194 bool AccessibilityManager::AccessibilityActionReadFromTop()
1196 Dali::Toolkit::AccessibilityManager handle( this );
1197 if( !mActionReadFromTopSignal.Empty() )
1199 mActionReadFromTopSignal.Emit( handle );
1202 // TODO: Move to the top item on screen and read from the item continuously
1204 return mIsAccessibilityTtsEnabled;
1207 bool AccessibilityManager::AccessibilityActionReadFromNext()
1209 Dali::Toolkit::AccessibilityManager handle( this );
1211 if( !mActionReadFromNextSignal.Empty() )
1213 mActionReadFromNextSignal.Emit( handle );
1216 if( mIsAccessibilityTtsEnabled )
1218 // Mark that we are in continuous play mode, so TTS signals can move focus.
1219 mContinuousPlayMode = true;
1221 // Attempt to move to the next item and read from the item continuously.
1225 return mIsAccessibilityTtsEnabled;
1228 void AccessibilityManager::TtsStateChanged( const Dali::TtsPlayer::State previousState, const Dali::TtsPlayer::State currentState )
1230 if( mContinuousPlayMode )
1232 // If we were playing and now we have stopped, attempt to play the next item.
1233 if( ( previousState == Dali::TtsPlayer::PLAYING ) && ( currentState == Dali::TtsPlayer::READY ) )
1235 // Attempt to move the focus forward and play.
1236 // If we can't cancel continuous play mode.
1237 if( !MoveFocusForward() )
1239 // We are done, exit continuous play mode.
1240 mContinuousPlayMode = false;
1245 // Unexpected play state change, exit continuous play mode.
1246 mContinuousPlayMode = false;
1251 bool AccessibilityManager::AccessibilityActionZoom()
1253 Dali::Toolkit::AccessibilityManager handle( this );
1254 if( !mActionZoomSignal.Empty() )
1256 mActionZoomSignal.Emit( handle );
1261 if(mIsAccessibilityTtsEnabled)
1263 Actor actor = GetCurrentFocusActor();
1266 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1269 // Notify the control to zoom
1270 ret = GetImplementation( control ).OnAccessibilityZoom();
1278 bool AccessibilityManager::AccessibilityActionReadPauseResume()
1280 Dali::Toolkit::AccessibilityManager handle( this );
1281 if( !mActionReadPauseResumeSignal.Empty() )
1283 mActionReadPauseResumeSignal.Emit( handle );
1288 if(mIsAccessibilityTtsEnabled)
1290 // Pause or resume the TTS player
1291 Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
1292 Dali::TtsPlayer::State state = player.GetState();
1293 if(state == Dali::TtsPlayer::PLAYING)
1298 else if(state == Dali::TtsPlayer::PAUSED)
1308 bool AccessibilityManager::AccessibilityActionStartStop()
1310 Dali::Toolkit::AccessibilityManager handle( this );
1311 if( !mActionStartStopSignal.Empty() )
1313 mActionStartStopSignal.Emit( handle );
1316 // TODO: Start/stop the current action
1318 return mIsAccessibilityTtsEnabled;
1321 bool AccessibilityManager::HandlePanGesture(const AccessibilityGestureEvent& panEvent)
1323 bool handled = false;
1325 if( panEvent.state == AccessibilityGestureEvent::STARTED )
1327 // Find the focusable actor at the event position
1328 Dali::HitTestAlgorithm::Results results;
1329 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
1331 Dali::HitTestAlgorithm::HitTest( Stage::GetCurrent(), panEvent.currentPosition, results, IsActorFocusableFunction );
1332 mCurrentGesturedActor = results.actor;
1334 if(!mCurrentGesturedActor)
1336 DALI_LOG_ERROR("Gesture detected, but no hit actor\n");
1340 // GestureState::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 )
1344 // Store the previous position for next GestureState::FINISHED iteration.
1345 mPreviousPosition = panEvent.previousPosition;
1348 Actor rootActor = Stage::GetCurrent().GetRootLayer();
1350 Dali::PanGesture pan = DevelPanGesture::New( static_cast<Dali::GestureState>(panEvent.state) );
1351 DevelPanGesture::SetTime( pan, panEvent.time );
1352 DevelPanGesture::SetNumberOfTouches( pan, panEvent.numberOfTouches );
1353 DevelPanGesture::SetScreenPosition( pan, panEvent.currentPosition );
1354 DevelPanGesture::SetScreenDisplacement( pan, mPreviousPosition - panEvent.currentPosition );
1355 DevelPanGesture::SetScreenVelocity( pan, Vector2( pan.GetScreenDisplacement().x / panEvent.timeDelta, pan.GetScreenDisplacement().y / panEvent.timeDelta ) );
1357 // Only handle the pan gesture when the current focused actor is scrollable or within a scrollable actor
1358 while(mCurrentGesturedActor && mCurrentGesturedActor != rootActor && !handled)
1360 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(mCurrentGesturedActor);
1363 Vector2 localCurrent;
1364 control.ScreenToLocal( localCurrent.x, localCurrent.y, panEvent.currentPosition.x, panEvent.currentPosition.y );
1365 DevelPanGesture::SetPosition( pan, localCurrent );
1367 Vector2 localPrevious;
1368 control.ScreenToLocal( localPrevious.x, localPrevious.y, mPreviousPosition.x, mPreviousPosition.y );
1370 DevelPanGesture::SetDisplacement( pan, localCurrent - localPrevious );
1371 DevelPanGesture::SetVelocity( pan, Vector2( pan.GetDisplacement().x / panEvent.timeDelta, pan.GetDisplacement().y / panEvent.timeDelta ));
1373 handled = GetImplementation( control ).OnAccessibilityPan(pan);
1376 // If the gesture is not handled by the control, check its parent
1379 mCurrentGesturedActor = mCurrentGesturedActor.GetParent();
1381 if(!mCurrentGesturedActor)
1383 DALI_LOG_ERROR("no more gestured actor\n");
1388 // If handled, then update the pan gesture properties
1389 PanGestureDetector::SetPanGestureProperties( pan );
1396 Toolkit::AccessibilityManager::FocusChangedSignalType& AccessibilityManager::FocusChangedSignal()
1398 return mFocusChangedSignal;
1401 Toolkit::AccessibilityManager::FocusOvershotSignalType& AccessibilityManager::FocusOvershotSignal()
1403 return mFocusOvershotSignal;
1406 Toolkit::AccessibilityManager::FocusedActorActivatedSignalType& AccessibilityManager::FocusedActorActivatedSignal()
1408 return mFocusedActorActivatedSignal;
1411 } // namespace Internal
1413 } // namespace Toolkit