From baaf20b32eb8453ccaeca358caddafe60a6e0563 Mon Sep 17 00:00:00 2001 From: Chuck Greb Date: Tue, 17 Jan 2017 14:25:26 -0500 Subject: [PATCH] Upgrades Robolectric to version 3.2.1 (#153) * Upgrades Robolectric to version 3.2.1 * Add checkstyle suppression for copied file `LostShadowLocationManager.java` --- config/checkstyle/suppressions.xml | 1 + lost/build.gradle | 3 +- .../android/lost/api/LocationRequestTest.java | 4 +- .../lost/api/LocationServicesTest.java | 4 +- .../FusedLocationProviderServiceImplTest.java | 17 +- .../lost/internal/FusionEngineTest.java | 4 +- .../internal/GeofencingIntentSenderTest.java | 4 +- .../lost/internal/LostApiClientImplTest.java | 4 +- .../shadows/LostShadowLocationManager.java | 1054 ++++++++--------- 9 files changed, 517 insertions(+), 578 deletions(-) diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml index 83f4ab1..628f076 100644 --- a/config/checkstyle/suppressions.xml +++ b/config/checkstyle/suppressions.xml @@ -10,4 +10,5 @@ + diff --git a/lost/build.gradle b/lost/build.gradle index 7906319..eae2991 100644 --- a/lost/build.gradle +++ b/lost/build.gradle @@ -69,10 +69,9 @@ dependencies { testCompile 'com.google.guava:guava:18.0' testCompile 'junit:junit:4.12' - testCompile 'org.robolectric:robolectric:3.1.2' + testCompile 'org.robolectric:robolectric:3.2.1' testCompile 'com.squareup:fest-android:1.0.7' testCompile 'org.mockito:mockito-core:1.9.5' - } apply from: file('../gradle-mvn-push.gradle') diff --git a/lost/src/test/java/com/mapzen/android/lost/api/LocationRequestTest.java b/lost/src/test/java/com/mapzen/android/lost/api/LocationRequestTest.java index babdaa6..d662483 100644 --- a/lost/src/test/java/com/mapzen/android/lost/api/LocationRequestTest.java +++ b/lost/src/test/java/com/mapzen/android/lost/api/LocationRequestTest.java @@ -5,14 +5,14 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.robolectric.RobolectricGradleTestRunner; +import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import static com.mapzen.android.lost.api.LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY; import static com.mapzen.android.lost.api.LocationRequest.PRIORITY_HIGH_ACCURACY; import static org.fest.assertions.api.Assertions.assertThat; -@RunWith(RobolectricGradleTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, sdk = 21, manifest = Config.NONE) public class LocationRequestTest { private LocationRequest locationRequest; diff --git a/lost/src/test/java/com/mapzen/android/lost/api/LocationServicesTest.java b/lost/src/test/java/com/mapzen/android/lost/api/LocationServicesTest.java index 385737a..2667187 100644 --- a/lost/src/test/java/com/mapzen/android/lost/api/LocationServicesTest.java +++ b/lost/src/test/java/com/mapzen/android/lost/api/LocationServicesTest.java @@ -7,12 +7,12 @@ import org.junit.Test; import org.junit.runner.RunWith; -import org.robolectric.RobolectricGradleTestRunner; +import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import static org.fest.assertions.api.Assertions.assertThat; -@RunWith(RobolectricGradleTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, sdk = 21, manifest = Config.NONE) public class LocationServicesTest { diff --git a/lost/src/test/java/com/mapzen/android/lost/internal/FusedLocationProviderServiceImplTest.java b/lost/src/test/java/com/mapzen/android/lost/internal/FusedLocationProviderServiceImplTest.java index a4852e4..5c34abc 100644 --- a/lost/src/test/java/com/mapzen/android/lost/internal/FusedLocationProviderServiceImplTest.java +++ b/lost/src/test/java/com/mapzen/android/lost/internal/FusedLocationProviderServiceImplTest.java @@ -521,9 +521,10 @@ private File getTestGpxTrace() throws IOException { @Test public void requestLocationUpdates_shouldNotNotifyRemovedPendingIntent() throws Exception { LocationRequest request = LocationRequest.create().setPriority(PRIORITY_HIGH_ACCURACY); - Intent intent = new Intent(application, TestService.class); - PendingIntent pendingIntent1 = PendingIntent.getService(application, 0, intent, 0); - PendingIntent pendingIntent2 = PendingIntent.getService(application, 0, intent, 0); + Intent intent1 = new Intent(application, TestService.class); + Intent intent2 = new Intent(application, OtherTestService.class); + PendingIntent pendingIntent1 = PendingIntent.getService(application, 0, intent1, 0); + PendingIntent pendingIntent2 = PendingIntent.getService(application, 0, intent2, 0); api.requestLocationUpdates(client, request, pendingIntent1); api.requestLocationUpdates(client, request, pendingIntent2); @@ -1300,11 +1301,19 @@ private File getTestGpxTrace() throws IOException { public class TestService extends IntentService { public TestService() { - super("test"); + super("test service"); } @Override protected void onHandleIntent(Intent intent) { } } + public class OtherTestService extends IntentService { + public OtherTestService() { + super("other test service"); + } + + @Override protected void onHandleIntent(Intent intent) { + } + } } diff --git a/lost/src/test/java/com/mapzen/android/lost/internal/FusionEngineTest.java b/lost/src/test/java/com/mapzen/android/lost/internal/FusionEngineTest.java index d8b413c..8891bcd 100644 --- a/lost/src/test/java/com/mapzen/android/lost/internal/FusionEngineTest.java +++ b/lost/src/test/java/com/mapzen/android/lost/internal/FusionEngineTest.java @@ -6,7 +6,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.robolectric.RobolectricGradleTestRunner; +import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowLocationManager; @@ -36,7 +36,7 @@ import static org.robolectric.RuntimeEnvironment.application; import static org.robolectric.Shadows.shadowOf; -@RunWith(RobolectricGradleTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, sdk = 21, manifest = Config.NONE) public class FusionEngineTest { private FusionEngine fusionEngine; diff --git a/lost/src/test/java/com/mapzen/android/lost/internal/GeofencingIntentSenderTest.java b/lost/src/test/java/com/mapzen/android/lost/internal/GeofencingIntentSenderTest.java index b2d37e8..da7c9ee 100644 --- a/lost/src/test/java/com/mapzen/android/lost/internal/GeofencingIntentSenderTest.java +++ b/lost/src/test/java/com/mapzen/android/lost/internal/GeofencingIntentSenderTest.java @@ -8,7 +8,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.robolectric.RobolectricGradleTestRunner; +import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import android.content.Context; @@ -25,7 +25,7 @@ import static org.mockito.Mockito.when; @SuppressWarnings("MissingPermission") -@RunWith(RobolectricGradleTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, sdk = 21, manifest = Config.NONE) public class GeofencingIntentSenderTest { diff --git a/lost/src/test/java/com/mapzen/android/lost/internal/LostApiClientImplTest.java b/lost/src/test/java/com/mapzen/android/lost/internal/LostApiClientImplTest.java index 4d24556..4b48a18 100644 --- a/lost/src/test/java/com/mapzen/android/lost/internal/LostApiClientImplTest.java +++ b/lost/src/test/java/com/mapzen/android/lost/internal/LostApiClientImplTest.java @@ -10,7 +10,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.robolectric.RobolectricGradleTestRunner; +import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import android.content.ComponentName; @@ -24,7 +24,7 @@ import static org.robolectric.RuntimeEnvironment.application; import static org.robolectric.Shadows.shadowOf; -@RunWith(RobolectricGradleTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, sdk = 21, manifest = Config.NONE) public class LostApiClientImplTest { private LostApiClient client; diff --git a/lost/src/test/java/com/mapzen/android/lost/shadows/LostShadowLocationManager.java b/lost/src/test/java/com/mapzen/android/lost/shadows/LostShadowLocationManager.java index ad896b4..bd3b495 100644 --- a/lost/src/test/java/com/mapzen/android/lost/shadows/LostShadowLocationManager.java +++ b/lost/src/test/java/com/mapzen/android/lost/shadows/LostShadowLocationManager.java @@ -1,23 +1,24 @@ package com.mapzen.android.lost.shadows; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; +import org.robolectric.shadows.ShadowApplication; import android.app.PendingIntent; +import android.app.PendingIntent.CanceledException; import android.content.Intent; import android.location.Criteria; -import android.location.GpsStatus; +import android.location.GpsStatus.Listener; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Looper; -import org.robolectric.annotation.Implementation; -import org.robolectric.annotation.Implements; -import org.robolectric.shadows.ShadowApplication; - import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; @@ -25,567 +26,496 @@ import java.util.Set; /** - * Shadow that pastes in existing Robolectric class and adds missing method: + * Shadow that copies existing Robolectric class and adds missing method: * {@link LocationManager#requestSingleUpdate(String provider, LocationListener listener, * Looper looper)} */ @Implements(LocationManager.class) public class LostShadowLocationManager { - - private final Map providersEnabled = new LinkedHashMap<>(); - private final Map lastKnownLocations = new HashMap<>(); - private final Map requestLocationUdpateCriteriaPendingIntents = - new HashMap<>(); - private final Map requestLocationUdpateProviderPendingIntents = - new HashMap<>(); - private final ArrayList removedLocationListeners = new ArrayList<>(); - - private final ArrayList gpsStatusListeners = new ArrayList<>(); - private Criteria lastBestProviderCriteria; - private boolean lastBestProviderEnabled; - private String bestEnabledProvider, bestDisabledProvider; - - /** Location listeners along with metadata on when they should be fired. */ - private static final class ListenerRegistration { - final long minTime; - final float minDistance; - final LocationListener listener; - final String provider; - Location lastSeenLocation; - long lastSeenTime; - - ListenerRegistration(String provider, long minTime, float minDistance, Location - locationAtCreation, LocationListener listener) { - this.provider = provider; - this.minTime = minTime; - this.minDistance = minDistance; - this.lastSeenTime = locationAtCreation == null ? 0 : locationAtCreation.getTime(); - this.lastSeenLocation = locationAtCreation; - this.listener = listener; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - ListenerRegistration that = (ListenerRegistration) o; - - if (minTime != that.minTime) { - return false; - } - if (Float.compare(that.minDistance, minDistance) != 0) { - return false; - } - if (lastSeenTime != that.lastSeenTime) { - return false; - } - if (!listener.equals(that.listener)) { - return false; - } - if (!provider.equals(that.provider)) { - return false; - } - return lastSeenLocation != null ? lastSeenLocation.equals(that.lastSeenLocation) : - that.lastSeenLocation == null; - - } - - @Override - public int hashCode() { - int result = (int) (minTime ^ (minTime >>> 32)); - result = 31 * result + (minDistance != +0.0f ? Float.floatToIntBits(minDistance) : 0); - result = 31 * result + listener.hashCode(); - result = 31 * result + provider.hashCode(); - result = 31 * result + (lastSeenLocation != null ? lastSeenLocation.hashCode() : 0); - result = 31 * result + (int) (lastSeenTime ^ (lastSeenTime >>> 32)); - return result; - } - } - - /** Mapped by provider. */ - private final Map> locationListeners = - new HashMap<>(); - - @Implementation - public boolean isProviderEnabled(String provider) { - LocationProviderEntry map = providersEnabled.get(provider); - if (map != null) { - Boolean isEnabled = map.getKey(); - return isEnabled == null ? true : isEnabled; - } + private final Map providersEnabled = new LinkedHashMap<>(); + private final Map lastKnownLocations = new HashMap<>(); + private final Map requestLocationUdpateCriteriaPendingIntents = new HashMap<>(); + private final Map requestLocationUdpateProviderPendingIntents = new HashMap<>(); + private final ArrayList removedLocationListeners = new ArrayList<>(); + + private final ArrayList gpsStatusListeners = new ArrayList<>(); + private Criteria lastBestProviderCriteria; + private boolean lastBestProviderEnabled; + private String bestEnabledProvider, bestDisabledProvider; + + /** Location listeners along with metadata on when they should be fired. */ + private static final class ListenerRegistration { + final long minTime; + final float minDistance; + final LocationListener listener; + final String provider; + Location lastSeenLocation; + long lastSeenTime; + + ListenerRegistration(String provider, long minTime, float minDistance, Location locationAtCreation, + LocationListener listener) { + this.provider = provider; + this.minTime = minTime; + this.minDistance = minDistance; + this.lastSeenTime = locationAtCreation == null ? 0 : locationAtCreation.getTime(); + this.lastSeenLocation = locationAtCreation; + this.listener = listener; + } + } + + /** Mapped by provider. */ + private final Map> locationListeners = + new HashMap<>(); + + @Implementation + public boolean isProviderEnabled(String provider) { + LocationProviderEntry map = providersEnabled.get(provider); + if (map != null) { + Boolean isEnabled = map.getKey(); + return isEnabled == null ? true : isEnabled; + } + return false; + } + + @Implementation + public List getAllProviders() { + Set allKnownProviders = new LinkedHashSet<>(providersEnabled.keySet()); + allKnownProviders.add(LocationManager.GPS_PROVIDER); + allKnownProviders.add(LocationManager.NETWORK_PROVIDER); + allKnownProviders.add(LocationManager.PASSIVE_PROVIDER); + + return new ArrayList<>(allKnownProviders); + } + + /** + * Sets the value to return from {@link #isProviderEnabled(String)} for the given {@code provider} + * + * @param provider + * name of the provider whose status to set + * @param isEnabled + * whether that provider should appear enabled + */ + public void setProviderEnabled(String provider, boolean isEnabled) { + setProviderEnabled(provider, isEnabled, null); + } + + public void setProviderEnabled(String provider, boolean isEnabled, List criteria) { + LocationProviderEntry providerEntry = providersEnabled.get(provider); + if (providerEntry == null) { + providerEntry = new LocationProviderEntry(); + } + providerEntry.enabled = isEnabled; + providerEntry.criteria = criteria; + providersEnabled.put(provider, providerEntry); + List locationUpdateListeners = new ArrayList<>(getRequestLocationUpdateListeners()); + for (LocationListener locationUpdateListener : locationUpdateListeners) { + if (isEnabled) { + locationUpdateListener.onProviderEnabled(provider); + } else { + locationUpdateListener.onProviderDisabled(provider); + } + } + // Send intent to notify about provider status + final Intent intent = new Intent(); + intent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, isEnabled); + ShadowApplication.getInstance().sendBroadcast(intent); + Set requestLocationUdpatePendingIntentSet = requestLocationUdpateCriteriaPendingIntents + .keySet(); + for (PendingIntent requestLocationUdpatePendingIntent : requestLocationUdpatePendingIntentSet) { + try { + requestLocationUdpatePendingIntent.send(); + } catch (CanceledException e) { + requestLocationUdpateCriteriaPendingIntents + .remove(requestLocationUdpatePendingIntent); + } + } + // if this provider gets disabled and it was the best active provider, then it's not anymore + if (provider.equals(bestEnabledProvider) && !isEnabled) { + bestEnabledProvider = null; + } + } + + @Implementation + public List getProviders(boolean enabledOnly) { + ArrayList enabledProviders = new ArrayList<>(); + for (String provider : getAllProviders()) { + if (!enabledOnly || providersEnabled.get(provider) != null) { + enabledProviders.add(provider); + } + } + return enabledProviders; + } + + @Implementation + public Location getLastKnownLocation(String provider) { + return lastKnownLocations.get(provider); + } + + @Implementation + public boolean addGpsStatusListener(Listener listener) { + if (!gpsStatusListeners.contains(listener)) { + gpsStatusListeners.add(listener); + } + return true; + } + + @Implementation + public void removeGpsStatusListener(Listener listener) { + gpsStatusListeners.remove(listener); + } + + @Implementation + public String getBestProvider(Criteria criteria, boolean enabled) { + lastBestProviderCriteria = criteria; + lastBestProviderEnabled = enabled; + + if (criteria == null) { + return getBestProviderWithNoCriteria(enabled); + } + + return getBestProviderWithCriteria(criteria, enabled); + } + + private String getBestProviderWithCriteria(Criteria criteria, boolean enabled) { + List providers = getProviders(enabled); + int powerRequirement = criteria.getPowerRequirement(); + int accuracy = criteria.getAccuracy(); + for (String provider : providers) { + LocationProviderEntry locationProviderEntry = providersEnabled.get(provider); + if (locationProviderEntry == null) { + continue; + } + List criteriaList = locationProviderEntry.getValue(); + if (criteriaList == null) { + continue; + } + for (Criteria criteriaListItem : criteriaList) { + if (criteria.equals(criteriaListItem)) { + return provider; + } else if (criteriaListItem.getAccuracy() == accuracy) { + return provider; + } else if (criteriaListItem.getPowerRequirement() == powerRequirement) { + return provider; + } + } + } + // TODO: these conditions are incomplete + for (String provider : providers) { + if (provider.equals(LocationManager.NETWORK_PROVIDER) && (accuracy == Criteria.ACCURACY_COARSE || powerRequirement == Criteria.POWER_LOW)) { + return provider; + } else if (provider.equals(LocationManager.GPS_PROVIDER) && accuracy == Criteria.ACCURACY_FINE && powerRequirement != Criteria.POWER_LOW) { + return provider; + } + } + + // No enabled provider found with the desired criteria, then return the the first registered provider(?) + return providers.isEmpty()? null : providers.get(0); + } + + private String getBestProviderWithNoCriteria(boolean enabled) { + List providers = getProviders(enabled); + + if (enabled && bestEnabledProvider != null) { + return bestEnabledProvider; + } else if (bestDisabledProvider != null) { + return bestDisabledProvider; + } else if (providers.contains(LocationManager.GPS_PROVIDER)) { + return LocationManager.GPS_PROVIDER; + } else if (providers.contains(LocationManager.NETWORK_PROVIDER)) { + return LocationManager.NETWORK_PROVIDER; + } + return null; + } + + @Implementation + public void requestLocationUpdates(String provider, long minTime, float minDistance, LocationListener listener) { + addLocationListener(provider, listener, minTime, minDistance); + } + + private void addLocationListener(String provider, LocationListener listener, long minTime, float minDistance) { + List providerListeners = locationListeners.get(provider); + if (providerListeners == null) { + providerListeners = new ArrayList<>(); + locationListeners.put(provider, providerListeners); + } + removeDuplicates(listener, providerListeners); + providerListeners.add(new ListenerRegistration(provider, + minTime, minDistance, copyOf(getLastKnownLocation(provider)), listener)); + + } + + private void removeDuplicates(LocationListener listener, + List providerListeners) { + final Iterator iterator = providerListeners.iterator(); + while (iterator.hasNext()) { + if (iterator.next().listener.equals(listener)) { + iterator.remove(); + } + } + } + + @Implementation + public void requestLocationUpdates(String provider, long minTime, float minDistance, LocationListener listener, + Looper looper) { + addLocationListener(provider, listener, minTime, minDistance); + } + + @Implementation + public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria, PendingIntent pendingIntent) { + if (pendingIntent == null) { + throw new IllegalStateException("Intent must not be null"); + } + if (getBestProvider(criteria, true) == null) { + throw new IllegalArgumentException("no providers found for criteria"); + } + requestLocationUdpateCriteriaPendingIntents.put(pendingIntent, criteria); + } + + @Implementation + public void requestLocationUpdates(String provider, long minTime, float minDistance, + PendingIntent pendingIntent) { + if (pendingIntent == null) { + throw new IllegalStateException("Intent must not be null"); + } + if (!providersEnabled.containsKey(provider)) { + throw new IllegalArgumentException("no providers found"); + } + + requestLocationUdpateProviderPendingIntents.put(pendingIntent, provider); + } + + @Implementation + public void removeUpdates(LocationListener listener) { + removedLocationListeners.add(listener); + } + + private void cleanupRemovedLocationListeners() { + for (Map.Entry> entry : locationListeners.entrySet()) { + List listenerRegistrations = entry.getValue(); + for (int i = listenerRegistrations.size() - 1; i >= 0; i--) { + LocationListener listener = listenerRegistrations.get(i).listener; + if(removedLocationListeners.contains(listener)) { + listenerRegistrations.remove(i); + } + } + } + } + + @Implementation + public void removeUpdates(PendingIntent pendingIntent) { + while (requestLocationUdpateCriteriaPendingIntents.remove(pendingIntent) != null); + while (requestLocationUdpateProviderPendingIntents.remove(pendingIntent) != null); + } + + public boolean hasGpsStatusListener(Listener listener) { + return gpsStatusListeners.contains(listener); + } + + /** + * Non-Android accessor. + * + *

+ * Gets the criteria value used in the last call to {@link #getBestProvider(android.location.Criteria, boolean)} + * + * @return the criteria used to find the best provider + */ + public Criteria getLastBestProviderCriteria() { + return lastBestProviderCriteria; + } + + /** + * Non-Android accessor. + * + *

+ * Gets the enabled value used in the last call to {@link #getBestProvider(android.location.Criteria, boolean)} + * + * @return the enabled value used to find the best provider + */ + public boolean getLastBestProviderEnabledOnly() { + return lastBestProviderEnabled; + } + + /** + * Sets the value to return from {@link #getBestProvider(android.location.Criteria, boolean)} for the given + * {@code provider} + * + * @param provider name of the provider who should be considered best + * @param enabled Enabled + * @param criteria List of criteria + * @throws Exception if provider is not known + * @return false If provider is not enabled but it is supposed to be set as the best enabled provider don't set it, otherwise true + */ + public boolean setBestProvider(String provider, boolean enabled, List criteria) throws Exception { + if (!getAllProviders().contains(provider)) { + throw new IllegalStateException("Best provider is not a known provider"); + } + // If provider is not enabled but it is supposed to be set as the best enabled provider don't set it. + for (String prvdr : providersEnabled.keySet()) { + if (provider.equals(prvdr) && providersEnabled.get(prvdr).enabled != enabled) { return false; - } - - @Implementation - public List getAllProviders() { - Set allKnownProviders = new LinkedHashSet<>(providersEnabled.keySet()); - allKnownProviders.add(LocationManager.GPS_PROVIDER); - allKnownProviders.add(LocationManager.NETWORK_PROVIDER); - allKnownProviders.add(LocationManager.PASSIVE_PROVIDER); - - return new ArrayList<>(allKnownProviders); - } - - /** - * Sets the value to return from {@link #isProviderEnabled(String)} for the given {@code - * provider} - * - * @param provider - * name of the provider whose status to set - * @param isEnabled - * whether that provider should appear enabled - */ - public void setProviderEnabled(String provider, boolean isEnabled) { - setProviderEnabled(provider, isEnabled, null); - } - - public void setProviderEnabled(String provider, boolean isEnabled, List criteria) { - LocationProviderEntry providerEntry = providersEnabled.get(provider); - if (providerEntry == null) { - providerEntry = new LocationProviderEntry(); - } - providerEntry.enabled = isEnabled; - providerEntry.criteria = criteria; - providersEnabled.put(provider, providerEntry); - List locationUpdateListeners = new ArrayList<>( - getRequestLocationUpdateListeners()); - for (LocationListener locationUpdateListener : locationUpdateListeners) { - if (isEnabled) { - locationUpdateListener.onProviderEnabled(provider); - } else { - locationUpdateListener.onProviderDisabled(provider); - } - } - // Send intent to notify about provider status - final Intent intent = new Intent(); - intent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, isEnabled); - ShadowApplication.getInstance().sendBroadcast(intent); - Set requestLocationUdpatePendingIntentSet = - requestLocationUdpateCriteriaPendingIntents.keySet(); - for (PendingIntent requestLocationUdpatePendingIntent : - requestLocationUdpatePendingIntentSet) { - try { - requestLocationUdpatePendingIntent.send(); - } catch (PendingIntent.CanceledException e) { - requestLocationUdpateCriteriaPendingIntents - .remove(requestLocationUdpatePendingIntent); - } - } - // if this provider gets disabled and it was the best active provider, then it's not anymore - if (provider.equals(bestEnabledProvider) && !isEnabled) { - bestEnabledProvider = null; - } - } - - @Implementation - public List getProviders(boolean enabledOnly) { - ArrayList enabledProviders = new ArrayList<>(); - for (String provider : getAllProviders()) { - if (!enabledOnly || providersEnabled.get(provider) != null) { - enabledProviders.add(provider); - } - } - return enabledProviders; - } - - @Implementation - public Location getLastKnownLocation(String provider) { - return lastKnownLocations.get(provider); - } - - @Implementation - public boolean addGpsStatusListener(GpsStatus.Listener listener) { - if (!gpsStatusListeners.contains(listener)) { - gpsStatusListeners.add(listener); - } - return true; - } - - @Implementation - public void removeGpsStatusListener(GpsStatus.Listener listener) { - gpsStatusListeners.remove(listener); - } - - @Implementation - public String getBestProvider(Criteria criteria, boolean enabled) { - lastBestProviderCriteria = criteria; - lastBestProviderEnabled = enabled; - - if (criteria == null) { - return getBestProviderWithNoCriteria(enabled); - } - - return getBestProviderWithCriteria(criteria, enabled); - } - - private String getBestProviderWithCriteria(Criteria criteria, boolean enabled) { - List providers = getProviders(enabled); - int powerRequirement = criteria.getPowerRequirement(); - int accuracy = criteria.getAccuracy(); - for (String provider : providers) { - LocationProviderEntry locationProviderEntry = providersEnabled.get(provider); - if (locationProviderEntry == null) { - continue; - } - List criteriaList = locationProviderEntry.getValue(); - if (criteriaList == null) { - continue; - } - for (Criteria criteriaListItem : criteriaList) { - if (criteria.equals(criteriaListItem)) { - return provider; - } else if (criteriaListItem.getAccuracy() == accuracy) { - return provider; - } else if (criteriaListItem.getPowerRequirement() == powerRequirement) { - return provider; - } - } - } - // TODO: these conditions are incomplete - for (String provider : providers) { - if (provider.equals(LocationManager.NETWORK_PROVIDER) && (accuracy == - Criteria.ACCURACY_COARSE || powerRequirement == Criteria.POWER_LOW)) { - return provider; - } else if (provider.equals(LocationManager.GPS_PROVIDER) && accuracy == - Criteria.ACCURACY_FINE && powerRequirement != Criteria.POWER_LOW) { - return provider; - } - } - - // No enabled provider found with the desired criteria, then return the the first registered - // provider(?) - return providers.isEmpty() ? null : providers.get(0); - } - - private String getBestProviderWithNoCriteria(boolean enabled) { - List providers = getProviders(enabled); - - if (enabled && bestEnabledProvider != null) { - return bestEnabledProvider; - } else if (bestDisabledProvider != null) { - return bestDisabledProvider; - } else if (providers.contains(LocationManager.GPS_PROVIDER)) { - return LocationManager.GPS_PROVIDER; - } else if (providers.contains(LocationManager.NETWORK_PROVIDER)) { - return LocationManager.NETWORK_PROVIDER; - } - return null; - } - - @Implementation - public void requestLocationUpdates(String provider, long minTime, float minDistance, - LocationListener listener) { - addLocationListener(provider, listener, minTime, minDistance); - } - - private void addLocationListener(String provider, LocationListener listener, long minTime, - float minDistance) { - List providerListeners = locationListeners.get(provider); - if (providerListeners == null) { - providerListeners = new ArrayList<>(); - locationListeners.put(provider, providerListeners); - } - ListenerRegistration registrationToAdd = new ListenerRegistration(provider, - minTime, minDistance, copyOf(getLastKnownLocation(provider)), listener); - for (ListenerRegistration registration : providerListeners) { - if (registration.equals(registrationToAdd)) { - providerListeners.remove(registration); - break; - } - } - providerListeners.add(registrationToAdd); - - } - - @Implementation - public void requestLocationUpdates(String provider, long minTime, float minDistance, - LocationListener listener, Looper looper) { - addLocationListener(provider, listener, minTime, minDistance); - } - - @Implementation - public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria, - PendingIntent pendingIntent) { - if (pendingIntent == null) { - throw new IllegalStateException("Intent must not be null"); - } - if (getBestProvider(criteria, true) == null) { - throw new IllegalArgumentException("no providers found for criteria"); - } - requestLocationUdpateCriteriaPendingIntents.put(pendingIntent, criteria); - } - - @Implementation - public void requestLocationUpdates(String provider, long minTime, float minDistance, - PendingIntent pendingIntent) { - if (pendingIntent == null) { - throw new IllegalStateException("Intent must not be null"); - } - if (!providersEnabled.containsKey(provider)) { - throw new IllegalArgumentException("no providers found"); - } - - requestLocationUdpateProviderPendingIntents.put(pendingIntent, provider); - } - - @Implementation - public void removeUpdates(LocationListener listener) { - removedLocationListeners.add(listener); - } - - private void cleanupRemovedLocationListeners() { - for (Map.Entry> entry : locationListeners.entrySet()) { - List listenerRegistrations = entry.getValue(); - for (int i = listenerRegistrations.size() - 1; i >= 0; i--) { - LocationListener listener = listenerRegistrations.get(i).listener; - if (removedLocationListeners.contains(listener)) { - listenerRegistrations.remove(i); - } - } - } - } - - @Implementation - public void removeUpdates(PendingIntent pendingIntent) { - while (requestLocationUdpateCriteriaPendingIntents.containsKey(pendingIntent)) { - requestLocationUdpateCriteriaPendingIntents.remove(pendingIntent); - } - while (requestLocationUdpateProviderPendingIntents.containsKey(pendingIntent)) { - requestLocationUdpateProviderPendingIntents.remove(pendingIntent); - } - } - - public boolean hasGpsStatusListener(GpsStatus.Listener listener) { - return gpsStatusListeners.contains(listener); - } - - /** - * Non-Android accessor. - * - *

- * Gets the criteria value used in the last call to {@link #getBestProvider( - * android.location.Criteria, boolean)} - * - * @return the criteria used to find the best provider - */ - public Criteria getLastBestProviderCriteria() { - return lastBestProviderCriteria; - } - - /** - * Non-Android accessor. - * - *

- * Gets the enabled value used in the last call to {@link #getBestProvider( - * android.location.Criteria, boolean)} - * - * @return the enabled value used to find the best provider - */ - public boolean getLastBestProviderEnabledOnly() { - return lastBestProviderEnabled; - } - - /** - * Sets the value to return from {@link #getBestProvider(android.location.Criteria, boolean)} - * for the given - * {@code provider} - * - * @param provider name of the provider who should be considered best - * @param enabled Enabled - * @param criteria List of criteria - * @throws Exception if provider is not known - * @return false If provider is not enabled but it is supposed to be set as the best enabled - * provider don't set it, otherwise true - */ - public boolean setBestProvider(String provider, boolean enabled, List criteria) - throws Exception { - if (!getAllProviders().contains(provider)) { - throw new IllegalStateException("Best provider is not a known provider"); - } - // If provider is not enabled but it is supposed to be set as the best enabled provider - // don't set it. - for (String prvdr : providersEnabled.keySet()) { - if (provider.equals(prvdr) && providersEnabled.get(prvdr).enabled != enabled) { - return false; - } - } - - if (enabled) { - bestEnabledProvider = provider; - if (provider.equals(bestDisabledProvider)) { - bestDisabledProvider = null; - } - } else { - bestDisabledProvider = provider; - if (provider.equals(bestEnabledProvider)) { - bestEnabledProvider = null; - } - } - if (criteria == null) { - return true; - } - LocationProviderEntry entry; - if (!providersEnabled.containsKey(provider)) { - entry = new LocationProviderEntry(); - entry.enabled = enabled; - entry.criteria = criteria; - } else { - entry = providersEnabled.get(provider); - } - providersEnabled.put(provider, entry); - - return true; - } - - public boolean setBestProvider(String provider, boolean enabled) throws Exception { - return setBestProvider(provider, enabled, null); - } - - /** - * Sets the value to return from {@link #getLastKnownLocation(String)} for the given {@code - * provider} - * - * @param provider - * name of the provider whose location to set - * @param location - * the last known location for the provider - */ - public void setLastKnownLocation(String provider, Location location) { - lastKnownLocations.put(provider, location); - } - - /** - * Non-Android accessor. - * - * @return lastRequestedLocationUpdatesLocationListener - */ - public List getRequestLocationUpdateListeners() { - cleanupRemovedLocationListeners(); - List all = new ArrayList<>(); - for (Map.Entry> entry : locationListeners.entrySet()) { - for (ListenerRegistration reg : entry.getValue()) { - all.add(reg.listener); - } - } - - return all; - } - - public void simulateLocation(Location location) { - cleanupRemovedLocationListeners(); - setLastKnownLocation(location.getProvider(), location); - - List providerListeners = locationListeners.get( - location.getProvider()); - if (providerListeners == null) { - return; - } - - for (ListenerRegistration listenerReg : providerListeners) { - if (listenerReg.lastSeenLocation != null && location != null) { - float distanceChange = distanceBetween(location, listenerReg.lastSeenLocation); - boolean withinMinDistance = distanceChange < listenerReg.minDistance; - boolean exceededMinTime = location.getTime() - listenerReg.lastSeenTime > - listenerReg.minTime; - if (withinMinDistance || !exceededMinTime) { - continue; - } - } - listenerReg.lastSeenLocation = copyOf(location); - listenerReg.lastSeenTime = location == null ? 0 : location.getTime(); - listenerReg.listener.onLocationChanged(copyOf(location)); - } - cleanupRemovedLocationListeners(); - } - - private Location copyOf(Location location) { - if (location == null) { - return null; - } - Location copy = new Location(location); - copy.setAccuracy(location.getAccuracy()); - copy.setAltitude(location.getAltitude()); - copy.setBearing(location.getBearing()); - copy.setExtras(location.getExtras()); - copy.setLatitude(location.getLatitude()); - copy.setLongitude(location.getLongitude()); - copy.setProvider(location.getProvider()); - copy.setSpeed(location.getSpeed()); - copy.setTime(location.getTime()); - return copy; - } - - /** - * Returns the distance between the two locations in meters. - * Adapted from: http://stackoverflow.com/questions/837872/calculate-distance-in-meters-when- - * you-know-longitude-and-latitude-in-java - */ - private static float distanceBetween(Location location1, Location location2) { - double earthRadius = 3958.75; - double latDifference = Math.toRadians(location2.getLatitude() - location1.getLatitude()); - double lonDifference = Math.toRadians(location2.getLongitude() - location2.getLongitude()); - double a = Math.sin(latDifference / 2) * Math.sin(latDifference / 2) + - Math.cos(Math.toRadians(location1.getLatitude())) * Math.cos(Math.toRadians( - location2.getLatitude())) * - Math.sin(lonDifference / 2) * Math.sin(lonDifference / 2); - double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); - double dist = Math.abs(earthRadius * c); - - int meterConversion = 1609; - - return new Float(dist * meterConversion); - } - - public Map getRequestLocationUdpateCriteriaPendingIntents() { - return requestLocationUdpateCriteriaPendingIntents; - } - - public Map getRequestLocationUdpateProviderPendingIntents() { - return requestLocationUdpateProviderPendingIntents; - } - - public Collection getProvidersForListener(LocationListener listener) { - cleanupRemovedLocationListeners(); - Set providers = new HashSet<>(); - for (List listenerRegistrations : locationListeners.values()) { - for (ListenerRegistration listenerRegistration : listenerRegistrations) { - if (listenerRegistration.listener == listener) { - providers.add(listenerRegistration.provider); - } - } - } - return providers; - } - - @Implementation - public void requestSingleUpdate(String provider, LocationListener listener, Looper looper) { - } - - private final class LocationProviderEntry implements Map.Entry> { - private Boolean enabled; - private List criteria; - - @Override - public Boolean getKey() { - return enabled; - } - - @Override - public List getValue() { - return criteria; - } - - @Override - public List setValue(List criteria) { - List oldCriteria = this.criteria; - this.criteria = criteria; - return oldCriteria; - } - } - + } + } + + if (enabled) { + bestEnabledProvider = provider; + if (provider.equals(bestDisabledProvider)) { + bestDisabledProvider = null; + } + } else { + bestDisabledProvider = provider; + if (provider.equals(bestEnabledProvider)) { + bestEnabledProvider = null; + } + } + if (criteria == null) { + return true; + } + LocationProviderEntry entry; + if (!providersEnabled.containsKey(provider)) { + entry = new LocationProviderEntry(); + entry.enabled = enabled; + entry.criteria = criteria; + } else { + entry = providersEnabled.get(provider); + } + providersEnabled.put(provider, entry); + + return true; + } + + public boolean setBestProvider(String provider, boolean enabled) throws Exception { + return setBestProvider(provider, enabled, null); + } + + /** + * Sets the value to return from {@link #getLastKnownLocation(String)} for the given {@code provider} + * + * @param provider + * name of the provider whose location to set + * @param location + * the last known location for the provider + */ + public void setLastKnownLocation(String provider, Location location) { + lastKnownLocations.put(provider, location); + } + + /** + * Non-Android accessor. + * + * @return lastRequestedLocationUpdatesLocationListener + */ + public List getRequestLocationUpdateListeners() { + cleanupRemovedLocationListeners(); + List all = new ArrayList<>(); + for (Map.Entry> entry : locationListeners.entrySet()) { + for (ListenerRegistration reg : entry.getValue()) { + all.add(reg.listener); + } + } + + return all; + } + + public void simulateLocation(Location location) { + cleanupRemovedLocationListeners(); + setLastKnownLocation(location.getProvider(), location); + + List providerListeners = locationListeners.get( + location.getProvider()); + if (providerListeners == null) return; + + for (ListenerRegistration listenerReg : providerListeners) { + if(listenerReg.lastSeenLocation != null && location != null) { + float distanceChange = distanceBetween(location, listenerReg.lastSeenLocation); + boolean withinMinDistance = distanceChange < listenerReg.minDistance; + boolean exceededMinTime = location.getTime() - listenerReg.lastSeenTime > listenerReg.minTime; + if (withinMinDistance || !exceededMinTime) continue; + } + listenerReg.lastSeenLocation = copyOf(location); + listenerReg.lastSeenTime = location == null ? 0 : location.getTime(); + listenerReg.listener.onLocationChanged(copyOf(location)); + } + cleanupRemovedLocationListeners(); + } + + private Location copyOf(Location location) { + if (location == null) return null; + Location copy = new Location(location); + copy.setAccuracy(location.getAccuracy()); + copy.setAltitude(location.getAltitude()); + copy.setBearing(location.getBearing()); + copy.setExtras(location.getExtras()); + copy.setLatitude(location.getLatitude()); + copy.setLongitude(location.getLongitude()); + copy.setProvider(location.getProvider()); + copy.setSpeed(location.getSpeed()); + copy.setTime(location.getTime()); + return copy; + } + + /** + * Returns the distance between the two locations in meters. + * Adapted from: http://stackoverflow.com/questions/837872/calculate-distance-in-meters-when-you-know-longitude-and-latitude-in-java + */ + private static float distanceBetween(Location location1, Location location2) { + double earthRadius = 3958.75; + double latDifference = Math.toRadians(location2.getLatitude() - location1.getLatitude()); + double lonDifference = Math.toRadians(location2.getLongitude() - location2.getLongitude()); + double a = Math.sin(latDifference/2) * Math.sin(latDifference/2) + + Math.cos(Math.toRadians(location1.getLatitude())) * Math.cos(Math.toRadians(location2.getLatitude())) * + Math.sin(lonDifference/2) * Math.sin(lonDifference/2); + double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); + double dist = Math.abs(earthRadius * c); + + int meterConversion = 1609; + + return new Float(dist * meterConversion); + } + + public Map getRequestLocationUdpateCriteriaPendingIntents() { + return requestLocationUdpateCriteriaPendingIntents; + } + + public Map getRequestLocationUdpateProviderPendingIntents() { + return requestLocationUdpateProviderPendingIntents; + } + + public Collection getProvidersForListener(LocationListener listener) { + cleanupRemovedLocationListeners(); + Set providers = new HashSet<>(); + for (List listenerRegistrations : locationListeners.values()) { + for (ListenerRegistration listenerRegistration : listenerRegistrations) { + if (listenerRegistration.listener == listener) { + providers.add(listenerRegistration.provider); + } + } + } + return providers; + } + + @Implementation + public void requestSingleUpdate(String provider, LocationListener listener, Looper looper) { + } + + final private class LocationProviderEntry implements Map.Entry> { + private Boolean enabled; + private List criteria; + + @Override + public Boolean getKey() { + return enabled; + } + + @Override + public List getValue() { + return criteria; + } + + @Override + public List setValue(List criteria) { + List oldCriteria = this.criteria; + this.criteria = criteria; + return oldCriteria; + } + } }