2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include "accessibility-manager-impl.h"
22 #include <cstring> // for strcmp
23 #include <dali/public-api/actors/layer.h>
24 #include <dali/devel-api/adaptor-framework/accessibility-adaptor.h>
25 #include <dali/devel-api/adaptor-framework/sound-player.h>
26 #include <dali/public-api/animation/constraints.h>
27 #include <dali/devel-api/events/hit-test-algorithm.h>
28 #include <dali/devel-api/events/pan-gesture-devel.h>
29 #include <dali/integration-api/debug.h>
32 #include <dali-toolkit/devel-api/asset-manager/asset-manager.h>
33 #include <dali-toolkit/public-api/controls/control.h>
34 #include <dali-toolkit/public-api/controls/control-impl.h>
35 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
46 namespace // unnamed namespace
51 const char* const SIGNAL_FOCUS_CHANGED = "focusChanged";
52 const char* const SIGNAL_FOCUS_OVERSHOT = "focusOvershot";
53 const char* const SIGNAL_FOCUSED_ACTOR_ACTIVATED = "focusedActorActivated";
55 #if defined(DEBUG_ENABLED)
56 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_FOCUS_MANAGER");
59 const char* const ACTOR_FOCUSABLE("focusable");
60 const char* const IS_FOCUS_GROUP("isFocusGroup");
62 const char* FOCUS_BORDER_IMAGE_FILE_NAME = "B16-8_TTS_focus.9.png";
64 const char* FOCUS_SOUND_FILE_NAME = "Focus.ogg";
65 const char* FOCUS_CHAIN_END_SOUND_FILE_NAME = "End_of_List.ogg";
68 * The function to be used in the hit-test algorithm to check whether the actor is hittable.
70 bool IsActorFocusableFunction(Actor actor, Dali::HitTestAlgorithm::TraverseType type)
72 bool hittable = false;
76 case Dali::HitTestAlgorithm::CHECK_ACTOR:
78 // Check whether the actor is visible and not fully transparent.
79 if( actor.GetCurrentProperty< bool >( Actor::Property::VISIBLE )
80 && actor.GetCurrentProperty< Vector4 >( Actor::Property::WORLD_COLOR ).a > 0.01f) // not FULLY_TRANSPARENT
82 // Check whether the actor is focusable
83 Property::Index propertyActorFocusable = actor.GetPropertyIndex(ACTOR_FOCUSABLE);
84 if(propertyActorFocusable != Property::INVALID_INDEX)
86 hittable = actor.GetProperty<bool>(propertyActorFocusable);
91 case Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE:
93 if( actor.GetCurrentProperty< bool >( Actor::Property::VISIBLE ) ) // Actor is visible, if not visible then none of its children are visible.
110 AccessibilityManager::AccessibilityManager()
111 : mCurrentFocusActor(FocusIDPair(0, 0)),
112 mCurrentGesturedActor(),
113 mFocusIndicatorActor(),
114 mPreviousPosition( 0.0f, 0.0f ),
115 mRecursiveFocusMoveCounter(0),
116 mFocusSoundFilePath(),
117 mFocusChainEndSoundFilePath(),
119 mIsFocusWithinGroup(false),
120 mIsEndcapFeedbackEnabled(false),
121 mIsEndcapFeedbackPlayed(false),
122 mIsAccessibilityTtsEnabled(false),
124 mIsFocusIndicatorEnabled(false),
125 mContinuousPlayMode(false),
126 mIsFocusSoundFilePathSet(false),
127 mIsFocusChainEndSoundFilePathSet(false)
131 AccessibilityManager::~AccessibilityManager()
135 void AccessibilityManager::Initialise()
137 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
138 adaptor.SetActionHandler(*this);
139 adaptor.SetGestureHandler(*this);
141 ChangeAccessibilityStatus();
144 AccessibilityManager::ActorAdditionalInfo AccessibilityManager::GetActorAdditionalInfo(const unsigned int actorID) const
146 ActorAdditionalInfo data;
147 IDAdditionalInfoConstIter iter = mIDAdditionalInfoContainer.find(actorID);
148 if(iter != mIDAdditionalInfoContainer.end())
150 data = (*iter).second;
156 void AccessibilityManager::SynchronizeActorAdditionalInfo(const unsigned int actorID, const unsigned int order)
158 ActorAdditionalInfo actorInfo = GetActorAdditionalInfo(actorID);
159 actorInfo.mFocusOrder = order;
160 mIDAdditionalInfoContainer.erase(actorID);
161 mIDAdditionalInfoContainer.insert(IDAdditionalInfoPair(actorID, actorInfo));
164 void AccessibilityManager::SetAccessibilityAttribute(Actor actor, Toolkit::AccessibilityManager::AccessibilityAttribute type, const std::string& text)
168 unsigned int actorID = actor.GetProperty< int >( Actor::Property::ID );
170 ActorAdditionalInfo info = GetActorAdditionalInfo(actorID);
171 info.mAccessibilityAttributes[type] = text;
173 mIDAdditionalInfoContainer.erase(actorID);
174 mIDAdditionalInfoContainer.insert(IDAdditionalInfoPair(actorID, info));
178 void AccessibilityManager::DeleteAccessibilityAttribute(Actor actor)
182 unsigned int actorID = actor.GetProperty< int >( Actor::Property::ID );
183 mIDAdditionalInfoContainer.erase(actorID);
187 std::string AccessibilityManager::GetAccessibilityAttribute(Actor actor, Toolkit::AccessibilityManager::AccessibilityAttribute type) const
193 ActorAdditionalInfo data = GetActorAdditionalInfo(actor.GetProperty< int >( Actor::Property::ID ));
194 text = data.mAccessibilityAttributes[type];
200 void AccessibilityManager::SetFocusOrder(Actor actor, const unsigned int order)
202 // Do nothing if the focus order of the actor is not changed.
203 if(actor && GetFocusOrder(actor) != order)
205 // Firstly delete the actor from the focus chain if it's already there with a different focus order.
206 mFocusIDContainer.erase(GetFocusOrder(actor));
208 // Create/retrieve actor focusable property
209 Property::Index propertyActorFocusable = actor.RegisterProperty( ACTOR_FOCUSABLE, true, Property::READ_WRITE );
213 // The actor is not focusable without a defined focus order.
214 actor.SetProperty(propertyActorFocusable, false);
216 // If the actor is currently being focused, it should clear the focus
217 if(actor == GetCurrentFocusActor())
222 else // Insert the actor to the focus chain
224 // Check whether there is another actor in the focus chain with the same focus order already.
225 FocusIDIter focusIDIter = mFocusIDContainer.find(order);
226 if(focusIDIter != mFocusIDContainer.end())
228 // We need to increase the focus order of that actor and all the actors followed it
229 // in the focus chain.
230 FocusIDIter lastIter = mFocusIDContainer.end();
231 --lastIter;//We want forward iterator to the last element here
232 mFocusIDContainer.insert(FocusIDPair((*lastIter).first + 1, (*lastIter).second));
234 // Update the actor's focus order in its additional data
235 SynchronizeActorAdditionalInfo((*lastIter).second, (*lastIter).first + 1);
237 for(FocusIDIter iter = lastIter; iter != focusIDIter; iter--)
239 FocusIDIter previousIter = iter;
240 --previousIter;//We want forward iterator to the previous element here
241 unsigned int actorID = (*previousIter).second;
242 (*iter).second = actorID;
244 // Update the actor's focus order in its additional data
245 SynchronizeActorAdditionalInfo(actorID, (*iter).first);
248 mFocusIDContainer.erase(order);
251 // The actor is focusable
252 actor.SetProperty(propertyActorFocusable, true);
254 // Now we insert the actor into the focus chain with the specified focus order
255 mFocusIDContainer.insert(FocusIDPair(order, actor.GetProperty< int >( Actor::Property::ID )));
258 // Update the actor's focus order in its additional data
259 SynchronizeActorAdditionalInfo(actor.GetProperty< int >( Actor::Property::ID ), order);
263 unsigned int AccessibilityManager::GetFocusOrder(Actor actor) const
265 unsigned int focusOrder = 0;
269 ActorAdditionalInfo data = GetActorAdditionalInfo(actor.GetProperty< int >( Actor::Property::ID ));
270 focusOrder = data.mFocusOrder;
276 unsigned int AccessibilityManager::GenerateNewFocusOrder() const
278 unsigned int order = 1;
279 FocusIDContainer::const_reverse_iterator iter = mFocusIDContainer.rbegin();
281 if(iter != mFocusIDContainer.rend())
283 order = (*iter).first + 1;
289 Actor AccessibilityManager::GetActorByFocusOrder(const unsigned int order)
291 Actor actor = Actor();
293 FocusIDIter focusIDIter = mFocusIDContainer.find(order);
294 if(focusIDIter != mFocusIDContainer.end())
296 Actor rootActor = Stage::GetCurrent().GetRootLayer();
297 actor = rootActor.FindChildById(mFocusIDContainer[order]);
303 bool AccessibilityManager::SetCurrentFocusActor(Actor actor)
307 return DoSetCurrentFocusActor(actor.GetProperty< int >( Actor::Property::ID ));
313 bool AccessibilityManager::DoSetCurrentFocusActor(const unsigned int actorID)
315 Actor rootActor = Stage::GetCurrent().GetRootLayer();
317 // If the group mode is enabled, check which focus group the current focused actor belongs to
319 if(mIsFocusWithinGroup)
321 focusGroup = GetFocusGroup(GetCurrentFocusActor());
326 focusGroup = rootActor;
329 Actor actor = focusGroup.FindChildById(actorID);
331 // Check whether the actor is in the stage
334 // Check whether the actor is focusable
335 bool actorFocusable = false;
336 Property::Index propertyActorFocusable = actor.GetPropertyIndex(ACTOR_FOCUSABLE);
337 if(propertyActorFocusable != Property::INVALID_INDEX)
339 actorFocusable = actor.GetProperty<bool>(propertyActorFocusable);
342 // Go through the actor's hierarchy to check whether the actor is visible
343 bool actorVisible = actor.GetCurrentProperty< bool >( Actor::Property::VISIBLE );
344 Actor parent = actor.GetParent();
345 while (actorVisible && parent && parent != rootActor)
347 actorVisible = parent.GetCurrentProperty< bool >( Actor::Property::VISIBLE );
348 parent = parent.GetParent();
351 // Check whether the actor is fully transparent
352 bool actorOpaque = actor.GetCurrentProperty< Vector4 >( Actor::Property::WORLD_COLOR ).a > 0.01f;
354 // Set the focus only when the actor is focusable and visible and not fully transparent
355 if(actorVisible && actorFocusable && actorOpaque)
357 // Draw the focus indicator upon the focused actor
358 if( mIsFocusIndicatorEnabled )
360 actor.Add( GetFocusIndicatorActor() );
363 // Send notification for the change of focus actor
364 mFocusChangedSignal.Emit( GetCurrentFocusActor(), actor );
366 // Save the current focused actor
367 mCurrentFocusActor = FocusIDPair(GetFocusOrder(actor), actorID);
369 if(mIsAccessibilityTtsEnabled)
371 Dali::SoundPlayer soundPlayer = Dali::SoundPlayer::Get();
374 if (!mIsFocusSoundFilePathSet)
376 const std::string soundDirPath = AssetManager::GetDaliSoundPath();
377 mFocusSoundFilePath = soundDirPath + FOCUS_SOUND_FILE_NAME;
378 mIsFocusSoundFilePathSet = true;
380 soundPlayer.PlaySound(mFocusSoundFilePath);
383 // Play the accessibility attributes with the TTS player.
384 Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
386 // Combine attribute texts to one text
387 std::string informationText;
388 for(int i = 0; i < Toolkit::AccessibilityManager::ACCESSIBILITY_ATTRIBUTE_NUM; i++)
390 if(!GetActorAdditionalInfo(actorID).mAccessibilityAttributes[i].empty())
394 informationText += ", "; // for space time between each information
396 informationText += GetActorAdditionalInfo(actorID).mAccessibilityAttributes[i];
399 player.Play(informationText);
406 DALI_LOG_WARNING("[%s:%d] FAILED\n", __FUNCTION__, __LINE__);
410 Actor AccessibilityManager::GetCurrentFocusActor()
412 Actor rootActor = Stage::GetCurrent().GetRootLayer();
413 return rootActor.FindChildById(mCurrentFocusActor.second);
416 Actor AccessibilityManager::GetCurrentFocusGroup()
418 return GetFocusGroup(GetCurrentFocusActor());
421 unsigned int AccessibilityManager::GetCurrentFocusOrder()
423 return mCurrentFocusActor.first;
426 bool AccessibilityManager::MoveFocusForward()
429 mRecursiveFocusMoveCounter = 0;
431 FocusIDIter focusIDIter = mFocusIDContainer.find(mCurrentFocusActor.first);
432 if(focusIDIter != mFocusIDContainer.end())
434 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
435 ret = DoMoveFocus(focusIDIter, true, mIsWrapped);
439 // TODO: if there is not focused actor, move first actor
440 if(!mFocusIDContainer.empty())
442 //if there is not focused actor, move 1st actor
443 focusIDIter = mFocusIDContainer.begin(); // TODO: I'm not sure it was sorted.
444 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
445 ret = DoSetCurrentFocusActor((*focusIDIter).second);
449 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s] %s\n", __FUNCTION__, ret?"SUCCEED!!!":"FAILED!!!");
454 bool AccessibilityManager::MoveFocusBackward()
457 mRecursiveFocusMoveCounter = 0;
459 FocusIDIter focusIDIter = mFocusIDContainer.find(mCurrentFocusActor.first);
460 if(focusIDIter != mFocusIDContainer.end())
462 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
463 ret = DoMoveFocus(focusIDIter, false, mIsWrapped);
467 // TODO: if there is not focused actor, move last actor
468 if(!mFocusIDContainer.empty())
470 //if there is not focused actor, move last actor
471 focusIDIter = mFocusIDContainer.end();
472 --focusIDIter;//We want forward iterator to the last element here
473 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
474 ret = DoSetCurrentFocusActor((*focusIDIter).second);
478 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s] %s\n", __FUNCTION__, ret?"SUCCEED!!!":"FAILED!!!");
483 void AccessibilityManager::DoActivate(Actor actor)
487 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
490 // Notify the control that it is activated
491 GetImplementation( control ).AccessibilityActivate();
494 // Send notification for the activation of focused actor
495 mFocusedActorActivatedSignal.Emit(actor);
499 void AccessibilityManager::ClearFocus()
501 Actor actor = GetCurrentFocusActor();
502 if( actor && mFocusIndicatorActor )
504 actor.Remove( mFocusIndicatorActor );
507 mCurrentFocusActor = FocusIDPair(0, 0);
509 // Send notification for the change of focus actor
510 mFocusChangedSignal.Emit(actor, Actor());
512 if(mIsAccessibilityTtsEnabled)
514 // Stop the TTS playing if any
515 Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
520 void AccessibilityManager::Reset()
523 mFocusIDContainer.clear();
524 mIDAdditionalInfoContainer.clear();
527 void AccessibilityManager::SetFocusGroup(Actor actor, bool isFocusGroup)
531 // Create/Set focus group property.
532 actor.RegisterProperty( IS_FOCUS_GROUP, isFocusGroup, Property::READ_WRITE );
536 bool AccessibilityManager::IsFocusGroup(Actor actor) const
538 // Check whether the actor is a focus group
539 bool isFocusGroup = false;
543 Property::Index propertyIsFocusGroup = actor.GetPropertyIndex(IS_FOCUS_GROUP);
544 if(propertyIsFocusGroup != Property::INVALID_INDEX)
546 isFocusGroup = actor.GetProperty<bool>(propertyIsFocusGroup);
553 Actor AccessibilityManager::GetFocusGroup(Actor actor)
555 // Go through the actor's hierarchy to check which focus group the actor belongs to
556 while (actor && !IsFocusGroup(actor))
558 actor = actor.GetParent();
564 Vector2 AccessibilityManager::GetReadPosition() const
566 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
567 return adaptor.GetReadPosition();
570 void AccessibilityManager::EnableAccessibility(bool enabled)
572 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] Set Enabled Forcibly : %d \n", __FUNCTION__, __LINE__, enabled );
573 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
574 adaptor.EnableAccessibility(enabled);
577 bool AccessibilityManager::IsEnabled() const
579 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
580 return adaptor.IsEnabled();
583 void AccessibilityManager::SetGroupMode(bool enabled)
585 mIsFocusWithinGroup = enabled;
588 bool AccessibilityManager::GetGroupMode() const
590 return mIsFocusWithinGroup;
593 void AccessibilityManager::SetWrapMode(bool wrapped)
595 mIsWrapped = wrapped;
598 bool AccessibilityManager::GetWrapMode() const
603 void AccessibilityManager::SetFocusIndicatorActor(Actor indicator)
605 if( mFocusIndicatorActor != indicator )
607 Actor currentFocusActor = GetCurrentFocusActor();
608 if( currentFocusActor )
610 // The new focus indicator should be added to the current focused actor immediately
611 if( mFocusIndicatorActor )
613 currentFocusActor.Remove( mFocusIndicatorActor );
618 currentFocusActor.Add( indicator );
622 mFocusIndicatorActor = indicator;
626 Actor AccessibilityManager::GetFocusIndicatorActor()
628 if( ! mFocusIndicatorActor )
630 // Create the default if it hasn't been set and one that's shared by all the keyboard focusable actors
631 const std::string imageDirPath = AssetManager::GetDaliImagePath();
632 const std::string focusBorderImagePath = imageDirPath + FOCUS_BORDER_IMAGE_FILE_NAME;
634 mFocusIndicatorActor = Toolkit::ImageView::New(focusBorderImagePath);
635 mFocusIndicatorActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
636 mFocusIndicatorActor.SetProperty( Actor::Property::POSITION_Z, 1.0f );
638 // Apply size constraint to the focus indicator
639 mFocusIndicatorActor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
642 return mFocusIndicatorActor;
645 bool AccessibilityManager::DoMoveFocus(FocusIDIter focusIDIter, bool forward, bool wrapped)
647 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] %d focusable actors\n", __FUNCTION__, __LINE__, mFocusIDContainer.size());
648 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
650 if( (forward && ++focusIDIter == mFocusIDContainer.end())
651 || (!forward && focusIDIter-- == mFocusIDContainer.begin()) )
653 if(mIsEndcapFeedbackEnabled)
655 if(mIsEndcapFeedbackPlayed == false)
657 // play sound & skip moving once
658 Dali::SoundPlayer soundPlayer = Dali::SoundPlayer::Get();
661 if (!mIsFocusChainEndSoundFilePathSet)
663 const std::string soundDirPath = AssetManager::GetDaliSoundPath();
664 mFocusChainEndSoundFilePath = soundDirPath + FOCUS_CHAIN_END_SOUND_FILE_NAME;
665 mIsFocusChainEndSoundFilePathSet = true;
667 soundPlayer.PlaySound(mFocusChainEndSoundFilePath);
670 mIsEndcapFeedbackPlayed = true;
673 mIsEndcapFeedbackPlayed = false;
680 focusIDIter = mFocusIDContainer.begin();
684 focusIDIter = mFocusIDContainer.end();
685 --focusIDIter;//We want forward iterator to the last element here
690 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] Overshot\n", __FUNCTION__, __LINE__);
691 // Send notification for handling overshooted situation
692 mFocusOvershotSignal.Emit(GetCurrentFocusActor(), forward ? Toolkit::AccessibilityManager::OVERSHOT_NEXT : Toolkit::AccessibilityManager::OVERSHOT_PREVIOUS);
694 return false; // Try to move the focus out of the scope
699 if( focusIDIter == mFocusIDContainer.end() )
704 // Note: This function performs the focus change.
705 if( !DoSetCurrentFocusActor( (*focusIDIter).second ) )
707 mRecursiveFocusMoveCounter++;
708 if(mRecursiveFocusMoveCounter > mFocusIDContainer.size())
710 // We've attempted to focus all the actors in the whole focus chain and no actor
711 // can be focused successfully.
712 DALI_LOG_WARNING("[%s] There is no more focusable actor in %d focus chains\n", __FUNCTION__, mRecursiveFocusMoveCounter);
718 return DoMoveFocus(focusIDIter, forward, wrapped);
725 void AccessibilityManager::SetFocusable(Actor actor, bool focusable)
729 // Create/Set actor focusable property.
730 actor.RegisterProperty( ACTOR_FOCUSABLE, focusable, Property::READ_WRITE );
734 bool AccessibilityManager::ChangeAccessibilityStatus()
736 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
737 mIsAccessibilityTtsEnabled = adaptor.IsEnabled();
738 Dali::Toolkit::AccessibilityManager handle( this );
740 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] TtsEnabled : %d \n", __FUNCTION__, __LINE__, mIsAccessibilityTtsEnabled );
742 if(mIsAccessibilityTtsEnabled)
744 // Show indicator when tts turned on if there is focused actor.
745 Actor actor = GetCurrentFocusActor();
748 actor.Add( GetFocusIndicatorActor() );
750 mIsFocusIndicatorEnabled = true;
752 // Connect a signal to the TTS player to implement continuous reading mode.
753 Dali::TtsPlayer player = Dali::TtsPlayer::Get( Dali::TtsPlayer::SCREEN_READER );
754 player.StateChangedSignal().Connect( this, &AccessibilityManager::TtsStateChanged );
759 // Hide indicator when tts turned off
760 Actor actor = GetCurrentFocusActor();
761 if( actor && mFocusIndicatorActor )
763 actor.Remove( mFocusIndicatorActor );
765 mIsFocusIndicatorEnabled = false;
769 // Disconnect the TTS state change signal.
770 Dali::TtsPlayer player = Dali::TtsPlayer::Get( Dali::TtsPlayer::SCREEN_READER );
771 player.StateChangedSignal().Disconnect( this, &AccessibilityManager::TtsStateChanged );
776 mStatusChangedSignal.Emit( handle );
781 bool AccessibilityManager::AccessibilityActionNext(bool allowEndFeedback)
783 Dali::Toolkit::AccessibilityManager handle( this );
784 if( !mActionNextSignal.Empty() )
786 mActionNextSignal.Emit( handle );
789 if(mIsAccessibilityTtsEnabled)
791 mIsEndcapFeedbackEnabled = allowEndFeedback;
792 return MoveFocusForward();
800 bool AccessibilityManager::AccessibilityActionPrevious(bool allowEndFeedback)
802 Dali::Toolkit::AccessibilityManager handle( this );
803 if( !mActionPreviousSignal.Empty() )
805 mActionPreviousSignal.Emit( handle );
808 if(mIsAccessibilityTtsEnabled)
810 mIsEndcapFeedbackEnabled = allowEndFeedback;
811 return MoveFocusBackward();
819 bool AccessibilityManager::AccessibilityActionActivate()
821 Dali::Toolkit::AccessibilityManager handle( this );
822 if( !mActionActivateSignal.Empty() )
824 mActionActivateSignal.Emit( handle );
829 Actor actor = GetCurrentFocusActor();
839 bool AccessibilityManager::AccessibilityActionRead(bool allowReadAgain)
841 Dali::Toolkit::AccessibilityManager handle( this );
845 if ( !mActionReadSignal.Empty() )
847 mActionReadSignal.Emit( handle );
852 if ( !mActionOverSignal.Empty() )
854 mActionOverSignal.Emit( handle );
860 if(mIsAccessibilityTtsEnabled)
862 // Find the focusable actor at the read position
863 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
864 Dali::HitTestAlgorithm::Results results;
865 Dali::HitTestAlgorithm::HitTest( Stage::GetCurrent(), adaptor.GetReadPosition(), results, IsActorFocusableFunction );
867 FocusIDIter focusIDIter = mFocusIDContainer.find(GetFocusOrder(results.actor));
868 if(focusIDIter != mFocusIDContainer.end())
870 if( allowReadAgain || (results.actor != GetCurrentFocusActor()) )
872 // Move the focus to the actor
873 ret = SetCurrentFocusActor(results.actor);
874 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] SetCurrentFocusActor returns %s\n", __FUNCTION__, __LINE__, ret?"TRUE":"FALSE");
882 bool AccessibilityManager::AccessibilityActionReadNext(bool allowEndFeedback)
884 Dali::Toolkit::AccessibilityManager handle( this );
885 if( !mActionReadNextSignal.Empty() )
887 mActionReadNextSignal.Emit( handle );
890 if(mIsAccessibilityTtsEnabled)
892 return MoveFocusForward();
900 bool AccessibilityManager::AccessibilityActionReadPrevious(bool allowEndFeedback)
902 Dali::Toolkit::AccessibilityManager handle( this );
903 if( !mActionReadPreviousSignal.Empty() )
905 mActionReadPreviousSignal.Emit( handle );
908 if(mIsAccessibilityTtsEnabled)
910 return MoveFocusBackward();
918 bool AccessibilityManager::AccessibilityActionUp()
920 Dali::Toolkit::AccessibilityManager handle( this );
921 if( !mActionUpSignal.Empty() )
923 mActionUpSignal.Emit( handle );
928 if(mIsAccessibilityTtsEnabled)
930 Actor actor = GetCurrentFocusActor();
933 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
936 // Notify the control that it is activated
937 ret = GetImplementation( control ).OnAccessibilityValueChange(true);
945 bool AccessibilityManager::AccessibilityActionDown()
947 Dali::Toolkit::AccessibilityManager handle( this );
948 if( !mActionDownSignal.Empty() )
950 mActionDownSignal.Emit( handle );
955 if(mIsAccessibilityTtsEnabled)
957 Actor actor = GetCurrentFocusActor();
960 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
963 // Notify the control that it is activated
964 ret = GetImplementation( control ).OnAccessibilityValueChange(false);
972 bool AccessibilityManager::ClearAccessibilityFocus()
974 Dali::Toolkit::AccessibilityManager handle( this );
975 if( !mActionClearFocusSignal.Empty() )
977 mActionClearFocusSignal.Emit( handle );
980 if(mIsAccessibilityTtsEnabled)
991 bool AccessibilityManager::AccessibilityActionScroll( Dali::TouchEvent& touch )
993 Dali::Toolkit::AccessibilityManager handle( this );
994 if( !mActionScrollSignal.Empty() )
996 mActionScrollSignal.Emit( handle, touch );
1002 bool AccessibilityManager::AccessibilityActionBack()
1004 Dali::Toolkit::AccessibilityManager handle( this );
1005 if( !mActionBackSignal.Empty() )
1007 mActionBackSignal.Emit( handle );
1010 // TODO: Back to previous view
1012 return mIsAccessibilityTtsEnabled;
1015 bool AccessibilityManager::AccessibilityActionScrollUp()
1017 Dali::Toolkit::AccessibilityManager handle( this );
1018 if( !mActionScrollUpSignal.Empty() )
1020 mActionScrollUpSignal.Emit( handle );
1025 if(mIsAccessibilityTtsEnabled)
1027 Actor actor = GetCurrentFocusActor();
1030 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1033 // TODO: Notify the control to scroll up. Should control handle this?
1034 // ret = GetImplementation( control ).OnAccessibilityScroll(Direction::UP);
1042 bool AccessibilityManager::AccessibilityActionScrollDown()
1044 Dali::Toolkit::AccessibilityManager handle( this );
1045 if( !mActionScrollDownSignal.Empty() )
1047 mActionScrollDownSignal.Emit( handle );
1052 if(mIsAccessibilityTtsEnabled)
1054 Actor actor = GetCurrentFocusActor();
1057 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1060 // TODO: Notify the control to scroll down. Should control handle this?
1061 // ret = GetImplementation( control ).OnAccessibilityScrollDown(Direction::DOWN);
1069 bool AccessibilityManager::AccessibilityActionPageLeft()
1071 Dali::Toolkit::AccessibilityManager handle( this );
1072 if( !mActionPageLeftSignal.Empty() )
1074 mActionPageLeftSignal.Emit( handle );
1079 if(mIsAccessibilityTtsEnabled)
1081 Actor actor = GetCurrentFocusActor();
1084 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1087 // TODO: Notify the control to scroll left to the previous page. Should control handle this?
1088 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::LEFT);
1096 bool AccessibilityManager::AccessibilityActionPageRight()
1098 Dali::Toolkit::AccessibilityManager handle( this );
1099 if( !mActionPageRightSignal.Empty() )
1101 mActionPageRightSignal.Emit( handle );
1106 if(mIsAccessibilityTtsEnabled)
1108 Actor actor = GetCurrentFocusActor();
1111 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1114 // TODO: Notify the control to scroll right to the next page. Should control handle this?
1115 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::RIGHT);
1123 bool AccessibilityManager::AccessibilityActionPageUp()
1125 Dali::Toolkit::AccessibilityManager handle( this );
1126 if( !mActionPageUpSignal.Empty() )
1128 mActionPageUpSignal.Emit( handle );
1133 if(mIsAccessibilityTtsEnabled)
1135 Actor actor = GetCurrentFocusActor();
1138 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1141 // TODO: Notify the control to scroll up to the previous page. Should control handle this?
1142 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::UP);
1150 bool AccessibilityManager::AccessibilityActionPageDown()
1152 Dali::Toolkit::AccessibilityManager handle( this );
1153 if( !mActionPageDownSignal.Empty() )
1155 mActionPageDownSignal.Emit( handle );
1160 if(mIsAccessibilityTtsEnabled)
1162 Actor actor = GetCurrentFocusActor();
1165 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1168 // TODO: Notify the control to scroll down to the next page. Should control handle this?
1169 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::DOWN);
1177 bool AccessibilityManager::AccessibilityActionMoveToFirst()
1179 Dali::Toolkit::AccessibilityManager handle( this );
1180 if( !mActionMoveToFirstSignal.Empty() )
1182 mActionMoveToFirstSignal.Emit( handle );
1185 // TODO: Move to the first item on screen
1187 return mIsAccessibilityTtsEnabled;
1190 bool AccessibilityManager::AccessibilityActionMoveToLast()
1192 Dali::Toolkit::AccessibilityManager handle( this );
1193 if( !mActionMoveToLastSignal.Empty() )
1195 mActionMoveToLastSignal.Emit( handle );
1198 // TODO: Move to the last item on screen
1200 return mIsAccessibilityTtsEnabled;
1203 bool AccessibilityManager::AccessibilityActionReadFromTop()
1205 Dali::Toolkit::AccessibilityManager handle( this );
1206 if( !mActionReadFromTopSignal.Empty() )
1208 mActionReadFromTopSignal.Emit( handle );
1211 // TODO: Move to the top item on screen and read from the item continuously
1213 return mIsAccessibilityTtsEnabled;
1216 bool AccessibilityManager::AccessibilityActionReadFromNext()
1218 Dali::Toolkit::AccessibilityManager handle( this );
1220 if( !mActionReadFromNextSignal.Empty() )
1222 mActionReadFromNextSignal.Emit( handle );
1225 if( mIsAccessibilityTtsEnabled )
1227 // Mark that we are in continuous play mode, so TTS signals can move focus.
1228 mContinuousPlayMode = true;
1230 // Attempt to move to the next item and read from the item continuously.
1234 return mIsAccessibilityTtsEnabled;
1237 void AccessibilityManager::TtsStateChanged( const Dali::TtsPlayer::State previousState, const Dali::TtsPlayer::State currentState )
1239 if( mContinuousPlayMode )
1241 // If we were playing and now we have stopped, attempt to play the next item.
1242 if( ( previousState == Dali::TtsPlayer::PLAYING ) && ( currentState == Dali::TtsPlayer::READY ) )
1244 // Attempt to move the focus forward and play.
1245 // If we can't cancel continuous play mode.
1246 if( !MoveFocusForward() )
1248 // We are done, exit continuous play mode.
1249 mContinuousPlayMode = false;
1254 // Unexpected play state change, exit continuous play mode.
1255 mContinuousPlayMode = false;
1260 bool AccessibilityManager::AccessibilityActionZoom()
1262 Dali::Toolkit::AccessibilityManager handle( this );
1263 if( !mActionZoomSignal.Empty() )
1265 mActionZoomSignal.Emit( handle );
1270 if(mIsAccessibilityTtsEnabled)
1272 Actor actor = GetCurrentFocusActor();
1275 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1278 // Notify the control to zoom
1279 ret = GetImplementation( control ).OnAccessibilityZoom();
1287 bool AccessibilityManager::AccessibilityActionReadPauseResume()
1289 Dali::Toolkit::AccessibilityManager handle( this );
1290 if( !mActionReadPauseResumeSignal.Empty() )
1292 mActionReadPauseResumeSignal.Emit( handle );
1297 if(mIsAccessibilityTtsEnabled)
1299 // Pause or resume the TTS player
1300 Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
1301 Dali::TtsPlayer::State state = player.GetState();
1302 if(state == Dali::TtsPlayer::PLAYING)
1307 else if(state == Dali::TtsPlayer::PAUSED)
1317 bool AccessibilityManager::AccessibilityActionStartStop()
1319 Dali::Toolkit::AccessibilityManager handle( this );
1320 if( !mActionStartStopSignal.Empty() )
1322 mActionStartStopSignal.Emit( handle );
1325 // TODO: Start/stop the current action
1327 return mIsAccessibilityTtsEnabled;
1330 bool AccessibilityManager::HandlePanGesture(const AccessibilityGestureEvent& panEvent)
1332 bool handled = false;
1334 if( panEvent.state == AccessibilityGestureEvent::STARTED )
1336 // Find the focusable actor at the event position
1337 Dali::HitTestAlgorithm::Results results;
1338 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
1340 Dali::HitTestAlgorithm::HitTest( Stage::GetCurrent(), panEvent.currentPosition, results, IsActorFocusableFunction );
1341 mCurrentGesturedActor = results.actor;
1343 if(!mCurrentGesturedActor)
1345 DALI_LOG_ERROR("Gesture detected, but no hit actor\n");
1349 // GestureState::FINISHED (Up) events are delivered with previous (Motion) event position
1350 // Use the real previous position; otherwise we may incorrectly get a ZERO velocity
1351 if ( AccessibilityGestureEvent::FINISHED != panEvent.state )
1353 // Store the previous position for next GestureState::FINISHED iteration.
1354 mPreviousPosition = panEvent.previousPosition;
1357 Actor rootActor = Stage::GetCurrent().GetRootLayer();
1359 Dali::PanGesture pan = DevelPanGesture::New( static_cast<Dali::GestureState>(panEvent.state) );
1360 DevelPanGesture::SetTime( pan, panEvent.time );
1361 DevelPanGesture::SetNumberOfTouches( pan, panEvent.numberOfTouches );
1362 DevelPanGesture::SetScreenPosition( pan, panEvent.currentPosition );
1363 DevelPanGesture::SetScreenDisplacement( pan, mPreviousPosition - panEvent.currentPosition );
1364 DevelPanGesture::SetScreenVelocity( pan, Vector2( pan.GetScreenDisplacement().x / panEvent.timeDelta, pan.GetScreenDisplacement().y / panEvent.timeDelta ) );
1366 // Only handle the pan gesture when the current focused actor is scrollable or within a scrollable actor
1367 while(mCurrentGesturedActor && mCurrentGesturedActor != rootActor && !handled)
1369 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(mCurrentGesturedActor);
1372 Vector2 localCurrent;
1373 control.ScreenToLocal( localCurrent.x, localCurrent.y, panEvent.currentPosition.x, panEvent.currentPosition.y );
1374 DevelPanGesture::SetPosition( pan, localCurrent );
1376 Vector2 localPrevious;
1377 control.ScreenToLocal( localPrevious.x, localPrevious.y, mPreviousPosition.x, mPreviousPosition.y );
1379 DevelPanGesture::SetDisplacement( pan, localCurrent - localPrevious );
1380 DevelPanGesture::SetVelocity( pan, Vector2( pan.GetDisplacement().x / panEvent.timeDelta, pan.GetDisplacement().y / panEvent.timeDelta ));
1382 handled = GetImplementation( control ).OnAccessibilityPan(pan);
1385 // If the gesture is not handled by the control, check its parent
1388 mCurrentGesturedActor = mCurrentGesturedActor.GetParent();
1390 if(!mCurrentGesturedActor)
1392 DALI_LOG_ERROR("no more gestured actor\n");
1397 // If handled, then update the pan gesture properties
1398 PanGestureDetector::SetPanGestureProperties( pan );
1405 Toolkit::AccessibilityManager::FocusChangedSignalType& AccessibilityManager::FocusChangedSignal()
1407 return mFocusChangedSignal;
1410 Toolkit::AccessibilityManager::FocusOvershotSignalType& AccessibilityManager::FocusOvershotSignal()
1412 return mFocusOvershotSignal;
1415 Toolkit::AccessibilityManager::FocusedActorActivatedSignalType& AccessibilityManager::FocusedActorActivatedSignal()
1417 return mFocusedActorActivatedSignal;
1420 } // namespace Internal
1422 } // namespace Toolkit