+ if( ! mFocusIndicatorActor )
+ {
+ // Create the default if it hasn't been set and one that's shared by all the keyboard focusable actors
+ mFocusIndicatorActor = Toolkit::ImageView::New( FOCUS_BORDER_IMAGE_PATH );
+ mFocusIndicatorActor.SetParentOrigin( ParentOrigin::CENTER );
+ mFocusIndicatorActor.SetZ( 1.0f );
+
+ // Apply size constraint to the focus indicator
+ mFocusIndicatorActor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
+ }
+
+ return mFocusIndicatorActor;
+}
+
+bool AccessibilityManager::DoMoveFocus(FocusIDIter focusIDIter, bool forward, bool wrapped)
+{
+ DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] %d focusable actors\n", __FUNCTION__, __LINE__, mFocusIDContainer.size());
+ DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first);
+
+ if( (forward && ++focusIDIter == mFocusIDContainer.end())
+ || (!forward && focusIDIter-- == mFocusIDContainer.begin()) )
+ {
+ if(mIsEndcapFeedbackEnabled)
+ {
+ if(mIsEndcapFeedbackPlayed == false)
+ {
+ // play sound & skip moving once
+ Dali::SoundPlayer soundPlayer = Dali::SoundPlayer::Get();
+ if(soundPlayer)
+ {
+ soundPlayer.PlaySound(FOCUS_CHAIN_END_SOUND_FILE);
+ }
+
+ mIsEndcapFeedbackPlayed = true;
+ return true;
+ }
+ mIsEndcapFeedbackPlayed = false;
+ }
+
+ if(wrapped)
+ {
+ if(forward)
+ {
+ focusIDIter = mFocusIDContainer.begin();
+ }
+ else
+ {
+ focusIDIter = mFocusIDContainer.end();
+ --focusIDIter;//We want forward iterator to the last element here
+ }
+ }
+ else
+ {
+ DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] Overshot\n", __FUNCTION__, __LINE__);
+ // Send notification for handling overshooted situation
+ mFocusOvershotSignal.Emit(GetCurrentFocusActor(), forward ? Toolkit::AccessibilityManager::OVERSHOT_NEXT : Toolkit::AccessibilityManager::OVERSHOT_PREVIOUS);
+
+ return false; // Try to move the focus out of the scope
+ }
+ }
+
+ // Invalid focus.
+ if( focusIDIter == mFocusIDContainer.end() )
+ {
+ return false;
+ }
+
+ // Note: This function performs the focus change.
+ if( !DoSetCurrentFocusActor( (*focusIDIter).second ) )
+ {
+ mRecursiveFocusMoveCounter++;
+ if(mRecursiveFocusMoveCounter > mFocusIDContainer.size())
+ {
+ // We've attempted to focus all the actors in the whole focus chain and no actor
+ // can be focused successfully.
+ DALI_LOG_WARNING("[%s] There is no more focusable actor in %d focus chains\n", __FUNCTION__, mRecursiveFocusMoveCounter);
+
+ return false;
+ }
+ else
+ {
+ return DoMoveFocus(focusIDIter, forward, wrapped);
+ }
+ }
+
+ return true;
+}
+
+void AccessibilityManager::SetFocusable(Actor actor, bool focusable)
+{
+ if(actor)
+ {
+ // Create/Set actor focusable property.
+ actor.RegisterProperty( ACTOR_FOCUSABLE, focusable, Property::READ_WRITE );
+ }
+}
+
+bool AccessibilityManager::ChangeAccessibilityStatus()
+{
+ AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
+ mIsAccessibilityTtsEnabled = adaptor.IsEnabled();
+ Dali::Toolkit::AccessibilityManager handle( this );
+
+ if(mIsAccessibilityTtsEnabled)
+ {
+ // Show indicator when tts turned on if there is focused actor.
+ Actor actor = GetCurrentFocusActor();
+ if(actor)
+ {
+ actor.Add( GetFocusIndicatorActor() );
+ }
+ mIsFocusIndicatorEnabled = true;
+
+ // Connect a signal to the TTS player to implement continuous reading mode.
+ Dali::TtsPlayer player = Dali::TtsPlayer::Get( Dali::TtsPlayer::SCREEN_READER );
+ player.StateChangedSignal().Connect( this, &AccessibilityManager::TtsStateChanged );
+ mTtsCreated = true;
+ }
+ else
+ {
+ // Hide indicator when tts turned off
+ Actor actor = GetCurrentFocusActor();
+ if( actor && mFocusIndicatorActor )
+ {
+ actor.Remove( mFocusIndicatorActor );
+ }
+ mIsFocusIndicatorEnabled = false;
+
+ if( mTtsCreated )
+ {
+ // Disconnect the TTS state change signal.
+ Dali::TtsPlayer player = Dali::TtsPlayer::Get( Dali::TtsPlayer::SCREEN_READER );
+ player.StateChangedSignal().Disconnect( this, &AccessibilityManager::TtsStateChanged );
+ mTtsCreated = true;
+ }
+ }
+
+ mStatusChangedSignal.Emit( handle );
+
+ return true;
+}
+
+bool AccessibilityManager::AccessibilityActionNext(bool allowEndFeedback)
+{
+ Dali::Toolkit::AccessibilityManager handle( this );
+ if( !mActionNextSignal.Empty() )
+ {
+ mActionNextSignal.Emit( handle );
+ }
+
+ if(mIsAccessibilityTtsEnabled)
+ {
+ mIsEndcapFeedbackEnabled = allowEndFeedback;
+ return MoveFocusForward();
+ }
+ else
+ {
+ return false;
+ }
+}
+
+bool AccessibilityManager::AccessibilityActionPrevious(bool allowEndFeedback)
+{
+ Dali::Toolkit::AccessibilityManager handle( this );
+ if( !mActionPreviousSignal.Empty() )
+ {
+ mActionPreviousSignal.Emit( handle );
+ }
+
+ if(mIsAccessibilityTtsEnabled)
+ {
+ mIsEndcapFeedbackEnabled = allowEndFeedback;
+ return MoveFocusBackward();
+ }
+ else
+ {
+ return false;
+ }
+}
+
+bool AccessibilityManager::AccessibilityActionActivate()
+{
+ Dali::Toolkit::AccessibilityManager handle( this );
+ if( !mActionActivateSignal.Empty() )
+ {
+ mActionActivateSignal.Emit( handle );
+ }
+
+ bool ret = false;
+
+ Actor actor = GetCurrentFocusActor();
+ if(actor)
+ {
+ DoActivate(actor);
+ ret = true;
+ }
+
+ return ret;
+}
+
+bool AccessibilityManager::AccessibilityActionRead(bool allowReadAgain)
+{
+ Dali::Toolkit::AccessibilityManager handle( this );
+
+ if( allowReadAgain )
+ {
+ if ( !mActionReadSignal.Empty() )
+ {
+ mActionReadSignal.Emit( handle );
+ }
+ }
+ else
+ {
+ if ( !mActionOverSignal.Empty() )
+ {
+ mActionOverSignal.Emit( handle );
+ }
+ }
+
+ bool ret = false;
+
+ if(mIsAccessibilityTtsEnabled)
+ {
+ // Find the focusable actor at the read position
+ AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
+ Dali::HitTestAlgorithm::Results results;
+ Dali::HitTestAlgorithm::HitTest( Stage::GetCurrent(), adaptor.GetReadPosition(), results, IsActorFocusableFunction );
+
+ FocusIDIter focusIDIter = mFocusIDContainer.find(GetFocusOrder(results.actor));
+ if(focusIDIter != mFocusIDContainer.end())
+ {
+ if( allowReadAgain || (results.actor != GetCurrentFocusActor()) )
+ {
+ // Move the focus to the actor
+ ret = SetCurrentFocusActor(results.actor);
+ DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] SetCurrentFocusActor returns %s\n", __FUNCTION__, __LINE__, ret?"TRUE":"FALSE");
+ }
+ }
+ }
+
+ return ret;
+}
+
+bool AccessibilityManager::AccessibilityActionReadNext(bool allowEndFeedback)
+{
+ Dali::Toolkit::AccessibilityManager handle( this );
+ if( !mActionReadNextSignal.Empty() )
+ {
+ mActionReadNextSignal.Emit( handle );
+ }
+
+ if(mIsAccessibilityTtsEnabled)
+ {
+ return MoveFocusForward();
+ }
+ else
+ {
+ return false;
+ }
+}
+
+bool AccessibilityManager::AccessibilityActionReadPrevious(bool allowEndFeedback)
+{
+ Dali::Toolkit::AccessibilityManager handle( this );
+ if( !mActionReadPreviousSignal.Empty() )
+ {
+ mActionReadPreviousSignal.Emit( handle );
+ }
+
+ if(mIsAccessibilityTtsEnabled)
+ {
+ return MoveFocusBackward();
+ }
+ else
+ {
+ return false;
+ }
+}
+
+bool AccessibilityManager::AccessibilityActionUp()
+{
+ Dali::Toolkit::AccessibilityManager handle( this );
+ if( !mActionUpSignal.Empty() )
+ {
+ mActionUpSignal.Emit( handle );
+ }
+
+ bool ret = false;
+
+ if(mIsAccessibilityTtsEnabled)
+ {
+ Actor actor = GetCurrentFocusActor();
+ if(actor)
+ {
+ Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
+ if(control)
+ {
+ // Notify the control that it is activated
+ ret = GetImplementation( control ).OnAccessibilityValueChange(true);
+ }
+ }
+ }
+
+ return ret;
+}
+
+bool AccessibilityManager::AccessibilityActionDown()
+{
+ Dali::Toolkit::AccessibilityManager handle( this );
+ if( !mActionDownSignal.Empty() )
+ {
+ mActionDownSignal.Emit( handle );
+ }
+
+ bool ret = false;
+
+ if(mIsAccessibilityTtsEnabled)
+ {
+ Actor actor = GetCurrentFocusActor();
+ if(actor)
+ {
+ Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
+ if(control)
+ {
+ // Notify the control that it is activated
+ ret = GetImplementation( control ).OnAccessibilityValueChange(false);
+ }
+ }
+ }
+
+ return ret;
+}
+
+bool AccessibilityManager::ClearAccessibilityFocus()
+{
+ Dali::Toolkit::AccessibilityManager handle( this );
+ if( !mActionClearFocusSignal.Empty() )
+ {
+ mActionClearFocusSignal.Emit( handle );
+ }
+
+ if(mIsAccessibilityTtsEnabled)
+ {
+ ClearFocus();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+bool AccessibilityManager::AccessibilityActionScroll( Dali::TouchEvent& touchEvent )
+{
+ Dali::Toolkit::AccessibilityManager handle( this );
+ if( !mActionScrollSignal.Empty() )
+ {
+ mActionScrollSignal.Emit( handle, touchEvent );
+ }
+
+ return true;
+}
+
+bool AccessibilityManager::AccessibilityActionBack()
+{
+ Dali::Toolkit::AccessibilityManager handle( this );
+ if( !mActionBackSignal.Empty() )
+ {
+ mActionBackSignal.Emit( handle );
+ }
+
+ // TODO: Back to previous view
+
+ return mIsAccessibilityTtsEnabled;
+}
+
+bool AccessibilityManager::AccessibilityActionScrollUp()
+{
+ Dali::Toolkit::AccessibilityManager handle( this );
+ if( !mActionScrollUpSignal.Empty() )
+ {
+ mActionScrollUpSignal.Emit( handle );
+ }
+
+ bool ret = false;
+
+ if(mIsAccessibilityTtsEnabled)
+ {
+ Actor actor = GetCurrentFocusActor();
+ if(actor)
+ {
+ Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
+ if(control)
+ {
+ // TODO: Notify the control to scroll up. Should control handle this?
+// ret = GetImplementation( control ).OnAccessibilityScroll(Direction::UP);
+ }
+ }
+ }
+
+ return ret;
+}
+
+bool AccessibilityManager::AccessibilityActionScrollDown()
+{
+ Dali::Toolkit::AccessibilityManager handle( this );
+ if( !mActionScrollDownSignal.Empty() )
+ {
+ mActionScrollDownSignal.Emit( handle );
+ }
+
+ bool ret = false;
+
+ if(mIsAccessibilityTtsEnabled)
+ {
+ Actor actor = GetCurrentFocusActor();
+ if(actor)
+ {
+ Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
+ if(control)
+ {
+ // TODO: Notify the control to scroll down. Should control handle this?
+// ret = GetImplementation( control ).OnAccessibilityScrollDown(Direction::DOWN);
+ }
+ }
+ }
+
+ return ret;
+}
+
+bool AccessibilityManager::AccessibilityActionPageLeft()
+{
+ Dali::Toolkit::AccessibilityManager handle( this );
+ if( !mActionPageLeftSignal.Empty() )
+ {
+ mActionPageLeftSignal.Emit( handle );
+ }
+
+ bool ret = false;
+
+ if(mIsAccessibilityTtsEnabled)
+ {
+ Actor actor = GetCurrentFocusActor();
+ if(actor)
+ {
+ Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
+ if(control)
+ {
+ // TODO: Notify the control to scroll left to the previous page. Should control handle this?
+// ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::LEFT);
+ }
+ }
+ }
+
+ return ret;
+}
+
+bool AccessibilityManager::AccessibilityActionPageRight()
+{
+ Dali::Toolkit::AccessibilityManager handle( this );
+ if( !mActionPageRightSignal.Empty() )
+ {
+ mActionPageRightSignal.Emit( handle );
+ }
+
+ bool ret = false;
+
+ if(mIsAccessibilityTtsEnabled)
+ {
+ Actor actor = GetCurrentFocusActor();
+ if(actor)
+ {
+ Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
+ if(control)
+ {
+ // TODO: Notify the control to scroll right to the next page. Should control handle this?
+// ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::RIGHT);
+ }
+ }
+ }
+
+ return ret;
+}
+
+bool AccessibilityManager::AccessibilityActionPageUp()
+{
+ Dali::Toolkit::AccessibilityManager handle( this );
+ if( !mActionPageUpSignal.Empty() )
+ {
+ mActionPageUpSignal.Emit( handle );
+ }
+
+ bool ret = false;
+
+ if(mIsAccessibilityTtsEnabled)
+ {
+ Actor actor = GetCurrentFocusActor();
+ if(actor)
+ {
+ Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
+ if(control)
+ {
+ // TODO: Notify the control to scroll up to the previous page. Should control handle this?
+// ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::UP);
+ }
+ }
+ }
+
+ return ret;
+}
+
+bool AccessibilityManager::AccessibilityActionPageDown()
+{
+ Dali::Toolkit::AccessibilityManager handle( this );
+ if( !mActionPageDownSignal.Empty() )
+ {
+ mActionPageDownSignal.Emit( handle );
+ }
+
+ bool ret = false;
+
+ if(mIsAccessibilityTtsEnabled)
+ {
+ Actor actor = GetCurrentFocusActor();
+ if(actor)
+ {
+ Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
+ if(control)
+ {
+ // TODO: Notify the control to scroll down to the next page. Should control handle this?
+// ret = GetImplementation( control ).OnAccessibilityScrollToPage(Direction::DOWN);
+ }
+ }
+ }
+
+ return ret;
+}
+
+bool AccessibilityManager::AccessibilityActionMoveToFirst()
+{
+ Dali::Toolkit::AccessibilityManager handle( this );
+ if( !mActionMoveToFirstSignal.Empty() )
+ {
+ mActionMoveToFirstSignal.Emit( handle );
+ }
+
+ // TODO: Move to the first item on screen
+
+ return mIsAccessibilityTtsEnabled;
+}
+
+bool AccessibilityManager::AccessibilityActionMoveToLast()
+{
+ Dali::Toolkit::AccessibilityManager handle( this );
+ if( !mActionMoveToLastSignal.Empty() )
+ {
+ mActionMoveToLastSignal.Emit( handle );
+ }
+
+ // TODO: Move to the last item on screen
+
+ return mIsAccessibilityTtsEnabled;
+}
+
+bool AccessibilityManager::AccessibilityActionReadFromTop()
+{
+ Dali::Toolkit::AccessibilityManager handle( this );
+ if( !mActionReadFromTopSignal.Empty() )
+ {
+ mActionReadFromTopSignal.Emit( handle );
+ }
+
+ // TODO: Move to the top item on screen and read from the item continuously
+
+ return mIsAccessibilityTtsEnabled;
+}
+
+bool AccessibilityManager::AccessibilityActionReadFromNext()
+{
+ Dali::Toolkit::AccessibilityManager handle( this );
+
+ if( !mActionReadFromNextSignal.Empty() )
+ {
+ mActionReadFromNextSignal.Emit( handle );
+ }
+
+ if( mIsAccessibilityTtsEnabled )
+ {
+ // Mark that we are in continuous play mode, so TTS signals can move focus.
+ mContinuousPlayMode = true;
+
+ // Attempt to move to the next item and read from the item continuously.
+ MoveFocusForward();
+ }
+
+ return mIsAccessibilityTtsEnabled;
+}
+
+void AccessibilityManager::TtsStateChanged( const Dali::TtsPlayer::State previousState, const Dali::TtsPlayer::State currentState )
+{
+ if( mContinuousPlayMode )
+ {
+ // If we were playing and now we have stopped, attempt to play the next item.
+ if( ( previousState == Dali::TtsPlayer::PLAYING ) && ( currentState == Dali::TtsPlayer::READY ) )
+ {
+ // Attempt to move the focus forward and play.
+ // If we can't cancel continuous play mode.
+ if( !MoveFocusForward() )
+ {
+ // We are done, exit continuous play mode.
+ mContinuousPlayMode = false;
+ }
+ }
+ else
+ {
+ // Unexpected play state change, exit continuous play mode.
+ mContinuousPlayMode = false;
+ }
+ }
+}
+
+bool AccessibilityManager::AccessibilityActionZoom()
+{
+ Dali::Toolkit::AccessibilityManager handle( this );
+ if( !mActionZoomSignal.Empty() )
+ {
+ mActionZoomSignal.Emit( handle );
+ }
+
+ bool ret = false;
+
+ if(mIsAccessibilityTtsEnabled)
+ {
+ Actor actor = GetCurrentFocusActor();
+ if(actor)
+ {
+ Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor);
+ if(control)
+ {
+ // Notify the control to zoom
+ ret = GetImplementation( control ).OnAccessibilityZoom();
+ }
+ }
+ }
+
+ return ret;
+}
+
+bool AccessibilityManager::AccessibilityActionReadPauseResume()
+{
+ Dali::Toolkit::AccessibilityManager handle( this );
+ if( !mActionReadPauseResumeSignal.Empty() )
+ {
+ mActionReadPauseResumeSignal.Emit( handle );
+ }
+
+ bool ret = false;
+
+ if(mIsAccessibilityTtsEnabled)
+ {
+ // Pause or resume the TTS player
+ Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER);
+ Dali::TtsPlayer::State state = player.GetState();
+ if(state == Dali::TtsPlayer::PLAYING)
+ {
+ player.Pause();
+ ret = true;
+ }
+ else if(state == Dali::TtsPlayer::PAUSED)
+ {
+ player.Resume();
+ ret = true;
+ }
+ }
+
+ return ret;
+}
+
+bool AccessibilityManager::AccessibilityActionStartStop()
+{
+ Dali::Toolkit::AccessibilityManager handle( this );
+ if( !mActionStartStopSignal.Empty() )
+ {
+ mActionStartStopSignal.Emit( handle );
+ }
+
+ // TODO: Start/stop the current action
+
+ return mIsAccessibilityTtsEnabled;
+}
+
+bool AccessibilityManager::AccessibilityActionTouch(const TouchEvent& touchEvent)
+{
+ bool handled = false;
+
+ // TODO: Need to convert the touchevent for the focused actor?
+
+ Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(GetCurrentFocusActor());
+ if(control)
+ {
+ handled = GetImplementation( control ).OnAccessibilityTouch(touchEvent);
+ }
+
+ return handled;
+}
+
+bool AccessibilityManager::HandlePanGesture(const AccessibilityGestureEvent& panEvent)
+{
+ bool handled = false;
+
+ if( panEvent.state == AccessibilityGestureEvent::Started )
+ {
+ // Find the focusable actor at the event position
+ Dali::HitTestAlgorithm::Results results;
+ AccessibilityAdaptor adaptor = AccessibilityAdaptor::Get();
+
+ Dali::HitTestAlgorithm::HitTest( Stage::GetCurrent(), panEvent.currentPosition, results, IsActorFocusableFunction );
+ mCurrentGesturedActor = results.actor;
+
+ if(!mCurrentGesturedActor)
+ {
+ DALI_LOG_ERROR("Gesture detected, but no hit actor\n");
+ }
+ }
+
+ // Gesture::Finished (Up) events are delivered with previous (Motion) event position
+ // Use the real previous position; otherwise we may incorrectly get a ZERO velocity
+ if ( AccessibilityGestureEvent::Finished != panEvent.state )
+ {
+ // Store the previous position for next Gesture::Finished iteration.
+ mPreviousPosition = panEvent.previousPosition;
+ }
+
+ Actor rootActor = Stage::GetCurrent().GetRootLayer();
+
+ Dali::PanGesture pan( static_cast<Dali::Gesture::State>(panEvent.state) );
+
+ pan.time = panEvent.time;
+ pan.numberOfTouches = panEvent.numberOfTouches;
+ pan.screenPosition = panEvent.currentPosition;
+ pan.screenDisplacement = mPreviousPosition - panEvent.currentPosition;
+ pan.screenVelocity.x = pan.screenDisplacement.x / panEvent.timeDelta;
+ pan.screenVelocity.y = pan.screenDisplacement.y / panEvent.timeDelta;
+
+ // Only handle the pan gesture when the current focused actor is scrollable or within a scrollable actor
+ while(mCurrentGesturedActor && mCurrentGesturedActor != rootActor && !handled)
+ {
+ Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(mCurrentGesturedActor);
+ if(control)
+ {
+ Vector2 localCurrent;
+ control.ScreenToLocal( localCurrent.x, localCurrent.y, panEvent.currentPosition.x, panEvent.currentPosition.y );
+ pan.position = localCurrent;
+
+ Vector2 localPrevious;
+ control.ScreenToLocal( localPrevious.x, localPrevious.y, mPreviousPosition.x, mPreviousPosition.y );
+
+ pan.displacement = localCurrent - localPrevious;
+ pan.velocity.x = pan.displacement.x / panEvent.timeDelta;
+ pan.velocity.y = pan.displacement.y / panEvent.timeDelta;
+
+ handled = GetImplementation( control ).OnAccessibilityPan(pan);
+ }
+
+ // If the gesture is not handled by the control, check its parent
+ if(!handled)
+ {
+ mCurrentGesturedActor = mCurrentGesturedActor.GetParent();
+
+ if(!mCurrentGesturedActor)
+ {
+ DALI_LOG_ERROR("no more gestured actor\n");
+ }
+ }
+ else
+ {
+ // If handled, then update the pan gesture properties
+ PanGestureDetector::SetPanGestureProperties( pan );
+ }
+ }
+
+ return handled;