diff --git a/android/jni/com/mapswithme/maps/LocationState.cpp b/android/jni/com/mapswithme/maps/LocationState.cpp index 20302dea26..f3f16d8492 100644 --- a/android/jni/com/mapswithme/maps/LocationState.cpp +++ b/android/jni/com/mapswithme/maps/LocationState.cpp @@ -17,28 +17,28 @@ static void LocationStateModeChanged(location::EMyPositionMode mode, shared_ptr< // public static void nativeSwitchToNextMode(); JNIEXPORT void JNICALL -Java_com_mapswithme_maps_LocationState_nativeSwitchToNextMode(JNIEnv * env, jclass clazz) +Java_com_mapswithme_maps_location_LocationState_nativeSwitchToNextMode(JNIEnv * env, jclass clazz) { g_framework->SwitchMyPositionNextMode(); } // private static int nativeGetMode(); JNIEXPORT jint JNICALL -Java_com_mapswithme_maps_LocationState_nativeGetMode(JNIEnv * env, jclass clazz) +Java_com_mapswithme_maps_location_LocationState_nativeGetMode(JNIEnv * env, jclass clazz) { return g_framework->GetMyPositionMode(); } // public static void nativeSetListener(ModeChangeListener listener); JNIEXPORT void JNICALL -Java_com_mapswithme_maps_LocationState_nativeSetListener(JNIEnv * env, jclass clazz, jobject listener) +Java_com_mapswithme_maps_location_LocationState_nativeSetListener(JNIEnv * env, jclass clazz, jobject listener) { g_framework->SetMyPositionModeListener(bind(&LocationStateModeChanged, _1, jni::make_global_ref(listener))); } // public static void nativeRemoveListener(); JNIEXPORT void JNICALL -Java_com_mapswithme_maps_LocationState_nativeRemoveListener(JNIEnv * env, jclass clazz) +Java_com_mapswithme_maps_location_LocationState_nativeRemoveListener(JNIEnv * env, jclass clazz) { g_framework->SetMyPositionModeListener(location::TMyPositionModeChanged()); } diff --git a/android/src/com/mapswithme/maps/MwmActivity.java b/android/src/com/mapswithme/maps/MwmActivity.java index 2094c56fdc..9d2a2c1bf4 100644 --- a/android/src/com/mapswithme/maps/MwmActivity.java +++ b/android/src/com/mapswithme/maps/MwmActivity.java @@ -2,6 +2,7 @@ package com.mapswithme.maps; import android.annotation.SuppressLint; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.res.Configuration; import android.graphics.Rect; @@ -129,6 +130,7 @@ public class MwmActivity extends BaseMwmFragmentActivity // Instance state private static final String STATE_PP = "PpState"; private static final String STATE_MAP_OBJECT = "MapObject"; + private static final String EXTRA_LOCATION_DIALOG_IS_ANNOYING = "LOCATION_DIALOG_IS_ANNOYING"; // Map tasks that we run AFTER rendering initialized private final Stack mTasks = new Stack<>(); @@ -173,6 +175,21 @@ public class MwmActivity extends BaseMwmFragmentActivity private boolean mFirstStart; private boolean mPlacePageRestored; + private boolean mLocationErrorDialogAnnoying = false; + + @NonNull + private final OnClickListener mOnMyPositionClickListener = new OnClickListener() + { + @Override + public void onClick(View v) + { + mLocationErrorDialogAnnoying = false; + LocationHelper.INSTANCE.switchToNextMode(); + Statistics.INSTANCE.trackEvent(Statistics.EventName.TOOLBAR_MY_POSITION); + AlohaHelper.logClick(AlohaHelper.TOOLBAR_MY_POSITION); + } + }; + public interface LeftAnimationTrackListener { void onTrackStarted(boolean collapsed); @@ -430,6 +447,9 @@ public class MwmActivity extends BaseMwmFragmentActivity { super.onCreate(savedInstanceState); + if (savedInstanceState != null) + mLocationErrorDialogAnnoying = savedInstanceState.getBoolean(EXTRA_LOCATION_DIALOG_IS_ANNOYING); + mIsFragmentContainer = getResources().getBoolean(R.bool.tabletLayout); if (!mIsFragmentContainer && (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)) @@ -602,7 +622,7 @@ public class MwmActivity extends BaseMwmFragmentActivity View zoomOut = frame.findViewById(R.id.nav_zoom_out); zoomOut.setOnClickListener(this); View myPosition = frame.findViewById(R.id.my_position); - mNavMyPosition = new MyPositionButton(myPosition); + mNavMyPosition = new MyPositionButton(myPosition, mOnMyPositionClickListener); ImageButton traffic = (ImageButton) frame.findViewById(R.id.traffic); mTraffic = new TrafficButton(this, traffic); mTrafficButtonController = new TrafficButtonController(mTraffic, this); @@ -842,6 +862,7 @@ public class MwmActivity extends BaseMwmFragmentActivity mNavigationController.onSaveState(outState); RoutingController.get().onSaveState(); + outState.putBoolean(EXTRA_LOCATION_DIALOG_IS_ANNOYING, mLocationErrorDialogAnnoying); super.onSaveInstanceState(outState); } @@ -960,14 +981,14 @@ public class MwmActivity extends BaseMwmFragmentActivity mFirstStart = FirstStartFragment.showOn(this); if (mFirstStart) { - if (LocationState.isTurnedOn()) + if (LocationHelper.INSTANCE.isTurnedOn()) addTask(new MwmActivity.MapTask() { @Override public boolean run(MwmActivity target) { - if (LocationState.isTurnedOn()) - LocationState.nativeSwitchToNextMode(); + if (LocationHelper.INSTANCE.isTurnedOn()) + LocationHelper.INSTANCE.switchToNextMode(); return false; } }); @@ -1733,4 +1754,53 @@ public class MwmActivity extends BaseMwmFragmentActivity { return (mMapFragment != null && !mMapFragment.isFirstStart()); } + + @Override + public void onLocationError() + { + if (mLocationErrorDialogAnnoying) + return; + + Intent intent = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS); + if (intent.resolveActivity(MwmApplication.get().getPackageManager()) == null) + { + intent = new Intent(android.provider.Settings.ACTION_SECURITY_SETTINGS); + if (intent.resolveActivity(MwmApplication.get().getPackageManager()) == null) + return; + } + + showLocationErrorDialog(intent); + } + + private void showLocationErrorDialog(@NonNull final Intent intent) + { + new AlertDialog.Builder(this) + .setTitle(R.string.enable_location_services) + .setMessage(R.string.location_is_disabled_long_text) + .setNegativeButton(R.string.close, new DialogInterface.OnClickListener() + { + @Override + public void onClick(DialogInterface dialog, int which) + { + mLocationErrorDialogAnnoying = true; + } + }) + .setOnCancelListener(new DialogInterface.OnCancelListener() + { + @Override + public void onCancel(DialogInterface dialog) + { + mLocationErrorDialogAnnoying = true; + } + }) + .setPositiveButton(R.string.connection_settings, new DialogInterface.OnClickListener() + { + @Override + public void onClick(DialogInterface dialog, int which) + { + //TODO: try to use startActivityForResult to handle settings result back + startActivity(intent); + } + }).show(); + } } diff --git a/android/src/com/mapswithme/maps/NavigationButtonsAnimationController.java b/android/src/com/mapswithme/maps/NavigationButtonsAnimationController.java index a01d89f4b1..ec9de52851 100644 --- a/android/src/com/mapswithme/maps/NavigationButtonsAnimationController.java +++ b/android/src/com/mapswithme/maps/NavigationButtonsAnimationController.java @@ -6,6 +6,7 @@ import android.content.res.Resources; import android.support.annotation.NonNull; import android.view.View; +import com.mapswithme.maps.location.LocationState; import com.mapswithme.maps.routing.RoutingController; import com.mapswithme.util.Animations; import com.mapswithme.util.Config; diff --git a/android/src/com/mapswithme/maps/location/GoogleFusedLocationProvider.java b/android/src/com/mapswithme/maps/location/GoogleFusedLocationProvider.java index ef2121b2c7..b15c39d189 100644 --- a/android/src/com/mapswithme/maps/location/GoogleFusedLocationProvider.java +++ b/android/src/com/mapswithme/maps/location/GoogleFusedLocationProvider.java @@ -93,6 +93,7 @@ class GoogleFusedLocationProvider extends BaseLocationProvider private void checkSettingsAndRequestUpdates() { + sLogger.d("checkSettingsAndRequestUpdates()"); LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().addLocationRequest(mLocationRequest); builder.setAlwaysShow(true); // hides 'never' button in resolve dialog afterwards. mLocationSettingsResult = LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient, builder.build()); @@ -102,6 +103,7 @@ class GoogleFusedLocationProvider extends BaseLocationProvider public void onResult(@NonNull LocationSettingsResult locationSettingsResult) { final Status status = locationSettingsResult.getStatus(); + sLogger.d("onResult status: " + status); switch (status.getStatusCode()) { case LocationSettingsStatusCodes.SUCCESS: @@ -124,6 +126,7 @@ class GoogleFusedLocationProvider extends BaseLocationProvider private static void resolveError(Status status) { + sLogger.d("resolveError()"); if (LocationHelper.INSTANCE.isLocationStopped()) return; diff --git a/android/src/com/mapswithme/maps/location/LocationHelper.java b/android/src/com/mapswithme/maps/location/LocationHelper.java index 336d51f112..3e813d3072 100644 --- a/android/src/com/mapswithme/maps/location/LocationHelper.java +++ b/android/src/com/mapswithme/maps/location/LocationHelper.java @@ -9,14 +9,12 @@ import android.location.LocationManager; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.app.AlertDialog; -import android.util.Log; import java.lang.ref.WeakReference; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GoogleApiAvailability; import com.mapswithme.maps.Framework; -import com.mapswithme.maps.LocationState; import com.mapswithme.maps.MwmApplication; import com.mapswithme.maps.R; import com.mapswithme.maps.bookmarks.data.Banner; @@ -55,12 +53,15 @@ public enum LocationHelper private static final long STOP_DELAY_MS = 5000; static final int REQUEST_RESOLVE_ERROR = 101; + private boolean mErrorOccurred; + public interface UiCallback { Activity getActivity(); void onMyPositionModeChanged(int newMode); void onLocationUpdated(@NonNull Location location); void onCompassUpdated(@NonNull CompassData compass); + void onLocationError(); boolean shouldNotifyLocationNotFound(); } @@ -87,11 +88,11 @@ public enum LocationHelper { MwmApplication.get().unregisterReceiver(mReceiver); mReceiverRegistered = false; - return; } } }; + @NonNull private final LocationListener mLocationListener = new LocationListener() { @Override @@ -125,43 +126,35 @@ public enum LocationHelper mUiCallback.onCompassUpdated(mCompassData); } + @Override public void onLocationError(int errorCode) { + mErrorOccurred = true; + mLogger.d("onLocationError errorCode = " + errorCode); if (mLocationStopped) return; nativeOnLocationError(errorCode); + mLogger.d("nativeOnLocationError errorCode = " + errorCode +", " + + "state machine should stop pending, current state = " + + LocationState.nameOf(LocationState.getMode())); if (mUiCallback == null) return; - Intent intent = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS); - if (intent.resolveActivity(MwmApplication.get().getPackageManager()) == null) - { - intent = new Intent(android.provider.Settings.ACTION_SECURITY_SETTINGS); - if (intent.resolveActivity(MwmApplication.get().getPackageManager()) == null) - return; - } + mUiCallback.onLocationError(); + } - final Intent finIntent = intent; - new AlertDialog.Builder(mUiCallback.getActivity()) - .setTitle(R.string.enable_location_services) - .setMessage(R.string.location_is_disabled_long_text) - .setNegativeButton(R.string.close, null) - .setPositiveButton(R.string.connection_settings, new DialogInterface.OnClickListener() - { - @Override - public void onClick(DialogInterface dialog, int which) - { - if (mUiCallback != null) - mUiCallback.getActivity().startActivity(finIntent); - } - }).show(); + @Override + public String toString() + { + return "LocationHelper.mLocationListener"; } }; private final Logger mLogger = new DebugLogger(LocationHelper.class.getSimpleName()); + @NonNull private final Listeners mListeners = new Listeners<>(); private boolean mActive; @@ -172,10 +165,12 @@ public enum LocationHelper private Location mSavedLocation; private MapObject mMyPosition; private long mSavedLocationTime; - + @NonNull private final SensorHelper mSensorHelper = new SensorHelper(); private BaseLocationProvider mLocationProvider; + @NonNull private final LocationPredictor mPredictor = new LocationPredictor(mLocationListener); + @Nullable private UiCallback mUiCallback; private WeakReference mPrevUiCallback; @@ -190,7 +185,8 @@ public enum LocationHelper public void onMyPositionModeChanged(int newMode) { notifyMyPositionModeChanged(newMode); - + mLogger.d("onMyPositionModeChanged mode = " + LocationState.nameOf(newMode) + + " mColdStart = " + mColdStart + " mErrorOccurred = " + mErrorOccurred); switch (newMode) { case LocationState.PENDING_POSITION: @@ -198,7 +194,7 @@ public enum LocationHelper break; case LocationState.NOT_FOLLOW_NO_POSITION: - if (mColdStart) + if (mColdStart && !mErrorOccurred) { LocationState.nativeSwitchToNextMode(); break; @@ -223,6 +219,7 @@ public enum LocationHelper } mColdStart = false; + mErrorOccurred = false; } }; @@ -256,9 +253,13 @@ public enum LocationHelper public void initProvider(boolean forceNative) { + mLogger.d("initProvider forceNative = " + forceNative); mActive = !mListeners.isEmpty(); if (mActive) + { + mLogger.d("Stop the active provider '" + mLocationProvider + "' before starting the new one"); stopInternal(); + } final MwmApplication application = MwmApplication.get(); final boolean containsGoogleServices = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(application) == ConnectionResult.SUCCESS; @@ -320,6 +321,25 @@ public enum LocationHelper public long getSavedLocationTime() { return mSavedLocationTime; } + public void switchToNextMode() + { + if (mErrorOccurred) + { + mLogger.d("Location services are still not available, no need to switch to the next mode."); + notifyLocationError(ERROR_DENIED); + return; + } + LocationState.nativeSwitchToNextMode(); + } + + /** + * @see LocationState#isTurnedOn() + */ + public boolean isTurnedOn() + { + return LocationState.isTurnedOn(); + } + void notifyCompassUpdated(long time, double magneticNorth, double trueNorth, double accuracy) { for (LocationListener listener : mListeners) @@ -428,7 +448,7 @@ public enum LocationHelper return mLocationStopped; } - void stop() + public void stop() { mLogger.d("stop()"); mLocationStopped = true; @@ -511,6 +531,12 @@ public enum LocationHelper removeListener(listener, false); } + @android.support.annotation.UiThread + public void removeListener() + { + removeListener(mLocationListener); + } + void startSensors() { mSensorHelper.start(); @@ -584,6 +610,7 @@ public enum LocationHelper */ public void restart() { + mLogger.d("restart()"); mActive &= !mListeners.isEmpty(); if (!mActive) { @@ -624,13 +651,16 @@ public enum LocationHelper */ private void startInternal() { - mLogger.d("startInternal()"); + mLogger.d("startInternal(), current provider is '" + mLocationProvider + "'"); mActive = mLocationProvider.start(); mLogger.d(mActive ? "SUCCESS" : "FAILURE"); if (mActive) + { + mErrorOccurred = false; mPredictor.resume(); + } else notifyLocationError(LocationHelper.ERROR_DENIED); } @@ -663,7 +693,7 @@ public enum LocationHelper */ public void attach(UiCallback callback) { - mLogger.d("attach()"); + mLogger.d("attach() callback = " + callback); if (mUiCallback != null) { diff --git a/android/src/com/mapswithme/maps/location/LocationPredictor.java b/android/src/com/mapswithme/maps/location/LocationPredictor.java index 5830ae054c..1aa45b5ce6 100644 --- a/android/src/com/mapswithme/maps/location/LocationPredictor.java +++ b/android/src/com/mapswithme/maps/location/LocationPredictor.java @@ -3,7 +3,6 @@ package com.mapswithme.maps.location; import android.location.Location; import com.mapswithme.maps.Framework; -import com.mapswithme.maps.LocationState; import com.mapswithme.util.concurrency.UiThread; class LocationPredictor diff --git a/android/src/com/mapswithme/maps/LocationState.java b/android/src/com/mapswithme/maps/location/LocationState.java similarity index 76% rename from android/src/com/mapswithme/maps/LocationState.java rename to android/src/com/mapswithme/maps/location/LocationState.java index 5a817b86c8..2244a3dd0c 100644 --- a/android/src/com/mapswithme/maps/LocationState.java +++ b/android/src/com/mapswithme/maps/location/LocationState.java @@ -1,8 +1,10 @@ -package com.mapswithme.maps; +package com.mapswithme.maps.location; + +import com.mapswithme.maps.MwmApplication; public final class LocationState { - public interface ModeChangeListener + interface ModeChangeListener { @SuppressWarnings("unused") void onMyPositionModeChanged(int newMode); @@ -15,23 +17,23 @@ public final class LocationState public static final int FOLLOW = 3; public static final int FOLLOW_AND_ROTATE = 4; - public static native void nativeSwitchToNextMode(); + static native void nativeSwitchToNextMode(); private static native int nativeGetMode(); - public static native void nativeSetListener(ModeChangeListener listener); - public static native void nativeRemoveListener(); + static native void nativeSetListener(ModeChangeListener listener); + static native void nativeRemoveListener(); private LocationState() {} /** * Checks if location state on the map is active (so its not turned off or pending). */ - public static boolean isTurnedOn() + static boolean isTurnedOn() { return hasLocation(getMode()); } - public static boolean hasLocation(int mode) + static boolean hasLocation(int mode) { return (mode > NOT_FOLLOW_NO_POSITION); } @@ -42,7 +44,7 @@ public final class LocationState return nativeGetMode(); } - public static String nameOf(int mode) + static String nameOf(int mode) { switch (mode) { diff --git a/android/src/com/mapswithme/maps/routing/ResultCodesHelper.java b/android/src/com/mapswithme/maps/routing/ResultCodesHelper.java index e8e1b0651d..84b21da173 100644 --- a/android/src/com/mapswithme/maps/routing/ResultCodesHelper.java +++ b/android/src/com/mapswithme/maps/routing/ResultCodesHelper.java @@ -6,7 +6,7 @@ import android.util.Pair; import java.util.ArrayList; import java.util.List; -import com.mapswithme.maps.LocationState; +import com.mapswithme.maps.location.LocationState; import com.mapswithme.maps.MwmApplication; import com.mapswithme.maps.R; diff --git a/android/src/com/mapswithme/maps/widget/menu/MyPositionButton.java b/android/src/com/mapswithme/maps/widget/menu/MyPositionButton.java index 7e86c70fc5..7c4497c46d 100644 --- a/android/src/com/mapswithme/maps/widget/menu/MyPositionButton.java +++ b/android/src/com/mapswithme/maps/widget/menu/MyPositionButton.java @@ -7,15 +7,13 @@ import android.util.SparseArray; import android.view.View; import android.widget.ImageView; -import com.mapswithme.maps.LocationState; +import com.mapswithme.maps.location.LocationState; import com.mapswithme.maps.R; import com.mapswithme.maps.routing.RoutingController; import com.mapswithme.util.Animations; import com.mapswithme.util.Graphics; import com.mapswithme.util.ThemeUtils; import com.mapswithme.util.UiUtils; -import com.mapswithme.util.statistics.AlohaHelper; -import com.mapswithme.util.statistics.Statistics; public class MyPositionButton { @@ -25,20 +23,10 @@ public class MyPositionButton private int mMode; - public MyPositionButton(@NonNull View button) + public MyPositionButton(@NonNull View button, @NonNull View.OnClickListener listener) { mButton = (ImageView) button; - mButton.setOnClickListener(new View.OnClickListener() - { - @Override - public void onClick(View v) - { - LocationState.nativeSwitchToNextMode(); - Statistics.INSTANCE.trackEvent(Statistics.EventName.TOOLBAR_MY_POSITION); - AlohaHelper.logClick(AlohaHelper.TOOLBAR_MY_POSITION); - } - }); - + mButton.setOnClickListener(listener); mIcons.clear(); }