Merged Rodrigo's changes.

This commit is contained in:
Sandor Dornbush
2010-09-23 11:07:31 -07:00
parent 19a770445e
commit 21b0a517a9
2 changed files with 211 additions and 177 deletions
@@ -70,8 +70,9 @@ import android.widget.Toast;
* @author Leif Hendrik Wilden
*/
public class MyTracksMap extends MapActivity
implements LocationListener, SensorEventListener, View.OnTouchListener,
View.OnClickListener, SharedPreferences.OnSharedPreferenceChangeListener {
implements View.OnTouchListener, View.OnClickListener,
SharedPreferences.OnSharedPreferenceChangeListener {
private static final int TRACKPOINT_BUFFER_SIZE = 1024;
// Saved instance state keys:
// ---------------------------
@@ -82,9 +83,9 @@ public class MyTracksMap extends MapActivity
public static final String KEY_HAVE_GOOD_FIX = "haveGoodFix";
/**
* The currently selected track (or null if nothing selected).
* The ID of the currently selected track (or -1 if nothing selected).
*/
private Track selectedTrack;
private long selectedTrackId = -1;
/**
* The id of the currently recording track.
@@ -97,13 +98,19 @@ public class MyTracksMap extends MapActivity
*/
private boolean keepMyLocationVisible;
/**
* Id of the first location that was seen when reading tracks from the
* provider.
*/
private long firstSeenLocationId = -1;
/**
* Id of the last location that was seen when reading tracks from the
* provider. This is used to determine which locations are new compared to the
* last time the mapOverlay was updated.
*/
private long lastSeenLocationId = -1;
/**
* Magnetic variation.
*/
@@ -130,19 +137,13 @@ public class MyTracksMap extends MapActivity
* A thread with a looper. Post to updateTrackHandler to execute Runnables on
* this thread.
*/
private final HandlerThread updateTrackThread =
new HandlerThread("updateTrackThread");
private HandlerThread updateTrackThread;
/** Handler for updateTrackThread */
private Handler updateTrackHandler;
private MyTracksProviderUtils providerUtils;
/**
* This value is used to decide how many points to display.
*/
private int samplingFrequency = 1;
/**
* A runnable that updates the track from the provider (looking for points
* added after "lastSeenLocationId".
@@ -150,7 +151,7 @@ public class MyTracksMap extends MapActivity
private final Runnable updateTrackRunnable = new Runnable() {
@Override
public void run() {
if (selectedTrack == null) {
if (!isATrackSelected()) {
return;
}
readAllNewTrackPoints();
@@ -163,11 +164,12 @@ public class MyTracksMap extends MapActivity
private Runnable restoreTrackRunnable = new Runnable() {
@Override
public void run() {
if (selectedTrack == null) {
if (!isATrackSelected()) {
return;
}
mapOverlay.clearPoints();
lastSeenLocationId = selectedTrack.getStartId();
firstSeenLocationId = -1;
lastSeenLocationId = -1;
readAllNewTrackPoints();
}
};
@@ -178,7 +180,7 @@ public class MyTracksMap extends MapActivity
private final Runnable restoreWaypointsRunnable = new Runnable() {
@Override
public void run() {
if (selectedTrack == null) {
if (!isATrackSelected()) {
return;
}
@@ -188,7 +190,7 @@ public class MyTracksMap extends MapActivity
// We will silently drop extra waypoints to make the app responsive.
// TODO: Try to only load the waypoints in the view port.
cursor = providerUtils.getWaypointsCursor(
selectedTrack.getId(), 0,
selectedTrackId, 0,
MyTracksConstants.MAX_DISPLAYED_WAYPOINTS_POINTS);
if (cursor != null) {
if (cursor.moveToFirst()) {
@@ -219,10 +221,9 @@ public class MyTracksMap extends MapActivity
public void run() {
uiHandler.post(new Runnable() {
public void run() {
showTrack(selectedTrack);
mapOverlay.setSelectedTrack(selectedTrack);
mapOverlay.setShowEndMarker(selectedTrack == null ||
selectedTrack.getId() != recordingTrackId);
showTrack(selectedTrackId);
mapOverlay.setTrackDrawingEnabled(isATrackSelected());
mapOverlay.setShowEndMarker(!isRecordingSelected());
mapView.invalidate();
busyPane.setVisibility(View.GONE);
updateOptionsButton();
@@ -316,6 +317,7 @@ public class MyTracksMap extends MapActivity
locationManager =
(LocationManager) getSystemService(Context.LOCATION_SERVICE);
updateTrackThread = new HandlerThread("updateTrackThread");
updateTrackThread.start();
updateTrackHandler = new Handler(updateTrackThread.getLooper());
@@ -331,8 +333,7 @@ public class MyTracksMap extends MapActivity
// No track is being recorded. We should not be here.
return;
}
if (selectedTrack == null
|| selectedTrack.getId() != recordingTrackId) {
if (!isRecordingSelected()) {
// No track, or one other than the recording track is selected,
// don't bother.
return;
@@ -348,7 +349,7 @@ public class MyTracksMap extends MapActivity
public void onChange(boolean selfChange) {
Log.d(MyTracksConstants.TAG,
"MyTracksMap: ContentObserver.onChange waypoints");
if (selectedTrack == null) {
if (!isATrackSelected()) {
return;
}
updateTrackHandler.post(restoreWaypointsRunnable);
@@ -375,6 +376,21 @@ public class MyTracksMap extends MapActivity
}
}
/**
* Returns whether there's a track currently selected for display.
*/
private boolean isATrackSelected() {
return selectedTrackId >= 0;
}
/**
* Returns whether we're currently recording the same track that's selected
* for display.
*/
private boolean isRecordingSelected() {
return isATrackSelected() && selectedTrackId == recordingTrackId;
}
protected void setupZoomControls() {
mapView.setBuiltInZoomControls(true);
}
@@ -384,6 +400,9 @@ public class MyTracksMap extends MapActivity
// Final cleanup before activity is destroyed.
// May not be called at all in some situations.
Log.d(MyTracksConstants.TAG, "MyTracksMap.onDestroy");
updateTrackThread.quit();
super.onDestroy();
}
@@ -449,10 +468,9 @@ public class MyTracksMap extends MapActivity
// While this activity was paused the user may have deleted the selected
// track. In that case the map overlay needs to be cleared:
Track track = mapOverlay.getSelectedTrack();
if (track != null && !providerUtils.trackExists(track.getId())) {
if (isATrackSelected() && !providerUtils.trackExists(selectedTrackId)) {
// The recording track must have been deleted meanwhile.
mapOverlay.setSelectedTrack(null);
mapOverlay.setTrackDrawingEnabled(false);
mapView.invalidate();
}
}
@@ -514,10 +532,10 @@ public class MyTracksMap extends MapActivity
+ gpsProvider.getName());
}
locationManager.requestLocationUpdates(gpsProvider.getName(),
0 /*minTime*/, 0 /*minDist*/, this);
0 /*minTime*/, 0 /*minDist*/, locationListener);
try {
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
1000 * 60 * 5 /*minTime*/, 0 /*minDist*/, this);
1000 * 60 * 5 /*minTime*/, 0 /*minDist*/, locationListener);
} catch (RuntimeException e) {
// If anything at all goes wrong with getting a cell location do not
// abort. Cell location is not essential to this app.
@@ -535,7 +553,7 @@ public class MyTracksMap extends MapActivity
Log.d(MyTracksConstants.TAG,
"MyTracksMap: Now registering sensor listeners.");
sensorManager.registerListener(
this, compass, SensorManager.SENSOR_DELAY_UI);
sensorListener, compass, SensorManager.SENSOR_DELAY_UI);
}
/**
@@ -545,12 +563,12 @@ public class MyTracksMap extends MapActivity
if (locationManager != null) {
Log.d(MyTracksConstants.TAG,
"MyTracksMap: Now unregistering location listeners.");
locationManager.removeUpdates(this);
locationManager.removeUpdates(locationListener);
}
if (sensorManager != null) {
Log.d(MyTracksConstants.TAG,
"MyTracksMap: Now unregistering sensor listeners.");
sensorManager.unregisterListener(this);
sensorManager.unregisterListener(sensorListener);
}
}
@@ -579,7 +597,7 @@ public class MyTracksMap extends MapActivity
*/
private void updateOptionsButton() {
optionsBtn.setVisibility(
selectedTrack != null ? View.VISIBLE : View.INVISIBLE);
isATrackSelected() ? View.VISIBLE : View.INVISIBLE);
}
/**
@@ -632,12 +650,18 @@ public class MyTracksMap extends MapActivity
/**
* Zooms and pans the map so that the given track is visible.
*
* @param track a given track
* @param trackId a given track ID
*/
public void showTrack(Track track) {
if (track == null || mapView == null || track.getNumberOfPoints() < 2) {
public void showTrack(long trackId) {
if (mapView == null) {
return;
}
Track track = providerUtils.getTrack(trackId);
if (track == null || track.getNumberOfPoints() < 2) {
return;
}
TripStatistics stats = track.getStatistics();
int bottom = stats.getBottom();
int left = stats.getLeft();
@@ -680,24 +704,30 @@ public class MyTracksMap extends MapActivity
* @param trackId a given track id
*/
public void setSelectedTrack(final long trackId) {
if (selectedTrack != null && selectedTrack.getId() == trackId) {
if (selectedTrackId == trackId) {
// Selected track did not change, nothing to do.
mapOverlay.setSelectedTrack(selectedTrack);
mapOverlay.setTrackDrawingEnabled(isATrackSelected());
mapView.invalidate();
updateOptionsButton();
return;
}
if (trackId < 0) {
// Remove selection.
selectedTrack = null;
mapOverlay.setSelectedTrack(null);
selectedTrackId = -1;
mapOverlay.setTrackDrawingEnabled(false);
mapOverlay.clearWaypoints();
updateOptionsButton();
mapView.invalidate();
return;
}
busyPane.setVisibility(View.VISIBLE);
selectedTrack = providerUtils.getTrack(trackId);
selectedTrackId = trackId;
loadSelectedTrack();
}
private void loadSelectedTrack() {
updateTrackHandler.post(restoreTrackRunnable);
updateTrackHandler.post(restoreWaypointsRunnable);
updateTrackHandler.post(setSelectedTrackRunnable);
@@ -753,8 +783,7 @@ public class MyTracksMap extends MapActivity
menu.setHeaderTitle(R.string.tracklist_this_track);
menu.add(0, MyTracksConstants.MENU_EDIT, 0,
R.string.tracklist_edit_track);
if (!MyTracks.getInstance().isRecording() || (selectedTrack != null
&& selectedTrack.getId() != recordingTrackId)) {
if (!MyTracks.getInstance().isRecording() || !isRecordingSelected()) {
menu.add(0, MyTracksConstants.MENU_SEND_TO_GOOGLE, 0,
R.string.tracklist_send_to_google);
SubMenu share = menu.addSubMenu(0, MyTracksConstants.MENU_SHARE, 0,
@@ -791,7 +820,7 @@ public class MyTracksMap extends MapActivity
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
if (!super.onMenuItemSelected(featureId, item)) {
if (selectedTrack != null) {
if (isATrackSelected()) {
MyTracks.getInstance().onActivityResult(
MyTracksConstants.getActionFromMenuId(item.getItemId()), RESULT_OK,
new Intent());
@@ -852,8 +881,7 @@ public class MyTracksMap extends MapActivity
public void onClick(View v) {
if (v == messagePane) {
launchMyLocationSettings();
}
if (v == optionsBtn) {
} else if (v == optionsBtn) {
optionsBtn.performLongClick();
}
}
@@ -895,9 +923,8 @@ public class MyTracksMap extends MapActivity
sharedPreferences.getLong(
getString(R.string.recording_track_key),
-1);
if (selectedTrack != null) {
mapOverlay.setShowEndMarker(
selectedTrack.getId() != recordingTrackId);
if (isATrackSelected()) {
mapOverlay.setShowEndMarker(!isRecordingSelected());
mapView.postInvalidate();
}
}
@@ -906,136 +933,139 @@ public class MyTracksMap extends MapActivity
}
}
@Override
public void onProviderEnabled(String provider) {
if (provider.equals(MyTracksConstants.GPS_PROVIDER)) {
messageText.setText(R.string.wait_for_fix);
}
}
@Override
public void onProviderDisabled(String provider) {
if (provider.equals(MyTracksConstants.GPS_PROVIDER)) {
messageText.setText(R.string.status_enable_gps);
messagePane.setVisibility(View.VISIBLE);
messagePane.setOnClickListener(this);
screen.requestLayout();
}
}
@Override
public void onLocationChanged(Location location) {
if (location.getProvider().equals(MyTracksConstants.GPS_PROVIDER)) {
// Recalculate the variation if there was a jump in location > 1km:
if (currentLocation == null
|| location.distanceTo(currentLocation) > 1000) {
setVariation(location);
private final LocationListener locationListener = new LocationListener() {
@Override
public void onProviderEnabled(String provider) {
if (provider.equals(MyTracksConstants.GPS_PROVIDER)) {
messageText.setText(R.string.wait_for_fix);
}
currentLocation = location;
boolean haveGoodFixNow =
currentLocation.getAccuracy() < minRequiredAccuracy;
if (haveGoodFixNow != haveGoodFix) {
haveGoodFix = haveGoodFixNow;
messagePane.setVisibility(haveGoodFix ? View.GONE : View.VISIBLE);
}
@Override
public void onProviderDisabled(String provider) {
if (provider.equals(MyTracksConstants.GPS_PROVIDER)) {
messageText.setText(R.string.status_enable_gps);
messagePane.setVisibility(View.VISIBLE);
messagePane.setOnClickListener(MyTracksMap.this);
screen.requestLayout();
}
showCurrentLocation();
} else {
Log.d(MyTracksConstants.TAG,
"MyTracksMap: Network location update received.");
}
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
if (provider.equals(MyTracksConstants.GPS_PROVIDER)) {
switch (status) {
case LocationProvider.OUT_OF_SERVICE:
case LocationProvider.TEMPORARILY_UNAVAILABLE:
haveGoodFix = false;
messagePane.setVisibility(View.VISIBLE);
@Override
public void onLocationChanged(Location location) {
if (location.getProvider().equals(MyTracksConstants.GPS_PROVIDER)) {
// Recalculate the variation if there was a jump in location > 1km:
if (currentLocation == null
|| location.distanceTo(currentLocation) > 1000) {
setVariation(location);
}
currentLocation = location;
boolean haveGoodFixNow =
currentLocation.getAccuracy() < minRequiredAccuracy;
if (haveGoodFixNow != haveGoodFix) {
haveGoodFix = haveGoodFixNow;
messagePane.setVisibility(haveGoodFix ? View.GONE : View.VISIBLE);
screen.requestLayout();
break;
}
showCurrentLocation();
} else {
Log.d(MyTracksConstants.TAG,
"MyTracksMap: Network location update received.");
}
}
}
@Override
public void onSensorChanged(SensorEvent se) {
synchronized (this) {
float magneticHeading = se.values[0];
double heading = magneticHeading + variation;
if (mapOverlay.setHeading((float) heading)) {
mapView.invalidate();
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
if (provider.equals(MyTracksConstants.GPS_PROVIDER)) {
switch (status) {
case LocationProvider.OUT_OF_SERVICE:
case LocationProvider.TEMPORARILY_UNAVAILABLE:
haveGoodFix = false;
messagePane.setVisibility(View.VISIBLE);
screen.requestLayout();
break;
}
}
}
}
};
@Override
public void onAccuracyChanged(Sensor s, int accuracy) {
// do nothing
}
/**
* Set the sampling frequency from the total number of points in the track.
*
* @param track The track to read the total number of points from.
*/
private void setSamplingFrequency(Track track) {
long totalLocations = track.getStopId() - track.getStartId();
private final SensorEventListener sensorListener = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent se) {
synchronized (this) {
float magneticHeading = se.values[0];
double heading = magneticHeading + variation;
if (mapOverlay.setHeading((float) heading)) {
mapView.invalidate();
}
}
}
@Override
public void onAccuracyChanged(Sensor s, int accuracy) {
// do nothing
}
};
// Limit the number of map points readings.
samplingFrequency =
(int) (1 +
totalLocations / MyTracksConstants.TARGET_DISPLAYED_TRACK_POINTS);
Log.i(MyTracksConstants.TAG, "Sampling locations: " + samplingFrequency);
}
private void readAllNewTrackPoints() {
Cursor cursor = null;
// Refetch the track to get the latest StopId
selectedTrack = providerUtils.getTrack(selectedTrack.getId());
long totalLocations = selectedTrack.getStopId() -
selectedTrack.getStartId();
int numPoints = mapOverlay.getNumLocations();
if (numPoints >= MyTracksConstants.MAX_DISPLAYED_TRACK_POINTS) {
// We're about to exceed the maximum allowed number of points, so reload
// the whole track with fewer points (the sampling frequency will be
// lower).
loadSelectedTrack();
return;
}
long lastStoredLocationId =
providerUtils.getLastLocationId(selectedTrackId);
int samplingFrequency = -1;
for (;;) {
Cursor cursor = null;
try {
cursor = providerUtils.getLocationsCursor(selectedTrackId,
lastSeenLocationId + 1, TRACKPOINT_BUFFER_SIZE, false);
if (cursor == null || !cursor.moveToFirst()) {
// No (more) data
break;
}
setSamplingFrequency(selectedTrack);
int bufferSize = 1024;
int points = 0;
long tailLocationId = selectedTrack.getStopId() - 10;
Location location;
while (lastSeenLocationId < (selectedTrack.getStopId() + 10)) {
cursor = providerUtils.getLocationsCursor(
selectedTrack.getId(), lastSeenLocationId + 1, bufferSize, false);
if (cursor != null && cursor.moveToFirst()) {
final int idColumnIdx = cursor.getColumnIndexOrThrow(
TrackPointsColumns._ID);
do {
points++;
location = providerUtils.createLocation(cursor);
lastSeenLocationId = cursor.getLong(idColumnIdx);
// Include a point if it fits one of the following criteria:
// - Has the mod for the sampling frequency.
// - Is the first point.
// - Is on of the last n points.
// => This is to make sure we draw the last points people are probably zoomed in to.
if (!MyTracksUtils.isValidLocation(location) ||
points % samplingFrequency == 0 ||
points == 0 ||
lastSeenLocationId > tailLocationId) {
mapOverlay.addLocation(new Location(location));
long locationId = cursor.getLong(idColumnIdx);
lastSeenLocationId = locationId;
if (firstSeenLocationId == -1) {
// This was our first point, keep its ID
firstSeenLocationId = locationId;
}
if (samplingFrequency == -1) {
// Now we already have at least one point, calculate the sampling
// frequency
long numTotalPoints = lastStoredLocationId - firstSeenLocationId;
samplingFrequency = (int) (1 +
numTotalPoints / MyTracksConstants.TARGET_DISPLAYED_TRACK_POINTS);
}
Location location = providerUtils.createLocation(cursor);
// Include a point if it fits one of the following criteria:
// - Has the mod for the sampling frequency (includes first point).
// - Is the last point and we are not recording this track.
// - The point is a segment split
if (numPoints % samplingFrequency == 0 ||
(!isRecordingSelected() && locationId == lastStoredLocationId) ||
!MyTracksUtils.isValidLocation(location)) {
mapOverlay.addLocation(location);
}
numPoints++;
} while (cursor.moveToNext());
} else {
// TODO this is wrong sometimes. Should just return.
if (lastSeenLocationId >= selectedTrack.getStopId()) {
} finally {
if (cursor != null) {
cursor.close();
mapView.postInvalidate();
return;
}
lastSeenLocationId += bufferSize;
}
cursor.close();
cursor = null;
}
mapView.postInvalidate();
}
@@ -15,10 +15,8 @@
*/
package com.google.android.apps.mytracks;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.util.MyTracksUtils;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
@@ -58,7 +56,7 @@ public class MyTracksOverlay extends Overlay {
private final ArrayList<Waypoint> waypoints = new ArrayList<Waypoint>();
private final ArrayList<Location> points = new ArrayList<Location>(1024);
private Track selectedTrack;
private boolean trackDrawingEnabled;
private int lastHeading = 0;
private Location myLocation;
private boolean showEndMarker = true;
@@ -122,14 +120,6 @@ public class MyTracksOverlay extends Overlay {
errorCirclePaint.setAntiAlias(true);
}
public void setSelectedTrack(Track track) {
selectedTrack = track;
}
public Track getSelectedTrack() {
return selectedTrack;
}
/**
* Add a location to the map overlay.
*
@@ -145,6 +135,10 @@ public class MyTracksOverlay extends Overlay {
waypoints.add(wpt);
}
public int getNumLocations() {
return points.size();
}
public void clearWaypoints() {
waypoints.clear();
}
@@ -153,21 +147,33 @@ public class MyTracksOverlay extends Overlay {
points.clear();
}
public void setTrackDrawingEnabled(boolean trackDrawingEnabled) {
this.trackDrawingEnabled = trackDrawingEnabled;
}
public void setShowEndMarker(boolean showEndMarker) {
this.showEndMarker = showEndMarker;
}
@Override
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
if (shadow) {
return;
}
// Draw the selected track:
drawTrack(canvas, mapView, selectedTrack, true);
if (trackDrawingEnabled) {
// Draw the selected track:
drawTrack(canvas, mapView);
// Draw the waypoints:
// Draw the waypoints:
drawWaypoints(canvas, mapView);
}
// Draw the current location
drawMyLocation(canvas, mapView);
}
private void drawWaypoints(Canvas canvas, MapView mapView) {
ArrayList<Waypoint> currentWaypoints = waypoints;
for (int i = 1; i < currentWaypoints.size(); i++) {
Waypoint wpt = currentWaypoints.get(i);
@@ -190,11 +196,14 @@ public class MyTracksOverlay extends Overlay {
}
canvas.restore();
}
}
private void drawMyLocation(Canvas canvas, MapView mapView) {
// Draw the arrow icon:
if (myLocation == null) {
return;
}
GeoPoint geoPoint = new GeoPoint(
(int) (myLocation.getLatitude() * 1E6),
(int) (myLocation.getLongitude() * 1E6));
@@ -211,12 +220,7 @@ public class MyTracksOverlay extends Overlay {
canvas.drawCircle(pt.x, pt.y, radius, errorCirclePaint);
}
public void drawTrack(Canvas canvas, MapView mapView, Track track,
boolean selected) {
if (track == null) {
return;
}
private void drawTrack(Canvas canvas, MapView mapView) {
if (points.size() < 2) {
return;
}