All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.robolectric.shadows.ShadowLocationManager Maven / Gradle / Ivy

There is a newer version: 3.1.4
Show newest version
package org.robolectric.shadows;

import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.content.Intent;
import android.location.Criteria;
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 java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Shadow for {@link android.location.LocationManager}.
 */
@Implements(LocationManager.class)
public class ShadowLocationManager {
  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);
    }
    providerListeners.add(new ListenerRegistration(provider,
        minTime, minDistance, copyOf(getLastKnownLocation(provider)), listener));

  }

  @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; } } 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; } 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; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy