/** * */ package com.nbs.client.assassins.views; import android.app.Application; import android.content.Context; import android.content.res.Configuration; import android.graphics.Color; import android.location.Location; import android.location.LocationManager; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.util.Log; import android.view.View; import com.actionbarsherlock.app.SherlockMapFragment; import com.google.android.gms.maps.CameraUpdate; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.GoogleMap.CancelableCallback; import com.google.android.gms.maps.UiSettings; import com.google.android.gms.maps.model.BitmapDescriptorFactory; import com.google.android.gms.maps.model.CameraPosition; import com.google.android.gms.maps.model.Circle; import com.google.android.gms.maps.model.CircleOptions; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.Marker; import com.google.android.gms.maps.model.MarkerOptions; import com.google.android.gms.maps.model.Polygon; import com.google.android.gms.maps.model.PolygonOptions; import com.google.android.gms.maps.model.Polyline; import com.google.android.gms.maps.model.PolylineOptions; import com.googlecode.androidannotations.annotations.EFragment; import com.nbs.client.assassins.models.App; import com.nbs.client.assassins.models.Match; import com.nbs.client.assassins.models.Player; import com.nbs.client.assassins.models.Repository; import com.nbs.client.assassins.models.User; import com.nbs.client.assassins.sensors.BearingProvider; import com.nbs.client.assassins.sensors.BearingReceiver; /** * @author cam * */ @EFragment public class MapFragment extends SherlockMapFragment implements BearingReceiver { protected static final String TAG = "MapFragment"; @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); } BearingProvider bearingProvider; public static final int MODE_NORTH = 0; public static final int MODE_BEARING = 1; private int MODE = MODE_NORTH; private float bearing; private static final float DEFAULT_ZOOM = 18.0f; private static final float DEFAULT_TILT = 67.5f; private static final double METERS_PER_MILE = 1609.0d; private static final double RADIUS_OF_EARTH = 6371.0d; private GoogleMap map; private Marker targetLocationMarker; private Circle aRangeCircle; private Circle hRangeCircle; private Polygon boundsPolygon; private Marker myLocationMarker; private int RED = Color.argb(100, Color.red(Color.RED), Color.green(Color.RED), Color.blue(Color.RED)); private int BLUE = Color.argb(100, Color.red(Color.BLUE), Color.green(Color.BLUE), Color.blue(Color.BLUE)); private boolean animating = false; private Float tBearing; private Polyline tBearingLine; public MapFragment(){ } @Override public void onViewCreated(View view, Bundle savedInstanceState) { map = getMap(); UiSettings uiSettings = map.getUiSettings(); uiSettings.setCompassEnabled(true); map.setMapType(GoogleMap.MAP_TYPE_NORMAL); uiSettings.setScrollGesturesEnabled(false); uiSettings.setRotateGesturesEnabled(false); uiSettings.setZoomGesturesEnabled(true); uiSettings.setZoomControlsEnabled(true); Location lastLocation = getBestLastKnownLocation(); LatLng lastLatLng; Repository model = App.getRepo(); User user = model.getUser(); Player player = model.getMyFocusedPlayer(); if(lastLocation == null) { lastLatLng = user.getLocation(); } else { lastLatLng = new LatLng(lastLocation.getLatitude(), lastLocation.getLongitude()); if(user.getLocation() == null) { user.setLocation(lastLocation); } } if(lastLatLng != null) showMyLocation(lastLatLng); if(model.inActiveMatch()) { showGameBoundary(); showRangeCircles(lastLatLng); showDirectionToTarget(tBearing); showTargetLocation(player.getTargetLatLng()); } moveMapPositionTo(lastLatLng, DEFAULT_ZOOM, DEFAULT_TILT, true, 2000); super.onViewCreated(view, savedInstanceState); } @Override public void onResume() { super.onResume(); if(MODE == MODE_BEARING) { registerForSensorUpdates(); } else if (MODE == MODE_NORTH) { stopSensorUpdates(); } } @Override public void onPause() { super.onPause(); stopSensorUpdates(); } public int getCompassMode() { return this.MODE; } public void toggleCompassMode() { if(getCompassMode() == MODE_BEARING) { MODE = MODE_NORTH; stopSensorUpdates(); Repository model = App.getRepo(); User user = model.getUser(); this.moveMapPositionTo(user.getLocation(), true, 800); map.getUiSettings().setZoomControlsEnabled(true); } else { MODE = MODE_BEARING; map.getUiSettings().setZoomControlsEnabled(false); if(!animating) registerForSensorUpdates(); } } public void setBearingProvider(BearingProvider bp) { bearingProvider = bp; } private void stopSensorUpdates() { if(bearingProvider != null) bearingProvider.unregisterForBearingUpdates(this); } private void registerForSensorUpdates() { if(bearingProvider != null) bearingProvider.registerForBearingUpdates(this); } public void onLocationChanged(LatLng location) { showMyLocation(location); Repository model = App.getRepo(); if(model.inActiveMatch()) { showRangeCircles(location); showDirectionToTarget(tBearing); } else { hideRangeCircles(); hideTargetLocation(); hideDirectionToTarget(); } moveMapPositionTo(location); } public void moveMapPositionTo(LatLng location) { moveMapPositionTo(location, false, null); } public void moveMapPositionTo(LatLng location, boolean animate, Integer duration) { CameraPosition cam = map.getCameraPosition(); moveMapPositionTo(location, cam.zoom, cam.tilt, animate, duration) ; } public void moveMapPositionTo(LatLng location, Float zoom, Float tilt, boolean animate, Integer duration) { if(!animating && location != null) { Log.d(TAG, "move map to [position=" + location.toString() + ", bearing=" + bearing + ", animate="+ animate + "]"); CameraPosition cameraPosition = new CameraPosition.Builder() .target(location) .zoom(zoom != null ? zoom : DEFAULT_ZOOM) .bearing(MODE == MODE_NORTH ? 0 : bearing) .tilt(tilt != null ? tilt : DEFAULT_TILT) .build(); CameraUpdate camUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition); if(animate) { stopSensorUpdates(); animating = true; map.animateCamera(camUpdate, duration != null ? duration : 1000, new CancelableCallback(){ @Override public void onCancel() { animating = false; if(MODE == MODE_BEARING) registerForSensorUpdates(); } @Override public void onFinish() { animating = false; if(MODE == MODE_BEARING) registerForSensorUpdates(); } }); } else { //map.stopAnimation(); map.moveCamera(camUpdate); } } } public void showMyLocation(LatLng location) { if(myLocationMarker == null) { myLocationMarker = map.addMarker(new MarkerOptions() .position(location) .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN))); } else { myLocationMarker.setPosition(location); } } public void showTargetLocation(LatLng tLatLng) { if(tLatLng != null) { if(targetLocationMarker == null) { Repository model = App.getRepo(); Player player = model.getMyFocusedPlayer(); Log.d(TAG, "tLatLng: " + tLatLng); Log.d(TAG, player.toString()); targetLocationMarker = getMap().addMarker( new MarkerOptions() .position(tLatLng) .title("target") .snippet(String.valueOf(player.getTargetLife())) .icon(BitmapDescriptorFactory .defaultMarker(BitmapDescriptorFactory.HUE_AZURE))); targetLocationMarker.showInfoWindow(); } else { targetLocationMarker.setPosition(tLatLng); } targetLocationMarker.setVisible(true); } } public void showGameBoundary() { if(boundsPolygon == null) { boundsPolygon = map.addPolygon(new PolygonOptions() .zIndex(0) //TODO: show actual match bounds polygon .add(new LatLng(40.3, -111.6), new LatLng(40.4, -111.6), new LatLng(40.4, -111.7), new LatLng(40.3, -111.7)) .strokeColor(BLUE) .fillColor(Color.TRANSPARENT)); } } public void showRangeCircles(LatLng location) { Repository model = App.getRepo(); Match match = model.getFocusedMatch(); Double aRange = match.getAttackRange(); //if in match with attack range, draw/update the circle position if(aRange != null) { if(aRangeCircle == null) { aRangeCircle = map.addCircle(new CircleOptions() .center(location) .radius(aRange*METERS_PER_MILE) .strokeColor(RED) .strokeWidth(5) .fillColor(Color.TRANSPARENT)); } else { aRangeCircle.setCenter(location); } } else if (aRangeCircle != null) { //if not in a match and showing an attack range circle, clear it aRangeCircle.remove(); } Double hRange = match.getHuntRange(); if(hRange != null) { if(hRangeCircle == null) { hRangeCircle = map.addCircle(new CircleOptions() .center(location) .radius(hRange*METERS_PER_MILE) .strokeColor(BLUE) .strokeWidth(5) .fillColor(Color.TRANSPARENT)); } else { hRangeCircle.setCenter(location); } } else if (hRangeCircle != null) { hRangeCircle.remove(); } } private void showDirectionToTarget(Float tBearing) { Repository model = App.getRepo(); Match match = model.getFocusedMatch(); Double aRange = match.getAttackRange(); if(tBearing != null && myLocationMarker != null && aRange != null) { LatLng myLocation = this.myLocationMarker.getPosition(); tBearing = (float) Math.toRadians(tBearing); double dist = aRange/RADIUS_OF_EARTH; double lat1 = Math.toRadians(myLocation.latitude); double lon1 = Math.toRadians(myLocation.longitude); double lat2 = Math.asin( Math.sin(lat1)*Math.cos(dist) + Math.cos(lat1)*Math.sin(dist)*Math.cos(tBearing) ); double a = Math.atan2(Math.sin(tBearing)*Math.sin(dist)*Math.cos(lat1), Math.cos(dist)-Math.sin(lat1)*Math.sin(lat2)); double lon2 = lon1 + a; //Is this necessary? //lon2 = (lon2+ 3.0*Math.PI) % (2.0*Math.PI) - Math.PI; if(tBearingLine != null) { tBearingLine.remove(); } tBearingLine = map.addPolyline(new PolylineOptions() .add(myLocation, new LatLng(Math.toDegrees(lat2), Math.toDegrees(lon2))) .color(RED) .width(5)); } else if(tBearingLine != null) { tBearingLine.remove(); } } private Location getBestLastKnownLocation() { LocationManager locationManager = (LocationManager) (getSherlockActivity().getSystemService(Context.LOCATION_SERVICE)); Location location = null; if((location = locationManager .getLastKnownLocation(LocationManager.GPS_PROVIDER)) != null) return location; else if((location = locationManager .getLastKnownLocation(LocationManager.NETWORK_PROVIDER)) != null) return location; else if ((location = locationManager .getLastKnownLocation(LocationManager.PASSIVE_PROVIDER)) != null) return location; return location; } @Override public void onBearingChanged(Float bearing) { this.bearing = bearing; moveMapPositionTo(this.myLocationMarker.getPosition()); } public void onTargetBearingChanged(Float tBearing) { this.tBearing = tBearing; showDirectionToTarget(tBearing); } public void onMatchEnd() { hideRangeCircles(); hideDirectionToTarget(); hideTargetLocation(); } public void hideRangeCircles() { if(aRangeCircle != null) aRangeCircle.remove(); if(hRangeCircle != null) hRangeCircle.remove(); } public void hideDirectionToTarget() { if(tBearingLine != null) { tBearingLine.remove(); } } public void hideTargetLocation() { if(targetLocationMarker != null) targetLocationMarker.setVisible(false); } public void onTargetLocationChanged(LatLng tLoc) { if(tLoc != null) { showTargetLocation(tLoc); } else { hideTargetLocation(); } } }