/*******************************************************************************
* Gaggle is Copyright 2010 by Geeksville Industries LLC, a California limited liability corporation.
*
* Gaggle is distributed under a dual license. We've chosen this approach because within Gaggle we've used a number
* of components that Geeksville Industries LLC might reuse for commercial products. Gaggle can be distributed under
* either of the two licenses listed below.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Commercial Distribution License
* If you would like to distribute Gaggle (or portions thereof) under a license other than
* the "GNU General Public License, version 2", contact Geeksville Industries. Geeksville Industries reserves
* the right to release Gaggle source code under a commercial license of its choice.
*
* GNU Public License, version 2
* All other distribution of Gaggle must conform to the terms of the GNU Public License, version 2. The full
* text of this license is included in the Gaggle source, see assets/manual/gpl-2.0.txt.
******************************************************************************/
package com.geeksville.gaggle;
import java.io.IOException;
import java.io.InputStream;
import java.util.Observable;
import java.util.Observer;
import org.andnav.osm.views.OpenStreetMapViewController;
import org.andnav.osm.views.overlay.MyLocationOverlay;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import com.flurry.android.FlurryAgent;
import com.geeksville.android.AndroidUtil;
import com.geeksville.info.Units;
import com.geeksville.location.IGCReader;
import com.geeksville.location.LocationList;
import com.geeksville.maps.CenteredMyLocationOverlay;
import com.geeksville.maps.GeeksvilleMapActivity;
import com.geeksville.maps.TracklogOverlay;
import com.geeksville.maps.WaypointOverlay;
public class FlyMapActivity extends GeeksvilleMapActivity implements Observer {
/**
* Extra data we look for in our Intent. If specified it will be a Bundle
* generated by LocationList
*/
private static final String EXTRA_TRACKLOG = "tracklog";
/**
* Extra intend data, a boolean, true == show the user's position
*/
private static final String EXTRA_ISLIVE = "live";
/**
* Are we showing the current user position?
*/
private boolean isLive = false;
private WaypointOverlay over;
private AltitudeView altitudeView;
/**
* Generate an intent that can be used to play back a tracklog
*
* @param parent
* @param locs
* @return
*/
public static Intent createIntentLogView(Context parent, LocationList locs) {
Bundle locbundle = new Bundle();
locs.writeTo(locbundle);
Intent i = new Intent(parent, FlyMapActivity.class);
i.putExtra(FlyMapActivity.EXTRA_TRACKLOG, locbundle);
return i;
}
// FIXME - skanky, find a better way to pass in ptrs when we ain't
// crossing process boundaries
public static LocationList liveList;
/**
* Generate an intent suitable for live flight tracking
*
* @param parent
* @return
*/
public static Intent createIntentLive(Context parent) {
Intent i = new Intent(parent, FlyMapActivity.class);
i.putExtra(FlyMapActivity.EXTRA_ISLIVE, true);
return i;
}
/**
* Collect app metrics on Flurry
*
* @see android.app.Activity#onStart()
*/
@Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();
FlurryAgent.onStartSession(this, "XBPNNCR4T72PEBX17GKF");
}
/**
* Collect app metrics on Flurry
*
* @see android.app.Activity#onStop()
*/
@Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
FlurryAgent.onEndSession(this);
}
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
Intent intent = getIntent();
Bundle extras = intent.getExtras();
if (extras != null) {
// Do we show the current user position?
isLive = extras.getBoolean(FlyMapActivity.EXTRA_ISLIVE, isLive);
}
int layoutId = isLive ? R.layout.map_view_live : R.layout.map_view_delayed;
int mapviewId = isLive ? R.id.mapview_live : R.id.mapview_delayed;
super.onCreate(savedInstanceState, layoutId, mapviewId);
altitudeView = (AltitudeView) findViewById(R.id.altitude_view);
addWaypoints();
perhapsAddFromUri();
perhapsAddExtraTracklog();
if (isLive)
showCurrentPosition(true);
}
/**
* See if the user wants us to open an IGC file
*/
private void perhapsAddFromUri() {
Uri uri = getIntent().getData();
String action = getIntent().getAction();
if (uri != null && action != null && action.equals(Intent.ACTION_VIEW)) {
// See if we can read the file
try {
FlurryAgent.onEvent("IGC view start");
InputStream s = AndroidUtil.getFromURI(this, uri);
IGCReader iread = new IGCReader("gps", s);
LocationList loclist = iread.toLocationList();
iread.close();
FlurryAgent.onEvent("IGC view success");
// Show the points
altitudeView.setLocs(loclist);
mapView.getOverlays().add(createTracklogOverlay(loclist));
} catch (IOException ex) {
FlurryAgent.onEvent("IGC view fail");
// FIXME - move this alert goo into a standard localized utility
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.unable_to_open_igc_file);
builder.setMessage(ex.toString());
builder.setPositiveButton(R.string.okay, null);
AlertDialog alert = builder.create();
alert.show();
}
}
}
private void addWaypoints() {
over = new WaypointOverlay(this, mapView);
mapView.getOverlays().add(over);
}
/**
* If a tracklog was added to our intent, then show it
*/
private void perhapsAddExtraTracklog() {
FlurryAgent.onEvent("View delayed");
Intent intent = getIntent();
Bundle extras = intent.getExtras();
// Parse our params
if (extras != null) {
// Any stored tracklogs? if yes, then show them
Bundle locBundle = extras.getBundle(FlyMapActivity.EXTRA_TRACKLOG);
// Draw our track log on top of the (big) overlay icons?
if (locBundle != null) {
LocationList locs = new LocationList(locBundle);
altitudeView.setLocs(locs);
mapView.getOverlays().add(createTracklogOverlay(locs));
}
}
}
@Override
protected void onPause() {
super.onPause();
over.onPause();
// Remove any live tracklog (in case the next instance doesn't want it -
// YUCK)
if (liveTracklogOverlay != null) {
liveList.deleteObserver(this);
mapView.getOverlays().remove(liveTracklogOverlay);
liveTracklogOverlay = null;
}
}
/**
* FIXME, move all the live map stuff into a subclass?
*/
private TracklogOverlay liveTracklogOverlay;
@Override
protected void onResume() {
super.onResume();
Units.instance.setFromPrefs(this);
over.onResume();
// Show our latest live tracklog
if (liveList != null && isLive) {
LocationList locs = liveList; // Super skanky, find a better way
// to pass in ptrs FIXME.
liveList.addObserver(this);
altitudeView.setLocs(locs);
liveTracklogOverlay = new TracklogOverlay(this, locs);
mapView.getOverlays().add(liveTracklogOverlay);
}
}
/**
* Create an overlay for a canned tracklog
*
* @param locBundle
*/
private TracklogOverlay createTracklogOverlay(final LocationList locs) {
// Show a tracklog
TracklogOverlay tlog = new TracklogOverlay(this, locs);
// Center on the tracklog (we do this in a callback, because OSM only
// works after the view has been layed out
if (locs.numPoints() > 0) {
mapView.setPostLayout(new Runnable() {
@Override
public void run() {
OpenStreetMapViewController control = mapView.getController();
control.setCenter(locs.getGeoPoint(0));
// it is about 80 feet to one second of a degree. So if we
// want to
// limit the max default zoom to about 500 feet
double maxZoomFeet = 2500;
double feetPerSecond = 80;
double minDegrees = (maxZoomFeet / feetPerSecond) / 60 / 60;
int minDegreesE6 = (int) (minDegrees * 1e6);
// FIXME - the following is busted on OSM, it must be called
// later -
// after the view has been sized and fully created
control.zoomToSpan(Math.max(locs.latitudeSpanE6(), minDegreesE6), Math.max(locs
.longitudeSpanE6(), minDegreesE6));
// control.setZoom(11); // hack till fixed for OSM
}
});
}
return tlog;
}
/**
* Use our spiffy heading display
*
* @see com.geeksville.maps.GeeksvilleMapActivity#createLocationOverlay()
*/
@Override
protected MyLocationOverlay createLocationOverlay() {
return new CenteredMyLocationOverlay(this, mapView);
}
/**
* Called when our tracklog gets updated
*
* @param observable
* @param data
*/
@Override
public void update(Observable observable, Object data) {
mapView.postInvalidateDelayed(1000);
}
}