diff --git a/android/jni/com/mapswithme/maps/MWMApplication.cpp b/android/jni/com/mapswithme/maps/MWMApplication.cpp index 9b615700cd..a8badd73f4 100644 --- a/android/jni/com/mapswithme/maps/MWMApplication.cpp +++ b/android/jni/com/mapswithme/maps/MWMApplication.cpp @@ -11,6 +11,8 @@ #include "../platform/Platform.hpp" #include "../../../../../platform/settings.hpp" +#include "../../../../../map/information_display.hpp" +#include "../../../../../map/location_state.hpp" extern "C" { @@ -77,4 +79,52 @@ extern "C" (void)Settings::Set(jni::ToNativeString(env, name), flag); } + JNIEXPORT jboolean JNICALL + Java_com_mapswithme_maps_MWMApplication_nativeIsFollowingCompass(JNIEnv * env, + jobject thiz) + { + location::ECompassProcessMode compassMode = g_framework->NativeFramework()->GetInformationDisplay().locationState()->CompassProcessMode(); + return compassMode == location::ECompassFollow; + } + + JNIEXPORT void JNICALL + Java_com_mapswithme_maps_MWMApplication_nativeStartCompassFollowing(JNIEnv * env, + jobject thiz) + { + shared_ptr ls = g_framework->NativeFramework()->GetInformationDisplay().locationState(); + if (!ls->IsCentered()) + ls->AnimateToPositionAndEnqueueFollowing(); + else + ls->StartCompassFollowing(); + } + + JNIEXPORT void JNICALL + Java_com_mapswithme_maps_MWMApplication_nativeStopCompassFollowing(JNIEnv * env, + jobject thiz) + { + g_framework->NativeFramework()->GetInformationDisplay().locationState()->StopCompassFollowing(); + } + + void CompassStatusChanged(int mode, shared_ptr const & obj) + { + JNIEnv * env = jni::GetEnv(); + jmethodID methodID = jni::GetJavaMethodID(env, *obj.get(), "OnCompassStatusChanged", "(I)V"); + jint val = static_cast(mode); + env->CallVoidMethod(*obj.get(), methodID, val); + } + + JNIEXPORT jint JNICALL + Java_com_mapswithme_maps_MWMApplication_nativeAddCompassStatusListener(JNIEnv * env, jobject thiz, jobject obj) + { + location::State::TCompassStatusListener fn = bind(&CompassStatusChanged, _1, jni::make_global_ref(obj)); + shared_ptr ls = g_framework->NativeFramework()->GetInformationDisplay().locationState(); + return ls->AddCompassStatusListener(fn); + } + + JNIEXPORT void JNICALL + Java_com_mapswithme_maps_MWMApplication_nativeRemoveCompassStatusListener(JNIEnv * env, jobject thiz, jint slotID) + { + shared_ptr ls = g_framework->NativeFramework()->GetInformationDisplay().locationState(); + ls->RemoveCompassStatusListener(slotID); + } } diff --git a/android/src/com/mapswithme/maps/MWMActivity.java b/android/src/com/mapswithme/maps/MWMActivity.java index 2cd8778d71..1f7f3fc00c 100644 --- a/android/src/com/mapswithme/maps/MWMActivity.java +++ b/android/src/com/mapswithme/maps/MWMActivity.java @@ -22,7 +22,6 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.LinearLayout; -import android.widget.Toast; import com.mapswithme.maps.location.LocationService; import com.mapswithme.util.ConnectionState; @@ -40,12 +39,15 @@ public class MWMActivity extends NvEventQueueActivity implements LocationService private AlertDialog m_storageDisconnectedDialog = null; private boolean m_shouldStartLocationService = false; private boolean m_hasLocation = false; + private boolean m_hasCompass = false; + private boolean m_isLocationActive = false; private boolean m_suggestAutoFollowMode = false; private LocationService getLocationService() { return mApplication.getLocationService(); } + private MapStorage getMapStorage() { return mApplication.getMapStorage(); @@ -53,6 +55,7 @@ public class MWMActivity extends NvEventQueueActivity implements LocationService private void startLocation() { + m_isLocationActive = true; getLocationService().startUpdate(this); // Do not turn off the screen while displaying position Utils.automaticIdleScreen(false, getWindow()); @@ -61,6 +64,8 @@ public class MWMActivity extends NvEventQueueActivity implements LocationService private void stopLocation() { m_hasLocation = false; + m_hasCompass = false; + m_isLocationActive = false; getLocationService().stopUpdate(this); // Enable automatic turning screen off while app is idle Utils.automaticIdleScreen(true, getWindow()); @@ -206,18 +211,41 @@ public class MWMActivity extends NvEventQueueActivity implements LocationService public void onMyPositionClicked(View v) { - v.setBackgroundResource(R.drawable.myposition_button_normal); - - final boolean isLocationActive = v.isSelected(); - if (isLocationActive) - stopLocation(); - else + if (!m_isLocationActive) + { startLocation(); - v.setSelected(!isLocationActive); + v.setBackgroundResource(R.drawable.myposition_button_normal); + v.setSelected(true); + } + else + { + if (!m_hasCompass) + { + stopLocation(); + v.setBackgroundResource(R.drawable.myposition_button_normal); + v.setSelected(false); + } + else + { + if(!mApplication.nativeIsFollowingCompass()) + { + mApplication.nativeStartCompassFollowing(); + v.setBackgroundResource(R.drawable.myposition_button_follow); + v.setSelected(true); + } + else + { + mApplication.nativeStopCompassFollowing(); + v.setBackgroundResource(R.drawable.myposition_button_normal); + v.setSelected(false); + stopLocation(); + } + } + } // Store active state of My Position SharedPreferences.Editor prefsEdit = getSharedPreferences(mApplication.getPackageName(), MODE_PRIVATE).edit(); - prefsEdit.putBoolean(PREFERENCES_MYPOSITION, !isLocationActive); + prefsEdit.putBoolean(PREFERENCES_MYPOSITION, !m_isLocationActive); prefsEdit.commit(); } @@ -562,6 +590,37 @@ public class MWMActivity extends NvEventQueueActivity implements LocationService } } + public void onCompassStatusChanged(int newStatus) + { + View v = findViewById(R.id.map_button_myposition); + + if (newStatus == 1) + { + v.setSelected(true); + v.setBackgroundResource(R.drawable.myposition_button_follow); + } + else + { + if (m_hasLocation) + v.setBackgroundResource(R.drawable.myposition_button_found); + else + v.setBackgroundResource(R.drawable.myposition_button_normal); + } + } + + public void OnCompassStatusChanged(int newStatus) + { + final int val = newStatus; + runOnUiThread(new Runnable() + { + @Override + public void run() + { + onCompassStatusChanged(val); + } + }); + } + @Override public void onLocationUpdated(long time, double lat, double lon, float accuracy) { @@ -578,15 +637,9 @@ public class MWMActivity extends NvEventQueueActivity implements LocationService magneticNorth = LocationService.correctAngle(magneticNorth, correction); trueNorth = LocationService.correctAngle(trueNorth, correction); - nativeCompassUpdated(time, magneticNorth, trueNorth, accuracy); + m_hasCompass = true; - if (m_hasLocation - && mApplication.isProVersion() - && m_suggestAutoFollowMode) - { - Toast.makeText(this, R.string.suggest_auto_follow_mode, Toast.LENGTH_LONG).show(); - m_suggestAutoFollowMode = false; - } + nativeCompassUpdated(time, magneticNorth, trueNorth, accuracy); } //@} @@ -601,6 +654,18 @@ public class MWMActivity extends NvEventQueueActivity implements LocationService findViewById(R.id.map_button_myposition).setSelected(isMyPositionEnabled); } + private int m_compassStatusListenerID = -1; + + private void startWatchingCompassStatusUpdate() + { + m_compassStatusListenerID = mApplication.nativeAddCompassStatusListener(this); + } + + private void stopWatchingCompassStatusUpdate() + { + mApplication.nativeRemoveCompassStatusListener(m_compassStatusListenerID); + } + @Override protected void onPause() { @@ -608,6 +673,8 @@ public class MWMActivity extends NvEventQueueActivity implements LocationService stopWatchingExternalStorage(); + stopWatchingCompassStatusUpdate(); + super.onPause(); } @@ -627,6 +694,8 @@ public class MWMActivity extends NvEventQueueActivity implements LocationService m_shouldStartLocationService = true; } + startWatchingCompassStatusUpdate(); + startWatchingExternalStorage(); super.onResume(); diff --git a/android/src/com/mapswithme/maps/MWMApplication.java b/android/src/com/mapswithme/maps/MWMApplication.java index 3b0d535213..2e47781a89 100644 --- a/android/src/com/mapswithme/maps/MWMApplication.java +++ b/android/src/com/mapswithme/maps/MWMApplication.java @@ -183,4 +183,11 @@ public class MWMApplication extends android.app.Application implements MapStorag /// Dealing with Settings public native boolean nativeGetBoolean(String name, boolean defaultVal); public native void nativeSetBoolean(String name, boolean val); + + public native boolean nativeIsFollowingCompass(); + public native void nativeStartCompassFollowing(); + public native void nativeStopCompassFollowing(); + + public native int nativeAddCompassStatusListener(Object l); + public native void nativeRemoveCompassStatusListener(int slotID); } diff --git a/anim/angle_interpolation.cpp b/anim/angle_interpolation.cpp index 0d8ba70dfa..acb588cae6 100644 --- a/anim/angle_interpolation.cpp +++ b/anim/angle_interpolation.cpp @@ -1,6 +1,8 @@ #include "angle_interpolation.hpp" +#include "controller.hpp" #include "../geometry/angles.hpp" +#include "../base/logging.hpp" namespace anim { @@ -22,20 +24,21 @@ namespace anim void AngleInterpolation::OnStart(double ts) { m_startTime = ts; + m_outAngle = m_startAngle; Task::OnStart(ts); } void AngleInterpolation::OnStep(double ts) { + if (!IsRunning()) + return; + if (ts - m_startTime >= m_interval) { End(); return; } - if (!IsRunning()) - return; - double elapsedSec = ts - m_startTime; m_curAngle = m_outAngle = m_startAngle + m_dist * elapsedSec / m_interval; @@ -56,6 +59,7 @@ namespace anim void AngleInterpolation::SetEndAngle(double val) { + m_startTime = GetController()->GetCurrentTime(); m_startAngle = m_curAngle; m_dist = ang::GetShortestDistance(m_startAngle, val); m_endAngle = m_startAngle + m_dist; diff --git a/anim/anyrect_interpolation.cpp b/anim/anyrect_interpolation.cpp index 74ca20eace..51565f1561 100644 --- a/anim/anyrect_interpolation.cpp +++ b/anim/anyrect_interpolation.cpp @@ -1,13 +1,15 @@ #include "anyrect_interpolation.hpp" +#include "../base/logging.hpp" + namespace anim { AnyRectInterpolation::AnyRectInterpolation(m2::AnyRectD const & startRect, m2::AnyRectD const & endRect, double rotationSpeed, m2::AnyRectD & outRect) - : m_interval(rotationSpeed * fabs(ang::GetShortestDistance(startRect.Angle().val(), - endRect.Angle().val())) / (2 * math::pi)), + : m_interval(max(0.5, rotationSpeed * fabs(ang::GetShortestDistance(startRect.Angle().val(), + endRect.Angle().val())) / (2 * math::pi))), m_angleInt(startRect.Angle().val(), endRect.Angle().val(), rotationSpeed, @@ -35,9 +37,16 @@ namespace anim m_startTime = ts; m_angleInt.OnStart(ts); + m_angleInt.Start(); + m_segmentInt.OnStart(ts); + m_segmentInt.Start(); + m_sizeXInt.OnStart(ts); + m_sizeXInt.Start(); + m_sizeYInt.OnStart(ts); + m_sizeYInt.Start(); m_outRect = m_startRect; @@ -46,34 +55,68 @@ namespace anim void AnyRectInterpolation::OnStep(double ts) { + if (!IsRunning()) + return; + if (ts - m_startTime >= m_interval) { End(); return; } - if (!IsRunning()) - return; + if (m_angleInt.IsRunning()) + m_angleInt.OnStep(ts); - m_angleInt.OnStep(ts); - m_segmentInt.OnStep(ts); - m_sizeXInt.OnStep(ts); - m_sizeYInt.OnStep(ts); + if (m_segmentInt.IsRunning()) + m_segmentInt.OnStep(ts); - m_outRect = m2::AnyRectD(m_curCenter, m_curAngle, m2::RectD(-m_curSizeX / 2, -m_curSizeY / 2, m_curSizeX / 2, m_curSizeY / 2)); + if (m_sizeXInt.IsRunning()) + m_sizeXInt.OnStep(ts); + + if (m_sizeYInt.IsRunning()) + m_sizeYInt.OnStep(ts); + + m_outRect = m2::AnyRectD(m_curCenter, + m_curAngle, + m2::RectD(-m_curSizeX / 2, -m_curSizeY / 2, + m_curSizeX / 2, m_curSizeY / 2)); anim::Task::OnStep(ts); } void AnyRectInterpolation::OnEnd(double ts) { - m_angleInt.OnEnd(ts); - m_segmentInt.OnEnd(ts); - m_sizeXInt.OnEnd(ts); - m_sizeYInt.OnEnd(ts); + if (m_angleInt.IsRunning()) + m_angleInt.OnEnd(ts); + + if (m_segmentInt.IsRunning()) + m_segmentInt.OnEnd(ts); + + if (m_sizeXInt.IsRunning()) + m_sizeXInt.OnEnd(ts); + + if (m_sizeYInt.IsRunning()) + m_sizeYInt.OnEnd(ts); m_outRect = m_endRect; anim::Task::OnEnd(ts); } + + void AnyRectInterpolation::OnCancel(double ts) + { + if (m_angleInt.IsRunning()) + m_angleInt.Cancel(); + + if (m_segmentInt.IsRunning()) + m_segmentInt.Cancel(); + + if (m_sizeXInt.IsRunning()) + m_sizeXInt.Cancel(); + + if (m_sizeYInt.IsRunning()) + m_sizeYInt.Cancel(); + + anim::Task::OnCancel(ts); + } } diff --git a/anim/anyrect_interpolation.hpp b/anim/anyrect_interpolation.hpp index 13df48c8db..9ba9272b0a 100644 --- a/anim/anyrect_interpolation.hpp +++ b/anim/anyrect_interpolation.hpp @@ -41,5 +41,6 @@ namespace anim void OnStart(double ts); void OnStep(double ts); void OnEnd(double ts); + void OnCancel(double ts); }; } diff --git a/anim/controller.cpp b/anim/controller.cpp index 3f217cf48c..2482a21370 100644 --- a/anim/controller.cpp +++ b/anim/controller.cpp @@ -20,6 +20,7 @@ namespace anim void Controller::AddTask(shared_ptr const & task) { + task->SetController(this); m_tasks.PushBack(task); m_IdleFrames = m_IdleThreshold; } @@ -30,6 +31,11 @@ namespace anim swap(from, to); } + void Controller::MergeTasks(TTasks & from, TTasks & to) + { + copy(from.begin(), from.end(), back_inserter(to)); + } + bool Controller::HasTasks() { return !m_tasks.Empty(); @@ -55,7 +61,7 @@ namespace anim { m_tasks.ProcessList(bind(&Controller::CopyAndClearTasks, _1, ref(m_tasksList))); - double ts = my::Timer::LocalTime(); + double ts = GetCurrentTime(); TTasks l; @@ -66,30 +72,43 @@ namespace anim m_IdleFrames = m_IdleThreshold; shared_ptr const & task = *it; - if (task->State() == Task::EStarted) + + task->Lock(); + + if (task->IsReady()) + { + task->Start(); task->OnStart(ts); - if (task->State() == Task::EInProgress) + } + if (task->IsRunning()) task->OnStep(ts); - if (task->State() == Task::EInProgress) + if (task->IsRunning()) l.push_back(task); else { - if (task->State() == Task::ECancelled) + if (task->IsCancelled()) task->OnCancel(ts); - if (task->State() == Task::EEnded) + if (task->IsEnded()) task->OnEnd(ts); } + + task->Unlock(); } if (!hasTasks && m_IdleFrames > 0) m_IdleFrames -= 1; - m_tasks.ProcessList(bind(&Controller::CopyAndClearTasks, ref(l), _1)); + m_tasks.ProcessList(bind(&Controller::MergeTasks, ref(l), _1)); } bool Controller::IsPreWarmed() const { return m_IdleFrames > 0; } + + double Controller::GetCurrentTime() const + { + return my::Timer::LocalTime(); + } } diff --git a/anim/controller.hpp b/anim/controller.hpp index ff8a66e863..398f191a4d 100644 --- a/anim/controller.hpp +++ b/anim/controller.hpp @@ -26,6 +26,7 @@ namespace anim int m_IdleFrames; static void CopyAndClearTasks(list > & from, list > & to); + static void MergeTasks(list > & from, list > & to); public: // Constructor @@ -53,5 +54,7 @@ namespace anim // interrupting rendering process, which might had happened in these // "frames-in-the-middle". bool IsPreWarmed() const; + // Getting current simulation time + double GetCurrentTime() const; }; } diff --git a/anim/task.cpp b/anim/task.cpp index b012ce2fc7..92a5c4b04e 100644 --- a/anim/task.cpp +++ b/anim/task.cpp @@ -1,9 +1,13 @@ #include "task.hpp" +#include "../std/bind.hpp" + +#include "../base/assert.hpp" + namespace anim { Task::Task() - : m_State(EStarted) + : m_State(EReady) {} Task::~Task() @@ -26,46 +30,50 @@ namespace anim void Task::SetState(EState State) { - Lock(); m_State = State; - Unlock(); } - void Task::PerformCallback(EState state) + void Task::PerformCallbacks(EState state) { - TCallback const & cb = m_Callbacks[state]; - if (cb) - cb(); + list const & cb = m_Callbacks[state]; + for_each(cb.begin(), cb.end(), bind(&TCallback::operator(), _1)); } void Task::OnStart(double ts) { - PerformCallback(EStarted); - SetState(EInProgress); + PerformCallbacks(EReady); } void Task::OnStep(double ts) { - PerformCallback(EInProgress); + PerformCallbacks(EInProgress); } void Task::OnCancel(double ts) { - PerformCallback(ECancelled); + PerformCallbacks(ECancelled); } void Task::OnEnd(double ts) { - PerformCallback(EEnded); + PerformCallbacks(EEnded); + } + + void Task::Start() + { + ASSERT(IsReady(), ()); + SetState(EInProgress); } void Task::Cancel() { + ASSERT(IsRunning() || IsReady(), ()); SetState(ECancelled); } void Task::End() { + ASSERT(IsRunning() || IsReady(), ()); SetState(EEnded); } @@ -84,8 +92,23 @@ namespace anim return State() == EInProgress; } - void Task::SetCallback(EState state, TCallback const & cb) + bool Task::IsReady() const { - m_Callbacks[state] = cb; + return State() == EReady; + } + + void Task::AddCallback(EState state, TCallback const & cb) + { + m_Callbacks[state].push_back(cb); + } + + void Task::SetController(Controller * controller) + { + m_controller = controller; + } + + Controller * Task::GetController() const + { + return m_controller; } } diff --git a/anim/task.hpp b/anim/task.hpp index d55c61d129..8cea0054dd 100644 --- a/anim/task.hpp +++ b/anim/task.hpp @@ -1,12 +1,15 @@ #pragma once #include "../std/map.hpp" +#include "../std/list.hpp" #include "../std/function.hpp" #include "../base/mutex.hpp" namespace anim { + class Controller; + // Interface for single animation task class Task { @@ -16,7 +19,7 @@ namespace anim enum EState { - EStarted, + EReady, EInProgress, ECancelled, EEnded @@ -26,21 +29,27 @@ namespace anim EState m_State; - map m_Callbacks; + map > m_Callbacks; - void PerformCallback(EState state); + void PerformCallbacks(EState state); threads::Mutex m_mutex; + Controller * m_controller; + protected: void SetState(EState state); + friend class Controller; + void SetController(Controller * controller); public: Task(); virtual ~Task(); + Controller * GetController() const; + EState State() const; virtual void OnStart(double ts); @@ -48,16 +57,18 @@ namespace anim virtual void OnEnd(double ts); virtual void OnCancel(double ts); + void Start(); void Cancel(); void End(); bool IsCancelled() const; bool IsEnded() const; bool IsRunning() const; + bool IsReady() const; void Lock(); void Unlock(); - void SetCallback(EState state, TCallback const & cb); + void AddCallback(EState state, TCallback const & cb); }; } diff --git a/map/animator.cpp b/map/animator.cpp index 2dfd7e89e5..0eb8913eac 100644 --- a/map/animator.cpp +++ b/map/animator.cpp @@ -26,8 +26,9 @@ void Animator::RotateScreen(double startAngle, double endAngle, double duration) { if (m_rotateScreenTask) { + if (!m_rotateScreenTask->IsCancelled()) + m_rotateScreenTask->Cancel(); m_rotateScreenTask->Unlock(); - m_rotateScreenTask->Cancel(); m_rotateScreenTask.reset(); } @@ -54,8 +55,8 @@ void Animator::StopRotation() && !m_rotateScreenTask->IsEnded() && !m_rotateScreenTask->IsCancelled()) { - m_rotateScreenTask->Unlock(); m_rotateScreenTask->Cancel(); + m_rotateScreenTask->Unlock(); m_rotateScreenTask.reset(); return; } diff --git a/map/basic_tiling_render_policy.cpp b/map/basic_tiling_render_policy.cpp index d6bf199c50..2811a5308f 100644 --- a/map/basic_tiling_render_policy.cpp +++ b/map/basic_tiling_render_policy.cpp @@ -36,7 +36,7 @@ size_t BasicTilingRenderPolicy::CalculateTileSize(size_t screenWidth, size_t scr BasicTilingRenderPolicy::BasicTilingRenderPolicy(Params const & p, bool doUseQueuedRenderer) - : RenderPolicy(p, GetPlatform().IsPro(), GetPlatform().CpuCores()), + : RenderPolicy(p, true, GetPlatform().CpuCores()), m_DrawScale(0), m_IsEmptyModel(false), m_DoRecreateCoverage(false), diff --git a/map/framework.cpp b/map/framework.cpp index dea8da12ae..abc77a3d19 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -840,7 +840,8 @@ void Framework::StartDrag(DragEvent const & e) if (m_renderPolicy) m_renderPolicy->StartDrag(); - m_dragCompassProcessMode = m_informationDisplay.locationState()->CompassProcessMode(); + shared_ptr locationState = m_informationDisplay.locationState(); + m_dragCompassProcessMode = locationState->CompassProcessMode(); } void Framework::DoDrag(DragEvent const & e) @@ -879,7 +880,12 @@ void Framework::StopDrag(DragEvent const & e) if (GetPixelCenter().Length(s.GtoP(locationState->Position())) >= s.GetMinPixelRectSize() / 2.0) locationState->SetLocationProcessMode(location::ELocationDoNothing); else - locationState->SetCompassProcessMode(m_dragCompassProcessMode); + { + if (m_dragCompassProcessMode == location::ECompassFollow) + locationState->AnimateToPositionAndEnqueueFollowing(); + else + locationState->AnimateToPosition(); + } } if (m_renderPolicy) diff --git a/map/location_state.cpp b/map/location_state.cpp index 328e1323d7..09ce5736cb 100644 --- a/map/location_state.cpp +++ b/map/location_state.cpp @@ -22,6 +22,8 @@ #include "../indexer/mercator.hpp" +#include "../base/logging.hpp" + namespace location { State::Params::Params() @@ -34,7 +36,8 @@ namespace location m_hasCompass(false), m_isCentered(false), m_locationProcessMode(ELocationDoNothing), - m_compassProcessMode(ECompassDoNothing) + m_compassProcessMode(ECompassDoNothing), + m_currentSlotID(0) { m_drawHeading = m_compassFilter.GetHeadingRad(); m_locationAreaColor = p.m_locationAreaColor; @@ -103,7 +106,12 @@ namespace location void State::SetCompassProcessMode(ECompassProcessMode mode) { + bool stateChanged = (m_compassProcessMode != mode); + m_compassProcessMode = mode; + + if (stateChanged) + CallCompassStatusListeners(mode); } void State::OnLocationStatusChanged(location::TLocationStatus newStatus) @@ -122,7 +130,7 @@ namespace location { // set centering mode for the first location m_locationProcessMode = ELocationCenterAndScale; - m_compassProcessMode = ECompassDoNothing; + SetCompassProcessMode(ECompassDoNothing); } break; @@ -175,7 +183,7 @@ namespace location SetIsCentered(true); CheckCompassRotation(); - CheckFollowCompass(); + CheckCompassFollowing(); m_locationProcessMode = ELocationCenterOnly; break; @@ -186,7 +194,8 @@ namespace location SetIsCentered(true); CheckCompassRotation(); - CheckFollowCompass(); + CheckCompassFollowing(); + break; case ELocationSkipCentering: @@ -208,7 +217,7 @@ namespace location m_compassFilter.OnCompassUpdate(info); CheckCompassRotation(); - CheckFollowCompass(); + CheckCompassFollowing(); m_framework->Invalidate(); } @@ -430,7 +439,6 @@ namespace location bool State::hitTest(m2::PointD const & pt) const { double radius = m_arrowHeight * m_controller->GetVisualScale(); - return m_hasCompass && (pt.SquareLength(pivot()) <= my::sq(radius)); } @@ -486,7 +494,7 @@ namespace location #endif } - void State::CheckFollowCompass() + void State::CheckCompassFollowing() { if (m_hasCompass && (CompassProcessMode() == ECompassFollow) @@ -511,15 +519,23 @@ namespace location controller->Unlock(); } - void State::MarkCenteredAndFollowCompass() + void State::AnimateToPosition() { - m_isCentered = true; - SetCompassProcessMode(ECompassFollow); - FollowCompass(); - (void)Settings::Set("SuggestAutoFollowMode", false); + anim::Controller * controller = m_framework->GetAnimController(); + + controller->Lock(); + + m2::AnyRectD startRect = m_framework->GetNavigator().Screen().GlobalRect(); + m2::AnyRectD endRect = m2::AnyRectD(Position(), + startRect.Angle().val(), + m2::RectD(startRect.GetLocalRect())); + + m_framework->GetAnimator().ChangeViewport(startRect, endRect, 2); + + controller->Unlock(); } - void State::CenterScreenAndEnqueueFollowing() + void State::AnimateToPositionAndEnqueueFollowing() { anim::Controller * controller = m_framework->GetAnimController(); @@ -533,12 +549,21 @@ namespace location shared_ptr const & t = m_framework->GetAnimator().ChangeViewport(startRect, endRect, 2); t->Lock(); - t->SetCallback(anim::Task::EEnded, bind(&State::MarkCenteredAndFollowCompass, this)); + t->AddCallback(anim::Task::EEnded, bind(&State::SetIsCentered, this, true)); + t->AddCallback(anim::Task::EEnded, bind(&State::StartCompassFollowing, this)); t->Unlock(); controller->Unlock(); } + void State::StartCompassFollowing() + { + SetCompassProcessMode(ECompassFollow); + CheckCompassRotation(); + CheckCompassFollowing(); + setState(EPressed); + } + void State::StopCompassFollowing() { SetCompassProcessMode(ECompassDoNothing); @@ -557,6 +582,26 @@ namespace location m_isCentered = flag; } + void State::CallCompassStatusListeners(ECompassProcessMode mode) + { + for (TCompassStatusListeners::const_iterator it = m_compassStatusListeners.begin(); + it != m_compassStatusListeners.end(); + ++it) + it->second(mode); + } + + int State::AddCompassStatusListener(TCompassStatusListener const & l) + { + int slotID = m_currentSlotID++; + m_compassStatusListeners[slotID] = l; + return slotID; + } + + void State::RemoveCompassStatusListener(int slotID) + { + m_compassStatusListeners.erase(slotID); + } + bool State::onTapEnded(m2::PointD const & pt) { if (!m_framework->GetNavigator().DoSupportRotation()) @@ -570,32 +615,16 @@ namespace location { case EActive: if (m_hasCompass) - { - if (!IsCentered()) - CenterScreenAndEnqueueFollowing(); - else - { - SetCompassProcessMode(ECompassFollow); - FollowCompass(); - - (void)Settings::Set("SuggestAutoFollowMode", false); - } - - setState(EPressed); - } + StartCompassFollowing(); break; case EPressed: - setState(EActive); StopCompassFollowing(); break; - default: - LOG(LWARNING, ("not-used EState values encountered")); }; controller->Unlock(); m_framework->Invalidate(); - return true; } } diff --git a/map/location_state.hpp b/map/location_state.hpp index a9b3584cf2..36030c137a 100644 --- a/map/location_state.hpp +++ b/map/location_state.hpp @@ -51,6 +51,10 @@ namespace location // and draws location and compass marks. class State : public gui::Element { + public: + + typedef function TCompassStatusListener; + private: double m_errorRadius; //< error radius in mercator @@ -67,8 +71,6 @@ namespace location ECompassProcessMode m_compassProcessMode; void FollowCompass(); - void MarkCenteredAndFollowCompass(); - void CenterScreenAndEnqueueFollowing(); /// GUI element related fields. @@ -117,6 +119,12 @@ namespace location shared_ptr m_headingInterpolation; + typedef map TCompassStatusListeners; + TCompassStatusListeners m_compassStatusListeners; + int m_currentSlotID; + + void CallCompassStatusListeners(ECompassProcessMode mode); + public: struct Params : base_t::Params @@ -146,12 +154,20 @@ namespace location void TurnOff(); + void StartCompassFollowing(); void StopCompassFollowing(); + + int AddCompassStatusListener(TCompassStatusListener const & l); + void RemoveCompassStatusListener(int slotID); + void SetIsCentered(bool flag); bool IsCentered() const; + void AnimateToPosition(); + void AnimateToPositionAndEnqueueFollowing(); + void CheckCompassRotation(); - void CheckFollowCompass(); + void CheckCompassFollowing(); /// @name GPS location updates routine. //@{