From 4e17bc38008e51bbe738cdb31001a9b1c56a6462 Mon Sep 17 00:00:00 2001 From: Chteufleur Date: Wed, 15 Aug 2018 16:12:49 +0200 Subject: [PATCH] Add GPX import. --- app/src/main/AndroidManifest.xml | 2 + .../chteufleur/mytrackingdog/FilePicker.java | 213 +++++++++++++++++ .../mytrackingdog/MainActivity.java | 113 ++++----- .../chteufleur/mytrackingdog/models/Gpx.java | 3 + .../mytrackingdog/models/ImportGpx.java | 219 ++++++++++++++++++ .../models/beans/MyLocation.java | 70 +++++- .../models/beans/TraceLocation.java | 13 ++ .../models/beans/WayPointLocation.java | 14 ++ .../mytrackingdog/services/ServiceGps.java | 41 +++- app/src/main/res/drawable/ic_file.png | Bin 0 -> 5613 bytes app/src/main/res/drawable/ic_folder.png | Bin 0 -> 6323 bytes app/src/main/res/layout/empty_view.xml | 9 + app/src/main/res/layout/list_item.xml | 29 +++ app/src/main/res/layout/main.xml | 27 ++- app/src/main/res/menu/menu_main.xml | 4 +- app/src/main/res/values/strings.xml | 3 +- 16 files changed, 682 insertions(+), 78 deletions(-) create mode 100644 app/src/main/java/fr/chteufleur/mytrackingdog/FilePicker.java create mode 100644 app/src/main/java/fr/chteufleur/mytrackingdog/models/ImportGpx.java create mode 100644 app/src/main/java/fr/chteufleur/mytrackingdog/models/beans/TraceLocation.java create mode 100644 app/src/main/java/fr/chteufleur/mytrackingdog/models/beans/WayPointLocation.java create mode 100644 app/src/main/res/drawable/ic_file.png create mode 100644 app/src/main/res/drawable/ic_folder.png create mode 100644 app/src/main/res/layout/empty_view.xml create mode 100644 app/src/main/res/layout/list_item.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7334098..425d1d6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -20,6 +20,8 @@ + + diff --git a/app/src/main/java/fr/chteufleur/mytrackingdog/FilePicker.java b/app/src/main/java/fr/chteufleur/mytrackingdog/FilePicker.java new file mode 100644 index 0000000..3ad7e96 --- /dev/null +++ b/app/src/main/java/fr/chteufleur/mytrackingdog/FilePicker.java @@ -0,0 +1,213 @@ +package fr.chteufleur.mytrackingdog; + +import android.app.ListActivity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.TextView; + +import java.io.File; +import java.io.FilenameFilter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import fr.chteufleur.mytrackingdog.R; + +public class FilePicker extends ListActivity { + + public final static String EXTRA_FILE_PATH = "file_path"; + public final static String EXTRA_SHOW_HIDDEN_FILES = "show_hidden_files"; + public final static String EXTRA_ACCEPTED_FILE_EXTENSIONS = "accepted_file_extensions"; + private final static String DEFAULT_INITIAL_DIRECTORY = "/"; + + protected File Directory; + protected ArrayList Files; + protected FilePickerListAdapter Adapter; + protected boolean ShowHiddenFiles = false; + protected String[] acceptedFileExtensions; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LayoutInflater inflator = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + View emptyView = inflator.inflate(R.layout.empty_view, null); + ((ViewGroup) getListView().getParent()).addView(emptyView); + getListView().setEmptyView(emptyView); + + // Set initial directory + Directory = new File(DEFAULT_INITIAL_DIRECTORY); + + // Initialize the ArrayList + Files = new ArrayList(); + + // Set the ListAdapter + Adapter = new FilePickerListAdapter(this, Files); + setListAdapter(Adapter); + + // Initialize the extensions array to allow any file extensions + acceptedFileExtensions = new String[] {}; + + // Get intent extras + if (getIntent().hasExtra(EXTRA_FILE_PATH)) { + Directory = new File(getIntent().getStringExtra(EXTRA_FILE_PATH)); + } + + if (getIntent().hasExtra(EXTRA_SHOW_HIDDEN_FILES)) { + ShowHiddenFiles = getIntent().getBooleanExtra(EXTRA_SHOW_HIDDEN_FILES, false); + } + + if (getIntent().hasExtra(EXTRA_ACCEPTED_FILE_EXTENSIONS)) { + ArrayList collection = getIntent().getStringArrayListExtra(EXTRA_ACCEPTED_FILE_EXTENSIONS); + acceptedFileExtensions = collection.toArray(new String[collection.size()]); + } + } + + @Override + protected void onResume() { + refreshFilesList(); + super.onResume(); + } + + protected void refreshFilesList() { + Files.clear(); + ExtensionFilenameFilter filter = new ExtensionFilenameFilter(acceptedFileExtensions); + + File[] files = Directory.listFiles(filter); + if (files != null && files.length > 0) { + for (File f: files) { + if (f.isHidden() && !ShowHiddenFiles) { + continue; + } + Files.add(f); + } + Collections.sort(Files, new FileComparator()); + } + Adapter.notifyDataSetChanged(); + } + + @Override + public void onBackPressed() { + if (Directory.getParentFile() != null) { + Directory = Directory.getParentFile(); + refreshFilesList(); + return; + } + super.onBackPressed(); + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + File newFile = (File)l.getItemAtPosition(position); + if (newFile.isFile()) { + Intent extra = new Intent(); + extra.putExtra(EXTRA_FILE_PATH, newFile.getAbsolutePath()); + setResult(RESULT_OK, extra); + finish(); + } else { + Directory = newFile; + refreshFilesList(); + } + super.onListItemClick(l, v, position, id); + } + + private class FilePickerListAdapter extends ArrayAdapter { + private List mObjects; + + public FilePickerListAdapter(Context context, List objects) { + super(context, R.layout.list_item, android.R.id.text1, objects); + mObjects = objects; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View row = null; + + if (convertView == null) { + LayoutInflater inflater = (LayoutInflater) + getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + row = inflater.inflate(R.layout.list_item, parent, false); + } else { + row = convertView; + } + + File object = mObjects.get(position); + + ImageView imageView = row.findViewById(R.id.file_picker_image); + TextView textView = row.findViewById(R.id.file_picker_text); + textView.setSingleLine(true); + textView.setText(object.getName()); + + if (object.isFile()) { + imageView.setImageResource(R.drawable.ic_file); + } else { + imageView.setImageResource(R.drawable.ic_folder); + } + + return row; + } + } + + private class FileComparator implements Comparator { + + public int compare(File f1, File f2) { + + if (f1 == f2) { + return 0; + } + + if (f1.isDirectory() && f2.isFile()) { + // Show directories above files + return -1; + } + + if (f1.isFile() && f2.isDirectory()) { + // Show files below directories + return 1; + } + + // Sort the directories alphabetically + return f1.getName().compareToIgnoreCase(f2.getName()); + } + } + + private class ExtensionFilenameFilter implements FilenameFilter { + + private String[] Extensions; + + public ExtensionFilenameFilter(String[] extensions) { + super(); + Extensions = extensions; + } + + public boolean accept(File dir, String filename) { + + if (new File(dir, filename).isDirectory()) { + // Accept all directory names + return true; + } + + if (Extensions != null && Extensions.length > 0) { + for (int i = 0; i < Extensions.length; i++) { + if (filename.endsWith(Extensions[i])) { + // The filename ends with the extension + return true; + } + } + // The filename did not match any of the extensions + return false; + } + // No extensions has been set. Accept all file extensions. + return true; + } + } +} diff --git a/app/src/main/java/fr/chteufleur/mytrackingdog/MainActivity.java b/app/src/main/java/fr/chteufleur/mytrackingdog/MainActivity.java index 3b1b5eb..fd622b7 100644 --- a/app/src/main/java/fr/chteufleur/mytrackingdog/MainActivity.java +++ b/app/src/main/java/fr/chteufleur/mytrackingdog/MainActivity.java @@ -2,26 +2,26 @@ package fr.chteufleur.mytrackingdog; import android.Manifest; import android.content.Context; +import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.graphics.Color; import android.hardware.GeomagneticField; -import android.location.Location; import android.location.LocationManager; import android.os.Build; import android.os.Bundle; +import android.os.Environment; import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.annotation.RequiresApi; import android.support.design.widget.FloatingActionButton; import android.support.v7.app.AppCompatActivity; -import android.util.Log; +import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuItem; import android.view.Surface; import android.view.View; import android.view.WindowManager; -import android.widget.ImageButton; import android.widget.Toast; import org.osmdroid.api.IMapController; @@ -31,7 +31,6 @@ import org.osmdroid.util.GeoPoint; import org.osmdroid.views.MapView; import org.osmdroid.views.overlay.Marker; import org.osmdroid.views.overlay.Polyline; -import org.osmdroid.views.overlay.compass.CompassOverlay; import org.osmdroid.views.overlay.compass.IOrientationConsumer; import org.osmdroid.views.overlay.compass.IOrientationProvider; import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider; @@ -39,11 +38,13 @@ import org.osmdroid.views.overlay.infowindow.BasicInfoWindow; import org.osmdroid.views.overlay.mylocation.GpsMyLocationProvider; import org.osmdroid.views.overlay.mylocation.MyLocationNewOverlay; +import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.Observable; import java.util.Observer; +import fr.chteufleur.mytrackingdog.models.Gpx; import fr.chteufleur.mytrackingdog.models.beans.MyLocation; import fr.chteufleur.mytrackingdog.services.ServiceGps; @@ -54,13 +55,11 @@ public class MainActivity extends AppCompatActivity implements IOrientationConsu public static String appName = ""; private MyLocationNewOverlay mLocationOverlay; - private CompassOverlay mCompassOverlay; private IOrientationProvider compass = null; private ServiceGps serviceGps = null; private Context ctx = null; private MapView map = null; - private ImageButton btFollowMe; private int deviceOrientation = 0; private boolean zoomed = false; @@ -99,6 +98,9 @@ public class MainActivity extends AppCompatActivity implements IOrientationConsu // Keep screen ON getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + Toolbar toolbar = findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + map = findViewById(R.id.map); map.setTileSource(TileSourceFactory.MAPNIK); @@ -112,11 +114,6 @@ public class MainActivity extends AppCompatActivity implements IOrientationConsu GeoPoint startPoint = new GeoPoint(45.0000, 5.0000); mapController.setCenter(startPoint); - // Compass - this.mCompassOverlay = new CompassOverlay(ctx, new InternalCompassOrientationProvider(ctx), map); - map.getOverlays().add(this.mCompassOverlay); - this.mCompassOverlay.enableCompass(); - // Current position on the map this.mLocationOverlay = new MyLocationNewOverlay(new GpsMyLocationProvider(ctx), map); map.getOverlays().add(this.mLocationOverlay); @@ -124,20 +121,6 @@ public class MainActivity extends AppCompatActivity implements IOrientationConsu mLocationOverlay.enableFollowLocation(); mLocationOverlay.setOptionsMenuEnabled(true); - // Follow me - btFollowMe = findViewById(R.id.ic_follow_me); - btFollowMe.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (!mLocationOverlay.isFollowLocationEnabled()) { - mLocationOverlay.enableFollowLocation(); - btFollowMe.setImageResource(R.drawable.osm_ic_follow_me_on); - } else { - mLocationOverlay.disableFollowLocation(); - btFollowMe.setImageResource(R.drawable.osm_ic_follow_me); - } - } - }); start_stop_trace = findViewById(R.id.start_stop_trace); start_stop_trace.setOnClickListener(new View.OnClickListener() { @@ -198,6 +181,10 @@ public class MainActivity extends AppCompatActivity implements IOrientationConsu private void addMarker() { MyLocation loc = serviceGps.addPointObject(); GeoPoint gp = new GeoPoint(loc.getLatitude(), loc.getLongitude(), loc.getAltitude()); + addMarker(gp); + } + + private void addMarker(GeoPoint gp) { Marker marker = new Marker(map); marker.setPosition(gp); marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM); @@ -262,9 +249,6 @@ public class MainActivity extends AppCompatActivity implements IOrientationConsu } private void pause() { - if (mCompassOverlay != null) { - mCompassOverlay.disableCompass(); - } if (compass != null) { compass.stopOrientationProvider(); compass.destroy(); @@ -308,13 +292,34 @@ public class MainActivity extends AppCompatActivity implements IOrientationConsu int id = item.getItemId(); //noinspection SimplifiableIfStatement - if (id == R.id.action_settings) { + if (id == R.id.action_import_gpx) { + Intent intent = new Intent(this, FilePicker.class); + intent.putExtra(FilePicker.EXTRA_FILE_PATH, Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+appName); + startActivityForResult(intent, REQUEST_PICK_FILE); return true; } return super.onOptionsItemSelected(item); } + private static final int REQUEST_PICK_FILE = 1; + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode == RESULT_OK) { + switch (requestCode) { + case REQUEST_PICK_FILE: + if (data.hasExtra(FilePicker.EXTRA_FILE_PATH)) { + serviceGps.importGpxTrace(new File(data.getStringExtra(FilePicker.EXTRA_FILE_PATH))); + updateDogTrace(); + updateTrailTrace(); + for (MyLocation loc: serviceGps.getListGeoPointObjects()) { + addMarker(new GeoPoint(loc.getLatitude(), loc.getLongitude(), loc.getAltitude())); + } + } + break; + } + } + } @Override @@ -376,40 +381,44 @@ public class MainActivity extends AppCompatActivity implements IOrientationConsu } } + private void updateDogTrace() { + Polyline line = new Polyline(map); + line.setTitle(Gpx.DOG_TRACE_NAME); + line.setColor(Color.BLUE); + line.setPoints(convertListLocation(serviceGps.getListGeoPointDog())); + line.setWidth(LINE_WIDTH_BIG); + line.setGeodesic(true); + line.setInfoWindow(new BasicInfoWindow(R.layout.bonuspack_bubble, map)); + map.getOverlayManager().add(line); + map.invalidate(); + } + + private void updateTrailTrace() { + Polyline line = new Polyline(map); + line.setTitle(Gpx.TRAIL_TRACE_NAME); + line.setColor(Color.RED); + line.setPoints(convertListLocation(serviceGps.getListGeoPointTraceur())); + line.setWidth(LINE_WIDTH_BIG); + line.setGeodesic(true); + line.setInfoWindow(new BasicInfoWindow(R.layout.bonuspack_bubble, map)); + map.getOverlayManager().add(line); + map.invalidate(); + } @Override public void update(Observable observable, Object o) { if (observable == serviceGps) { if (o instanceof String && o.equals(ServiceGps.NOTIF_NEW_LOCATION)) { - Location loc = serviceGps.getCurrentLocation(); + MyLocation loc = serviceGps.getCurrentLocation(); if (loc != null) { GeoPoint currentPoint = new GeoPoint(loc.getLatitude(), loc.getLongitude(), loc.getAltitude()); - Polyline line = null; + map.getController().setCenter(currentPoint); if (serviceGps.isTraceurActivated()) { - map.getController().setCenter(currentPoint); - - line = new Polyline(map); - line.setTitle("Trail"); - line.setColor(Color.RED); - line.setPoints(convertListLocation(serviceGps.getListGeoPointTraceur())); - + updateTrailTrace(); } else if (serviceGps.isDogActivated()) { - map.getController().setCenter(currentPoint); - - line = new Polyline(map); - line.setTitle("Dog"); - line.setColor(Color.BLUE); - line.setPoints(convertListLocation(serviceGps.getListGeoPointDog())); + updateDogTrace(); } - if (line != null) { - line.setSubDescription(Polyline.class.getCanonicalName()); - line.setWidth(LINE_WIDTH_BIG); - line.setGeodesic(true); - line.setInfoWindow(new BasicInfoWindow(R.layout.bonuspack_bubble, map)); - map.getOverlayManager().add(line); - map.invalidate(); - } float orientation = serviceGps.getOrientation(deviceOrientation); if (orientation >= 0) { map.setMapOrientation(orientation); diff --git a/app/src/main/java/fr/chteufleur/mytrackingdog/models/Gpx.java b/app/src/main/java/fr/chteufleur/mytrackingdog/models/Gpx.java index eeedcbc..17d37ce 100644 --- a/app/src/main/java/fr/chteufleur/mytrackingdog/models/Gpx.java +++ b/app/src/main/java/fr/chteufleur/mytrackingdog/models/Gpx.java @@ -4,6 +4,9 @@ import java.io.File; public class Gpx { + public static final String DOG_TRACE_NAME = "dog"; + public static final String TRAIL_TRACE_NAME = "trail"; + protected final File filePath; public Gpx(File filePath) { diff --git a/app/src/main/java/fr/chteufleur/mytrackingdog/models/ImportGpx.java b/app/src/main/java/fr/chteufleur/mytrackingdog/models/ImportGpx.java new file mode 100644 index 0000000..055a867 --- /dev/null +++ b/app/src/main/java/fr/chteufleur/mytrackingdog/models/ImportGpx.java @@ -0,0 +1,219 @@ +package fr.chteufleur.mytrackingdog.models; + +import android.util.Log; +import android.util.Xml; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import fr.chteufleur.mytrackingdog.models.beans.MyLocation; +import fr.chteufleur.mytrackingdog.models.beans.TraceLocation; +import fr.chteufleur.mytrackingdog.models.beans.WayPointLocation; + +public class ImportGpx extends Gpx { + + private static final String TAG = Traces.class.getName(); + + private String traceName = ""; + + + public ImportGpx(File filePath) { + super(filePath); + } + + public String getTraceName() { + return this.traceName; + } + + public List parse() throws XmlPullParserException, IOException { + InputStream in = new FileInputStream(filePath); + try { + + XmlPullParser parser = Xml.newPullParser(); + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); + parser.setInput(in, null); + parser.nextTag(); + return readGpx(parser); + } finally { + in.close(); + } + } + + + private List readGpx(XmlPullParser parser) throws XmlPullParserException, IOException { + List entries = new ArrayList(); + + parser.require(XmlPullParser.START_TAG, null, "gpx"); + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + if (name.equals("wpt")) { + entries.add(readWaypoint(parser)); + } else if (name.equals("trk")) { + entries.addAll(readTrk(parser)); + } else { + skip(parser); + } + } + return entries; + } + + private List readTrk(XmlPullParser parser) throws XmlPullParserException, IOException { + List entries = new ArrayList(); + + parser.require(XmlPullParser.START_TAG, null, "trk"); + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + if (name.equals("trkseg")) { + entries = readTrkseq(parser); + } else { + skip(parser); + } + } + return entries; + } + + private List readTrkseq(XmlPullParser parser) throws XmlPullParserException, IOException { + List entries = new ArrayList(); + + parser.require(XmlPullParser.START_TAG, null, "trkseg"); + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + if (name.equals("trkpt")) { + entries.add(readTrkpt(parser)); + } else if (name.equals("name")) { + traceName = readName(parser); + } else { + skip(parser); + } + } + return entries; + } + + + private MyLocation readTrkpt(XmlPullParser parser) throws XmlPullParserException, IOException { + parser.require(XmlPullParser.START_TAG, null, "trkpt"); + + String latStr = parser.getAttributeValue(null, "lat"); + String lonStr = parser.getAttributeValue(null, "lon"); + double lat = Double.parseDouble(latStr); + double lon = Double.parseDouble(lonStr); + long time = -1; + + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + if (name.equals("time")) { + time = readTime(parser); + } else { + skip(parser); + } + } + return new TraceLocation(lat, lon, time); + } + + + private MyLocation readWaypoint(XmlPullParser parser) throws XmlPullParserException, IOException { + parser.require(XmlPullParser.START_TAG, null, "wpt"); + + String latStr = parser.getAttributeValue(null, "lat"); + String lonStr = parser.getAttributeValue(null, "lon"); + double lat = Double.parseDouble(latStr); + double lon = Double.parseDouble(lonStr); + long time = -1; + + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + if (name.equals("time")) { + time = readTime(parser); + } else { + skip(parser); + } + } + return new WayPointLocation(lat, lon, time); + } + + + private long readTime(XmlPullParser parser) throws XmlPullParserException, IOException { + long ret = 0; + parser.require(XmlPullParser.START_TAG, null, "time"); + String timeStr = readText(parser); + + SimpleDateFormat formater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Pattern pattern = Pattern.compile("([0-9]{4}-[0-9]{2}-[0-9]{2})T([0-9]{2}:[0-9]{2}:[0-9]{2})Z"); + Matcher matcher = pattern.matcher(timeStr); + + while (matcher.find()) { + if (matcher.groupCount() == 2) { + try { + Date date = formater.parse(matcher.group(1) + " " + matcher.group(2)); + ret = date.getTime(); + } catch (ParseException ex) { + Log.e(TAG, "Failed to parse time", ex); + } + } + } + parser.require(XmlPullParser.END_TAG, null, "time"); + return ret; + } + + + private String readName(XmlPullParser parser) throws XmlPullParserException, IOException { + String ret; + parser.require(XmlPullParser.START_TAG, null, "name"); + ret = readText(parser); + parser.require(XmlPullParser.END_TAG, null, "name"); + return ret; + } + + private String readText(XmlPullParser parser) throws IOException, XmlPullParserException { + String result = ""; + if (parser.next() == XmlPullParser.TEXT) { + result = parser.getText(); + parser.nextTag(); + } + return result; + } + + private void skip(XmlPullParser parser) throws XmlPullParserException, IOException { + if (parser.getEventType() != XmlPullParser.START_TAG) { + throw new IllegalStateException(); + } + int depth = 1; + while (depth != 0) { + switch (parser.next()) { + case XmlPullParser.END_TAG: + depth--; + break; + case XmlPullParser.START_TAG: + depth++; + break; + } + } + } +} diff --git a/app/src/main/java/fr/chteufleur/mytrackingdog/models/beans/MyLocation.java b/app/src/main/java/fr/chteufleur/mytrackingdog/models/beans/MyLocation.java index fe07ad6..4f01b1e 100644 --- a/app/src/main/java/fr/chteufleur/mytrackingdog/models/beans/MyLocation.java +++ b/app/src/main/java/fr/chteufleur/mytrackingdog/models/beans/MyLocation.java @@ -2,28 +2,88 @@ package fr.chteufleur.mytrackingdog.models.beans; import android.location.Location; -public class MyLocation extends Location { +import java.text.SimpleDateFormat; +import java.util.Date; + +public class MyLocation { + + private final double longitude; + private final double latitude; + private final long time; + + private final double altitude; + private final float speed; + private final float bearing; public MyLocation(Location l) { - super(l); + if (l == null) { + throw new NullPointerException(); + } + this.latitude = l.getLatitude(); + this.longitude = l.getLongitude(); + this.time = l.getTime(); + this.altitude = l.getAltitude(); + this.speed = l.getSpeed(); + this.bearing = l.getBearing(); + } + + public MyLocation(double lat, double lon, long time) { + this.latitude = lat; + this.longitude = lon; + this.time = time; + this.altitude = 0; + this.speed = 0; + this.bearing = 0; + } + + + public double getLatitude() { + return this.latitude; + } + + public double getLongitude() { + return this.longitude; + } + + public long getTime() { + return this.time; + } + + public String getDate() { + Date date = new Date(this.time); + SimpleDateFormat formaterDate = new SimpleDateFormat("yyyy-MM-dd"); + SimpleDateFormat formaterTime = new SimpleDateFormat("HH:mm:ss"); + return formaterDate.format(date)+"T"+formaterTime.format(date)+"Z"; + } + + public double getAltitude() { + return this.altitude; + } + + public float getSpeed() { + return this.speed; + } + + public float getBearing() { + return this.bearing; } public String toWayPoint() { String ret = ""; - ret += ""; + ret += ""; ret += ""; return ret; } public String toTracePoint() { String ret = ""; - ret += ""; + ret += ""; ret += ""; return ret; } @Override public String toString() { - return String.format("lat=%,4f ; lon=%,4f ; time=%d", getLatitude(), getLongitude(), getTime()); + return String.format("lat=%,4f ; lon=%,4f ; time=%s", getLatitude(), getLongitude(), getDate()); } } diff --git a/app/src/main/java/fr/chteufleur/mytrackingdog/models/beans/TraceLocation.java b/app/src/main/java/fr/chteufleur/mytrackingdog/models/beans/TraceLocation.java new file mode 100644 index 0000000..264a836 --- /dev/null +++ b/app/src/main/java/fr/chteufleur/mytrackingdog/models/beans/TraceLocation.java @@ -0,0 +1,13 @@ +package fr.chteufleur.mytrackingdog.models.beans; + +import android.location.Location; + +public class TraceLocation extends MyLocation { + public TraceLocation(Location l) { + super(l); + } + + public TraceLocation(double lat, double lon, long time) { + super(lat, lon, time); + } +} diff --git a/app/src/main/java/fr/chteufleur/mytrackingdog/models/beans/WayPointLocation.java b/app/src/main/java/fr/chteufleur/mytrackingdog/models/beans/WayPointLocation.java new file mode 100644 index 0000000..f707d91 --- /dev/null +++ b/app/src/main/java/fr/chteufleur/mytrackingdog/models/beans/WayPointLocation.java @@ -0,0 +1,14 @@ +package fr.chteufleur.mytrackingdog.models.beans; + +import android.location.Location; + + +public class WayPointLocation extends MyLocation { + public WayPointLocation(Location l) { + super(l); + } + + public WayPointLocation(double lat, double lon, long time) { + super(lat, lon, time); + } +} diff --git a/app/src/main/java/fr/chteufleur/mytrackingdog/services/ServiceGps.java b/app/src/main/java/fr/chteufleur/mytrackingdog/services/ServiceGps.java index 775c14f..e36e116 100644 --- a/app/src/main/java/fr/chteufleur/mytrackingdog/services/ServiceGps.java +++ b/app/src/main/java/fr/chteufleur/mytrackingdog/services/ServiceGps.java @@ -7,15 +7,22 @@ import android.os.Bundle; import android.os.Environment; import android.util.Log; +import org.xmlpull.v1.XmlPullParserException; + import java.io.File; +import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.Observable; import fr.chteufleur.mytrackingdog.models.ExportGpx; +import fr.chteufleur.mytrackingdog.models.Gpx; +import fr.chteufleur.mytrackingdog.models.ImportGpx; import fr.chteufleur.mytrackingdog.models.Traces; import fr.chteufleur.mytrackingdog.models.beans.MyLocation; +import fr.chteufleur.mytrackingdog.models.beans.TraceLocation; +import fr.chteufleur.mytrackingdog.models.beans.WayPointLocation; public class ServiceGps extends Observable implements IServiceGps, LocationListener { @@ -142,19 +149,41 @@ public class ServiceGps extends Observable implements IServiceGps, LocationListe } public boolean exportDogTraceToGpx() { - File file = new File(getFileName("dog")); - ExportGpx exportGpx = new ExportGpx(file, "dog"); + File file = new File(getFileName(Gpx.DOG_TRACE_NAME)); + ExportGpx exportGpx = new ExportGpx(file, Gpx.DOG_TRACE_NAME); exportGpx.setTrace(traces.getListPointDog()); return exportGpx.export(); } public boolean exportTrailTraceToGpx() { - SimpleDateFormat formater = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss"); - String date = formater.format(new Date()); - File file = new File(getFileName("trail")); - ExportGpx exportGpx = new ExportGpx(file, "trail"); + File file = new File(getFileName(Gpx.TRAIL_TRACE_NAME)); + ExportGpx exportGpx = new ExportGpx(file, Gpx.TRAIL_TRACE_NAME); exportGpx.setObjects(traces.getListPointObjects()); exportGpx.setTrace(traces.getListPointTraceur()); return exportGpx.export(); } + + public void importGpxTrace(File file) { + ImportGpx importGpx = new ImportGpx(file); + try { + List list = importGpx.parse(); + String traceName = importGpx.getTraceName(); + for (int i=0; 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/ic_folder.png b/app/src/main/res/drawable/ic_folder.png new file mode 100644 index 0000000000000000000000000000000000000000..dff9378d182ee904979fe508d7081dccc677eff8 GIT binary patch literal 6323 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/layout/empty_view.xml b/app/src/main/res/layout/empty_view.xml new file mode 100644 index 0000000..53f90fd --- /dev/null +++ b/app/src/main/res/layout/empty_view.xml @@ -0,0 +1,9 @@ + + diff --git a/app/src/main/res/layout/list_item.xml b/app/src/main/res/layout/list_item.xml new file mode 100644 index 0000000..b4e509d --- /dev/null +++ b/app/src/main/res/layout/list_item.xml @@ -0,0 +1,29 @@ + + + + + + + + diff --git a/app/src/main/res/layout/main.xml b/app/src/main/res/layout/main.xml index adeab10..8f885e1 100644 --- a/app/src/main/res/layout/main.xml +++ b/app/src/main/res/layout/main.xml @@ -1,4 +1,5 @@ + + + + + + + + - - diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dcda8dc..e3fc7ca 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,5 +1,4 @@ MyTrackingDog - Settings - Follow me + Import GPX