mirror of
https://codeberg.org/OpenTracksApp/OpenTracks.git
synced 2026-10-02 17:43:06 +02:00
Merge heads
This commit is contained in:
@@ -81,9 +81,6 @@ public class ChartView extends View {
|
||||
private static final int SPACER = 4;
|
||||
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 ArrayList<double[]> chartData = new ArrayList<double[]>();
|
||||
private final ArrayList<Waypoint> waypoints = new ArrayList<Waypoint>();
|
||||
@@ -559,8 +556,8 @@ public class ChartView extends View {
|
||||
float x = getX(getWaypointXValue(waypoint));
|
||||
canvas.drawLine(
|
||||
x, topBorder + spacer + markerHeight / 2, x, topBorder + effectiveHeight, markerPaint);
|
||||
canvas.translate(
|
||||
x - (float) (markerWidth * MARKER_PIN_X_POSITION_PERCENTAGE), topBorder + spacer);
|
||||
canvas.translate(x - (float) (markerWidth * Constants.WAYPOINT_X_OFFSET_PERCENTAGE),
|
||||
topBorder + spacer);
|
||||
if (waypoints.get(i).getType() == Waypoint.TYPE_STATISTICS) {
|
||||
statisticsMarker.draw(canvas);
|
||||
} else {
|
||||
|
||||
@@ -126,6 +126,10 @@ public abstract class Constants {
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package com.google.android.apps.mytracks;
|
||||
|
||||
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.maps.TrackPathPainter;
|
||||
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.LocationUtils;
|
||||
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.Projection;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -50,47 +52,47 @@ import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
/**
|
||||
* A map overlay that displays a "MyLocation" arrow, an error circle, the
|
||||
* currently recording track and optionally a selected track.
|
||||
*
|
||||
* A map overlay that displays my location arrow, error circle, and track info.
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
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 List<Waypoint> waypoints;
|
||||
private final List<CachedLocation> points;
|
||||
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 int lastHeading = 0;
|
||||
private Location myLocation;
|
||||
private boolean showEndMarker = true;
|
||||
// TODO: Remove it completely after completing performance tests.
|
||||
private boolean alwaysVisible = true;
|
||||
private int headingIndex = 0;
|
||||
private Location myLocation;
|
||||
|
||||
private GeoPoint lastReferencePoint;
|
||||
private Rect lastViewRect;
|
||||
private boolean lastPathExists;
|
||||
private TrackPathPainter trackPathPainter;
|
||||
|
||||
/**
|
||||
* Represents a pre-processed {@code Location} to speed up drawing.
|
||||
* This class is more like a data object and doesn't provide accessors.
|
||||
* A pre-processed {@link Location} to speed up drawing.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public static class CachedLocation {
|
||||
public final boolean valid;
|
||||
public final GeoPoint geoPoint;
|
||||
public final int speed;
|
||||
|
||||
|
||||
private final boolean valid;
|
||||
private final GeoPoint geoPoint;
|
||||
private final int speed;
|
||||
|
||||
/**
|
||||
* 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.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) {
|
||||
this.context = context;
|
||||
|
||||
this.waypoints = new ArrayList<Waypoint>();
|
||||
this.points = new ArrayList<CachedLocation>(1024);
|
||||
this.pendingPoints = new ArrayBlockingQueue<CachedLocation>(
|
||||
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();
|
||||
arrows = new Drawable[] {
|
||||
resources.getDrawable(R.drawable.arrow_0),
|
||||
resources.getDrawable(R.drawable.arrow_20),
|
||||
resources.getDrawable(R.drawable.arrow_40),
|
||||
resources.getDrawable(R.drawable.arrow_60),
|
||||
resources.getDrawable(R.drawable.arrow_80),
|
||||
resources.getDrawable(R.drawable.arrow_100),
|
||||
resources.getDrawable(R.drawable.arrow_120),
|
||||
resources.getDrawable(R.drawable.arrow_140),
|
||||
resources.getDrawable(R.drawable.arrow_160),
|
||||
resources.getDrawable(R.drawable.arrow_180),
|
||||
resources.getDrawable(R.drawable.arrow_200),
|
||||
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();
|
||||
arrows = new Drawable[] { resources.getDrawable(R.drawable.arrow_0),
|
||||
resources.getDrawable(R.drawable.arrow_20), resources.getDrawable(R.drawable.arrow_40),
|
||||
resources.getDrawable(R.drawable.arrow_60), resources.getDrawable(R.drawable.arrow_80),
|
||||
resources.getDrawable(R.drawable.arrow_100), resources.getDrawable(R.drawable.arrow_120),
|
||||
resources.getDrawable(R.drawable.arrow_140), resources.getDrawable(R.drawable.arrow_160),
|
||||
resources.getDrawable(R.drawable.arrow_180), resources.getDrawable(R.drawable.arrow_200),
|
||||
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[headingIndex].getIntrinsicWidth();
|
||||
arrowHeight = arrows[headingIndex].getIntrinsicHeight();
|
||||
for (Drawable arrow : arrows) {
|
||||
arrow.setBounds(0, 0, arrowWidth, arrowHeight);
|
||||
}
|
||||
@@ -152,36 +166,36 @@ public class MapOverlay extends Overlay implements OnSharedPreferenceChangeListe
|
||||
markerHeight = statsMarker.getIntrinsicHeight();
|
||||
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.setBounds(0, 0, markerWidth, markerHeight);
|
||||
|
||||
endMarker = resources.getDrawable(R.drawable.red_dot);
|
||||
endMarker.setBounds(0, 0, markerWidth, markerHeight);
|
||||
|
||||
waypointMarker = resources.getDrawable(R.drawable.blue_pushpin);
|
||||
waypointMarker.setBounds(0, 0, markerWidth, markerHeight);
|
||||
|
||||
errorCirclePaint = TrackPathUtilities.getPaint(R.color.blue, context);
|
||||
errorCirclePaint = TrackPathUtils.getPaint(context, R.color.blue);
|
||||
errorCirclePaint.setAlpha(127);
|
||||
|
||||
|
||||
trackPathPainter = TrackPathPainterFactory.getTrackPathPainter(context);
|
||||
|
||||
|
||||
context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE)
|
||||
.registerOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a location to the map overlay.
|
||||
*
|
||||
* <p>
|
||||
* NOTE: This method doesn't take ownership of the given location, so it is
|
||||
* 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) {
|
||||
// Queue up in the pending queue until it's merged with {@code #points}.
|
||||
if (!pendingPoints.offer(new CachedLocation(l))) {
|
||||
Log.e(TAG, "Unable to add pending points");
|
||||
public void addLocation(Location location) {
|
||||
// Queue up in the pendingPoints until it's merged with points.
|
||||
if (!pendingPoints.offer(new CachedLocation(location))) {
|
||||
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.
|
||||
*/
|
||||
public void addSegmentSplit() {
|
||||
// Queue up in the pendingPoints until it's merged with points.
|
||||
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.
|
||||
if (wpt != null && wpt.getLocation() != null) {
|
||||
/**
|
||||
* Clears the locations.
|
||||
*/
|
||||
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) {
|
||||
waypoints.add(wpt);
|
||||
waypoints.add(waypoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getNumLocations() {
|
||||
synchronized (points) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the waypoints.
|
||||
*/
|
||||
public void clearWaypoints() {
|
||||
synchronized (waypoints) {
|
||||
waypoints.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether to draw the track or not.
|
||||
*
|
||||
* @param trackDrawingEnabled true to draw track
|
||||
*/
|
||||
public void setTrackDrawingEnabled(boolean 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) {
|
||||
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
|
||||
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
|
||||
if (shadow) {
|
||||
return;
|
||||
}
|
||||
|
||||
// It's safe to keep projection within a single draw operation.
|
||||
final Projection projection = getMapProjection(mapView);
|
||||
// It's safe to keep projection within a single draw operation
|
||||
Projection projection = getMapProjection(mapView);
|
||||
if (projection == null) {
|
||||
Log.w(TAG, "No projection, unable to draw");
|
||||
Log.w(TAG, "No projection, unable to draw.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the current viewing window.
|
||||
if (trackDrawingEnabled) {
|
||||
// Get the current viewing Rect
|
||||
Rect viewRect = getMapViewRect(mapView);
|
||||
|
||||
// Draw the selected track:
|
||||
// Draw the selected track
|
||||
drawTrack(canvas, projection, viewRect);
|
||||
|
||||
// Draw the "Start" and "End" markers:
|
||||
// Draw the start and end markers
|
||||
drawMarkers(canvas, projection);
|
||||
|
||||
// Draw the waypoints:
|
||||
|
||||
// Draw the waypoints
|
||||
drawWaypoints(canvas, projection);
|
||||
}
|
||||
|
||||
@@ -271,164 +329,19 @@ public class MapOverlay extends Overlay implements OnSharedPreferenceChangeListe
|
||||
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
|
||||
public boolean onTap(GeoPoint p, MapView mapView) {
|
||||
if (p.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
|
||||
// location is equal to the map center,then it was a track ball press with
|
||||
// very high likelihood.
|
||||
public boolean onTap(GeoPoint geoPoint, MapView mapView) {
|
||||
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
|
||||
* location is equal to the map center,then it was a track ball press with
|
||||
* very high likelihood.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
final Location tapLocation = LocationUtils.getLocation(p);
|
||||
double dmin = Double.MAX_VALUE;
|
||||
final Location tapLocation = LocationUtils.getLocation(geoPoint);
|
||||
double minDistance = Double.MAX_VALUE;
|
||||
Waypoint waypoint = null;
|
||||
synchronized (waypoints) {
|
||||
for (int i = 0; i < waypoints.size(); i++) {
|
||||
@@ -436,35 +349,231 @@ public class MapOverlay extends Overlay implements OnSharedPreferenceChangeListe
|
||||
if (waypointLocation == null) {
|
||||
continue;
|
||||
}
|
||||
final double d = waypointLocation.distanceTo(tapLocation);
|
||||
if (d < dmin) {
|
||||
dmin = d;
|
||||
final double distance = waypointLocation.distanceTo(tapLocation);
|
||||
if (distance < minDistance) {
|
||||
minDistance = distance;
|
||||
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)
|
||||
.putExtra(MarkerDetailActivity.EXTRA_MARKER_ID, waypoint.getId());
|
||||
context.startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
return super.onTap(p, mapView);
|
||||
return super.onTap(geoPoint, mapView);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the points
|
||||
*/
|
||||
public List<CachedLocation> getPoints() {
|
||||
return points;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
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)) {
|
||||
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;
|
||||
|
||||
import com.google.android.apps.mytracks.content.TrackDataHub;
|
||||
import com.google.android.apps.mytracks.services.RemoveTempFilesService;
|
||||
import com.google.android.apps.mytracks.util.AnalyticsUtils;
|
||||
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
|
||||
@@ -26,13 +25,11 @@ import android.content.Intent;
|
||||
|
||||
/**
|
||||
* MyTracksApplication for keeping global state.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class MyTracksApplication extends Application {
|
||||
|
||||
private TrackDataHub trackDataHub;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
@@ -43,16 +40,4 @@ public class MyTracksApplication extends Application {
|
||||
Intent intent = new Intent(this, RemoveTempFilesService.class);
|
||||
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)
|
||||
.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, null);
|
||||
trackDataHub = ((MyTracksApplication) getApplication()).getTrackDataHub();
|
||||
trackDataHub.loadTrack(trackId);
|
||||
trackDataHub = TrackDataHub.newInstance(this);
|
||||
|
||||
mapViewContainer = getLayoutInflater().inflate(R.layout.map, null);
|
||||
ApiAdapterFactory.getApiAdapter().disableHardwareAccelerated(mapViewContainer);
|
||||
@@ -167,7 +166,6 @@ public class TrackDetailActivity extends AbstractMyTracksActivity implements Del
|
||||
public void onNewIntent(Intent intent) {
|
||||
setIntent(intent);
|
||||
handleIntent(intent);
|
||||
trackDataHub.loadTrack(trackId);
|
||||
showMarker();
|
||||
}
|
||||
|
||||
@@ -180,6 +178,7 @@ public class TrackDetailActivity extends AbstractMyTracksActivity implements Del
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
trackDataHub.loadTrack(trackId);
|
||||
TrackRecordingServiceConnectionUtils.resume(this, trackRecordingServiceConnection);
|
||||
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() {
|
||||
return mapViewContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link TrackDataHub}.
|
||||
*/
|
||||
public TrackDataHub getTrackDataHub() {
|
||||
return trackDataHub;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public void registerCompassListener(SensorEventListener listener) {
|
||||
Sensor compass = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
|
||||
if (compass == null) {
|
||||
Log.d(TAG, "No compass sensor.");
|
||||
public void registerHeadingListener(SensorEventListener listener) {
|
||||
Sensor heading = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
|
||||
if (heading == null) {
|
||||
Log.d(TAG, "No heading sensor.");
|
||||
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
|
||||
*/
|
||||
public void unregisterCompassListener(SensorEventListener listener) {
|
||||
public void unregisterHeadingListener(SensorEventListener listener) {
|
||||
sensorManager.unregisterListener(listener);
|
||||
}
|
||||
|
||||
|
||||
@@ -133,11 +133,11 @@ public class DataSourceManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for compass changes.
|
||||
* Listener for heading changes.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
private class CompassListener implements SensorEventListener {
|
||||
private class HeadingListener implements SensorEventListener {
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
@@ -174,7 +174,7 @@ public class DataSourceManager {
|
||||
private final WaypointsTableObserver waypointsTableObserver;
|
||||
private final TrackPointsTableObserver trackPointsTableObserver;
|
||||
private final CurrentLocationListener currentLocationListener;
|
||||
private final CompassListener compassListener;
|
||||
private final HeadingListener headingListener;
|
||||
private final PreferenceListener preferenceListener;
|
||||
|
||||
public DataSourceManager(DataSource dataSource, DataSourceListener dataSourceListener) {
|
||||
@@ -186,7 +186,7 @@ public class DataSourceManager {
|
||||
waypointsTableObserver = new WaypointsTableObserver();
|
||||
trackPointsTableObserver = new TrackPointsTableObserver();
|
||||
currentLocationListener = new CurrentLocationListener();
|
||||
compassListener = new CompassListener();
|
||||
headingListener = new HeadingListener();
|
||||
preferenceListener = new PreferenceListener();
|
||||
}
|
||||
|
||||
@@ -202,9 +202,9 @@ public class DataSourceManager {
|
||||
* Map SAMPLED_OUT_POINT_UPDATES to POINT_UPDATES since they correspond to
|
||||
* the same internal listener
|
||||
*/
|
||||
if (neededListeners.contains(TrackDataType.SAMPLED_OUT_TRACK_POINTS)) {
|
||||
neededListeners.remove(TrackDataType.SAMPLED_OUT_TRACK_POINTS);
|
||||
neededListeners.add(TrackDataType.TRACK_POINTS_TABLE);
|
||||
if (neededListeners.contains(TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE)) {
|
||||
neededListeners.remove(TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE);
|
||||
neededListeners.add(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE);
|
||||
}
|
||||
|
||||
Log.d(TAG, "Updating listeners " + neededListeners);
|
||||
@@ -248,18 +248,18 @@ public class DataSourceManager {
|
||||
case WAYPOINTS_TABLE:
|
||||
dataSource.registerContentObserver(WaypointsColumns.CONTENT_URI, waypointsTableObserver);
|
||||
break;
|
||||
case TRACK_POINTS_TABLE:
|
||||
case SAMPLED_IN_TRACK_POINTS_TABLE:
|
||||
dataSource.registerContentObserver(
|
||||
TrackPointsColumns.CONTENT_URI, trackPointsTableObserver);
|
||||
break;
|
||||
case SAMPLED_OUT_TRACK_POINTS:
|
||||
case SAMPLED_OUT_TRACK_POINTS_TABLE:
|
||||
// Do nothing. SAMPLED_OUT_POINT_UPDATES is mapped to POINT_UPDATES.
|
||||
break;
|
||||
case LOCATION:
|
||||
dataSource.registerLocationListener(currentLocationListener);
|
||||
break;
|
||||
case COMPASS:
|
||||
dataSource.registerCompassListener(compassListener);
|
||||
case HEADING:
|
||||
dataSource.registerHeadingListener(headingListener);
|
||||
break;
|
||||
case PREFERENCE:
|
||||
dataSource.registerOnSharedPreferenceChangeListener(preferenceListener);
|
||||
@@ -285,17 +285,17 @@ public class DataSourceManager {
|
||||
case WAYPOINTS_TABLE:
|
||||
dataSource.unregisterContentObserver(waypointsTableObserver);
|
||||
break;
|
||||
case TRACK_POINTS_TABLE:
|
||||
case SAMPLED_IN_TRACK_POINTS_TABLE:
|
||||
dataSource.unregisterContentObserver(trackPointsTableObserver);
|
||||
break;
|
||||
case SAMPLED_OUT_TRACK_POINTS:
|
||||
case SAMPLED_OUT_TRACK_POINTS_TABLE:
|
||||
// Do nothing. SAMPLED_OUT_POINT_UPDATES is mapped to POINT_UPDATES.
|
||||
break;
|
||||
case LOCATION:
|
||||
dataSource.unregisterLocationListener(currentLocationListener);
|
||||
break;
|
||||
case COMPASS:
|
||||
dataSource.unregisterCompassListener(compassListener);
|
||||
case HEADING:
|
||||
dataSource.unregisterHeadingListener(headingListener);
|
||||
break;
|
||||
case PREFERENCE:
|
||||
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
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.content;
|
||||
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
package com.google.android.apps.mytracks.content;
|
||||
|
||||
import android.location.Location;
|
||||
|
||||
/**
|
||||
* Listener for track data, for both initial and incremental loading.
|
||||
*
|
||||
* Listener for track data changes.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public interface TrackDataListener {
|
||||
|
||||
/** States for the GPS location provider. */
|
||||
public enum ProviderState {
|
||||
DISABLED,
|
||||
NO_FIX,
|
||||
BAD_FIX,
|
||||
GOOD_FIX;
|
||||
/**
|
||||
* Location state.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
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.
|
||||
* This is meant for immediate location display only - track point data is
|
||||
* delivered by other methods below, such as {@link #onNewTrackPoint}.
|
||||
*
|
||||
* @param loc the last known location
|
||||
*/
|
||||
void onCurrentLocationChanged(Location loc);
|
||||
|
||||
/**
|
||||
* Called when the current heading changes.
|
||||
*
|
||||
* @param heading the current heading, already accounting magnetic declination
|
||||
*/
|
||||
void onCurrentHeadingChanged(double heading);
|
||||
|
||||
/**
|
||||
* Called when the currently-selected track changes.
|
||||
* This will be followed by calls to data methods such as
|
||||
* {@link #onTrackUpdated}, {@link #clearTrackPoints},
|
||||
* {@link #onNewTrackPoint(Location)}, etc., even if no track is currently
|
||||
* selected (in which case you'll only get calls to clear the current data).
|
||||
* Called when the location changes. This is meant for location display only,
|
||||
* track point data is reported with other methods like
|
||||
* {@link #onSampledInTrackPoint(Location)} and
|
||||
* {@link #onSampledOutTrackPoint(Location)}.
|
||||
*
|
||||
* @param track the selected track, or null if no track is selected
|
||||
* @param isRecording whether we're currently recording the selected track
|
||||
* @param location the location
|
||||
*/
|
||||
void onSelectedTrackChanged(Track track, boolean isRecording);
|
||||
public void onLocationChanged(Location location);
|
||||
|
||||
/**
|
||||
* Called when the track and/or its statistics have been updated.
|
||||
*
|
||||
* @param track the updated version of the track
|
||||
* Called when the heading changes.
|
||||
*
|
||||
* @param heading the heading
|
||||
*/
|
||||
void onTrackUpdated(Track track);
|
||||
public void onHeadingChanged(double heading);
|
||||
|
||||
/**
|
||||
* Called to clear any 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.
|
||||
* Called when the selected track changes. This will be followed by calls to
|
||||
* data methods such as {@link #onTrackUpdated(Track)},
|
||||
* {@link #clearTrackPoints()}, {@link #onSampledInTrackPoint(Location)},
|
||||
* etc., even if no track is currently selected (in which case you'll only get
|
||||
* calls to clear the current data).
|
||||
*
|
||||
* @param track the selected track or null if no track is selected
|
||||
* @param isRecording true if the selected track is recording
|
||||
*/
|
||||
void clearTrackPoints();
|
||||
public void onSelectedTrackChanged(Track track, boolean isRecording);
|
||||
|
||||
/**
|
||||
* Called when a new interesting 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
|
||||
* Called when the track or its statistics has been updated.
|
||||
*
|
||||
* @param track the track
|
||||
*/
|
||||
void onNewTrackPoint(Location loc);
|
||||
public void onTrackUpdated(Track track);
|
||||
|
||||
/**
|
||||
* Called when a uninteresting track point is read.
|
||||
* Uninteresting points are all points that get sampled out of the track.
|
||||
*
|
||||
* @param loc the new track point
|
||||
* Called to clear previously-sent track points.
|
||||
*/
|
||||
void onSampledOutTrackPoint(Location loc);
|
||||
public void clearTrackPoints();
|
||||
|
||||
/**
|
||||
* Called when an invalid point (representing a segment split) is read.
|
||||
* Called when a sampled in track point is read.
|
||||
*
|
||||
* @param location the location
|
||||
*/
|
||||
void onSegmentSplit();
|
||||
public void onSampledInTrackPoint(Location location);
|
||||
|
||||
/**
|
||||
* Called when we're done (for the time being) sending new points.
|
||||
* This gets called after every batch of calls to {@link #onNewTrackPoint},
|
||||
* {@link #onSampledOutTrackPoint} and {@link #onSegmentSplit}.
|
||||
* Called when a sampled out track point is read.
|
||||
*
|
||||
* @param location the location
|
||||
*/
|
||||
void onNewTrackPointsDone();
|
||||
public void onSampledOutTrackPoint(Location location);
|
||||
|
||||
/**
|
||||
* Called to clear any previously-sent waypoints.
|
||||
* This can be called at any time that we decide the data needs to be
|
||||
* reloaded.
|
||||
* Called when an invalid track point representing a segment split is read.
|
||||
*/
|
||||
void clearWaypoints();
|
||||
public void onSegmentSplit();
|
||||
|
||||
/**
|
||||
* Called when finish sending new track points. This gets called after every
|
||||
* batch of calls to {@link #onSampledInTrackPoint(Location)},
|
||||
* {@link #onSampledOutTrackPoint(Location)} and {@link #onSegmentSplit()}.
|
||||
*/
|
||||
public void onNewTrackPointsDone();
|
||||
|
||||
/**
|
||||
* Called to clear previously sent waypoints.
|
||||
*/
|
||||
public void clearWaypoints();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* This gets called after every batch of calls to {@link #clearWaypoints} and
|
||||
* {@link #onNewWaypoint}.
|
||||
* Called when finish sending new waypoints. This gets called after every
|
||||
* batch of calls to {@link #clearWaypoints()} and
|
||||
* {@link #onNewWaypoint(Waypoint)}.
|
||||
*/
|
||||
void onNewWaypointsDone();
|
||||
public void onNewWaypointsDone();
|
||||
|
||||
/**
|
||||
* Called when the display units are changed by the user.
|
||||
*
|
||||
* @param metric true if the units are metric, false if imperial
|
||||
* @return true to reload all the data, false otherwise
|
||||
* Called when the metric units preference value is change.
|
||||
*
|
||||
* @param metricUnits true to use metric units, false to use imperial units
|
||||
* @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.
|
||||
*
|
||||
* @param reportSpeed true to report speed, false for pace
|
||||
* @return true to reload all the data, false otherwise
|
||||
* Called when the report speed preference value is changed.
|
||||
*
|
||||
* @param reportSpeed true to report speed, false to report pace
|
||||
* @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 {
|
||||
|
||||
// Map of listener to its state
|
||||
private final Map<TrackDataListener, ListenerState>
|
||||
listenerToStateMap = new HashMap<TrackDataListener, ListenerState>();
|
||||
// Map of listener to its track data types
|
||||
private final Map<TrackDataListener, EnumSet<TrackDataType>>
|
||||
listenerToTypesMap = new HashMap<TrackDataListener, EnumSet<TrackDataType>>();
|
||||
|
||||
// Map of track data type to listeners
|
||||
private final Map<TrackDataType, Set<TrackDataListener>>
|
||||
@@ -55,20 +55,15 @@ public class TrackDataManager {
|
||||
* @param listener the listener
|
||||
* @param trackDataTypes the track data types the listener is interested
|
||||
*/
|
||||
public ListenerState registerListener(
|
||||
public void registerListener(
|
||||
TrackDataListener listener, EnumSet<TrackDataType> trackDataTypes) {
|
||||
if (listenerToStateMap.containsKey(listener)) {
|
||||
if (listenerToTypesMap.containsKey(listener)) {
|
||||
throw new IllegalStateException("Listener is already registered");
|
||||
}
|
||||
|
||||
ListenerState listenerState = new ListenerState(listener, trackDataTypes);
|
||||
listenerToStateMap.put(listener, listenerState);
|
||||
|
||||
listenerToTypesMap.put(listener, trackDataTypes);
|
||||
for (TrackDataType trackDataType : trackDataTypes) {
|
||||
typeToListenersMap.get(trackDataType).add(listener);
|
||||
}
|
||||
|
||||
return listenerState;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,14 +72,14 @@ public class TrackDataManager {
|
||||
* @param listener the listener
|
||||
*/
|
||||
public void unregisterListener(TrackDataListener listener) {
|
||||
ListenerState removed = listenerToStateMap.remove(listener);
|
||||
if (removed == null) {
|
||||
EnumSet<TrackDataType> removedTypes = listenerToTypesMap.remove(listener);
|
||||
if (removedTypes == null) {
|
||||
Log.w(TAG, "Tried to unregister a listener that is not registered.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the listener from the typeToListenersMap
|
||||
for (TrackDataType trackDataType : removed.getTrackDataTypes()) {
|
||||
for (TrackDataType trackDataType : removedTypes) {
|
||||
typeToListenersMap.get(trackDataType).remove(listener);
|
||||
}
|
||||
}
|
||||
@@ -93,16 +88,16 @@ public class TrackDataManager {
|
||||
* Gets the number of {@link TrackDataListener}.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
public ListenerState getListenerState(TrackDataListener listener) {
|
||||
return listenerToStateMap.get(listener);
|
||||
public EnumSet<TrackDataType> getTrackDataTypes(TrackDataListener listener) {
|
||||
return listenerToTypesMap.get(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,9 +114,11 @@ public class TrackDataManager {
|
||||
*/
|
||||
public EnumSet<TrackDataType> getRegisteredTrackDataTypes() {
|
||||
EnumSet<TrackDataType> types = EnumSet.noneOf(TrackDataType.class);
|
||||
for (ListenerState registration : this.listenerToStateMap.values()) {
|
||||
types.addAll(registration.getTrackDataTypes());
|
||||
for (EnumSet<TrackDataType> value : listenerToTypesMap.values()) {
|
||||
types.addAll(value);
|
||||
}
|
||||
// Always include preference
|
||||
types.add(TrackDataType.PREFERENCE);
|
||||
return types;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,13 +22,12 @@ package com.google.android.apps.mytracks.content;
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public enum TrackDataType {
|
||||
|
||||
SELECTED_TRACK, // selected track changes
|
||||
TRACKS_TABLE, // tracks table changes
|
||||
WAYPOINTS_TABLE, // waypoints table changes
|
||||
TRACK_POINTS_TABLE, // track points table changes
|
||||
SAMPLED_OUT_TRACK_POINTS, // sampled-out track points changes
|
||||
SAMPLED_IN_TRACK_POINTS_TABLE, // sampled-in track points table changes
|
||||
SAMPLED_OUT_TRACK_POINTS_TABLE, // sampled-out track points table changes
|
||||
LOCATION, // location changes
|
||||
COMPASS, // compass changes
|
||||
PREFERENCE; // preference changes
|
||||
HEADING, // heading 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.Constants;
|
||||
import com.google.android.apps.mytracks.MyTracksApplication;
|
||||
import com.google.android.apps.mytracks.content.TrackDataType;
|
||||
import com.google.android.apps.mytracks.TrackDetailActivity;
|
||||
import com.google.android.apps.mytracks.content.MyTracksLocation;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
import com.google.android.apps.mytracks.content.Sensor.SensorDataSet;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.TrackDataHub;
|
||||
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.stats.DoubleBuffer;
|
||||
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
|
||||
@@ -96,7 +96,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
|
||||
zoomControls.setIsZoomInEnabled(chartView.canZoomIn());
|
||||
zoomControls.setIsZoomOutEnabled(chartView.canZoomOut());
|
||||
chartView.setShowPointer(isRecording());
|
||||
chartView.setShowPointer(isSelectedTrackRecording());
|
||||
chartView.invalidate();
|
||||
}
|
||||
};
|
||||
@@ -164,17 +164,17 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderStateChange(ProviderState state) {
|
||||
public void onLocationStateChanged(LocationState state) {
|
||||
// We don't care.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCurrentLocationChanged(Location loc) {
|
||||
public void onLocationChanged(Location loc) {
|
||||
// We don't care.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCurrentHeadingChanged(double heading) {
|
||||
public void onHeadingChanged(double heading) {
|
||||
// We don't care.
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewTrackPoint(Location location) {
|
||||
public void onSampledInTrackPoint(Location location) {
|
||||
if (LocationUtils.isValidLocation(location)) {
|
||||
double[] data = new double[ChartView.NUM_SERIES + 1];
|
||||
fillDataPoint(location, data);
|
||||
@@ -259,7 +259,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onUnitsChanged(boolean metric) {
|
||||
public boolean onMetricUnitsChanged(boolean metric) {
|
||||
if (metricUnits == metric) {
|
||||
return false;
|
||||
}
|
||||
@@ -358,13 +358,13 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
* accessed by multiple threads.
|
||||
*/
|
||||
private synchronized void resumeTrackDataHub() {
|
||||
trackDataHub = ((MyTracksApplication) getActivity().getApplication()).getTrackDataHub();
|
||||
trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub();
|
||||
trackDataHub.registerTrackDataListener(this, EnumSet.of(
|
||||
TrackDataType.SELECTED_TRACK,
|
||||
TrackDataType.TRACKS_TABLE,
|
||||
TrackDataType.WAYPOINTS_TABLE,
|
||||
TrackDataType.TRACK_POINTS_TABLE,
|
||||
TrackDataType.SAMPLED_OUT_TRACK_POINTS,
|
||||
TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE,
|
||||
TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE,
|
||||
TrackDataType.PREFERENCE));
|
||||
}
|
||||
|
||||
@@ -378,11 +378,11 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if recording. Needs to be synchronized because trackDataHub
|
||||
* can be accessed by multiple threads.
|
||||
* Returns true if the selected track is recording. Needs to be synchronized
|
||||
* because trackDataHub can be accessed by multiple threads.
|
||||
*/
|
||||
private synchronized boolean isRecording() {
|
||||
return trackDataHub != null && trackDataHub.isRecordingSelected();
|
||||
private synchronized boolean isSelectedTrackRecording() {
|
||||
return trackDataHub != null && trackDataHub.isSelectedTrackRecording();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,14 +17,13 @@
|
||||
package com.google.android.apps.mytracks.fragments;
|
||||
|
||||
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.content.TrackDataType;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils.Factory;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.TrackDataHub;
|
||||
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.stats.TripStatistics;
|
||||
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
|
||||
@@ -271,7 +270,7 @@ public class MapFragment extends Fragment
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderStateChange(ProviderState state) {
|
||||
public void onLocationStateChanged(LocationState state) {
|
||||
final int messageId;
|
||||
final boolean isGpsDisabled;
|
||||
switch (state) {
|
||||
@@ -315,13 +314,13 @@ public class MapFragment extends Fragment
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCurrentLocationChanged(Location location) {
|
||||
public void onLocationChanged(Location location) {
|
||||
currentLocation = location;
|
||||
updateCurrentLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCurrentHeadingChanged(double heading) {
|
||||
public void onHeadingChanged(double heading) {
|
||||
if (mapOverlay.setHeading((float) heading)) {
|
||||
mapView.postInvalidate();
|
||||
}
|
||||
@@ -362,7 +361,7 @@ public class MapFragment extends Fragment
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewTrackPoint(Location location) {
|
||||
public void onSampledInTrackPoint(Location location) {
|
||||
if (LocationUtils.isValidLocation(location)) {
|
||||
mapOverlay.addLocation(location);
|
||||
}
|
||||
@@ -402,7 +401,7 @@ public class MapFragment extends Fragment
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onUnitsChanged(boolean metric) {
|
||||
public boolean onMetricUnitsChanged(boolean metric) {
|
||||
// We don't care.
|
||||
return false;
|
||||
}
|
||||
@@ -418,13 +417,13 @@ public class MapFragment extends Fragment
|
||||
* accessed by multiple threads.
|
||||
*/
|
||||
private synchronized void resumeTrackDataHub() {
|
||||
trackDataHub = ((MyTracksApplication) getActivity().getApplication()).getTrackDataHub();
|
||||
trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub();
|
||||
trackDataHub.registerTrackDataListener(this, EnumSet.of(
|
||||
TrackDataType.SELECTED_TRACK,
|
||||
TrackDataType.WAYPOINTS_TABLE,
|
||||
TrackDataType.TRACK_POINTS_TABLE,
|
||||
TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE,
|
||||
TrackDataType.LOCATION,
|
||||
TrackDataType.COMPASS));
|
||||
TrackDataType.HEADING));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,11 +16,11 @@
|
||||
|
||||
package com.google.android.apps.mytracks.fragments;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksApplication;
|
||||
import com.google.android.apps.mytracks.content.TrackDataType;
|
||||
import com.google.android.apps.mytracks.TrackDetailActivity;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.TrackDataHub;
|
||||
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.stats.TripStatistics;
|
||||
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.
|
||||
private final Runnable updateTotalTime = new Runnable() {
|
||||
public void run() {
|
||||
if (isRecording()) {
|
||||
if (isSelectedTrackRecording()) {
|
||||
StatsUtils.setTotalTimeValue(getActivity(), System.currentTimeMillis() - startTime);
|
||||
}
|
||||
}
|
||||
@@ -117,8 +117,8 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderStateChange(ProviderState state) {
|
||||
if (isResumed() && (state == ProviderState.DISABLED || state == ProviderState.NO_FIX)) {
|
||||
public void onLocationStateChanged(LocationState state) {
|
||||
if (isResumed() && (state == LocationState.DISABLED || state == LocationState.NO_FIX)) {
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -130,8 +130,8 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCurrentLocationChanged(final Location location) {
|
||||
if (isResumed() && isRecording()) {
|
||||
public void onLocationChanged(final Location location) {
|
||||
if (isResumed() && isSelectedTrackRecording()) {
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -143,7 +143,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCurrentHeadingChanged(double heading) {
|
||||
public void onHeadingChanged(double heading) {
|
||||
// We don't care.
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
lastTripStatistics = track.getTripStatistics();
|
||||
|
||||
startTime = track.getTripStatistics().getStartTime();
|
||||
if (!isRecording()) {
|
||||
if (!isSelectedTrackRecording()) {
|
||||
lastLocation = null;
|
||||
}
|
||||
updateUi();
|
||||
@@ -190,7 +190,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewTrackPoint(Location loc) {
|
||||
public void onSampledInTrackPoint(Location loc) {
|
||||
// We don't care.
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onUnitsChanged(final boolean metric) {
|
||||
public boolean onMetricUnitsChanged(final boolean metric) {
|
||||
if (isResumed()) {
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
@@ -255,7 +255,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
* be accessed by multiple threads.
|
||||
*/
|
||||
private synchronized void resumeTrackDataHub() {
|
||||
trackDataHub = ((MyTracksApplication) getActivity().getApplication()).getTrackDataHub();
|
||||
trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub();
|
||||
trackDataHub.registerTrackDataListener(this, EnumSet.of(
|
||||
TrackDataType.SELECTED_TRACK,
|
||||
TrackDataType.TRACKS_TABLE,
|
||||
@@ -273,11 +273,11 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if recording. Needs to be synchronized because trackDataHub
|
||||
* can be accessed by multiple threads.
|
||||
* Returns true if the selected track is recording. Needs to be synchronized
|
||||
* because trackDataHub can be accessed by multiple threads.
|
||||
*/
|
||||
private synchronized boolean isRecording() {
|
||||
return trackDataHub != null && trackDataHub.isRecordingSelected();
|
||||
private synchronized boolean isSelectedTrackRecording() {
|
||||
return trackDataHub != null && trackDataHub.isSelectedTrackRecording();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+59
-68
@@ -13,6 +13,7 @@
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package com.google.android.apps.mytracks.maps;
|
||||
|
||||
import static com.google.android.apps.mytracks.Constants.TAG;
|
||||
@@ -36,60 +37,43 @@ import android.util.Log;
|
||||
*
|
||||
* @author Vangelis S.
|
||||
*/
|
||||
public class DynamicSpeedTrackPathDescriptor implements TrackPathDescriptor,
|
||||
OnSharedPreferenceChangeListener {
|
||||
public class DynamicSpeedTrackPathDescriptor
|
||||
implements TrackPathDescriptor, OnSharedPreferenceChangeListener {
|
||||
|
||||
private final Context context;
|
||||
private int speedMargin;
|
||||
private int slowSpeed;
|
||||
private int normalSpeed;
|
||||
private int speedMargin;
|
||||
private double averageMovingSpeed;
|
||||
private final Context context;
|
||||
|
||||
@VisibleForTesting
|
||||
static final int CRITICAL_DIFFERENCE_PERCENTAGE = 20;
|
||||
|
||||
public DynamicSpeedTrackPathDescriptor(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,
|
||||
PreferencesUtils.TRACK_COLOR_MODE_PERCENTAGE_DEFAULT);
|
||||
context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE)
|
||||
.registerOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the slow speed calculated based on the % below the average speed.
|
||||
*
|
||||
* @return The speed limit considered as slow.
|
||||
*/
|
||||
@Override
|
||||
public int getSlowSpeed() {
|
||||
slowSpeed = (int) (averageMovingSpeed - (averageMovingSpeed * speedMargin / 100));
|
||||
slowSpeed = (int) (averageMovingSpeed - (averageMovingSpeed * speedMargin / 100.0));
|
||||
return slowSpeed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the medium speed calculated based on the % above the average speed.
|
||||
*
|
||||
* @return The speed limit considered as normal.
|
||||
*/
|
||||
@Override
|
||||
public int getNormalSpeed() {
|
||||
normalSpeed = (int) (averageMovingSpeed + (averageMovingSpeed * speedMargin / 100));
|
||||
normalSpeed = (int) (averageMovingSpeed + (averageMovingSpeed * speedMargin / 100.0));
|
||||
return normalSpeed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
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() {
|
||||
public boolean updateState() {
|
||||
long selectedTrackId = PreferencesUtils.getLong(context, R.string.selected_track_id_key);
|
||||
if (selectedTrackId == PreferencesUtils.SELECTED_TRACK_ID_DEFAULT) {
|
||||
// Could not find track.
|
||||
Log.d(TAG, "No selected track id.");
|
||||
return false;
|
||||
}
|
||||
Track track = MyTracksProviderUtils.Factory.get(context).getTrack(selectedTrackId);
|
||||
@@ -97,40 +81,48 @@ public class DynamicSpeedTrackPathDescriptor implements TrackPathDescriptor,
|
||||
Log.d(TAG, "No track for " + selectedTrackId);
|
||||
return false;
|
||||
}
|
||||
TripStatistics stats = track.getTripStatistics();
|
||||
TripStatistics tripStatistics = track.getTripStatistics();
|
||||
double newAverageMovingSpeed = (int) Math.floor(
|
||||
stats.getAverageMovingSpeed() * UnitConversions.MS_TO_KMH);
|
||||
tripStatistics.getAverageMovingSpeed() * UnitConversions.MS_TO_KMH);
|
||||
|
||||
return isDifferenceSignificant(averageMovingSpeed, newAverageMovingSpeed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the old speed and the new speed differ significantly or not.
|
||||
*/
|
||||
public boolean isDifferenceSignificant(double oldAverageMovingSpeed, double newAverageMovingSpeed) {
|
||||
if (oldAverageMovingSpeed == 0) {
|
||||
if (newAverageMovingSpeed == 0) {
|
||||
return false;
|
||||
} else {
|
||||
averageMovingSpeed = newAverageMovingSpeed;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Here, both oldAverageMovingSpeed and newAverageMovingSpeed are not zero.
|
||||
double maxValue = Math.max(oldAverageMovingSpeed, newAverageMovingSpeed);
|
||||
double differencePercentage = Math.abs(oldAverageMovingSpeed - newAverageMovingSpeed)
|
||||
/ maxValue * 100;
|
||||
if (differencePercentage >= CRITICAL_DIFFERENCE_PERCENTAGE) {
|
||||
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);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of variable speedMargin to check the result of test.
|
||||
* @return the value of speedMargin.
|
||||
* Returns true if the average moving speed and the new average moving speed
|
||||
* are significantly different.
|
||||
*
|
||||
* @param oldAverageMovingSpeed
|
||||
* @param newAverageMovingSpeed
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean isDifferenceSignificant(double oldAverageMovingSpeed, double newAverageMovingSpeed) {
|
||||
if (oldAverageMovingSpeed == 0) {
|
||||
return newAverageMovingSpeed != 0;
|
||||
}
|
||||
|
||||
// Here, both oldAverageMovingSpeed and newAverageMovingSpeed are not zero.
|
||||
double maxValue = Math.max(oldAverageMovingSpeed, newAverageMovingSpeed);
|
||||
double differencePercentage = Math.abs(oldAverageMovingSpeed - newAverageMovingSpeed) / maxValue
|
||||
* 100.0;
|
||||
return differencePercentage >= CRITICAL_DIFFERENCE_PERCENTAGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the speed margin.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
int getSpeedMargin() {
|
||||
@@ -138,21 +130,20 @@ public class DynamicSpeedTrackPathDescriptor implements TrackPathDescriptor,
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of newAverageMovingSpeed to test the method isDifferenceSignificant.
|
||||
* @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
|
||||
* Gets the average moving speed.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
double getAverageMovingSpeed() {
|
||||
return averageMovingSpeed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the average moving speed.
|
||||
*
|
||||
* @param value the value
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void setAverageMovingSpeed(double value) {
|
||||
averageMovingSpeed = value;
|
||||
}
|
||||
}
|
||||
+68
-81
@@ -25,7 +25,6 @@ import com.google.common.annotations.VisibleForTesting;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
|
||||
@@ -33,117 +32,105 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A path painter that varies the path colors based on fixed speeds or average speed margin
|
||||
* depending of the TrackPathDescriptor passed to its constructor.
|
||||
* A path painter that varies the path colors based on fixed speeds or average
|
||||
* speed margin depending of the TrackPathDescriptor passed to its constructor.
|
||||
*
|
||||
* @author Vangelis S.
|
||||
* @author Vangelis S.
|
||||
*/
|
||||
public class DynamicSpeedTrackPathPainter implements TrackPathPainter {
|
||||
private final Paint selectedTrackPaintSlow;
|
||||
private final Paint selectedTrackPaintMedium;
|
||||
private final Paint selectedTrackPaintFast;
|
||||
private final List<ColoredPath> coloredPaths;
|
||||
public class DynamicSpeedTrackPathPainter implements TrackPathPainter {
|
||||
private final TrackPathDescriptor trackPathDescriptor;
|
||||
private int slowSpeed;
|
||||
private int normalSpeed;
|
||||
|
||||
public DynamicSpeedTrackPathPainter (Context context, TrackPathDescriptor trackPathDescriptor) {
|
||||
private final Paint slowPaint;
|
||||
private final Paint mediumPaint;
|
||||
private final Paint fastPaint;
|
||||
private final List<ColoredPath> coloredPaths;
|
||||
|
||||
public DynamicSpeedTrackPathPainter(Context context, TrackPathDescriptor trackPathDescriptor) {
|
||||
this.trackPathDescriptor = trackPathDescriptor;
|
||||
|
||||
selectedTrackPaintSlow = TrackPathUtilities.getPaint(R.color.slow_path, context);
|
||||
selectedTrackPaintMedium = TrackPathUtilities.getPaint(R.color.normal_path, context);
|
||||
selectedTrackPaintFast = TrackPathUtilities.getPaint(R.color.fast_path, context);
|
||||
|
||||
this.coloredPaths = new ArrayList<ColoredPath>();
|
||||
slowPaint = TrackPathUtils.getPaint(context, R.color.slow_path);
|
||||
mediumPaint = TrackPathUtils.getPaint(context, R.color.normal_path);
|
||||
fastPaint = TrackPathUtils.getPaint(context, R.color.fast_path);
|
||||
coloredPaths = new ArrayList<ColoredPath>();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void drawTrack(Canvas canvas) {
|
||||
for(int i = 0; i < coloredPaths.size(); ++i) {
|
||||
ColoredPath coloredPath = coloredPaths.get(i);
|
||||
canvas.drawPath(coloredPath.getPath(), coloredPath.getPathPaint());
|
||||
public boolean hasPath() {
|
||||
return !coloredPaths.isEmpty();
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePath(Projection projection, Rect viewRect, int startLocationIdx,
|
||||
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();
|
||||
|
||||
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) {
|
||||
boolean newSegment = !hasLastPoint;
|
||||
// Assume if last point exists, it is visible
|
||||
boolean lastPointVisible = hasLastPoint;
|
||||
int slowSpeed = trackPathDescriptor.getSlowSpeed();
|
||||
int normalSpeed = trackPathDescriptor.getNormalSpeed();
|
||||
|
||||
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;
|
||||
continue;
|
||||
}
|
||||
|
||||
final GeoPoint geoPoint = loc.geoPoint;
|
||||
|
||||
GeoPoint geoPoint = cachedLocation.getGeoPoint();
|
||||
// Check if this breaks the existing segment.
|
||||
boolean visible = alwaysVisible || viewRect.contains(
|
||||
geoPoint.getLongitudeE6(), geoPoint.getLatitudeE6());
|
||||
if (!visible && !lastVisible) {
|
||||
// This is a point outside view not connected to a visible one.
|
||||
boolean pointVisible = viewRect.contains(geoPoint.getLongitudeE6(), geoPoint.getLatitudeE6());
|
||||
if (!pointVisible && !lastPointVisible) {
|
||||
// This point and the last point are both outside visible area.
|
||||
newSegment = true;
|
||||
}
|
||||
lastVisible = visible;
|
||||
|
||||
// Either move to beginning of a new segment or continue the old one.
|
||||
lastPointVisible = pointVisible;
|
||||
|
||||
// Either update point or draw a line from the last point
|
||||
if (newSegment) {
|
||||
projection.toPixels(geoPoint, pt);
|
||||
projection.toPixels(geoPoint, point);
|
||||
newSegment = false;
|
||||
} else {
|
||||
ColoredPath coloredPath;
|
||||
if(loc.speed <= slowSpeed) {
|
||||
coloredPath = new ColoredPath(selectedTrackPaintSlow);
|
||||
}
|
||||
else if(loc.speed <= normalSpeed) {
|
||||
coloredPath = new ColoredPath(selectedTrackPaintMedium);
|
||||
if (cachedLocation.getSpeed() <= slowSpeed) {
|
||||
coloredPath = new ColoredPath(slowPaint);
|
||||
} else if (cachedLocation.getSpeed() <= normalSpeed) {
|
||||
coloredPath = new ColoredPath(mediumPaint);
|
||||
} else {
|
||||
coloredPath = new ColoredPath(selectedTrackPaintFast);
|
||||
coloredPath = new ColoredPath(fastPaint);
|
||||
}
|
||||
coloredPath.getPath().moveTo(pt.x, pt.y);
|
||||
projection.toPixels(geoPoint, pt);
|
||||
coloredPath.getPath().lineTo(pt.x, pt.y);
|
||||
coloredPath.getPath().moveTo(point.x, point.y);
|
||||
projection.toPixels(geoPoint, point);
|
||||
coloredPath.getPath().lineTo(point.x, point.y);
|
||||
coloredPaths.add(coloredPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void clear()
|
||||
{
|
||||
public void clearPath() {
|
||||
coloredPaths.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsRedraw() {
|
||||
return trackPathDescriptor.needsRedraw();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getLastPath() {
|
||||
Path path = new Path();
|
||||
for(int i = 0; i < coloredPaths.size(); ++i) {
|
||||
path.addPath(coloredPaths.get(i).getPath());
|
||||
public void drawPath(Canvas canvas) {
|
||||
for (int i = 0; i < coloredPaths.size(); i++) {
|
||||
ColoredPath coloredPath = coloredPaths.get(i);
|
||||
canvas.drawPath(coloredPath.getPath(), coloredPath.getPathPaint());
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns coloredPaths.
|
||||
*
|
||||
* @return coloredPaths
|
||||
* Gets the colored paths.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
List<ColoredPath> getColoredPaths() {
|
||||
|
||||
+10
-19
@@ -31,40 +31,36 @@ import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
*/
|
||||
public class FixedSpeedTrackPathDescriptor
|
||||
implements TrackPathDescriptor, OnSharedPreferenceChangeListener {
|
||||
private final Context context;
|
||||
private int slowSpeed;
|
||||
private int normalSpeed;
|
||||
private final Context context;
|
||||
|
||||
public FixedSpeedTrackPathDescriptor(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,
|
||||
PreferencesUtils.TRACK_COLOR_MODE_SLOW_DEFAULT);
|
||||
normalSpeed = PreferencesUtils.getInt(context, R.string.track_color_mode_medium_key,
|
||||
PreferencesUtils.TRACK_COLOR_MODE_MEDIUM_DEFAULT);
|
||||
context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE)
|
||||
.registerOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the slow speed for reference.
|
||||
*
|
||||
* @return The speed limit considered as slow.
|
||||
*/
|
||||
@Override
|
||||
public int getSlowSpeed() {
|
||||
return slowSpeed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the normal speed for reference.
|
||||
*
|
||||
* @return The speed limit considered as normal.
|
||||
*/
|
||||
@Override
|
||||
public int getNormalSpeed() {
|
||||
return normalSpeed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateState() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String 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);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsRedraw() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+64
-49
@@ -40,88 +40,103 @@ public class SingleColorTrackPathPainter implements TrackPathPainter {
|
||||
private Path path;
|
||||
|
||||
public SingleColorTrackPathPainter(Context context) {
|
||||
selectedTrackPaint = TrackPathUtilities.getPaint(R.color.red, context);
|
||||
selectedTrackPaint = TrackPathUtils.getPaint(context, R.color.red);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawTrack(Canvas canvas) {
|
||||
canvas.drawPath(path, selectedTrackPaint);
|
||||
public boolean hasPath() {
|
||||
return path != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePath(Projection projection, Rect viewRect, int startLocationIdx,
|
||||
Boolean alwaysVisible, List<CachedLocation> points) {
|
||||
path = newPath();
|
||||
updatePath(projection, viewRect, startLocationIdx, alwaysVisible, points, path);
|
||||
public boolean updateState() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@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.
|
||||
*
|
||||
* @param projection The Canvas to draw upon.
|
||||
* @param viewRect The Path to be drawn.
|
||||
* @param startLocationIdx The start point from where update the path.
|
||||
* @param alwaysVisible Flag for always visible.
|
||||
* @param points The list of points used to update the path.
|
||||
* @param pathToUpdate The path to be created.
|
||||
* @param projection the projection
|
||||
* @param viewRect the view rectangle
|
||||
* @param startIndex the start index
|
||||
* @param points the points
|
||||
* @param pathToUpdate the path to update
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void updatePath(Projection projection, Rect viewRect, int startLocationIdx,
|
||||
Boolean alwaysVisible, List<CachedLocation> points, Path pathToUpdate) {
|
||||
pathToUpdate.incReserve(points.size());
|
||||
// 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);
|
||||
void updatePath(Projection projection, Rect viewRect, int startIndex, List<CachedLocation> points,
|
||||
Path pathToUpdate) {
|
||||
pathToUpdate.incReserve(points.size() - startIndex);
|
||||
|
||||
// Check if valid, if not then indicate a new segment.
|
||||
if (!loc.valid) {
|
||||
boolean hasLastPoint = startIndex != 0 && points.get(startIndex - 1).isValid();
|
||||
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;
|
||||
continue;
|
||||
}
|
||||
|
||||
final GeoPoint geoPoint = loc.geoPoint;
|
||||
GeoPoint geoPoint = cachedLocation.getGeoPoint();
|
||||
// Check if this breaks the existing segment.
|
||||
boolean visible = alwaysVisible
|
||||
|| viewRect.contains(geoPoint.getLongitudeE6(), geoPoint.getLatitudeE6());
|
||||
if (!visible && !lastVisible) {
|
||||
// This is a point outside view not connected to a visible one.
|
||||
boolean pointVisible = viewRect.contains(geoPoint.getLongitudeE6(), geoPoint.getLatitudeE6());
|
||||
if (!pointVisible && !lastPointVisible) {
|
||||
// This point and the last point are both outside visible area.
|
||||
newSegment = true;
|
||||
}
|
||||
lastVisible = visible;
|
||||
lastPointVisible = pointVisible;
|
||||
|
||||
// Either move to beginning of a new segment or continue the old one.
|
||||
projection.toPixels(geoPoint, pt);
|
||||
// Either update point or draw a line from the last point
|
||||
projection.toPixels(geoPoint, point);
|
||||
if (newSegment) {
|
||||
pathToUpdate.moveTo(pt.x, pt.y);
|
||||
pathToUpdate.moveTo(point.x, point.y);
|
||||
newSegment = false;
|
||||
} else {
|
||||
pathToUpdate.lineTo(pt.x, pt.y);
|
||||
pathToUpdate.lineTo(point.x, point.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
path = null;
|
||||
/**
|
||||
* Creates a new path.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected Path newPath() {
|
||||
return new Path();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsRedraw() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getLastPath() {
|
||||
/**
|
||||
* Gets the path.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public Path getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
// Visible for testing
|
||||
public Path newPath() {
|
||||
return new Path();
|
||||
}
|
||||
}
|
||||
@@ -13,26 +13,28 @@
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package com.google.android.apps.mytracks.maps;
|
||||
|
||||
/**
|
||||
* An interface for classes which describe how to draw a track path.
|
||||
*
|
||||
* An interface for classes which describe how to draw a track path.
|
||||
*
|
||||
* @author Vangelis S.
|
||||
*/
|
||||
public interface TrackPathDescriptor {
|
||||
|
||||
/**
|
||||
* @return The maximum speed which is considered slow.
|
||||
* Gets the maximum speed which is considered slow.
|
||||
*/
|
||||
int getSlowSpeed();
|
||||
|
||||
public int getSlowSpeed();
|
||||
|
||||
/**
|
||||
* @return The maximum speed which is considered normal.
|
||||
* Gets the maximum speed which is considered normal.
|
||||
*/
|
||||
int getNormalSpeed();
|
||||
|
||||
public int getNormalSpeed();
|
||||
|
||||
/**
|
||||
* @return True if the path needs to be updated.
|
||||
* Updates state. Returns true if the state is updated.
|
||||
*/
|
||||
boolean needsRedraw();
|
||||
public boolean updateState();
|
||||
}
|
||||
@@ -13,56 +13,54 @@
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package com.google.android.apps.mytracks.maps;
|
||||
|
||||
import com.google.android.apps.mytracks.MapOverlay.CachedLocation;
|
||||
import com.google.android.maps.Projection;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Rect;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An interface for classes which paint the track path.
|
||||
*
|
||||
* An interface for classes which paint the track path.
|
||||
*
|
||||
* @author Vangelis S.
|
||||
*/
|
||||
public interface TrackPathPainter {
|
||||
|
||||
|
||||
/**
|
||||
* Clears the related data.
|
||||
* Returns true if has path.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
public boolean hasPath();
|
||||
|
||||
/**
|
||||
* Draws the path to the canvas.
|
||||
* @param canvas The Canvas to draw upon
|
||||
* Updates state. Returns true if the state is updated.
|
||||
*/
|
||||
void drawTrack(Canvas canvas);
|
||||
|
||||
public boolean updateState();
|
||||
|
||||
/**
|
||||
* Updates the path.
|
||||
* @param projection The Canvas to draw upon.
|
||||
* @param viewRect The Path to be drawn.
|
||||
* @param startLocationIdx The start point from where update the path.
|
||||
* @param alwaysVisible Flag for alwaysvisible.
|
||||
* @param points The list of points used to update the path.
|
||||
* Updates the path. Creates a new path if necessary
|
||||
*
|
||||
* @param projection the projection
|
||||
* @param viewRect the view rectangle
|
||||
* @param startIndex the start index
|
||||
* @param points the points
|
||||
*/
|
||||
void updatePath(Projection projection, Rect viewRect, int startLocationIdx,
|
||||
Boolean alwaysVisible, List<CachedLocation> points);
|
||||
|
||||
public void updatePath(
|
||||
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
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package com.google.android.apps.mytracks.maps;
|
||||
|
||||
import static com.google.android.apps.mytracks.Constants.TAG;
|
||||
@@ -24,32 +25,31 @@ import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* A factory for TrackPathPainters.
|
||||
*
|
||||
* A factory for {@link TrackPathPainter}.
|
||||
*
|
||||
* @author Vangelis S.
|
||||
*/
|
||||
public class TrackPathPainterFactory {
|
||||
|
||||
private TrackPathPainterFactory() {
|
||||
}
|
||||
private TrackPathPainterFactory() {}
|
||||
|
||||
/**
|
||||
* Get a new TrackPathPainter.
|
||||
* @param context Context to fetch system preferences.
|
||||
* @return The TrackPathPainter that corresponds to the track color mode setting.
|
||||
* Get a new {@link TrackPathPainter}.
|
||||
*
|
||||
* @param context the context
|
||||
*/
|
||||
public static TrackPathPainter getTrackPathPainter(Context context) {
|
||||
String trackColorMode = PreferencesUtils.getString(context, R.string.track_color_mode_key,
|
||||
context.getString(R.string.settings_map_track_color_mode_single_value));
|
||||
Log.i(TAG, "Creating track path painter of type: " + trackColorMode);
|
||||
|
||||
if (context.getString(R.string.settings_map_track_color_mode_fixed_value)
|
||||
.equals(trackColorMode)) {
|
||||
return new DynamicSpeedTrackPathPainter(context, new FixedSpeedTrackPathDescriptor(context));
|
||||
} else if (context.getString(R.string.settings_map_track_color_mode_dynamic_value)
|
||||
if (context.getString(R.string.settings_map_track_color_mode_dynamic_value)
|
||||
.equals(trackColorMode)) {
|
||||
return new DynamicSpeedTrackPathPainter(context, new DynamicSpeedTrackPathDescriptor(
|
||||
context));
|
||||
} else if (context.getString(R.string.settings_map_track_color_mode_fixed_value)
|
||||
.equals(trackColorMode)) {
|
||||
return new DynamicSpeedTrackPathPainter(context, new FixedSpeedTrackPathDescriptor(context));
|
||||
} else {
|
||||
return new SingleColorTrackPathPainter(context);
|
||||
}
|
||||
|
||||
+15
-6
@@ -13,21 +13,30 @@
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package com.google.android.apps.mytracks.maps;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Paint;
|
||||
|
||||
/**
|
||||
* Various utility functions for TrackPath painting.
|
||||
*
|
||||
* Various utility functions for track path painting.
|
||||
*
|
||||
* @author Vangelis S.
|
||||
*/
|
||||
public class TrackPathUtilities {
|
||||
|
||||
public static Paint getPaint(int id, Context context) {
|
||||
public class TrackPathUtils {
|
||||
|
||||
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.setColor(context.getResources().getColor(id));
|
||||
paint.setColor(context.getResources().getColor(colorId));
|
||||
paint.setStrokeWidth(3);
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
paint.setAntiAlias(true);
|
||||
+4
-3
@@ -681,7 +681,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
||||
return waypoint;
|
||||
}
|
||||
|
||||
private ContentValues createContentValues(Waypoint waypoint) {
|
||||
ContentValues createContentValues(Waypoint waypoint) {
|
||||
ContentValues values = new ContentValues();
|
||||
|
||||
// Value < 0 indicates no id is available
|
||||
@@ -824,7 +824,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
||||
throw new IllegalArgumentException("locationFactory is null");
|
||||
}
|
||||
return new LocationIterator() {
|
||||
private long lastTrackPointId = startTrackPointId;
|
||||
private long lastTrackPointId = -1L;
|
||||
private Cursor cursor = getCursor(startTrackPointId);
|
||||
private final CachedTrackPointsIndexes
|
||||
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.
|
||||
*/
|
||||
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);
|
||||
cursor.close();
|
||||
cursor = getCursor(trackPointId);
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -25,121 +25,137 @@ import android.location.Location;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
/**
|
||||
* Tests for the MyTracks map overlay.
|
||||
* Tests {@link MapOverlay}.
|
||||
*
|
||||
* @author Bartlomiej Niechwiej
|
||||
* @author Vangelis S.
|
||||
*/
|
||||
public class MapOverlayTest extends AndroidTestCase {
|
||||
private Canvas canvas;
|
||||
private MockMyTracksOverlay myTracksOverlay;
|
||||
private MapView mockView;
|
||||
private MockMyTracksOverlay mockMyTracksOverlay;
|
||||
private MapView mapView;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
canvas = new Canvas();
|
||||
myTracksOverlay = new MockMyTracksOverlay(getContext());
|
||||
mockMyTracksOverlay = new MockMyTracksOverlay(getContext());
|
||||
|
||||
// Enable drawing.
|
||||
myTracksOverlay.setTrackDrawingEnabled(true);
|
||||
mockMyTracksOverlay.setTrackDrawingEnabled(true);
|
||||
|
||||
// Set a TrackPathPainter with a MockPath.
|
||||
myTracksOverlay.setTrackPathPainter(new SingleColorTrackPathPainter(getContext()) {
|
||||
@Override
|
||||
mockMyTracksOverlay.setTrackPathPainter(new SingleColorTrackPathPainter(getContext()) {
|
||||
@Override
|
||||
public Path newPath() {
|
||||
return new MockPath();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mockView = null;
|
||||
|
||||
mapView = null;
|
||||
}
|
||||
|
||||
public void testAddLocation() throws Exception {
|
||||
|
||||
/**
|
||||
* Tests {@link MapOverlay#addLocation(Location)}.
|
||||
*/
|
||||
public void testAddLocation() throws Exception {
|
||||
Location location = new Location("gps");
|
||||
location.setLatitude(10);
|
||||
location.setLongitude(20);
|
||||
myTracksOverlay.addLocation(location);
|
||||
assertEquals(1, myTracksOverlay.getNumLocations());
|
||||
assertEquals(0, myTracksOverlay.getNumWaypoints());
|
||||
|
||||
mockMyTracksOverlay.addLocation(location);
|
||||
assertEquals(1, mockMyTracksOverlay.getNumLocations());
|
||||
assertEquals(0, mockMyTracksOverlay.getNumWaypoints());
|
||||
|
||||
location.setLatitude(20);
|
||||
location.setLongitude(30);
|
||||
myTracksOverlay.addLocation(location);
|
||||
assertEquals(2, myTracksOverlay.getNumLocations());
|
||||
assertEquals(0, myTracksOverlay.getNumWaypoints());
|
||||
assertNull(myTracksOverlay.getTrackPathPainter().getLastPath());
|
||||
|
||||
mockMyTracksOverlay.addLocation(location);
|
||||
assertEquals(2, mockMyTracksOverlay.getNumLocations());
|
||||
assertEquals(0, mockMyTracksOverlay.getNumWaypoints());
|
||||
assertFalse(mockMyTracksOverlay.getTrackPathPainter().hasPath());
|
||||
|
||||
// Draw and make sure that we don't lose any point.
|
||||
myTracksOverlay.draw(canvas, mockView, false);
|
||||
assertEquals(2, myTracksOverlay.getNumLocations());
|
||||
assertEquals(0, myTracksOverlay.getNumWaypoints());
|
||||
assertNotNull(myTracksOverlay.getTrackPathPainter().getLastPath());
|
||||
assertTrue(myTracksOverlay.getTrackPathPainter().getLastPath() instanceof MockPath);
|
||||
MockPath path = (MockPath) myTracksOverlay.getTrackPathPainter().getLastPath();
|
||||
assertEquals(2, path.totalPoints);
|
||||
|
||||
myTracksOverlay.draw(canvas, mockView, true);
|
||||
assertEquals(2, myTracksOverlay.getNumLocations());
|
||||
assertEquals(0, myTracksOverlay.getNumWaypoints());
|
||||
assertNotNull(myTracksOverlay.getTrackPathPainter().getLastPath());
|
||||
mockMyTracksOverlay.draw(canvas, mapView, false);
|
||||
assertEquals(2, mockMyTracksOverlay.getNumLocations());
|
||||
assertEquals(0, mockMyTracksOverlay.getNumWaypoints());
|
||||
assertTrue(mockMyTracksOverlay.getTrackPathPainter().hasPath());
|
||||
SingleColorTrackPathPainter trackPathPainter = (SingleColorTrackPathPainter) mockMyTracksOverlay
|
||||
.getTrackPathPainter();
|
||||
MockPath path = (MockPath) trackPathPainter.getPath();
|
||||
assertEquals(2, path.getTotalPoints());
|
||||
|
||||
mockMyTracksOverlay.draw(canvas, mapView, true);
|
||||
assertEquals(2, mockMyTracksOverlay.getNumLocations());
|
||||
assertEquals(0, mockMyTracksOverlay.getNumWaypoints());
|
||||
assertTrue(mockMyTracksOverlay.getTrackPathPainter().hasPath());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests {@link MapOverlay#clearPoints()}.
|
||||
*/
|
||||
public void testClearPoints() throws Exception {
|
||||
Location location = new Location("gps");
|
||||
location.setLatitude(10);
|
||||
location.setLongitude(20);
|
||||
myTracksOverlay.addLocation(location);
|
||||
assertEquals(1, myTracksOverlay.getNumLocations());
|
||||
myTracksOverlay.clearPoints();
|
||||
assertEquals(0, myTracksOverlay.getNumLocations());
|
||||
mockMyTracksOverlay.addLocation(location);
|
||||
assertEquals(1, mockMyTracksOverlay.getNumLocations());
|
||||
mockMyTracksOverlay.clearPoints();
|
||||
assertEquals(0, mockMyTracksOverlay.getNumLocations());
|
||||
|
||||
// Same after drawing on canvas.
|
||||
// Test after drawing on canvas
|
||||
final int locations = 100;
|
||||
for (int i = 0; i < locations; ++i) {
|
||||
myTracksOverlay.addLocation(location);
|
||||
mockMyTracksOverlay.addLocation(location);
|
||||
}
|
||||
assertEquals(locations, myTracksOverlay.getNumLocations());
|
||||
myTracksOverlay.draw(canvas, mockView, false);
|
||||
myTracksOverlay.draw(canvas, mockView, true);
|
||||
myTracksOverlay.clearPoints();
|
||||
assertEquals(0, myTracksOverlay.getNumLocations());
|
||||
assertEquals(locations, mockMyTracksOverlay.getNumLocations());
|
||||
mockMyTracksOverlay.draw(canvas, mapView, false);
|
||||
mockMyTracksOverlay.draw(canvas, mapView, true);
|
||||
mockMyTracksOverlay.clearPoints();
|
||||
assertEquals(0, mockMyTracksOverlay.getNumLocations());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link MapOverlay#addWaypoint(Waypoint)}.
|
||||
*/
|
||||
public void testAddWaypoint() throws Exception {
|
||||
Location location = new Location("gps");
|
||||
location.setLatitude(10);
|
||||
location.setLongitude(20);
|
||||
Waypoint waypoint = new Waypoint();
|
||||
waypoint.setLocation(location);
|
||||
myTracksOverlay.addWaypoint(waypoint);
|
||||
assertEquals(1, myTracksOverlay.getNumWaypoints());
|
||||
assertEquals(0, myTracksOverlay.getNumLocations());
|
||||
assertNull(myTracksOverlay.getTrackPathPainter().getLastPath());
|
||||
|
||||
mockMyTracksOverlay.addWaypoint(waypoint);
|
||||
assertEquals(1, mockMyTracksOverlay.getNumWaypoints());
|
||||
assertEquals(0, mockMyTracksOverlay.getNumLocations());
|
||||
assertFalse(mockMyTracksOverlay.getTrackPathPainter().hasPath());
|
||||
|
||||
final int waypoints = 10;
|
||||
for (int i = 0; i < waypoints; ++i) {
|
||||
waypoint = new Waypoint();
|
||||
waypoint.setLocation(location);
|
||||
myTracksOverlay.addWaypoint(waypoint);
|
||||
mockMyTracksOverlay.addWaypoint(waypoint);
|
||||
}
|
||||
assertEquals(1 + waypoints, myTracksOverlay.getNumWaypoints());
|
||||
assertEquals(0, myTracksOverlay.getNumLocations());
|
||||
assertNull(myTracksOverlay.getTrackPathPainter().getLastPath());
|
||||
assertEquals(1 + waypoints, mockMyTracksOverlay.getNumWaypoints());
|
||||
assertEquals(0, mockMyTracksOverlay.getNumLocations());
|
||||
assertFalse(mockMyTracksOverlay.getTrackPathPainter().hasPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link MapOverlay#clearWaypoints()}.
|
||||
*/
|
||||
public void testClearWaypoints() throws Exception {
|
||||
Location location = new Location("gps");
|
||||
location.setLatitude(10);
|
||||
location.setLongitude(20);
|
||||
Waypoint waypoint = new Waypoint();
|
||||
waypoint.setLocation(location);
|
||||
myTracksOverlay.addWaypoint(waypoint);
|
||||
assertEquals(1, myTracksOverlay.getNumWaypoints());
|
||||
myTracksOverlay.clearWaypoints();
|
||||
assertEquals(0, myTracksOverlay.getNumWaypoints());
|
||||
mockMyTracksOverlay.addWaypoint(waypoint);
|
||||
assertEquals(1, mockMyTracksOverlay.getNumWaypoints());
|
||||
mockMyTracksOverlay.clearWaypoints();
|
||||
assertEquals(0, mockMyTracksOverlay.getNumWaypoints());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests {@link MapOverlay#draw(Canvas, MapView, boolean)}.
|
||||
*/
|
||||
public void testDrawing() {
|
||||
Location location = new Location("gps");
|
||||
location.setLatitude(10);
|
||||
@@ -147,30 +163,30 @@ public class MapOverlayTest extends AndroidTestCase {
|
||||
location.setLongitude(20 + i);
|
||||
Waypoint waypoint = new Waypoint();
|
||||
waypoint.setLocation(location);
|
||||
myTracksOverlay.addWaypoint(waypoint);
|
||||
mockMyTracksOverlay.addWaypoint(waypoint);
|
||||
}
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
location = new Location("gps");
|
||||
location.setLatitude(20 + i / 2);
|
||||
location.setLongitude(150 - i);
|
||||
myTracksOverlay.addLocation(location);
|
||||
mockMyTracksOverlay.addLocation(location);
|
||||
}
|
||||
|
||||
|
||||
// Shadow.
|
||||
myTracksOverlay.draw(canvas, mockView, true);
|
||||
// We don't expect to do anything if
|
||||
assertNull(myTracksOverlay.getTrackPathPainter().getLastPath());
|
||||
assertEquals(40, myTracksOverlay.getNumWaypoints());
|
||||
assertEquals(100, myTracksOverlay.getNumLocations());
|
||||
mockMyTracksOverlay.draw(canvas, mapView, true);
|
||||
// We don't expect to do anything if
|
||||
assertFalse(mockMyTracksOverlay.getTrackPathPainter().hasPath());
|
||||
assertEquals(40, mockMyTracksOverlay.getNumWaypoints());
|
||||
assertEquals(100, mockMyTracksOverlay.getNumLocations());
|
||||
|
||||
// No shadow.
|
||||
myTracksOverlay.draw(canvas, mockView, false);
|
||||
assertNotNull(myTracksOverlay.getTrackPathPainter().getLastPath());
|
||||
assertTrue(myTracksOverlay.getTrackPathPainter().getLastPath() instanceof MockPath);
|
||||
MockPath path = (MockPath) myTracksOverlay.getTrackPathPainter().getLastPath();
|
||||
assertEquals(40, myTracksOverlay.getNumWaypoints());
|
||||
assertEquals(100, myTracksOverlay.getNumLocations());
|
||||
assertEquals(100, path.totalPoints);
|
||||
// TODO: Check the points from the path (and the segments).
|
||||
mockMyTracksOverlay.draw(canvas, mapView, false);
|
||||
assertTrue(mockMyTracksOverlay.getTrackPathPainter().hasPath());
|
||||
SingleColorTrackPathPainter trackPathPainter = (SingleColorTrackPathPainter) mockMyTracksOverlay
|
||||
.getTrackPathPainter();
|
||||
MockPath path = (MockPath) trackPathPainter.getPath();
|
||||
assertEquals(40, mockMyTracksOverlay.getNumWaypoints());
|
||||
assertEquals(100, mockMyTracksOverlay.getNumLocations());
|
||||
assertEquals(100, path.getTotalPoints());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,30 +22,28 @@ import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
|
||||
/**
|
||||
* Elements for Tests for the MyTracks map overlay.
|
||||
* A mock {@link MapOverlay} for testing.
|
||||
*
|
||||
* @author Bartlomiej Niechwiej
|
||||
* @author Vangelis S.
|
||||
*
|
||||
* A mock version of {@code MapOverlay} that does not use
|
||||
* {@class MapView}.
|
||||
*/
|
||||
public class MockMyTracksOverlay extends MapOverlay {
|
||||
|
||||
private Projection mockProjection;
|
||||
private Projection projection;
|
||||
|
||||
public MockMyTracksOverlay(Context context) {
|
||||
super(context);
|
||||
mockProjection = new MockProjection();
|
||||
projection = new MockProjection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Projection getMapProjection(MapView mapView) {
|
||||
return mockProjection;
|
||||
return projection;
|
||||
}
|
||||
|
||||
@Override
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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 Vangelis S.
|
||||
*
|
||||
* A mock class that intercepts {@code Path}'s and records calls to
|
||||
* {@code #moveTo()} and {@code #lineTo()}.
|
||||
*/
|
||||
public class MockPath extends Path {
|
||||
|
||||
// A list of disjointed path segments.
|
||||
private final List<List<PointF>> segments = new LinkedList<List<PointF>>();
|
||||
|
||||
/** A list of disjoined path segments. */
|
||||
public 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;
|
||||
|
||||
@Override
|
||||
@@ -49,12 +48,20 @@ public class MockPath extends Path {
|
||||
currentSegment.add(new PointF(x, y));
|
||||
totalPoints++;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void moveTo(float x, float y) {
|
||||
super.moveTo(x, y);
|
||||
segments.add(currentSegment =
|
||||
new ArrayList<PointF>(Arrays.asList(new PointF(x, y))));
|
||||
currentSegment = new ArrayList<PointF>(Arrays.asList(new PointF(x, y)));
|
||||
segments.add(currentSegment);
|
||||
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
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package com.google.android.apps.mytracks;
|
||||
|
||||
import com.google.android.maps.GeoPoint;
|
||||
@@ -21,18 +22,17 @@ import com.google.android.maps.Projection;
|
||||
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 Vangelis S.
|
||||
*
|
||||
* A mock {@code Projection} that acts as the identity matrix.
|
||||
*/
|
||||
public class MockProjection implements Projection {
|
||||
|
||||
@Override
|
||||
public Point toPixels(GeoPoint in, Point out) {
|
||||
return out;
|
||||
public Point toPixels(GeoPoint geoPoint, Point point) {
|
||||
return point;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+26
-22
@@ -51,6 +51,9 @@ public class MyTracksProviderUtilsImplTest extends AndroidTestCase {
|
||||
private static final String MOCK_DESC = "Mock Next Waypoint Desc!";
|
||||
private static final String TEST_DESC = "Test Desc!";
|
||||
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
|
||||
protected void setUp() throws Exception {
|
||||
@@ -273,13 +276,13 @@ public class MyTracksProviderUtilsImplTest extends AndroidTestCase {
|
||||
// Check whether all have been deleted.
|
||||
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null,
|
||||
TracksColumns._ID);
|
||||
assertTrue(tracksCursor.getCount() == 0);
|
||||
assertEquals(0, tracksCursor.getCount());
|
||||
tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI, null, null,
|
||||
null, TrackPointsColumns._ID);
|
||||
assertTrue(tracksPointsCursor.getCount() == 0);
|
||||
assertEquals(0, tracksPointsCursor.getCount());
|
||||
waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null,
|
||||
null, WaypointsColumns._ID);
|
||||
assertTrue(waypointCursor.getCount() == 0);
|
||||
assertEquals(0, waypointCursor.getCount());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -328,9 +331,7 @@ public class MyTracksProviderUtilsImplTest extends AndroidTestCase {
|
||||
@UsesMocks(Cursor.class)
|
||||
public void testCreateTrack() {
|
||||
Cursor cursorMock = AndroidMock.createNiceMock(Cursor.class);
|
||||
// The startColumnIndex can be any value, just make it to 2 in case it's
|
||||
// hard coded to 0 or 1 in some where.
|
||||
int startColumnIndex = 2;
|
||||
int startColumnIndex = 1;
|
||||
int columnIndex = startColumnIndex;
|
||||
AndroidMock.expect(cursorMock.getColumnIndexOrThrow(TracksColumns._ID))
|
||||
.andReturn(columnIndex++);
|
||||
@@ -359,7 +360,7 @@ public class MyTracksProviderUtilsImplTest extends AndroidTestCase {
|
||||
* Tests the method {@link MyTracksProviderUtilsImpl#createContentValues(Track)}.
|
||||
*/
|
||||
@UsesMocks(TripStatistics.class)
|
||||
public void testCreateContentValues_Track() {
|
||||
public void testCreateContentValues_track() {
|
||||
// ID
|
||||
long trackId = System.currentTimeMillis();
|
||||
// Name
|
||||
@@ -645,9 +646,7 @@ public class MyTracksProviderUtilsImplTest extends AndroidTestCase {
|
||||
@UsesMocks(Cursor.class)
|
||||
public void testCreateWaypoint() {
|
||||
Cursor cursorMock = AndroidMock.createNiceMock(Cursor.class);
|
||||
// The startColumnIndex can be any value, just make it to 2 in case it's
|
||||
// hard coded to 0 or 1 in some where.
|
||||
int startColumnIndex = 2;
|
||||
int startColumnIndex = 1;
|
||||
int columnIndex = startColumnIndex;
|
||||
AndroidMock.expect(cursorMock.getColumnIndexOrThrow(WaypointsColumns._ID))
|
||||
.andReturn(columnIndex++);
|
||||
@@ -683,7 +682,7 @@ public class MyTracksProviderUtilsImplTest extends AndroidTestCase {
|
||||
/**
|
||||
* Tests the method {@link MyTracksProviderUtilsImpl#createContentValues(Waypoint)}.
|
||||
*/
|
||||
public void testCreateContentValues_Waypoint() {
|
||||
public void testCreateContentValues_waypoint() {
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
providerUtils.insertTrack(track);
|
||||
@@ -819,10 +818,10 @@ public class MyTracksProviderUtilsImplTest extends AndroidTestCase {
|
||||
|
||||
LocationIterator locationIterator = providerUtils.getLocationIterator(trackId,
|
||||
startTrackPointId, true, MyTracksProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
assertEquals(startTrackPointId, locationIterator.getLocationId());
|
||||
for (int i = 1; i >= 0; i--) {
|
||||
assertTrue(locationIterator.hasNext());
|
||||
Location location = locationIterator.next();
|
||||
assertEquals(2 + (i - 1), locationIterator.getLocationId());
|
||||
checkLocation(i, location);
|
||||
}
|
||||
assertFalse(locationIterator.hasNext());
|
||||
@@ -843,10 +842,11 @@ public class MyTracksProviderUtilsImplTest extends AndroidTestCase {
|
||||
|
||||
LocationIterator locationIterator = providerUtils.getLocationIterator(trackId,
|
||||
startTrackPointId, false, MyTracksProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
assertEquals(startTrackPointId, locationIterator.getLocationId());
|
||||
|
||||
for (int i = 1; i < 10; i++) {
|
||||
assertTrue(locationIterator.hasNext());
|
||||
Location location = locationIterator.next();
|
||||
assertEquals(2 + (i - 1), locationIterator.getLocationId());
|
||||
checkLocation(i, location);
|
||||
}
|
||||
assertFalse(locationIterator.hasNext());
|
||||
@@ -871,6 +871,7 @@ public class MyTracksProviderUtilsImplTest extends AndroidTestCase {
|
||||
* test also covers the method
|
||||
* {@link MyTracksProviderUtilsImpl#createContentValues(Location, long)}.
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
public void testInsertTrackPoint() {
|
||||
// Insert track, point at first.
|
||||
long trackId = System.currentTimeMillis();
|
||||
@@ -888,6 +889,7 @@ public class MyTracksProviderUtilsImplTest extends AndroidTestCase {
|
||||
* {@link MyTracksProviderUtilsImpl#createContentValues(Location, long)} and
|
||||
* the inner class {@link MyTracksProviderUtilsImpl.CachedTrackPointsIndexes}.
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
public void testBulkInsertTrackPoints() {
|
||||
// Insert track, point at first.
|
||||
long trackId = System.currentTimeMillis();
|
||||
@@ -908,6 +910,7 @@ public class MyTracksProviderUtilsImplTest extends AndroidTestCase {
|
||||
* and the inner class
|
||||
* {@link MyTracksProviderUtilsImpl.CachedTrackPointsIndexes}.
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
@UsesMocks(Cursor.class)
|
||||
public void testCreateLocation() {
|
||||
Cursor cursorMock = AndroidMock.createNiceMock(Cursor.class);
|
||||
@@ -984,7 +987,7 @@ public class MyTracksProviderUtilsImplTest extends AndroidTestCase {
|
||||
track.setId(id);
|
||||
track.setName("Test: " + id);
|
||||
track.setNumberOfPoints(numPoints);
|
||||
for(int i=0; i < numPoints; i++) {
|
||||
for(int i = 0; i < numPoints; i++) {
|
||||
track.addLocation(createLocation(i));
|
||||
}
|
||||
return track;
|
||||
@@ -996,26 +999,27 @@ public class MyTracksProviderUtilsImplTest extends AndroidTestCase {
|
||||
* @return created location
|
||||
*/
|
||||
private Location createLocation(int i) {
|
||||
|
||||
Location loc = new Location("test");
|
||||
loc.setLatitude(37.0 + (double) i / 10000.0);
|
||||
loc.setLongitude(57.0 - (double) i / 10000.0);
|
||||
loc.setLatitude(INITIAL_LATITUDE + (double) i / 10000.0);
|
||||
loc.setLongitude(INITIAL_LONGITUDE - (double) i / 10000.0);
|
||||
loc.setAccuracy((float) i / 100.0f);
|
||||
loc.setAltitude(i * 2.5);
|
||||
loc.setAltitude(i * ALTITUDE_INTERVAL);
|
||||
return loc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the value of a location.
|
||||
*
|
||||
* @param i the index of this location which created in the method {@link
|
||||
* this#getTrack(long, int)}
|
||||
* @param i the index of this location which created in the method
|
||||
* {@link MyTracksProviderUtilsImplTest#getTrack(long, int)}
|
||||
* @param location the location to be checked
|
||||
*/
|
||||
private void checkLocation(int i, Location location) {
|
||||
assertEquals(37.0 + (double) i / 10000.0, location.getLatitude());
|
||||
assertEquals(57.0 - (double) i / 10000.0, location.getLongitude());
|
||||
assertEquals(INITIAL_LATITUDE + (double) i / 10000.0, location.getLatitude());
|
||||
assertEquals(INITIAL_LONGITUDE - (double) i / 10000.0, location.getLongitude());
|
||||
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
+10
-9
@@ -273,24 +273,25 @@ public class CreateAndSendTrackTest extends ActivityInstrumentationTestCase2<Tra
|
||||
*/
|
||||
public void testSplitSetting() {
|
||||
EndToEndTestUtils.startRecording();
|
||||
|
||||
|
||||
EndToEndTestUtils.findMenuItem(activityMyTracks.getString(R.string.menu_split_frequency), true);
|
||||
boolean isFoundKM = EndToEndTestUtils.SOLO.searchText(EndToEndTestUtils.ONE_KM);
|
||||
if(isFoundKM) {
|
||||
EndToEndTestUtils.SOLO.clickOnText(EndToEndTestUtils.ONE_KM);
|
||||
boolean isFoundKM = EndToEndTestUtils.SOLO.searchText(EndToEndTestUtils.KM);
|
||||
if (isFoundKM) {
|
||||
EndToEndTestUtils.SOLO.clickOnText(EndToEndTestUtils.KM, 0);
|
||||
} 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.
|
||||
EndToEndTestUtils.sendGps(20);
|
||||
|
||||
|
||||
|
||||
EndToEndTestUtils.findMenuItem(activityMyTracks.getString(R.string.menu_markers), true);
|
||||
instrumentation.waitForIdleSync();
|
||||
assertTrue(EndToEndTestUtils.SOLO.getCurrentListViews().get(0).getCount() > 0);
|
||||
EndToEndTestUtils.SOLO.goBack();
|
||||
|
||||
|
||||
EndToEndTestUtils.stopRecording(true);
|
||||
}
|
||||
|
||||
|
||||
+21
-19
@@ -77,18 +77,20 @@ public class EndToEndTestUtils {
|
||||
// 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> 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> ONE_MILE_MULTILINGUAL = new HashMap<String, String>();
|
||||
private static final HashMap<String, String> KM_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 VIEW_MODE = "";
|
||||
public static String ONE_KM = "";
|
||||
public static String ONE_MILE = "";
|
||||
public static String KM = "";
|
||||
public static String MILE = "";
|
||||
|
||||
public static int SHORT_WAIT_TIME = 2000;
|
||||
public static int NORMAL_WAIT_TIME = 8000;
|
||||
public static int LONG_WAIT_TIME = 15000;
|
||||
public static int SUPER_LONG_WAIT_TIME = 100000;
|
||||
|
||||
public static String deviceLanguage = "";
|
||||
|
||||
static {
|
||||
RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL.put("es", "mins ago");
|
||||
RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL.put("de", "Minuten");
|
||||
@@ -102,17 +104,17 @@ public class EndToEndTestUtils {
|
||||
VIEW_MODE_MULTILINGUAL.put("ar", "وضع");
|
||||
VIEW_MODE_MULTILINGUAL.put("zh", "模式");
|
||||
|
||||
ONE_KM_MULTILINGUAL.put("es", "1 km");
|
||||
ONE_KM_MULTILINGUAL.put("de", "1 km");
|
||||
ONE_KM_MULTILINGUAL.put("fr", "1 km");
|
||||
ONE_KM_MULTILINGUAL.put("ar", "١ كم");
|
||||
ONE_KM_MULTILINGUAL.put("zh", "1 公里");
|
||||
KM_MULTILINGUAL.put("es", "km");
|
||||
KM_MULTILINGUAL.put("de", "km");
|
||||
KM_MULTILINGUAL.put("fr", "km");
|
||||
KM_MULTILINGUAL.put("ar", "كم");
|
||||
KM_MULTILINGUAL.put("zh", "公里");
|
||||
|
||||
ONE_MILE_MULTILINGUAL.put("es", "1 mi");
|
||||
ONE_MILE_MULTILINGUAL.put("de", "1 mi");
|
||||
ONE_MILE_MULTILINGUAL.put("fr", "1 mile");
|
||||
ONE_MILE_MULTILINGUAL.put("ar", "١ ميل");
|
||||
ONE_MILE_MULTILINGUAL.put("zh", "1 英里");
|
||||
MILE_MULTILINGUAL.put("es", "mi");
|
||||
MILE_MULTILINGUAL.put("de", "mi");
|
||||
MILE_MULTILINGUAL.put("fr", "mile");
|
||||
MILE_MULTILINGUAL.put("ar", "ميل");
|
||||
MILE_MULTILINGUAL.put("zh", "英里");
|
||||
}
|
||||
|
||||
static Solo SOLO;
|
||||
@@ -130,17 +132,17 @@ public class EndToEndTestUtils {
|
||||
* Checks the language, then sets the fields with right string.
|
||||
*/
|
||||
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) {
|
||||
RELATIVE_STARTTIME_POSTFIX = RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL.get(deviceLanguage);
|
||||
VIEW_MODE = VIEW_MODE_MULTILINGUAL.get(deviceLanguage);
|
||||
ONE_KM = ONE_KM_MULTILINGUAL.get(deviceLanguage);
|
||||
ONE_MILE = ONE_MILE_MULTILINGUAL.get(deviceLanguage);
|
||||
KM = KM_MULTILINGUAL.get(deviceLanguage);
|
||||
MILE = MILE_MULTILINGUAL.get(deviceLanguage);
|
||||
} else {
|
||||
RELATIVE_STARTTIME_POSTFIX = RELATIVE_STARTTIME_POSTFIX_MULTILINGUAL.get("es");
|
||||
VIEW_MODE = VIEW_MODE_MULTILINGUAL.get("es");
|
||||
ONE_KM = ONE_KM_MULTILINGUAL.get("es");
|
||||
ONE_MILE = ONE_MILE_MULTILINGUAL.get("es");
|
||||
KM = KM_MULTILINGUAL.get("es");
|
||||
MILE = MILE_MULTILINGUAL.get("es");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-34
@@ -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,
|
||||
* 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.
|
||||
*/
|
||||
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);
|
||||
DynamicSpeedTrackPathDescriptor dynamicSpeedTrackPathDescriptor = new DynamicSpeedTrackPathDescriptor(
|
||||
context);
|
||||
assertEquals(false, dynamicSpeedTrackPathDescriptor.needsRedraw());
|
||||
assertEquals(false, dynamicSpeedTrackPathDescriptor.updateState());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link DynamicSpeedTrackPathDescriptor#needsRedraw()} by different
|
||||
* Tests {@link DynamicSpeedTrackPathDescriptor#updateState()} by different
|
||||
* averageMovingSpeed.
|
||||
*/
|
||||
public void testIsDiffereceSignificant() {
|
||||
@@ -145,8 +116,7 @@ public class DynamicSpeedTrackPathDescriptorTest extends AndroidTestCase {
|
||||
// Difference is more than CRITICAL_DIFFERENCE_PERCENTAGE
|
||||
30 * (1 + (DynamicSpeedTrackPathDescriptor.CRITICAL_DIFFERENCE_PERCENTAGE / 100.00) * 2) };
|
||||
boolean[] expectedValues = { true, false, false, true };
|
||||
double[] expectedAverageMovingSpeed = { 20, 30, 30,
|
||||
30 * (1 + (DynamicSpeedTrackPathDescriptor.CRITICAL_DIFFERENCE_PERCENTAGE / 100.00) * 2) };
|
||||
double[] expectedAverageMovingSpeed = { 0, 30, 30, 30 };
|
||||
// Test
|
||||
for (int i = 0; i < newAverageMovingSpeed.length; i++) {
|
||||
dynamicSpeedTrackPathDescriptor.setAverageMovingSpeed(averageMovingSpeeds[i]);
|
||||
|
||||
+3
-3
@@ -62,7 +62,7 @@ public class DynamicSpeedTrackPathPainterTest extends TrackPathPainterTestCase {
|
||||
public void testUpdatePath_AllInvalidLocation() {
|
||||
List<CachedLocation> points = createCachedLocations(NUMBER_OF_LOCATIONS, INVALID_LATITUDE, -1);
|
||||
dynamicSpeedTrackPathPainter.updatePath(myTracksOverlay.getMapProjection(mockView),
|
||||
myTracksOverlay.getMapViewRect(mockView), 1, true, points);
|
||||
myTracksOverlay.getMapViewRect(mockView), 1, points);
|
||||
AndroidMock.verify(trackPathDescriptor);
|
||||
// Should be zero for there is no valid locations.
|
||||
assertEquals(0, dynamicSpeedTrackPathPainter.getColoredPaths().size());
|
||||
@@ -80,7 +80,7 @@ public class DynamicSpeedTrackPathPainterTest extends TrackPathPainterTestCase {
|
||||
// Gets a number as the start index of points.
|
||||
int startLocationIdx = NUMBER_OF_LOCATIONS / 2;
|
||||
dynamicSpeedTrackPathPainter.updatePath(myTracksOverlay.getMapProjection(mockView),
|
||||
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, true, points);
|
||||
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, points);
|
||||
|
||||
AndroidMock.verify(trackPathDescriptor);
|
||||
assertEquals(NUMBER_OF_LOCATIONS - startLocationIdx, dynamicSpeedTrackPathPainter
|
||||
@@ -123,7 +123,7 @@ public class DynamicSpeedTrackPathPainterTest extends TrackPathPainterTestCase {
|
||||
slowSpeed));
|
||||
|
||||
dynamicSpeedTrackPathPainter.updatePath(myTracksOverlay.getMapProjection(mockView),
|
||||
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, true, points);
|
||||
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, points);
|
||||
|
||||
AndroidMock.verify(trackPathDescriptor);
|
||||
List<ColoredPath> coloredPath = dynamicSpeedTrackPathPainter.getColoredPaths();
|
||||
|
||||
+16
-14
@@ -31,9 +31,10 @@ import java.util.List;
|
||||
*/
|
||||
public class SingleColorTrackPathPainterTest extends TrackPathPainterTestCase {
|
||||
|
||||
private static final int NUMBER_OF_LOCATIONS = 10;
|
||||
private SingleColorTrackPathPainter singleColorTrackPathPainter;
|
||||
private Path pathMock;
|
||||
private static final int NUMBER_OF_LOCATIONS = 100;
|
||||
|
||||
|
||||
/**
|
||||
* Initials a mocked TrackPathDescriptor object and
|
||||
@@ -44,8 +45,8 @@ public class SingleColorTrackPathPainterTest extends TrackPathPainterTestCase {
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
pathMock = AndroidMock.createStrictMock(Path.class);
|
||||
singleColorTrackPathPainter = new SingleColorTrackPathPainter(getContext());
|
||||
pathMock = AndroidMock.createStrictMock(Path.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,11 +55,12 @@ public class SingleColorTrackPathPainterTest extends TrackPathPainterTestCase {
|
||||
* method when all locations are valid.
|
||||
*/
|
||||
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.
|
||||
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++) {
|
||||
pathMock.lineTo(0, 0);
|
||||
@@ -66,7 +68,7 @@ public class SingleColorTrackPathPainterTest extends TrackPathPainterTestCase {
|
||||
|
||||
AndroidMock.replay(pathMock);
|
||||
singleColorTrackPathPainter.updatePath(myTracksOverlay.getMapProjection(mockView),
|
||||
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, true, points, pathMock);
|
||||
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, points, pathMock);
|
||||
AndroidMock.verify(pathMock);
|
||||
}
|
||||
|
||||
@@ -76,20 +78,20 @@ public class SingleColorTrackPathPainterTest extends TrackPathPainterTestCase {
|
||||
* method when all locations are invalid.
|
||||
*/
|
||||
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);
|
||||
// Gets a random number from 1 to numberOfLocations.
|
||||
int startLocationIdx = NUMBER_OF_LOCATIONS / 2;
|
||||
AndroidMock.replay(pathMock);
|
||||
singleColorTrackPathPainter.updatePath(myTracksOverlay.getMapProjection(mockView),
|
||||
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, true, points, pathMock);
|
||||
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, points, pathMock);
|
||||
AndroidMock.verify(pathMock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the
|
||||
* {@link SingleColorTrackPathPainter#updatePath(com.google.android.maps.Projection, android.graphics.Rect, int, Boolean, List, Path)}
|
||||
* method when there are three segments.
|
||||
* Tests the {@link SingleColorTrackPathPainter#updatePath(com.google.android.maps.Projection,
|
||||
* android.graphics.Rect, int, Boolean, List, Path)} method when there are
|
||||
* three segments.
|
||||
*/
|
||||
public void testUpdatePath_ThreeSegments() {
|
||||
// First segment.
|
||||
@@ -103,7 +105,7 @@ public class SingleColorTrackPathPainterTest extends TrackPathPainterTestCase {
|
||||
points.addAll(createCachedLocations(NUMBER_OF_LOCATIONS, TrackStubUtils.INITIAL_LATITUDE, -1));
|
||||
// Gets a random number from 1 to numberOfLocations.
|
||||
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++) {
|
||||
pathMock.lineTo(0, 0);
|
||||
}
|
||||
@@ -118,7 +120,7 @@ public class SingleColorTrackPathPainterTest extends TrackPathPainterTestCase {
|
||||
|
||||
AndroidMock.replay(pathMock);
|
||||
singleColorTrackPathPainter.updatePath(myTracksOverlay.getMapProjection(mockView),
|
||||
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, true, points, pathMock);
|
||||
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, points, pathMock);
|
||||
AndroidMock.verify(pathMock);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-5
@@ -40,13 +40,12 @@ public class TrackPathDescriptorDynamicSpeedTest extends TrackPathPainterTestCas
|
||||
myTracksOverlay.setTrackPathPainter(painter);
|
||||
|
||||
int startLocationIdx = 0;
|
||||
Boolean alwaysVisible = true;
|
||||
|
||||
|
||||
assertNotNull(painter);
|
||||
painter.updatePath(myTracksOverlay.getMapProjection(mockView),
|
||||
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, alwaysVisible,
|
||||
myTracksOverlay.getMapViewRect(mockView), startLocationIdx,
|
||||
myTracksOverlay.getPoints());
|
||||
assertNotNull(myTracksOverlay.getTrackPathPainter().getLastPath());
|
||||
painter.drawTrack(canvas);
|
||||
assertNotNull(myTracksOverlay.getTrackPathPainter().hasPath());
|
||||
painter.drawPath(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-6
@@ -40,13 +40,11 @@ public class TrackPathDescriptorFixedSpeedTest extends TrackPathPainterTestCase
|
||||
myTracksOverlay.setTrackPathPainter(painter);
|
||||
|
||||
int startLocationIdx = 0;
|
||||
Boolean alwaysVisible = true;
|
||||
|
||||
|
||||
assertNotNull(painter);
|
||||
painter.updatePath(myTracksOverlay.getMapProjection(mockView),
|
||||
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, alwaysVisible,
|
||||
myTracksOverlay.getPoints());
|
||||
assertNotNull(myTracksOverlay.getTrackPathPainter().getLastPath());
|
||||
painter.drawTrack(canvas);
|
||||
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, myTracksOverlay.getPoints());
|
||||
assertNotNull(myTracksOverlay.getTrackPathPainter().hasPath());
|
||||
painter.drawPath(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-5
@@ -55,8 +55,7 @@ public class TrackPathPainterFactoryTest extends TrackPathPainterTestCase {
|
||||
context, R.string.track_color_mode_key, context.getString(track_color_mode));
|
||||
|
||||
int startLocationIdx = 0;
|
||||
Boolean alwaysVisible = true;
|
||||
|
||||
|
||||
TrackPathPainter painter = TrackPathPainterFactory.getTrackPathPainter(context);
|
||||
myTracksOverlay.setTrackPathPainter(painter);
|
||||
|
||||
@@ -64,9 +63,9 @@ public class TrackPathPainterFactoryTest extends TrackPathPainterTestCase {
|
||||
assertTrue(c.isInstance(painter));
|
||||
|
||||
painter.updatePath(myTracksOverlay.getMapProjection(mockView),
|
||||
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, alwaysVisible,
|
||||
myTracksOverlay.getMapViewRect(mockView), startLocationIdx,
|
||||
myTracksOverlay.getPoints());
|
||||
assertNotNull(myTracksOverlay.getTrackPathPainter().getLastPath());
|
||||
painter.drawTrack(canvas);
|
||||
assertNotNull(myTracksOverlay.getTrackPathPainter().hasPath());
|
||||
painter.drawPath(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-4
@@ -39,13 +39,12 @@ public class TrackPathPainterSingleColorTest extends TrackPathPainterTestCase {
|
||||
myTracksOverlay.setTrackPathPainter(painter);
|
||||
|
||||
int startLocationIdx = 0;
|
||||
Boolean alwaysVisible = true;
|
||||
|
||||
assertNotNull(painter);
|
||||
painter.updatePath(myTracksOverlay.getMapProjection(mockView),
|
||||
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, alwaysVisible,
|
||||
myTracksOverlay.getMapViewRect(mockView), startLocationIdx,
|
||||
myTracksOverlay.getPoints());
|
||||
assertNotNull(myTracksOverlay.getTrackPathPainter().getLastPath());
|
||||
painter.drawTrack(canvas);
|
||||
assertNotNull(myTracksOverlay.getTrackPathPainter().hasPath());
|
||||
painter.drawPath(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user