forked from upstream-mirrors/OpenTracks
Merging japanese translation
This commit is contained in:
@@ -0,0 +1,12 @@
|
|||||||
|
#Thu Aug 12 18:54:52 BRT 2010
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||||
|
org.eclipse.jdt.core.compiler.compliance=1.6
|
||||||
|
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||||
|
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||||
|
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||||
|
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||||
|
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||||
|
org.eclipse.jdt.core.compiler.source=1.6
|
||||||
@@ -10,4 +10,4 @@
|
|||||||
# Indicates whether an apk should be generated for each density.
|
# Indicates whether an apk should be generated for each density.
|
||||||
split.density=false
|
split.density=false
|
||||||
# Project target.
|
# Project target.
|
||||||
target=Google Inc.:Google APIs:7
|
target=Google Inc.:Google APIs:8
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import com.google.android.apps.mytracks.content.Track;
|
|||||||
import com.google.android.apps.mytracks.content.Waypoint;
|
import com.google.android.apps.mytracks.content.Waypoint;
|
||||||
import com.google.android.apps.mytracks.io.AuthManager;
|
import com.google.android.apps.mytracks.io.AuthManager;
|
||||||
import com.google.android.apps.mytracks.io.AuthManagerFactory;
|
import com.google.android.apps.mytracks.io.AuthManagerFactory;
|
||||||
import com.google.android.apps.mytracks.io.GpxImport;
|
import com.google.android.apps.mytracks.io.GpxImporter;
|
||||||
import com.google.android.apps.mytracks.io.SendToDocs;
|
import com.google.android.apps.mytracks.io.SendToDocs;
|
||||||
import com.google.android.apps.mytracks.io.SendToMyMaps;
|
import com.google.android.apps.mytracks.io.SendToMyMaps;
|
||||||
import com.google.android.apps.mytracks.io.TrackWriter;
|
import com.google.android.apps.mytracks.io.TrackWriter;
|
||||||
@@ -62,24 +62,25 @@ import android.view.Menu;
|
|||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.Window;
|
||||||
import android.view.View.OnTouchListener;
|
import android.view.View.OnTouchListener;
|
||||||
import android.view.ViewGroup.LayoutParams;
|
import android.view.ViewGroup.LayoutParams;
|
||||||
import android.view.Window;
|
|
||||||
import android.view.WindowManager.BadTokenException;
|
import android.view.WindowManager.BadTokenException;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
import android.widget.TabHost;
|
import android.widget.TabHost;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.xml.sax.SAXException;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.io.InputStream;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The super activity that embeds our sub activities.
|
* The super activity that embeds our sub activities.
|
||||||
*
|
*
|
||||||
@@ -950,13 +951,14 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
|||||||
Thread t = new Thread() {
|
Thread t = new Thread() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
boolean success = false;
|
|
||||||
ArrayList<Track> tracks = new ArrayList<Track>();
|
|
||||||
int message = R.string.success;
|
int message = R.string.success;
|
||||||
|
|
||||||
|
long[] trackIdsImported = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
try {
|
try {
|
||||||
GpxImport.importGPXFile(fileName, tracks);
|
InputStream is = new FileInputStream(fileName);
|
||||||
success = true;
|
trackIdsImported = GpxImporter.importGPXFile(is, providerUtils);
|
||||||
} catch (SAXException e) {
|
} catch (SAXException e) {
|
||||||
Log.e(MyTracksConstants.TAG, "Caught an unexpected exception.", e);
|
Log.e(MyTracksConstants.TAG, "Caught an unexpected exception.", e);
|
||||||
message = R.string.error_generic;
|
message = R.string.error_generic;
|
||||||
@@ -973,15 +975,11 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
|||||||
Log.e(MyTracksConstants.TAG, "Caught an unexpected exception.", e);
|
Log.e(MyTracksConstants.TAG, "Caught an unexpected exception.", e);
|
||||||
message = R.string.error_out_of_memory;
|
message = R.string.error_out_of_memory;
|
||||||
}
|
}
|
||||||
if (success) {
|
if (trackIdsImported != null && trackIdsImported.length > 0) {
|
||||||
long trackId = -1;
|
// select last track from import file
|
||||||
for (Track track : tracks) {
|
setSelectedTrack(trackIdsImported[trackIdsImported.length - 1]);
|
||||||
Uri uri = providerUtils.insertTrackAndTrackPoints(track);
|
|
||||||
trackId = Long.parseLong(uri.getLastPathSegment());
|
|
||||||
}
|
|
||||||
setSelectedTrack(trackId);
|
|
||||||
} else {
|
} else {
|
||||||
MyTracks.this.showMessageDialog(message, false/*success*/);
|
MyTracks.this.showMessageDialog(message, false/* success */);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
runOnUiThread(new Runnable() {
|
runOnUiThread(new Runnable() {
|
||||||
|
|||||||
@@ -167,24 +167,25 @@ public class MyTracksProvider extends ContentProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int delete(Uri url, String where, String[] selectionArgs) {
|
public int delete(Uri url, String where, String[] selectionArgs) {
|
||||||
if (urlMatcher.match(url) == TRACKPOINTS) {
|
String table;
|
||||||
Log.w(MyTracksProvider.TAG, "provider trackpoints delete!");
|
switch (urlMatcher.match(url)) {
|
||||||
int count = db.delete(TRACKPOINTS_TABLE, where, selectionArgs);
|
case TRACKPOINTS:
|
||||||
getContext().getContentResolver().notifyChange(url, null, true);
|
table = TRACKPOINTS_TABLE;
|
||||||
return count;
|
break;
|
||||||
} else if (urlMatcher.match(url) == TRACKS) {
|
case TRACKS:
|
||||||
Log.w(MyTracksProvider.TAG, "provider track delete!");
|
table = TRACKS_TABLE;
|
||||||
int count = db.delete(TRACKS_TABLE, where, selectionArgs);
|
break;
|
||||||
getContext().getContentResolver().notifyChange(url, null, true);
|
case WAYPOINTS:
|
||||||
return count;
|
table = WAYPOINTS_TABLE;
|
||||||
} else if (urlMatcher.match(url) == WAYPOINTS) {
|
break;
|
||||||
Log.w(MyTracksProvider.TAG, "provider waypoint delete!");
|
default:
|
||||||
int count = db.delete(WAYPOINTS_TABLE, where, selectionArgs);
|
throw new IllegalArgumentException("Unknown URL " + url);
|
||||||
getContext().getContentResolver().notifyChange(url, null, true);
|
|
||||||
return count;
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("Unknown URL " + url);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log.w(MyTracksProvider.TAG, "provider delete in " + table + "!");
|
||||||
|
int count = db.delete(table, where, selectionArgs);
|
||||||
|
getContext().getContentResolver().notifyChange(url, null, true);
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -216,19 +217,49 @@ public class MyTracksProvider extends ContentProvider {
|
|||||||
} else {
|
} else {
|
||||||
values = new ContentValues();
|
values = new ContentValues();
|
||||||
}
|
}
|
||||||
if (urlMatcher.match(url) == TRACKPOINTS) {
|
|
||||||
return insertTrackPoint(url, values);
|
int urlMatchType = urlMatcher.match(url);
|
||||||
} else if (urlMatcher.match(url) == TRACKS) {
|
return insertType(url, urlMatchType, values);
|
||||||
return insertTrack(url, values);
|
}
|
||||||
} else if (urlMatcher.match(url) == WAYPOINTS) {
|
|
||||||
return insertWaypoint(url, values);
|
private Uri insertType(Uri url, int urlMatchType, ContentValues values) {
|
||||||
} else {
|
switch (urlMatchType) {
|
||||||
throw new IllegalArgumentException("Unknown URL " + url);
|
case TRACKPOINTS:
|
||||||
|
return insertTrackPoint(url, values);
|
||||||
|
case TRACKS:
|
||||||
|
return insertTrack(url, values);
|
||||||
|
case WAYPOINTS:
|
||||||
|
return insertWaypoint(url, values);
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unknown URL " + url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int bulkInsert(Uri url, ContentValues[] valuesBulk) {
|
||||||
|
Log.d(MyTracksProvider.TAG, "MyTracksProvider.bulkInsert");
|
||||||
|
int numInserted = 0;
|
||||||
|
try {
|
||||||
|
// Use a transaction in order to make the insertions run as a single batch
|
||||||
|
db.beginTransaction();
|
||||||
|
|
||||||
|
int urlMatch = urlMatcher.match(url);
|
||||||
|
for (numInserted = 0; numInserted < valuesBulk.length; numInserted++) {
|
||||||
|
ContentValues values = valuesBulk[numInserted];
|
||||||
|
if (values == null) { values = new ContentValues(); }
|
||||||
|
|
||||||
|
insertType(url, urlMatch, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
db.setTransactionSuccessful();
|
||||||
|
} finally {
|
||||||
|
db.endTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
return numInserted;
|
||||||
|
}
|
||||||
|
|
||||||
private Uri insertTrackPoint(Uri url, ContentValues values) {
|
private Uri insertTrackPoint(Uri url, ContentValues values) {
|
||||||
Log.d(MyTracksProvider.TAG, "MyTracksProvider.insertTrackPoint");
|
|
||||||
boolean hasLat = values.containsKey(TrackPointsColumns.LATITUDE);
|
boolean hasLat = values.containsKey(TrackPointsColumns.LATITUDE);
|
||||||
boolean hasLong = values.containsKey(TrackPointsColumns.LONGITUDE);
|
boolean hasLong = values.containsKey(TrackPointsColumns.LONGITUDE);
|
||||||
boolean hasTime = values.containsKey(TrackPointsColumns.TIME);
|
boolean hasTime = values.containsKey(TrackPointsColumns.TIME);
|
||||||
|
|||||||
@@ -232,6 +232,17 @@ public interface MyTracksProviderUtils {
|
|||||||
*/
|
*/
|
||||||
Uri insertTrackPoint(Location location, long trackId);
|
Uri insertTrackPoint(Location location, long trackId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts multiple track points in a single operation.
|
||||||
|
*
|
||||||
|
* @param locations an array of locations to insert
|
||||||
|
* @param length the number of locations (from the beginning of the array)
|
||||||
|
* to actually insert, or -1 for all of them
|
||||||
|
* @param trackId the ID of the track to insert the points into
|
||||||
|
* @return the number of points inserted
|
||||||
|
*/
|
||||||
|
int bulkInsertTrackPoints(Location[] locations, int length, long trackId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts a waypoint in the provider.
|
* Inserts a waypoint in the provider.
|
||||||
*
|
*
|
||||||
@@ -240,14 +251,6 @@ public interface MyTracksProviderUtils {
|
|||||||
*/
|
*/
|
||||||
Uri insertWaypoint(Waypoint waypoint);
|
Uri insertWaypoint(Waypoint waypoint);
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts a track including its track points in the provider.
|
|
||||||
*
|
|
||||||
* @param track the track to insert
|
|
||||||
* @return the content provider URI for the inserted track
|
|
||||||
*/
|
|
||||||
Uri insertTrackAndTrackPoints(Track track);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests if a track with given id exists.
|
* Tests if a track with given id exists.
|
||||||
*
|
*
|
||||||
@@ -270,7 +273,7 @@ public interface MyTracksProviderUtils {
|
|||||||
* @param cursor a cursor pointing at a db or provider with locations
|
* @param cursor a cursor pointing at a db or provider with locations
|
||||||
* @return a new location object
|
* @return a new location object
|
||||||
*/
|
*/
|
||||||
public Location createLocation(Cursor cursor);
|
Location createLocation(Cursor cursor);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a waypoint object from a given cursor.
|
* Creates a waypoint object from a given cursor.
|
||||||
@@ -278,7 +281,7 @@ public interface MyTracksProviderUtils {
|
|||||||
* @param cursor a cursor pointing at a db or provider with waypoints.
|
* @param cursor a cursor pointing at a db or provider with waypoints.
|
||||||
* @return a new waypoint object
|
* @return a new waypoint object
|
||||||
*/
|
*/
|
||||||
public Waypoint createWaypoint(Cursor cursor);
|
Waypoint createWaypoint(Cursor cursor);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A factory which can produce instances of {@link MyTracksProviderUtils},
|
* A factory which can produce instances of {@link MyTracksProviderUtils},
|
||||||
|
|||||||
+14
-27
@@ -840,8 +840,6 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
Cursor cursor = getLocationsCursor(track.getId(),
|
Cursor cursor = getLocationsCursor(track.getId(),
|
||||||
startingPoint,
|
startingPoint,
|
||||||
buffer.getSize(), false);
|
buffer.getSize(), false);
|
||||||
final int idColumnIdx =
|
|
||||||
cursor.getColumnIndexOrThrow(TrackPointsColumns._ID);
|
|
||||||
if (cursor == null) {
|
if (cursor == null) {
|
||||||
Log.w(MyTracksProvider.TAG, "Cannot get a locations cursor!");
|
Log.w(MyTracksProvider.TAG, "Cannot get a locations cursor!");
|
||||||
buffer.setInvalid();
|
buffer.setInvalid();
|
||||||
@@ -860,6 +858,8 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final int idColumnIdx =
|
||||||
|
cursor.getColumnIndexOrThrow(TrackPointsColumns._ID);
|
||||||
do {
|
do {
|
||||||
Location location = createLocation(cursor);
|
Location location = createLocation(cursor);
|
||||||
if (location == null) {
|
if (location == null) {
|
||||||
@@ -900,6 +900,18 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
createContentValues(location, trackId));
|
createContentValues(location, trackId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int bulkInsertTrackPoints(Location[] locations, int length, long trackId) {
|
||||||
|
if (length == -1) { length = locations.length; }
|
||||||
|
|
||||||
|
ContentValues[] values = new ContentValues[length];
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
values[i] = createContentValues(locations[i], trackId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.getContentResolver().bulkInsert(TrackPointsColumns.CONTENT_URI, values);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Uri insertWaypoint(Waypoint waypoint) {
|
public Uri insertWaypoint(Waypoint waypoint) {
|
||||||
Log.d(MyTracksProvider.TAG, "MyTracksProviderUtilsImpl.insertWaypoint");
|
Log.d(MyTracksProvider.TAG, "MyTracksProviderUtilsImpl.insertWaypoint");
|
||||||
@@ -908,31 +920,6 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
createContentValues(waypoint));
|
createContentValues(waypoint));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Uri insertTrackAndTrackPoints(Track track) {
|
|
||||||
// Insert the track right away to have its ID
|
|
||||||
Uri trackUri = insertTrack(track);
|
|
||||||
long trackId = Long.parseLong(trackUri.getLastPathSegment());
|
|
||||||
track.setId(trackId);
|
|
||||||
|
|
||||||
// Insert all the points (associated with the track ID)
|
|
||||||
boolean firstPoint = true;
|
|
||||||
long pointId = -1;
|
|
||||||
for (Location location : track.getLocations()) {
|
|
||||||
Uri pointUri = insertTrackPoint(location, trackId);
|
|
||||||
pointId = Long.parseLong(pointUri.getLastPathSegment());
|
|
||||||
if (firstPoint) {
|
|
||||||
track.setStartId(pointId);
|
|
||||||
firstPoint = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
track.setStopId(pointId);
|
|
||||||
|
|
||||||
// Update the track with the start and end point IDs
|
|
||||||
updateTrack(track);
|
|
||||||
return trackUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean trackExists(long id) {
|
public boolean trackExists(long id) {
|
||||||
Cursor cursor = null;
|
Cursor cursor = null;
|
||||||
|
|||||||
@@ -1,237 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2008 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.io;
|
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.content.Track;
|
|
||||||
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
|
|
||||||
import com.google.android.apps.mytracks.util.MyTracksUtils;
|
|
||||||
|
|
||||||
import android.location.Location;
|
|
||||||
|
|
||||||
import org.w3c.dom.Document;
|
|
||||||
import org.w3c.dom.NamedNodeMap;
|
|
||||||
import org.w3c.dom.Node;
|
|
||||||
import org.w3c.dom.NodeList;
|
|
||||||
import org.xml.sax.SAXException;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.text.ParseException;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import javax.xml.parsers.DocumentBuilder;
|
|
||||||
import javax.xml.parsers.DocumentBuilderFactory;
|
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper class to parse a GPX file or string and convert it into a track
|
|
||||||
* object.
|
|
||||||
*
|
|
||||||
* TODO: See if we can use a SAX style parser as the DOM style
|
|
||||||
* parsing uses too much memory and will not allow import of very large GPX
|
|
||||||
* files (limit currently set to 500KB).
|
|
||||||
*
|
|
||||||
* @author Leif Hendrik Wilden
|
|
||||||
*/
|
|
||||||
public class GpxImport {
|
|
||||||
private static final SimpleDateFormat DATE_FORMAT1 =
|
|
||||||
new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ssZ");
|
|
||||||
private static final SimpleDateFormat DATE_FORMAT2 =
|
|
||||||
new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'");
|
|
||||||
private static final SimpleDateFormat DATE_FORMAT3 =
|
|
||||||
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads GPS tracks from a GPX file and append tracks and their coordinates to
|
|
||||||
* the given list of tracks.
|
|
||||||
*
|
|
||||||
* @param tracks a list of tracks
|
|
||||||
* @param filename a file name
|
|
||||||
* @throws SAXException a parsing error
|
|
||||||
* @throws ParserConfigurationException internal error
|
|
||||||
* @throws IOException a file reading problem
|
|
||||||
*/
|
|
||||||
public static void importGPXFile(
|
|
||||||
final String filename, final ArrayList<Track> tracks)
|
|
||||||
throws SAXException, ParserConfigurationException, IOException,
|
|
||||||
OutOfMemoryError {
|
|
||||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
|
||||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
|
||||||
File file = new File(filename);
|
|
||||||
if (file.length() > 500 * 1024) {
|
|
||||||
// Better to throw an error now then, to let the VM do it. If the VM does
|
|
||||||
// it, it will throw another exception while throwing the OutOfMemoryError
|
|
||||||
// and that will kill out app with a nasty error message. In that way we
|
|
||||||
// can at least display a reasonable message to the user and let her know
|
|
||||||
// what the problem is.
|
|
||||||
throw new OutOfMemoryError();
|
|
||||||
}
|
|
||||||
Document doc = builder.parse(file);
|
|
||||||
importGPXDocument(tracks, doc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is a utility class with only static members.
|
|
||||||
*/
|
|
||||||
private GpxImport() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads GPS tracks from a GPX document and append then to the given list of
|
|
||||||
* tracks.
|
|
||||||
*
|
|
||||||
* TODO: Break this method down into smaller ones
|
|
||||||
*
|
|
||||||
* @param tracks an array list of tracks
|
|
||||||
* @param doc a document
|
|
||||||
*/
|
|
||||||
public static void importGPXDocument(
|
|
||||||
final ArrayList<Track> tracks, final Document doc) {
|
|
||||||
NodeList trackNodes = doc.getElementsByTagName("trk");
|
|
||||||
for (int n = 0; n < trackNodes.getLength(); n++) {
|
|
||||||
Track track = new Track();
|
|
||||||
tracks.add(track);
|
|
||||||
ArrayList<Location> locations = track.getLocations();
|
|
||||||
Location lastLocation = null;
|
|
||||||
|
|
||||||
Node trkNode = trackNodes.item(n);
|
|
||||||
NodeList segmentNodes = trkNode.getChildNodes();
|
|
||||||
ArrayList<Node> nodes = new ArrayList<Node>();
|
|
||||||
int nSegments = 0;
|
|
||||||
for (int k = 0; k < segmentNodes.getLength(); k++) {
|
|
||||||
Node segmentNode = segmentNodes.item(k);
|
|
||||||
if (segmentNode.getNodeName().equals("name")) {
|
|
||||||
track.setName(segmentNode.getFirstChild().getNodeValue());
|
|
||||||
} else if (segmentNode.getNodeName().equals("description")) {
|
|
||||||
track.setDescription(segmentNode.getFirstChild().getNodeValue());
|
|
||||||
} else if (segmentNode.getNodeName().equals("trkseg")) {
|
|
||||||
if (nSegments > 0) {
|
|
||||||
// Add a segment separator:
|
|
||||||
Location location = new Location("gps");
|
|
||||||
location.setLatitude(100.0);
|
|
||||||
location.setLongitude(100.0);
|
|
||||||
location.setAltitude(0);
|
|
||||||
if (locations.size() > 0) {
|
|
||||||
long pointTime = locations.get(locations.size() - 1).getTime();
|
|
||||||
location.setTime(pointTime);
|
|
||||||
}
|
|
||||||
track.addLocation(location);
|
|
||||||
lastLocation = null;
|
|
||||||
}
|
|
||||||
nSegments++;
|
|
||||||
NodeList segmentChildren = segmentNode.getChildNodes();
|
|
||||||
nodes.clear();
|
|
||||||
for (int j = 0; j < segmentChildren.getLength(); j++) {
|
|
||||||
Node child = segmentChildren.item(j);
|
|
||||||
if (child.getNodeName().equals("trkpt")) {
|
|
||||||
nodes.add(child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int i = 0; i < nodes.size(); i++) {
|
|
||||||
NamedNodeMap namedNodes = nodes.get(i).getAttributes();
|
|
||||||
double lat = Double.parseDouble(namedNodes.getNamedItem("lat")
|
|
||||||
.getNodeValue());
|
|
||||||
double lon = Double.parseDouble(namedNodes.getNamedItem("lon")
|
|
||||||
.getNodeValue());
|
|
||||||
NodeList children = nodes.get(i).getChildNodes();
|
|
||||||
Node elementNode = null;
|
|
||||||
Node timeNode = null;
|
|
||||||
for (int j = 0; j < children.getLength(); j++) {
|
|
||||||
Node child = children.item(j);
|
|
||||||
if (child.getNodeName().equals("ele")) {
|
|
||||||
elementNode = child;
|
|
||||||
} else if (child.getNodeName().equals("time")) {
|
|
||||||
timeNode = child;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String altitudeStr = null;
|
|
||||||
if (elementNode != null) {
|
|
||||||
altitudeStr = elementNode.getFirstChild().getNodeValue();
|
|
||||||
} else {
|
|
||||||
altitudeStr = "0";
|
|
||||||
}
|
|
||||||
String timeContents = null;
|
|
||||||
if (timeNode != null) {
|
|
||||||
timeContents = timeNode.getFirstChild().getNodeValue();
|
|
||||||
} else {
|
|
||||||
timeContents = "";
|
|
||||||
}
|
|
||||||
double altitude = Double.parseDouble(altitudeStr);
|
|
||||||
long t = -1;
|
|
||||||
try {
|
|
||||||
// 1st try with time zone at end a la "+0000"
|
|
||||||
t = DATE_FORMAT1.parse(timeContents).getTime();
|
|
||||||
} catch (ParseException e) {
|
|
||||||
// if that fails, try with a literal "Z" at the end (this is not
|
|
||||||
// according to xml standard, but some gpx files are like that):
|
|
||||||
try {
|
|
||||||
t = DATE_FORMAT2.parse(timeContents).getTime();
|
|
||||||
} catch (ParseException ex) {
|
|
||||||
// some gpx timestamps have 3 additional digits at the end.
|
|
||||||
try {
|
|
||||||
t = DATE_FORMAT3.parse(timeContents).getTime();
|
|
||||||
} catch (ParseException exc) {
|
|
||||||
t = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Location location = new Location("gps");
|
|
||||||
location.setLatitude(lat);
|
|
||||||
location.setLongitude(lon);
|
|
||||||
location.setAltitude(altitude);
|
|
||||||
location.setTime(t);
|
|
||||||
// We don't have a speed and bearing in GPX, make something up from
|
|
||||||
// the last two points:
|
|
||||||
if (lastLocation != null) {
|
|
||||||
final long dt = location.getTime() - lastLocation.getTime();
|
|
||||||
if (dt > 0) {
|
|
||||||
final float speed =
|
|
||||||
location.distanceTo(lastLocation) / (dt / 1000);
|
|
||||||
location.setSpeed(speed);
|
|
||||||
}
|
|
||||||
location.setBearing(lastLocation.bearingTo(location));
|
|
||||||
}
|
|
||||||
lastLocation = location;
|
|
||||||
if (MyTracksUtils.isValidLocation(location)) {
|
|
||||||
track.addLocation(location);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (locations.size() > 0) {
|
|
||||||
long startTime = locations.get(0).getTime();
|
|
||||||
|
|
||||||
// Calculate statistics for the imported track
|
|
||||||
TripStatisticsBuilder statsBuilder = new TripStatisticsBuilder();
|
|
||||||
statsBuilder.resumeAt(startTime);
|
|
||||||
for (Location location : locations) {
|
|
||||||
if (MyTracksUtils.isValidLocation(location)) {
|
|
||||||
/* Any time works here. The totalTime will be set by "pauseAt" later: */
|
|
||||||
statsBuilder.addLocation(location, location.getTime());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
long lastPointTime = locations.get(locations.size() - 1).getTime();
|
|
||||||
statsBuilder.pauseAt(lastPointTime);
|
|
||||||
|
|
||||||
track.setStatistics(statsBuilder.getStatistics());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,574 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010 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.io;
|
||||||
|
|
||||||
|
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||||
|
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||||
|
import com.google.android.apps.mytracks.content.Track;
|
||||||
|
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
|
||||||
|
import com.google.android.apps.mytracks.util.MyTracksUtils;
|
||||||
|
|
||||||
|
import android.location.Location;
|
||||||
|
import android.location.LocationManager;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.SimpleTimeZone;
|
||||||
|
|
||||||
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
import javax.xml.parsers.SAXParser;
|
||||||
|
import javax.xml.parsers.SAXParserFactory;
|
||||||
|
|
||||||
|
import org.xml.sax.Attributes;
|
||||||
|
import org.xml.sax.Locator;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
import org.xml.sax.helpers.DefaultHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Imports GPX XML files to the my tracks provider.
|
||||||
|
*
|
||||||
|
* TODO: Show progress indication to the user.
|
||||||
|
*
|
||||||
|
* @author Leif Hendrik Wilden
|
||||||
|
* @author Steffen Horlacher
|
||||||
|
* @author Rodrigo Damazio
|
||||||
|
*/
|
||||||
|
public class GpxImporter extends DefaultHandler {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Different date formats used in GPX files
|
||||||
|
*/
|
||||||
|
static final SimpleDateFormat DATE_FORMAT1 = new SimpleDateFormat(
|
||||||
|
"yyyy-MM-dd'T'HH:mm:ssZ");
|
||||||
|
static final SimpleDateFormat DATE_FORMAT2 = new SimpleDateFormat(
|
||||||
|
"yyyy-MM-dd'T'HH:mm:ss'Z'");
|
||||||
|
static final SimpleDateFormat DATE_FORMAT3 = new SimpleDateFormat(
|
||||||
|
"yyyy-MM-dd'T'HH:mm:ss.SSSZ");
|
||||||
|
static final SimpleTimeZone UTC_TIMEZONE = new SimpleTimeZone(0, "UTC");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GPX-XML tag names and attributes.
|
||||||
|
*/
|
||||||
|
private static final String TAG_TRACK = "trk";
|
||||||
|
private static final String TAG_TRACK_POINT = "trkpt";
|
||||||
|
private static final Object TAG_TRACK_SEGMENT = "trkseg";
|
||||||
|
private static final String TAG_NAME = "name";
|
||||||
|
private static final String TAG_DESCRIPTION = "desc";
|
||||||
|
private static final String TAG_ALTITUDE = "ele";
|
||||||
|
private static final String TAG_TIME = "time";
|
||||||
|
private static final String ATT_LAT = "lat";
|
||||||
|
private static final String ATT_LON = "lon";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum number of locations to buffer for bulk-insertion into the database.
|
||||||
|
*/
|
||||||
|
private static final int MAX_BUFFERED_LOCATIONS = 512;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilities for accessing the contnet provider.
|
||||||
|
*/
|
||||||
|
private final MyTracksProviderUtils providerUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of track ids written in the database. Only contains successfully
|
||||||
|
* written tracks.
|
||||||
|
*/
|
||||||
|
private final List<Long> tracksWritten;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains the current elements content.
|
||||||
|
*/
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Currently reading location.
|
||||||
|
*/
|
||||||
|
private Location location;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Previous location, required for calculations.
|
||||||
|
*/
|
||||||
|
private Location lastLocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Currently reading track.
|
||||||
|
*/
|
||||||
|
private Track track;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Statistics builder for the current track.
|
||||||
|
*/
|
||||||
|
private TripStatisticsBuilder statsBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Buffer of locations to be bulk-inserted into the database.
|
||||||
|
*/
|
||||||
|
private Location[] bufferedPointInserts = new Location[MAX_BUFFERED_LOCATIONS];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of locations buffered to be inserted into the database.
|
||||||
|
*/
|
||||||
|
private int numBufferedPointInserts = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of locations already processed.
|
||||||
|
*/
|
||||||
|
private int numberOfLocations;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of segments already processed.
|
||||||
|
*/
|
||||||
|
private int numberOfSegments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to identify if a track was written to the database but not yet
|
||||||
|
* finished successfully.
|
||||||
|
*/
|
||||||
|
private boolean isCurrentTrackRollbackable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag to indicate if we're inside a track's xml element.
|
||||||
|
* Some sub elements like name may be used in other parts of the gpx file,
|
||||||
|
* and we use this to ignore them.
|
||||||
|
*/
|
||||||
|
private boolean isInTrackElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counter to find out which child level of track we are processing.
|
||||||
|
*/
|
||||||
|
private int trackChildDepth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SAX-Locator to get current line information.
|
||||||
|
*/
|
||||||
|
private Locator locator;
|
||||||
|
private Location lastSegmentLocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads GPS tracks from a GPX file and writes tracks and their coordinates to
|
||||||
|
* the database.
|
||||||
|
*
|
||||||
|
* @param tracks a list of tracks
|
||||||
|
* @param is a input steam with gpx-xml data
|
||||||
|
* @return long[] array of track ids written in the database
|
||||||
|
* @throws SAXException a parsing error
|
||||||
|
* @throws ParserConfigurationException internal error
|
||||||
|
* @throws IOException a file reading problem
|
||||||
|
*/
|
||||||
|
public static long[] importGPXFile(final InputStream is,
|
||||||
|
final MyTracksProviderUtils providerUtils)
|
||||||
|
throws ParserConfigurationException, SAXException, IOException {
|
||||||
|
SAXParserFactory factory = SAXParserFactory.newInstance();
|
||||||
|
GpxImporter handler = new GpxImporter(providerUtils);
|
||||||
|
SAXParser parser = factory.newSAXParser();
|
||||||
|
long[] trackIds = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
|
||||||
|
parser.parse(is, handler);
|
||||||
|
|
||||||
|
long end = System.currentTimeMillis();
|
||||||
|
Log.d(MyTracksConstants.TAG, "Total import time: " + (end - start) + "ms");
|
||||||
|
|
||||||
|
trackIds = handler.getImportedTrackIds();
|
||||||
|
} finally {
|
||||||
|
// delete track if not finished
|
||||||
|
handler.rollbackUnfinishedTracks();
|
||||||
|
}
|
||||||
|
|
||||||
|
return trackIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor, requires providerUtils for writing tracks the database.
|
||||||
|
*/
|
||||||
|
public GpxImporter(MyTracksProviderUtils providerUtils) {
|
||||||
|
this.providerUtils = providerUtils;
|
||||||
|
tracksWritten = new ArrayList<Long>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void characters(char[] ch, int start, int length) throws SAXException {
|
||||||
|
String newContent = new String(ch, start, length);
|
||||||
|
if (content == null) {
|
||||||
|
content = newContent;
|
||||||
|
} else {
|
||||||
|
// In 99% of the cases, a single call to this method will be made for each
|
||||||
|
// sequence of characters we're interested in, so we'll rarely be
|
||||||
|
// concatenating strings, thus not justifying the use of a StringBuilder.
|
||||||
|
content += newContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startElement(String uri, String localName, String name,
|
||||||
|
Attributes attributes) throws SAXException {
|
||||||
|
if (isInTrackElement) {
|
||||||
|
trackChildDepth++;
|
||||||
|
if (localName.equals(TAG_TRACK_POINT)) {
|
||||||
|
onTrackPointElementStart(attributes);
|
||||||
|
} else if (localName.equals(TAG_TRACK_SEGMENT)) {
|
||||||
|
onTrackSegmentElementStart();
|
||||||
|
} else if (localName.equals(TAG_TRACK)) {
|
||||||
|
String msg = createErrorMessage("Invalid GPX-XML detected");
|
||||||
|
throw new SAXException(msg);
|
||||||
|
}
|
||||||
|
} else if (localName.equals(TAG_TRACK)) {
|
||||||
|
isInTrackElement = true;
|
||||||
|
trackChildDepth = 0;
|
||||||
|
onTrackElementStart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endElement(String uri, String localName, String name)
|
||||||
|
throws SAXException {
|
||||||
|
if (!isInTrackElement) { return; }
|
||||||
|
|
||||||
|
// process these elements only as sub-elements of track
|
||||||
|
if (localName.equals(TAG_TRACK_POINT)) {
|
||||||
|
onTrackPointElementEnd();
|
||||||
|
} else if (localName.equals(TAG_ALTITUDE)) {
|
||||||
|
onAltitudeElementEnd();
|
||||||
|
} else if (localName.equals(TAG_TIME)) {
|
||||||
|
onTimeElementEnd();
|
||||||
|
} else if (localName.equals(TAG_NAME)) {
|
||||||
|
// we are only interested in the first level name element
|
||||||
|
if (trackChildDepth == 1) {
|
||||||
|
onNameElementEnd();
|
||||||
|
}
|
||||||
|
} else if (localName.equals(TAG_DESCRIPTION)) {
|
||||||
|
// we are only interested in the first level description element
|
||||||
|
if (trackChildDepth == 1) {
|
||||||
|
onDescriptionElementEnd();
|
||||||
|
}
|
||||||
|
} else if (localName.equals(TAG_TRACK_SEGMENT)) {
|
||||||
|
onTrackSegmentElementEnd();
|
||||||
|
} else if (localName.equals(TAG_TRACK)) {
|
||||||
|
onTrackElementEnd();
|
||||||
|
isInTrackElement = false;
|
||||||
|
trackChildDepth = 0;
|
||||||
|
}
|
||||||
|
trackChildDepth--;
|
||||||
|
|
||||||
|
// reset element content
|
||||||
|
content = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDocumentLocator(Locator locator) {
|
||||||
|
this.locator = locator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new Track object and insert empty track in database. Track will be
|
||||||
|
* updated with missing values later.
|
||||||
|
*/
|
||||||
|
private void onTrackElementStart() {
|
||||||
|
track = new Track();
|
||||||
|
numberOfLocations = 0;
|
||||||
|
|
||||||
|
Uri trackUri = providerUtils.insertTrack(track);
|
||||||
|
long trackId = Long.parseLong(trackUri.getLastPathSegment());
|
||||||
|
track.setId(trackId);
|
||||||
|
isCurrentTrackRollbackable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onDescriptionElementEnd() {
|
||||||
|
track.setDescription(content.toString().trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onNameElementEnd() {
|
||||||
|
track.setName(content.toString().trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Track segment started.
|
||||||
|
*/
|
||||||
|
private void onTrackSegmentElementStart() {
|
||||||
|
if (numberOfSegments > 0) {
|
||||||
|
// Add a segment separator:
|
||||||
|
location = new Location(LocationManager.GPS_PROVIDER);
|
||||||
|
location.setLatitude(100.0);
|
||||||
|
location.setLongitude(100.0);
|
||||||
|
location.setAltitude(0);
|
||||||
|
if (lastLocation != null) {
|
||||||
|
location.setTime(lastLocation.getTime());
|
||||||
|
}
|
||||||
|
insertTrackPoint(location);
|
||||||
|
lastLocation = location;
|
||||||
|
lastSegmentLocation = null;
|
||||||
|
location = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
numberOfSegments++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads trackpoint attributes and assigns them to the current location.
|
||||||
|
*
|
||||||
|
* @param attributes xml attributes
|
||||||
|
*/
|
||||||
|
private void onTrackPointElementStart(Attributes attributes) throws SAXException {
|
||||||
|
if (location != null) {
|
||||||
|
String errorMsg = createErrorMessage("Found a track point inside another one.");
|
||||||
|
throw new SAXException(errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
location = createLocationFromAttributes(attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and returns a location with the position parsed from the given
|
||||||
|
* attributes.
|
||||||
|
*
|
||||||
|
* @param attributes the attributes to parse
|
||||||
|
* @return the created location
|
||||||
|
* @throws SAXException if the attributes cannot be parsed
|
||||||
|
*/
|
||||||
|
private Location createLocationFromAttributes(Attributes attributes) throws SAXException {
|
||||||
|
String latitude = attributes.getValue(ATT_LAT);
|
||||||
|
String longitude = attributes.getValue(ATT_LON);
|
||||||
|
|
||||||
|
if (latitude == null || longitude == null) {
|
||||||
|
throw new SAXException(createErrorMessage("Point with no longitude or latitude"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// create new location and set attributes
|
||||||
|
Location loc = new Location(LocationManager.GPS_PROVIDER);
|
||||||
|
loc.setLatitude(Double.parseDouble(latitude));
|
||||||
|
loc.setLongitude(Double.parseDouble(longitude));
|
||||||
|
return loc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Track point finished, write in database.
|
||||||
|
*
|
||||||
|
* @throws SAXException - thrown if track point is invalid
|
||||||
|
*/
|
||||||
|
private void onTrackPointElementEnd() throws SAXException {
|
||||||
|
if (MyTracksUtils.isValidLocation(location)) {
|
||||||
|
statsBuilder.addLocation(location, location.getTime());
|
||||||
|
|
||||||
|
// insert in db
|
||||||
|
insertTrackPoint(location);
|
||||||
|
|
||||||
|
// first track point?
|
||||||
|
if (lastLocation == null && numberOfSegments == 1) {
|
||||||
|
track.setStartId(getLastPointId());
|
||||||
|
}
|
||||||
|
|
||||||
|
lastLocation = location;
|
||||||
|
lastSegmentLocation = location;
|
||||||
|
location = null;
|
||||||
|
} else {
|
||||||
|
// invalid location - abort import
|
||||||
|
String msg = createErrorMessage("Invalid location detected: " + location);
|
||||||
|
throw new SAXException(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void insertTrackPoint(Location loc) {
|
||||||
|
bufferedPointInserts[numBufferedPointInserts] = loc;
|
||||||
|
numBufferedPointInserts++;
|
||||||
|
numberOfLocations++;
|
||||||
|
|
||||||
|
if (numBufferedPointInserts >= MAX_BUFFERED_LOCATIONS) {
|
||||||
|
flushPointInserts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void flushPointInserts() {
|
||||||
|
if (numBufferedPointInserts <= 0) { return; }
|
||||||
|
|
||||||
|
providerUtils.bulkInsertTrackPoints(bufferedPointInserts, numBufferedPointInserts, track.getId());
|
||||||
|
numBufferedPointInserts = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Track segment finished.
|
||||||
|
*/
|
||||||
|
private void onTrackSegmentElementEnd() {
|
||||||
|
// Nothing to be done
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Track finished - update in database.
|
||||||
|
*/
|
||||||
|
private void onTrackElementEnd() {
|
||||||
|
if (lastLocation != null) {
|
||||||
|
flushPointInserts();
|
||||||
|
|
||||||
|
// Calculate statistics for the imported track and update
|
||||||
|
statsBuilder.pauseAt(lastLocation.getTime());
|
||||||
|
track.setStopId(getLastPointId());
|
||||||
|
track.setNumberOfPoints(numberOfLocations);
|
||||||
|
track.setStatistics(statsBuilder.getStatistics());
|
||||||
|
providerUtils.updateTrack(track);
|
||||||
|
tracksWritten.add(track.getId());
|
||||||
|
isCurrentTrackRollbackable = false;
|
||||||
|
lastSegmentLocation = null;
|
||||||
|
lastLocation = null;
|
||||||
|
statsBuilder = null;
|
||||||
|
} else {
|
||||||
|
// track contains no track points makes no real
|
||||||
|
// sense to import it as we have no location
|
||||||
|
// information -> roll back
|
||||||
|
rollbackUnfinishedTracks();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setting time and doing additional calculations as this is the last value
|
||||||
|
* required. Also sets the start time for track and statistics as there is no
|
||||||
|
* start time in the track root element.
|
||||||
|
*
|
||||||
|
* @throws SAXException on parsing errors
|
||||||
|
*/
|
||||||
|
private void onTimeElementEnd() throws SAXException {
|
||||||
|
if (location == null) { return; }
|
||||||
|
|
||||||
|
long time = parseTimeForAllFormats(content);
|
||||||
|
if (lastSegmentLocation != null) {
|
||||||
|
long timeDifference = time - lastSegmentLocation.getTime();
|
||||||
|
|
||||||
|
// check for negative time change
|
||||||
|
if (timeDifference < 0) {
|
||||||
|
String msg = createErrorMessage("Found negative time change.");
|
||||||
|
throw new SAXException(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't have a speed and bearing in GPX, make something up from
|
||||||
|
// the last two points.
|
||||||
|
// TODO GPS points tend to have some inherent imprecision,
|
||||||
|
// speed and bearing will likely be off, so the statistics for things like
|
||||||
|
// max speed will also be off.
|
||||||
|
float speed = location.distanceTo(lastLocation) * 1000.0f / timeDifference;
|
||||||
|
location.setSpeed(speed);
|
||||||
|
location.setBearing(lastSegmentLocation.bearingTo(location));
|
||||||
|
}
|
||||||
|
|
||||||
|
location.setTime(time);
|
||||||
|
// initialize start time with time of first track point
|
||||||
|
if (statsBuilder == null) {
|
||||||
|
statsBuilder = new TripStatisticsBuilder();
|
||||||
|
statsBuilder.resumeAt(time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onAltitudeElementEnd() {
|
||||||
|
if (location != null) {
|
||||||
|
location.setAltitude(Double.parseDouble(content));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the last track if it was not completely imported.
|
||||||
|
*/
|
||||||
|
public void rollbackUnfinishedTracks() {
|
||||||
|
if (isCurrentTrackRollbackable) {
|
||||||
|
providerUtils.deleteTrack(track.getId());
|
||||||
|
isCurrentTrackRollbackable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all track ids of the tracks created by this importer run.
|
||||||
|
*
|
||||||
|
* @return array of track ids
|
||||||
|
*/
|
||||||
|
private long[] getImportedTrackIds() {
|
||||||
|
// Convert from java.lang.Long for convenience
|
||||||
|
long[] result = new long[tracksWritten.size()];
|
||||||
|
for (int i = 0; i < result.length; i++) {
|
||||||
|
result[i] = tracksWritten.get(i);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse time trying different formats used in GPX files.
|
||||||
|
*
|
||||||
|
* @param timeContents string with time information
|
||||||
|
* @return time as long
|
||||||
|
* @throws SAXException on time parsing errors
|
||||||
|
*/
|
||||||
|
private long parseTimeForAllFormats(String timeContents) throws SAXException {
|
||||||
|
long time;
|
||||||
|
timeContents = timeContents.trim();
|
||||||
|
|
||||||
|
// 1st try with time zone at end a la "+0000"
|
||||||
|
time = parseTime(timeContents, DATE_FORMAT1);
|
||||||
|
if (time > -1) { return time; }
|
||||||
|
|
||||||
|
// if that fails, try with a literal "Z" at the end
|
||||||
|
// (this is not according to xml standard, but some gpx files are like
|
||||||
|
// that):
|
||||||
|
time = parseTime(timeContents, DATE_FORMAT2);
|
||||||
|
if (time > -1) { return time; }
|
||||||
|
|
||||||
|
// some gpx timestamps have 3 additional digits at the end.
|
||||||
|
time = parseTime(timeContents, DATE_FORMAT3);
|
||||||
|
if (time > -1) { return time; }
|
||||||
|
|
||||||
|
// everything failed - abort the import
|
||||||
|
String msg = createErrorMessage("Invalid time format: " + timeContents);
|
||||||
|
throw new SAXException(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long parseTime(String timeContents, SimpleDateFormat formatParam) {
|
||||||
|
SimpleDateFormat format = formatParam;
|
||||||
|
try {
|
||||||
|
format.setTimeZone(UTC_TIMEZONE);
|
||||||
|
return format.parse(timeContents).getTime();
|
||||||
|
} catch (ParseException ex) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ID of the last point inserted into the database.
|
||||||
|
*/
|
||||||
|
private long getLastPointId() {
|
||||||
|
flushPointInserts();
|
||||||
|
|
||||||
|
return providerUtils.getLastLocationId(track.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a parsing error message with current line information.
|
||||||
|
*
|
||||||
|
* @param details details about the error, will be appended
|
||||||
|
* @return error message string with current line information
|
||||||
|
*/
|
||||||
|
private String createErrorMessage(String details) {
|
||||||
|
StringBuffer msg = new StringBuffer();
|
||||||
|
msg.append("Parsing error at line: ");
|
||||||
|
msg.append(locator.getLineNumber());
|
||||||
|
msg.append(" column: ");
|
||||||
|
msg.append(locator.getColumnNumber());
|
||||||
|
msg.append(". ");
|
||||||
|
msg.append(details);
|
||||||
|
return msg.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,11 @@
|
|||||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||||
<classpathentry kind="src" path="src"/>
|
<classpathentry kind="src" path="src"/>
|
||||||
<classpathentry kind="src" path="gen"/>
|
<classpathentry kind="src" path="gen"/>
|
||||||
<classpathentry kind="lib" path="libs/easymock.jar"/>
|
<classpathentry kind="lib" path="libs/AndroidMockRuntime.jar"/>
|
||||||
|
<classpathentry kind="src" path=".apt_generated">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="optional" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
<classpathentry kind="output" path="bin"/>
|
<classpathentry kind="output" path="bin"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<factorypath>
|
||||||
|
<factorypathentry kind="PLUGIN" id="org.eclipse.jst.ws.annotations.core" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="WKSPJAR" id="/MyTracksTest/libs/AndroidMockGenerator.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
</factorypath>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#Thu Aug 12 19:30:48 BRT 2010
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
org.eclipse.jdt.apt.aptEnabled=true
|
||||||
|
org.eclipse.jdt.apt.genSrcDir=.apt_generated
|
||||||
|
org.eclipse.jdt.apt.processorOptions/bin_dir=MyTracksTest/bin
|
||||||
|
org.eclipse.jdt.apt.processorOptions/logfile=MyTracksTest/annotations.log
|
||||||
|
org.eclipse.jdt.apt.reconcileEnabled=false
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#Thu Aug 12 19:30:48 BRT 2010
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||||
|
org.eclipse.jdt.core.compiler.compliance=1.6
|
||||||
|
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||||
|
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||||
|
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||||
|
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||||
|
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||||
|
org.eclipse.jdt.core.compiler.processAnnotations=enabled
|
||||||
|
org.eclipse.jdt.core.compiler.source=1.6
|
||||||
@@ -10,4 +10,4 @@
|
|||||||
# Indicates whether an apk should be generated for each density.
|
# Indicates whether an apk should be generated for each density.
|
||||||
split.density=false
|
split.density=false
|
||||||
# Project target.
|
# Project target.
|
||||||
target=Google Inc.:Google APIs:4
|
target=Google Inc.:Google APIs:8
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,258 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010 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.io;
|
||||||
|
|
||||||
|
import static com.google.android.testing.mocking.AndroidMock.eq;
|
||||||
|
import static com.google.android.testing.mocking.AndroidMock.expect;
|
||||||
|
|
||||||
|
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.TracksColumns;
|
||||||
|
import com.google.android.apps.mytracks.testing.TestingProviderUtilsFactory;
|
||||||
|
import com.google.android.testing.mocking.AndroidMock;
|
||||||
|
import com.google.android.testing.mocking.UsesMocks;
|
||||||
|
|
||||||
|
import android.content.ContentUris;
|
||||||
|
import android.location.Location;
|
||||||
|
import android.location.LocationManager;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.test.AndroidTestCase;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
|
||||||
|
import org.easymock.Capture;
|
||||||
|
import org.easymock.IArgumentMatcher;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for the GPX importer.
|
||||||
|
*
|
||||||
|
* @author Steffen Horlacher
|
||||||
|
*/
|
||||||
|
public class GpxImporterTest extends AndroidTestCase {
|
||||||
|
|
||||||
|
private static final String TRACK_NAME = "blablub";
|
||||||
|
private static final String TRACK_DESC = "s'Laebe isch koi Schlotzer";
|
||||||
|
|
||||||
|
private static final String TRACK_LAT_1 = "48.768364";
|
||||||
|
private static final String TRACK_LON_1 = "9.177886";
|
||||||
|
private static final String TRACK_ELE_1 = "324.0";
|
||||||
|
private static final String TRACK_TIME_1 = "2010-04-22T18:21:00Z";
|
||||||
|
|
||||||
|
private static final String TRACK_LAT_2 = "48.768374";
|
||||||
|
private static final String TRACK_LON_2 = "9.177816";
|
||||||
|
private static final String TRACK_ELE_2 = "333.0";
|
||||||
|
private static final String TRACK_TIME_2 = "2010-04-22T18:21:50Z";
|
||||||
|
|
||||||
|
// TODO use real files from different sources with more track points
|
||||||
|
private static final String VALID_TEST_GPX = "<gpx><trk><name><![CDATA["
|
||||||
|
+ TRACK_NAME + "]]></name><desc><![CDATA[" + TRACK_DESC
|
||||||
|
+ "]]></desc><trkseg>" + "<trkpt lat=\"" + TRACK_LAT_1 + "\" lon=\""
|
||||||
|
+ TRACK_LON_1 + "\"><ele>" + TRACK_ELE_1 + "</ele><time>" + TRACK_TIME_1
|
||||||
|
+ "</time></trkpt> +" + "<trkpt lat=\"" + TRACK_LAT_2 + "\" lon=\""
|
||||||
|
+ TRACK_LON_2 + "\"><ele>" + TRACK_ELE_2 + "</ele><time>" + TRACK_TIME_2
|
||||||
|
+ "</time></trkpt>" + "</trkseg></trk></gpx>";
|
||||||
|
|
||||||
|
// invalid xml
|
||||||
|
private static final String INVALID_XML_TEST_GPX = VALID_TEST_GPX.substring(
|
||||||
|
0, VALID_TEST_GPX.length() - 50);
|
||||||
|
private static final String INVALID_LOCATION_TEST_GPX = VALID_TEST_GPX
|
||||||
|
.replaceAll(TRACK_LAT_1, "1000.0");
|
||||||
|
private static final String INVALID_TIME_TEST_GPX = VALID_TEST_GPX
|
||||||
|
.replaceAll(TRACK_TIME_1, "invalid");
|
||||||
|
|
||||||
|
private static final long TRACK_ID = 1;
|
||||||
|
private static final long TRACK_POINT_ID_1 = 1;
|
||||||
|
private static final long TRACK_POINT_ID_2 = 2;
|
||||||
|
|
||||||
|
private static final Uri TRACK_ID_URI = ContentUris.appendId(
|
||||||
|
TracksColumns.CONTENT_URI.buildUpon(), TRACK_ID).build();
|
||||||
|
|
||||||
|
private MyTracksProviderUtils providerUtils;
|
||||||
|
|
||||||
|
private Factory oldProviderUtilsFactory;
|
||||||
|
|
||||||
|
@UsesMocks(MyTracksProviderUtils.class)
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
|
||||||
|
providerUtils = AndroidMock.createMock(MyTracksProviderUtils.class);
|
||||||
|
oldProviderUtilsFactory =
|
||||||
|
TestingProviderUtilsFactory.installWithInstance(providerUtils);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void tearDown() throws Exception {
|
||||||
|
TestingProviderUtilsFactory.restoreOldFactory(oldProviderUtilsFactory);
|
||||||
|
super.tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test import success
|
||||||
|
*/
|
||||||
|
public void testImportSuccess() throws Exception {
|
||||||
|
Capture<Track> trackParam = new Capture<Track>();
|
||||||
|
|
||||||
|
SimpleDateFormat format = GpxImporter.DATE_FORMAT2;
|
||||||
|
Location loc1 = new Location(LocationManager.GPS_PROVIDER);
|
||||||
|
loc1.setTime(format.parse(TRACK_TIME_1).getTime());
|
||||||
|
loc1.setLatitude(Double.parseDouble(TRACK_LAT_1));
|
||||||
|
loc1.setLongitude(Double.parseDouble(TRACK_LON_1));
|
||||||
|
loc1.setAltitude(Double.parseDouble(TRACK_ELE_1));
|
||||||
|
|
||||||
|
Location loc2 = new Location(LocationManager.GPS_PROVIDER);
|
||||||
|
loc2.setTime(format.parse(TRACK_TIME_2).getTime());
|
||||||
|
loc2.setLatitude(Double.parseDouble(TRACK_LAT_2));
|
||||||
|
loc2.setLongitude(Double.parseDouble(TRACK_LON_2));
|
||||||
|
loc2.setAltitude(Double.parseDouble(TRACK_ELE_2));
|
||||||
|
|
||||||
|
expect(providerUtils.insertTrack(AndroidMock.capture(trackParam)))
|
||||||
|
.andReturn(TRACK_ID_URI);
|
||||||
|
|
||||||
|
expect(providerUtils.getLastLocationId(TRACK_ID)).andReturn(TRACK_POINT_ID_1).andReturn(TRACK_POINT_ID_2);
|
||||||
|
|
||||||
|
// A flush happens after the first insertion to get the starting point ID,
|
||||||
|
// which is why we get two calls
|
||||||
|
expect(providerUtils.bulkInsertTrackPoints(LocationsMatcher.eqLoc(loc1),
|
||||||
|
eq(1), eq(TRACK_ID))).andReturn(1);
|
||||||
|
expect(providerUtils.bulkInsertTrackPoints(LocationsMatcher.eqLoc(loc2),
|
||||||
|
eq(1), eq(TRACK_ID))).andReturn(1);
|
||||||
|
|
||||||
|
providerUtils.updateTrack(AndroidMock.capture(trackParam));
|
||||||
|
|
||||||
|
AndroidMock.replay(providerUtils);
|
||||||
|
|
||||||
|
InputStream is = new ByteArrayInputStream(VALID_TEST_GPX.getBytes());
|
||||||
|
GpxImporter.importGPXFile(is, providerUtils);
|
||||||
|
|
||||||
|
AndroidMock.verify(providerUtils);
|
||||||
|
|
||||||
|
// verify track parameter
|
||||||
|
Track track = trackParam.getValue();
|
||||||
|
assertEquals(TRACK_NAME, track.getName());
|
||||||
|
assertEquals(TRACK_DESC, track.getDescription());
|
||||||
|
assertEquals(format.parse(TRACK_TIME_1).getTime(), track.getStatistics()
|
||||||
|
.getStartTime());
|
||||||
|
assertNotSame(-1, track.getStartId());
|
||||||
|
assertNotSame(-1, track.getStopId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test with invalid location - track should be deleted
|
||||||
|
*/
|
||||||
|
public void testImportLocationFailure() throws ParserConfigurationException,
|
||||||
|
SAXException, IOException {
|
||||||
|
testInvalidXML(INVALID_LOCATION_TEST_GPX);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test with invalid time - track should be deleted
|
||||||
|
*/
|
||||||
|
public void testImportTimeFailure() throws ParserConfigurationException,
|
||||||
|
SAXException, IOException {
|
||||||
|
testInvalidXML(INVALID_TIME_TEST_GPX);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test with invalid xml - track should be deleted
|
||||||
|
*/
|
||||||
|
public void testImportXMLFailure() throws ParserConfigurationException,
|
||||||
|
SAXException, IOException {
|
||||||
|
testInvalidXML(INVALID_XML_TEST_GPX);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testInvalidXML(String xml) throws ParserConfigurationException,
|
||||||
|
IOException {
|
||||||
|
expect(providerUtils.insertTrack((Track) AndroidMock.anyObject()))
|
||||||
|
.andReturn(TRACK_ID_URI);
|
||||||
|
|
||||||
|
expect(providerUtils.bulkInsertTrackPoints((Location[]) AndroidMock.anyObject(),
|
||||||
|
AndroidMock.anyInt(), AndroidMock.anyLong())).andStubReturn(1);
|
||||||
|
expect(providerUtils.getLastLocationId(TRACK_ID)).andStubReturn(TRACK_POINT_ID_1);
|
||||||
|
|
||||||
|
providerUtils.deleteTrack(TRACK_ID);
|
||||||
|
|
||||||
|
AndroidMock.replay(providerUtils);
|
||||||
|
|
||||||
|
InputStream is = new ByteArrayInputStream(xml.getBytes());
|
||||||
|
try {
|
||||||
|
GpxImporter.importGPXFile(is, providerUtils);
|
||||||
|
} catch (SAXException e) {
|
||||||
|
// expected exception
|
||||||
|
}
|
||||||
|
|
||||||
|
AndroidMock.verify(providerUtils);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Workaround because of capture bug 2617107 in easymock:
|
||||||
|
* http://sourceforge.net
|
||||||
|
* /tracker/?func=detail&aid=2617107&group_id=82958&atid=567837
|
||||||
|
*/
|
||||||
|
private static class LocationsMatcher implements IArgumentMatcher {
|
||||||
|
private final Location[] matchLocs;
|
||||||
|
|
||||||
|
private LocationsMatcher(Location[] expected) {
|
||||||
|
this.matchLocs = expected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Location[] eqLoc(Location[] expected) {
|
||||||
|
IArgumentMatcher matcher = new LocationsMatcher(expected);
|
||||||
|
AndroidMock.reportMatcher(matcher);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Location[] eqLoc(Location expected) {
|
||||||
|
return eqLoc(new Location[] { expected});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void appendTo(StringBuffer buf) {
|
||||||
|
buf.append("eqLoc(").append(Arrays.toString(matchLocs)).append(")");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(Object obj) {
|
||||||
|
if (! (obj instanceof Location[])) { return false; }
|
||||||
|
Location[] locs = (Location[]) obj;
|
||||||
|
if (locs.length < matchLocs.length) { return false; }
|
||||||
|
|
||||||
|
// Only check the first elements (those that will be taken into account)
|
||||||
|
for (int i = 0; i < matchLocs.length; i++) {
|
||||||
|
if (!locationsMatch(locs[i], matchLocs[i])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean locationsMatch(Location loc1, Location loc2) {
|
||||||
|
return (loc1.getTime() == loc2.getTime()) &&
|
||||||
|
(loc1.getLatitude() == loc2.getLatitude()) &&
|
||||||
|
(loc1.getLongitude() == loc2.getLongitude()) &&
|
||||||
|
(loc1.getAltitude() == loc2.getAltitude());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user