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 Focused actor information
364 Vector2 windowSize = rootActor.GetCurrentProperty<Vector2>(Actor::Property::SIZE);
365 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
366 adaptor.SetFocusedActorPosition( Vector2((actor.GetCurrentProperty<Vector3>(Actor::Property::WORLD_POSITION).x + (windowSize.width / 2)),
367 (actor.GetCurrentProperty<Vector3>(Actor::Property::WORLD_POSITION).y + (windowSize.height / 2))) );
369 // Send notification for the change of focus actor
370 mFocusChangedSignal.Emit( GetCurrentFocusActor(), actor );
372 // Save the current focused actor
373 mCurrentFocusActor = FocusIDPair(GetFocusOrder(actor), actorID);
375 if(mIsAccessibilityTtsEnabled)
377 Dali::SoundPlayer soundPlayer = Dali::SoundPlayer::Get();
380 if (!mIsFocusSoundFilePathSet)
382 const std::string soundDirPath = AssetManager::GetDaliSoundPath();
383 mFocusSoundFilePath = soundDirPath + FOCUS_SOUND_FILE_NAME;
384 mIsFocusSoundFilePathSet = true;
386 soundPlayer.PlaySound(mFocusSoundFilePath);
389 // Play the accessibility attributes with the TTS player.
390 Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
392 // Combine attribute texts to one text
393 std::string informationText;
394 for(int i = 0; i < Toolkit::AccessibilityManager::ACCESSIBILITY_ATTRIBUTE_NUM; i++)
396 if(!GetActorAdditionalInfo(actorID).mAccessibilityAttributes[i].empty())
400 informationText += ", "; // for space time between each information
402 informationText += GetActorAdditionalInfo(actorID).mAccessibilityAttributes[i];
405 player.Play(informationText);
412 DALI_LOG_WARNING("[%s:%d] FAILED\n", __FUNCTION__, __LINE__);
416 Actor AccessibilityManager::GetCurrentFocusActor()
418 Actor rootActor = Stage::GetCurrent().GetRootLayer();
419 return rootActor.FindChildById(mCurrentFocusActor.second);
422 Actor AccessibilityManager::GetCurrentFocusGroup()
424 return GetFocusGroup(GetCurrentFocusActor());
427 unsigned int AccessibilityManager::GetCurrentFocusOrder()
429 return mCurrentFocusActor.first;
432 bool AccessibilityManager::MoveFocusForward()
435 mRecursiveFocusMoveCounter = 0;
437 FocusIDIter focusIDIter = mFocusIDContainer.find(mCurrentFocusActor.first);
438 if(focusIDIter != mFocusIDContainer.end())
440 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
441 ret = DoMoveFocus(focusIDIter, true, mIsWrapped);
445 // TODO: if there is not focused actor, move first actor
446 if(!mFocusIDContainer.empty())
448 //if there is not focused actor, move 1st actor
449 focusIDIter = mFocusIDContainer.begin(); // TODO: I'm not sure it was sorted.
450 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
451 ret = DoSetCurrentFocusActor((*focusIDIter).second);
455 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s] %s\n", __FUNCTION__, ret?"SUCCEED!!!":"FAILED!!!");
460 bool AccessibilityManager::MoveFocusBackward()
463 mRecursiveFocusMoveCounter = 0;
465 FocusIDIter focusIDIter = mFocusIDContainer.find(mCurrentFocusActor.first);
466 if(focusIDIter != mFocusIDContainer.end())
468 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
469 ret = DoMoveFocus(focusIDIter, false, mIsWrapped);
473 // TODO: if there is not focused actor, move last actor
474 if(!mFocusIDContainer.empty())
476 //if there is not focused actor, move last actor
477 focusIDIter = mFocusIDContainer.end();
478 --focusIDIter;//We want forward iterator to the last element here
479 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
480 ret = DoSetCurrentFocusActor((*focusIDIter).second);
484 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s] %s\n", __FUNCTION__, ret?"SUCCEED!!!":"FAILED!!!");
489 void AccessibilityManager::DoActivate(Actor actor)
493 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
496 // Notify the control that it is activated
497 GetImplementation( control ).AccessibilityActivate();
500 // Send notification for the activation of focused actor
501 mFocusedActorActivatedSignal.Emit(actor);
505 void AccessibilityManager::ClearFocus()
507 Actor actor = GetCurrentFocusActor();
508 if( actor && mFocusIndicatorActor )
510 actor.Remove( mFocusIndicatorActor );
513 mCurrentFocusActor = FocusIDPair(0, 0);
515 // Send notification for the change of focus actor
516 mFocusChangedSignal.Emit(actor, Actor());
518 if(mIsAccessibilityTtsEnabled)
520 // Stop the TTS playing if any
521 Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
526 void AccessibilityManager::Reset()
529 mFocusIDContainer.clear();
530 mIDAdditionalInfoContainer.clear();
533 void AccessibilityManager::SetFocusGroup(Actor actor, bool isFocusGroup)
537 // Create/Set focus group property.
538 actor.RegisterProperty( IS_FOCUS_GROUP, isFocusGroup, Property::READ_WRITE );
542 bool AccessibilityManager::IsFocusGroup(Actor actor) const
544 // Check whether the actor is a focus group
545 bool isFocusGroup = false;
549 Property::Index propertyIsFocusGroup = actor.GetPropertyIndex(IS_FOCUS_GROUP);
550 if(propertyIsFocusGroup != Property::INVALID_INDEX)
552 isFocusGroup = actor.GetProperty<bool>(propertyIsFocusGroup);
559 Actor AccessibilityManager::GetFocusGroup(Actor actor)
561 // Go through the actor's hierarchy to check which focus group the actor belongs to
562 while (actor && !IsFocusGroup(actor))
564 actor = actor.GetParent();
570 Vector2 AccessibilityManager::GetReadPosition() const
572 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
573 return adaptor.GetReadPosition();
576 void AccessibilityManager::EnableAccessibility(bool enabled)
578 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] Set Enabled Forcibly : %d \n", __FUNCTION__, __LINE__, enabled );
579 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
580 adaptor.EnableAccessibility(enabled);
583 bool AccessibilityManager::IsEnabled() const
585 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
586 return adaptor.IsEnabled();
589 void AccessibilityManager::SetGroupMode(bool enabled)
591 mIsFocusWithinGroup = enabled;
594 bool AccessibilityManager::GetGroupMode() const
596 return mIsFocusWithinGroup;
599 void AccessibilityManager::SetWrapMode(bool wrapped)
601 mIsWrapped = wrapped;
604 bool AccessibilityManager::GetWrapMode() const
609 void AccessibilityManager::SetFocusIndicatorActor(Actor indicator)
611 if( mFocusIndicatorActor != indicator )
613 Actor currentFocusActor = GetCurrentFocusActor();
614 if( currentFocusActor )
616 // The new focus indicator should be added to the current focused actor immediately
617 if( mFocusIndicatorActor )
619 currentFocusActor.Remove( mFocusIndicatorActor );
624 currentFocusActor.Add( indicator );
628 mFocusIndicatorActor = indicator;
632 Actor AccessibilityManager::GetFocusIndicatorActor()
634 if( ! mFocusIndicatorActor )
636 // Create the default if it hasn't been set and one that's shared by all the keyboard focusable actors
637 const std::string imageDirPath = AssetManager::GetDaliImagePath();
638 const std::string focusBorderImagePath = imageDirPath + FOCUS_BORDER_IMAGE_FILE_NAME;
640 mFocusIndicatorActor = Toolkit::ImageView::New(focusBorderImagePath);
641 mFocusIndicatorActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
642 mFocusIndicatorActor.SetProperty( Actor::Property::POSITION_Z, 1.0f );
644 // Apply size constraint to the focus indicator
645 mFocusIndicatorActor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
648 return mFocusIndicatorActor;
651 bool AccessibilityManager::DoMoveFocus(FocusIDIter focusIDIter, bool forward, bool wrapped)
653 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] %d focusable actors\n", __FUNCTION__, __LINE__, mFocusIDContainer.size());
654 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
656 if( (forward && ++focusIDIter == mFocusIDContainer.end())
657 || (!forward && focusIDIter-- == mFocusIDContainer.begin()) )
659 if(mIsEndcapFeedbackEnabled)
661 if(mIsEndcapFeedbackPlayed == false)
663 // play sound & skip moving once
664 Dali::SoundPlayer soundPlayer = Dali::SoundPlayer::Get();
667 if (!mIsFocusChainEndSoundFilePathSet)
669 const std::string soundDirPath = AssetManager::GetDaliSoundPath();
670 mFocusChainEndSoundFilePath = soundDirPath + FOCUS_CHAIN_END_SOUND_FILE_NAME;
671 mIsFocusChainEndSoundFilePathSet = true;
673 soundPlayer.PlaySound(mFocusChainEndSoundFilePath);
676 mIsEndcapFeedbackPlayed = true;
679 mIsEndcapFeedbackPlayed = false;
686 focusIDIter = mFocusIDContainer.begin();
690 focusIDIter = mFocusIDContainer.end();
691 --focusIDIter;//We want forward iterator to the last element here
696 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] Overshot\n", __FUNCTION__, __LINE__);
697 // Send notification for handling overshooted situation
698 mFocusOvershotSignal.Emit(GetCurrentFocusActor(), forward ? Toolkit::AccessibilityManager::OVERSHOT_NEXT : Toolkit::AccessibilityManager::OVERSHOT_PREVIOUS);
700 return false; // Try to move the focus out of the scope
705 if( focusIDIter == mFocusIDContainer.end() )
710 // Note: This function performs the focus change.
711 if( !DoSetCurrentFocusActor( (*focusIDIter).second ) )
713 mRecursiveFocusMoveCounter++;
714 if(mRecursiveFocusMoveCounter > mFocusIDContainer.size())
716 // We've attempted to focus all the actors in the whole focus chain and no actor
717 // can be focused successfully.
718 DALI_LOG_WARNING("[%s] There is no more focusable actor in %d focus chains\n", __FUNCTION__, mRecursiveFocusMoveCounter);
724 return DoMoveFocus(focusIDIter, forward, wrapped);
731 void AccessibilityManager::SetFocusable(Actor actor, bool focusable)
735 // Create/Set actor focusable property.
736 actor.RegisterProperty( ACTOR_FOCUSABLE, focusable, Property::READ_WRITE );
740 bool AccessibilityManager::ChangeAccessibilityStatus()
742 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
743 mIsAccessibilityTtsEnabled = adaptor.IsEnabled();
744 Dali::Toolkit::AccessibilityManager handle( this );
746 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] TtsEnabled : %d \n", __FUNCTION__, __LINE__, mIsAccessibilityTtsEnabled );
748 if(mIsAccessibilityTtsEnabled)
750 // Show indicator when tts turned on if there is focused actor.
751 Actor actor = GetCurrentFocusActor();
754 actor.Add( GetFocusIndicatorActor() );
756 mIsFocusIndicatorEnabled = true;
758 // Connect a signal to the TTS player to implement continuous reading mode.
759 Dali::TtsPlayer player = Dali::TtsPlayer::Get( Dali::TtsPlayer::SCREEN_READER );
760 player.StateChangedSignal().Connect( this, &AccessibilityManager::TtsStateChanged );
765 // Hide indicator when tts turned off
766 Actor actor = GetCurrentFocusActor();
767 if( actor && mFocusIndicatorActor )
769 actor.Remove( mFocusIndicatorActor );
771 mIsFocusIndicatorEnabled = false;
775 // Disconnect the TTS state change signal.
776 Dali::TtsPlayer player = Dali::TtsPlayer::Get( Dali::TtsPlayer::SCREEN_READER );
777 player.StateChangedSignal().Disconnect( this, &AccessibilityManager::TtsStateChanged );
782 mStatusChangedSignal.Emit( handle );
787 bool AccessibilityManager::AccessibilityActionNext(bool allowEndFeedback)
789 Dali::Toolkit::AccessibilityManager handle( this );
790 if( !mActionNextSignal.Empty() )
792 mActionNextSignal.Emit( handle );
795 if(mIsAccessibilityTtsEnabled)
797 mIsEndcapFeedbackEnabled = allowEndFeedback;
798 return MoveFocusForward();
806 bool AccessibilityManager::AccessibilityActionPrevious(bool allowEndFeedback)
808 Dali::Toolkit::AccessibilityManager handle( this );
809 if( !mActionPreviousSignal.Empty() )
811 mActionPreviousSignal.Emit( handle );
814 if(mIsAccessibilityTtsEnabled)
816 mIsEndcapFeedbackEnabled = allowEndFeedback;
817 return MoveFocusBackward();
825 bool AccessibilityManager::AccessibilityActionActivate()
827 Dali::Toolkit::AccessibilityManager handle( this );
828 if( !mActionActivateSignal.Empty() )
830 mActionActivateSignal.Emit( handle );
835 Actor actor = GetCurrentFocusActor();
845 bool AccessibilityManager::AccessibilityActionRead(bool allowReadAgain)
847 Dali::Toolkit::AccessibilityManager handle( this );
851 if ( !mActionReadSignal.Empty() )
853 mActionReadSignal.Emit( handle );
858 if ( !mActionOverSignal.Empty() )
860 mActionOverSignal.Emit( handle );
866 if(mIsAccessibilityTtsEnabled)
868 // Find the focusable actor at the read position
869 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
870 Dali::HitTestAlgorithm::Results results;
871 Dali::HitTestAlgorithm::HitTest( Stage::GetCurrent(), adaptor.GetReadPosition(), results, IsActorFocusableFunction );
873 FocusIDIter focusIDIter = mFocusIDContainer.find(GetFocusOrder(results.actor));
874 if(focusIDIter != mFocusIDContainer.end())
876 if( allowReadAgain || (results.actor != GetCurrentFocusActor()) )
878 // Move the focus to the actor
879 ret = SetCurrentFocusActor(results.actor);
880 DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] SetCurrentFocusActor returns %s\n", __FUNCTION__, __LINE__, ret?"TRUE":"FALSE");
888 bool AccessibilityManager::AccessibilityActionReadNext(bool allowEndFeedback)
890 Dali::Toolkit::AccessibilityManager handle( this );
891 if( !mActionReadNextSignal.Empty() )
893 mActionReadNextSignal.Emit( handle );
896 if(mIsAccessibilityTtsEnabled)
898 return MoveFocusForward();
906 bool AccessibilityManager::AccessibilityActionReadPrevious(bool allowEndFeedback)
908 Dali::Toolkit::AccessibilityManager handle( this );
909 if( !mActionReadPreviousSignal.Empty() )
911 mActionReadPreviousSignal.Emit( handle );
914 if(mIsAccessibilityTtsEnabled)
916 return MoveFocusBackward();
924 bool AccessibilityManager::AccessibilityActionUp()
926 Dali::Toolkit::AccessibilityManager handle( this );
927 if( !mActionUpSignal.Empty() )
929 mActionUpSignal.Emit( handle );
934 if(mIsAccessibilityTtsEnabled)
936 Actor actor = GetCurrentFocusActor();
939 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
942 // Notify the control that it is activated
943 ret = GetImplementation( control ).OnAccessibilityValueChange(true);
951 bool AccessibilityManager::AccessibilityActionDown()
953 Dali::Toolkit::AccessibilityManager handle( this );
954 if( !mActionDownSignal.Empty() )
956 mActionDownSignal.Emit( handle );
961 if(mIsAccessibilityTtsEnabled)
963 Actor actor = GetCurrentFocusActor();
966 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
969 // Notify the control that it is activated
970 ret = GetImplementation( control ).OnAccessibilityValueChange(false);
978 bool AccessibilityManager::ClearAccessibilityFocus()
980 Dali::Toolkit::AccessibilityManager handle( this );
981 if( !mActionClearFocusSignal.Empty() )
983 mActionClearFocusSignal.Emit( handle );
986 if(mIsAccessibilityTtsEnabled)
997 bool AccessibilityManager::AccessibilityActionScroll( Dali::TouchEvent& touch )
999 Dali::Toolkit::AccessibilityManager handle( this );
1000 if( !mActionScrollSignal.Empty() )
1002 mActionScrollSignal.Emit( handle, touch );
1008 bool AccessibilityManager::AccessibilityActionBack()
1010 Dali::Toolkit::AccessibilityManager handle( this );
1011 if( !mActionBackSignal.Empty() )
1013 mActionBackSignal.Emit( handle );
1016 // TODO: Back to previous view
1018 return mIsAccessibilityTtsEnabled;
1021 bool AccessibilityManager::AccessibilityActionScrollUp()
1023 Dali::Toolkit::AccessibilityManager handle( this );
1024 if( !mActionScrollUpSignal.Empty() )
1026 mActionScrollUpSignal.Emit( handle );
1031 if(mIsAccessibilityTtsEnabled)
1033 Actor actor = GetCurrentFocusActor();
1036 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1039 // TODO: Notify the control to scroll up. Should control handle this?
1040 // ret = GetImplementation( control ).OnAccessibilityScroll(Direction::UP);
1048 bool AccessibilityManager::AccessibilityActionScrollDown()
1050 Dali::Toolkit::AccessibilityManager handle( this );
1051 if( !mActionScrollDownSignal.Empty() )
1053 mActionScrollDownSignal.Emit( handle );
1058 if(mIsAccessibilityTtsEnabled)
1060 Actor actor = GetCurrentFocusActor();
1063 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1066 // TODO: Notify the control to scroll down. Should control handle this?
1067 // ret = GetImplementation( control ).OnAccessibilityScrollDown(Direction::DOWN);
1075 bool AccessibilityManager::AccessibilityActionPageLeft()
1077 Dali::Toolkit::AccessibilityManager handle( this );
1078 if( !mActionPageLeftSignal.Empty() )
1080 mActionPageLeftSignal.Emit( handle );
1085 if(mIsAccessibilityTtsEnabled)
1087 Actor actor = GetCurrentFocusActor();
1090 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1093 // TODO: Notify the control to scroll left to the previous page. Should control handle this?
1094 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::LEFT);
1102 bool AccessibilityManager::AccessibilityActionPageRight()
1104 Dali::Toolkit::AccessibilityManager handle( this );
1105 if( !mActionPageRightSignal.Empty() )
1107 mActionPageRightSignal.Emit( handle );
1112 if(mIsAccessibilityTtsEnabled)
1114 Actor actor = GetCurrentFocusActor();
1117 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1120 // TODO: Notify the control to scroll right to the next page. Should control handle this?
1121 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::RIGHT);
1129 bool AccessibilityManager::AccessibilityActionPageUp()
1131 Dali::Toolkit::AccessibilityManager handle( this );
1132 if( !mActionPageUpSignal.Empty() )
1134 mActionPageUpSignal.Emit( handle );
1139 if(mIsAccessibilityTtsEnabled)
1141 Actor actor = GetCurrentFocusActor();
1144 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1147 // TODO: Notify the control to scroll up to the previous page. Should control handle this?
1148 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::UP);
1156 bool AccessibilityManager::AccessibilityActionPageDown()
1158 Dali::Toolkit::AccessibilityManager handle( this );
1159 if( !mActionPageDownSignal.Empty() )
1161 mActionPageDownSignal.Emit( handle );
1166 if(mIsAccessibilityTtsEnabled)
1168 Actor actor = GetCurrentFocusActor();
1171 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1174 // TODO: Notify the control to scroll down to the next page. Should control handle this?
1175 // ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::DOWN);
1183 bool AccessibilityManager::AccessibilityActionMoveToFirst()
1185 Dali::Toolkit::AccessibilityManager handle( this );
1186 if( !mActionMoveToFirstSignal.Empty() )
1188 mActionMoveToFirstSignal.Emit( handle );
1191 // TODO: Move to the first item on screen
1193 return mIsAccessibilityTtsEnabled;
1196 bool AccessibilityManager::AccessibilityActionMoveToLast()
1198 Dali::Toolkit::AccessibilityManager handle( this );
1199 if( !mActionMoveToLastSignal.Empty() )
1201 mActionMoveToLastSignal.Emit( handle );
1204 // TODO: Move to the last item on screen
1206 return mIsAccessibilityTtsEnabled;
1209 bool AccessibilityManager::AccessibilityActionReadFromTop()
1211 Dali::Toolkit::AccessibilityManager handle( this );
1212 if( !mActionReadFromTopSignal.Empty() )
1214 mActionReadFromTopSignal.Emit( handle );
1217 // TODO: Move to the top item on screen and read from the item continuously
1219 return mIsAccessibilityTtsEnabled;
1222 bool AccessibilityManager::AccessibilityActionReadFromNext()
1224 Dali::Toolkit::AccessibilityManager handle( this );
1226 if( !mActionReadFromNextSignal.Empty() )
1228 mActionReadFromNextSignal.Emit( handle );
1231 if( mIsAccessibilityTtsEnabled )
1233 // Mark that we are in continuous play mode, so TTS signals can move focus.
1234 mContinuousPlayMode = true;
1236 // Attempt to move to the next item and read from the item continuously.
1240 return mIsAccessibilityTtsEnabled;
1243 void AccessibilityManager::TtsStateChanged( const Dali::TtsPlayer::State previousState, const Dali::TtsPlayer::State currentState )
1245 if( mContinuousPlayMode )
1247 // If we were playing and now we have stopped, attempt to play the next item.
1248 if( ( previousState == Dali::TtsPlayer::PLAYING ) && ( currentState == Dali::TtsPlayer::READY ) )
1250 // Attempt to move the focus forward and play.
1251 // If we can't cancel continuous play mode.
1252 if( !MoveFocusForward() )
1254 // We are done, exit continuous play mode.
1255 mContinuousPlayMode = false;
1260 // Unexpected play state change, exit continuous play mode.
1261 mContinuousPlayMode = false;
1266 bool AccessibilityManager::AccessibilityActionZoom()
1268 Dali::Toolkit::AccessibilityManager handle( this );
1269 if( !mActionZoomSignal.Empty() )
1271 mActionZoomSignal.Emit( handle );
1276 if(mIsAccessibilityTtsEnabled)
1278 Actor actor = GetCurrentFocusActor();
1281 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
1284 // Notify the control to zoom
1285 ret = GetImplementation( control ).OnAccessibilityZoom();
1293 bool AccessibilityManager::AccessibilityActionReadPauseResume()
1295 Dali::Toolkit::AccessibilityManager handle( this );
1296 if( !mActionReadPauseResumeSignal.Empty() )
1298 mActionReadPauseResumeSignal.Emit( handle );
1303 if(mIsAccessibilityTtsEnabled)
1305 // Pause or resume the TTS player
1306 Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
1307 Dali::TtsPlayer::State state = player.GetState();
1308 if(state == Dali::TtsPlayer::PLAYING)
1313 else if(state == Dali::TtsPlayer::PAUSED)
1323 bool AccessibilityManager::AccessibilityActionStartStop()
1325 Dali::Toolkit::AccessibilityManager handle( this );
1326 if( !mActionStartStopSignal.Empty() )
1328 mActionStartStopSignal.Emit( handle );
1331 // TODO: Start/stop the current action
1333 return mIsAccessibilityTtsEnabled;
1336 bool AccessibilityManager::HandlePanGesture(const AccessibilityGestureEvent& panEvent)
1338 bool handled = false;
1340 if( panEvent.state == AccessibilityGestureEvent::STARTED )
1342 // Find the focusable actor at the event position
1343 Dali::HitTestAlgorithm::Results results;
1344 AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
1346 Dali::HitTestAlgorithm::HitTest( Stage::GetCurrent(), panEvent.currentPosition, results, IsActorFocusableFunction );
1347 mCurrentGesturedActor = results.actor;
1349 if(!mCurrentGesturedActor)
1351 DALI_LOG_ERROR("Gesture detected, but no hit actor\n");
1355 // GestureState::FINISHED (Up) events are delivered with previous (Motion) event position
1356 // Use the real previous position; otherwise we may incorrectly get a ZERO velocity
1357 if ( AccessibilityGestureEvent::FINISHED != panEvent.state )
1359 // Store the previous position for next GestureState::FINISHED iteration.
1360 mPreviousPosition = panEvent.previousPosition;
1363 Actor rootActor = Stage::GetCurrent().GetRootLayer();
1365 Dali::PanGesture pan = DevelPanGesture::New( static_cast<Dali::GestureState>(panEvent.state) );
1366 DevelPanGesture::SetTime( pan, panEvent.time );
1367 DevelPanGesture::SetNumberOfTouches( pan, panEvent.numberOfTouches );
1368 DevelPanGesture::SetScreenPosition( pan, panEvent.currentPosition );
1369 DevelPanGesture::SetScreenDisplacement( pan, mPreviousPosition - panEvent.currentPosition );
1370 DevelPanGesture::SetScreenVelocity( pan, Vector2( pan.GetScreenDisplacement().x / panEvent.timeDelta, pan.GetScreenDisplacement().y / panEvent.timeDelta ) );
1372 // Only handle the pan gesture when the current focused actor is scrollable or within a scrollable actor
1373 while(mCurrentGesturedActor && mCurrentGesturedActor != rootActor && !handled)
1375 Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(mCurrentGesturedActor);
1378 Vector2 localCurrent;
1379 control.ScreenToLocal( localCurrent.x, localCurrent.y, panEvent.currentPosition.x, panEvent.currentPosition.y );
1380 DevelPanGesture::SetPosition( pan, localCurrent );
1382 Vector2 localPrevious;
1383 control.ScreenToLocal( localPrevious.x, localPrevious.y, mPreviousPosition.x, mPreviousPosition.y );
1385 DevelPanGesture::SetDisplacement( pan, localCurrent - localPrevious );
1386 DevelPanGesture::SetVelocity( pan, Vector2( pan.GetDisplacement().x / panEvent.timeDelta, pan.GetDisplacement().y / panEvent.timeDelta ));
1388 handled = GetImplementation( control ).OnAccessibilityPan(pan);
1391 // If the gesture is not handled by the control, check its parent
1394 mCurrentGesturedActor = mCurrentGesturedActor.GetParent();
1396 if(!mCurrentGesturedActor)
1398 DALI_LOG_ERROR("no more gestured actor\n");
1403 // If handled, then update the pan gesture properties
1404 PanGestureDetector::SetPanGestureProperties( pan );
1411 Toolkit::AccessibilityManager::FocusChangedSignalType& AccessibilityManager::FocusChangedSignal()
1413 return mFocusChangedSignal;
1416 Toolkit::AccessibilityManager::FocusOvershotSignalType& AccessibilityManager::FocusOvershotSignal()
1418 return mFocusOvershotSignal;
1421 Toolkit::AccessibilityManager::FocusedActorActivatedSignalType& AccessibilityManager::FocusedActorActivatedSignal()
1423 return mFocusedActorActivatedSignal;
1426 } // namespace Internal
1428 } // namespace Toolkit