dtu.ds.warnme.app.location.FollowMeLocationSource.java Source code

Java tutorial

Introduction

Here is the source code for dtu.ds.warnme.app.location.FollowMeLocationSource.java

Source

/**
 * 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;
                        }
                    });
        }
    }

}