Created abstract class UserOverlayBase.java to replace most of the code that is redundant in UserOverlay.java ands SkyHookUserOverlay.java. This will make it easyer to update the UserOverlays across the board
|
After Width: | Height: | Size: 898 B |
|
After Width: | Height: | Size: 760 B |
|
After Width: | Height: | Size: 751 B |
|
After Width: | Height: | Size: 466 B |
|
After Width: | Height: | Size: 776 B |
|
After Width: | Height: | Size: 527 B |
|
After Width: | Height: | Size: 417 B |
|
After Width: | Height: | Size: 473 B |
|
After Width: | Height: | Size: 276 B |
|
After Width: | Height: | Size: 410 B |
|
After Width: | Height: | Size: 664 B |
|
After Width: | Height: | Size: 567 B |
|
After Width: | Height: | Size: 608 B |
|
After Width: | Height: | Size: 359 B |
|
After Width: | Height: | Size: 523 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 949 B |
|
After Width: | Height: | Size: 943 B |
|
After Width: | Height: | Size: 579 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 136 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 825 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
@@ -5,107 +5,22 @@
|
|||||||
*/
|
*/
|
||||||
package com.TwentyCodes.android.SkyHook;
|
package com.TwentyCodes.android.SkyHook;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.app.ProgressDialog;
|
|
||||||
import android.content.Context;
|
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.Paint.Style;
|
|
||||||
import android.graphics.Point;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.SystemClock;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.TwentyCodes.android.debug.Debug;
|
import com.TwentyCodes.android.location.UserOverlayBase;
|
||||||
import com.TwentyCodes.android.location.CompasOverlay;
|
|
||||||
import com.TwentyCodes.android.location.CompassListener;
|
|
||||||
import com.TwentyCodes.android.location.GeoPointLocationListener;
|
|
||||||
import com.TwentyCodes.android.location.GeoUtils;
|
|
||||||
import com.google.android.maps.GeoPoint;
|
|
||||||
import com.google.android.maps.MapView;
|
import com.google.android.maps.MapView;
|
||||||
import com.google.android.maps.Overlay;
|
|
||||||
import com.google.android.maps.Projection;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* this class will be used to display the users location on the map using skyhook's call back methods
|
* this class will be used to display the users location on the map using skyhook's call back methods
|
||||||
* @author ricky barrette
|
* @author ricky barrette
|
||||||
*/
|
*/
|
||||||
public class SkyHookUserOverlay extends Overlay implements GeoPointLocationListener, CompassListener{
|
public class SkyHookUserOverlay extends UserOverlayBase{
|
||||||
|
|
||||||
private float mBearing = 0;
|
|
||||||
private int mAccuracy;
|
|
||||||
private GeoPoint mPoint;
|
|
||||||
private Context mContext;
|
|
||||||
private SkyHook mSkyHook;
|
private SkyHook mSkyHook;
|
||||||
private boolean isEnabled;
|
|
||||||
private MapView mMapView;
|
|
||||||
private ProgressDialog mGPSprogress;
|
|
||||||
private boolean isFistFix = true;
|
|
||||||
private GeoPointLocationListener mListener;
|
|
||||||
public boolean isFollowingUser = true;
|
|
||||||
private final String TAG = "SkyHookUserOverlay";
|
|
||||||
private CompasOverlay mCompass;
|
|
||||||
private boolean isCompassEnabled;
|
|
||||||
private int mUserArrow = R.drawable.user_arrow_animation_1;
|
|
||||||
private volatile Thread mAnimationThread;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a new SkyHookUserOverlaymFollowUser
|
|
||||||
* @param mapView
|
|
||||||
* @param context
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public SkyHookUserOverlay(MapView mapView, Context context) {
|
public SkyHookUserOverlay(MapView mapView, Context context) {
|
||||||
mContext = context;
|
super(mapView, context);
|
||||||
mMapView = mapView;
|
|
||||||
mSkyHook = new SkyHook(context);
|
mSkyHook = new SkyHook(context);
|
||||||
mCompass = new CompasOverlay(context);
|
|
||||||
mUserArrow = R.drawable.user_arrow_animation_1;
|
|
||||||
mAnimationThread = new Thread(new Runnable(){
|
|
||||||
@Override
|
|
||||||
public void run(){
|
|
||||||
int index = 0;
|
|
||||||
boolean isCountingUp = true;
|
|
||||||
while(! isEnabled){
|
|
||||||
switch(index){
|
|
||||||
case 1:
|
|
||||||
mUserArrow = R.drawable.user_arrow_animation_2;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
mUserArrow = R.drawable.user_arrow_animation_3;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
mUserArrow = R.drawable.user_arrow_animation_1;
|
|
||||||
try {
|
|
||||||
Thread.sleep(300l);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
Thread.sleep(200l);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(isCountingUp){
|
|
||||||
if(index++ == 2)
|
|
||||||
isCountingUp = false;
|
|
||||||
} else if(index-- == 0)
|
|
||||||
isCountingUp = true;
|
|
||||||
|
|
||||||
if(! isEnabled)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -116,341 +31,29 @@ public class SkyHookUserOverlay extends Overlay implements GeoPointLocationListe
|
|||||||
* @author ricky barrette
|
* @author ricky barrette
|
||||||
*/
|
*/
|
||||||
public SkyHookUserOverlay(MapView mapView, Context context, boolean followUser) {
|
public SkyHookUserOverlay(MapView mapView, Context context, boolean followUser) {
|
||||||
this(mapView, context);
|
super(mapView, context, followUser);
|
||||||
isFollowingUser = followUser;
|
mSkyHook = new SkyHook(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disables the compass
|
* Called when the location provider needs to be disabled
|
||||||
* @author ricky barrette
|
* (non-Javadoc)
|
||||||
|
* @see com.TwentyCodes.android.location.UserOverlayBase#onMyLocationDisabled()
|
||||||
*/
|
*/
|
||||||
public void disableCompass(){
|
@Override
|
||||||
isCompassEnabled = false;
|
public void onMyLocationDisabled() {
|
||||||
mMapView.getOverlays().remove(mCompass);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stops location updates and removes the overlay from view
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public synchronized void disableMyLocation(){
|
|
||||||
// mAnimationThread.stop();
|
|
||||||
Log.d(TAG,"disableMyLocation()");
|
|
||||||
mSkyHook.removeUpdates();
|
mSkyHook.removeUpdates();
|
||||||
isEnabled = false;
|
|
||||||
mCompass.disable();
|
|
||||||
mGPSprogress.cancel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disables the Acquiring GPS dialog
|
* Called when the location provider needs to be enabled
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public void disableGPSDialog(){
|
|
||||||
if(mGPSprogress != null)
|
|
||||||
mGPSprogress.dismiss();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* we override this methods so we can provide a drawable and a location to draw on the canvas.
|
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
* @see com.google.android.maps.Overlay#draw(android.graphics.Canvas, com.google.android.maps.MapView, boolean)
|
* @see com.TwentyCodes.android.location.UserOverlayBase#onMyLocationEnabled()
|
||||||
* @param canvas
|
|
||||||
* @param mapView
|
|
||||||
* @param shadow
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void draw(Canvas canvas, MapView mapView, boolean shadow){
|
public void onMyLocationEnabled() {
|
||||||
if (isEnabled && mPoint != null) {
|
mSkyHook.setLocationListener(this);
|
||||||
Point center = new Point();
|
mSkyHook.getUpdates();
|
||||||
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(SkyHook.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 synchronized 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
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public void enableGPSDialog(){
|
|
||||||
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 sky hook
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public void enableMyLocation(){
|
|
||||||
Log.d(TAG,"enableMyLocation()");
|
|
||||||
if (! isEnabled) {
|
|
||||||
// this.mAnimationThread.start();
|
|
||||||
mSkyHook.setLocationListener(this);
|
|
||||||
mSkyHook.getUpdates();
|
|
||||||
isEnabled = true;
|
|
||||||
mCompass.enable(this);
|
|
||||||
isFistFix = true;
|
|
||||||
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.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()");
|
|
||||||
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) );
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -5,498 +5,36 @@
|
|||||||
*/
|
*/
|
||||||
package com.TwentyCodes.android.location;
|
package com.TwentyCodes.android.location;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.app.ProgressDialog;
|
|
||||||
import android.content.Context;
|
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.Paint.Style;
|
|
||||||
import android.graphics.Point;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.SystemClock;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.TwentyCodes.android.SkyHook.R;
|
|
||||||
import com.TwentyCodes.android.debug.Debug;
|
|
||||||
import com.google.android.maps.GeoPoint;
|
|
||||||
import com.google.android.maps.MapView;
|
import com.google.android.maps.MapView;
|
||||||
import com.google.android.maps.Overlay;
|
|
||||||
import com.google.android.maps.Projection;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the standard version of the UserOverlay.
|
* This is the standard version of the UserOverlay.
|
||||||
* @author ricky barrette
|
* @author ricky barrette
|
||||||
*/
|
*/
|
||||||
public class UserOverlay extends Overlay implements GeoPointLocationListener, CompassListener {
|
public class UserOverlay extends UserOverlayBase{
|
||||||
|
|
||||||
private boolean isEnabled;
|
|
||||||
private int mUserArrow = R.drawable.user_arrow_animation_1;
|
|
||||||
private Thread mAnimationThread;
|
|
||||||
|
|
||||||
private float mBearing = 0;
|
|
||||||
private int mAccuracy;
|
|
||||||
private GeoPoint mPoint;
|
|
||||||
private Context mContext;
|
|
||||||
private AndroidGPS mAndroidGPS;
|
private AndroidGPS mAndroidGPS;
|
||||||
private MapView mMapView;
|
|
||||||
private ProgressDialog mGPSprogress;
|
|
||||||
private boolean isFistFix = true;
|
|
||||||
private GeoPointLocationListener mListener;
|
|
||||||
public boolean isFollowingUser = true;
|
|
||||||
private final String TAG = "SkyHookUserOverlay";
|
|
||||||
private CompasOverlay mCompass;
|
|
||||||
private boolean isCompassEnabled;
|
|
||||||
private boolean isGPSDialogEnabled;
|
|
||||||
private CompassListener mCompassListener;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a new SkyHookUserOverlaymFollowUser
|
|
||||||
* @param mapView
|
|
||||||
* @param context
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public UserOverlay(MapView mapView, Context context) {
|
public UserOverlay(MapView mapView, Context context) {
|
||||||
mContext = context;
|
super(mapView, context);
|
||||||
mMapView = mapView;
|
|
||||||
mAndroidGPS = new AndroidGPS(context);
|
mAndroidGPS = new AndroidGPS(context);
|
||||||
mCompass = new CompasOverlay(context);
|
|
||||||
mUserArrow = R.drawable.user_arrow_animation_1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a new SkyHookUserOverlay
|
|
||||||
* @param mapView
|
|
||||||
* @param context
|
|
||||||
* @param followUser
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public UserOverlay(MapView mapView, Context context, boolean followUser) {
|
public UserOverlay(MapView mapView, Context context, boolean followUser) {
|
||||||
this(mapView, context);
|
super(mapView, context, followUser);
|
||||||
isFollowingUser = followUser;
|
mAndroidGPS = new AndroidGPS(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Disables the compass
|
public void onMyLocationDisabled() {
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public void disableCompass(){
|
|
||||||
isCompassEnabled = false;
|
|
||||||
mMapView.getOverlays().remove(mCompass);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stops location updates and removes the overlay from view
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public void disableMyLocation(){
|
|
||||||
Log.d(TAG,"disableMyLocation()");
|
|
||||||
mAndroidGPS.disableLocationUpdates();
|
mAndroidGPS.disableLocationUpdates();
|
||||||
isEnabled = false;
|
|
||||||
mCompass.disable();
|
|
||||||
if(mGPSprogress != null)
|
|
||||||
mGPSprogress.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disables the Acquiring GPS dialog
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public void disableGPSDialog(){
|
|
||||||
isGPSDialogEnabled = false;
|
|
||||||
if(mGPSprogress != null)
|
|
||||||
mGPSprogress.dismiss();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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(SkyHook.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 synchronized 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
|
|
||||||
* @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 sky hook
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
public void enableMyLocation(){
|
|
||||||
Log.d(TAG,"enableMyLocation()");
|
|
||||||
if (! isEnabled) {
|
|
||||||
|
|
||||||
mAnimationThread = new AnimationThread();
|
|
||||||
mAnimationThread.start();
|
|
||||||
|
|
||||||
mAndroidGPS.enableLocationUpdates(this);
|
|
||||||
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
|
@Override
|
||||||
public void onCompassUpdate(float bearing) {
|
public void onMyLocationEnabled() {
|
||||||
if(Debug.DEBUG)
|
mAndroidGPS.enableLocationUpdates(this);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This thread is responsible for animating the user icon
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
private class AnimationThread extends Thread {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main method of this animation thread
|
|
||||||
* (non-Javadoc)
|
|
||||||
* @see java.lang.Thread#run()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void run(){
|
|
||||||
super.run();
|
|
||||||
int index = 0;
|
|
||||||
boolean isCountingUp = true;
|
|
||||||
while (true) {
|
|
||||||
synchronized (this) {
|
|
||||||
if (! isEnabled) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(index){
|
|
||||||
case 1:
|
|
||||||
setDrawable(R.drawable.user_arrow_animation_2);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
setDrawable(R.drawable.user_arrow_animation_3);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
setDrawable(R.drawable.user_arrow_animation_1);
|
|
||||||
try {
|
|
||||||
sleep(300l);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
sleep(200l);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(isCountingUp){
|
|
||||||
if(index++ == 2)
|
|
||||||
isCountingUp = false;
|
|
||||||
} else if(index-- == 0)
|
|
||||||
isCountingUp = true;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the id of the user drawable
|
|
||||||
* @param id
|
|
||||||
* @author ricky barrette
|
|
||||||
*/
|
|
||||||
private synchronized void setDrawable(int id){
|
|
||||||
mUserArrow = id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,520 @@
|
|||||||
|
/**
|
||||||
|
* UserOverlayBase.java
|
||||||
|
* @date Jan 12, 2012
|
||||||
|
* @author ricky barrette
|
||||||
|
* @author Twenty Codes, LLC
|
||||||
|
*/
|
||||||
|
package com.TwentyCodes.android.location;
|
||||||
|
|
||||||
|
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.SkyHook.R;
|
||||||
|
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 synchronized 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 isCountingUp = true;
|
||||||
|
while (true) {
|
||||||
|
synchronized (this) {
|
||||||
|
if (! isAborted) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(index){
|
||||||
|
case 1:
|
||||||
|
setDrawable(R.drawable.user_arrow_animation_2);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
setDrawable(R.drawable.user_arrow_animation_3);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
setDrawable(R.drawable.user_arrow_animation_1);
|
||||||
|
try {
|
||||||
|
sleep(300l);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
sleep(200l);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isCountingUp){
|
||||||
|
if(index++ == 2)
|
||||||
|
isCountingUp = false;
|
||||||
|
} else if(index-- == 0)
|
||||||
|
isCountingUp = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the id of the user drawable
|
||||||
|
* @param id
|
||||||
|
* @author ricky barrette
|
||||||
|
*/
|
||||||
|
private synchronized void setDrawable(int id){
|
||||||
|
mUserArrow = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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();
|
||||||
|
mAnimationThread.abort();
|
||||||
|
if(mGPSprogress != null)
|
||||||
|
mGPSprogress.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
}
|
||||||