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/integration-api/debug.h>
31 #include <dali-toolkit/devel-api/asset-manager/asset-manager.h>
32 #include <dali-toolkit/public-api/controls/control.h>
33 #include <dali-toolkit/public-api/controls/control-impl.h>
34 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
45 namespace // unnamed namespace
50 const char* const SIGNAL_FOCUS_CHANGED = "focusChanged";
51 const char* const SIGNAL_FOCUS_OVERSHOT = "focusOvershot";
52 const char* const SIGNAL_FOCUSED_ACTOR_ACTIVATED = "focusedActorActivated";
54 #if defined(DEBUG_ENABLED)
55 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_FOCUS_MANAGER");
58 const char* const ACTOR_FOCUSABLE("focusable");
59 const char* const IS_FOCUS_GROUP("isFocusGroup");
61 const char* FOCUS_BORDER_IMAGE_FILE_NAME = "B16-8_TTS_focus.9.png";
63 const char* FOCUS_SOUND_FILE_NAME = "Focus.ogg";
64 const char* FOCUS_CHAIN_END_SOUND_FILE_NAME = "End_of_List.ogg";
67 * The function to be used in the hit-test algorithm to check whether the actor is hittable.
69 bool IsActorFocusableFunction(Actor actor, Dali::HitTestAlgorithm::TraverseType type)
71 bool hittable = false;
75 case Dali::HitTestAlgorithm::CHECK_ACTOR:
77 // Check whether the actor is visible and not fully transparent.
78 if( actor.GetCurrentProperty< bool >( Actor::Property::VISIBLE )
79 && actor.GetCurrentProperty< Vector4 >( Actor::Property::WORLD_COLOR ).a > 0.01f) // not FULLY_TRANSPARENT
81 // Check whether the actor is focusable
82 Property::Index propertyActorFocusable = actor.GetPropertyIndex(ACTOR_FOCUSABLE);
83 if(propertyActorFocusable != Property::INVALID_INDEX)
85 hittable = actor.GetProperty<bool>(propertyActorFocusable);
90 case Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE:
92 if( actor.GetCurrentProperty< bool >( Actor::Property::VISIBLE ) ) // Actor is visible, if not visible then none of its children are visible.
109 AccessibilityManager::AccessibilityManager()
110 : mCurrentFocusActor(FocusIDPair(0, 0)),
111 mCurrentGesturedActor(),
112 mFocusIndicatorActor(),
113 mPreviousPosition( 0.0f, 0.0f ),
114 mRecursiveFocusMoveCounter(0),
115 mFocusSoundFilePath(),
116 mFocusChainEndSoundFilePath(),
118 mIsFocusWithinGroup(false),
119 mIsEndcapFeedbackEnabled(false),
120 mIsEndcapFeedbackPlayed(false),
121 mIsAccessibilityTtsEnabled(false),
123 mIsFocusIndicatorEnabled(false),
124 mContinuousPlayMode(false),
125 mIsFocusSoundFilePathSet(false),
126 mIsFocusChainEndSoundFilePathSet(false)
130 AccessibilityManager::~AccessibilityManager()
134 void AccessibilityManager::Initialise()
136 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
137 adaptor.SetActionHandler(*this);
138 adaptor.SetGestureHandler(*this);
140 ChangeAccessibilityStatus();
143 AccessibilityManager::ActorAdditionalInfo AccessibilityManager::GetActorAdditionalInfo(const unsigned int actorID) const
145 ActorAdditionalInfo data;
146 IDAdditionalInfoConstIter iter = mIDAdditionalInfoContainer.find(actorID);
147 if(iter != mIDAdditionalInfoContainer.end())
149 data = (*iter).second;
155 void AccessibilityManager::SynchronizeActorAdditionalInfo(const unsigned int actorID, const unsigned int order)
157 ActorAdditionalInfo actorInfo = GetActorAdditionalInfo(actorID);
158 actorInfo.mFocusOrder = order;
159 mIDAdditionalInfoContainer.erase(actorID);
160 mIDAdditionalInfoContainer.insert(IDAdditionalInfoPair(actorID, actorInfo));
163 void AccessibilityManager::SetAccessibilityAttribute(Actor actor, Toolkit::AccessibilityManager::AccessibilityAttribute type, const std::string& text)
167 unsigned int actorID = actor.GetProperty< int >( Actor::Property::ID );
169 ActorAdditionalInfo info = GetActorAdditionalInfo(actorID);
170 info.mAccessibilityAttributes[type] = text;
172 mIDAdditionalInfoContainer.erase(actorID);
173 mIDAdditionalInfoContainer.insert(IDAdditionalInfoPair(actorID, info));
177 std::string AccessibilityManager::GetAccessibilityAttribute(Actor actor, Toolkit::AccessibilityManager::AccessibilityAttribute type) const
183 ActorAdditionalInfo data = GetActorAdditionalInfo(actor.GetProperty< int >( Actor::Property::ID ));
184 text = data.mAccessibilityAttributes[type];
190 void AccessibilityManager::SetFocusOrder(Actor actor, const unsigned int order)
192 // Do nothing if the focus order of the actor is not changed.
193 if(actor && GetFocusOrder(actor) != order)
195 // Firstly delete the actor from the focus chain if it's already there with a different focus order.
196 mFocusIDContainer.erase(GetFocusOrder(actor));
198 // Create/retrieve actor focusable property
199 Property::Index propertyActorFocusable = actor.RegisterProperty( ACTOR_FOCUSABLE, true, Property::READ_WRITE );
203 // The actor is not focusable without a defined focus order.
204 actor.SetProperty(propertyActorFocusable, false);
206 // If the actor is currently being focused, it should clear the focus
207 if(actor == GetCurrentFocusActor())
212 else // Insert the actor to the focus chain
214 // Check whether there is another actor in the focus chain with the same focus order already.
215 FocusIDIter focusIDIter = mFocusIDContainer.find(order);
216 if(focusIDIter != mFocusIDContainer.end())
218 // We need to increase the focus order of that actor and all the actors followed it
219 // in the focus chain.
220 FocusIDIter lastIter = mFocusIDContainer.end();
221 --lastIter;//We want forward iterator to the last element here
222 mFocusIDContainer.insert(FocusIDPair((*lastIter).first + 1, (*lastIter).second));
224 // Update the actor's focus order in its additional data
225 SynchronizeActorAdditionalInfo((*lastIter).second, (*lastIter).first + 1);
227 for(FocusIDIter iter = lastIter; iter != focusIDIter; iter--)
229 FocusIDIter previousIter = iter;
230 --previousIter;//We want forward iterator to the previous element here
231 unsigned int actorID = (*previousIter).second;
232 (*iter).second = actorID;
234 // Update the actor's focus order in its additional data
235 SynchronizeActorAdditionalInfo(actorID, (*iter).first);
238 mFocusIDContainer.erase(order);
241 // The actor is focusable
242 actor.SetProperty(propertyActorFocusable, true);
244 // Now we insert the actor into the focus chain with the specified focus order
245 mFocusIDContainer.insert(FocusIDPair(order, actor.GetProperty< int >( Actor::Property::ID )));
248 // Update the actor's focus order in its additional data
249 SynchronizeActorAdditionalInfo(actor.GetProperty< int >( Actor::Property::ID ), order);
253 unsigned int AccessibilityManager::GetFocusOrder(Actor actor) const
255 unsigned int focusOrder = 0;
259 ActorAdditionalInfo data = GetActorAdditionalInfo(actor.GetProperty< int >( Actor::Property::ID ));
260 focusOrder = data.mFocusOrder;
266 unsigned int AccessibilityManager::GenerateNewFocusOrder() const
268 unsigned int order = 1;
269 FocusIDContainer::const_reverse_iterator iter = mFocusIDContainer.rbegin();
271 if(iter != mFocusIDContainer.rend())
273 order = (*iter).first + 1;
279 Actor AccessibilityManager::GetActorByFocusOrder(const unsigned int order)
281 Actor actor = Actor();
283 FocusIDIter focusIDIter = mFocusIDContainer.find(order);
284 if(focusIDIter != mFocusIDContainer.end())
286 Actor rootActor = Stage::GetCurrent().GetRootLayer();
287 actor = rootActor.FindChildById(mFocusIDContainer[order]);
293 bool AccessibilityManager::SetCurrentFocusActor(Actor actor)
297 return DoSetCurrentFocusActor(actor.GetProperty< int >( Actor::Property::ID ));
303 bool AccessibilityManager::DoSetCurrentFocusActor(const unsigned int actorID)
305 Actor rootActor = Stage::GetCurrent().GetRootLayer();
307 // If the group mode is enabled, check which focus group the current focused actor belongs to
309 if(mIsFocusWithinGroup)
311 focusGroup = GetFocusGroup(GetCurrentFocusActor());
316 focusGroup = rootActor;
319 Actor actor = focusGroup.FindChildById(actorID);
321 // Check whether the actor is in the stage
324 // Check whether the actor is focusable
325 bool actorFocusable = false;
326 Property::Index propertyActorFocusable = actor.GetPropertyIndex(ACTOR_FOCUSABLE);
327 if(propertyActorFocusable != Property::INVALID_INDEX)
329 actorFocusable = actor.GetProperty<bool>(propertyActorFocusable);
332 // Go through the actor's hierarchy to check whether the actor is visible
333 bool actorVisible = actor.GetCurrentProperty< bool >( Actor::Property::VISIBLE );
334 Actor parent = actor.GetParent();
335 while (actorVisible && parent && parent != rootActor)
337 actorVisible = parent.GetCurrentProperty< bool >( Actor::Property::VISIBLE );
338 parent = parent.GetParent();
341 // Check whether the actor is fully transparent
342 bool actorOpaque = actor.GetCurrentProperty< Vector4 >( Actor::Property::WORLD_COLOR ).a > 0.01f;
344 // Set the focus only when the actor is focusable and visible and not fully transparent
345 if(actorVisible && actorFocusable && actorOpaque)
347 // Draw the focus indicator upon the focused actor
348 if( mIsFocusIndicatorEnabled )
350 actor.Add( GetFocusIndicatorActor() );
353 // Send notification for the change of focus actor
354 mFocusChangedSignal.Emit( GetCurrentFocusActor(), actor );
356 // Save the current focused actor
357 mCurrentFocusActor = FocusIDPair(GetFocusOrder(actor), actorID);
359 if(mIsAccessibilityTtsEnabled)
361 Dali::SoundPlayer soundPlayer = Dali::SoundPlayer::Get();
364 if (!mIsFocusSoundFilePathSet)
366 const std::string soundDirPath = AssetManager::GetDaliSoundPath();
367 mFocusSoundFilePath = soundDirPath + FOCUS_SOUND_FILE_NAME;
368 mIsFocusSoundFilePathSet = true;
370 soundPlayer.PlaySound(mFocusSoundFilePath);
373 // Play the accessibility attributes with the TTS player.
374 Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
376 // Combine attribute texts to one text
377 std::string informationText;
378 for(int i = 0; i < Toolkit::AccessibilityManager::ACCESSIBILITY_ATTRIBUTE_NUM; i++)
380 if(!GetActorAdditionalInfo(actorID).mAccessibilityAttributes[i].empty())
384 informationText += ", "; // for space time between each information
386 informationText += GetActorAdditionalInfo(actorID).mAccessibilityAttributes[i];
389 player.Play(informationText);
396 DALI_LOG_WARNING("[%s:%d] FAILED\n", __FUNCTION__, __LINE__);
400 Actor AccessibilityManager::GetCurrentFocusActor()
402 Actor rootActor = Stage::GetCurrent().GetRootLayer();
403 return rootActor.FindChildById(mCurrentFocusActor.second);
406 Actor AccessibilityManager::GetCurrentFocusGroup()
408 return GetFocusGroup(GetCurrentFocusActor());
411 unsigned int AccessibilityManager::GetCurrentFocusOrder()
413 return mCurrentFocusActor.first;
416 bool AccessibilityManager::MoveFocusForward()
419 mRecursiveFocusMoveCounter = 0;
421 FocusIDIter focusIDIter = mFocusIDContainer.find(mCurrentFocusActor.first);
422 if(focusIDIter != mFocusIDContainer.end())
424 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
425 ret = DoMoveFocus(focusIDIter, true, mIsWrapped);
429 // TODO: if there is not focused actor, move first actor
430 if(!mFocusIDContainer.empty())
432 //if there is not focused actor, move 1st actor
433 focusIDIter = mFocusIDContainer.begin(); // TODO: I'm not sure it was sorted.
434 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
435 ret = DoSetCurrentFocusActor((*focusIDIter).second);
439 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s] %s\n", __FUNCTION__, ret?"SUCCEED!!!":"FAILED!!!");
444 bool AccessibilityManager::MoveFocusBackward()
447 mRecursiveFocusMoveCounter = 0;
449 FocusIDIter focusIDIter = mFocusIDContainer.find(mCurrentFocusActor.first);
450 if(focusIDIter != mFocusIDContainer.end())
452 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
453 ret = DoMoveFocus(focusIDIter, false, mIsWrapped);
457 // TODO: if there is not focused actor, move last actor
458 if(!mFocusIDContainer.empty())
460 //if there is not focused actor, move last actor
461 focusIDIter = mFocusIDContainer.end();
462 --focusIDIter;//We want forward iterator to the last element here
463 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
464 ret = DoSetCurrentFocusActor((*focusIDIter).second);
468 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s] %s\n", __FUNCTION__, ret?"SUCCEED!!!":"FAILED!!!");
473 void AccessibilityManager::DoActivate(Actor actor)
477 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
480 // Notify the control that it is activated
481 GetImplementation( control ).AccessibilityActivate();
484 // Send notification for the activation of focused actor
485 mFocusedActorActivatedSignal.Emit(actor);
489 void AccessibilityManager::ClearFocus()
491 Actor actor = GetCurrentFocusActor();
492 if( actor && mFocusIndicatorActor )
494 actor.Remove( mFocusIndicatorActor );
497 mCurrentFocusActor = FocusIDPair(0, 0);
499 // Send notification for the change of focus actor
500 mFocusChangedSignal.Emit(actor, Actor());
502 if(mIsAccessibilityTtsEnabled)
504 // Stop the TTS playing if any
505 Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
510 void AccessibilityManager::Reset()
513 mFocusIDContainer.clear();
514 mIDAdditionalInfoContainer.clear();
517 void AccessibilityManager::SetFocusGroup(Actor actor, bool isFocusGroup)
521 // Create/Set focus group property.
522 actor.RegisterProperty( IS_FOCUS_GROUP, isFocusGroup, Property::READ_WRITE );
526 bool AccessibilityManager::IsFocusGroup(Actor actor) const
528 // Check whether the actor is a focus group
529 bool isFocusGroup = false;
533 Property::Index propertyIsFocusGroup = actor.GetPropertyIndex(IS_FOCUS_GROUP);
534 if(propertyIsFocusGroup != Property::INVALID_INDEX)
536 isFocusGroup = actor.GetProperty<bool>(propertyIsFocusGroup);
543 Actor AccessibilityManager::GetFocusGroup(Actor actor)
545 // Go through the actor's hierarchy to check which focus group the actor belongs to
546 while (actor && !IsFocusGroup(actor))
548 actor = actor.GetParent();
554 Vector2 AccessibilityManager::GetReadPosition() const
556 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
557 return adaptor.GetReadPosition();
560 void AccessibilityManager::SetGroupMode(bool enabled)
562 mIsFocusWithinGroup = enabled;
565 bool AccessibilityManager::GetGroupMode() const
567 return mIsFocusWithinGroup;
570 void AccessibilityManager::SetWrapMode(bool wrapped)
572 mIsWrapped = wrapped;
575 bool AccessibilityManager::GetWrapMode() const
580 void AccessibilityManager::SetFocusIndicatorActor(Actor indicator)
582 if( mFocusIndicatorActor != indicator )
584 Actor currentFocusActor = GetCurrentFocusActor();
585 if( currentFocusActor )
587 // The new focus indicator should be added to the current focused actor immediately
588 if( mFocusIndicatorActor )
590 currentFocusActor.Remove( mFocusIndicatorActor );
595 currentFocusActor.Add( indicator );
599 mFocusIndicatorActor = indicator;
603 Actor AccessibilityManager::GetFocusIndicatorActor()
605 if( ! mFocusIndicatorActor )
607 // Create the default if it hasn't been set and one that's shared by all the keyboard focusable actors
608 const std::string imageDirPath = AssetManager::GetDaliImagePath();
609 const std::string focusBorderImagePath = imageDirPath + FOCUS_BORDER_IMAGE_FILE_NAME;
611 mFocusIndicatorActor = Toolkit::ImageView::New(focusBorderImagePath);
612 mFocusIndicatorActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
613 mFocusIndicatorActor.SetProperty( Actor::Property::POSITION_Z, 1.0f );
615 // Apply size constraint to the focus indicator
616 mFocusIndicatorActor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
619 return mFocusIndicatorActor;
622 bool AccessibilityManager::DoMoveFocus(FocusIDIter focusIDIter, bool forward, bool wrapped)
624 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] %d focusable actors\n", __FUNCTION__, __LINE__, mFocusIDContainer.size());
625 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
627 if( (forward && ++focusIDIter == mFocusIDContainer.end())
628 || (!forward && focusIDIter-- == mFocusIDContainer.begin()) )
630 if(mIsEndcapFeedbackEnabled)
632 if(mIsEndcapFeedbackPlayed == false)
634 // play sound & skip moving once
635 Dali::SoundPlayer soundPlayer = Dali::SoundPlayer::Get();
638 if (!mIsFocusChainEndSoundFilePathSet)
640 const std::string soundDirPath = AssetManager::GetDaliSoundPath();
641 mFocusChainEndSoundFilePath = soundDirPath + FOCUS_CHAIN_END_SOUND_FILE_NAME;
642 mIsFocusChainEndSoundFilePathSet = true;
644 soundPlayer.PlaySound(mFocusChainEndSoundFilePath);
647 mIsEndcapFeedbackPlayed = true;
650 mIsEndcapFeedbackPlayed = false;
657 focusIDIter = mFocusIDContainer.begin();
661 focusIDIter = mFocusIDContainer.end();
662 --focusIDIter;//We want forward iterator to the last element here
667 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] Overshot\n", __FUNCTION__, __LINE__);
668 // Send notification for handling overshooted situation
669 mFocusOvershotSignal.Emit(GetCurrentFocusActor(), forward ? Toolkit::AccessibilityManager::OVERSHOT_NEXT : Toolkit::AccessibilityManager::OVERSHOT_PREVIOUS);
671 return false; // Try to move the focus out of the scope
676 if( focusIDIter == mFocusIDContainer.end() )
681 // Note: This function performs the focus change.
682 if( !DoSetCurrentFocusActor( (*focusIDIter).second ) )
684 mRecursiveFocusMoveCounter++;
685 if(mRecursiveFocusMoveCounter > mFocusIDContainer.size())
687 // We've attempted to focus all the actors in the whole focus chain and no actor
688 // can be focused successfully.
689 DALI_LOG_WARNING("[%s] There is no more focusable actor in %d focus chains\n", __FUNCTION__, mRecursiveFocusMoveCounter);
695 return DoMoveFocus(focusIDIter, forward, wrapped);
702 void AccessibilityManager::SetFocusable(Actor actor, bool focusable)
706 // Create/Set actor focusable property.
707 actor.RegisterProperty( ACTOR_FOCUSABLE, focusable, Property::READ_WRITE );
711 bool AccessibilityManager::ChangeAccessibilityStatus()
713 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
714 mIsAccessibilityTtsEnabled = adaptor.IsEnabled();
715 Dali::Toolkit::AccessibilityManager handle( this );
717 if(mIsAccessibilityTtsEnabled)
719 // Show indicator when tts turned on if there is focused actor.
720 Actor actor = GetCurrentFocusActor();
723 actor.Add( GetFocusIndicatorActor() );
725 mIsFocusIndicatorEnabled = true;
727 // Connect a signal to the TTS player to implement continuous reading mode.
728 Dali::TtsPlayer player = Dali::TtsPlayer::Get( Dali::TtsPlayer::SCREEN_READER );
729 player.StateChangedSignal().Connect( this, &AccessibilityManager::TtsStateChanged );
734 // Hide indicator when tts turned off
735 Actor actor = GetCurrentFocusActor();
736 if( actor && mFocusIndicatorActor )
738 actor.Remove( mFocusIndicatorActor );
740 mIsFocusIndicatorEnabled = false;
744 // Disconnect the TTS state change signal.
745 Dali::TtsPlayer player = Dali::TtsPlayer::Get( Dali::TtsPlayer::SCREEN_READER );
746 player.StateChangedSignal().Disconnect( this, &AccessibilityManager::TtsStateChanged );
751 mStatusChangedSignal.Emit( handle );
756 bool AccessibilityManager::AccessibilityActionNext(bool allowEndFeedback)
758 Dali::Toolkit::AccessibilityManager handle( this );
759 if( !mActionNextSignal.Empty() )
761 mActionNextSignal.Emit( handle );
764 if(mIsAccessibilityTtsEnabled)
766 mIsEndcapFeedbackEnabled = allowEndFeedback;
767 return MoveFocusForward();
775 bool AccessibilityManager::AccessibilityActionPrevious(bool allowEndFeedback)
777 Dali::Toolkit::AccessibilityManager handle( this );
778 if( !mActionPreviousSignal.Empty() )
780 mActionPreviousSignal.Emit( handle );
783 if(mIsAccessibilityTtsEnabled)
785 mIsEndcapFeedbackEnabled = allowEndFeedback;
786 return MoveFocusBackward();
794 bool AccessibilityManager::AccessibilityActionActivate()
796 Dali::Toolkit::AccessibilityManager handle( this );
797 if( !mActionActivateSignal.Empty() )
799 mActionActivateSignal.Emit( handle );
804 Actor actor = GetCurrentFocusActor();
814 bool AccessibilityManager::AccessibilityActionRead(bool allowReadAgain)
816 Dali::Toolkit::AccessibilityManager handle( this );
820 if ( !mActionReadSignal.Empty() )
822 mActionReadSignal.Emit( handle );
827 if ( !mActionOverSignal.Empty() )
829 mActionOverSignal.Emit( handle );
835 if(mIsAccessibilityTtsEnabled)
837 // Find the focusable actor at the read position
838 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
839 Dali::HitTestAlgorithm::Results results;
840 Dali::HitTestAlgorithm::HitTest( Stage::GetCurrent(), adaptor.GetReadPosition(), results, IsActorFocusableFunction );
842 FocusIDIter focusIDIter = mFocusIDContainer.find(GetFocusOrder(results.actor));
843 if(focusIDIter != mFocusIDContainer.end())
845 if( allowReadAgain || (results.actor != GetCurrentFocusActor()) )
847 // Move the focus to the actor
848 ret = SetCurrentFocusActor(results.actor);
849 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] SetCurrentFocusActor returns %s\n", __FUNCTION__, __LINE__, ret?"TRUE":"FALSE");
857 bool AccessibilityManager::AccessibilityActionReadNext(bool allowEndFeedback)
859 Dali::Toolkit::AccessibilityManager handle( this );
860 if( !mActionReadNextSignal.Empty() )
862 mActionReadNextSignal.Emit( handle );
865 if(mIsAccessibilityTtsEnabled)
867 return MoveFocusForward();
875 bool AccessibilityManager::AccessibilityActionReadPrevious(bool allowEndFeedback)
877 Dali::Toolkit::AccessibilityManager handle( this );
878 if( !mActionReadPreviousSignal.Empty() )
880 mActionReadPreviousSignal.Emit( handle );
883 if(mIsAccessibilityTtsEnabled)
885 return MoveFocusBackward();
893 bool AccessibilityManager::AccessibilityActionUp()
895 Dali::Toolkit::AccessibilityManager handle( this );
896 if( !mActionUpSignal.Empty() )
898 mActionUpSignal.Emit( handle );
903 if(mIsAccessibilityTtsEnabled)
905 Actor actor = GetCurrentFocusActor();
908 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
911 // Notify the control that it is activated
912 ret = GetImplementation( control ).OnAccessibilityValueChange(true);
920 bool AccessibilityManager::AccessibilityActionDown()
922 Dali::Toolkit::AccessibilityManager handle( this );
923 if( !mActionDownSignal.Empty() )
925 mActionDownSignal.Emit( handle );
930 if(mIsAccessibilityTtsEnabled)
932 Actor actor = GetCurrentFocusActor();
935 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
938 // Notify the control that it is activated
939 ret = GetImplementation( control ).OnAccessibilityValueChange(false);
947 bool AccessibilityManager::ClearAccessibilityFocus()
949 Dali::Toolkit::AccessibilityManager handle( this );
950 if( !mActionClearFocusSignal.Empty() )
952 mActionClearFocusSignal.Emit( handle );
955 if(mIsAccessibilityTtsEnabled)
966 bool AccessibilityManager::AccessibilityActionScroll( Dali::TouchEvent& touchEvent )
968 Dali::Toolkit::AccessibilityManager handle( this );
969 if( !mActionScrollSignal.Empty() )
971 mActionScrollSignal.Emit( handle, touchEvent );
977 bool AccessibilityManager::AccessibilityActionBack()
979 Dali::Toolkit::AccessibilityManager handle( this );
980 if( !mActionBackSignal.Empty() )
982 mActionBackSignal.Emit( handle );
985 // TODO: Back to previous view
987 return mIsAccessibilityTtsEnabled;
990 bool AccessibilityManager::AccessibilityActionScrollUp()
992 Dali::Toolkit::AccessibilityManager handle( this );
993 if( !mActionScrollUpSignal.Empty() )
995 mActionScrollUpSignal.Emit( handle );
1000 if(mIsAccessibilityTtsEnabled)
1002 Actor actor = GetCurrentFocusActor();
1005 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1008 // TODO: Notify the control to scroll up. Should control handle this?
1009 // ret = GetImplementation( control ).OnAccessibilityScroll(Direction::UP);
1017 bool AccessibilityManager::AccessibilityActionScrollDown()
1019 Dali::Toolkit::AccessibilityManager handle( this );
1020 if( !mActionScrollDownSignal.Empty() )
1022 mActionScrollDownSignal.Emit( handle );
1027 if(mIsAccessibilityTtsEnabled)
1029 Actor actor = GetCurrentFocusActor();
1032 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1035 // TODO: Notify the control to scroll down. Should control handle this?
1036 // ret = GetImplementation( control ).OnAccessibilityScrollDown(Direction::DOWN);
1044 bool AccessibilityManager::AccessibilityActionPageLeft()
1046 Dali::Toolkit::AccessibilityManager handle( this );
1047 if( !mActionPageLeftSignal.Empty() )
1049 mActionPageLeftSignal.Emit( handle );
1054 if(mIsAccessibilityTtsEnabled)
1056 Actor actor = GetCurrentFocusActor();
1059 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1062 // TODO: Notify the control to scroll left to the previous page. Should control handle this?
1063 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::LEFT);
1071 bool AccessibilityManager::AccessibilityActionPageRight()
1073 Dali::Toolkit::AccessibilityManager handle( this );
1074 if( !mActionPageRightSignal.Empty() )
1076 mActionPageRightSignal.Emit( handle );
1081 if(mIsAccessibilityTtsEnabled)
1083 Actor actor = GetCurrentFocusActor();
1086 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1089 // TODO: Notify the control to scroll right to the next page. Should control handle this?
1090 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::RIGHT);
1098 bool AccessibilityManager::AccessibilityActionPageUp()
1100 Dali::Toolkit::AccessibilityManager handle( this );
1101 if( !mActionPageUpSignal.Empty() )
1103 mActionPageUpSignal.Emit( handle );
1108 if(mIsAccessibilityTtsEnabled)
1110 Actor actor = GetCurrentFocusActor();
1113 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1116 // TODO: Notify the control to scroll up to the previous page. Should control handle this?
1117 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::UP);
1125 bool AccessibilityManager::AccessibilityActionPageDown()
1127 Dali::Toolkit::AccessibilityManager handle( this );
1128 if( !mActionPageDownSignal.Empty() )
1130 mActionPageDownSignal.Emit( handle );
1135 if(mIsAccessibilityTtsEnabled)
1137 Actor actor = GetCurrentFocusActor();
1140 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1143 // TODO: Notify the control to scroll down to the next page. Should control handle this?
1144 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::DOWN);
1152 bool AccessibilityManager::AccessibilityActionMoveToFirst()
1154 Dali::Toolkit::AccessibilityManager handle( this );
1155 if( !mActionMoveToFirstSignal.Empty() )
1157 mActionMoveToFirstSignal.Emit( handle );
1160 // TODO: Move to the first item on screen
1162 return mIsAccessibilityTtsEnabled;
1165 bool AccessibilityManager::AccessibilityActionMoveToLast()
1167 Dali::Toolkit::AccessibilityManager handle( this );
1168 if( !mActionMoveToLastSignal.Empty() )
1170 mActionMoveToLastSignal.Emit( handle );
1173 // TODO: Move to the last item on screen
1175 return mIsAccessibilityTtsEnabled;
1178 bool AccessibilityManager::AccessibilityActionReadFromTop()
1180 Dali::Toolkit::AccessibilityManager handle( this );
1181 if( !mActionReadFromTopSignal.Empty() )
1183 mActionReadFromTopSignal.Emit( handle );
1186 // TODO: Move to the top item on screen and read from the item continuously
1188 return mIsAccessibilityTtsEnabled;
1191 bool AccessibilityManager::AccessibilityActionReadFromNext()
1193 Dali::Toolkit::AccessibilityManager handle( this );
1195 if( !mActionReadFromNextSignal.Empty() )
1197 mActionReadFromNextSignal.Emit( handle );
1200 if( mIsAccessibilityTtsEnabled )
1202 // Mark that we are in continuous play mode, so TTS signals can move focus.
1203 mContinuousPlayMode = true;
1205 // Attempt to move to the next item and read from the item continuously.
1209 return mIsAccessibilityTtsEnabled;
1212 void AccessibilityManager::TtsStateChanged( const Dali::TtsPlayer::State previousState, const Dali::TtsPlayer::State currentState )
1214 if( mContinuousPlayMode )
1216 // If we were playing and now we have stopped, attempt to play the next item.
1217 if( ( previousState == Dali::TtsPlayer::PLAYING ) && ( currentState == Dali::TtsPlayer::READY ) )
1219 // Attempt to move the focus forward and play.
1220 // If we can't cancel continuous play mode.
1221 if( !MoveFocusForward() )
1223 // We are done, exit continuous play mode.
1224 mContinuousPlayMode = false;
1229 // Unexpected play state change, exit continuous play mode.
1230 mContinuousPlayMode = false;
1235 bool AccessibilityManager::AccessibilityActionZoom()
1237 Dali::Toolkit::AccessibilityManager handle( this );
1238 if( !mActionZoomSignal.Empty() )
1240 mActionZoomSignal.Emit( handle );
1245 if(mIsAccessibilityTtsEnabled)
1247 Actor actor = GetCurrentFocusActor();
1250 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1253 // Notify the control to zoom
1254 ret = GetImplementation( control ).OnAccessibilityZoom();
1262 bool AccessibilityManager::AccessibilityActionReadPauseResume()
1264 Dali::Toolkit::AccessibilityManager handle( this );
1265 if( !mActionReadPauseResumeSignal.Empty() )
1267 mActionReadPauseResumeSignal.Emit( handle );
1272 if(mIsAccessibilityTtsEnabled)
1274 // Pause or resume the TTS player
1275 Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
1276 Dali::TtsPlayer::State state = player.GetState();
1277 if(state == Dali::TtsPlayer::PLAYING)
1282 else if(state == Dali::TtsPlayer::PAUSED)
1292 bool AccessibilityManager::AccessibilityActionStartStop()
1294 Dali::Toolkit::AccessibilityManager handle( this );
1295 if( !mActionStartStopSignal.Empty() )
1297 mActionStartStopSignal.Emit( handle );
1300 // TODO: Start/stop the current action
1302 return mIsAccessibilityTtsEnabled;
1305 bool AccessibilityManager::AccessibilityActionTouch(const TouchEvent& touchEvent)
1307 bool handled = false;
1309 // TODO: Need to convert the touchevent for the focused actor?
1311 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(GetCurrentFocusActor());
1314 handled = GetImplementation( control ).OnAccessibilityTouch(touchEvent);
1320 bool AccessibilityManager::HandlePanGesture(const AccessibilityGestureEvent& panEvent)
1322 bool handled = false;
1324 if( panEvent.state == AccessibilityGestureEvent::Started )
1326 // Find the focusable actor at the event position
1327 Dali::HitTestAlgorithm::Results results;
1328 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
1330 Dali::HitTestAlgorithm::HitTest( Stage::GetCurrent(), panEvent.currentPosition, results, IsActorFocusableFunction );
1331 mCurrentGesturedActor = results.actor;
1333 if(!mCurrentGesturedActor)
1335 DALI_LOG_ERROR("Gesture detected, but no hit actor\n");
1339 // Gesture::Finished (Up) events are delivered with previous (Motion) event position
1340 // Use the real previous position; otherwise we may incorrectly get a ZERO velocity
1341 if ( AccessibilityGestureEvent::Finished != panEvent.state )
1343 // Store the previous position for next Gesture::Finished iteration.
1344 mPreviousPosition = panEvent.previousPosition;
1347 Actor rootActor = Stage::GetCurrent().GetRootLayer();
1349 Dali::PanGesture pan( static_cast<Dali::Gesture::State>(panEvent.state) );
1351 pan.time = panEvent.time;
1352 pan.numberOfTouches = panEvent.numberOfTouches;
1353 pan.screenPosition = panEvent.currentPosition;
1354 pan.screenDisplacement = mPreviousPosition - panEvent.currentPosition;
1355 pan.screenVelocity.x = pan.screenDisplacement.x / panEvent.timeDelta;
1356 pan.screenVelocity.y = pan.screenDisplacement.y / panEvent.timeDelta;
1358 // Only handle the pan gesture when the current focused actor is scrollable or within a scrollable actor
1359 while(mCurrentGesturedActor && mCurrentGesturedActor != rootActor && !handled)
1361 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(mCurrentGesturedActor);
1364 Vector2 localCurrent;
1365 control.ScreenToLocal( localCurrent.x, localCurrent.y, panEvent.currentPosition.x, panEvent.currentPosition.y );
1366 pan.position = localCurrent;
1368 Vector2 localPrevious;
1369 control.ScreenToLocal( localPrevious.x, localPrevious.y, mPreviousPosition.x, mPreviousPosition.y );
1371 pan.displacement = localCurrent - localPrevious;
1372 pan.velocity.x = pan.displacement.x / panEvent.timeDelta;
1373 pan.velocity.y = pan.displacement.y / panEvent.timeDelta;
1375 handled = GetImplementation( control ).OnAccessibilityPan(pan);
1378 // If the gesture is not handled by the control, check its parent
1381 mCurrentGesturedActor = mCurrentGesturedActor.GetParent();
1383 if(!mCurrentGesturedActor)
1385 DALI_LOG_ERROR("no more gestured actor\n");
1390 // If handled, then update the pan gesture properties
1391 PanGestureDetector::SetPanGestureProperties( pan );
1398 Toolkit::AccessibilityManager::FocusChangedSignalType& AccessibilityManager::FocusChangedSignal()
1400 return mFocusChangedSignal;
1403 Toolkit::AccessibilityManager::FocusOvershotSignalType& AccessibilityManager::FocusOvershotSignal()
1405 return mFocusOvershotSignal;
1408 Toolkit::AccessibilityManager::FocusedActorActivatedSignalType& AccessibilityManager::FocusedActorActivatedSignal()
1410 return mFocusedActorActivatedSignal;
1413 } // namespace Internal
1415 } // namespace Toolkit