Refactored the entire library

I also moved the directions listfragment, overlay, and other required
classes from FMC into the library

Change-Id: Iba27e29d89e864dbeca3a2670aed552a8be4f2b8
Signed-off-by: Ricky Barrette <rickbarrette@gmail.com>
This commit is contained in:
2012-03-05 08:28:47 -05:00
parent 2d53cf69cb
commit e1e1bea1ef
17 changed files with 747 additions and 21 deletions

View File

@@ -0,0 +1,239 @@
/**
* CompasOverlay.java
* @date Mar 9, 2011
* @author ricky barrette
* @author Twenty Codes, LLC
*/
package com.TwentyCodes.android.overlays;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Point;
import android.util.TypedValue;
import com.TwentyCodes.android.location.CompassListener;
import com.TwentyCodes.android.location.CompassSensor;
import com.TwentyCodes.android.location.GeoUtils;
import com.TwentyCodes.android.location.R;
import com.TwentyCodes.android.location.R.drawable;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
/**
* A Simple compass overlay that will be used to point towards a destination or north
* @author ricky barrette
*/
public class CompasOverlay extends Overlay implements CompassListener {
private float mBearing;
private Context mContext;
private GeoPoint mDestination;
private GeoPoint mLocation;
private boolean isEnabled;
private CompassSensor mCompassSensor;
private int mNeedleResId = R.drawable.needle_sm;
private int mBackgroundResId = R.drawable.compass_sm;
private int mX;
private int mY;
private CompassListener mListener;
/**
* Creates a new CompasOverlay
* @author ricky barrette
*/
public CompasOverlay(Context context) {
mContext = context;
mCompassSensor = new CompassSensor(context);
mX = convertDipToPx(40);
mY = mX;
}
/**
* Creates a new CompasOverlay
* @param context
* @param destination
* @author ricky barrette
*/
public CompasOverlay(Context context, GeoPoint destination){
this(context);
mDestination = destination;
}
/**
* Creates a new CompasOverlay
* @param context
* @param destination
* @param needleResId
* @param backgroundResId
* @param x dip
* @param y dip
* @author ricky barrette
*/
public CompasOverlay(Context context, GeoPoint destination, int needleResId, int backgroundResId, int x, int y){
this(context, destination);
mX = convertDipToPx(x);
mY = convertDipToPx(y);
mNeedleResId = needleResId;
mBackgroundResId = backgroundResId;
}
/**
* Creates a new CompasOverlay
* @param context
* @param needleResId
* @param backgroundResId
* @param x
* @param y
* @author ricky barrette
*/
public CompasOverlay(Context context, int needleResId, int backgroundResId, int x, int y){
this(context, null, needleResId, backgroundResId, x, y);
}
/**
* Converts dip to px
* @param dip
* @return px
* @author ricky barrette
*/
private int convertDipToPx(int i) {
Resources r = mContext.getResources();
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, i, r.getDisplayMetrics());
}
/**
* Disables the compass overlay
* @author ricky barrette
*/
public void disable(){
isEnabled = false;
mCompassSensor.disable();
mListener = null;
}
/**
* (non-Javadoc)
* @see com.google.android.maps.Overlay#draw(android.graphics.Canvas, com.google.android.maps.MapView, boolean)
* @author ricky barrette
*/
@Override
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
if(isEnabled){
//set the center of the compass in the top left corner of the screen
Point point = new Point();
point.set(mX, mY);
//draw compass background
Bitmap compass = BitmapFactory.decodeResource( mContext.getResources(), mBackgroundResId);
canvas.drawBitmap(compass,
point.x - (compass.getWidth() / 2),
point.y - (compass.getHeight() / 2),
null
);
//draw the compass needle
Bitmap arrowBitmap = BitmapFactory.decodeResource( mContext.getResources(), mNeedleResId);
Matrix matrix = new Matrix();
matrix.postRotate(GeoUtils.calculateBearing(mLocation, mDestination, mBearing));
Bitmap rotatedBmp = Bitmap.createBitmap(
arrowBitmap,
0, 0,
arrowBitmap.getWidth(),
arrowBitmap.getHeight(),
matrix,
true
);
canvas.drawBitmap(
rotatedBmp,
point.x - (rotatedBmp.getWidth() / 2),
point.y - (rotatedBmp.getHeight() / 2),
null
);
mapView.invalidate();
}
super.draw(canvas, mapView, shadow);
}
/**
* Enables the compass overlay
* @author ricky barrette
*/
public void enable(){
if(! isEnabled){
isEnabled = true;
mCompassSensor.enable(this);
}
}
/**
* Enables the compass overlay
* @param listener
* @author ricky barrette
*/
public void enable(CompassListener listener){
mListener = listener;
enable();
}
/**
* @return the current bearing
* @author ricky barrette
*/
public float getBearing(){
return mBearing;
}
/**
* Called from the compass Sensor to update the current bearing
* (non-Javadoc)
* @see com.TwentyCodes.android.location.CompassListener#onCompassUpdate(float)
* @author ricky barrette
*/
@Override
public void onCompassUpdate(float bearing) {
mBearing = bearing;
/*
* pass it down the chain
*/
if(mListener != null)
mListener.onCompassUpdate(bearing);
}
/**
* @param destination
* @author ricky barrette
*/
public void setDestination(GeoPoint destination){
mDestination = destination;
}
/**
* @param needleResId
* @param backgroundResId
* @param x dip
* @param y dip
* @author ricky barrette
*/
public void setDrawables(int needleResId, int backgroundResId, int x, int y){
mX = convertDipToPx(x);
mY = convertDipToPx(y);
mNeedleResId = needleResId;
mBackgroundResId = backgroundResId;
}
/**
* @param location
* @author ricky barrette
*/
public void setLocation(GeoPoint location){
mLocation = location;
}
}

View File

@@ -0,0 +1,352 @@
/**
* DirectionsOverlay.java
* @date Nov 10, 2011
* @author ricky barrette
* @author Twenty Codes, LLC
*/
package com.TwentyCodes.android.overlays;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.graphics.Color;
import android.util.Log;
import com.TwentyCodes.android.debug.Debug;
import com.TwentyCodes.android.location.MapView;
import com.google.android.maps.GeoPoint;
/**
* This Overlay class will be used to display provided by the Google Directions API on a map
* @author ricky barrette
*/
public class DirectionsOverlay {
private static final String TAG = "DirectionsOverlay";
ArrayList<PathOverlay> mPath;
ArrayList<String> mDirections;
private MapView mMapView;
private OnDirectionsCompleteListener mListener;
private String mCopyRights;
private ArrayList<GeoPoint> mPoints;
private ArrayList<String> mDistance;
private ArrayList<String> mDuration;
private ArrayList<String> mWarnings;
/**
* Downloads and Creates a new DirectionsOverlay from the provided points
* @param origin point
* @param destination point
* @author ricky barrette
* @throws IOException
* @throws ClientProtocolException
* @throws IllegalStateException
* @throws JSONException
*/
public DirectionsOverlay(MapView map, GeoPoint origin, GeoPoint destination, OnDirectionsCompleteListener listener) throws IllegalStateException, ClientProtocolException, IOException, JSONException {
mMapView = map;
mListener = listener;
String json = downloadJSON(generateUrl(origin, destination));
drawPath(json);
}
/**
* Creates a new DirectionsOverlay from the provided String JSON
* @param json
* @throws JSONException
* @author ricky barrette
*/
public DirectionsOverlay(MapView map, String json, OnDirectionsCompleteListener listener) throws JSONException{
mListener = listener;
mMapView = map;
drawPath(json);
}
/**
* Downloads Google Directions JSON from the Internet
* @param url
* @return
* @throws IllegalStateException
* @throws ClientProtocolException
* @throws IOException
* @author ricky barrette
*/
private String downloadJSON(String url) throws IllegalStateException, ClientProtocolException, IOException {
if(Debug.DEBUG)
Log.d(TAG, url);
if(url == null)
throw new NullPointerException();
StringBuffer response = new StringBuffer();
BufferedReader br = new BufferedReader(new InputStreamReader(new DefaultHttpClient().execute(new HttpGet(url)).getEntity().getContent()));
String buff = null;
while ((buff = br.readLine()) != null)
response.append(buff);
return response.toString();
}
/**
* Creates a new DirectionsOverlay from the json provided
* @param json of Google Directions API
* @author ricky barrette
* @return
* @throws JSONException
*/
public void drawPath(String json) throws JSONException{
if(Debug.DEBUG){
Log.d(TAG, "drawPath");
Log.d(TAG, json);
}
mPath = new ArrayList<PathOverlay>();
mDirections = new ArrayList<String>();
mPoints = new ArrayList<GeoPoint>();
mDistance = new ArrayList<String>();
mDuration = new ArrayList<String>();
//get first route
JSONObject route = new JSONObject(json).getJSONArray("routes").getJSONObject(0);
mCopyRights = route.getString("copyrights");
//route.getString("status");
JSONObject leg = route.getJSONArray("legs").getJSONObject(0);
getDistance(leg);
getDuration(leg);
// mMapView.getOverlays().add(new PathOverlay(getGeoPoint(leg.getJSONObject("start_location")), 12, Color.GREEN));
// mMapView.getOverlays().add(new PathOverlay(getGeoPoint(leg.getJSONObject("end_location")), 12, Color.RED));
leg.getString("start_address");
leg.getString("end_address");
// JSONArray warnings = leg.getJSONArray("warnings");
// for(int i = 0; i < warnings.length(); i++){
// mWarnings.add(warnings.get)w
// }w
/*
* here we will parse the steps of the directions
*/
if(Debug.DEBUG)
Log.d(TAG, "processing steps");
JSONArray steps = leg.getJSONArray("steps");
JSONObject step = null;
for(int i = 0; i < steps.length(); i++){
if(Debug.DEBUG)
Log.d(TAG, "step "+i);
step = steps.getJSONObject(i);
if(Debug.DEBUG){
Log.d(TAG, "start "+getGeoPoint(step.getJSONObject("start_location")).toString());
Log.d(TAG, "end "+getGeoPoint(step.getJSONObject("end_location")).toString());
}
// if(Debug.DEBUG)
// mMapView.getOverlays().add(new PathOverlay(getGeoPoint(step.getJSONObject("start_location")), getGeoPoint(step.getJSONObject("end_location")), Color.MAGENTA));
decodePoly(step);
mDuration.add(getDuration(step));
mDistance.add(getDistance(step));
mDirections.add(step.getString("html_instructions"));
// Log.d("TEST", step.getString("html_instructions"));
mPoints.add(getGeoPoint(step.getJSONObject("start_location")));
}
if(Debug.DEBUG)
Log.d(TAG, "finished parsing");
if(mMapView != null){
mMapView.getOverlays().addAll(mPath);
mMapView.postInvalidate();
}
if(mListener != null)
mListener.onDirectionsComplete(DirectionsOverlay.this);
}
/**
* @param origin
* @param destination
* @return The Google API url for our directions
* @author ricky barrette
*/
private String generateUrl(GeoPoint origin, GeoPoint destination){
return "http://maps.googleapis.com/maps/api/directions/json?&origin="+
Double.toString(origin.getLatitudeE6() / 1.0E6)+
","+
Double.toString(origin.getLongitudeE6() / 1.0E6)+
"&destination="+
Double.toString(destination.getLatitudeE6() / 1.0E6)+
","+
Double.toString(destination.getLongitudeE6() / 1.0E6)+
"&sensor=true&mode=walking";
}
/**
* Deocodes googles polyline
* @param encoded
* @return a list of geopoints representing the path
* @author Mark McClure http://facstaff.unca.edu/mcmcclur/googlemaps/encodepolyline/
* @author ricky barrette
* @throws JSONException
*/
private void decodePoly(JSONObject step) throws JSONException {
if(Debug.DEBUG)
Log.d(TAG, "decodePoly");
String encoded = step.getJSONObject("polyline").getString("points");
int index = 0, len = encoded.length();
int lat = 0, lng = 0;
GeoPoint last = null;
while (index < len) {
int b, shift = 0, result = 0;
do {
b = encoded.charAt(index++) - 63;
result |= (b & 0x1f) << shift;
shift += 5;
} while (b >= 0x20);
int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
lat += dlat;
shift = 0;
result = 0;
do {
b = encoded.charAt(index++) - 63;
result |= (b & 0x1f) << shift;
shift += 5;
} while (b >= 0x20);
int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
lng += dlng;
GeoPoint p = new GeoPoint((int) (((double) lat / 1E5) * 1E6), (int) (((double) lng / 1E5) * 1E6));
if(Debug.DEBUG){
Log.d(TAG, "current = "+ p.toString());
if(last != null)
Log.d(TAG, "last = "+ last.toString());
}
if(last != null)
mPath.add(new PathOverlay(last, p, Color.RED));
last = p;
}
}
/**
* Converts a JSON location object into a GeoPoint
* @param point
* @return Geopoint parsed from the provided JSON Object
* @throws JSONException
* @author ricky barrette
*/
private GeoPoint getGeoPoint(JSONObject point) throws JSONException{
return new GeoPoint((int) (point.getDouble("lat")*1E6), (int) (point.getDouble("lng")*1E6));
}
/**
* @param step
* @return the duration of a step
* @throws JSONException
* @author ricky barrette
*/
private String getDuration(JSONObject step) throws JSONException{
return step.getJSONObject("duration").getString("text");
}
/**
* @param step
* @return the distance of a step
* @throws JSONException
* @author ricky barrette
*/
private String getDistance(JSONObject step) throws JSONException{
return step.getJSONObject("distance").getString("text");
}
/**
* @return the array of PathOverlays
* @author ricky barrette
*/
public ArrayList<PathOverlay> getPath(){
return mPath;
}
/**
* Removes the directions overlay from the map view
* @author ricky barrette
*/
public void removePath() {
if(mMapView.getOverlays().removeAll(mPath));
}
/**
* @author ricky barrette
*/
public interface OnDirectionsCompleteListener{
public void onDirectionsComplete(DirectionsOverlay directionsOverlay);
}
/**
* @return
* @author ricky barrette
*/
public ArrayList<String> getDirections() {
return mDirections;
}
/**
* @return
* @author ricky barrette
*/
public ArrayList<GeoPoint> getPoints() {
return mPoints;
}
/**
* @return
* @author ricky barrette
*/
public ArrayList<String> getDurations(){
return mDuration;
}
/**
* @return
* @author ricky barrette
*/
public ArrayList<String> getDistances(){
return mDistance;
}
/**
* @return
* @author ricky barrette
*/
public String getCopyrights(){
return mCopyRights;
}
/**
* @return
* @author ricky barrette
*/
public ArrayList<String> getWarnings() {
return mWarnings;
}
}

View File

@@ -0,0 +1,86 @@
/**
* PathOverlay.java
* @date Nov 11, 2011
* @author ricky barrette
* @author Twenty Codes, LLC
*/
package com.TwentyCodes.android.overlays;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.RectF;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.Projection;
/**
* This overlay class is used to draw a path and points on a map
* @author ricky barrette
*/
public class PathOverlay extends Overlay {
private static final int PATH = 0;
private static final int POINT = 1;
private GeoPoint mStart;
private GeoPoint mEnd;
private int mColor;
private int mMode;
private int mRadius;
/**
* Creates a new PathOverlay in path mode
* @author ricky barrette
*/
public PathOverlay(GeoPoint start, GeoPoint end, int color) {
mStart = start;
mEnd = end;
mColor = color;
mMode = PATH;
}
/**
* Creates a new PathOverlay in point mode. This is used to draw end points.
* @param point
* @param radius
* @param color
* @author ricky barrette
*/
public PathOverlay(GeoPoint point, int radius, int color){
mMode = POINT;
mRadius = radius;
mStart = point;
}
/**
*
* @param canvas canvas to be drawn on
* @param mapView
* @param shadow
* @param when
*/
@Override
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
Projection projection = mapView.getProjection();
Paint paint = new Paint();
paint.setColor(mColor);
paint.setAntiAlias(true);
Point point = new Point();
projection.toPixels(mStart, point);
switch (mMode){
case POINT:
RectF oval = new RectF(point.x - mRadius, point.y - mRadius, point.x + mRadius, point.y + mRadius);
canvas.drawOval(oval, paint);
case PATH:
Point point2 = new Point();
projection.toPixels(mEnd, point2);
paint.setStrokeWidth(5);
paint.setAlpha(120);
canvas.drawLine(point.x, point.y, point2.x, point2.y, paint);
}
super.draw(canvas, mapView, shadow);
}
}

View File

@@ -0,0 +1,158 @@
/**
* @author Twenty Codes
* @author ricky barrette
*/
package com.TwentyCodes.android.overlays;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Point;
import android.graphics.RectF;
import com.TwentyCodes.android.location.GeoUtils;
import com.TwentyCodes.android.location.OnLocationSelectedListener;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.OverlayItem;
import com.google.android.maps.Projection;
/**
* This class will used to draw a radius of a specified size in a specified location, then inserted into
* an overlay list to be displayed a map
* @author ricky barrette
*/
public class RadiusOverlay extends Overlay{
public OverlayItem mOverlayItem;
private GeoPoint mPoint;
private float mRadius = 0;
private int mColor = Color.GREEN;
private GeoPoint mRadiusPoint;
private OnLocationSelectedListener mListener;
/**
* Creates a new RadiusOverlay
* @author ricky barrette
*/
public RadiusOverlay(){
}
/**
* Creates a new RadiusOverlay object that can be inserted into an overlay list.
* @param point center of radius geopoint
* @param radius radius in meters
* @param color desired color of the radius from Color API
* @author ricky barrette
*/
public RadiusOverlay(GeoPoint point, float radius, int color) {
mPoint = point;
mRadius = radius;
mColor = color;
}
/**
* draws a specific radius on the mapview that is handed to it
* @param canvas canvas to be drawn on
* @param mapView
* @param shadow
* @param when
*/
@Override
public void draw(Canvas canvas, MapView mapView, boolean shadow){
if(mPoint != null){
Paint paint = new Paint();
Point center = new Point();
Point left = new Point();
Projection projection = mapView.getProjection();
/*
* Calculate a geopoint that is "radius" meters away from geopoint point and
* convert the given GeoPoint and leftGeo to onscreen pixel coordinates,
* relative to the top-left of the MapView that provided this Projection.
*/
mRadiusPoint = GeoUtils.distanceFrom(mPoint , mRadius);
projection.toPixels(mRadiusPoint, left);
projection.toPixels(mPoint, center);
/*
* get radius of the circle being drawn by
*/
int circleRadius = center.x - left.x;
if(circleRadius <= 0){
circleRadius = left.x - center.x;
}
/*
* paint a circle on the map
*/
paint.setAntiAlias(true);
paint.setStrokeWidth(2.0f);
paint.setColor(mColor);
paint.setStyle(Style.STROKE);
canvas.drawCircle(center.x, center.y, circleRadius, paint);
//draw a dot over the geopoint
RectF oval = new RectF(center.x - 2, center.y - 2, center.x + 2, center.y + 2);
canvas.drawOval(oval, paint);
//fill the radius with a nice green
paint.setAlpha(25);
paint.setStyle(Style.FILL);
canvas.drawCircle(center.x, center.y, circleRadius, paint);
}
}
/**
* @return the selected location
* @author ricky barrette
*/
public GeoPoint getLocation(){
return mPoint;
}
@Override
public boolean onTap(GeoPoint p, MapView mapView) {
mPoint = p;
if(this.mListener != null)
this.mListener.onLocationSelected(p);
return super.onTap(p, mapView);
}
/**
* @param color
* @author ricky barrette
*/
public void setColor(int color){
mColor = color;
}
/**
* @param location
* @author ricky barrette
*/
public void setLocation(GeoPoint location){
mPoint = location;
}
/**
* @param radius in meters
* @author ricky barrette
* @param radius
*/
public void setRadius(int radius){
mRadius = radius;
}
public int getZoomLevel() {
// GeoUtils.GeoUtils.distanceFrom(mPoint , mRadius)
return 0;
}
public void setLocationSelectedListener(OnLocationSelectedListener listener) {
this.mListener = listener;
}
}

View File

@@ -0,0 +1,59 @@
/**
* @author Twenty Codes, LLC
* @author ricky barrette
* @date Oct 2, 2010
*/
package com.TwentyCodes.android.overlays;
import android.content.Context;
import com.TwentyCodes.android.SkyHook.SkyHook;
import com.google.android.maps.MapView;
/**
* this class will be used to display the users location on the map using skyhook's call back methods
* @author ricky barrette
*/
public class SkyHookUserOverlay extends UserOverlayBase{
private SkyHook mSkyHook;
public SkyHookUserOverlay(MapView mapView, Context context) {
super(mapView, context);
mSkyHook = new SkyHook(context);
}
/**
* Construct a new SkyHookUserOverlay
* @param mapView
* @param context
* @param followUser
* @author ricky barrette
*/
public SkyHookUserOverlay(MapView mapView, Context context, boolean followUser) {
super(mapView, context, followUser);
mSkyHook = new SkyHook(context);
}
/**
* Called when the location provider needs to be disabled
* (non-Javadoc)
* @see com.TwentyCodes.android.overlays.UserOverlayBase#onMyLocationDisabled()
*/
@Override
public void onMyLocationDisabled() {
mSkyHook.removeUpdates();
}
/**
* Called when the location provider needs to be enabled
* (non-Javadoc)
* @see com.TwentyCodes.android.overlays.UserOverlayBase#onMyLocationEnabled()
*/
@Override
public void onMyLocationEnabled() {
mSkyHook.setLocationListener(this);
mSkyHook.getUpdates();
}
}

View File

@@ -0,0 +1,41 @@
/**
* @author Twenty Codes, LLC
* @author ricky barrette
* @date Dec 28, 2010
*/
package com.TwentyCodes.android.overlays;
import android.content.Context;
import com.TwentyCodes.android.location.AndroidGPS;
import com.google.android.maps.MapView;
/**
* This is the standard version of the UserOverlay.
* @author ricky barrette
*/
public class UserOverlay extends UserOverlayBase{
private AndroidGPS mAndroidGPS;
public UserOverlay(MapView mapView, Context context) {
super(mapView, context);
mAndroidGPS = new AndroidGPS(context);
}
public UserOverlay(MapView mapView, Context context, boolean followUser) {
super(mapView, context, followUser);
mAndroidGPS = new AndroidGPS(context);
}
@Override
public void onMyLocationDisabled() {
mAndroidGPS.disableLocationUpdates();
}
@Override
public void onMyLocationEnabled() {
mAndroidGPS.enableLocationUpdates(this);
}
}

View File

@@ -0,0 +1,524 @@
/**
* UserOverlayBase.java
* @date Jan 12, 2012
* @author ricky barrette
* @author Twenty Codes, LLC
*/
package com.TwentyCodes.android.overlays;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Paint.Style;
import android.os.Handler;
import android.os.SystemClock;
import android.util.Log;
import com.TwentyCodes.android.location.CompassListener;
import com.TwentyCodes.android.location.GeoPointLocationListener;
import com.TwentyCodes.android.location.GeoUtils;
import com.TwentyCodes.android.location.R;
import com.TwentyCodes.android.location.R.drawable;
import com.TwentyCodes.android.location.R.string;
import com.TwentyCodes.android.debug.Debug;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.Projection;
/**
* This class will be used to build user overlays
* @author ricky barrette
*/
public abstract class UserOverlayBase extends Overlay implements GeoPointLocationListener, CompassListener {
/**
* This thread is responsible for animating the user icon
* @author ricky barrette
*/
public class AnimationThread extends Thread {
private boolean isAborted;
public void abort(){
isAborted = true;
}
/**
* Main method of this animation thread
* (non-Javadoc)
* @see java.lang.Thread#run()
*/
@Override
public void run(){
super.run();
int index = 0;
boolean isCountingDown = false;
while (true) {
synchronized (this) {
if (isAborted) {
break;
}
switch(index){
case 1:
mUserArrow = R.drawable.user_arrow_animation_2;
if(isCountingDown)
index--;
else
index++;
try {
sleep(100l);
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
case 2:
mUserArrow = R.drawable.user_arrow_animation_3;
index--;
isCountingDown = true;
try {
sleep(200l);
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
default:
mUserArrow = R.drawable.user_arrow_animation_1;
index++;
isCountingDown = false;
try {
sleep(700l);
} catch (InterruptedException e) {
e.printStackTrace();
return;
}
break;
}
}
}
}
}
private final String TAG = "UserOverlayBase";
private boolean isEnabled;
private int mUserArrow = R.drawable.user_arrow_animation_1;
private AnimationThread mAnimationThread;
private float mBearing = 0;
private int mAccuracy;
private GeoPoint mPoint;
private Context mContext;
private MapView mMapView;
private ProgressDialog mGPSprogress;
private boolean isFistFix = true;
private GeoPointLocationListener mListener;
public boolean isFollowingUser = true;
private CompasOverlay mCompass;
private boolean isCompassEnabled;
private boolean isGPSDialogEnabled;
private CompassListener mCompassListener;
/**
* Construct a new UserOverlay
* @param mapView
* @param context
* @author ricky barrette
*/
public UserOverlayBase(MapView mapView, Context context) {
super();
mContext = context;
mMapView = mapView;
mCompass = new CompasOverlay(context);
mUserArrow = R.drawable.user_arrow_animation_1;
}
/**
* Construct a new UserOverlayTODO Auto-generated method stub
* @param mapView
* @param context
* @param followUser
* @author ricky barrette
*/
public UserOverlayBase(MapView mapView, Context context, boolean followUser) {
this(mapView, context);
isFollowingUser = followUser;
}
/**
* Disables the compass
* @author ricky barrette
*/
public final void disableCompass(){
isCompassEnabled = false;
mMapView.getOverlays().remove(mCompass);
}
/**
* Disables the Acquiring GPS dialog
* @author ricky barrette
*/
public void disableGPSDialog(){
isGPSDialogEnabled = false;
if(mGPSprogress != null)
mGPSprogress.dismiss();
}
/**
* Stops location updates and removes the overlay from view
* @author ricky barrette
*/
public final void disableMyLocation(){
Log.d(TAG,"disableMyLocation()");
onMyLocationDisabled();
isEnabled = false;
mCompass.disable();
if(mGPSprogress != null)
mGPSprogress.cancel();
mAnimationThread.abort();
}
/**
* we override this methods so we can provide a drawable and a location to draw on the canvas.
* (non-Javadoc)
* @see com.google.android.maps.Overlay#draw(android.graphics.Canvas, com.google.android.maps.MapView, boolean)
* @param canvas
* @param mapView
* @param shadow
* @author ricky barrette
*/
@Override
public void draw(Canvas canvas, MapView mapView, boolean shadow){
if (isEnabled && mPoint != null) {
Point center = new Point();
Point left = new Point();
Projection projection = mapView.getProjection();
GeoPoint leftGeo = GeoUtils.distanceFrom(mPoint, mAccuracy);
projection.toPixels(leftGeo, left);
projection.toPixels(mPoint, center);
canvas = drawAccuracyCircle(center, left, canvas);
canvas = drawUser(center, mBearing, canvas);
/*
* the following log is used to demonstrate if the leftGeo point is the correct
*/
// Log.d(TAG, (GeoUtils.distanceKm(mPoint, leftGeo) * 1000)+"m");
}
super.draw(canvas, mapView, shadow);
}
/**
* draws an accuracy circle onto the canvas supplied
* @param center point of the circle
* @param left point of the circle
* @param canvas to be drawn on
* @return modified canvas
* @author ricky barrette
*/
private Canvas drawAccuracyCircle(Point center, Point left, Canvas canvas) {
Paint paint = new Paint();
/*
* get radius of the circle being drawn by
*/
int circleRadius = center.x - left.x;
if(circleRadius <= 0){
circleRadius = left.x - center.x;
}
/*
* paint a blue circle on the map
*/
paint.setAntiAlias(true);
paint.setStrokeWidth(2.0f);
paint.setColor(Color.BLUE);
paint.setStyle(Style.STROKE);
canvas.drawCircle(center.x, center.y, circleRadius, paint);
/*
* fill the radius with a alpha blue
*/
paint.setAlpha(30);
paint.setStyle(Style.FILL);
canvas.drawCircle(center.x, center.y, circleRadius, paint);
/*
* for testing
* draw a dot over the left geopoint
*/
// paint.setColor(Color.RED);
// RectF oval = new RectF(left.x - 1, left.y - 1, left.x + 1, left.y + 1);
// canvas.drawOval(oval, paint);
return canvas;
}
/**
* draws user arrow that points north based on bearing onto the supplied canvas
* @param point to draw user arrow on
* @param bearing of the device
* @param canvas to draw on
* @return modified canvas
* @author ricky barrette
*/
private Canvas drawUser(Point point, float bearing, Canvas canvas){
Bitmap user = BitmapFactory.decodeResource(mContext.getResources(), mUserArrow);
Matrix matrix = new Matrix();
matrix.postRotate(bearing);
Bitmap rotatedBmp = Bitmap.createBitmap(
user,
0, 0,
user.getWidth(),
user.getHeight(),
matrix,
true
);
canvas.drawBitmap(
rotatedBmp,
point.x - (rotatedBmp.getWidth() / 2),
point.y - (rotatedBmp.getHeight() / 2),
null
);
return canvas;
}
/**
* Enables the compass
* @author ricky barrette
*/
public void enableCompass(){
if(! this.isCompassEnabled){
this.mMapView.getOverlays().add(this.mCompass);
this.isCompassEnabled = true;
}
}
/**
* Enables the Acquiring GPS dialog if the location has not been acquired
*
* TODO fix funtion currently generates bad window token
* @author ricky barrette
*/
public void enableGPSDialog(){
isGPSDialogEnabled = true;
if(isFistFix)
if(mGPSprogress != null){
if(! mGPSprogress.isShowing())
mGPSprogress = ProgressDialog.show(mContext, "", mContext.getText(R.string.gps_fix), true, true);
} else
mGPSprogress = ProgressDialog.show(mContext, "", mContext.getText(R.string.gps_fix), true, true);
}
/**
* Attempts to enable MyLocation, registering for updates from provider
* @author ricky barrette
*/
public void enableMyLocation(){
Log.d(TAG,"enableMyLocation()");
if (! isEnabled) {
mAnimationThread = new AnimationThread();
mAnimationThread.start();
onMyLocationEnabled();
isEnabled = true;
mCompass.enable(this);
isFistFix = true;
if(isGPSDialogEnabled)
enableGPSDialog();
/**
* this is a message that tells the user that we are having trouble getting an GPS signal
*/
new Handler().postAtTime(new Runnable() {
@Override
public void run() {
if(mGPSprogress != null)
if (mGPSprogress.isShowing()) {
mGPSprogress.cancel();
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setMessage(
mContext.getText(R.string.sorry_theres_trouble))
.setCancelable(false)
.setPositiveButton(mContext.getText(android.R.string.ok),
new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dialog, int id) {
dialog.cancel();
}
});
builder.show();
}
}
}, SystemClock.uptimeMillis()+90000L);
}
}
/**
* Allows the map to follow the user
* @param followUser
* @author ricky barrette
*/
public void followUser(boolean followUser){
Log.d(TAG,"followUser()");
isFollowingUser = followUser;
}
/**
* returns the users current bearing
* @return
* @author ricky barrette
*/
public float getUserBearing(){
return mBearing;
}
/**
* returns the users current location
* @return
* @author ricky barrette
*/
public GeoPoint getUserLocation(){
return mPoint;
}
@Override
public void onCompassUpdate(float bearing) {
if(Debug.DEBUG)
Log.v(TAG, "onCompassUpdate()");
if(mCompassListener != null)
mCompassListener.onCompassUpdate(bearing);
mBearing = bearing;
mMapView.invalidate();
}
/**
* called when the SkyHook location changes, this mthod is resposiable for updating the overlay location and accuracy circle.
* (non-Javadoc)
* @see com.TwentyCodes.android.SkyHook.GeoPointLocationListener.location.LocationListener#onLocationChanged(com.google.android.maps.GeoPoint, float)
* @param point
* @param accuracy
* @author ricky barrette
*/
@Override
public void onLocationChanged(GeoPoint point, int accuracy) {
if(mCompass != null)
mCompass.setLocation(point);
/*
* if this is the first fix
* set map center the users location, and zoom to the max zoom level
*/
if(point != null && isFistFix){
mMapView.getController().setCenter(point);
mMapView.getController().setZoom( (mMapView.getMaxZoomLevel() - 2) );
if(mGPSprogress != null)
mGPSprogress.dismiss();
isFistFix = false;
}
//update the users point, and accuracy for the UI
mPoint = point;
mAccuracy = accuracy;
mMapView.invalidate();
if(mListener != null){
mListener.onLocationChanged(point, accuracy);
}
if (isFollowingUser) {
panToUserIfOffMap(point);
}
}
/**
* Called when disableMyLocation is called. This is where you want to disable any location updates from your provider
* @author ricky barrette
*/
public abstract void onMyLocationDisabled();
/**
* Called when the enableMyLocation() is called. This is where you want to ask your location provider for updates
* @author ricky barrette
*/
public abstract void onMyLocationEnabled();
/**
* pans the map view if the user is off screen.
* @author ricky barrette
*/
private void panToUserIfOffMap(GeoPoint user) {
GeoPoint center = mMapView.getMapCenter();
double distance = GeoUtils.distanceKm(center, user);
double distanceLat = GeoUtils.distanceKm(center, new GeoPoint((center.getLatitudeE6() + (int) (mMapView.getLatitudeSpan() / 2)), center.getLongitudeE6()));
double distanceLon = GeoUtils.distanceKm(center, new GeoPoint(center.getLatitudeE6(), (center.getLongitudeE6() + (int) (mMapView.getLongitudeSpan() / 2))));
double whichIsGreater = (distanceLat > distanceLon) ? distanceLat : distanceLon;
/**
* if the user is one the map, keep them their
* else don't pan to user unless they pan pack to them
*/
if( ! (distance > whichIsGreater) )
if (distance > distanceLat || distance > distanceLon){
mMapView.getController().animateTo(user);
}
}
/**
* Attempts to register the listener for location updates
* @param listener
* @author Ricky Barrette
*/
public void registerListener(GeoPointLocationListener listener){
Log.d(TAG,"registerListener()");
if (mListener == null){
mListener = listener;
}
}
/**
* Set the compass drawables and location
* @param needleResId
* @param backgroundResId
* @param x
* @param y
* @author ricky barrette
*/
public void setCompassDrawables(int needleResId, int backgroundResId, int x, int y) {
mCompass.setDrawables(needleResId, backgroundResId, x, y);
}
/**
* Sets the CompassListener
* @param listener
* @author ricky barrette
*/
public void setCompassListener(CompassListener listener){
mCompassListener = listener;
}
/**
* Sets the destination for the compass
* @author ricky barrette
*/
public void setDestination(GeoPoint destination){
if(mCompass != null)
mCompass.setDestination(destination);
}
/**
* UnResgisters the listener. after this call you will no longer get location updates
* @author Ricky Barrette
*/
public void unRegisterListener(){
Log.d(TAG,"unRegisterListener()");
mListener = null;
}
}