Java tutorial
/** * Project: warnme-app * File: FollowMeLocationSource.java * License: * This file is licensed under GNU General Public License version 3 * http://www.gnu.org/licenses/gpl-3.0.txt * * Copyright: Bartosz Cichecki [ cichecki.bartosz@gmail.com ] * Date: Mar 23, 2014 */ package dtu.ds.warnme.app.location; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.apache.http.Header; import org.apache.http.HttpStatus; import android.app.Activity; import android.content.Context; import android.location.Criteria; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.util.Log; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.LocationSource; import com.google.android.gms.maps.model.CameraPosition; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.MarkerOptions; import com.google.gson.reflect.TypeToken; import dtu.ds.warnme.app.application.Prefs; import dtu.ds.warnme.app.holders.UserProfileHolder; import dtu.ds.warnme.app.model.impl.Event; import dtu.ds.warnme.app.model.impl.EventType; import dtu.ds.warnme.app.ws.client.https.GsonHttpResponseHandler; import dtu.ds.warnme.app.ws.client.restful.RestClientHolder; /** * @author Bartosz Cichecki * */ public class FollowMeLocationSource implements LocationSource, LocationListener { private static final String TAG = "FollowMeLocationSource"; private OnLocationChangedListener locationChangedListener; private LocationManager locationManager; private Criteria criteria; private String bestAvailableProvider; private final int minTime = 2 * 1000; private final int minDistance = 5; private Context context; private GoogleMap map; private Location lastPullLocation; private Location lastKnownLocation; private FollowMeLocationSourceListener locationSourceListener; private List<Event> cachedEvents = Collections.synchronizedList(new ArrayList<Event>()); private Location previousLocation; public FollowMeLocationSource(Activity activity, Context context, GoogleMap map) { this.context = context; this.map = map; try { locationSourceListener = (FollowMeLocationSourceListener) activity; } catch (ClassCastException ex) { throw new ClassCastException(activity.toString() + " must implement FollowMeLocationSource"); } init(); } @Override public void activate(OnLocationChangedListener listener) { Log.i(TAG, "Location source activated."); locationChangedListener = listener; if (bestAvailableProvider != null) { locationManager.requestLocationUpdates(bestAvailableProvider, minTime, minDistance, this); } else { Log.i(TAG, "No location providers available"); } } private void checkEventsProximity(Location location) { if (cachedEvents == null || cachedEvents.isEmpty()) { return; } Float closestDistance = Float.MAX_VALUE; Event closestEvent = null; for (Event event : cachedEvents) { Float distance = location.distanceTo(event.getLocation()); Float bearing = location.bearingTo(event.getLocation()); if (distance < closestDistance && bearing < 90f) { closestDistance = distance; closestEvent = event; } } if (closestEvent != null && closestDistance < 500f) { locationSourceListener.onApproachingEvent(closestEvent); } } @Override public void deactivate() { Log.i(TAG, "Location source deactivated."); locationManager.removeUpdates(this); locationChangedListener = null; } private boolean eventsNeedRefresh(Location location1, Location location2) { return location1.distanceTo(location2) > 0.5 * Prefs.getRadius() || Math.abs(location1.getBearing() - location2.getBearing()) > 30; } public void getBestAvailableProvider() { bestAvailableProvider = locationManager.getBestProvider(criteria, true); } public Location getCurrentLocation() { return lastKnownLocation; } private void init() { locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); criteria = new Criteria(); criteria.setAccuracy(Criteria.ACCURACY_FINE); criteria.setPowerRequirement(Criteria.POWER_MEDIUM); criteria.setAltitudeRequired(true); criteria.setBearingRequired(true); criteria.setSpeedRequired(true); criteria.setCostAllowed(true); } @Override public void onLocationChanged(Location location) { Log.i(TAG, "Location changed: " + location); if (!location.hasBearing()) { Log.w(TAG, "Location does not have bearing. Will calculate on our own..."); if (previousLocation != null) { float bearing = previousLocation.bearingTo(location); location.setBearing(bearing); Log.w(TAG, "New bearing: " + bearing); } previousLocation = location; } if (locationChangedListener != null) { locationChangedListener.onLocationChanged(location); } CameraPosition cameraPosition = new CameraPosition.Builder() .target(new LatLng(location.getLatitude(), location.getLongitude())).zoom(17) .bearing(location.getBearing()).tilt(40).build(); map.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition)); lastKnownLocation = location; pullNewEvents(location); checkEventsProximity(location); } @Override public void onProviderDisabled(String provider) { Log.i(TAG, "Location provider disabled: " + provider); RestClientHolder.getRestClient().cancelRequests(context, true); } @Override public void onProviderEnabled(String provider) { Log.i(TAG, "Location provider enabled: " + provider); } @Override public void onStatusChanged(String provider, int status, Bundle extras) { Log.i(TAG, "Location provider status changed: " + provider); } private void pullNewEvents(final Location newLocation) { if (!UserProfileHolder.isLoggedIn()) { Log.i(TAG, "User is not logged in. Will not pull events data."); return; } if (lastPullLocation == null || eventsNeedRefresh(newLocation, lastPullLocation)) { if (lastPullLocation == null) { lastPullLocation = newLocation; } RestClientHolder.getRestClient().getEvents(context, newLocation, Prefs.getRadius(), EventType.values(), new GsonHttpResponseHandler<List<Event>>(new TypeToken<List<Event>>() { }.getType()) { @Override public void onFailure(int statusCode, Header[] headers, String responseBody, Throwable error) { // If the request failed then too bad... Log.e(TAG, "Couldn't fetch events list. [statusCode=" + statusCode + ", error=" + error); if (statusCode == 0) { locationSourceListener.onDownloadEventsFailed(); } if (statusCode == HttpStatus.SC_UNAUTHORIZED) { locationSourceListener.onCredentialsExpired(); } } @Override public void onSuccess(int statusCode, Header[] headers, List<Event> events) { locationSourceListener.onDownloadEventsSuccessful(); cachedEvents.clear(); cachedEvents.addAll(events); map.clear(); for (Event event : events) { MarkerOptions markerOptions = new MarkerOptions(); markerOptions.position(event.getPostition()); map.addMarker(markerOptions); } lastPullLocation = newLocation; } }); } } }