Merge heads

This commit is contained in:
youtaol
2012-07-27 10:24:20 +08:00
parent 38f317fb35
commit b6fd63a512
41 changed files with 2215 additions and 2578 deletions
@@ -81,9 +81,6 @@ public class ChartView extends View {
private static final int SPACER = 4; private static final int SPACER = 4;
private static final int Y_AXIS_OFFSET = 16; private static final int Y_AXIS_OFFSET = 16;
// Marker pin x position / marker icon width
private static final double MARKER_PIN_X_POSITION_PERCENTAGE = 13 / 48.0;
private final ChartValueSeries[] series = new ChartValueSeries[NUM_SERIES]; private final ChartValueSeries[] series = new ChartValueSeries[NUM_SERIES];
private final ArrayList<double[]> chartData = new ArrayList<double[]>(); private final ArrayList<double[]> chartData = new ArrayList<double[]>();
private final ArrayList<Waypoint> waypoints = new ArrayList<Waypoint>(); private final ArrayList<Waypoint> waypoints = new ArrayList<Waypoint>();
@@ -559,8 +556,8 @@ public class ChartView extends View {
float x = getX(getWaypointXValue(waypoint)); float x = getX(getWaypointXValue(waypoint));
canvas.drawLine( canvas.drawLine(
x, topBorder + spacer + markerHeight / 2, x, topBorder + effectiveHeight, markerPaint); x, topBorder + spacer + markerHeight / 2, x, topBorder + effectiveHeight, markerPaint);
canvas.translate( canvas.translate(x - (float) (markerWidth * Constants.WAYPOINT_X_OFFSET_PERCENTAGE),
x - (float) (markerWidth * MARKER_PIN_X_POSITION_PERCENTAGE), topBorder + spacer); topBorder + spacer);
if (waypoints.get(i).getType() == Waypoint.TYPE_STATISTICS) { if (waypoints.get(i).getType() == Waypoint.TYPE_STATISTICS) {
statisticsMarker.draw(canvas); statisticsMarker.draw(canvas);
} else { } else {
@@ -126,6 +126,10 @@ public abstract class Constants {
public static final String SETTINGS_NAME = "SettingsActivity"; public static final String SETTINGS_NAME = "SettingsActivity";
public static final double WAYPOINT_X_OFFSET_PERCENTAGE = 13 / 48.0;
public static final double MARKER_Y_OFFSET_PERCENTAGE = 91 / 96.0;
/** /**
* This is an abstract utility class. * This is an abstract utility class.
*/ */
@@ -13,6 +13,7 @@
* License for the specific language governing permissions and limitations under * License for the specific language governing permissions and limitations under
* the License. * the License.
*/ */
package com.google.android.apps.mytracks; package com.google.android.apps.mytracks;
import static com.google.android.apps.mytracks.Constants.TAG; import static com.google.android.apps.mytracks.Constants.TAG;
@@ -20,7 +21,7 @@ import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.maps.TrackPathPainter; import com.google.android.apps.mytracks.maps.TrackPathPainter;
import com.google.android.apps.mytracks.maps.TrackPathPainterFactory; import com.google.android.apps.mytracks.maps.TrackPathPainterFactory;
import com.google.android.apps.mytracks.maps.TrackPathUtilities; import com.google.android.apps.mytracks.maps.TrackPathUtils;
import com.google.android.apps.mytracks.util.IntentUtils; import com.google.android.apps.mytracks.util.IntentUtils;
import com.google.android.apps.mytracks.util.LocationUtils; import com.google.android.apps.mytracks.util.LocationUtils;
import com.google.android.apps.mytracks.util.PreferencesUtils; import com.google.android.apps.mytracks.util.PreferencesUtils;
@@ -30,6 +31,7 @@ import com.google.android.maps.MapView;
import com.google.android.maps.Overlay; import com.google.android.maps.Overlay;
import com.google.android.maps.Projection; import com.google.android.maps.Projection;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
import com.google.common.annotations.VisibleForTesting;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@@ -50,46 +52,46 @@ import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
/** /**
* A map overlay that displays a "MyLocation" arrow, an error circle, the * A map overlay that displays my location arrow, error circle, and track info.
* currently recording track and optionally a selected track.
* *
* @author Leif Hendrik Wilden * @author Leif Hendrik Wilden
*/ */
public class MapOverlay extends Overlay implements OnSharedPreferenceChangeListener { public class MapOverlay extends Overlay implements OnSharedPreferenceChangeListener {
private final Drawable[] arrows;
private final int arrowWidth, arrowHeight;
private final Drawable statsMarker;
private final Drawable waypointMarker;
private final Drawable startMarker;
private final Drawable endMarker;
private final int markerWidth, markerHeight;
private final Paint errorCirclePaint;
private final Context context; private final Context context;
private final List<Waypoint> waypoints; private final List<Waypoint> waypoints;
private final List<CachedLocation> points; private final List<CachedLocation> points;
private final BlockingQueue<CachedLocation> pendingPoints; private final BlockingQueue<CachedLocation> pendingPoints;
private final Drawable[] arrows;
private final int arrowWidth;
private final int arrowHeight;
private final Drawable statsMarker;
private final Drawable waypointMarker;
private final Drawable startMarker;
private final Drawable endMarker;
private final int markerWidth;
private final int markerHeight;
private final Paint errorCirclePaint;
private TrackPathPainter trackPathPainter;
private boolean trackDrawingEnabled; private boolean trackDrawingEnabled;
private int lastHeading = 0;
private Location myLocation;
private boolean showEndMarker = true; private boolean showEndMarker = true;
// TODO: Remove it completely after completing performance tests. private int headingIndex = 0;
private boolean alwaysVisible = true; private Location myLocation;
private GeoPoint lastReferencePoint; private GeoPoint lastReferencePoint;
private Rect lastViewRect; private Rect lastViewRect;
private boolean lastPathExists;
private TrackPathPainter trackPathPainter;
/** /**
* Represents a pre-processed {@code Location} to speed up drawing. * A pre-processed {@link Location} to speed up drawing.
* This class is more like a data object and doesn't provide accessors. *
* @author Jimmy Shih
*/ */
public static class CachedLocation { public static class CachedLocation {
public final boolean valid;
public final GeoPoint geoPoint; private final boolean valid;
public final int speed; private final GeoPoint geoPoint;
private final int speed;
/** /**
* Constructor for an invalid cached location. * Constructor for an invalid cached location.
@@ -108,41 +110,53 @@ public class MapOverlay extends Overlay implements OnSharedPreferenceChangeListe
this.geoPoint = valid ? LocationUtils.getGeoPoint(location) : null; this.geoPoint = valid ? LocationUtils.getGeoPoint(location) : null;
this.speed = (int) Math.floor(location.getSpeed() * UnitConversions.MS_TO_KMH); this.speed = (int) Math.floor(location.getSpeed() * UnitConversions.MS_TO_KMH);
} }
/**
* Returns true if the location is valid.
*/
public boolean isValid() {
return valid;
}
/**
* Gets the {@link GeoPoint}.
*/
public GeoPoint getGeoPoint() {
return geoPoint;
}
/**
* Gets the speed in kilometers per hour.
*/
public int getSpeed() {
return speed;
}
}; };
public MapOverlay(Context context) { public MapOverlay(Context context) {
this.context = context; this.context = context;
this.waypoints = new ArrayList<Waypoint>(); this.waypoints = new ArrayList<Waypoint>();
this.points = new ArrayList<CachedLocation>(1024); this.points = new ArrayList<CachedLocation>(1024);
this.pendingPoints = new ArrayBlockingQueue<CachedLocation>( this.pendingPoints = new ArrayBlockingQueue<CachedLocation>(
Constants.MAX_DISPLAYED_TRACK_POINTS, true); Constants.MAX_DISPLAYED_TRACK_POINTS, true);
// TODO: Can we use a FrameAnimation or similar here rather /*
// than individual resources for each arrow direction? * TODO: Use animation rather than individual resources for each arrow
* direction.
*/
final Resources resources = context.getResources(); final Resources resources = context.getResources();
arrows = new Drawable[] { arrows = new Drawable[] { resources.getDrawable(R.drawable.arrow_0),
resources.getDrawable(R.drawable.arrow_0), resources.getDrawable(R.drawable.arrow_20), resources.getDrawable(R.drawable.arrow_40),
resources.getDrawable(R.drawable.arrow_20), resources.getDrawable(R.drawable.arrow_60), resources.getDrawable(R.drawable.arrow_80),
resources.getDrawable(R.drawable.arrow_40), resources.getDrawable(R.drawable.arrow_100), resources.getDrawable(R.drawable.arrow_120),
resources.getDrawable(R.drawable.arrow_60), resources.getDrawable(R.drawable.arrow_140), resources.getDrawable(R.drawable.arrow_160),
resources.getDrawable(R.drawable.arrow_80), resources.getDrawable(R.drawable.arrow_180), resources.getDrawable(R.drawable.arrow_200),
resources.getDrawable(R.drawable.arrow_100), resources.getDrawable(R.drawable.arrow_220), resources.getDrawable(R.drawable.arrow_240),
resources.getDrawable(R.drawable.arrow_120), resources.getDrawable(R.drawable.arrow_260), resources.getDrawable(R.drawable.arrow_280),
resources.getDrawable(R.drawable.arrow_140), resources.getDrawable(R.drawable.arrow_300), resources.getDrawable(R.drawable.arrow_320),
resources.getDrawable(R.drawable.arrow_160), resources.getDrawable(R.drawable.arrow_340) };
resources.getDrawable(R.drawable.arrow_180), arrowWidth = arrows[headingIndex].getIntrinsicWidth();
resources.getDrawable(R.drawable.arrow_200), arrowHeight = arrows[headingIndex].getIntrinsicHeight();
resources.getDrawable(R.drawable.arrow_220),
resources.getDrawable(R.drawable.arrow_240),
resources.getDrawable(R.drawable.arrow_260),
resources.getDrawable(R.drawable.arrow_280),
resources.getDrawable(R.drawable.arrow_300),
resources.getDrawable(R.drawable.arrow_320),
resources.getDrawable(R.drawable.arrow_340)
};
arrowWidth = arrows[lastHeading].getIntrinsicWidth();
arrowHeight = arrows[lastHeading].getIntrinsicHeight();
for (Drawable arrow : arrows) { for (Drawable arrow : arrows) {
arrow.setBounds(0, 0, arrowWidth, arrowHeight); arrow.setBounds(0, 0, arrowWidth, arrowHeight);
} }
@@ -152,16 +166,16 @@ public class MapOverlay extends Overlay implements OnSharedPreferenceChangeListe
markerHeight = statsMarker.getIntrinsicHeight(); markerHeight = statsMarker.getIntrinsicHeight();
statsMarker.setBounds(0, 0, markerWidth, markerHeight); statsMarker.setBounds(0, 0, markerWidth, markerHeight);
waypointMarker = resources.getDrawable(R.drawable.blue_pushpin);
waypointMarker.setBounds(0, 0, markerWidth, markerHeight);
startMarker = resources.getDrawable(R.drawable.green_dot); startMarker = resources.getDrawable(R.drawable.green_dot);
startMarker.setBounds(0, 0, markerWidth, markerHeight); startMarker.setBounds(0, 0, markerWidth, markerHeight);
endMarker = resources.getDrawable(R.drawable.red_dot); endMarker = resources.getDrawable(R.drawable.red_dot);
endMarker.setBounds(0, 0, markerWidth, markerHeight); endMarker.setBounds(0, 0, markerWidth, markerHeight);
waypointMarker = resources.getDrawable(R.drawable.blue_pushpin); errorCirclePaint = TrackPathUtils.getPaint(context, R.color.blue);
waypointMarker.setBounds(0, 0, markerWidth, markerHeight);
errorCirclePaint = TrackPathUtilities.getPaint(R.color.blue, context);
errorCirclePaint.setAlpha(127); errorCirclePaint.setAlpha(127);
trackPathPainter = TrackPathPainterFactory.getTrackPathPainter(context); trackPathPainter = TrackPathPainterFactory.getTrackPathPainter(context);
@@ -172,16 +186,16 @@ public class MapOverlay extends Overlay implements OnSharedPreferenceChangeListe
/** /**
* Add a location to the map overlay. * Add a location to the map overlay.
* * <p>
* NOTE: This method doesn't take ownership of the given location, so it is * NOTE: This method doesn't take ownership of the given location, so it is
* safe to reuse the same location while calling this method. * safe to reuse the same location while calling this method.
* *
* @param l the location to add. * @param location the location
*/ */
public void addLocation(Location l) { public void addLocation(Location location) {
// Queue up in the pending queue until it's merged with {@code #points}. // Queue up in the pendingPoints until it's merged with points.
if (!pendingPoints.offer(new CachedLocation(l))) { if (!pendingPoints.offer(new CachedLocation(location))) {
Log.e(TAG, "Unable to add pending points"); Log.e(TAG, "Unable to add to pendingPoints.");
} }
} }
@@ -189,81 +203,125 @@ public class MapOverlay extends Overlay implements OnSharedPreferenceChangeListe
* Adds a segment split to the map overlay. * Adds a segment split to the map overlay.
*/ */
public void addSegmentSplit() { public void addSegmentSplit() {
// Queue up in the pendingPoints until it's merged with points.
if (!pendingPoints.offer(new CachedLocation())) { if (!pendingPoints.offer(new CachedLocation())) {
Log.e(TAG, "Unable to add pending points"); Log.e(TAG, "Unable to add to pendingPoints");
} }
} }
public void addWaypoint(Waypoint wpt) { /**
// Note: We don't cache waypoints, because it's not worth the effort. * Clears the locations.
if (wpt != null && wpt.getLocation() != null) { */
public void clearPoints() {
synchronized (points) {
points.clear();
pendingPoints.clear();
trackPathPainter.clearPath();
lastReferencePoint = null;
lastViewRect = null;
}
}
/**
* Adds a waypoint to the map overlay.
*
* @param waypoint the waypoint
*/
public void addWaypoint(Waypoint waypoint) {
// Note: We don't cache waypoints because it's not worth the effort.
if (waypoint != null && waypoint.getLocation() != null) {
synchronized (waypoints) { synchronized (waypoints) {
waypoints.add(wpt); waypoints.add(waypoint);
} }
} }
} }
public int getNumLocations() { /**
synchronized (points) { * Clears the waypoints.
return points.size() + pendingPoints.size(); */
}
}
// Visible for testing.
public int getNumWaypoints() {
synchronized (waypoints) {
return waypoints.size();
}
}
public void clearPoints() {
synchronized (getPoints()) {
getPoints().clear();
pendingPoints.clear();
lastPathExists = false;
lastViewRect = null;
trackPathPainter.clear();
}
}
public void clearWaypoints() { public void clearWaypoints() {
synchronized (waypoints) { synchronized (waypoints) {
waypoints.clear(); waypoints.clear();
} }
} }
/**
* Sets whether to draw the track or not.
*
* @param trackDrawingEnabled true to draw track
*/
public void setTrackDrawingEnabled(boolean trackDrawingEnabled) { public void setTrackDrawingEnabled(boolean trackDrawingEnabled) {
this.trackDrawingEnabled = trackDrawingEnabled; this.trackDrawingEnabled = trackDrawingEnabled;
} }
/**
* Sets whether to draw the end maker or not.
*
* @param showEndMarker true to draw end marker
*/
public void setShowEndMarker(boolean showEndMarker) { public void setShowEndMarker(boolean showEndMarker) {
this.showEndMarker = showEndMarker; this.showEndMarker = showEndMarker;
} }
/**
* Sets my location.
*
* @param myLocation my location
*/
public void setMyLocation(Location myLocation) {
this.myLocation = myLocation;
}
/**
* Sets the heading.
*
* @param heading the heading
* @return true if the visible heading has changed.
*/
public boolean setHeading(float heading) {
/*
* Use -heading because the arrow images are counter-clockwise rather than
* clockwise.
*/
int index = Math.round(-heading / 360 * 18);
while (index < 0) {
index += 18;
}
while (index > 17) {
index -= 18;
}
if (index != headingIndex) {
headingIndex = index;
return true;
} else {
return false;
}
}
@Override @Override
public void draw(Canvas canvas, MapView mapView, boolean shadow) { public void draw(Canvas canvas, MapView mapView, boolean shadow) {
if (shadow) { if (shadow) {
return; return;
} }
// It's safe to keep projection within a single draw operation. // It's safe to keep projection within a single draw operation
final Projection projection = getMapProjection(mapView); Projection projection = getMapProjection(mapView);
if (projection == null) { if (projection == null) {
Log.w(TAG, "No projection, unable to draw"); Log.w(TAG, "No projection, unable to draw.");
return; return;
} }
// Get the current viewing window.
if (trackDrawingEnabled) { if (trackDrawingEnabled) {
// Get the current viewing Rect
Rect viewRect = getMapViewRect(mapView); Rect viewRect = getMapViewRect(mapView);
// Draw the selected track: // Draw the selected track
drawTrack(canvas, projection, viewRect); drawTrack(canvas, projection, viewRect);
// Draw the "Start" and "End" markers: // Draw the start and end markers
drawMarkers(canvas, projection); drawMarkers(canvas, projection);
// Draw the waypoints: // Draw the waypoints
drawWaypoints(canvas, projection); drawWaypoints(canvas, projection);
} }
@@ -271,164 +329,19 @@ public class MapOverlay extends Overlay implements OnSharedPreferenceChangeListe
drawMyLocation(canvas, projection); drawMyLocation(canvas, projection);
} }
private void drawMarkers(Canvas canvas, Projection projection) {
// Draw the "End" marker.
if (showEndMarker) {
for (int i = getPoints().size() - 1; i >= 0; --i) {
if (getPoints().get(i).valid) {
drawElement(canvas, projection, getPoints().get(i).geoPoint, endMarker,
-markerWidth / 2, -markerHeight);
break;
}
}
}
// Draw the "Start" marker.
for (int i = 0; i < getPoints().size(); ++i) {
if (getPoints().get(i).valid) {
drawElement(canvas, projection, getPoints().get(i).geoPoint, startMarker,
-markerWidth / 2, -markerHeight);
break;
}
}
}
// Visible for testing.
Projection getMapProjection(MapView mapView) {
return mapView.getProjection();
}
// Visible for testing.
Rect getMapViewRect(MapView mapView) {
int w = mapView.getLongitudeSpan();
int h = mapView.getLatitudeSpan();
int cx = mapView.getMapCenter().getLongitudeE6();
int cy = mapView.getMapCenter().getLatitudeE6();
return new Rect(cx - w / 2, cy - h / 2, cx + w / 2, cy + h / 2);
}
// For use in testing only.
public TrackPathPainter getTrackPathPainter() {
return trackPathPainter;
}
// For use in testing only.
public void setTrackPathPainter(TrackPathPainter trackPathPainter) {
this.trackPathPainter = trackPathPainter;
}
private void drawWaypoints(Canvas canvas, Projection projection) {
synchronized (waypoints) {;
for (Waypoint wpt : waypoints) {
Location loc = wpt.getLocation();
drawElement(canvas, projection, LocationUtils.getGeoPoint(loc),
wpt.getType() == Waypoint.TYPE_STATISTICS ? statsMarker
: waypointMarker, -(markerWidth / 2) + 3, -markerHeight);
}
}
}
private void drawMyLocation(Canvas canvas, Projection projection) {
// Draw the arrow icon.
if (myLocation == null) {
return;
}
Point pt = drawElement(canvas, projection,
LocationUtils.getGeoPoint(myLocation), arrows[lastHeading],
-(arrowWidth / 2) + 3, -(arrowHeight / 2));
// Draw the error circle.
float radius = projection.metersToEquatorPixels(myLocation.getAccuracy());
canvas.drawCircle(pt.x, pt.y, radius, errorCirclePaint);
}
private void drawTrack(Canvas canvas, Projection projection, Rect viewRect)
{
boolean draw;
synchronized (points) {
// Merge the pending points with the list of cached locations.
final GeoPoint referencePoint = projection.fromPixels(0, 0);
int newPoints = pendingPoints.drainTo(points);
boolean newProjection = !viewRect.equals(lastViewRect) ||
!referencePoint.equals(lastReferencePoint);
if (newPoints == 0 && lastPathExists && !newProjection) {
// No need to recreate path (same points and viewing area).
draw = true;
} else {
int numPoints = points.size();
if (numPoints < 2) {
// Not enough points to draw a path.
draw = false;
} else if (!trackPathPainter.needsRedraw() && lastPathExists && !newProjection) {
// Incremental update of the path, without repositioning the view.
draw = true;
trackPathPainter.updatePath(projection, viewRect, numPoints - newPoints, alwaysVisible, points);
} else {
// The view has changed so we have to start from scratch.
draw = true;
trackPathPainter.updatePath(projection, viewRect, 0, alwaysVisible, points);
}
}
lastReferencePoint = referencePoint;
lastViewRect = viewRect;
}
if (draw) {
trackPathPainter.drawTrack(canvas);
}
}
// Visible for testing.
Point drawElement(Canvas canvas, Projection projection, GeoPoint geoPoint,
Drawable element, int offsetX, int offsetY) {
Point pt = new Point();
projection.toPixels(geoPoint, pt);
canvas.save();
canvas.translate(pt.x + offsetX, pt.y + offsetY);
element.draw(canvas);
canvas.restore();
return pt;
}
/**
* Sets the pointer location (will be drawn on next invalidate).
*/
public void setMyLocation(Location myLocation) {
this.myLocation = myLocation;
}
/**
* Sets the pointer heading in degrees (will be drawn on next invalidate).
*
* @return true if the visible heading changed (i.e. a redraw of pointer is
* potentially necessary)
*/
public boolean setHeading(float heading) {
int newhdg = Math.round(-heading / 360 * 18 + 180);
while (newhdg < 0)
newhdg += 18;
while (newhdg > 17)
newhdg -= 18;
if (newhdg != lastHeading) {
lastHeading = newhdg;
return true;
} else {
return false;
}
}
@Override @Override
public boolean onTap(GeoPoint p, MapView mapView) { public boolean onTap(GeoPoint geoPoint, MapView mapView) {
if (p.equals(mapView.getMapCenter())) { if (geoPoint.equals(mapView.getMapCenter())) {
// There is (unfortunately) no good way to determine whether the tap was /*
// caused by an actual tap on the screen or the track ball. If the * There is (unfortunately) no good way to determine whether the tap was
// location is equal to the map center,then it was a track ball press with * caused by an actual tap on the screen or the track ball. If the
// very high likelihood. * location is equal to the map center,then it was a track ball press with
* very high likelihood.
*/
return false; return false;
} }
final Location tapLocation = LocationUtils.getLocation(geoPoint);
final Location tapLocation = LocationUtils.getLocation(p); double minDistance = Double.MAX_VALUE;
double dmin = Double.MAX_VALUE;
Waypoint waypoint = null; Waypoint waypoint = null;
synchronized (waypoints) { synchronized (waypoints) {
for (int i = 0; i < waypoints.size(); i++) { for (int i = 0; i < waypoints.size(); i++) {
@@ -436,35 +349,231 @@ public class MapOverlay extends Overlay implements OnSharedPreferenceChangeListe
if (waypointLocation == null) { if (waypointLocation == null) {
continue; continue;
} }
final double d = waypointLocation.distanceTo(tapLocation); final double distance = waypointLocation.distanceTo(tapLocation);
if (d < dmin) { if (distance < minDistance) {
dmin = d; minDistance = distance;
waypoint = waypoints.get(i); waypoint = waypoints.get(i);
} }
} }
} }
if (waypoint != null && dmin < 15000000 / Math.pow(2, mapView.getZoomLevel())) { if (waypoint != null && minDistance < 15000000 / Math.pow(2, mapView.getZoomLevel())) {
Intent intent = IntentUtils.newIntent(context, MarkerDetailActivity.class) Intent intent = IntentUtils.newIntent(context, MarkerDetailActivity.class)
.putExtra(MarkerDetailActivity.EXTRA_MARKER_ID, waypoint.getId()); .putExtra(MarkerDetailActivity.EXTRA_MARKER_ID, waypoint.getId());
context.startActivity(intent); context.startActivity(intent);
return true; return true;
} }
return super.onTap(p, mapView); return super.onTap(geoPoint, mapView);
}
/**
* @return the points
*/
public List<CachedLocation> getPoints() {
return points;
} }
@Override @Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Log.d(TAG, "MapOverlay: onSharedPreferences changed " + key);
if (PreferencesUtils.getKey(context, R.string.track_color_mode_key).equals(key)) { if (PreferencesUtils.getKey(context, R.string.track_color_mode_key).equals(key)) {
trackPathPainter = TrackPathPainterFactory.getTrackPathPainter(context); trackPathPainter = TrackPathPainterFactory.getTrackPathPainter(context);
} }
} }
/**
* Gets the points.
*/
@VisibleForTesting
public List<CachedLocation> getPoints() {
return points;
}
/**
* Gets the track path painter.
*/
@VisibleForTesting
public TrackPathPainter getTrackPathPainter() {
return trackPathPainter;
}
/**
* Sets the track path painter.
*
* @param trackPathPainter the track path painter
*/
@VisibleForTesting
public void setTrackPathPainter(TrackPathPainter trackPathPainter) {
this.trackPathPainter = trackPathPainter;
}
/**
* Gets the map view projection.
*
* @param mapView the map view
*/
@VisibleForTesting
protected Projection getMapProjection(MapView mapView) {
return mapView.getProjection();
}
/**
* Gets the map view Rect.
*
* @param mapView the map view
*/
@VisibleForTesting
protected Rect getMapViewRect(MapView mapView) {
int width = mapView.getLongitudeSpan();
int height = mapView.getLatitudeSpan();
int centerX = mapView.getMapCenter().getLongitudeE6();
int centerY = mapView.getMapCenter().getLatitudeE6();
return new Rect(centerX - width / 2, centerY - height / 2, centerX + width / 2, centerY + height
/ 2);
}
/**
* Gets number of locations.
*/
@VisibleForTesting
int getNumLocations() {
synchronized (points) {
return points.size() + pendingPoints.size();
}
}
/**
* Gets number of waypoints.
*/
@VisibleForTesting
int getNumWaypoints() {
synchronized (waypoints) {
return waypoints.size();
}
}
/**
* Draws the track.
*
* @param canvas the canvas
* @param projection the projection
* @param viewRect the view rect
*/
private void drawTrack(Canvas canvas, Projection projection, Rect viewRect) {
boolean draw;
synchronized (points) {
// Merge the pending points with the list of cached locations.
GeoPoint referencePoint = projection.fromPixels(0, 0);
int newPoints = pendingPoints.drainTo(points);
boolean newProjection = !viewRect.equals(lastViewRect)
|| !referencePoint.equals(lastReferencePoint);
// Call updateState first to trigger its side effects.
boolean currentPathValid = !trackPathPainter.updateState() && !newProjection
&& trackPathPainter.hasPath();
if (newPoints == 0 && currentPathValid) {
// No need to update
draw = true;
} else {
int numPoints = points.size();
if (numPoints < 2) {
// Not enough points to draw a path
draw = false;
} else if (currentPathValid) {
// Incremental update of the path
draw = true;
trackPathPainter.updatePath(projection, viewRect, numPoints - newPoints, points);
} else {
// Reload the path
draw = true;
trackPathPainter.clearPath();
trackPathPainter.updatePath(projection, viewRect, 0, points);
}
}
lastReferencePoint = referencePoint;
lastViewRect = viewRect;
}
if (draw) {
trackPathPainter.drawPath(canvas);
}
}
/**
* Draws the start and end markers.
*
* @param canvas the canvas
* @param projection the projection
*/
private void drawMarkers(Canvas canvas, Projection projection) {
int offsetY = (int) (markerHeight * Constants.MARKER_Y_OFFSET_PERCENTAGE);
// Draw the end marker
if (showEndMarker) {
for (int i = points.size() - 1; i >= 0; i--) {
if (points.get(i).valid) {
drawElement(
canvas, projection, points.get(i).geoPoint, endMarker, -markerWidth / 2, -offsetY);
break;
}
}
}
// Draw the start marker
for (int i = 0; i < points.size(); i++) {
if (points.get(i).valid) {
drawElement(
canvas, projection, points.get(i).geoPoint, startMarker, -markerWidth / 2, -offsetY);
break;
}
}
}
/**
* Draws the waypoints.
*
* @param canvas the canvas
* @param projection the projection
*/
private void drawWaypoints(Canvas canvas, Projection projection) {
synchronized (waypoints) {
int offsetX = (int) (markerWidth * Constants.WAYPOINT_X_OFFSET_PERCENTAGE);
for (Waypoint waypoint : waypoints) {
Location location = waypoint.getLocation();
Drawable drawable = waypoint.getType() == Waypoint.TYPE_STATISTICS ? statsMarker
: waypointMarker;
drawElement(canvas, projection, LocationUtils.getGeoPoint(location), drawable, -offsetX,
-markerHeight);
}
}
}
/**
* Draws my location.
*
* @param canvas the canvas
* @param projection the projection
*/
private void drawMyLocation(Canvas canvas, Projection projection) {
if (myLocation == null) {
return;
}
Point point = drawElement(canvas, projection, LocationUtils.getGeoPoint(myLocation),
arrows[headingIndex], -(arrowWidth / 2), -(arrowHeight / 2));
// Draw the error circle
float radius = projection.metersToEquatorPixels(myLocation.getAccuracy());
canvas.drawCircle(point.x, point.y, radius, errorCirclePaint);
}
/**
* Draws an element.
*
* @param canvas the canvas
* @param projection the projection
* @param geoPoint the geo point
* @param drawable the drawable
* @param offsetX the x offset
* @param offsetY the y offset
* @return the point of the drawing.
*/
private Point drawElement(Canvas canvas, Projection projection, GeoPoint geoPoint,
Drawable drawable, int offsetX, int offsetY) {
Point point = new Point();
projection.toPixels(geoPoint, point);
canvas.save();
canvas.translate(point.x + offsetX, point.y + offsetY);
drawable.draw(canvas);
canvas.restore();
return point;
}
} }
@@ -15,7 +15,6 @@
*/ */
package com.google.android.apps.mytracks; package com.google.android.apps.mytracks;
import com.google.android.apps.mytracks.content.TrackDataHub;
import com.google.android.apps.mytracks.services.RemoveTempFilesService; import com.google.android.apps.mytracks.services.RemoveTempFilesService;
import com.google.android.apps.mytracks.util.AnalyticsUtils; import com.google.android.apps.mytracks.util.AnalyticsUtils;
import com.google.android.apps.mytracks.util.ApiAdapterFactory; import com.google.android.apps.mytracks.util.ApiAdapterFactory;
@@ -31,8 +30,6 @@ import android.content.Intent;
*/ */
public class MyTracksApplication extends Application { public class MyTracksApplication extends Application {
private TrackDataHub trackDataHub;
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
@@ -43,16 +40,4 @@ public class MyTracksApplication extends Application {
Intent intent = new Intent(this, RemoveTempFilesService.class); Intent intent = new Intent(this, RemoveTempFilesService.class);
startService(intent); startService(intent);
} }
/**
* Gets the application's TrackDataHub.
*
* Note: use synchronized to make sure only one instance is created per application.
*/
public synchronized TrackDataHub getTrackDataHub() {
if (trackDataHub == null) {
trackDataHub = TrackDataHub.newInstance(getApplicationContext());
}
return trackDataHub;
}
} }
@@ -137,8 +137,7 @@ public class TrackDetailActivity extends AbstractMyTracksActivity implements Del
getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE) getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE)
.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener); .registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, null); trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, null);
trackDataHub = ((MyTracksApplication) getApplication()).getTrackDataHub(); trackDataHub = TrackDataHub.newInstance(this);
trackDataHub.loadTrack(trackId);
mapViewContainer = getLayoutInflater().inflate(R.layout.map, null); mapViewContainer = getLayoutInflater().inflate(R.layout.map, null);
ApiAdapterFactory.getApiAdapter().disableHardwareAccelerated(mapViewContainer); ApiAdapterFactory.getApiAdapter().disableHardwareAccelerated(mapViewContainer);
@@ -167,7 +166,6 @@ public class TrackDetailActivity extends AbstractMyTracksActivity implements Del
public void onNewIntent(Intent intent) { public void onNewIntent(Intent intent) {
setIntent(intent); setIntent(intent);
handleIntent(intent); handleIntent(intent);
trackDataHub.loadTrack(trackId);
showMarker(); showMarker();
} }
@@ -180,6 +178,7 @@ public class TrackDetailActivity extends AbstractMyTracksActivity implements Del
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
trackDataHub.loadTrack(trackId);
TrackRecordingServiceConnectionUtils.resume(this, trackRecordingServiceConnection); TrackRecordingServiceConnectionUtils.resume(this, trackRecordingServiceConnection);
setTitle(trackId == PreferencesUtils.getLong(this, R.string.recording_track_id_key)); setTitle(trackId == PreferencesUtils.getLong(this, R.string.recording_track_id_key));
} }
@@ -350,12 +349,19 @@ public class TrackDetailActivity extends AbstractMyTracksActivity implements Del
} }
/** /**
* @return the mapViewContainer * Gets the map view container.
*/ */
public View getMapViewContainer() { public View getMapViewContainer() {
return mapViewContainer; return mapViewContainer;
} }
/**
* Gets the {@link TrackDataHub}.
*/
public TrackDataHub getTrackDataHub() {
return trackDataHub;
}
/** /**
* Handles the data in the intent. * Handles the data in the intent.
*/ */
@@ -149,25 +149,25 @@ public class DataSource {
} }
/** /**
* Registers a compass listener. * Registers a heading listener.
* *
* @param listener the listener * @param listener the listener
*/ */
public void registerCompassListener(SensorEventListener listener) { public void registerHeadingListener(SensorEventListener listener) {
Sensor compass = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION); Sensor heading = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
if (compass == null) { if (heading == null) {
Log.d(TAG, "No compass sensor."); Log.d(TAG, "No heading sensor.");
return; return;
} }
sensorManager.registerListener(listener, compass, SensorManager.SENSOR_DELAY_UI); sensorManager.registerListener(listener, heading, SensorManager.SENSOR_DELAY_UI);
} }
/** /**
* Unregisters a compass listener. * Unregisters a heading listener.
* *
* @param listener the listener * @param listener the listener
*/ */
public void unregisterCompassListener(SensorEventListener listener) { public void unregisterHeadingListener(SensorEventListener listener) {
sensorManager.unregisterListener(listener); sensorManager.unregisterListener(listener);
} }
@@ -133,11 +133,11 @@ public class DataSourceManager {
} }
/** /**
* Listener for compass changes. * Listener for heading changes.
* *
* @author Jimmy Shih * @author Jimmy Shih
*/ */
private class CompassListener implements SensorEventListener { private class HeadingListener implements SensorEventListener {
@Override @Override
public void onAccuracyChanged(Sensor sensor, int accuracy) { public void onAccuracyChanged(Sensor sensor, int accuracy) {
@@ -174,7 +174,7 @@ public class DataSourceManager {
private final WaypointsTableObserver waypointsTableObserver; private final WaypointsTableObserver waypointsTableObserver;
private final TrackPointsTableObserver trackPointsTableObserver; private final TrackPointsTableObserver trackPointsTableObserver;
private final CurrentLocationListener currentLocationListener; private final CurrentLocationListener currentLocationListener;
private final CompassListener compassListener; private final HeadingListener headingListener;
private final PreferenceListener preferenceListener; private final PreferenceListener preferenceListener;
public DataSourceManager(DataSource dataSource, DataSourceListener dataSourceListener) { public DataSourceManager(DataSource dataSource, DataSourceListener dataSourceListener) {
@@ -186,7 +186,7 @@ public class DataSourceManager {
waypointsTableObserver = new WaypointsTableObserver(); waypointsTableObserver = new WaypointsTableObserver();
trackPointsTableObserver = new TrackPointsTableObserver(); trackPointsTableObserver = new TrackPointsTableObserver();
currentLocationListener = new CurrentLocationListener(); currentLocationListener = new CurrentLocationListener();
compassListener = new CompassListener(); headingListener = new HeadingListener();
preferenceListener = new PreferenceListener(); preferenceListener = new PreferenceListener();
} }
@@ -202,9 +202,9 @@ public class DataSourceManager {
* Map SAMPLED_OUT_POINT_UPDATES to POINT_UPDATES since they correspond to * Map SAMPLED_OUT_POINT_UPDATES to POINT_UPDATES since they correspond to
* the same internal listener * the same internal listener
*/ */
if (neededListeners.contains(TrackDataType.SAMPLED_OUT_TRACK_POINTS)) { if (neededListeners.contains(TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE)) {
neededListeners.remove(TrackDataType.SAMPLED_OUT_TRACK_POINTS); neededListeners.remove(TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE);
neededListeners.add(TrackDataType.TRACK_POINTS_TABLE); neededListeners.add(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE);
} }
Log.d(TAG, "Updating listeners " + neededListeners); Log.d(TAG, "Updating listeners " + neededListeners);
@@ -248,18 +248,18 @@ public class DataSourceManager {
case WAYPOINTS_TABLE: case WAYPOINTS_TABLE:
dataSource.registerContentObserver(WaypointsColumns.CONTENT_URI, waypointsTableObserver); dataSource.registerContentObserver(WaypointsColumns.CONTENT_URI, waypointsTableObserver);
break; break;
case TRACK_POINTS_TABLE: case SAMPLED_IN_TRACK_POINTS_TABLE:
dataSource.registerContentObserver( dataSource.registerContentObserver(
TrackPointsColumns.CONTENT_URI, trackPointsTableObserver); TrackPointsColumns.CONTENT_URI, trackPointsTableObserver);
break; break;
case SAMPLED_OUT_TRACK_POINTS: case SAMPLED_OUT_TRACK_POINTS_TABLE:
// Do nothing. SAMPLED_OUT_POINT_UPDATES is mapped to POINT_UPDATES. // Do nothing. SAMPLED_OUT_POINT_UPDATES is mapped to POINT_UPDATES.
break; break;
case LOCATION: case LOCATION:
dataSource.registerLocationListener(currentLocationListener); dataSource.registerLocationListener(currentLocationListener);
break; break;
case COMPASS: case HEADING:
dataSource.registerCompassListener(compassListener); dataSource.registerHeadingListener(headingListener);
break; break;
case PREFERENCE: case PREFERENCE:
dataSource.registerOnSharedPreferenceChangeListener(preferenceListener); dataSource.registerOnSharedPreferenceChangeListener(preferenceListener);
@@ -285,17 +285,17 @@ public class DataSourceManager {
case WAYPOINTS_TABLE: case WAYPOINTS_TABLE:
dataSource.unregisterContentObserver(waypointsTableObserver); dataSource.unregisterContentObserver(waypointsTableObserver);
break; break;
case TRACK_POINTS_TABLE: case SAMPLED_IN_TRACK_POINTS_TABLE:
dataSource.unregisterContentObserver(trackPointsTableObserver); dataSource.unregisterContentObserver(trackPointsTableObserver);
break; break;
case SAMPLED_OUT_TRACK_POINTS: case SAMPLED_OUT_TRACK_POINTS_TABLE:
// Do nothing. SAMPLED_OUT_POINT_UPDATES is mapped to POINT_UPDATES. // Do nothing. SAMPLED_OUT_POINT_UPDATES is mapped to POINT_UPDATES.
break; break;
case LOCATION: case LOCATION:
dataSource.unregisterLocationListener(currentLocationListener); dataSource.unregisterLocationListener(currentLocationListener);
break; break;
case COMPASS: case HEADING:
dataSource.unregisterCompassListener(compassListener); dataSource.unregisterHeadingListener(headingListener);
break; break;
case PREFERENCE: case PREFERENCE:
dataSource.unregisterOnSharedPreferenceChangeListener(preferenceListener); dataSource.unregisterOnSharedPreferenceChangeListener(preferenceListener);
@@ -1,105 +0,0 @@
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.content;
import java.util.EnumSet;
/**
* State for a registered {@link TrackDataListener}.
*
* @author Jimmy Shih
*/
public class ListenerState {
private TrackDataListener trackDataListener;
private EnumSet<TrackDataType> trackDataTypes;
private long lastTrackId;
private long lastPointId;
private int lastSamplingFrequency;
private int numberOfLoadedPoints;
public ListenerState(
TrackDataListener trackDataListener, EnumSet<TrackDataType> trackDataTypes) {
this.trackDataListener = trackDataListener;
this.trackDataTypes = trackDataTypes;
}
/**
* Gets the {@link TrackDataListener}.
*/
public TrackDataListener getTrackDataListener() {
return trackDataListener;
}
/**
* Gets the track data types.
*/
public EnumSet<TrackDataType> getTrackDataTypes() {
return trackDataTypes;
}
/**
* Gets the last track id.
*/
public long getLastTrackId() {
return lastTrackId;
}
/**
* Gets the last point id.
*/
public long getLastPointId() {
return lastPointId;
}
/**
* Gets the last sampling frequency.
*/
public int getLastSamplingFrequency() {
return lastSamplingFrequency;
}
/**
* Get the number of loaded points.
*/
public int getNumberOfLoadedPoints() {
return numberOfLoadedPoints;
}
/**
* Sets the listener state.
*
* @param trackId track id
* @param pointId point id
* @param samplingFrequency sampling frequency
* @param loadedPoints number of loaded points
*/
public void setState(long trackId, long pointId, int samplingFrequency, int loadedPoints) {
lastTrackId = trackId;
lastPointId = pointId;
lastSamplingFrequency = samplingFrequency;
numberOfLoadedPoints = loadedPoints;
}
/**
* Resets state.
*/
public void resetState() {
setState(0L, 0L, 0, 0);
}
}
File diff suppressed because it is too large Load Diff
@@ -13,138 +13,131 @@
* License for the specific language governing permissions and limitations under * License for the specific language governing permissions and limitations under
* the License. * the License.
*/ */
package com.google.android.apps.mytracks.content;
import com.google.android.apps.mytracks.content.Track; package com.google.android.apps.mytracks.content;
import com.google.android.apps.mytracks.content.Waypoint;
import android.location.Location; import android.location.Location;
/** /**
* Listener for track data, for both initial and incremental loading. * Listener for track data changes.
* *
* @author Rodrigo Damazio * @author Rodrigo Damazio
*/ */
public interface TrackDataListener { public interface TrackDataListener {
/** States for the GPS location provider. */ /**
public enum ProviderState { * Location state.
DISABLED, *
NO_FIX, * @author Jimmy Shih
BAD_FIX, */
GOOD_FIX; public enum LocationState {
DISABLED, NO_FIX, BAD_FIX, GOOD_FIX;
} }
/** /**
* Called when the location provider changes state. * Called when the location state changes.
*/ */
void onProviderStateChange(ProviderState state); public void onLocationStateChanged(LocationState locationState);
/** /**
* Called when the current location changes. * Called when the location changes. This is meant for location display only,
* This is meant for immediate location display only - track point data is * track point data is reported with other methods like
* delivered by other methods below, such as {@link #onNewTrackPoint}. * {@link #onSampledInTrackPoint(Location)} and
* {@link #onSampledOutTrackPoint(Location)}.
* *
* @param loc the last known location * @param location the location
*/ */
void onCurrentLocationChanged(Location loc); public void onLocationChanged(Location location);
/** /**
* Called when the current heading changes. * Called when the heading changes.
* *
* @param heading the current heading, already accounting magnetic declination * @param heading the heading
*/ */
void onCurrentHeadingChanged(double heading); public void onHeadingChanged(double heading);
/** /**
* Called when the currently-selected track changes. * Called when the selected track changes. This will be followed by calls to
* This will be followed by calls to data methods such as * data methods such as {@link #onTrackUpdated(Track)},
* {@link #onTrackUpdated}, {@link #clearTrackPoints}, * {@link #clearTrackPoints()}, {@link #onSampledInTrackPoint(Location)},
* {@link #onNewTrackPoint(Location)}, etc., even if no track is currently * etc., even if no track is currently selected (in which case you'll only get
* selected (in which case you'll only get calls to clear the current data). * calls to clear the current data).
* *
* @param track the selected track, or null if no track is selected * @param track the selected track or null if no track is selected
* @param isRecording whether we're currently recording the selected track * @param isRecording true if the selected track is recording
*/ */
void onSelectedTrackChanged(Track track, boolean isRecording); public void onSelectedTrackChanged(Track track, boolean isRecording);
/** /**
* Called when the track and/or its statistics have been updated. * Called when the track or its statistics has been updated.
* *
* @param track the updated version of the track * @param track the track
*/ */
void onTrackUpdated(Track track); public void onTrackUpdated(Track track);
/** /**
* Called to clear any previously-sent track points. * Called to clear previously-sent track points.
* This can be called at any time that we decide the data needs to be
* reloaded, such as when it needs to be resampled.
*/ */
void clearTrackPoints(); public void clearTrackPoints();
/** /**
* Called when a new interesting track point is read. * Called when a sampled in track point is read.
* In this case, interesting means that the point has already undergone
* sampling and invalid point filtering.
* *
* @param loc the new track point * @param location the location
*/ */
void onNewTrackPoint(Location loc); public void onSampledInTrackPoint(Location location);
/** /**
* Called when a uninteresting track point is read. * Called when a sampled out track point is read.
* Uninteresting points are all points that get sampled out of the track.
* *
* @param loc the new track point * @param location the location
*/ */
void onSampledOutTrackPoint(Location loc); public void onSampledOutTrackPoint(Location location);
/** /**
* Called when an invalid point (representing a segment split) is read. * Called when an invalid track point representing a segment split is read.
*/ */
void onSegmentSplit(); public void onSegmentSplit();
/** /**
* Called when we're done (for the time being) sending new points. * Called when finish sending new track points. This gets called after every
* This gets called after every batch of calls to {@link #onNewTrackPoint}, * batch of calls to {@link #onSampledInTrackPoint(Location)},
* {@link #onSampledOutTrackPoint} and {@link #onSegmentSplit}. * {@link #onSampledOutTrackPoint(Location)} and {@link #onSegmentSplit()}.
*/ */
void onNewTrackPointsDone(); public void onNewTrackPointsDone();
/** /**
* Called to clear any previously-sent waypoints. * Called to clear previously sent waypoints.
* This can be called at any time that we decide the data needs to be
* reloaded.
*/ */
void clearWaypoints(); public void clearWaypoints();
/** /**
* Called when a new waypoint is read. * Called when a new waypoint is read.
* *
* @param wpt the new waypoint * @param waypoint the waypoint
*/ */
void onNewWaypoint(Waypoint wpt); public void onNewWaypoint(Waypoint waypoint);
/** /**
* Called when we're done (for the time being) sending new waypoints. * Called when finish sending new waypoints. This gets called after every
* This gets called after every batch of calls to {@link #clearWaypoints} and * batch of calls to {@link #clearWaypoints()} and
* {@link #onNewWaypoint}. * {@link #onNewWaypoint(Waypoint)}.
*/ */
void onNewWaypointsDone(); public void onNewWaypointsDone();
/** /**
* Called when the display units are changed by the user. * Called when the metric units preference value is change.
* *
* @param metric true if the units are metric, false if imperial * @param metricUnits true to use metric units, false to use imperial units
* @return true to reload all the data, false otherwise * @return true to reload all the data, false otherwise.
*/ */
boolean onUnitsChanged(boolean metric); public boolean onMetricUnitsChanged(boolean metricUnits);
/** /**
* Called when the speed/pace display unit is changed by the user. * Called when the report speed preference value is changed.
* *
* @param reportSpeed true to report speed, false for pace * @param reportSpeed true to report speed, false to report pace
* @return true to reload all the data, false otherwise * @return true to reload all the data, false otherwise.
*/ */
boolean onReportSpeedChanged(boolean reportSpeed); public boolean onReportSpeedChanged(boolean reportSpeed);
} }
@@ -35,9 +35,9 @@ import java.util.Set;
*/ */
public class TrackDataManager { public class TrackDataManager {
// Map of listener to its state // Map of listener to its track data types
private final Map<TrackDataListener, ListenerState> private final Map<TrackDataListener, EnumSet<TrackDataType>>
listenerToStateMap = new HashMap<TrackDataListener, ListenerState>(); listenerToTypesMap = new HashMap<TrackDataListener, EnumSet<TrackDataType>>();
// Map of track data type to listeners // Map of track data type to listeners
private final Map<TrackDataType, Set<TrackDataListener>> private final Map<TrackDataType, Set<TrackDataListener>>
@@ -55,20 +55,15 @@ public class TrackDataManager {
* @param listener the listener * @param listener the listener
* @param trackDataTypes the track data types the listener is interested * @param trackDataTypes the track data types the listener is interested
*/ */
public ListenerState registerListener( public void registerListener(
TrackDataListener listener, EnumSet<TrackDataType> trackDataTypes) { TrackDataListener listener, EnumSet<TrackDataType> trackDataTypes) {
if (listenerToStateMap.containsKey(listener)) { if (listenerToTypesMap.containsKey(listener)) {
throw new IllegalStateException("Listener is already registered"); throw new IllegalStateException("Listener is already registered");
} }
listenerToTypesMap.put(listener, trackDataTypes);
ListenerState listenerState = new ListenerState(listener, trackDataTypes);
listenerToStateMap.put(listener, listenerState);
for (TrackDataType trackDataType : trackDataTypes) { for (TrackDataType trackDataType : trackDataTypes) {
typeToListenersMap.get(trackDataType).add(listener); typeToListenersMap.get(trackDataType).add(listener);
} }
return listenerState;
} }
/** /**
@@ -77,14 +72,14 @@ public class TrackDataManager {
* @param listener the listener * @param listener the listener
*/ */
public void unregisterListener(TrackDataListener listener) { public void unregisterListener(TrackDataListener listener) {
ListenerState removed = listenerToStateMap.remove(listener); EnumSet<TrackDataType> removedTypes = listenerToTypesMap.remove(listener);
if (removed == null) { if (removedTypes == null) {
Log.w(TAG, "Tried to unregister a listener that is not registered."); Log.w(TAG, "Tried to unregister a listener that is not registered.");
return; return;
} }
// Remove the listener from the typeToListenersMap // Remove the listener from the typeToListenersMap
for (TrackDataType trackDataType : removed.getTrackDataTypes()) { for (TrackDataType trackDataType : removedTypes) {
typeToListenersMap.get(trackDataType).remove(listener); typeToListenersMap.get(trackDataType).remove(listener);
} }
} }
@@ -93,16 +88,16 @@ public class TrackDataManager {
* Gets the number of {@link TrackDataListener}. * Gets the number of {@link TrackDataListener}.
*/ */
public int getNumberOfListeners() { public int getNumberOfListeners() {
return listenerToStateMap.size(); return listenerToTypesMap.size();
} }
/** /**
* Gets the track listener state. * Gets the track data types for a listener.
* *
* @param listener the listener * @param listener the listener
*/ */
public ListenerState getListenerState(TrackDataListener listener) { public EnumSet<TrackDataType> getTrackDataTypes(TrackDataListener listener) {
return listenerToStateMap.get(listener); return listenerToTypesMap.get(listener);
} }
/** /**
@@ -119,9 +114,11 @@ public class TrackDataManager {
*/ */
public EnumSet<TrackDataType> getRegisteredTrackDataTypes() { public EnumSet<TrackDataType> getRegisteredTrackDataTypes() {
EnumSet<TrackDataType> types = EnumSet.noneOf(TrackDataType.class); EnumSet<TrackDataType> types = EnumSet.noneOf(TrackDataType.class);
for (ListenerState registration : this.listenerToStateMap.values()) { for (EnumSet<TrackDataType> value : listenerToTypesMap.values()) {
types.addAll(registration.getTrackDataTypes()); types.addAll(value);
} }
// Always include preference
types.add(TrackDataType.PREFERENCE);
return types; return types;
} }
} }
@@ -22,13 +22,12 @@ package com.google.android.apps.mytracks.content;
* @author Jimmy Shih * @author Jimmy Shih
*/ */
public enum TrackDataType { public enum TrackDataType {
SELECTED_TRACK, // selected track changes SELECTED_TRACK, // selected track changes
TRACKS_TABLE, // tracks table changes TRACKS_TABLE, // tracks table changes
WAYPOINTS_TABLE, // waypoints table changes WAYPOINTS_TABLE, // waypoints table changes
TRACK_POINTS_TABLE, // track points table changes SAMPLED_IN_TRACK_POINTS_TABLE, // sampled-in track points table changes
SAMPLED_OUT_TRACK_POINTS, // sampled-out track points changes SAMPLED_OUT_TRACK_POINTS_TABLE, // sampled-out track points table changes
LOCATION, // location changes LOCATION, // location changes
COMPASS, // compass changes HEADING, // heading changes
PREFERENCE; // preference changes PREFERENCE // preference changes
} }
@@ -18,14 +18,14 @@ package com.google.android.apps.mytracks.fragments;
import com.google.android.apps.mytracks.ChartView; import com.google.android.apps.mytracks.ChartView;
import com.google.android.apps.mytracks.Constants; import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.MyTracksApplication; import com.google.android.apps.mytracks.TrackDetailActivity;
import com.google.android.apps.mytracks.content.TrackDataType;
import com.google.android.apps.mytracks.content.MyTracksLocation; import com.google.android.apps.mytracks.content.MyTracksLocation;
import com.google.android.apps.mytracks.content.Sensor; import com.google.android.apps.mytracks.content.Sensor;
import com.google.android.apps.mytracks.content.Sensor.SensorDataSet; import com.google.android.apps.mytracks.content.Sensor.SensorDataSet;
import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.TrackDataHub; import com.google.android.apps.mytracks.content.TrackDataHub;
import com.google.android.apps.mytracks.content.TrackDataListener; import com.google.android.apps.mytracks.content.TrackDataListener;
import com.google.android.apps.mytracks.content.TrackDataType;
import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.stats.DoubleBuffer; import com.google.android.apps.mytracks.stats.DoubleBuffer;
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder; import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
@@ -96,7 +96,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
zoomControls.setIsZoomInEnabled(chartView.canZoomIn()); zoomControls.setIsZoomInEnabled(chartView.canZoomIn());
zoomControls.setIsZoomOutEnabled(chartView.canZoomOut()); zoomControls.setIsZoomOutEnabled(chartView.canZoomOut());
chartView.setShowPointer(isRecording()); chartView.setShowPointer(isSelectedTrackRecording());
chartView.invalidate(); chartView.invalidate();
} }
}; };
@@ -164,17 +164,17 @@ public class ChartFragment extends Fragment implements TrackDataListener {
} }
@Override @Override
public void onProviderStateChange(ProviderState state) { public void onLocationStateChanged(LocationState state) {
// We don't care. // We don't care.
} }
@Override @Override
public void onCurrentLocationChanged(Location loc) { public void onLocationChanged(Location loc) {
// We don't care. // We don't care.
} }
@Override @Override
public void onCurrentHeadingChanged(double heading) { public void onHeadingChanged(double heading) {
// We don't care. // We don't care.
} }
@@ -213,7 +213,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
} }
@Override @Override
public void onNewTrackPoint(Location location) { public void onSampledInTrackPoint(Location location) {
if (LocationUtils.isValidLocation(location)) { if (LocationUtils.isValidLocation(location)) {
double[] data = new double[ChartView.NUM_SERIES + 1]; double[] data = new double[ChartView.NUM_SERIES + 1];
fillDataPoint(location, data); fillDataPoint(location, data);
@@ -259,7 +259,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
} }
@Override @Override
public boolean onUnitsChanged(boolean metric) { public boolean onMetricUnitsChanged(boolean metric) {
if (metricUnits == metric) { if (metricUnits == metric) {
return false; return false;
} }
@@ -358,13 +358,13 @@ public class ChartFragment extends Fragment implements TrackDataListener {
* accessed by multiple threads. * accessed by multiple threads.
*/ */
private synchronized void resumeTrackDataHub() { private synchronized void resumeTrackDataHub() {
trackDataHub = ((MyTracksApplication) getActivity().getApplication()).getTrackDataHub(); trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub();
trackDataHub.registerTrackDataListener(this, EnumSet.of( trackDataHub.registerTrackDataListener(this, EnumSet.of(
TrackDataType.SELECTED_TRACK, TrackDataType.SELECTED_TRACK,
TrackDataType.TRACKS_TABLE, TrackDataType.TRACKS_TABLE,
TrackDataType.WAYPOINTS_TABLE, TrackDataType.WAYPOINTS_TABLE,
TrackDataType.TRACK_POINTS_TABLE, TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE,
TrackDataType.SAMPLED_OUT_TRACK_POINTS, TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE,
TrackDataType.PREFERENCE)); TrackDataType.PREFERENCE));
} }
@@ -378,11 +378,11 @@ public class ChartFragment extends Fragment implements TrackDataListener {
} }
/** /**
* Returns true if recording. Needs to be synchronized because trackDataHub * Returns true if the selected track is recording. Needs to be synchronized
* can be accessed by multiple threads. * because trackDataHub can be accessed by multiple threads.
*/ */
private synchronized boolean isRecording() { private synchronized boolean isSelectedTrackRecording() {
return trackDataHub != null && trackDataHub.isRecordingSelected(); return trackDataHub != null && trackDataHub.isSelectedTrackRecording();
} }
/** /**
@@ -17,14 +17,13 @@
package com.google.android.apps.mytracks.fragments; package com.google.android.apps.mytracks.fragments;
import com.google.android.apps.mytracks.MapOverlay; import com.google.android.apps.mytracks.MapOverlay;
import com.google.android.apps.mytracks.MyTracksApplication;
import com.google.android.apps.mytracks.TrackDetailActivity; import com.google.android.apps.mytracks.TrackDetailActivity;
import com.google.android.apps.mytracks.content.TrackDataType;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils; import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils.Factory; import com.google.android.apps.mytracks.content.MyTracksProviderUtils.Factory;
import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.TrackDataHub; import com.google.android.apps.mytracks.content.TrackDataHub;
import com.google.android.apps.mytracks.content.TrackDataListener; import com.google.android.apps.mytracks.content.TrackDataListener;
import com.google.android.apps.mytracks.content.TrackDataType;
import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.stats.TripStatistics; import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.apps.mytracks.util.ApiAdapterFactory; import com.google.android.apps.mytracks.util.ApiAdapterFactory;
@@ -271,7 +270,7 @@ public class MapFragment extends Fragment
} }
@Override @Override
public void onProviderStateChange(ProviderState state) { public void onLocationStateChanged(LocationState state) {
final int messageId; final int messageId;
final boolean isGpsDisabled; final boolean isGpsDisabled;
switch (state) { switch (state) {
@@ -315,13 +314,13 @@ public class MapFragment extends Fragment
} }
@Override @Override
public void onCurrentLocationChanged(Location location) { public void onLocationChanged(Location location) {
currentLocation = location; currentLocation = location;
updateCurrentLocation(); updateCurrentLocation();
} }
@Override @Override
public void onCurrentHeadingChanged(double heading) { public void onHeadingChanged(double heading) {
if (mapOverlay.setHeading((float) heading)) { if (mapOverlay.setHeading((float) heading)) {
mapView.postInvalidate(); mapView.postInvalidate();
} }
@@ -362,7 +361,7 @@ public class MapFragment extends Fragment
} }
@Override @Override
public void onNewTrackPoint(Location location) { public void onSampledInTrackPoint(Location location) {
if (LocationUtils.isValidLocation(location)) { if (LocationUtils.isValidLocation(location)) {
mapOverlay.addLocation(location); mapOverlay.addLocation(location);
} }
@@ -402,7 +401,7 @@ public class MapFragment extends Fragment
} }
@Override @Override
public boolean onUnitsChanged(boolean metric) { public boolean onMetricUnitsChanged(boolean metric) {
// We don't care. // We don't care.
return false; return false;
} }
@@ -418,13 +417,13 @@ public class MapFragment extends Fragment
* accessed by multiple threads. * accessed by multiple threads.
*/ */
private synchronized void resumeTrackDataHub() { private synchronized void resumeTrackDataHub() {
trackDataHub = ((MyTracksApplication) getActivity().getApplication()).getTrackDataHub(); trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub();
trackDataHub.registerTrackDataListener(this, EnumSet.of( trackDataHub.registerTrackDataListener(this, EnumSet.of(
TrackDataType.SELECTED_TRACK, TrackDataType.SELECTED_TRACK,
TrackDataType.WAYPOINTS_TABLE, TrackDataType.WAYPOINTS_TABLE,
TrackDataType.TRACK_POINTS_TABLE, TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE,
TrackDataType.LOCATION, TrackDataType.LOCATION,
TrackDataType.COMPASS)); TrackDataType.HEADING));
} }
/** /**
@@ -16,11 +16,11 @@
package com.google.android.apps.mytracks.fragments; package com.google.android.apps.mytracks.fragments;
import com.google.android.apps.mytracks.MyTracksApplication; import com.google.android.apps.mytracks.TrackDetailActivity;
import com.google.android.apps.mytracks.content.TrackDataType;
import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.TrackDataHub; import com.google.android.apps.mytracks.content.TrackDataHub;
import com.google.android.apps.mytracks.content.TrackDataListener; import com.google.android.apps.mytracks.content.TrackDataListener;
import com.google.android.apps.mytracks.content.TrackDataType;
import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.stats.TripStatistics; import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.apps.mytracks.util.PreferencesUtils; import com.google.android.apps.mytracks.util.PreferencesUtils;
@@ -61,7 +61,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
// A runnable to update the total time field. // A runnable to update the total time field.
private final Runnable updateTotalTime = new Runnable() { private final Runnable updateTotalTime = new Runnable() {
public void run() { public void run() {
if (isRecording()) { if (isSelectedTrackRecording()) {
StatsUtils.setTotalTimeValue(getActivity(), System.currentTimeMillis() - startTime); StatsUtils.setTotalTimeValue(getActivity(), System.currentTimeMillis() - startTime);
} }
} }
@@ -117,8 +117,8 @@ public class StatsFragment extends Fragment implements TrackDataListener {
} }
@Override @Override
public void onProviderStateChange(ProviderState state) { public void onLocationStateChanged(LocationState state) {
if (isResumed() && (state == ProviderState.DISABLED || state == ProviderState.NO_FIX)) { if (isResumed() && (state == LocationState.DISABLED || state == LocationState.NO_FIX)) {
getActivity().runOnUiThread(new Runnable() { getActivity().runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -130,8 +130,8 @@ public class StatsFragment extends Fragment implements TrackDataListener {
} }
@Override @Override
public void onCurrentLocationChanged(final Location location) { public void onLocationChanged(final Location location) {
if (isResumed() && isRecording()) { if (isResumed() && isSelectedTrackRecording()) {
getActivity().runOnUiThread(new Runnable() { getActivity().runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -143,7 +143,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
} }
@Override @Override
public void onCurrentHeadingChanged(double heading) { public void onHeadingChanged(double heading) {
// We don't care. // We don't care.
} }
@@ -175,7 +175,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
lastTripStatistics = track.getTripStatistics(); lastTripStatistics = track.getTripStatistics();
startTime = track.getTripStatistics().getStartTime(); startTime = track.getTripStatistics().getStartTime();
if (!isRecording()) { if (!isSelectedTrackRecording()) {
lastLocation = null; lastLocation = null;
} }
updateUi(); updateUi();
@@ -190,7 +190,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
} }
@Override @Override
public void onNewTrackPoint(Location loc) { public void onSampledInTrackPoint(Location loc) {
// We don't care. // We don't care.
} }
@@ -225,7 +225,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
} }
@Override @Override
public boolean onUnitsChanged(final boolean metric) { public boolean onMetricUnitsChanged(final boolean metric) {
if (isResumed()) { if (isResumed()) {
getActivity().runOnUiThread(new Runnable() { getActivity().runOnUiThread(new Runnable() {
@Override @Override
@@ -255,7 +255,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
* be accessed by multiple threads. * be accessed by multiple threads.
*/ */
private synchronized void resumeTrackDataHub() { private synchronized void resumeTrackDataHub() {
trackDataHub = ((MyTracksApplication) getActivity().getApplication()).getTrackDataHub(); trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub();
trackDataHub.registerTrackDataListener(this, EnumSet.of( trackDataHub.registerTrackDataListener(this, EnumSet.of(
TrackDataType.SELECTED_TRACK, TrackDataType.SELECTED_TRACK,
TrackDataType.TRACKS_TABLE, TrackDataType.TRACKS_TABLE,
@@ -273,11 +273,11 @@ public class StatsFragment extends Fragment implements TrackDataListener {
} }
/** /**
* Returns true if recording. Needs to be synchronized because trackDataHub * Returns true if the selected track is recording. Needs to be synchronized
* can be accessed by multiple threads. * because trackDataHub can be accessed by multiple threads.
*/ */
private synchronized boolean isRecording() { private synchronized boolean isSelectedTrackRecording() {
return trackDataHub != null && trackDataHub.isRecordingSelected(); return trackDataHub != null && trackDataHub.isSelectedTrackRecording();
} }
/** /**
@@ -13,6 +13,7 @@
* License for the specific language governing permissions and limitations under * License for the specific language governing permissions and limitations under
* the License. * the License.
*/ */
package com.google.android.apps.mytracks.maps; package com.google.android.apps.mytracks.maps;
import static com.google.android.apps.mytracks.Constants.TAG; import static com.google.android.apps.mytracks.Constants.TAG;
@@ -36,60 +37,43 @@ import android.util.Log;
* *
* @author Vangelis S. * @author Vangelis S.
*/ */
public class DynamicSpeedTrackPathDescriptor implements TrackPathDescriptor, public class DynamicSpeedTrackPathDescriptor
OnSharedPreferenceChangeListener { implements TrackPathDescriptor, OnSharedPreferenceChangeListener {
private final Context context;
private int speedMargin;
private int slowSpeed; private int slowSpeed;
private int normalSpeed; private int normalSpeed;
private int speedMargin;
private double averageMovingSpeed; private double averageMovingSpeed;
private final Context context;
@VisibleForTesting @VisibleForTesting
static final int CRITICAL_DIFFERENCE_PERCENTAGE = 20; static final int CRITICAL_DIFFERENCE_PERCENTAGE = 20;
public DynamicSpeedTrackPathDescriptor(Context context) { public DynamicSpeedTrackPathDescriptor(Context context) {
this.context = context; this.context = context;
context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE)
.registerOnSharedPreferenceChangeListener(this);
speedMargin = PreferencesUtils.getInt(context, R.string.track_color_mode_percentage_key, speedMargin = PreferencesUtils.getInt(context, R.string.track_color_mode_percentage_key,
PreferencesUtils.TRACK_COLOR_MODE_PERCENTAGE_DEFAULT); PreferencesUtils.TRACK_COLOR_MODE_PERCENTAGE_DEFAULT);
context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE)
.registerOnSharedPreferenceChangeListener(this);
} }
/** @Override
* Get the slow speed calculated based on the % below the average speed.
*
* @return The speed limit considered as slow.
*/
public int getSlowSpeed() { public int getSlowSpeed() {
slowSpeed = (int) (averageMovingSpeed - (averageMovingSpeed * speedMargin / 100)); slowSpeed = (int) (averageMovingSpeed - (averageMovingSpeed * speedMargin / 100.0));
return slowSpeed; return slowSpeed;
} }
/** @Override
* Gets the medium speed calculated based on the % above the average speed.
*
* @return The speed limit considered as normal.
*/
public int getNormalSpeed() { public int getNormalSpeed() {
normalSpeed = (int) (averageMovingSpeed + (averageMovingSpeed * speedMargin / 100)); normalSpeed = (int) (averageMovingSpeed + (averageMovingSpeed * speedMargin / 100.0));
return normalSpeed; return normalSpeed;
} }
@Override @Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { public boolean updateState() {
Log.d(TAG, "DynamicSpeedTrackPathDescriptor: onSharedPreferences changed " + key);
if (PreferencesUtils.getKey(context, R.string.track_color_mode_percentage_key).equals(key)) {
speedMargin = PreferencesUtils.getInt(context, R.string.track_color_mode_percentage_key,
PreferencesUtils.TRACK_COLOR_MODE_PERCENTAGE_DEFAULT);
}
}
@Override
public boolean needsRedraw() {
long selectedTrackId = PreferencesUtils.getLong(context, R.string.selected_track_id_key); long selectedTrackId = PreferencesUtils.getLong(context, R.string.selected_track_id_key);
if (selectedTrackId == PreferencesUtils.SELECTED_TRACK_ID_DEFAULT) { if (selectedTrackId == PreferencesUtils.SELECTED_TRACK_ID_DEFAULT) {
// Could not find track. Log.d(TAG, "No selected track id.");
return false; return false;
} }
Track track = MyTracksProviderUtils.Factory.get(context).getTrack(selectedTrackId); Track track = MyTracksProviderUtils.Factory.get(context).getTrack(selectedTrackId);
@@ -97,40 +81,48 @@ public class DynamicSpeedTrackPathDescriptor implements TrackPathDescriptor,
Log.d(TAG, "No track for " + selectedTrackId); Log.d(TAG, "No track for " + selectedTrackId);
return false; return false;
} }
TripStatistics stats = track.getTripStatistics(); TripStatistics tripStatistics = track.getTripStatistics();
double newAverageMovingSpeed = (int) Math.floor( double newAverageMovingSpeed = (int) Math.floor(
stats.getAverageMovingSpeed() * UnitConversions.MS_TO_KMH); tripStatistics.getAverageMovingSpeed() * UnitConversions.MS_TO_KMH);
return isDifferenceSignificant(averageMovingSpeed, newAverageMovingSpeed); if (isDifferenceSignificant(averageMovingSpeed, newAverageMovingSpeed)) {
averageMovingSpeed = newAverageMovingSpeed;
return true;
} else {
return false;
}
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (PreferencesUtils.getKey(context, R.string.track_color_mode_percentage_key).equals(key)) {
speedMargin = PreferencesUtils.getInt(context, R.string.track_color_mode_percentage_key,
PreferencesUtils.TRACK_COLOR_MODE_PERCENTAGE_DEFAULT);
}
} }
/** /**
* Checks whether the old speed and the new speed differ significantly or not. * Returns true if the average moving speed and the new average moving speed
* are significantly different.
*
* @param oldAverageMovingSpeed
* @param newAverageMovingSpeed
*/ */
public boolean isDifferenceSignificant(double oldAverageMovingSpeed, double newAverageMovingSpeed) { @VisibleForTesting
boolean isDifferenceSignificant(double oldAverageMovingSpeed, double newAverageMovingSpeed) {
if (oldAverageMovingSpeed == 0) { if (oldAverageMovingSpeed == 0) {
if (newAverageMovingSpeed == 0) { return newAverageMovingSpeed != 0;
return false;
} else {
averageMovingSpeed = newAverageMovingSpeed;
return true;
}
} }
// Here, both oldAverageMovingSpeed and newAverageMovingSpeed are not zero. // Here, both oldAverageMovingSpeed and newAverageMovingSpeed are not zero.
double maxValue = Math.max(oldAverageMovingSpeed, newAverageMovingSpeed); double maxValue = Math.max(oldAverageMovingSpeed, newAverageMovingSpeed);
double differencePercentage = Math.abs(oldAverageMovingSpeed - newAverageMovingSpeed) double differencePercentage = Math.abs(oldAverageMovingSpeed - newAverageMovingSpeed) / maxValue
/ maxValue * 100; * 100.0;
if (differencePercentage >= CRITICAL_DIFFERENCE_PERCENTAGE) { return differencePercentage >= CRITICAL_DIFFERENCE_PERCENTAGE;
averageMovingSpeed = newAverageMovingSpeed;
return true;
}
return false;
} }
/** /**
* Gets the value of variable speedMargin to check the result of test. * Gets the speed margin.
* @return the value of speedMargin.
*/ */
@VisibleForTesting @VisibleForTesting
int getSpeedMargin() { int getSpeedMargin() {
@@ -138,21 +130,20 @@ public class DynamicSpeedTrackPathDescriptor implements TrackPathDescriptor,
} }
/** /**
* Sets the value of newAverageMovingSpeed to test the method isDifferenceSignificant. * Gets the average moving speed.
* @param newAverageMovingSpeed the value to set.
*/
@VisibleForTesting
void setAverageMovingSpeed(double newAverageMovingSpeed) {
averageMovingSpeed = newAverageMovingSpeed;
}
/**
* Gets the value of averageMovingSpeed to check the result of test.
*
* @return the value of averageMovingSpeed
*/ */
@VisibleForTesting @VisibleForTesting
double getAverageMovingSpeed() { double getAverageMovingSpeed() {
return averageMovingSpeed; return averageMovingSpeed;
} }
/**
* Sets the average moving speed.
*
* @param value the value
*/
@VisibleForTesting
void setAverageMovingSpeed(double value) {
averageMovingSpeed = value;
}
} }
@@ -25,7 +25,6 @@ import com.google.common.annotations.VisibleForTesting;
import android.content.Context; import android.content.Context;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point; import android.graphics.Point;
import android.graphics.Rect; import android.graphics.Rect;
@@ -33,117 +32,105 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* A path painter that varies the path colors based on fixed speeds or average speed margin * A path painter that varies the path colors based on fixed speeds or average
* depending of the TrackPathDescriptor passed to its constructor. * speed margin depending of the TrackPathDescriptor passed to its constructor.
* *
* @author Vangelis S. * @author Vangelis S.
*/ */
public class DynamicSpeedTrackPathPainter implements TrackPathPainter { public class DynamicSpeedTrackPathPainter implements TrackPathPainter {
private final Paint selectedTrackPaintSlow;
private final Paint selectedTrackPaintMedium;
private final Paint selectedTrackPaintFast;
private final List<ColoredPath> coloredPaths;
private final TrackPathDescriptor trackPathDescriptor; private final TrackPathDescriptor trackPathDescriptor;
private int slowSpeed; private final Paint slowPaint;
private int normalSpeed; private final Paint mediumPaint;
private final Paint fastPaint;
private final List<ColoredPath> coloredPaths;
public DynamicSpeedTrackPathPainter (Context context, TrackPathDescriptor trackPathDescriptor) { public DynamicSpeedTrackPathPainter(Context context, TrackPathDescriptor trackPathDescriptor) {
this.trackPathDescriptor = trackPathDescriptor; this.trackPathDescriptor = trackPathDescriptor;
slowPaint = TrackPathUtils.getPaint(context, R.color.slow_path);
selectedTrackPaintSlow = TrackPathUtilities.getPaint(R.color.slow_path, context); mediumPaint = TrackPathUtils.getPaint(context, R.color.normal_path);
selectedTrackPaintMedium = TrackPathUtilities.getPaint(R.color.normal_path, context); fastPaint = TrackPathUtils.getPaint(context, R.color.fast_path);
selectedTrackPaintFast = TrackPathUtilities.getPaint(R.color.fast_path, context); coloredPaths = new ArrayList<ColoredPath>();
this.coloredPaths = new ArrayList<ColoredPath>();
} }
@Override @Override
public void drawTrack(Canvas canvas) { public boolean hasPath() {
for(int i = 0; i < coloredPaths.size(); ++i) { return !coloredPaths.isEmpty();
ColoredPath coloredPath = coloredPaths.get(i); }
canvas.drawPath(coloredPath.getPath(), coloredPath.getPathPaint());
@Override
public boolean updateState() {
return trackPathDescriptor.updateState();
}
@Override
public void updatePath(
Projection projection, Rect viewRect, int startIndex, List<CachedLocation> points) {
boolean hasLastPoint = startIndex != 0 && points.get(startIndex -1).isValid();
Point point = new Point();
if (hasLastPoint) {
GeoPoint geoPoint = points.get(startIndex -1).getGeoPoint();
projection.toPixels(geoPoint, point);
} }
} boolean newSegment = !hasLastPoint;
// Assume if last point exists, it is visible
boolean lastPointVisible = hasLastPoint;
int slowSpeed = trackPathDescriptor.getSlowSpeed();
int normalSpeed = trackPathDescriptor.getNormalSpeed();
@Override for (int i = startIndex; i < points.size(); ++i) {
public void updatePath(Projection projection, Rect viewRect, int startLocationIdx, CachedLocation cachedLocation = points.get(i);
Boolean alwaysVisible, List<CachedLocation> points) {
// Whether to start a new segment on new valid and visible point.
boolean newSegment = startLocationIdx <= 0 || !points.get(startLocationIdx - 1).valid;
boolean lastVisible = !newSegment;
final Point pt = new Point();
clear(); // If not valid, start a new segment
if (!cachedLocation.isValid()) {
slowSpeed = trackPathDescriptor.getSlowSpeed();
normalSpeed = trackPathDescriptor.getNormalSpeed();
// Loop over track points.
for (int i = startLocationIdx; i < points.size(); ++i) {
CachedLocation loc = points.get(i);
// Check if valid, if not then indicate a new segment.
if (!loc.valid) {
newSegment = true; newSegment = true;
continue; continue;
} }
final GeoPoint geoPoint = loc.geoPoint; GeoPoint geoPoint = cachedLocation.getGeoPoint();
// Check if this breaks the existing segment. // Check if this breaks the existing segment.
boolean visible = alwaysVisible || viewRect.contains( boolean pointVisible = viewRect.contains(geoPoint.getLongitudeE6(), geoPoint.getLatitudeE6());
geoPoint.getLongitudeE6(), geoPoint.getLatitudeE6()); if (!pointVisible && !lastPointVisible) {
if (!visible && !lastVisible) { // This point and the last point are both outside visible area.
// This is a point outside view not connected to a visible one.
newSegment = true; newSegment = true;
} }
lastVisible = visible; lastPointVisible = pointVisible;
// Either move to beginning of a new segment or continue the old one. // Either update point or draw a line from the last point
if (newSegment) { if (newSegment) {
projection.toPixels(geoPoint, pt); projection.toPixels(geoPoint, point);
newSegment = false; newSegment = false;
} else { } else {
ColoredPath coloredPath; ColoredPath coloredPath;
if(loc.speed <= slowSpeed) { if (cachedLocation.getSpeed() <= slowSpeed) {
coloredPath = new ColoredPath(selectedTrackPaintSlow); coloredPath = new ColoredPath(slowPaint);
} } else if (cachedLocation.getSpeed() <= normalSpeed) {
else if(loc.speed <= normalSpeed) { coloredPath = new ColoredPath(mediumPaint);
coloredPath = new ColoredPath(selectedTrackPaintMedium);
} else { } else {
coloredPath = new ColoredPath(selectedTrackPaintFast); coloredPath = new ColoredPath(fastPaint);
} }
coloredPath.getPath().moveTo(pt.x, pt.y); coloredPath.getPath().moveTo(point.x, point.y);
projection.toPixels(geoPoint, pt); projection.toPixels(geoPoint, point);
coloredPath.getPath().lineTo(pt.x, pt.y); coloredPath.getPath().lineTo(point.x, point.y);
coloredPaths.add(coloredPath); coloredPaths.add(coloredPath);
} }
} }
} }
@Override @Override
public void clear() public void clearPath() {
{
coloredPaths.clear(); coloredPaths.clear();
} }
@Override @Override
public boolean needsRedraw() { public void drawPath(Canvas canvas) {
return trackPathDescriptor.needsRedraw(); for (int i = 0; i < coloredPaths.size(); i++) {
} ColoredPath coloredPath = coloredPaths.get(i);
canvas.drawPath(coloredPath.getPath(), coloredPath.getPathPaint());
@Override
public Path getLastPath() {
Path path = new Path();
for(int i = 0; i < coloredPaths.size(); ++i) {
path.addPath(coloredPaths.get(i).getPath());
} }
return path;
} }
/** /**
* Returns coloredPaths. * Gets the colored paths.
*
* @return coloredPaths
*/ */
@VisibleForTesting @VisibleForTesting
List<ColoredPath> getColoredPaths() { List<ColoredPath> getColoredPaths() {
@@ -31,40 +31,36 @@ import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
*/ */
public class FixedSpeedTrackPathDescriptor public class FixedSpeedTrackPathDescriptor
implements TrackPathDescriptor, OnSharedPreferenceChangeListener { implements TrackPathDescriptor, OnSharedPreferenceChangeListener {
private final Context context;
private int slowSpeed; private int slowSpeed;
private int normalSpeed; private int normalSpeed;
private final Context context;
public FixedSpeedTrackPathDescriptor(Context context) { public FixedSpeedTrackPathDescriptor(Context context) {
this.context = context; this.context = context;
context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE)
.registerOnSharedPreferenceChangeListener(this);
slowSpeed = PreferencesUtils.getInt(context, R.string.track_color_mode_slow_key, slowSpeed = PreferencesUtils.getInt(context, R.string.track_color_mode_slow_key,
PreferencesUtils.TRACK_COLOR_MODE_SLOW_DEFAULT); PreferencesUtils.TRACK_COLOR_MODE_SLOW_DEFAULT);
normalSpeed = PreferencesUtils.getInt(context, R.string.track_color_mode_medium_key, normalSpeed = PreferencesUtils.getInt(context, R.string.track_color_mode_medium_key,
PreferencesUtils.TRACK_COLOR_MODE_MEDIUM_DEFAULT); PreferencesUtils.TRACK_COLOR_MODE_MEDIUM_DEFAULT);
context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE)
.registerOnSharedPreferenceChangeListener(this);
} }
/** @Override
* Gets the slow speed for reference.
*
* @return The speed limit considered as slow.
*/
public int getSlowSpeed() { public int getSlowSpeed() {
return slowSpeed; return slowSpeed;
} }
/** @Override
* Gets the normal speed for reference.
*
* @return The speed limit considered as normal.
*/
public int getNormalSpeed() { public int getNormalSpeed() {
return normalSpeed; return normalSpeed;
} }
@Override
public boolean updateState() {
return false;
}
@Override @Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (PreferencesUtils.getKey(context, R.string.track_color_mode_slow_key).equals(key)) { if (PreferencesUtils.getKey(context, R.string.track_color_mode_slow_key).equals(key)) {
@@ -75,9 +71,4 @@ public class FixedSpeedTrackPathDescriptor
PreferencesUtils.TRACK_COLOR_MODE_MEDIUM_DEFAULT); PreferencesUtils.TRACK_COLOR_MODE_MEDIUM_DEFAULT);
} }
} }
@Override
public boolean needsRedraw() {
return false;
}
} }
@@ -40,88 +40,103 @@ public class SingleColorTrackPathPainter implements TrackPathPainter {
private Path path; private Path path;
public SingleColorTrackPathPainter(Context context) { public SingleColorTrackPathPainter(Context context) {
selectedTrackPaint = TrackPathUtilities.getPaint(R.color.red, context); selectedTrackPaint = TrackPathUtils.getPaint(context, R.color.red);
} }
@Override @Override
public void drawTrack(Canvas canvas) { public boolean hasPath() {
canvas.drawPath(path, selectedTrackPaint); return path != null;
} }
@Override @Override
public void updatePath(Projection projection, Rect viewRect, int startLocationIdx, public boolean updateState() {
Boolean alwaysVisible, List<CachedLocation> points) { return false;
path = newPath(); }
updatePath(projection, viewRect, startLocationIdx, alwaysVisible, points, path);
@Override
public void updatePath(
Projection projection, Rect viewRect, int startIndex, List<CachedLocation> points) {
if (!hasPath()) {
path = newPath();
}
updatePath(projection, viewRect, startIndex, points, path);
}
@Override
public void clearPath() {
path = null;
}
@Override
public void drawPath(Canvas canvas) {
if (path != null) {
canvas.drawPath(path, selectedTrackPaint);
}
} }
/** /**
* Updates the path. * Updates the path.
* *
* @param projection The Canvas to draw upon. * @param projection the projection
* @param viewRect The Path to be drawn. * @param viewRect the view rectangle
* @param startLocationIdx The start point from where update the path. * @param startIndex the start index
* @param alwaysVisible Flag for always visible. * @param points the points
* @param points The list of points used to update the path. * @param pathToUpdate the path to update
* @param pathToUpdate The path to be created.
*/ */
@VisibleForTesting @VisibleForTesting
void updatePath(Projection projection, Rect viewRect, int startLocationIdx, void updatePath(Projection projection, Rect viewRect, int startIndex, List<CachedLocation> points,
Boolean alwaysVisible, List<CachedLocation> points, Path pathToUpdate) { Path pathToUpdate) {
pathToUpdate.incReserve(points.size()); pathToUpdate.incReserve(points.size() - startIndex);
// Whether to start a new segment on new valid and visible point.
boolean newSegment = startLocationIdx <= 0 || !points.get(startLocationIdx - 1).valid;
boolean lastVisible = !newSegment;
final Point pt = new Point();
// Loop over track points.
for (int i = startLocationIdx; i < points.size(); ++i) {
CachedLocation loc = points.get(i);
// Check if valid, if not then indicate a new segment. boolean hasLastPoint = startIndex != 0 && points.get(startIndex - 1).isValid();
if (!loc.valid) { boolean newSegment = !hasLastPoint;
// Assume if last point exists, it is visible
boolean lastPointVisible = hasLastPoint;
Point point = new Point();
for (int i = startIndex; i < points.size(); i++) {
CachedLocation cachedLocation = points.get(i);
// If not valid, start a new segment
if (!cachedLocation.isValid()) {
newSegment = true; newSegment = true;
continue; continue;
} }
final GeoPoint geoPoint = loc.geoPoint; GeoPoint geoPoint = cachedLocation.getGeoPoint();
// Check if this breaks the existing segment. // Check if this breaks the existing segment.
boolean visible = alwaysVisible boolean pointVisible = viewRect.contains(geoPoint.getLongitudeE6(), geoPoint.getLatitudeE6());
|| viewRect.contains(geoPoint.getLongitudeE6(), geoPoint.getLatitudeE6()); if (!pointVisible && !lastPointVisible) {
if (!visible && !lastVisible) { // This point and the last point are both outside visible area.
// This is a point outside view not connected to a visible one.
newSegment = true; newSegment = true;
} }
lastVisible = visible; lastPointVisible = pointVisible;
// Either move to beginning of a new segment or continue the old one. // Either update point or draw a line from the last point
projection.toPixels(geoPoint, pt); projection.toPixels(geoPoint, point);
if (newSegment) { if (newSegment) {
pathToUpdate.moveTo(pt.x, pt.y); pathToUpdate.moveTo(point.x, point.y);
newSegment = false; newSegment = false;
} else { } else {
pathToUpdate.lineTo(pt.x, pt.y); pathToUpdate.lineTo(point.x, point.y);
} }
} }
} }
@Override /**
public void clear() { * Creates a new path.
path = null; */
@VisibleForTesting
protected Path newPath() {
return new Path();
} }
@Override /**
public boolean needsRedraw() { * Gets the path.
return false; */
} @VisibleForTesting
public Path getPath() {
@Override
public Path getLastPath() {
return path; return path;
} }
// Visible for testing
public Path newPath() {
return new Path();
}
} }
@@ -13,6 +13,7 @@
* License for the specific language governing permissions and limitations under * License for the specific language governing permissions and limitations under
* the License. * the License.
*/ */
package com.google.android.apps.mytracks.maps; package com.google.android.apps.mytracks.maps;
/** /**
@@ -21,18 +22,19 @@ package com.google.android.apps.mytracks.maps;
* @author Vangelis S. * @author Vangelis S.
*/ */
public interface TrackPathDescriptor { public interface TrackPathDescriptor {
/**
* @return The maximum speed which is considered slow.
*/
int getSlowSpeed();
/** /**
* @return The maximum speed which is considered normal. * Gets the maximum speed which is considered slow.
*/ */
int getNormalSpeed(); public int getSlowSpeed();
/** /**
* @return True if the path needs to be updated. * Gets the maximum speed which is considered normal.
*/ */
boolean needsRedraw(); public int getNormalSpeed();
/**
* Updates state. Returns true if the state is updated.
*/
public boolean updateState();
} }
@@ -13,13 +13,13 @@
* License for the specific language governing permissions and limitations under * License for the specific language governing permissions and limitations under
* the License. * the License.
*/ */
package com.google.android.apps.mytracks.maps; package com.google.android.apps.mytracks.maps;
import com.google.android.apps.mytracks.MapOverlay.CachedLocation; import com.google.android.apps.mytracks.MapOverlay.CachedLocation;
import com.google.android.maps.Projection; import com.google.android.maps.Projection;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.Rect; import android.graphics.Rect;
import java.util.List; import java.util.List;
@@ -32,37 +32,35 @@ import java.util.List;
public interface TrackPathPainter { public interface TrackPathPainter {
/** /**
* Clears the related data. * Returns true if has path.
*/ */
void clear(); public boolean hasPath();
/** /**
* Draws the path to the canvas. * Updates state. Returns true if the state is updated.
* @param canvas The Canvas to draw upon
*/ */
void drawTrack(Canvas canvas); public boolean updateState();
/** /**
* Updates the path. * Updates the path. Creates a new path if necessary
* @param projection The Canvas to draw upon. *
* @param viewRect The Path to be drawn. * @param projection the projection
* @param startLocationIdx The start point from where update the path. * @param viewRect the view rectangle
* @param alwaysVisible Flag for alwaysvisible. * @param startIndex the start index
* @param points The list of points used to update the path. * @param points the points
*/ */
void updatePath(Projection projection, Rect viewRect, int startLocationIdx, public void updatePath(
Boolean alwaysVisible, List<CachedLocation> points); Projection projection, Rect viewRect, int startIndex, List<CachedLocation> points);
/** /**
* @return True if the path needs to be updated. * Clears the path.
*/ */
boolean needsRedraw(); public void clearPath();
/** /**
* @return The path being used currently. * Draws the path.
*
* @param canvas the canvas
*/ */
Path getLastPath(); public void drawPath(Canvas canvas);
} }
@@ -13,6 +13,7 @@
* License for the specific language governing permissions and limitations under * License for the specific language governing permissions and limitations under
* the License. * the License.
*/ */
package com.google.android.apps.mytracks.maps; package com.google.android.apps.mytracks.maps;
import static com.google.android.apps.mytracks.Constants.TAG; import static com.google.android.apps.mytracks.Constants.TAG;
@@ -24,32 +25,31 @@ import android.content.Context;
import android.util.Log; import android.util.Log;
/** /**
* A factory for TrackPathPainters. * A factory for {@link TrackPathPainter}.
* *
* @author Vangelis S. * @author Vangelis S.
*/ */
public class TrackPathPainterFactory { public class TrackPathPainterFactory {
private TrackPathPainterFactory() { private TrackPathPainterFactory() {}
}
/** /**
* Get a new TrackPathPainter. * Get a new {@link TrackPathPainter}.
* @param context Context to fetch system preferences. *
* @return The TrackPathPainter that corresponds to the track color mode setting. * @param context the context
*/ */
public static TrackPathPainter getTrackPathPainter(Context context) { public static TrackPathPainter getTrackPathPainter(Context context) {
String trackColorMode = PreferencesUtils.getString(context, R.string.track_color_mode_key, String trackColorMode = PreferencesUtils.getString(context, R.string.track_color_mode_key,
context.getString(R.string.settings_map_track_color_mode_single_value)); context.getString(R.string.settings_map_track_color_mode_single_value));
Log.i(TAG, "Creating track path painter of type: " + trackColorMode); Log.i(TAG, "Creating track path painter of type: " + trackColorMode);
if (context.getString(R.string.settings_map_track_color_mode_fixed_value) if (context.getString(R.string.settings_map_track_color_mode_dynamic_value)
.equals(trackColorMode)) {
return new DynamicSpeedTrackPathPainter(context, new FixedSpeedTrackPathDescriptor(context));
} else if (context.getString(R.string.settings_map_track_color_mode_dynamic_value)
.equals(trackColorMode)) { .equals(trackColorMode)) {
return new DynamicSpeedTrackPathPainter(context, new DynamicSpeedTrackPathDescriptor( return new DynamicSpeedTrackPathPainter(context, new DynamicSpeedTrackPathDescriptor(
context)); context));
} else if (context.getString(R.string.settings_map_track_color_mode_fixed_value)
.equals(trackColorMode)) {
return new DynamicSpeedTrackPathPainter(context, new FixedSpeedTrackPathDescriptor(context));
} else { } else {
return new SingleColorTrackPathPainter(context); return new SingleColorTrackPathPainter(context);
} }
@@ -13,21 +13,30 @@
* License for the specific language governing permissions and limitations under * License for the specific language governing permissions and limitations under
* the License. * the License.
*/ */
package com.google.android.apps.mytracks.maps; package com.google.android.apps.mytracks.maps;
import android.content.Context; import android.content.Context;
import android.graphics.Paint; import android.graphics.Paint;
/** /**
* Various utility functions for TrackPath painting. * Various utility functions for track path painting.
* *
* @author Vangelis S. * @author Vangelis S.
*/ */
public class TrackPathUtilities { public class TrackPathUtils {
public static Paint getPaint(int id, Context context) { private TrackPathUtils() {}
/**
* Gets a paint.
*
* @param context the context
* @param colorId the color id
*/
public static Paint getPaint(Context context, int colorId) {
Paint paint = new Paint(); Paint paint = new Paint();
paint.setColor(context.getResources().getColor(id)); paint.setColor(context.getResources().getColor(colorId));
paint.setStrokeWidth(3); paint.setStrokeWidth(3);
paint.setStyle(Paint.Style.STROKE); paint.setStyle(Paint.Style.STROKE);
paint.setAntiAlias(true); paint.setAntiAlias(true);
@@ -681,7 +681,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
return waypoint; return waypoint;
} }
private ContentValues createContentValues(Waypoint waypoint) { ContentValues createContentValues(Waypoint waypoint) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
// Value < 0 indicates no id is available // Value < 0 indicates no id is available
@@ -824,7 +824,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
throw new IllegalArgumentException("locationFactory is null"); throw new IllegalArgumentException("locationFactory is null");
} }
return new LocationIterator() { return new LocationIterator() {
private long lastTrackPointId = startTrackPointId; private long lastTrackPointId = -1L;
private Cursor cursor = getCursor(startTrackPointId); private Cursor cursor = getCursor(startTrackPointId);
private final CachedTrackPointsIndexes private final CachedTrackPointsIndexes
indexes = cursor != null ? new CachedTrackPointsIndexes(cursor) indexes = cursor != null ? new CachedTrackPointsIndexes(cursor)
@@ -843,7 +843,8 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
* Advances the cursor to the next batch. Returns true if successful. * Advances the cursor to the next batch. Returns true if successful.
*/ */
private boolean advanceCursorToNextBatch() { private boolean advanceCursorToNextBatch() {
long trackPointId = lastTrackPointId + (descending ? -1 : 1); long trackPointId = lastTrackPointId == -1L ? -1L : lastTrackPointId + (descending ? -1
: 1);
Log.d(TAG, "Advancing track point id: " + trackPointId); Log.d(TAG, "Advancing track point id: " + trackPointId);
cursor.close(); cursor.close();
cursor = getCursor(trackPointId); cursor = getCursor(trackPointId);
Binary file not shown.
Binary file not shown.
@@ -25,121 +25,137 @@ import android.location.Location;
import android.test.AndroidTestCase; import android.test.AndroidTestCase;
/** /**
* Tests for the MyTracks map overlay. * Tests {@link MapOverlay}.
* *
* @author Bartlomiej Niechwiej * @author Bartlomiej Niechwiej
* @author Vangelis S. * @author Vangelis S.
*/ */
public class MapOverlayTest extends AndroidTestCase { public class MapOverlayTest extends AndroidTestCase {
private Canvas canvas; private Canvas canvas;
private MockMyTracksOverlay myTracksOverlay; private MockMyTracksOverlay mockMyTracksOverlay;
private MapView mockView; private MapView mapView;
@Override @Override
protected void setUp() throws Exception { protected void setUp() throws Exception {
super.setUp(); super.setUp();
canvas = new Canvas(); canvas = new Canvas();
myTracksOverlay = new MockMyTracksOverlay(getContext()); mockMyTracksOverlay = new MockMyTracksOverlay(getContext());
// Enable drawing. // Enable drawing.
myTracksOverlay.setTrackDrawingEnabled(true); mockMyTracksOverlay.setTrackDrawingEnabled(true);
// Set a TrackPathPainter with a MockPath. // Set a TrackPathPainter with a MockPath.
myTracksOverlay.setTrackPathPainter(new SingleColorTrackPathPainter(getContext()) { mockMyTracksOverlay.setTrackPathPainter(new SingleColorTrackPathPainter(getContext()) {
@Override @Override
public Path newPath() { public Path newPath() {
return new MockPath(); return new MockPath();
} }
}); });
mockView = null; mapView = null;
} }
/**
* Tests {@link MapOverlay#addLocation(Location)}.
*/
public void testAddLocation() throws Exception { public void testAddLocation() throws Exception {
Location location = new Location("gps"); Location location = new Location("gps");
location.setLatitude(10); location.setLatitude(10);
location.setLongitude(20); location.setLongitude(20);
myTracksOverlay.addLocation(location); mockMyTracksOverlay.addLocation(location);
assertEquals(1, myTracksOverlay.getNumLocations()); assertEquals(1, mockMyTracksOverlay.getNumLocations());
assertEquals(0, myTracksOverlay.getNumWaypoints()); assertEquals(0, mockMyTracksOverlay.getNumWaypoints());
location.setLatitude(20); location.setLatitude(20);
location.setLongitude(30); location.setLongitude(30);
myTracksOverlay.addLocation(location); mockMyTracksOverlay.addLocation(location);
assertEquals(2, myTracksOverlay.getNumLocations()); assertEquals(2, mockMyTracksOverlay.getNumLocations());
assertEquals(0, myTracksOverlay.getNumWaypoints()); assertEquals(0, mockMyTracksOverlay.getNumWaypoints());
assertNull(myTracksOverlay.getTrackPathPainter().getLastPath()); assertFalse(mockMyTracksOverlay.getTrackPathPainter().hasPath());
// Draw and make sure that we don't lose any point. // Draw and make sure that we don't lose any point.
myTracksOverlay.draw(canvas, mockView, false); mockMyTracksOverlay.draw(canvas, mapView, false);
assertEquals(2, myTracksOverlay.getNumLocations()); assertEquals(2, mockMyTracksOverlay.getNumLocations());
assertEquals(0, myTracksOverlay.getNumWaypoints()); assertEquals(0, mockMyTracksOverlay.getNumWaypoints());
assertNotNull(myTracksOverlay.getTrackPathPainter().getLastPath()); assertTrue(mockMyTracksOverlay.getTrackPathPainter().hasPath());
assertTrue(myTracksOverlay.getTrackPathPainter().getLastPath() instanceof MockPath); SingleColorTrackPathPainter trackPathPainter = (SingleColorTrackPathPainter) mockMyTracksOverlay
MockPath path = (MockPath) myTracksOverlay.getTrackPathPainter().getLastPath(); .getTrackPathPainter();
assertEquals(2, path.totalPoints); MockPath path = (MockPath) trackPathPainter.getPath();
assertEquals(2, path.getTotalPoints());
myTracksOverlay.draw(canvas, mockView, true); mockMyTracksOverlay.draw(canvas, mapView, true);
assertEquals(2, myTracksOverlay.getNumLocations()); assertEquals(2, mockMyTracksOverlay.getNumLocations());
assertEquals(0, myTracksOverlay.getNumWaypoints()); assertEquals(0, mockMyTracksOverlay.getNumWaypoints());
assertNotNull(myTracksOverlay.getTrackPathPainter().getLastPath()); assertTrue(mockMyTracksOverlay.getTrackPathPainter().hasPath());
} }
/**
* Tests {@link MapOverlay#clearPoints()}.
*/
public void testClearPoints() throws Exception { public void testClearPoints() throws Exception {
Location location = new Location("gps"); Location location = new Location("gps");
location.setLatitude(10); location.setLatitude(10);
location.setLongitude(20); location.setLongitude(20);
myTracksOverlay.addLocation(location); mockMyTracksOverlay.addLocation(location);
assertEquals(1, myTracksOverlay.getNumLocations()); assertEquals(1, mockMyTracksOverlay.getNumLocations());
myTracksOverlay.clearPoints(); mockMyTracksOverlay.clearPoints();
assertEquals(0, myTracksOverlay.getNumLocations()); assertEquals(0, mockMyTracksOverlay.getNumLocations());
// Same after drawing on canvas. // Test after drawing on canvas
final int locations = 100; final int locations = 100;
for (int i = 0; i < locations; ++i) { for (int i = 0; i < locations; ++i) {
myTracksOverlay.addLocation(location); mockMyTracksOverlay.addLocation(location);
} }
assertEquals(locations, myTracksOverlay.getNumLocations()); assertEquals(locations, mockMyTracksOverlay.getNumLocations());
myTracksOverlay.draw(canvas, mockView, false); mockMyTracksOverlay.draw(canvas, mapView, false);
myTracksOverlay.draw(canvas, mockView, true); mockMyTracksOverlay.draw(canvas, mapView, true);
myTracksOverlay.clearPoints(); mockMyTracksOverlay.clearPoints();
assertEquals(0, myTracksOverlay.getNumLocations()); assertEquals(0, mockMyTracksOverlay.getNumLocations());
} }
/**
* Tests {@link MapOverlay#addWaypoint(Waypoint)}.
*/
public void testAddWaypoint() throws Exception { public void testAddWaypoint() throws Exception {
Location location = new Location("gps"); Location location = new Location("gps");
location.setLatitude(10); location.setLatitude(10);
location.setLongitude(20); location.setLongitude(20);
Waypoint waypoint = new Waypoint(); Waypoint waypoint = new Waypoint();
waypoint.setLocation(location); waypoint.setLocation(location);
myTracksOverlay.addWaypoint(waypoint); mockMyTracksOverlay.addWaypoint(waypoint);
assertEquals(1, myTracksOverlay.getNumWaypoints()); assertEquals(1, mockMyTracksOverlay.getNumWaypoints());
assertEquals(0, myTracksOverlay.getNumLocations()); assertEquals(0, mockMyTracksOverlay.getNumLocations());
assertNull(myTracksOverlay.getTrackPathPainter().getLastPath()); assertFalse(mockMyTracksOverlay.getTrackPathPainter().hasPath());
final int waypoints = 10; final int waypoints = 10;
for (int i = 0; i < waypoints; ++i) { for (int i = 0; i < waypoints; ++i) {
waypoint = new Waypoint(); waypoint = new Waypoint();
waypoint.setLocation(location); waypoint.setLocation(location);
myTracksOverlay.addWaypoint(waypoint); mockMyTracksOverlay.addWaypoint(waypoint);
} }
assertEquals(1 + waypoints, myTracksOverlay.getNumWaypoints()); assertEquals(1 + waypoints, mockMyTracksOverlay.getNumWaypoints());
assertEquals(0, myTracksOverlay.getNumLocations()); assertEquals(0, mockMyTracksOverlay.getNumLocations());
assertNull(myTracksOverlay.getTrackPathPainter().getLastPath()); assertFalse(mockMyTracksOverlay.getTrackPathPainter().hasPath());
} }
/**
* Tests {@link MapOverlay#clearWaypoints()}.
*/
public void testClearWaypoints() throws Exception { public void testClearWaypoints() throws Exception {
Location location = new Location("gps"); Location location = new Location("gps");
location.setLatitude(10); location.setLatitude(10);
location.setLongitude(20); location.setLongitude(20);
Waypoint waypoint = new Waypoint(); Waypoint waypoint = new Waypoint();
waypoint.setLocation(location); waypoint.setLocation(location);
myTracksOverlay.addWaypoint(waypoint); mockMyTracksOverlay.addWaypoint(waypoint);
assertEquals(1, myTracksOverlay.getNumWaypoints()); assertEquals(1, mockMyTracksOverlay.getNumWaypoints());
myTracksOverlay.clearWaypoints(); mockMyTracksOverlay.clearWaypoints();
assertEquals(0, myTracksOverlay.getNumWaypoints()); assertEquals(0, mockMyTracksOverlay.getNumWaypoints());
} }
/**
* Tests {@link MapOverlay#draw(Canvas, MapView, boolean)}.
*/
public void testDrawing() { public void testDrawing() {
Location location = new Location("gps"); Location location = new Location("gps");
location.setLatitude(10); location.setLatitude(10);
@@ -147,30 +163,30 @@ public class MapOverlayTest extends AndroidTestCase {
location.setLongitude(20 + i); location.setLongitude(20 + i);
Waypoint waypoint = new Waypoint(); Waypoint waypoint = new Waypoint();
waypoint.setLocation(location); waypoint.setLocation(location);
myTracksOverlay.addWaypoint(waypoint); mockMyTracksOverlay.addWaypoint(waypoint);
} }
for (int i = 0; i < 100; ++i) { for (int i = 0; i < 100; ++i) {
location = new Location("gps"); location = new Location("gps");
location.setLatitude(20 + i / 2); location.setLatitude(20 + i / 2);
location.setLongitude(150 - i); location.setLongitude(150 - i);
myTracksOverlay.addLocation(location); mockMyTracksOverlay.addLocation(location);
} }
// Shadow. // Shadow.
myTracksOverlay.draw(canvas, mockView, true); mockMyTracksOverlay.draw(canvas, mapView, true);
// We don't expect to do anything if // We don't expect to do anything if
assertNull(myTracksOverlay.getTrackPathPainter().getLastPath()); assertFalse(mockMyTracksOverlay.getTrackPathPainter().hasPath());
assertEquals(40, myTracksOverlay.getNumWaypoints()); assertEquals(40, mockMyTracksOverlay.getNumWaypoints());
assertEquals(100, myTracksOverlay.getNumLocations()); assertEquals(100, mockMyTracksOverlay.getNumLocations());
// No shadow. // No shadow.
myTracksOverlay.draw(canvas, mockView, false); mockMyTracksOverlay.draw(canvas, mapView, false);
assertNotNull(myTracksOverlay.getTrackPathPainter().getLastPath()); assertTrue(mockMyTracksOverlay.getTrackPathPainter().hasPath());
assertTrue(myTracksOverlay.getTrackPathPainter().getLastPath() instanceof MockPath); SingleColorTrackPathPainter trackPathPainter = (SingleColorTrackPathPainter) mockMyTracksOverlay
MockPath path = (MockPath) myTracksOverlay.getTrackPathPainter().getLastPath(); .getTrackPathPainter();
assertEquals(40, myTracksOverlay.getNumWaypoints()); MockPath path = (MockPath) trackPathPainter.getPath();
assertEquals(100, myTracksOverlay.getNumLocations()); assertEquals(40, mockMyTracksOverlay.getNumWaypoints());
assertEquals(100, path.totalPoints); assertEquals(100, mockMyTracksOverlay.getNumLocations());
// TODO: Check the points from the path (and the segments). assertEquals(100, path.getTotalPoints());
} }
} }
@@ -22,30 +22,28 @@ import android.content.Context;
import android.graphics.Rect; import android.graphics.Rect;
/** /**
* Elements for Tests for the MyTracks map overlay. * A mock {@link MapOverlay} for testing.
* *
* @author Bartlomiej Niechwiej * @author Bartlomiej Niechwiej
* @author Vangelis S. * @author Vangelis S.
* *
* A mock version of {@code MapOverlay} that does not use
* {@class MapView}.
*/ */
public class MockMyTracksOverlay extends MapOverlay { public class MockMyTracksOverlay extends MapOverlay {
private Projection mockProjection; private Projection projection;
public MockMyTracksOverlay(Context context) { public MockMyTracksOverlay(Context context) {
super(context); super(context);
mockProjection = new MockProjection(); projection = new MockProjection();
} }
@Override @Override
public Projection getMapProjection(MapView mapView) { public Projection getMapProjection(MapView mapView) {
return mockProjection; return projection;
} }
@Override @Override
public Rect getMapViewRect(MapView mapView) { public Rect getMapViewRect(MapView mapView) {
return new Rect(0, 0, 100, 100); return new Rect(0, 0, (int) (100 * 1E6), (int) (100 * 1E6));
} }
} }
@@ -26,20 +26,19 @@ import java.util.List;
import junit.framework.Assert; import junit.framework.Assert;
/** /**
* Elements for Tests for the MyTracks map overlay. * A mock {@link Path}. Records calls to {@link Path#lineTo(float, float)} and
* {@link Path#moveTo(float, float)}.
* *
* @author Bartlomiej Niechwiej * @author Bartlomiej Niechwiej
* @author Vangelis S. * @author Vangelis S.
*
* A mock class that intercepts {@code Path}'s and records calls to
* {@code #moveTo()} and {@code #lineTo()}.
*/ */
public class MockPath extends Path { public class MockPath extends Path {
/** A list of disjoined path segments. */ // A list of disjointed path segments.
public final List<List<PointF>> segments = new LinkedList<List<PointF>>(); private final List<List<PointF>> segments = new LinkedList<List<PointF>>();
/** The total number of points in this path. */
public int totalPoints; // The total number of points in this path.
private int totalPoints;
private List<PointF> currentSegment; private List<PointF> currentSegment;
@Override @Override
@@ -53,8 +52,16 @@ public class MockPath extends Path {
@Override @Override
public void moveTo(float x, float y) { public void moveTo(float x, float y) {
super.moveTo(x, y); super.moveTo(x, y);
segments.add(currentSegment = currentSegment = new ArrayList<PointF>(Arrays.asList(new PointF(x, y)));
new ArrayList<PointF>(Arrays.asList(new PointF(x, y)))); segments.add(currentSegment);
totalPoints++; totalPoints++;
} }
/**
* Gets the total number of points in this path.
*/
public int getTotalPoints() {
return totalPoints;
}
} }
@@ -13,6 +13,7 @@
* License for the specific language governing permissions and limitations under * License for the specific language governing permissions and limitations under
* the License. * the License.
*/ */
package com.google.android.apps.mytracks; package com.google.android.apps.mytracks;
import com.google.android.maps.GeoPoint; import com.google.android.maps.GeoPoint;
@@ -21,18 +22,17 @@ import com.google.android.maps.Projection;
import android.graphics.Point; import android.graphics.Point;
/** /**
* Elements for Tests for the MyTracks map overlay. * A mock {@link Projection}. Acts as an identity matrix.
* *
* @author Bartlomiej Niechwiej * @author Bartlomiej Niechwiej
* @author Vangelis S. * @author Vangelis S.
* *
* A mock {@code Projection} that acts as the identity matrix.
*/ */
public class MockProjection implements Projection { public class MockProjection implements Projection {
@Override @Override
public Point toPixels(GeoPoint in, Point out) { public Point toPixels(GeoPoint geoPoint, Point point) {
return out; return point;
} }
@Override @Override
@@ -51,6 +51,9 @@ public class MyTracksProviderUtilsImplTest extends AndroidTestCase {
private static final String MOCK_DESC = "Mock Next Waypoint Desc!"; private static final String MOCK_DESC = "Mock Next Waypoint Desc!";
private static final String TEST_DESC = "Test Desc!"; private static final String TEST_DESC = "Test Desc!";
private static final String TEST_DESC_NEW = "Test Desc new!"; private static final String TEST_DESC_NEW = "Test Desc new!";
private double INITIAL_LATITUDE = 37.0;
private double INITIAL_LONGITUDE = -57.0;
private double ALTITUDE_INTERVAL = 2.5;
@Override @Override
protected void setUp() throws Exception { protected void setUp() throws Exception {
@@ -273,13 +276,13 @@ public class MyTracksProviderUtilsImplTest extends AndroidTestCase {
// Check whether all have been deleted. // Check whether all have been deleted.
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null,
TracksColumns._ID); TracksColumns._ID);
assertTrue(tracksCursor.getCount() == 0); assertEquals(0, tracksCursor.getCount());
tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI, null, null, tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI, null, null,
null, TrackPointsColumns._ID); null, TrackPointsColumns._ID);
assertTrue(tracksPointsCursor.getCount() == 0); assertEquals(0, tracksPointsCursor.getCount());
waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null,
null, WaypointsColumns._ID); null, WaypointsColumns._ID);
assertTrue(waypointCursor.getCount() == 0); assertEquals(0, waypointCursor.getCount());
} }
/** /**
@@ -328,9 +331,7 @@ public class MyTracksProviderUtilsImplTest extends AndroidTestCase {
@UsesMocks(Cursor.class) @UsesMocks(Cursor.class)
public void testCreateTrack() { public void testCreateTrack() {
Cursor cursorMock = AndroidMock.createNiceMock(Cursor.class); Cursor cursorMock = AndroidMock.createNiceMock(Cursor.class);
// The startColumnIndex can be any value, just make it to 2 in case it's int startColumnIndex = 1;
// hard coded to 0 or 1 in some where.
int startColumnIndex = 2;
int columnIndex = startColumnIndex; int columnIndex = startColumnIndex;
AndroidMock.expect(cursorMock.getColumnIndexOrThrow(TracksColumns._ID)) AndroidMock.expect(cursorMock.getColumnIndexOrThrow(TracksColumns._ID))
.andReturn(columnIndex++); .andReturn(columnIndex++);
@@ -359,7 +360,7 @@ public class MyTracksProviderUtilsImplTest extends AndroidTestCase {
* Tests the method {@link MyTracksProviderUtilsImpl#createContentValues(Track)}. * Tests the method {@link MyTracksProviderUtilsImpl#createContentValues(Track)}.
*/ */
@UsesMocks(TripStatistics.class) @UsesMocks(TripStatistics.class)
public void testCreateContentValues_Track() { public void testCreateContentValues_track() {
// ID // ID
long trackId = System.currentTimeMillis(); long trackId = System.currentTimeMillis();
// Name // Name
@@ -645,9 +646,7 @@ public class MyTracksProviderUtilsImplTest extends AndroidTestCase {
@UsesMocks(Cursor.class) @UsesMocks(Cursor.class)
public void testCreateWaypoint() { public void testCreateWaypoint() {
Cursor cursorMock = AndroidMock.createNiceMock(Cursor.class); Cursor cursorMock = AndroidMock.createNiceMock(Cursor.class);
// The startColumnIndex can be any value, just make it to 2 in case it's int startColumnIndex = 1;
// hard coded to 0 or 1 in some where.
int startColumnIndex = 2;
int columnIndex = startColumnIndex; int columnIndex = startColumnIndex;
AndroidMock.expect(cursorMock.getColumnIndexOrThrow(WaypointsColumns._ID)) AndroidMock.expect(cursorMock.getColumnIndexOrThrow(WaypointsColumns._ID))
.andReturn(columnIndex++); .andReturn(columnIndex++);
@@ -683,7 +682,7 @@ public class MyTracksProviderUtilsImplTest extends AndroidTestCase {
/** /**
* Tests the method {@link MyTracksProviderUtilsImpl#createContentValues(Waypoint)}. * Tests the method {@link MyTracksProviderUtilsImpl#createContentValues(Waypoint)}.
*/ */
public void testCreateContentValues_Waypoint() { public void testCreateContentValues_waypoint() {
long trackId = System.currentTimeMillis(); long trackId = System.currentTimeMillis();
Track track = getTrack(trackId, 10); Track track = getTrack(trackId, 10);
providerUtils.insertTrack(track); providerUtils.insertTrack(track);
@@ -819,10 +818,10 @@ public class MyTracksProviderUtilsImplTest extends AndroidTestCase {
LocationIterator locationIterator = providerUtils.getLocationIterator(trackId, LocationIterator locationIterator = providerUtils.getLocationIterator(trackId,
startTrackPointId, true, MyTracksProviderUtils.DEFAULT_LOCATION_FACTORY); startTrackPointId, true, MyTracksProviderUtils.DEFAULT_LOCATION_FACTORY);
assertEquals(startTrackPointId, locationIterator.getLocationId());
for (int i = 1; i >= 0; i--) { for (int i = 1; i >= 0; i--) {
assertTrue(locationIterator.hasNext()); assertTrue(locationIterator.hasNext());
Location location = locationIterator.next(); Location location = locationIterator.next();
assertEquals(2 + (i - 1), locationIterator.getLocationId());
checkLocation(i, location); checkLocation(i, location);
} }
assertFalse(locationIterator.hasNext()); assertFalse(locationIterator.hasNext());
@@ -843,10 +842,11 @@ public class MyTracksProviderUtilsImplTest extends AndroidTestCase {
LocationIterator locationIterator = providerUtils.getLocationIterator(trackId, LocationIterator locationIterator = providerUtils.getLocationIterator(trackId,
startTrackPointId, false, MyTracksProviderUtils.DEFAULT_LOCATION_FACTORY); startTrackPointId, false, MyTracksProviderUtils.DEFAULT_LOCATION_FACTORY);
assertEquals(startTrackPointId, locationIterator.getLocationId());
for (int i = 1; i < 10; i++) { for (int i = 1; i < 10; i++) {
assertTrue(locationIterator.hasNext()); assertTrue(locationIterator.hasNext());
Location location = locationIterator.next(); Location location = locationIterator.next();
assertEquals(2 + (i - 1), locationIterator.getLocationId());
checkLocation(i, location); checkLocation(i, location);
} }
assertFalse(locationIterator.hasNext()); assertFalse(locationIterator.hasNext());
@@ -871,6 +871,7 @@ public class MyTracksProviderUtilsImplTest extends AndroidTestCase {
* test also covers the method * test also covers the method
* {@link MyTracksProviderUtilsImpl#createContentValues(Location, long)}. * {@link MyTracksProviderUtilsImpl#createContentValues(Location, long)}.
*/ */
@SuppressWarnings("javadoc")
public void testInsertTrackPoint() { public void testInsertTrackPoint() {
// Insert track, point at first. // Insert track, point at first.
long trackId = System.currentTimeMillis(); long trackId = System.currentTimeMillis();
@@ -888,6 +889,7 @@ public class MyTracksProviderUtilsImplTest extends AndroidTestCase {
* {@link MyTracksProviderUtilsImpl#createContentValues(Location, long)} and * {@link MyTracksProviderUtilsImpl#createContentValues(Location, long)} and
* the inner class {@link MyTracksProviderUtilsImpl.CachedTrackPointsIndexes}. * the inner class {@link MyTracksProviderUtilsImpl.CachedTrackPointsIndexes}.
*/ */
@SuppressWarnings("javadoc")
public void testBulkInsertTrackPoints() { public void testBulkInsertTrackPoints() {
// Insert track, point at first. // Insert track, point at first.
long trackId = System.currentTimeMillis(); long trackId = System.currentTimeMillis();
@@ -908,6 +910,7 @@ public class MyTracksProviderUtilsImplTest extends AndroidTestCase {
* and the inner class * and the inner class
* {@link MyTracksProviderUtilsImpl.CachedTrackPointsIndexes}. * {@link MyTracksProviderUtilsImpl.CachedTrackPointsIndexes}.
*/ */
@SuppressWarnings("javadoc")
@UsesMocks(Cursor.class) @UsesMocks(Cursor.class)
public void testCreateLocation() { public void testCreateLocation() {
Cursor cursorMock = AndroidMock.createNiceMock(Cursor.class); Cursor cursorMock = AndroidMock.createNiceMock(Cursor.class);
@@ -984,7 +987,7 @@ public class MyTracksProviderUtilsImplTest extends AndroidTestCase {
track.setId(id); track.setId(id);
track.setName("Test: " + id); track.setName("Test: " + id);
track.setNumberOfPoints(numPoints); track.setNumberOfPoints(numPoints);
for(int i=0; i < numPoints; i++) { for(int i = 0; i < numPoints; i++) {
track.addLocation(createLocation(i)); track.addLocation(createLocation(i));
} }
return track; return track;
@@ -996,26 +999,27 @@ public class MyTracksProviderUtilsImplTest extends AndroidTestCase {
* @return created location * @return created location
*/ */
private Location createLocation(int i) { private Location createLocation(int i) {
Location loc = new Location("test"); Location loc = new Location("test");
loc.setLatitude(37.0 + (double) i / 10000.0); loc.setLatitude(INITIAL_LATITUDE + (double) i / 10000.0);
loc.setLongitude(57.0 - (double) i / 10000.0); loc.setLongitude(INITIAL_LONGITUDE - (double) i / 10000.0);
loc.setAccuracy((float) i / 100.0f); loc.setAccuracy((float) i / 100.0f);
loc.setAltitude(i * 2.5); loc.setAltitude(i * ALTITUDE_INTERVAL);
return loc; return loc;
} }
/** /**
* Checks the value of a location. * Checks the value of a location.
* *
* @param i the index of this location which created in the method {@link * @param i the index of this location which created in the method
* this#getTrack(long, int)} * {@link MyTracksProviderUtilsImplTest#getTrack(long, int)}
* @param location the location to be checked * @param location the location to be checked
*/ */
private void checkLocation(int i, Location location) { private void checkLocation(int i, Location location) {
assertEquals(37.0 + (double) i / 10000.0, location.getLatitude()); assertEquals(INITIAL_LATITUDE + (double) i / 10000.0, location.getLatitude());
assertEquals(57.0 - (double) i / 10000.0, location.getLongitude()); assertEquals(INITIAL_LONGITUDE - (double) i / 10000.0, location.getLongitude());
assertEquals((float) i / 100.0f, location.getAccuracy()); assertEquals((float) i / 100.0f, location.getAccuracy());
assertEquals(i * 2.5, location.getAltitude()); assertEquals(i * ALTITUDE_INTERVAL, location.getAltitude());
} }
/** /**
File diff suppressed because it is too large Load Diff
@@ -275,17 +275,18 @@ public class CreateAndSendTrackTest extends ActivityInstrumentationTestCase2<Tra
EndToEndTestUtils.startRecording(); EndToEndTestUtils.startRecording();
EndToEndTestUtils.findMenuItem(activityMyTracks.getString(R.string.menu_split_frequency), true); EndToEndTestUtils.findMenuItem(activityMyTracks.getString(R.string.menu_split_frequency), true);
boolean isFoundKM = EndToEndTestUtils.SOLO.searchText(EndToEndTestUtils.ONE_KM); boolean isFoundKM = EndToEndTestUtils.SOLO.searchText(EndToEndTestUtils.KM);
if(isFoundKM) { if (isFoundKM) {
EndToEndTestUtils.SOLO.clickOnText(EndToEndTestUtils.ONE_KM); EndToEndTestUtils.SOLO.clickOnText(EndToEndTestUtils.KM, 0);
} else { } else {
EndToEndTestUtils.SOLO.clickOnText(EndToEndTestUtils.ONE_MILE); EndToEndTestUtils.SOLO.clickOnText(EndToEndTestUtils.MILE, 0);
} }
EndToEndTestUtils.getButtonOnScreen(activityMyTracks.getString(R.string.generic_ok), true, true);
EndToEndTestUtils
.getButtonOnScreen(activityMyTracks.getString(R.string.generic_ok), true, true);
// Send Gps to give a distance more than one kilometer or one mile. // Send Gps to give a distance more than one kilometer or one mile.
EndToEndTestUtils.sendGps(20); EndToEndTestUtils.sendGps(20);
EndToEndTestUtils.findMenuItem(activityMyTracks.getString(R.string.menu_markers), true); EndToEndTestUtils.findMenuItem(activityMyTracks.getString(R.string.menu_markers), true);
instrumentation.waitForIdleSync(); instrumentation.waitForIdleSync();
assertTrue(EndToEndTestUtils.SOLO.getCurrentListViews().get(0).getCount() > 0); assertTrue(EndToEndTestUtils.SOLO.getCurrentListViews().get(0).getCount() > 0);
@@ -77,18 +77,20 @@ public class EndToEndTestUtils {
// Following is some check strings in English and Chinese // Following is some check strings in English and Chinese
private static final HashMap<String, String> RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL = new HashMap<String, String>(); private static final HashMap<String, String> RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL = new HashMap<String, String>();
private static final HashMap<String, String> VIEW_MODE_MULTILINGUAL = new HashMap<String, String>(); private static final HashMap<String, String> VIEW_MODE_MULTILINGUAL = new HashMap<String, String>();
private static final HashMap<String, String> ONE_KM_MULTILINGUAL = new HashMap<String, String>(); private static final HashMap<String, String> KM_MULTILINGUAL = new HashMap<String, String>();
private static final HashMap<String, String> ONE_MILE_MULTILINGUAL = new HashMap<String, String>(); private static final HashMap<String, String> MILE_MULTILINGUAL = new HashMap<String, String>();
public static String RELATIVE_STARTTIME_POSTFIX = ""; public static String RELATIVE_STARTTIME_POSTFIX = "";
public static String VIEW_MODE = ""; public static String VIEW_MODE = "";
public static String ONE_KM = ""; public static String KM = "";
public static String ONE_MILE = ""; public static String MILE = "";
public static int SHORT_WAIT_TIME = 2000; public static int SHORT_WAIT_TIME = 2000;
public static int NORMAL_WAIT_TIME = 8000; public static int NORMAL_WAIT_TIME = 8000;
public static int LONG_WAIT_TIME = 15000; public static int LONG_WAIT_TIME = 15000;
public static int SUPER_LONG_WAIT_TIME = 100000; public static int SUPER_LONG_WAIT_TIME = 100000;
public static String deviceLanguage = "";
static { static {
RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL.put("es", "mins ago"); RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL.put("es", "mins ago");
RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL.put("de", "Minuten"); RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL.put("de", "Minuten");
@@ -102,17 +104,17 @@ public class EndToEndTestUtils {
VIEW_MODE_MULTILINGUAL.put("ar", "وضع"); VIEW_MODE_MULTILINGUAL.put("ar", "وضع");
VIEW_MODE_MULTILINGUAL.put("zh", "模式"); VIEW_MODE_MULTILINGUAL.put("zh", "模式");
ONE_KM_MULTILINGUAL.put("es", "1 km"); KM_MULTILINGUAL.put("es", "km");
ONE_KM_MULTILINGUAL.put("de", "1 km"); KM_MULTILINGUAL.put("de", "km");
ONE_KM_MULTILINGUAL.put("fr", "1 km"); KM_MULTILINGUAL.put("fr", "km");
ONE_KM_MULTILINGUAL.put("ar", "١ كم"); KM_MULTILINGUAL.put("ar", "كم");
ONE_KM_MULTILINGUAL.put("zh", "1 公里"); KM_MULTILINGUAL.put("zh", "公里");
ONE_MILE_MULTILINGUAL.put("es", "1 mi"); MILE_MULTILINGUAL.put("es", "mi");
ONE_MILE_MULTILINGUAL.put("de", "1 mi"); MILE_MULTILINGUAL.put("de", "mi");
ONE_MILE_MULTILINGUAL.put("fr", "1 mile"); MILE_MULTILINGUAL.put("fr", "mile");
ONE_MILE_MULTILINGUAL.put("ar", "١ ميل"); MILE_MULTILINGUAL.put("ar", "ميل");
ONE_MILE_MULTILINGUAL.put("zh", "1 英里"); MILE_MULTILINGUAL.put("zh", "英里");
} }
static Solo SOLO; static Solo SOLO;
@@ -130,17 +132,17 @@ public class EndToEndTestUtils {
* Checks the language, then sets the fields with right string. * Checks the language, then sets the fields with right string.
*/ */
private static void checkLanguage() { private static void checkLanguage() {
String deviceLanguage = instrumentation.getContext().getResources().getConfiguration().locale.getLanguage(); deviceLanguage = instrumentation.getContext().getResources().getConfiguration().locale.getLanguage();
if (RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL.get(deviceLanguage) != null) { if (RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL.get(deviceLanguage) != null) {
RELATIVE_STARTTIME_POSTFIX = RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL.get(deviceLanguage); RELATIVE_STARTTIME_POSTFIX = RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL.get(deviceLanguage);
VIEW_MODE = VIEW_MODE_MULTILINGUAL.get(deviceLanguage); VIEW_MODE = VIEW_MODE_MULTILINGUAL.get(deviceLanguage);
ONE_KM = ONE_KM_MULTILINGUAL.get(deviceLanguage); KM = KM_MULTILINGUAL.get(deviceLanguage);
ONE_MILE = ONE_MILE_MULTILINGUAL.get(deviceLanguage); MILE = MILE_MULTILINGUAL.get(deviceLanguage);
} else { } else {
RELATIVE_STARTTIME_POSTFIX = RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL.get("es"); RELATIVE_STARTTIME_POSTFIX = RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL.get("es");
VIEW_MODE = VIEW_MODE_MULTILINGUAL.get("es"); VIEW_MODE = VIEW_MODE_MULTILINGUAL.get("es");
ONE_KM = ONE_KM_MULTILINGUAL.get("es"); KM = KM_MULTILINGUAL.get("es");
ONE_MILE = ONE_MILE_MULTILINGUAL.get("es"); MILE = MILE_MULTILINGUAL.get("es");
} }
} }
@@ -57,35 +57,6 @@ public class DynamicSpeedTrackPathDescriptorTest extends AndroidTestCase {
} }
} }
/**
* Tests {@link DynamicSpeedTrackPathDescriptor#onSharedPreferenceChanged(SharedPreferences,
* String)} when the key is null.
*/
public void testOnSharedPreferenceChanged_nullKey() {
DynamicSpeedTrackPathDescriptor dynamicSpeedTrackPathDescriptor = new DynamicSpeedTrackPathDescriptor(
context);
int speedMargin = dynamicSpeedTrackPathDescriptor.getSpeedMargin();
// Change value in shared preferences.
PreferencesUtils.setInt(context, R.string.track_color_mode_percentage_key, speedMargin + 2);
dynamicSpeedTrackPathDescriptor.onSharedPreferenceChanged(sharedPreferences, null);
assertEquals(speedMargin, dynamicSpeedTrackPathDescriptor.getSpeedMargin());
}
/**
* Tests {@link DynamicSpeedTrackPathDescriptor#onSharedPreferenceChanged(SharedPreferences,
* String)} when the key is not null, and not trackColorModeDynamicVariation.
*/
public void testOnSharedPreferenceChanged_otherKey() {
DynamicSpeedTrackPathDescriptor dynamicSpeedTrackPathDescriptor = new DynamicSpeedTrackPathDescriptor(
context);
int speedMargin = dynamicSpeedTrackPathDescriptor.getSpeedMargin();
// Change value in shared preferences.
PreferencesUtils.setInt(context, R.string.track_color_mode_percentage_key, speedMargin + 2);
dynamicSpeedTrackPathDescriptor.onSharedPreferenceChanged(sharedPreferences, "anyKey");
assertEquals(speedMargin, dynamicSpeedTrackPathDescriptor.getSpeedMargin());
}
/** /**
* Tests {@link DynamicSpeedTrackPathDescriptor#onSharedPreferenceChanged(SharedPreferences, * Tests {@link DynamicSpeedTrackPathDescriptor#onSharedPreferenceChanged(SharedPreferences,
* String)} when the key is trackColorModeDynamicVariation. * String)} when the key is trackColorModeDynamicVariation.
@@ -120,7 +91,7 @@ public class DynamicSpeedTrackPathDescriptorTest extends AndroidTestCase {
} }
/** /**
* Tests {@link DynamicSpeedTrackPathDescriptor#needsRedraw()} by wrong track * Tests {@link DynamicSpeedTrackPathDescriptor#updateState()} by wrong track
* id. * id.
*/ */
public void testNeedsRedraw_WrongTrackId() { public void testNeedsRedraw_WrongTrackId() {
@@ -128,11 +99,11 @@ public class DynamicSpeedTrackPathDescriptorTest extends AndroidTestCase {
context, R.string.selected_track_id_key, PreferencesUtils.SELECTED_TRACK_ID_DEFAULT); context, R.string.selected_track_id_key, PreferencesUtils.SELECTED_TRACK_ID_DEFAULT);
DynamicSpeedTrackPathDescriptor dynamicSpeedTrackPathDescriptor = new DynamicSpeedTrackPathDescriptor( DynamicSpeedTrackPathDescriptor dynamicSpeedTrackPathDescriptor = new DynamicSpeedTrackPathDescriptor(
context); context);
assertEquals(false, dynamicSpeedTrackPathDescriptor.needsRedraw()); assertEquals(false, dynamicSpeedTrackPathDescriptor.updateState());
} }
/** /**
* Tests {@link DynamicSpeedTrackPathDescriptor#needsRedraw()} by different * Tests {@link DynamicSpeedTrackPathDescriptor#updateState()} by different
* averageMovingSpeed. * averageMovingSpeed.
*/ */
public void testIsDiffereceSignificant() { public void testIsDiffereceSignificant() {
@@ -145,8 +116,7 @@ public class DynamicSpeedTrackPathDescriptorTest extends AndroidTestCase {
// Difference is more than CRITICAL_DIFFERENCE_PERCENTAGE // Difference is more than CRITICAL_DIFFERENCE_PERCENTAGE
30 * (1 + (DynamicSpeedTrackPathDescriptor.CRITICAL_DIFFERENCE_PERCENTAGE / 100.00) * 2) }; 30 * (1 + (DynamicSpeedTrackPathDescriptor.CRITICAL_DIFFERENCE_PERCENTAGE / 100.00) * 2) };
boolean[] expectedValues = { true, false, false, true }; boolean[] expectedValues = { true, false, false, true };
double[] expectedAverageMovingSpeed = { 20, 30, 30, double[] expectedAverageMovingSpeed = { 0, 30, 30, 30 };
30 * (1 + (DynamicSpeedTrackPathDescriptor.CRITICAL_DIFFERENCE_PERCENTAGE / 100.00) * 2) };
// Test // Test
for (int i = 0; i < newAverageMovingSpeed.length; i++) { for (int i = 0; i < newAverageMovingSpeed.length; i++) {
dynamicSpeedTrackPathDescriptor.setAverageMovingSpeed(averageMovingSpeeds[i]); dynamicSpeedTrackPathDescriptor.setAverageMovingSpeed(averageMovingSpeeds[i]);
@@ -62,7 +62,7 @@ public class DynamicSpeedTrackPathPainterTest extends TrackPathPainterTestCase {
public void testUpdatePath_AllInvalidLocation() { public void testUpdatePath_AllInvalidLocation() {
List<CachedLocation> points = createCachedLocations(NUMBER_OF_LOCATIONS, INVALID_LATITUDE, -1); List<CachedLocation> points = createCachedLocations(NUMBER_OF_LOCATIONS, INVALID_LATITUDE, -1);
dynamicSpeedTrackPathPainter.updatePath(myTracksOverlay.getMapProjection(mockView), dynamicSpeedTrackPathPainter.updatePath(myTracksOverlay.getMapProjection(mockView),
myTracksOverlay.getMapViewRect(mockView), 1, true, points); myTracksOverlay.getMapViewRect(mockView), 1, points);
AndroidMock.verify(trackPathDescriptor); AndroidMock.verify(trackPathDescriptor);
// Should be zero for there is no valid locations. // Should be zero for there is no valid locations.
assertEquals(0, dynamicSpeedTrackPathPainter.getColoredPaths().size()); assertEquals(0, dynamicSpeedTrackPathPainter.getColoredPaths().size());
@@ -80,7 +80,7 @@ public class DynamicSpeedTrackPathPainterTest extends TrackPathPainterTestCase {
// Gets a number as the start index of points. // Gets a number as the start index of points.
int startLocationIdx = NUMBER_OF_LOCATIONS / 2; int startLocationIdx = NUMBER_OF_LOCATIONS / 2;
dynamicSpeedTrackPathPainter.updatePath(myTracksOverlay.getMapProjection(mockView), dynamicSpeedTrackPathPainter.updatePath(myTracksOverlay.getMapProjection(mockView),
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, true, points); myTracksOverlay.getMapViewRect(mockView), startLocationIdx, points);
AndroidMock.verify(trackPathDescriptor); AndroidMock.verify(trackPathDescriptor);
assertEquals(NUMBER_OF_LOCATIONS - startLocationIdx, dynamicSpeedTrackPathPainter assertEquals(NUMBER_OF_LOCATIONS - startLocationIdx, dynamicSpeedTrackPathPainter
@@ -123,7 +123,7 @@ public class DynamicSpeedTrackPathPainterTest extends TrackPathPainterTestCase {
slowSpeed)); slowSpeed));
dynamicSpeedTrackPathPainter.updatePath(myTracksOverlay.getMapProjection(mockView), dynamicSpeedTrackPathPainter.updatePath(myTracksOverlay.getMapProjection(mockView),
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, true, points); myTracksOverlay.getMapViewRect(mockView), startLocationIdx, points);
AndroidMock.verify(trackPathDescriptor); AndroidMock.verify(trackPathDescriptor);
List<ColoredPath> coloredPath = dynamicSpeedTrackPathPainter.getColoredPaths(); List<ColoredPath> coloredPath = dynamicSpeedTrackPathPainter.getColoredPaths();
@@ -31,9 +31,10 @@ import java.util.List;
*/ */
public class SingleColorTrackPathPainterTest extends TrackPathPainterTestCase { public class SingleColorTrackPathPainterTest extends TrackPathPainterTestCase {
private static final int NUMBER_OF_LOCATIONS = 10;
private SingleColorTrackPathPainter singleColorTrackPathPainter; private SingleColorTrackPathPainter singleColorTrackPathPainter;
private Path pathMock; private Path pathMock;
private static final int NUMBER_OF_LOCATIONS = 100;
/** /**
* Initials a mocked TrackPathDescriptor object and * Initials a mocked TrackPathDescriptor object and
@@ -44,8 +45,8 @@ public class SingleColorTrackPathPainterTest extends TrackPathPainterTestCase {
protected void setUp() throws Exception { protected void setUp() throws Exception {
super.setUp(); super.setUp();
pathMock = AndroidMock.createStrictMock(Path.class);
singleColorTrackPathPainter = new SingleColorTrackPathPainter(getContext()); singleColorTrackPathPainter = new SingleColorTrackPathPainter(getContext());
pathMock = AndroidMock.createStrictMock(Path.class);
} }
/** /**
@@ -54,19 +55,20 @@ public class SingleColorTrackPathPainterTest extends TrackPathPainterTestCase {
* method when all locations are valid. * method when all locations are valid.
*/ */
public void testUpdatePath_AllValidLocation() { public void testUpdatePath_AllValidLocation() {
pathMock.incReserve(NUMBER_OF_LOCATIONS);
List<CachedLocation> points = createCachedLocations(NUMBER_OF_LOCATIONS,
TrackStubUtils.INITIAL_LATITUDE, -1);
// Gets a number as the start index of points. // Gets a number as the start index of points.
int startLocationIdx = NUMBER_OF_LOCATIONS / 2; int startLocationIdx = NUMBER_OF_LOCATIONS / 2;
pathMock.incReserve(NUMBER_OF_LOCATIONS - startLocationIdx);
List<CachedLocation> points = createCachedLocations(NUMBER_OF_LOCATIONS,
TrackStubUtils.INITIAL_LATITUDE, -1);
for (int i = startLocationIdx; i < NUMBER_OF_LOCATIONS; i++) { for (int i = startLocationIdx; i < NUMBER_OF_LOCATIONS; i++) {
pathMock.lineTo(0, 0); pathMock.lineTo(0, 0);
} }
AndroidMock.replay(pathMock); AndroidMock.replay(pathMock);
singleColorTrackPathPainter.updatePath(myTracksOverlay.getMapProjection(mockView), singleColorTrackPathPainter.updatePath(myTracksOverlay.getMapProjection(mockView),
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, true, points, pathMock); myTracksOverlay.getMapViewRect(mockView), startLocationIdx, points, pathMock);
AndroidMock.verify(pathMock); AndroidMock.verify(pathMock);
} }
@@ -76,20 +78,20 @@ public class SingleColorTrackPathPainterTest extends TrackPathPainterTestCase {
* method when all locations are invalid. * method when all locations are invalid.
*/ */
public void testUpdatePath_AllInvalidLocation() { public void testUpdatePath_AllInvalidLocation() {
pathMock.incReserve(NUMBER_OF_LOCATIONS); int startLocationIdx = NUMBER_OF_LOCATIONS / 2;
pathMock.incReserve(NUMBER_OF_LOCATIONS - startLocationIdx);
List<CachedLocation> points = createCachedLocations(NUMBER_OF_LOCATIONS, INVALID_LATITUDE, -1); List<CachedLocation> points = createCachedLocations(NUMBER_OF_LOCATIONS, INVALID_LATITUDE, -1);
// Gets a random number from 1 to numberOfLocations. // Gets a random number from 1 to numberOfLocations.
int startLocationIdx = NUMBER_OF_LOCATIONS / 2;
AndroidMock.replay(pathMock); AndroidMock.replay(pathMock);
singleColorTrackPathPainter.updatePath(myTracksOverlay.getMapProjection(mockView), singleColorTrackPathPainter.updatePath(myTracksOverlay.getMapProjection(mockView),
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, true, points, pathMock); myTracksOverlay.getMapViewRect(mockView), startLocationIdx, points, pathMock);
AndroidMock.verify(pathMock); AndroidMock.verify(pathMock);
} }
/** /**
* Tests the * Tests the {@link SingleColorTrackPathPainter#updatePath(com.google.android.maps.Projection,
* {@link SingleColorTrackPathPainter#updatePath(com.google.android.maps.Projection, android.graphics.Rect, int, Boolean, List, Path)} * android.graphics.Rect, int, Boolean, List, Path)} method when there are
* method when there are three segments. * three segments.
*/ */
public void testUpdatePath_ThreeSegments() { public void testUpdatePath_ThreeSegments() {
// First segment. // First segment.
@@ -103,7 +105,7 @@ public class SingleColorTrackPathPainterTest extends TrackPathPainterTestCase {
points.addAll(createCachedLocations(NUMBER_OF_LOCATIONS, TrackStubUtils.INITIAL_LATITUDE, -1)); points.addAll(createCachedLocations(NUMBER_OF_LOCATIONS, TrackStubUtils.INITIAL_LATITUDE, -1));
// Gets a random number from 1 to numberOfLocations. // Gets a random number from 1 to numberOfLocations.
int startLocationIdx = NUMBER_OF_LOCATIONS / 2; int startLocationIdx = NUMBER_OF_LOCATIONS / 2;
pathMock.incReserve(NUMBER_OF_LOCATIONS *3 + 1 +1); pathMock.incReserve(NUMBER_OF_LOCATIONS *3 + 1 +1 - startLocationIdx);
for (int i = 0; i < NUMBER_OF_LOCATIONS - startLocationIdx; i++) { for (int i = 0; i < NUMBER_OF_LOCATIONS - startLocationIdx; i++) {
pathMock.lineTo(0, 0); pathMock.lineTo(0, 0);
} }
@@ -118,7 +120,7 @@ public class SingleColorTrackPathPainterTest extends TrackPathPainterTestCase {
AndroidMock.replay(pathMock); AndroidMock.replay(pathMock);
singleColorTrackPathPainter.updatePath(myTracksOverlay.getMapProjection(mockView), singleColorTrackPathPainter.updatePath(myTracksOverlay.getMapProjection(mockView),
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, true, points, pathMock); myTracksOverlay.getMapViewRect(mockView), startLocationIdx, points, pathMock);
AndroidMock.verify(pathMock); AndroidMock.verify(pathMock);
} }
} }
@@ -40,13 +40,12 @@ public class TrackPathDescriptorDynamicSpeedTest extends TrackPathPainterTestCas
myTracksOverlay.setTrackPathPainter(painter); myTracksOverlay.setTrackPathPainter(painter);
int startLocationIdx = 0; int startLocationIdx = 0;
Boolean alwaysVisible = true;
assertNotNull(painter); assertNotNull(painter);
painter.updatePath(myTracksOverlay.getMapProjection(mockView), painter.updatePath(myTracksOverlay.getMapProjection(mockView),
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, alwaysVisible, myTracksOverlay.getMapViewRect(mockView), startLocationIdx,
myTracksOverlay.getPoints()); myTracksOverlay.getPoints());
assertNotNull(myTracksOverlay.getTrackPathPainter().getLastPath()); assertNotNull(myTracksOverlay.getTrackPathPainter().hasPath());
painter.drawTrack(canvas); painter.drawPath(canvas);
} }
} }
@@ -40,13 +40,11 @@ public class TrackPathDescriptorFixedSpeedTest extends TrackPathPainterTestCase
myTracksOverlay.setTrackPathPainter(painter); myTracksOverlay.setTrackPathPainter(painter);
int startLocationIdx = 0; int startLocationIdx = 0;
Boolean alwaysVisible = true;
assertNotNull(painter); assertNotNull(painter);
painter.updatePath(myTracksOverlay.getMapProjection(mockView), painter.updatePath(myTracksOverlay.getMapProjection(mockView),
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, alwaysVisible, myTracksOverlay.getMapViewRect(mockView), startLocationIdx, myTracksOverlay.getPoints());
myTracksOverlay.getPoints()); assertNotNull(myTracksOverlay.getTrackPathPainter().hasPath());
assertNotNull(myTracksOverlay.getTrackPathPainter().getLastPath()); painter.drawPath(canvas);
painter.drawTrack(canvas);
} }
} }
@@ -55,7 +55,6 @@ public class TrackPathPainterFactoryTest extends TrackPathPainterTestCase {
context, R.string.track_color_mode_key, context.getString(track_color_mode)); context, R.string.track_color_mode_key, context.getString(track_color_mode));
int startLocationIdx = 0; int startLocationIdx = 0;
Boolean alwaysVisible = true;
TrackPathPainter painter = TrackPathPainterFactory.getTrackPathPainter(context); TrackPathPainter painter = TrackPathPainterFactory.getTrackPathPainter(context);
myTracksOverlay.setTrackPathPainter(painter); myTracksOverlay.setTrackPathPainter(painter);
@@ -64,9 +63,9 @@ public class TrackPathPainterFactoryTest extends TrackPathPainterTestCase {
assertTrue(c.isInstance(painter)); assertTrue(c.isInstance(painter));
painter.updatePath(myTracksOverlay.getMapProjection(mockView), painter.updatePath(myTracksOverlay.getMapProjection(mockView),
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, alwaysVisible, myTracksOverlay.getMapViewRect(mockView), startLocationIdx,
myTracksOverlay.getPoints()); myTracksOverlay.getPoints());
assertNotNull(myTracksOverlay.getTrackPathPainter().getLastPath()); assertNotNull(myTracksOverlay.getTrackPathPainter().hasPath());
painter.drawTrack(canvas); painter.drawPath(canvas);
} }
} }
@@ -39,13 +39,12 @@ public class TrackPathPainterSingleColorTest extends TrackPathPainterTestCase {
myTracksOverlay.setTrackPathPainter(painter); myTracksOverlay.setTrackPathPainter(painter);
int startLocationIdx = 0; int startLocationIdx = 0;
Boolean alwaysVisible = true;
assertNotNull(painter); assertNotNull(painter);
painter.updatePath(myTracksOverlay.getMapProjection(mockView), painter.updatePath(myTracksOverlay.getMapProjection(mockView),
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, alwaysVisible, myTracksOverlay.getMapViewRect(mockView), startLocationIdx,
myTracksOverlay.getPoints()); myTracksOverlay.getPoints());
assertNotNull(myTracksOverlay.getTrackPathPainter().getLastPath()); assertNotNull(myTracksOverlay.getTrackPathPainter().hasPath());
painter.drawTrack(canvas); painter.drawPath(canvas);
} }
} }