From f4ad769a5b60b7443e650e1633e3d9ae6066ab32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Mon, 12 Nov 2018 17:11:51 +0100 Subject: [android] refactor LocationComponent to utilize LocationEngineRequest --- .../mapboxsdk/location/LocationComponent.java | 286 +++++++++++++++------ .../location/LocationComponentConstants.java | 6 + .../location/LocationComponentOptions.java | 2 +- .../mapboxsdk/location/LocationComponentTest.kt | 140 ++++++++++ .../android/MapboxGLAndroidSDKTestApp/build.gradle | 1 - .../activity/location/LocationFragmentActivity.kt | 3 +- .../activity/location/LocationModesActivity.java | 86 ++----- .../location/ManualLocationUpdatesActivity.java | 117 +++------ platform/android/build.gradle | 2 +- platform/android/gradle/dependencies.gradle | 2 +- 10 files changed, 421 insertions(+), 224 deletions(-) create mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java index d0b0bb733d..b2f1206d06 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java @@ -9,6 +9,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.RequiresPermission; import android.support.annotation.StyleRes; +import android.support.annotation.VisibleForTesting; import android.view.WindowManager; import com.mapbox.android.core.location.LocationEngine; @@ -28,13 +29,13 @@ import com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraIdleListener; import com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveListener; import com.mapbox.mapboxsdk.maps.MapboxMap.OnMapClickListener; +import java.lang.ref.WeakReference; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; import static android.Manifest.permission.ACCESS_COARSE_LOCATION; import static android.Manifest.permission.ACCESS_FINE_LOCATION; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_FASTEST_INTERVAL_MILLIS; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_INTERVAL_MILLIS; import static com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_TRACKING_TILT_ANIM_DURATION; import static com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_TRACKING_ZOOM_ANIM_DURATION; @@ -60,14 +61,23 @@ import static com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_T *

* Using this component requires you to request permission beforehand manually or using * {@link com.mapbox.android.core.permissions.PermissionsManager}. Either - * {@code ACCESS_COARSE_LOCATION} or {@code ACCESS_FINE_LOCATION} permissions can be requested and - * this plugin work as expected. + * {@code ACCESS_COARSE_LOCATION} or {@code ACCESS_FINE_LOCATION} permissions can be requested for + * this component to work as expected. *

* This component offers a default, built-in {@link LocationEngine} with some of the activation methods. * This engine will be obtained by {@link LocationEngineProvider#getBestLocationEngine(Context, boolean)} which defaults - * to the {@link com.mapbox.android.core.location.MapboxFusedLocationEngineImpl}. If you'd like to utilize Google Play Services + * to the {@link com.mapbox.android.core.location.MapboxFusedLocationEngineImpl}. If you'd like to utilize Google + * Play Services * for more precise location updates, simply add the Google Play Location Services dependency in your build script. * This will make the default engine the {@link com.mapbox.android.core.location.GoogleLocationEngineImpl} instead. + * After a custom engine is passed to the component, or the built-in is initialized, + * the location updates are going to be requested with the {@link LocationEngineRequest}, either a default one, + * or the one passed during the activation. + * When using any engine, requesting/removing the location updates is going to be managed internally. + *

+ * You can also push location updates to the component without any internal engine management. + * To achieve that, use {@link #activateLocationComponent(Context, boolean)} with false. + * No engine is going to be initialized and you can push location updates with {@link #forceLocationUpdate(Location)}. *

* For location puck animation purposes, like navigation, * we recommend limiting the maximum zoom level of the map for the best user experience. @@ -76,17 +86,26 @@ import static com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_T */ public final class LocationComponent { private static final String TAG = "Mbgl-LocationComponent"; - private static final long DEFAULT_INTERVAL_MILLIS = 1000; - private static final long DEFAULT_FASTEST_INTERVAL_MILLIS = 1000; @NonNull private final MapboxMap mapboxMap; private LocationComponentOptions options; + @NonNull + private InternalLocationEngineProvider internalLocationEngineProvider = new InternalLocationEngineProvider(); @Nullable private LocationEngine locationEngine; - @Nullable + @NonNull + private LocationEngineRequest locationEngineRequest = + new LocationEngineRequest.Builder(DEFAULT_INTERVAL_MILLIS) + .setFastestInterval(DEFAULT_FASTEST_INTERVAL_MILLIS) + .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY) + .build(); + private LocationEngineCallback currentLocationEngineListener + = new CurrentLocationEngineCallback(this); + private LocationEngineCallback lastLocationEngineListener + = new LastLocationEngineCallback(this); + private CompassEngine compassEngine; - private boolean usingInternalLocationEngine; private LocationLayerController locationLayerController; private LocationCameraController locationCameraController; @@ -144,6 +163,28 @@ public final class LocationComponent { this.mapboxMap = mapboxMap; } + @VisibleForTesting + LocationComponent(@NonNull MapboxMap mapboxMap, + @NonNull LocationEngineCallback currentlistener, + @NonNull LocationEngineCallback lastListener, + @NonNull LocationLayerController locationLayerController, + @NonNull LocationCameraController locationCameraController, + @NonNull LocationAnimatorCoordinator locationAnimatorCoordinator, + @NonNull StaleStateManager staleStateManager, + @NonNull CompassEngine compassEngine, + @NonNull InternalLocationEngineProvider internalLocationEngineProvider) { + this.mapboxMap = mapboxMap; + this.currentLocationEngineListener = currentlistener; + this.lastLocationEngineListener = lastListener; + this.locationLayerController = locationLayerController; + this.locationCameraController = locationCameraController; + this.locationAnimatorCoordinator = locationAnimatorCoordinator; + this.staleStateManager = staleStateManager; + this.compassEngine = compassEngine; + this.internalLocationEngineProvider = internalLocationEngineProvider; + isInitialized = true; + } + /** * This method initializes the component and needs to be called before any other operations are performed. * Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}. @@ -154,8 +195,8 @@ public final class LocationComponent { */ @RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION}) public void activateLocationComponent(@NonNull Context context) { - activateLocationComponent(context, LocationComponentOptions.createFromAttributes(context, R.style - .mapbox_LocationComponent)); + activateLocationComponent(context, + LocationComponentOptions.createFromAttributes(context, R.style.mapbox_LocationComponent)); } /** @@ -175,6 +216,26 @@ public final class LocationComponent { } } + /** + * This method initializes the component and needs to be called before any other operations are performed. + * Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}. + * + * @param context the context + * @param useDefaultLocationEngine true if you want to initialize and use the built-in location engine or false if + * there should be no location engine initialized + * @param locationEngineRequest the location request + */ + @RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION}) + public void activateLocationComponent(@NonNull Context context, boolean useDefaultLocationEngine, + @NonNull LocationEngineRequest locationEngineRequest) { + setLocationEngineRequest(locationEngineRequest); + if (useDefaultLocationEngine) { + activateLocationComponent(context, R.style.mapbox_LocationComponent); + } else { + activateLocationComponent(context, null, R.style.mapbox_LocationComponent); + } + } + /** * This method initializes the component and needs to be called before any other operations are performed. * Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}. @@ -214,22 +275,53 @@ public final class LocationComponent { * @param locationEngine the engine, or null if you'd like to only force location updates * @param styleRes the LocationComponent style res */ + @RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION}) public void activateLocationComponent(@NonNull Context context, @Nullable LocationEngine locationEngine, @StyleRes int styleRes) { activateLocationComponent(context, locationEngine, LocationComponentOptions.createFromAttributes(context, styleRes)); } + /** + * This method initializes the component and needs to be called before any other operations are performed. + * Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}. + * + * @param context the context + * @param locationEngine the engine, or null if you'd like to only force location updates + * @param locationEngineRequest the location request + * @param styleRes the LocationComponent style res + */ + @RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION}) + public void activateLocationComponent(@NonNull Context context, @Nullable LocationEngine locationEngine, + @NonNull LocationEngineRequest locationEngineRequest, @StyleRes int styleRes) { + activateLocationComponent(context, locationEngine, locationEngineRequest, + LocationComponentOptions.createFromAttributes(context, styleRes)); + } + /** * This method will show the location icon and enable the camera tracking the location. * * @param context the context * @param locationEngine the engine */ + @RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION}) public void activateLocationComponent(@NonNull Context context, @NonNull LocationEngine locationEngine) { activateLocationComponent(context, locationEngine, R.style.mapbox_LocationComponent); } + /** + * This method will show the location icon and enable the camera tracking the location. + * + * @param context the context + * @param locationEngine the engine + * @param locationEngineRequest the location request + */ + @RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION}) + public void activateLocationComponent(@NonNull Context context, @NonNull LocationEngine locationEngine, + @NonNull LocationEngineRequest locationEngineRequest) { + activateLocationComponent(context, locationEngine, locationEngineRequest, R.style.mapbox_LocationComponent); + } + /** * This method initializes the component and needs to be called before any other operations are performed. * Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}. @@ -237,6 +329,7 @@ public final class LocationComponent { * @param locationEngine the engine, or null if you'd like to only force location updates * @param options the options */ + @RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION}) public void activateLocationComponent(@NonNull Context context, @Nullable LocationEngine locationEngine, @NonNull LocationComponentOptions options) { initialize(context, options); @@ -244,6 +337,24 @@ public final class LocationComponent { applyStyle(options); } + /** + * This method initializes the component and needs to be called before any other operations are performed. + * Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}. + * + * @param context the context + * @param locationEngine the engine, or null if you'd like to only force location updates + * @param locationEngineRequest the location request + * @param options the options + */ + public void activateLocationComponent(@NonNull Context context, @Nullable LocationEngine locationEngine, + @NonNull LocationEngineRequest locationEngineRequest, + @NonNull LocationComponentOptions options) { + initialize(context, options); + setLocationEngineRequest(locationEngineRequest); + setLocationEngine(locationEngine); + applyStyle(options); + } + /** * Manage component's visibility after activation. * @@ -491,22 +602,45 @@ public final class LocationComponent { * * @param locationEngine a {@link LocationEngine} this component should use to handle updates */ + @SuppressLint("MissingPermission") public void setLocationEngine(@Nullable LocationEngine locationEngine) { if (this.locationEngine != null) { // If internal location engines being used, extra steps need to be taken to deconstruct the // instance. - if (usingInternalLocationEngine) { - usingInternalLocationEngine = false; - } - this.locationEngine.removeLocationUpdates(locationEngineListener); + this.locationEngine.removeLocationUpdates(currentLocationEngineListener); this.locationEngine = null; } if (locationEngine != null) { this.locationEngine = locationEngine; + if (isLayerReady && isEnabled) { + setLastLocation(); + locationEngine.requestLocationUpdates( + locationEngineRequest, currentLocationEngineListener, Looper.getMainLooper()); + } } } + /** + * Set the location request that's going to be used when requesting location updates. + * + * @param locationEngineRequest the location request + */ + public void setLocationEngineRequest(@NonNull LocationEngineRequest locationEngineRequest) { + this.locationEngineRequest = locationEngineRequest; + + // reset internal LocationEngine ref to re-request location updates if needed + setLocationEngine(locationEngine); + } + + /** + * Get the location request that's going to be used when requesting location updates. + */ + @NonNull + public LocationEngineRequest getLocationEngineRequest() { + return locationEngineRequest; + } + /** * Returns the current {@link LocationEngine} being used for updating the user location. * @@ -546,31 +680,7 @@ public final class LocationComponent { @Nullable @RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION}) public Location getLastKnownLocation() { - if (locationEngine == null) { - return null; - } - // TODO: decide if we want to change signature of this method, for now make it work as is - final AtomicReference resultRef = new AtomicReference<>(); - CountDownLatch latch = new CountDownLatch(1); - locationEngine.getLastLocation(new LocationEngineCallback() { - @Override - public void onSuccess(LocationEngineResult result) { - resultRef.set(result); - latch.countDown(); - } - - @Override - public void onFailure(@NonNull Exception exception) { - // TODO: log exception - } - }); - try { - latch.await(2, TimeUnit.SECONDS); - } catch (InterruptedException ie) { - ie.printStackTrace(); - } - LocationEngineResult result = resultRef.get(); - return result != null ? result.getLastLocation() : null; + return lastLocation; } /** @@ -750,13 +860,11 @@ public final class LocationComponent { if (isEnabled) { if (locationEngine != null) { - if (usingInternalLocationEngine) { - try { - locationEngine.requestLocationUpdates(getLocationRequst(DEFAULT_INTERVAL_MILLIS), - locationEngineListener, Looper.getMainLooper()); - } catch (SecurityException se) { - se.printStackTrace(); - } + try { + locationEngine.requestLocationUpdates( + locationEngineRequest, currentLocationEngineListener, Looper.getMainLooper()); + } catch (SecurityException se) { + Logger.e(TAG, "Unable to request location updates", se); } } setCameraMode(locationCameraController.getCameraMode()); @@ -776,9 +884,7 @@ public final class LocationComponent { compassEngine.onStop(); locationAnimatorCoordinator.cancelAllAnimations(); if (locationEngine != null) { - if (usingInternalLocationEngine) { - locationEngine.removeLocationUpdates(locationEngineListener); - } + locationEngine.removeLocationUpdates(currentLocationEngineListener); } mapboxMap.removeOnCameraMoveListener(onCameraMoveListener); mapboxMap.removeOnCameraIdleListener(onCameraIdleListener); @@ -823,17 +929,9 @@ public final class LocationComponent { private void initializeLocationEngine(@NonNull Context context) { if (this.locationEngine != null) { - this.locationEngine.removeLocationUpdates(locationEngineListener); + this.locationEngine.removeLocationUpdates(currentLocationEngineListener); } - usingInternalLocationEngine = true; - locationEngine = LocationEngineProvider.getBestLocationEngine(context, false); - } - - private static LocationEngineRequest getLocationRequst(long interval) { - return new LocationEngineRequest.Builder(interval) - .setFastestInterval(DEFAULT_FASTEST_INTERVAL_MILLIS) - .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY) - .build(); + locationEngine = internalLocationEngineProvider.getBestLocationEngine(context, false); } private void enableLocationComponent() { @@ -846,10 +944,13 @@ public final class LocationComponent { onLocationLayerStop(); } - private void updateMapWithOptions(final LocationComponentOptions options) { - mapboxMap.setPadding( - options.padding()[0], options.padding()[1], options.padding()[2], options.padding()[3] - ); + private void updateMapWithOptions(@NonNull LocationComponentOptions options) { + int[] padding = options.padding(); + if (padding != null) { + mapboxMap.setPadding( + padding[0], padding[1], padding[2], padding[3] + ); + } } /** @@ -894,7 +995,11 @@ public final class LocationComponent { */ @SuppressLint("MissingPermission") private void setLastLocation() { - updateLocation(getLastKnownLocation(), true); + if (locationEngine != null) { + locationEngine.getLastLocation(lastLocationEngineListener); + } else { + updateLocation(getLastKnownLocation(), true); + } } private void setLastCompassHeading() { @@ -1005,22 +1110,49 @@ public final class LocationComponent { } }; - @NonNull - private LocationEngineCallback locationEngineListener = - new LocationEngineCallback() { + @VisibleForTesting + static final class CurrentLocationEngineCallback implements LocationEngineCallback { + private final WeakReference componentWeakReference; + + CurrentLocationEngineCallback(LocationComponent component) { + this.componentWeakReference = new WeakReference<>(component); + } + @Override public void onSuccess(LocationEngineResult result) { - Location location = result.getLastLocation(); - if (location != null) { - updateLocation(location, false); + LocationComponent component = componentWeakReference.get(); + if (component != null) { + component.updateLocation(result.getLastLocation(), false); } } @Override public void onFailure(@NonNull Exception exception) { - // TODO: handle error + Logger.e(TAG, "Failed to obtain location update", exception); + } + } + + @VisibleForTesting + static final class LastLocationEngineCallback implements LocationEngineCallback { + private final WeakReference componentWeakReference; + + LastLocationEngineCallback(LocationComponent component) { + this.componentWeakReference = new WeakReference<>(component); } - }; + + @Override + public void onSuccess(LocationEngineResult result) { + LocationComponent component = componentWeakReference.get(); + if (component != null) { + component.updateLocation(result.getLastLocation(), true); + } + } + + @Override + public void onFailure(@NonNull Exception exception) { + Logger.e(TAG, "Failed to obtain last location update", exception); + } + } @NonNull private OnCameraTrackingChangedListener cameraTrackingChangedListener = new OnCameraTrackingChangedListener() { @@ -1040,4 +1172,10 @@ public final class LocationComponent { } } }; + + static class InternalLocationEngineProvider { + LocationEngine getBestLocationEngine(@NonNull Context context, boolean background) { + return LocationEngineProvider.getBestLocationEngine(context, background); + } + } } \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentConstants.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentConstants.java index 4376ef4e60..29fe413a22 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentConstants.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentConstants.java @@ -26,6 +26,12 @@ final class LocationComponentConstants { // Threshold value to perform immediate camera/layer position update. static final double INSTANT_LOCATION_TRANSITION_THRESHOLD = 500_000; + // Default interval between location updates + static final long DEFAULT_INTERVAL_MILLIS = 1000; + + // Default fastest acceptable interval between location updates + static final long DEFAULT_FASTEST_INTERVAL_MILLIS = 1000; + // Sources static final String LOCATION_SOURCE = "mapbox-location-source"; static final String PROPERTY_GPS_BEARING = "mapbox-property-gps-bearing"; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java index 25666895aa..db86db925c 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java @@ -187,7 +187,7 @@ public class LocationComponentOptions implements Parcelable { * @return a new {@link LocationComponentOptions} object with the settings you defined in your style * resource */ - @Nullable + @NonNull public static LocationComponentOptions createFromAttributes(@NonNull Context context, @StyleRes int styleRes) { diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt new file mode 100644 index 0000000000..a1c7164d53 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt @@ -0,0 +1,140 @@ +package com.mapbox.mapboxsdk.location + +import android.content.Context +import android.content.res.Resources +import android.content.res.TypedArray +import android.os.Looper +import com.mapbox.android.core.location.LocationEngine +import com.mapbox.android.core.location.LocationEngineRequest +import com.mapbox.mapboxsdk.R +import com.mapbox.mapboxsdk.maps.MapboxMap +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.* +import org.mockito.MockitoAnnotations +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class LocationComponentTest { + private lateinit var locationComponent: LocationComponent + + @Mock + private lateinit var locationComponentOptions: LocationComponentOptions + + @Mock + private lateinit var mapboxMap: MapboxMap + + @Mock + private lateinit var context: Context + + @Mock + private lateinit var locationEngine: LocationEngine + + @Mock + private lateinit var locationEngineRequest: LocationEngineRequest + + @Mock + private lateinit var currentListener: LocationComponent.CurrentLocationEngineCallback + + @Mock + private lateinit var lastListener: LocationComponent.LastLocationEngineCallback + + @Mock + private lateinit var compassEngine: CompassEngine + + @Mock + private lateinit var locationLayerController: LocationLayerController + + @Mock + private lateinit var locationCameraController: LocationCameraController + + @Mock + private lateinit var locationAnimatorCoordinator: LocationAnimatorCoordinator + + @Mock + private lateinit var staleStateManager: StaleStateManager + + @Mock + private lateinit var locationEngineProvider: LocationComponent.InternalLocationEngineProvider + + @Before + fun before() { + MockitoAnnotations.initMocks(this) + locationComponent = LocationComponent(mapboxMap, currentListener, lastListener, locationLayerController, locationCameraController, locationAnimatorCoordinator, staleStateManager, compassEngine, locationEngineProvider) + doReturn(locationEngine).`when`(locationEngineProvider).getBestLocationEngine(context, false) + } + + @Test + fun activateWithRequestTest() { + locationComponent.activateLocationComponent(context, locationEngine, locationEngineRequest, locationComponentOptions) + Assert.assertEquals(locationEngineRequest, locationComponent.locationEngineRequest) + + doReturn(mock(TypedArray::class.java)).`when`(context) + .obtainStyledAttributes(R.style.mapbox_LocationComponent, R.styleable.mapbox_LocationComponent) + + val resources = mock(Resources::class.java) + + doReturn(resources).`when`(context).resources + doReturn(0f).`when`(resources) + .getDimension(R.dimen.mapbox_locationComponentTrackingMultiFingerMoveThreshold) + doReturn(0f).`when`(resources) + .getDimension(R.dimen.mapbox_locationComponentTrackingMultiFingerMoveThreshold) + locationComponent.activateLocationComponent(context, true, locationEngineRequest) + Assert.assertEquals(locationEngineRequest, locationComponent.locationEngineRequest) + } + + @Test + fun locationUpdatesWhenEnabledDisableTest() { + locationComponent.activateLocationComponent(context, locationEngine, locationEngineRequest, locationComponentOptions) + verify(locationEngine, times(0)).removeLocationUpdates(currentListener) + verify(locationEngine, times(0)).requestLocationUpdates(eq(locationEngineRequest), eq(currentListener), any(Looper::class.java)) + + locationComponent.onStart() + verify(locationEngine, times(0)).removeLocationUpdates(currentListener) + verify(locationEngine, times(0)).requestLocationUpdates(eq(locationEngineRequest), eq(currentListener), any(Looper::class.java)) + + locationComponent.isLocationComponentEnabled = true + verify(locationEngine).requestLocationUpdates(eq(locationEngineRequest), eq(currentListener), any(Looper::class.java)) + + locationComponent.isLocationComponentEnabled = false + verify(locationEngine).requestLocationUpdates(eq(locationEngineRequest), eq(currentListener), any(Looper::class.java)) + verify(locationEngine).removeLocationUpdates(currentListener) + } + + @Test + fun locationUpdatesWhenStartedStoppedTest() { + locationComponent.activateLocationComponent(context, locationEngine, locationEngineRequest, locationComponentOptions) + locationComponent.onStart() + locationComponent.isLocationComponentEnabled = true + + locationComponent.onStop() + verify(locationEngine).removeLocationUpdates(currentListener) + + locationComponent.onStart() + verify(locationEngine, times(2)).requestLocationUpdates(eq(locationEngineRequest), eq(currentListener), any(Looper::class.java)) + } + + @Test + fun locationUpdatesWhenNewRequestTest() { + locationComponent.activateLocationComponent(context, locationEngine, locationEngineRequest, locationComponentOptions) + locationComponent.onStart() + locationComponent.isLocationComponentEnabled = true + + val newRequest = mock(LocationEngineRequest::class.java) + locationComponent.locationEngineRequest = newRequest + verify(locationEngine).removeLocationUpdates(currentListener) + verify(locationEngine).requestLocationUpdates(eq(newRequest), eq(currentListener), any(Looper::class.java)) + } + + @Test + fun lastLocationUpdateOnStartTest() { + locationComponent.activateLocationComponent(context, locationEngine, locationEngineRequest, locationComponentOptions) + locationComponent.onStart() + locationComponent.isLocationComponentEnabled = true + + verify(locationEngine).getLastLocation(lastListener) + } +} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle index 5588db5fc9..384c49eb7f 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle +++ b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle @@ -65,7 +65,6 @@ dependencies { implementation dependenciesList.supportDesign implementation dependenciesList.supportConstraintLayout - // implementation dependenciesList.lost implementation dependenciesList.gmsLocation implementation dependenciesList.timber debugImplementation dependenciesList.leakCanaryDebug diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationFragmentActivity.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationFragmentActivity.kt index 98fe13e8eb..5626b82ccc 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationFragmentActivity.kt +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationFragmentActivity.kt @@ -105,8 +105,7 @@ class LocationFragmentActivity : AppCompatActivity() { mapView.getMapAsync { mapboxMap = it component = mapboxMap.locationComponent - component?.activateLocationComponent(activity, - LocationEngineProvider.getBestLocationEngine(activity, false)) + component?.activateLocationComponent(activity) component?.isLocationComponentEnabled = true component?.locationEngine?.getLastLocation(this) } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationModesActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationModesActivity.java index 078b5c7ab5..25d87c32c7 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationModesActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationModesActivity.java @@ -4,45 +4,36 @@ import android.annotation.SuppressLint; import android.content.res.Configuration; import android.location.Location; import android.os.Bundle; -import android.os.Looper; import android.support.annotation.NonNull; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.ListPopupWindow; import android.view.Menu; import android.view.MenuItem; -import android.view.View; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.Toast; -import com.mapbox.android.core.location.LocationEngine; -import com.mapbox.android.core.location.LocationEngineCallback; -import com.mapbox.android.core.location.LocationEngineProvider; import com.mapbox.android.core.location.LocationEngineRequest; -import com.mapbox.android.core.location.LocationEngineResult; import com.mapbox.android.core.permissions.PermissionsListener; import com.mapbox.android.core.permissions.PermissionsManager; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; import com.mapbox.mapboxsdk.constants.Style; -import com.mapbox.mapboxsdk.location.OnLocationClickListener; -import com.mapbox.mapboxsdk.maps.MapView; -import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; -import com.mapbox.mapboxsdk.location.LocationComponentOptions; import com.mapbox.mapboxsdk.location.LocationComponent; +import com.mapbox.mapboxsdk.location.LocationComponentOptions; import com.mapbox.mapboxsdk.location.OnCameraTrackingChangedListener; +import com.mapbox.mapboxsdk.location.OnLocationClickListener; import com.mapbox.mapboxsdk.location.modes.CameraMode; import com.mapbox.mapboxsdk.location.modes.RenderMode; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; import com.mapbox.mapboxsdk.testapp.R; import java.util.ArrayList; import java.util.List; public class LocationModesActivity extends AppCompatActivity implements OnMapReadyCallback, - OnLocationClickListener, OnCameraTrackingChangedListener { - - private static final long DEFAULT_INTERVAL_MILLIS = 1000; - private static final long DEFAULT_FASTEST_INTERVAL_MILLIS = 1000; + OnLocationClickListener, OnCameraTrackingChangedListener { private MapView mapView; private Button locationModeBtn; @@ -51,7 +42,6 @@ public class LocationModesActivity extends AppCompatActivity implements OnMapRea private PermissionsManager permissionsManager; private LocationComponent locationComponent; - private LocationEngine locationEngine; private MapboxMap mapboxMap; private boolean customStyle; @@ -67,8 +57,6 @@ public class LocationModesActivity extends AppCompatActivity implements OnMapRea private Location lastLocation; - private LocationEngineCallback locationEngineCallback; - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -77,25 +65,19 @@ public class LocationModesActivity extends AppCompatActivity implements OnMapRea mapView = findViewById(R.id.mapView); locationModeBtn = findViewById(R.id.button_location_mode); - locationModeBtn.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (locationComponent == null) { - return; - } - showModeListDialog(); + locationModeBtn.setOnClickListener(v -> { + if (locationComponent == null) { + return; } + showModeListDialog(); }); locationTrackingBtn = findViewById(R.id.button_location_tracking); - locationTrackingBtn.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (locationComponent == null) { - return; - } - showTrackingListDialog(); + locationTrackingBtn.setOnClickListener(v -> { + if (locationComponent == null) { + return; } + showTrackingListDialog(); }); @@ -141,20 +123,6 @@ public class LocationModesActivity extends AppCompatActivity implements OnMapRea public void onMapReady(@NonNull MapboxMap mapboxMap) { this.mapboxMap = mapboxMap; - locationEngine = LocationEngineProvider.getBestLocationEngine(getApplicationContext(), - false); - locationEngineCallback = new LocationEngineCallback() { - @Override - public void onSuccess(LocationEngineResult result) { - // noop - } - - @Override - public void onFailure(@NonNull Exception exception) { - // noop - } - }; - int[] padding; if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { padding = new int[] {0, 750, 0, 0}; @@ -168,7 +136,13 @@ public class LocationModesActivity extends AppCompatActivity implements OnMapRea .build(); locationComponent = mapboxMap.getLocationComponent(); - locationComponent.activateLocationComponent(this, locationEngine, options); + locationComponent.activateLocationComponent(this, true, + new LocationEngineRequest.Builder(750) + .setFastestInterval(750) + .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY) + .build() + ); + locationComponent.applyStyle(options); locationComponent.setLocationComponentEnabled(true); locationComponent.addOnLocationClickListener(this); locationComponent.addOnCameraTrackingChangedListener(this); @@ -248,14 +222,6 @@ public class LocationModesActivity extends AppCompatActivity implements OnMapRea protected void onStart() { super.onStart(); mapView.onStart(); - if (locationEngine != null) { - try { - locationEngine.requestLocationUpdates(getLocationRequst(DEFAULT_INTERVAL_MILLIS), - locationEngineCallback, Looper.getMainLooper()); - } catch (SecurityException se) { - se.printStackTrace(); - } - } } @Override @@ -274,9 +240,6 @@ public class LocationModesActivity extends AppCompatActivity implements OnMapRea protected void onStop() { super.onStop(); mapView.onStop(); - if (locationEngine != null) { - locationEngine.removeLocationUpdates(locationEngineCallback); - } } @SuppressLint("MissingPermission") @@ -303,13 +266,6 @@ public class LocationModesActivity extends AppCompatActivity implements OnMapRea mapView.onLowMemory(); } - private static LocationEngineRequest getLocationRequst(long interval) { - return new LocationEngineRequest.Builder(interval) - .setFastestInterval(DEFAULT_FASTEST_INTERVAL_MILLIS) - .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY) - .build(); - } - @Override public void onLocationComponentClick() { Toast.makeText(this, "OnLocationComponentClick", Toast.LENGTH_LONG).show(); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/ManualLocationUpdatesActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/ManualLocationUpdatesActivity.java index ee65b97570..50f86efbbd 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/ManualLocationUpdatesActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/ManualLocationUpdatesActivity.java @@ -1,38 +1,28 @@ package com.mapbox.mapboxsdk.testapp.activity.location; -import android.location.Location; +import android.annotation.SuppressLint; import android.os.Bundle; -import android.os.Looper; import android.support.annotation.NonNull; import android.support.design.widget.FloatingActionButton; import android.support.v7.app.AppCompatActivity; -import android.view.View; import android.widget.Toast; import com.mapbox.android.core.location.LocationEngine; -import com.mapbox.android.core.location.LocationEngineCallback; import com.mapbox.android.core.location.LocationEngineProvider; import com.mapbox.android.core.location.LocationEngineRequest; -import com.mapbox.android.core.location.LocationEngineResult; import com.mapbox.android.core.permissions.PermissionsListener; import com.mapbox.android.core.permissions.PermissionsManager; import com.mapbox.mapboxsdk.geometry.LatLngBounds; +import com.mapbox.mapboxsdk.location.LocationComponent; +import com.mapbox.mapboxsdk.location.modes.RenderMode; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; -import com.mapbox.mapboxsdk.location.LocationComponent; -import com.mapbox.mapboxsdk.location.modes.RenderMode; import com.mapbox.mapboxsdk.testapp.R; import java.util.List; -import timber.log.Timber; - -public class ManualLocationUpdatesActivity extends AppCompatActivity implements OnMapReadyCallback, - LocationEngineCallback { - - private static final long DEFAULT_INTERVAL_MILLIS = 1000; - private static final long DEFAULT_FASTEST_INTERVAL_MILLIS = 1000; +public class ManualLocationUpdatesActivity extends AppCompatActivity implements OnMapReadyCallback { private MapView mapView; private LocationComponent locationComponent; @@ -44,43 +34,38 @@ public class ManualLocationUpdatesActivity extends AppCompatActivity implements super.onCreate(savedInstanceState); setContentView(R.layout.activity_location_manual_update); + locationEngine = LocationEngineProvider.getBestLocationEngine(this, false); + FloatingActionButton fabManualUpdate = findViewById(R.id.fabManualLocationChange); - fabManualUpdate.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (locationComponent != null && locationComponent.getLocationEngine() == null) { - locationComponent.forceLocationUpdate( - Utils.getRandomLocation(LatLngBounds.from(60, 25, 40, -5))); - } + fabManualUpdate.setOnClickListener(v -> { + if (locationComponent != null && locationComponent.getLocationEngine() == null) { + locationComponent.forceLocationUpdate( + Utils.getRandomLocation(LatLngBounds.from(60, 25, 40, -5))); } }); fabManualUpdate.setEnabled(false); FloatingActionButton fabToggle = findViewById(R.id.fabToggleManualLocation); - fabToggle.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (locationComponent != null) { - locationComponent.setLocationEngine(locationComponent.getLocationEngine() == null ? locationEngine : - null); - - if (locationComponent.getLocationEngine() == null) { - fabToggle.setImageResource(R.drawable.ic_layers_clear); - fabManualUpdate.setEnabled(true); - fabManualUpdate.setAlpha(1f); - Toast.makeText( - ManualLocationUpdatesActivity.this.getApplicationContext(), - "LocationEngine disable, use manual updates", - Toast.LENGTH_SHORT).show(); - } else { - fabToggle.setImageResource(R.drawable.ic_layers); - fabManualUpdate.setEnabled(false); - fabManualUpdate.setAlpha(0.5f); - Toast.makeText( - ManualLocationUpdatesActivity.this.getApplicationContext(), - "LocationEngine enabled", - Toast.LENGTH_SHORT).show(); - } + fabToggle.setOnClickListener(v -> { + if (locationComponent != null) { + locationComponent.setLocationEngine(locationComponent.getLocationEngine() == null ? locationEngine : null); + + if (locationComponent.getLocationEngine() == null) { + fabToggle.setImageResource(R.drawable.ic_layers_clear); + fabManualUpdate.setEnabled(true); + fabManualUpdate.setAlpha(1f); + Toast.makeText( + ManualLocationUpdatesActivity.this.getApplicationContext(), + "LocationEngine disabled, use manual updates", + Toast.LENGTH_SHORT).show(); + } else { + fabToggle.setImageResource(R.drawable.ic_layers); + fabManualUpdate.setEnabled(false); + fabManualUpdate.setAlpha(0.5f); + Toast.makeText( + ManualLocationUpdatesActivity.this.getApplicationContext(), + "LocationEngine enabled", + Toast.LENGTH_SHORT).show(); } } }); @@ -119,35 +104,25 @@ public class ManualLocationUpdatesActivity extends AppCompatActivity implements permissionsManager.onRequestPermissionsResult(requestCode, permissions, grantResults); } + @SuppressLint("MissingPermission") @Override public void onMapReady(@NonNull MapboxMap mapboxMap) { - locationEngine = LocationEngineProvider.getBestLocationEngine(getApplicationContext(), - false); locationComponent = mapboxMap.getLocationComponent(); - locationComponent.activateLocationComponent(this, locationEngine); + locationComponent.activateLocationComponent( + this, + locationEngine, + new LocationEngineRequest.Builder(500) + .setFastestInterval(500) + .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY) + .build()); locationComponent.setLocationComponentEnabled(true); locationComponent.setRenderMode(RenderMode.COMPASS); } - private static LocationEngineRequest getLocationRequst(long interval) { - return new LocationEngineRequest.Builder(interval) - .setFastestInterval(DEFAULT_FASTEST_INTERVAL_MILLIS) - .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY) - .build(); - } - @Override protected void onStart() { super.onStart(); mapView.onStart(); - if (locationEngine != null) { - try { - locationEngine.requestLocationUpdates(getLocationRequst(DEFAULT_INTERVAL_MILLIS), - this, Looper.getMainLooper()); - } catch (SecurityException se) { - se.printStackTrace(); - } - } } @Override @@ -166,9 +141,6 @@ public class ManualLocationUpdatesActivity extends AppCompatActivity implements protected void onStop() { super.onStop(); mapView.onStop(); - if (locationEngine != null) { - locationEngine.removeLocationUpdates(this); - } } @Override @@ -188,17 +160,4 @@ public class ManualLocationUpdatesActivity extends AppCompatActivity implements super.onLowMemory(); mapView.onLowMemory(); } - - @Override - public void onSuccess(LocationEngineResult result) { - Location location = result.getLastLocation(); - if (location != null) { - Timber.d("Location change occurred: %s", location.toString()); - } - } - - @Override - public void onFailure(@NonNull Exception exception) { - Timber.d("Location engine error occurred: %s", exception.getMessage()); - } } \ No newline at end of file diff --git a/platform/android/build.gradle b/platform/android/build.gradle index 86e4f4f5f8..c2bcde180d 100644 --- a/platform/android/build.gradle +++ b/platform/android/build.gradle @@ -18,7 +18,7 @@ allprojects { google() jcenter() // Snapshot repository - //maven { url "http://oss.sonatype.org/content/repositories/snapshots/" } + maven { url "http://oss.sonatype.org/content/repositories/snapshots/" } } } diff --git a/platform/android/gradle/dependencies.gradle b/platform/android/gradle/dependencies.gradle index 296572d214..61f619964f 100644 --- a/platform/android/gradle/dependencies.gradle +++ b/platform/android/gradle/dependencies.gradle @@ -9,7 +9,7 @@ ext { versions = [ mapboxServices : '4.1.0', - mapboxTelemetry : '4.0.0', + mapboxTelemetry : '4.1.0-SNAPSHOT', mapboxGestures : '0.3.0', supportLib : '27.1.1', constraintLayout: '1.1.2', -- cgit v1.2.1