2 * Copyright (c) 2019 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/public-api/images/resource-image.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::SetGroupMode(bool enabled)
563 mIsFocusWithinGroup = enabled;
566 bool AccessibilityManager::GetGroupMode() const
568 return mIsFocusWithinGroup;
571 void AccessibilityManager::SetWrapMode(bool wrapped)
573 mIsWrapped = wrapped;
576 bool AccessibilityManager::GetWrapMode() const
581 void AccessibilityManager::SetFocusIndicatorActor(Actor indicator)
583 if( mFocusIndicatorActor != indicator )
585 Actor currentFocusActor = GetCurrentFocusActor();
586 if( currentFocusActor )
588 // The new focus indicator should be added to the current focused actor immediately
589 if( mFocusIndicatorActor )
591 currentFocusActor.Remove( mFocusIndicatorActor );
596 currentFocusActor.Add( indicator );
600 mFocusIndicatorActor = indicator;
604 Actor AccessibilityManager::GetFocusIndicatorActor()
606 if( ! mFocusIndicatorActor )
608 // Create the default if it hasn't been set and one that's shared by all the keyboard focusable actors
609 const std::string imageDirPath = AssetManager::GetDaliImagePath();
610 const std::string focusBorderImagePath = imageDirPath + FOCUS_BORDER_IMAGE_FILE_NAME;
612 mFocusIndicatorActor = Toolkit::ImageView::New(focusBorderImagePath);
613 mFocusIndicatorActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
614 mFocusIndicatorActor.SetProperty( Actor::Property::POSITION_Z, 1.0f );
616 // Apply size constraint to the focus indicator
617 mFocusIndicatorActor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
620 return mFocusIndicatorActor;
623 bool AccessibilityManager::DoMoveFocus(FocusIDIter focusIDIter, bool forward, bool wrapped)
625 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] %d focusable actors\n", __FUNCTION__, __LINE__, mFocusIDContainer.size());
626 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
628 if( (forward && ++focusIDIter == mFocusIDContainer.end())
629 || (!forward && focusIDIter-- == mFocusIDContainer.begin()) )
631 if(mIsEndcapFeedbackEnabled)
633 if(mIsEndcapFeedbackPlayed == false)
635 // play sound & skip moving once
636 Dali::SoundPlayer soundPlayer = Dali::SoundPlayer::Get();
639 if (!mIsFocusChainEndSoundFilePathSet)
641 const std::string soundDirPath = AssetManager::GetDaliSoundPath();
642 mFocusChainEndSoundFilePath = soundDirPath + FOCUS_CHAIN_END_SOUND_FILE_NAME;
643 mIsFocusChainEndSoundFilePathSet = true;
645 soundPlayer.PlaySound(mFocusChainEndSoundFilePath);
648 mIsEndcapFeedbackPlayed = true;
651 mIsEndcapFeedbackPlayed = false;
658 focusIDIter = mFocusIDContainer.begin();
662 focusIDIter = mFocusIDContainer.end();
663 --focusIDIter;//We want forward iterator to the last element here
668 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] Overshot\n", __FUNCTION__, __LINE__);
669 // Send notification for handling overshooted situation
670 mFocusOvershotSignal.Emit(GetCurrentFocusActor(), forward ? Toolkit::AccessibilityManager::OVERSHOT_NEXT : Toolkit::AccessibilityManager::OVERSHOT_PREVIOUS);
672 return false; // Try to move the focus out of the scope
677 if( focusIDIter == mFocusIDContainer.end() )
682 // Note: This function performs the focus change.
683 if( !DoSetCurrentFocusActor( (*focusIDIter).second ) )
685 mRecursiveFocusMoveCounter++;
686 if(mRecursiveFocusMoveCounter > mFocusIDContainer.size())
688 // We've attempted to focus all the actors in the whole focus chain and no actor
689 // can be focused successfully.
690 DALI_LOG_WARNING("[%s] There is no more focusable actor in %d focus chains\n", __FUNCTION__, mRecursiveFocusMoveCounter);
696 return DoMoveFocus(focusIDIter, forward, wrapped);
703 void AccessibilityManager::SetFocusable(Actor actor, bool focusable)
707 // Create/Set actor focusable property.
708 actor.RegisterProperty( ACTOR_FOCUSABLE, focusable, Property::READ_WRITE );
712 bool AccessibilityManager::ChangeAccessibilityStatus()
714 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
715 mIsAccessibilityTtsEnabled = adaptor.IsEnabled();
716 Dali::Toolkit::AccessibilityManager handle( this );
718 if(mIsAccessibilityTtsEnabled)
720 // Show indicator when tts turned on if there is focused actor.
721 Actor actor = GetCurrentFocusActor();
724 actor.Add( GetFocusIndicatorActor() );
726 mIsFocusIndicatorEnabled = true;
728 // Connect a signal to the TTS player to implement continuous reading mode.
729 Dali::TtsPlayer player = Dali::TtsPlayer::Get( Dali::TtsPlayer::SCREEN_READER );
730 player.StateChangedSignal().Connect( this, &AccessibilityManager::TtsStateChanged );
735 // Hide indicator when tts turned off
736 Actor actor = GetCurrentFocusActor();
737 if( actor && mFocusIndicatorActor )
739 actor.Remove( mFocusIndicatorActor );
741 mIsFocusIndicatorEnabled = false;
745 // Disconnect the TTS state change signal.
746 Dali::TtsPlayer player = Dali::TtsPlayer::Get( Dali::TtsPlayer::SCREEN_READER );
747 player.StateChangedSignal().Disconnect( this, &AccessibilityManager::TtsStateChanged );
752 mStatusChangedSignal.Emit( handle );
757 bool AccessibilityManager::AccessibilityActionNext(bool allowEndFeedback)
759 Dali::Toolkit::AccessibilityManager handle( this );
760 if( !mActionNextSignal.Empty() )
762 mActionNextSignal.Emit( handle );
765 if(mIsAccessibilityTtsEnabled)
767 mIsEndcapFeedbackEnabled = allowEndFeedback;
768 return MoveFocusForward();
776 bool AccessibilityManager::AccessibilityActionPrevious(bool allowEndFeedback)
778 Dali::Toolkit::AccessibilityManager handle( this );
779 if( !mActionPreviousSignal.Empty() )
781 mActionPreviousSignal.Emit( handle );
784 if(mIsAccessibilityTtsEnabled)
786 mIsEndcapFeedbackEnabled = allowEndFeedback;
787 return MoveFocusBackward();
795 bool AccessibilityManager::AccessibilityActionActivate()
797 Dali::Toolkit::AccessibilityManager handle( this );
798 if( !mActionActivateSignal.Empty() )
800 mActionActivateSignal.Emit( handle );
805 Actor actor = GetCurrentFocusActor();
815 bool AccessibilityManager::AccessibilityActionRead(bool allowReadAgain)
817 Dali::Toolkit::AccessibilityManager handle( this );
821 if ( !mActionReadSignal.Empty() )
823 mActionReadSignal.Emit( handle );
828 if ( !mActionOverSignal.Empty() )
830 mActionOverSignal.Emit( handle );
836 if(mIsAccessibilityTtsEnabled)
838 // Find the focusable actor at the read position
839 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
840 Dali::HitTestAlgorithm::Results results;
841 Dali::HitTestAlgorithm::HitTest( Stage::GetCurrent(), adaptor.GetReadPosition(), results, IsActorFocusableFunction );
843 FocusIDIter focusIDIter = mFocusIDContainer.find(GetFocusOrder(results.actor));
844 if(focusIDIter != mFocusIDContainer.end())
846 if( allowReadAgain || (results.actor != GetCurrentFocusActor()) )
848 // Move the focus to the actor
849 ret = SetCurrentFocusActor(results.actor);
850 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] SetCurrentFocusActor returns %s\n", __FUNCTION__, __LINE__, ret?"TRUE":"FALSE");
858 bool AccessibilityManager::AccessibilityActionReadNext(bool allowEndFeedback)
860 Dali::Toolkit::AccessibilityManager handle( this );
861 if( !mActionReadNextSignal.Empty() )
863 mActionReadNextSignal.Emit( handle );
866 if(mIsAccessibilityTtsEnabled)
868 return MoveFocusForward();
876 bool AccessibilityManager::AccessibilityActionReadPrevious(bool allowEndFeedback)
878 Dali::Toolkit::AccessibilityManager handle( this );
879 if( !mActionReadPreviousSignal.Empty() )
881 mActionReadPreviousSignal.Emit( handle );
884 if(mIsAccessibilityTtsEnabled)
886 return MoveFocusBackward();
894 bool AccessibilityManager::AccessibilityActionUp()
896 Dali::Toolkit::AccessibilityManager handle( this );
897 if( !mActionUpSignal.Empty() )
899 mActionUpSignal.Emit( handle );
904 if(mIsAccessibilityTtsEnabled)
906 Actor actor = GetCurrentFocusActor();
909 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
912 // Notify the control that it is activated
913 ret = GetImplementation( control ).OnAccessibilityValueChange(true);
921 bool AccessibilityManager::AccessibilityActionDown()
923 Dali::Toolkit::AccessibilityManager handle( this );
924 if( !mActionDownSignal.Empty() )
926 mActionDownSignal.Emit( handle );
931 if(mIsAccessibilityTtsEnabled)
933 Actor actor = GetCurrentFocusActor();
936 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
939 // Notify the control that it is activated
940 ret = GetImplementation( control ).OnAccessibilityValueChange(false);
948 bool AccessibilityManager::ClearAccessibilityFocus()
950 Dali::Toolkit::AccessibilityManager handle( this );
951 if( !mActionClearFocusSignal.Empty() )
953 mActionClearFocusSignal.Emit( handle );
956 if(mIsAccessibilityTtsEnabled)
967 bool AccessibilityManager::AccessibilityActionScroll( Dali::TouchEvent& touchEvent )
969 Dali::Toolkit::AccessibilityManager handle( this );
970 if( !mActionScrollSignal.Empty() )
972 mActionScrollSignal.Emit( handle, touchEvent );
978 bool AccessibilityManager::AccessibilityActionBack()
980 Dali::Toolkit::AccessibilityManager handle( this );
981 if( !mActionBackSignal.Empty() )
983 mActionBackSignal.Emit( handle );
986 // TODO: Back to previous view
988 return mIsAccessibilityTtsEnabled;
991 bool AccessibilityManager::AccessibilityActionScrollUp()
993 Dali::Toolkit::AccessibilityManager handle( this );
994 if( !mActionScrollUpSignal.Empty() )
996 mActionScrollUpSignal.Emit( handle );
1001 if(mIsAccessibilityTtsEnabled)
1003 Actor actor = GetCurrentFocusActor();
1006 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1009 // TODO: Notify the control to scroll up. Should control handle this?
1010 // ret = GetImplementation( control ).OnAccessibilityScroll(Direction::UP);
1018 bool AccessibilityManager::AccessibilityActionScrollDown()
1020 Dali::Toolkit::AccessibilityManager handle( this );
1021 if( !mActionScrollDownSignal.Empty() )
1023 mActionScrollDownSignal.Emit( handle );
1028 if(mIsAccessibilityTtsEnabled)
1030 Actor actor = GetCurrentFocusActor();
1033 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1036 // TODO: Notify the control to scroll down. Should control handle this?
1037 // ret = GetImplementation( control ).OnAccessibilityScrollDown(Direction::DOWN);
1045 bool AccessibilityManager::AccessibilityActionPageLeft()
1047 Dali::Toolkit::AccessibilityManager handle( this );
1048 if( !mActionPageLeftSignal.Empty() )
1050 mActionPageLeftSignal.Emit( handle );
1055 if(mIsAccessibilityTtsEnabled)
1057 Actor actor = GetCurrentFocusActor();
1060 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1063 // TODO: Notify the control to scroll left to the previous page. Should control handle this?
1064 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::LEFT);
1072 bool AccessibilityManager::AccessibilityActionPageRight()
1074 Dali::Toolkit::AccessibilityManager handle( this );
1075 if( !mActionPageRightSignal.Empty() )
1077 mActionPageRightSignal.Emit( handle );
1082 if(mIsAccessibilityTtsEnabled)
1084 Actor actor = GetCurrentFocusActor();
1087 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1090 // TODO: Notify the control to scroll right to the next page. Should control handle this?
1091 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::RIGHT);
1099 bool AccessibilityManager::AccessibilityActionPageUp()
1101 Dali::Toolkit::AccessibilityManager handle( this );
1102 if( !mActionPageUpSignal.Empty() )
1104 mActionPageUpSignal.Emit( handle );
1109 if(mIsAccessibilityTtsEnabled)
1111 Actor actor = GetCurrentFocusActor();
1114 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1117 // TODO: Notify the control to scroll up to the previous page. Should control handle this?
1118 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::UP);
1126 bool AccessibilityManager::AccessibilityActionPageDown()
1128 Dali::Toolkit::AccessibilityManager handle( this );
1129 if( !mActionPageDownSignal.Empty() )
1131 mActionPageDownSignal.Emit( handle );
1136 if(mIsAccessibilityTtsEnabled)
1138 Actor actor = GetCurrentFocusActor();
1141 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1144 // TODO: Notify the control to scroll down to the next page. Should control handle this?
1145 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::DOWN);
1153 bool AccessibilityManager::AccessibilityActionMoveToFirst()
1155 Dali::Toolkit::AccessibilityManager handle( this );
1156 if( !mActionMoveToFirstSignal.Empty() )
1158 mActionMoveToFirstSignal.Emit( handle );
1161 // TODO: Move to the first item on screen
1163 return mIsAccessibilityTtsEnabled;
1166 bool AccessibilityManager::AccessibilityActionMoveToLast()
1168 Dali::Toolkit::AccessibilityManager handle( this );
1169 if( !mActionMoveToLastSignal.Empty() )
1171 mActionMoveToLastSignal.Emit( handle );
1174 // TODO: Move to the last item on screen
1176 return mIsAccessibilityTtsEnabled;
1179 bool AccessibilityManager::AccessibilityActionReadFromTop()
1181 Dali::Toolkit::AccessibilityManager handle( this );
1182 if( !mActionReadFromTopSignal.Empty() )
1184 mActionReadFromTopSignal.Emit( handle );
1187 // TODO: Move to the top item on screen and read from the item continuously
1189 return mIsAccessibilityTtsEnabled;
1192 bool AccessibilityManager::AccessibilityActionReadFromNext()
1194 Dali::Toolkit::AccessibilityManager handle( this );
1196 if( !mActionReadFromNextSignal.Empty() )
1198 mActionReadFromNextSignal.Emit( handle );
1201 if( mIsAccessibilityTtsEnabled )
1203 // Mark that we are in continuous play mode, so TTS signals can move focus.
1204 mContinuousPlayMode = true;
1206 // Attempt to move to the next item and read from the item continuously.
1210 return mIsAccessibilityTtsEnabled;
1213 void AccessibilityManager::TtsStateChanged( const Dali::TtsPlayer::State previousState, const Dali::TtsPlayer::State currentState )
1215 if( mContinuousPlayMode )
1217 // If we were playing and now we have stopped, attempt to play the next item.
1218 if( ( previousState == Dali::TtsPlayer::PLAYING ) && ( currentState == Dali::TtsPlayer::READY ) )
1220 // Attempt to move the focus forward and play.
1221 // If we can't cancel continuous play mode.
1222 if( !MoveFocusForward() )
1224 // We are done, exit continuous play mode.
1225 mContinuousPlayMode = false;
1230 // Unexpected play state change, exit continuous play mode.
1231 mContinuousPlayMode = false;
1236 bool AccessibilityManager::AccessibilityActionZoom()
1238 Dali::Toolkit::AccessibilityManager handle( this );
1239 if( !mActionZoomSignal.Empty() )
1241 mActionZoomSignal.Emit( handle );
1246 if(mIsAccessibilityTtsEnabled)
1248 Actor actor = GetCurrentFocusActor();
1251 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1254 // Notify the control to zoom
1255 ret = GetImplementation( control ).OnAccessibilityZoom();
1263 bool AccessibilityManager::AccessibilityActionReadPauseResume()
1265 Dali::Toolkit::AccessibilityManager handle( this );
1266 if( !mActionReadPauseResumeSignal.Empty() )
1268 mActionReadPauseResumeSignal.Emit( handle );
1273 if(mIsAccessibilityTtsEnabled)
1275 // Pause or resume the TTS player
1276 Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
1277 Dali::TtsPlayer::State state = player.GetState();
1278 if(state == Dali::TtsPlayer::PLAYING)
1283 else if(state == Dali::TtsPlayer::PAUSED)
1293 bool AccessibilityManager::AccessibilityActionStartStop()
1295 Dali::Toolkit::AccessibilityManager handle( this );
1296 if( !mActionStartStopSignal.Empty() )
1298 mActionStartStopSignal.Emit( handle );
1301 // TODO: Start/stop the current action
1303 return mIsAccessibilityTtsEnabled;
1306 bool AccessibilityManager::AccessibilityActionTouch(const TouchEvent& touchEvent)
1308 bool handled = false;
1310 // TODO: Need to convert the touchevent for the focused actor?
1312 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(GetCurrentFocusActor());
1315 handled = GetImplementation( control ).OnAccessibilityTouch(touchEvent);
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 // Gesture::Finished (Up) events are delivered with previous (Motion) event position
1341 // Use the real previous position; otherwise we may incorrectly get a ZERO velocity
1342 if ( AccessibilityGestureEvent::Finished != panEvent.state )
1344 // Store the previous position for next Gesture::Finished iteration.
1345 mPreviousPosition = panEvent.previousPosition;
1348 Actor rootActor = Stage::GetCurrent().GetRootLayer();
1350 Dali::PanGesture pan( static_cast<Dali::Gesture::State>(panEvent.state) );
1352 pan.time = panEvent.time;
1353 pan.numberOfTouches = panEvent.numberOfTouches;
1354 pan.screenPosition = panEvent.currentPosition;
1355 pan.screenDisplacement = mPreviousPosition - panEvent.currentPosition;
1356 pan.screenVelocity.x = pan.screenDisplacement.x / panEvent.timeDelta;
1357 pan.screenVelocity.y = pan.screenDisplacement.y / panEvent.timeDelta;
1359 // Only handle the pan gesture when the current focused actor is scrollable or within a scrollable actor
1360 while(mCurrentGesturedActor && mCurrentGesturedActor != rootActor && !handled)
1362 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(mCurrentGesturedActor);
1365 Vector2 localCurrent;
1366 control.ScreenToLocal( localCurrent.x, localCurrent.y, panEvent.currentPosition.x, panEvent.currentPosition.y );
1367 pan.position = localCurrent;
1369 Vector2 localPrevious;
1370 control.ScreenToLocal( localPrevious.x, localPrevious.y, mPreviousPosition.x, mPreviousPosition.y );
1372 pan.displacement = localCurrent - localPrevious;
1373 pan.velocity.x = pan.displacement.x / panEvent.timeDelta;
1374 pan.velocity.y = pan.displacement.y / panEvent.timeDelta;
1376 handled = GetImplementation( control ).OnAccessibilityPan(pan);
1379 // If the gesture is not handled by the control, check its parent
1382 mCurrentGesturedActor = mCurrentGesturedActor.GetParent();
1384 if(!mCurrentGesturedActor)
1386 DALI_LOG_ERROR("no more gestured actor\n");
1391 // If handled, then update the pan gesture properties
1392 PanGestureDetector::SetPanGestureProperties( pan );
1399 Toolkit::AccessibilityManager::FocusChangedSignalType& AccessibilityManager::FocusChangedSignal()
1401 return mFocusChangedSignal;
1404 Toolkit::AccessibilityManager::FocusOvershotSignalType& AccessibilityManager::FocusOvershotSignal()
1406 return mFocusOvershotSignal;
1409 Toolkit::AccessibilityManager::FocusedActorActivatedSignalType& AccessibilityManager::FocusedActorActivatedSignal()
1411 return mFocusedActorActivatedSignal;
1414 } // namespace Internal
1416 } // namespace Toolkit