Skip to content
This repository has been archived by the owner on Apr 23, 2023. It is now read-only.

Respsect LocationRequest Parameters #146

Merged
merged 6 commits into from
Jan 6, 2017
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,11 @@ void addLocationCallback(LostApiClient client, LocationRequest request,
boolean removeListener(LostApiClient client, LocationListener listener);
boolean removePendingIntent(LostApiClient client, PendingIntent callbackIntent);
boolean removeLocationCallback(LostApiClient client, LocationCallback callback);
void reportLocationChanged(Location location);
void sendPendingIntent(Context context, Location location, LocationAvailability availability,
LocationResult result);
void reportLocationResult(final LocationResult result);
ReportedChanges reportLocationChanged(Location location);
ReportedChanges sendPendingIntent(Context context, Location location,
LocationAvailability availability, LocationResult result);
ReportedChanges reportLocationResult(Location location, final LocationResult result);
void updateReportedValues(ReportedChanges changes);
void reportProviderEnabled(String provider);
void reportProviderDisabled(String provider);
void notifyLocationAvailability(final LocationAvailability availability);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,16 +123,21 @@ public boolean isProviderEnabled(LostApiClient client, String provider) {

@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public void reportLocation(Location location) {
clientManager.reportLocationChanged(location);
ReportedChanges changes = clientManager.reportLocationChanged(location);

LocationAvailability availability = locationEngine.createLocationAvailability();
ArrayList<Location> locations = new ArrayList<>();
locations.add(location);
final LocationResult result = LocationResult.create(locations);
clientManager.sendPendingIntent(context, location, availability, result);
ReportedChanges pendingIntentChanges = clientManager.sendPendingIntent(
context, location, availability, result);

ReportedChanges callbackChanges = clientManager.reportLocationResult(
location, result);

clientManager.reportLocationResult(result);
changes.putAll(pendingIntentChanges);
changes.putAll(callbackChanges);
clientManager.updateReportedValues(changes);
}

@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,26 @@ public class LostClientManager implements ClientManager {
private Map<LostApiClient, Set<LocationCallback>> clientToLocationCallbacks;
private Map<LostApiClient, Map<LocationCallback, Looper>> clientCallbackToLoopers;

private Map<LocationListener, LocationRequest> listenerToLocationRequests;
private Map<PendingIntent, LocationRequest> intentToLocationRequests;
private Map<LocationCallback, LocationRequest> callbackToLocationRequests;
private Map<LocationRequest, Long> requestToLastReportedTime;
private Map<LocationRequest, Location> requestToLastReportedLocation;
Copy link
Collaborator

@ecgreb ecgreb Jan 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we replace requestToLastReportedTime and requestToLastReportedLocation with a single private instance of ReportedChanges since that seems to encapsulate the same thing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced


private LostClientManager() {
public LostClientManager() {
clients = new HashSet<>();

clientToListeners = new HashMap<>();
clientToPendingIntents = new HashMap<>();
clientToLocationCallbacks = new HashMap<>();
clientCallbackToLoopers = new HashMap<>();

listenerToLocationRequests = new HashMap<>();
intentToLocationRequests = new HashMap<>();
callbackToLocationRequests = new HashMap<>();
requestToLastReportedTime = new HashMap<>();
requestToLastReportedLocation = new HashMap<>();

}

public static LostClientManager shared() {
Expand Down Expand Up @@ -83,6 +95,7 @@ public void addListener(LostApiClient client, LocationRequest request,
}
listeners.add(listener);
clientToListeners.put(client, listeners);
listenerToLocationRequests.put(listener, request);
}

public void addPendingIntent(LostApiClient client, LocationRequest request,
Expand All @@ -93,6 +106,7 @@ public void addPendingIntent(LostApiClient client, LocationRequest request,
}
intents.add(callbackIntent);
clientToPendingIntents.put(client, intents);
intentToLocationRequests.put(callbackIntent, request);
}

public void addLocationCallback(LostApiClient client, LocationRequest request,
Expand All @@ -110,6 +124,7 @@ public void addLocationCallback(LostApiClient client, LocationRequest request,
}
looperMap.put(callback, looper);
clientCallbackToLoopers.put(client, looperMap);
callbackToLocationRequests.put(callback, request);
}

public boolean removeListener(LostApiClient client, LocationListener listener) {
Expand All @@ -124,7 +139,7 @@ public boolean removeListener(LostApiClient client, LocationListener listener) {
clientToListeners.remove(client);
}
}

listenerToLocationRequests.remove(listener);
return removedListener;
}

Expand All @@ -140,6 +155,7 @@ public boolean removePendingIntent(LostApiClient client, PendingIntent callbackI
clientToPendingIntents.remove(client);
}
}
intentToLocationRequests.remove(callbackIntent);
return removedPendingIntent;
}

Expand All @@ -163,51 +179,61 @@ public boolean removeLocationCallback(LostApiClient client, LocationCallback cal
clientCallbackToLoopers.remove(client);
}
}
callbackToLocationRequests.remove(callback);
return removedCallback;
}

public void reportLocationChanged(Location location) {
for (LostApiClient client : clientToListeners.keySet()) {
if (clientToListeners.get(client) != null) {
for (LocationListener listener : clientToListeners.get(client)) {
listener.onLocationChanged(location);
}
}
}
/**
* Reports location changed for all listeners respecting their corresponding
* {@link LocationRequest#getFastestInterval()} and
* {@link LocationRequest#getSmallestDisplacement()} values. Returns a map of the updated
* last reported times so that {@code LostClientManager#requestToLastReportedTime} after
* all reporting (including pending intents and location callbacks) has been done.
* @param location
* @return
*/
public ReportedChanges reportLocationChanged(final Location location) {
return iterateAndNotify(location,
clientToListeners, listenerToLocationRequests, new Notifier<LocationListener>() {
@Override void notify(LostApiClient client, LocationListener listener) {
listener.onLocationChanged(location);
}
});
}

public void sendPendingIntent(Context context, Location location,
LocationAvailability availability, LocationResult result) {
for (LostApiClient client : clientToPendingIntents.keySet()) {
if (clientToPendingIntents.get(client) != null) {
for (PendingIntent intent : clientToPendingIntents.get(client)) {
try {
Intent toSend = new Intent().putExtra(KEY_LOCATION_CHANGED, location);
toSend.putExtra(LocationAvailability.EXTRA_LOCATION_AVAILABILITY, availability);
toSend.putExtra(LocationResult.EXTRA_LOCATION_RESULT, result);
intent.send(context, 0, toSend);
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "Unable to send pending intent: " + intent);
/**
* Fires intent for all pending intents respecting their corresponding
* {@link LocationRequest#getFastestInterval()} and
* {@link LocationRequest#getSmallestDisplacement()} values. Returns a map of the updated
* last reported times so that {@code LostClientManager#requestToLastReportedTime} after
* all reporting (including pending intents and location callbacks) has been done.
* @param location
* @return
*/
public ReportedChanges sendPendingIntent(final Context context,
final Location location, final LocationAvailability availability,
final LocationResult result) {
return iterateAndNotify(location,
clientToPendingIntents, intentToLocationRequests, new Notifier<PendingIntent>() {
@Override void notify(LostApiClient client, PendingIntent pendingIntent) {
fireIntent(context, pendingIntent, location, availability, result);
}
}
}
}
});
}

public void reportLocationResult(final LocationResult result) {
for (LostApiClient client : clientToLocationCallbacks.keySet()) {
if (clientToLocationCallbacks.get(client) != null) {
for (final LocationCallback callback : clientToLocationCallbacks.get(client)) {
Looper looper = clientCallbackToLoopers.get(client).get(callback);
Handler handler = new Handler(looper);
handler.post(new Runnable() {
@Override public void run() {
callback.onLocationResult(result);
}
});
}
}
}
public ReportedChanges reportLocationResult(Location location,
final LocationResult result) {
return iterateAndNotify(location,
clientToLocationCallbacks, callbackToLocationRequests, new Notifier<LocationCallback>() {
@Override void notify(LostApiClient client, LocationCallback callback) {
notifyCallback(client, callback, result);
}
});
}

public void updateReportedValues(ReportedChanges changes) {
requestToLastReportedTime.putAll(changes.timeChanges());
requestToLastReportedLocation.putAll(changes.locationChanges());
}

public void reportProviderEnabled(String provider) {
Expand All @@ -234,13 +260,7 @@ public void notifyLocationAvailability(final LocationAvailability availability)
for (LostApiClient client : clientToLocationCallbacks.keySet()) {
if (clientToLocationCallbacks.get(client) != null) {
for (final LocationCallback callback : clientToLocationCallbacks.get(client)) {
Looper looper = clientCallbackToLoopers.get(client).get(callback);
Handler handler = new Handler(looper);
handler.post(new Runnable() {
@Override public void run() {
callback.onLocationAvailability(availability);
}
});
notifyAvailability(client, callback, availability);
}
}
}
Expand Down Expand Up @@ -269,4 +289,75 @@ public Map<LostApiClient, Set<PendingIntent>> getPendingIntents() {
public Map<LostApiClient, Set<LocationCallback>> getLocationCallbacks() {
return clientToLocationCallbacks;
}

private void fireIntent(Context context, PendingIntent intent, Location location,
LocationAvailability availability, LocationResult result) {
try {
Intent toSend = new Intent().putExtra(KEY_LOCATION_CHANGED, location);
toSend.putExtra(LocationAvailability.EXTRA_LOCATION_AVAILABILITY, availability);
toSend.putExtra(LocationResult.EXTRA_LOCATION_RESULT, result);
intent.send(context, 0, toSend);
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "Unable to send pending intent: " + intent);
}
}

private void notifyCallback(LostApiClient client, final LocationCallback callback,
final LocationResult result) {
Looper looper = clientCallbackToLoopers.get(client).get(callback);
Handler handler = new Handler(looper);
handler.post(new Runnable() {
@Override public void run() {
callback.onLocationResult(result);
}
});
}

private void notifyAvailability(LostApiClient client, final LocationCallback callback,
final LocationAvailability availability) {
Looper looper = clientCallbackToLoopers.get(client).get(callback);
Handler handler = new Handler(looper);
handler.post(new Runnable() {
@Override public void run() {
callback.onLocationAvailability(availability);
}
});
}

private <T> ReportedChanges iterateAndNotify(Location location,
Map<LostApiClient, Set<T>> clientToObj, Map<T, LocationRequest> objToLocationRequest,
Notifier notifier) {
Map<LocationRequest, Long> updatedRequestToReportedTime = new HashMap<>();
Map<LocationRequest, Location> updatedRequestToReportedLocation = new HashMap<>();
for (LostApiClient client : clientToObj.keySet()) {
if (clientToObj.get(client) != null) {
for (final T obj : clientToObj.get(client)) {
LocationRequest request = objToLocationRequest.get(obj);
Long lastReportedTime = requestToLastReportedTime.get(request);
Location lastReportedLocation = requestToLastReportedLocation.get(request);
if (lastReportedTime == null && lastReportedLocation == null) {
updatedRequestToReportedTime.put(request, System.currentTimeMillis());
updatedRequestToReportedLocation.put(request, location);
notifier.notify(client, obj);
} else {
long intervalSinceLastReport = System.currentTimeMillis() - lastReportedTime;
long fastestInterval = request.getFastestInterval();
float smallestDisplacement = request.getSmallestDisplacement();
float displacementSinceLastReport = location.distanceTo(lastReportedLocation);
if (intervalSinceLastReport >= fastestInterval &&
displacementSinceLastReport >= smallestDisplacement) {
updatedRequestToReportedTime.put(request, System.currentTimeMillis());
updatedRequestToReportedLocation.put(request, location);
notifier.notify(client, obj);
}
}
}
}
}
return new ReportedChanges(updatedRequestToReportedTime, updatedRequestToReportedLocation);
}

abstract class Notifier<T> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not make Notifier an interface since there is no implementation here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, changed to an interface

abstract void notify(LostApiClient client, T obj);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

I really like the use of generics to allows us to treat LocationListener, PendingIntent, and LocationCallback objects in a similar fashion when sending location updates.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😄

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.mapzen.android.lost.internal;

import com.mapzen.android.lost.api.LocationRequest;

import android.location.Location;

import java.util.Map;

/**
* Represents changes to reported locations and times at which locations were reported for
* different modes of notification (listeners, pending intents, and callbacks). After all types of
* callbacks are invoked, these changes will be committed so that the next time a location is
* reported, the {@link FusedLocationProviderServiceImpl} can properly determine if it should be
* reported to listeners, pending intents, and callbacks.
*/
public class ReportedChanges {

private Map<LocationRequest, Long> updatedRequestToReportedTime;
private Map<LocationRequest, Location> updatedRequestToReportedLocation;

public ReportedChanges(Map<LocationRequest, Long> timeChanges,
Map<LocationRequest, Location> locationChanges) {
updatedRequestToReportedTime = timeChanges;
updatedRequestToReportedLocation = locationChanges;
}

public Map<LocationRequest, Long> timeChanges() {
return updatedRequestToReportedTime;
}

public Map<LocationRequest, Location> locationChanges() {
return updatedRequestToReportedLocation;
}

public void putAll(ReportedChanges changes) {
updatedRequestToReportedTime.putAll(changes.timeChanges());
updatedRequestToReportedLocation.putAll(changes.locationChanges());
}
}
Loading