Update gpx utilities, small fixes osmo
This commit is contained in:
parent
64b07a2e41
commit
7853e24671
15 changed files with 1219 additions and 159 deletions
|
@ -62,7 +62,7 @@ android:layout_width="fill_parent" android:layout_height="fill_parent" android:b
|
||||||
<TextView android:layout_height="wrap_content" android:layout_width="0dp"
|
<TextView android:layout_height="wrap_content" android:layout_width="0dp"
|
||||||
android:id="@+id/FavoritesButtonText"
|
android:id="@+id/FavoritesButtonText"
|
||||||
android:layout_marginLeft="15dp" android:layout_gravity="center_vertical|left"
|
android:layout_marginLeft="15dp" android:layout_gravity="center_vertical|left"
|
||||||
android:text="@string/favorites_Button" android:textColor="#000000" android:typeface="serif"
|
android:text="@string/my_data_Button" android:textColor="#000000" android:typeface="serif"
|
||||||
android:layout_weight="1" android:textSize="16sp"/>
|
android:layout_weight="1" android:textSize="16sp"/>
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/FavoritesButtonImage"
|
android:id="@+id/FavoritesButtonImage"
|
||||||
|
|
|
@ -64,7 +64,7 @@ android:layout_width="fill_parent" android:layout_height="fill_parent" android:b
|
||||||
<TextView android:layout_height="wrap_content" android:layout_width="0dp"
|
<TextView android:layout_height="wrap_content" android:layout_width="0dp"
|
||||||
android:id="@+id/FavoritesButtonText"
|
android:id="@+id/FavoritesButtonText"
|
||||||
android:layout_marginLeft="15dp" android:layout_gravity="center_vertical|left"
|
android:layout_marginLeft="15dp" android:layout_gravity="center_vertical|left"
|
||||||
android:text="@string/favorites_Button" android:textColor="#000000" android:typeface="serif"
|
android:text="@string/my_data_Button" android:textColor="#000000" android:typeface="serif"
|
||||||
android:layout_weight="1" android:textSize="24sp"/>
|
android:layout_weight="1" android:textSize="24sp"/>
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/FavoritesButtonImage"
|
android:id="@+id/FavoritesButtonImage"
|
||||||
|
|
|
@ -62,7 +62,7 @@ android:layout_width="fill_parent" android:layout_height="fill_parent" android:b
|
||||||
<TextView android:layout_height="wrap_content" android:layout_width="wrap_content"
|
<TextView android:layout_height="wrap_content" android:layout_width="wrap_content"
|
||||||
android:id="@+id/FavoritesButtonText"
|
android:id="@+id/FavoritesButtonText"
|
||||||
android:layout_gravity="center_horizontal|top" android:textSize="27sp"
|
android:layout_gravity="center_horizontal|top" android:textSize="27sp"
|
||||||
android:text="@string/favorites_Button" android:typeface="serif" android:textColor="#000000"/>
|
android:text="@string/my_data_Button" android:typeface="serif" android:textColor="#000000"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
<LinearLayout android:layout_weight="2" android:layout_height="fill_parent"/>
|
<LinearLayout android:layout_weight="2" android:layout_height="fill_parent"/>
|
||||||
|
|
|
@ -60,7 +60,7 @@ android:layout_width="fill_parent" android:layout_height="fill_parent" android:b
|
||||||
<LinearLayout android:layout_height="wrap_content" android:layout_width="fill_parent" android:gravity="center">
|
<LinearLayout android:layout_height="wrap_content" android:layout_width="fill_parent" android:gravity="center">
|
||||||
<TextView android:layout_height="wrap_content" android:layout_width="wrap_content" android:id="@+id/FavoritesButtonText"
|
<TextView android:layout_height="wrap_content" android:layout_width="wrap_content" android:id="@+id/FavoritesButtonText"
|
||||||
android:layout_gravity="center_horizontal|top" android:textSize="18sp"
|
android:layout_gravity="center_horizontal|top" android:textSize="18sp"
|
||||||
android:text="@string/favorites_Button" android:typeface="serif" android:textColor="#000000"/>
|
android:text="@string/my_data_Button" android:typeface="serif" android:textColor="#000000"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
<LinearLayout android:layout_weight="2" android:layout_height="fill_parent"/>
|
<LinearLayout android:layout_weight="2" android:layout_height="fill_parent"/>
|
||||||
|
|
32
OsmAnd/res/layout/tab_content.xml
Normal file
32
OsmAnd/res/layout/tab_content.xml
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<TabHost
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@android:id/tabhost"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TabWidget
|
||||||
|
android:id="@android:id/tabs"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="0"/>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@android:id/tabcontent"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<android.support.v4.view.ViewPager
|
||||||
|
android:id="@+id/pager"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</TabHost>
|
|
@ -9,6 +9,8 @@
|
||||||
3. All your modified/created strings are in the top of the file (to make easier find what\'s translated).
|
3. All your modified/created strings are in the top of the file (to make easier find what\'s translated).
|
||||||
PLEASE: Have a look at http://code.google.com/p/osmand/wiki/UIConsistency, it may really improve your and our work :-) Thx - Hardy
|
PLEASE: Have a look at http://code.google.com/p/osmand/wiki/UIConsistency, it may really improve your and our work :-) Thx - Hardy
|
||||||
-->
|
-->
|
||||||
|
<string name="my_data_Button">My Data</string>
|
||||||
|
<string name="my_data_activity">My Data</string>
|
||||||
<string name="osmo_user_joined">User %1$s joined group %2$s</string>
|
<string name="osmo_user_joined">User %1$s joined group %2$s</string>
|
||||||
<string name="osmo_user_left">User %1$s left group %2$s</string>
|
<string name="osmo_user_left">User %1$s left group %2$s</string>
|
||||||
<string name="osmo_show_group_notifications">Show group notifications</string>
|
<string name="osmo_show_group_notifications">Show group notifications</string>
|
||||||
|
|
|
@ -109,6 +109,235 @@ public class GPXUtilities {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class GPXTrackAnalysis {
|
||||||
|
public float totalDistance = 0;
|
||||||
|
public int totalTracks = 0;
|
||||||
|
public long startTime = Long.MAX_VALUE;
|
||||||
|
public long endTime = Long.MIN_VALUE;
|
||||||
|
public long timeSpan = 0;
|
||||||
|
public long timeMoving = 0;
|
||||||
|
public float totalDistanceMoving = 0;
|
||||||
|
|
||||||
|
public double diffElevationUp = 0;
|
||||||
|
public double diffElevationDown = 0;
|
||||||
|
public double avgElevation = 0;
|
||||||
|
public double minElevation = 99999;
|
||||||
|
public double maxElevation = -100;
|
||||||
|
|
||||||
|
public float maxSpeed = 0;
|
||||||
|
public float avgSpeed;
|
||||||
|
|
||||||
|
public int points;
|
||||||
|
public int wptPoints = 0;
|
||||||
|
|
||||||
|
public int metricEnd;
|
||||||
|
|
||||||
|
public boolean isTimeSpecified() {
|
||||||
|
return startTime != Long.MAX_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTimeMoving() {
|
||||||
|
return timeMoving != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isElevationSpecified() {
|
||||||
|
return maxElevation != -100;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTimeHours(long time) {
|
||||||
|
return (int) ((time / 1000) / 3600);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTimeSeconds(long time) {
|
||||||
|
return (int) ((time / 1000) % 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTimeMinutes(long time) {
|
||||||
|
return (int) (((time / 1000) / 60) % 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSpeedSpecified() {
|
||||||
|
return avgSpeed > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void prepareInformation(long filestamp, SplitSegment... splitSegments) {
|
||||||
|
float[] calculations = new float[1];
|
||||||
|
|
||||||
|
float totalElevation = 0;
|
||||||
|
int elevationPoints = 0;
|
||||||
|
int speedCount = 0;
|
||||||
|
double totalSpeedSum = 0;
|
||||||
|
points = 0;
|
||||||
|
for (SplitSegment s : splitSegments) {
|
||||||
|
final int numberOfPoints = s.getNumberOfPoints();
|
||||||
|
metricEnd += s.metricEnd;
|
||||||
|
points += numberOfPoints;
|
||||||
|
for (int j = 0; j < numberOfPoints; j++) {
|
||||||
|
WptPt point = s.get(j);
|
||||||
|
long time = point.time;
|
||||||
|
if (time != 0) {
|
||||||
|
startTime = Math.min(startTime, time);
|
||||||
|
endTime = Math.max(startTime, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
double elevation = point.ele;
|
||||||
|
if (!Double.isNaN(elevation)) {
|
||||||
|
totalElevation += elevation;
|
||||||
|
elevationPoints++;
|
||||||
|
minElevation = Math.min(elevation, minElevation);
|
||||||
|
maxElevation = Math.max(elevation, maxElevation);
|
||||||
|
}
|
||||||
|
|
||||||
|
float speed = (float) point.speed;
|
||||||
|
if (speed > 0) {
|
||||||
|
totalSpeedSum += speed;
|
||||||
|
maxSpeed = Math.max(speed, maxSpeed);
|
||||||
|
speedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j > 0) {
|
||||||
|
WptPt prev = s.get(j - 1);
|
||||||
|
|
||||||
|
if (!Double.isNaN(point.ele) && !Double.isNaN(prev.ele)) {
|
||||||
|
double diff = point.ele - prev.ele;
|
||||||
|
if (diff > 0) {
|
||||||
|
diffElevationUp += diff;
|
||||||
|
} else {
|
||||||
|
diffElevationDown -= diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// totalDistance += MapUtils.getDistance(prev.lat, prev.lon, point.lat, point.lon);
|
||||||
|
// using ellipsoidal 'distanceBetween' instead of spherical haversine (MapUtils.getDistance) is
|
||||||
|
// a little more exact, also seems slightly faster:
|
||||||
|
net.osmand.Location.distanceBetween(prev.lat, prev.lon, point.lat, point.lon, calculations);
|
||||||
|
totalDistance += calculations[0];
|
||||||
|
|
||||||
|
// Averaging speed values is less exact than totalDistance/timeMoving
|
||||||
|
if (speed > 0 && point.time != 0 && prev.time != 0) {
|
||||||
|
timeMoving = timeMoving + (point.time - prev.time);
|
||||||
|
totalDistanceMoving += calculations[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!isTimeSpecified()){
|
||||||
|
startTime = filestamp;
|
||||||
|
endTime = filestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// OUTPUT:
|
||||||
|
// 1. Total distance, Start time, End time
|
||||||
|
// 2. Time span
|
||||||
|
timeSpan = endTime - startTime;
|
||||||
|
|
||||||
|
// 3. Time moving, if any
|
||||||
|
if (elevationPoints > 0) {
|
||||||
|
avgElevation = elevationPoints / totalElevation;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Elevation, eleUp, eleDown, if recorded
|
||||||
|
|
||||||
|
// 5. Max speed and Average speed, if any. Average speed is NOT overall (effective) speed, but only calculated for "moving" periods.
|
||||||
|
if(speedCount > 0) {
|
||||||
|
if(timeMoving > 0){
|
||||||
|
avgSpeed = (float) (totalDistanceMoving / timeMoving * 1000);
|
||||||
|
} else {
|
||||||
|
avgSpeed = (float) (totalSpeedSum / speedCount);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
avgSpeed = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SplitSegment {
|
||||||
|
TrkSegment segment;
|
||||||
|
float startCoeff = 0;
|
||||||
|
int startPointInd;
|
||||||
|
float endCoeff = 0;
|
||||||
|
int endPointInd;
|
||||||
|
int metricEnd;
|
||||||
|
|
||||||
|
public int getNumberOfPoints() {
|
||||||
|
return endPointInd - startPointInd + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WptPt get(int j) {
|
||||||
|
final int ind = j + startPointInd;
|
||||||
|
if(j == 0) {
|
||||||
|
if(startCoeff == 0) {
|
||||||
|
return segment.points.get(ind);
|
||||||
|
}
|
||||||
|
return approx(segment.points.get(ind), segment.points.get(ind + 1), startCoeff);
|
||||||
|
}
|
||||||
|
if(j == getNumberOfPoints()) {
|
||||||
|
if(startCoeff == 1) {
|
||||||
|
return segment.points.get(ind);
|
||||||
|
}
|
||||||
|
return approx(segment.points.get(ind - 1), segment.points.get(ind), endCoeff);
|
||||||
|
}
|
||||||
|
return segment.points.get(ind);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private WptPt approx(WptPt w1, WptPt w2, float cf) {
|
||||||
|
long time = value(w1.time, w2.time, 0, cf);
|
||||||
|
double speed = value(w1.speed, w2.speed, 0, cf);
|
||||||
|
double ele = value(w1.ele, w2.ele, 0, cf);
|
||||||
|
double hdop = value(w1.hdop, w2.hdop, 0, cf);
|
||||||
|
double lat = value(w1.lat, w2.lat, -360, cf);
|
||||||
|
double lon = value(w1.lon, w2.lon, -360, cf);
|
||||||
|
return new WptPt(lat, lon, time, ele, speed, hdop);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double value(double vl, double vl2, double none, float cf) {
|
||||||
|
if(vl == none || Double.isNaN(vl)) {
|
||||||
|
return vl2;
|
||||||
|
} else if (vl2 == none || Double.isNaN(vl2)) {
|
||||||
|
return vl;
|
||||||
|
}
|
||||||
|
return vl + cf * (vl2 - vl);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long value(long vl, long vl2, long none, float cf) {
|
||||||
|
if(vl == none) {
|
||||||
|
return vl2;
|
||||||
|
} else if(vl2 == none) {
|
||||||
|
return vl;
|
||||||
|
}
|
||||||
|
return vl + ((long) cf * (vl2 - vl));
|
||||||
|
}
|
||||||
|
|
||||||
|
public SplitSegment(TrkSegment s) {
|
||||||
|
startPointInd = 0;
|
||||||
|
startCoeff = 0;
|
||||||
|
endPointInd = s.points.size() - 1;
|
||||||
|
startCoeff = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SplitSegment(TrkSegment s, int pointInd, float cf) {
|
||||||
|
this.segment = s;
|
||||||
|
this.startPointInd = pointInd;
|
||||||
|
this.startCoeff = cf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float setLastPoint(int pointInd, float endCf) {
|
||||||
|
endCoeff = endCf;
|
||||||
|
endPointInd = pointInd;
|
||||||
|
return startCoeff;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private abstract static class SplitMetric {
|
||||||
|
|
||||||
|
public abstract int metric(WptPt p1, WptPt p2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public static class GPXFile extends GPXExtensions {
|
public static class GPXFile extends GPXExtensions {
|
||||||
public String author;
|
public String author;
|
||||||
public List<Track> tracks = new ArrayList<Track>();
|
public List<Track> tracks = new ArrayList<Track>();
|
||||||
|
@ -124,6 +353,98 @@ public class GPXUtilities {
|
||||||
return "cloudmade".equalsIgnoreCase(author);
|
return "cloudmade".equalsIgnoreCase(author);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GPXTrackAnalysis getAnalysis(long fileTimestamp) {
|
||||||
|
GPXTrackAnalysis g = new GPXTrackAnalysis();
|
||||||
|
g.wptPoints = points.size();
|
||||||
|
List<SplitSegment> splitSegments = new ArrayList<GPXUtilities.SplitSegment>();
|
||||||
|
for(int i = 0; i< tracks.size() ; i++){
|
||||||
|
Track subtrack = tracks.get(i);
|
||||||
|
for(TrkSegment segment : subtrack.segments){
|
||||||
|
g.totalTracks ++;
|
||||||
|
if(segment.points.size() > 1) {
|
||||||
|
splitSegments.add(new SplitSegment(segment));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.prepareInformation(fileTimestamp, splitSegments.toArray(new SplitSegment[splitSegments.size()]));
|
||||||
|
return g ;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public List<GPXTrackAnalysis> splitByDistance(int meters) {
|
||||||
|
|
||||||
|
return split(new SplitMetric() {
|
||||||
|
|
||||||
|
private float[] calculations = new float[1];
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int metric(WptPt p1, WptPt p2) {
|
||||||
|
net.osmand.Location.distanceBetween(p1.lat, p1.lon, p2.lat, p2.lon, calculations);
|
||||||
|
return (int) calculations[0];
|
||||||
|
}
|
||||||
|
}, meters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<GPXTrackAnalysis> splitByTime(int seconds) {
|
||||||
|
|
||||||
|
return split(new SplitMetric() {
|
||||||
|
|
||||||
|
private float[] calculations = new float[1];
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int metric(WptPt p1, WptPt p2) {
|
||||||
|
if(p1.time != 0 && p2.time != 0) {
|
||||||
|
return (int) ((p1.time - p2.time) / 1000);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}, seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<GPXTrackAnalysis> split(SplitMetric m, int metricLimit) {
|
||||||
|
int ml = metricLimit;
|
||||||
|
List<SplitSegment> splitSegments = new ArrayList<GPXUtilities.SplitSegment>();
|
||||||
|
for(int i = 0; i< tracks.size() ; i++){
|
||||||
|
Track subtrack = tracks.get(i);
|
||||||
|
for(int j = 0; j<subtrack.segments.size(); j++){
|
||||||
|
TrkSegment segment = subtrack.segments.get(j);
|
||||||
|
SplitSegment sp = new SplitSegment(segment, 0, 0);
|
||||||
|
int total = 0;
|
||||||
|
for (int k = 0; k < segment.points.size(); k++) {
|
||||||
|
WptPt point = segment.points.get(k);
|
||||||
|
if (k > 0) {
|
||||||
|
WptPt prev = segment.points.get(k - 1);
|
||||||
|
int currentSegment = m.metric(prev, point);
|
||||||
|
while(total + currentSegment > ml) {
|
||||||
|
int p = ml - total;
|
||||||
|
float cf = sp.setLastPoint(k - 1, p / ((float)currentSegment));
|
||||||
|
sp = new SplitSegment(segment, k - 1, cf);
|
||||||
|
sp.metricEnd = ml;
|
||||||
|
ml += metricLimit;
|
||||||
|
}
|
||||||
|
total += currentSegment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(segment.points.size() > 0 && !(sp.endPointInd == segment.points.size() -1 && sp.startCoeff == 1)) {
|
||||||
|
sp.metricEnd = total;
|
||||||
|
sp.setLastPoint(0, 1);
|
||||||
|
splitSegments.add(sp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return convert(splitSegments);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<GPXTrackAnalysis> convert(List<SplitSegment> splitSegments) {
|
||||||
|
List<GPXTrackAnalysis> ls = new ArrayList<GPXUtilities.GPXTrackAnalysis>();
|
||||||
|
for(SplitSegment s : splitSegments) {
|
||||||
|
GPXTrackAnalysis a = new GPXTrackAnalysis();
|
||||||
|
a.prepareInformation(0, s);
|
||||||
|
ls.add(a);
|
||||||
|
}
|
||||||
|
return ls;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasRtePt() {
|
public boolean hasRtePt() {
|
||||||
for(Route r : routes) {
|
for(Route r : routes) {
|
||||||
if(r.points.size() > 0) {
|
if(r.points.size() > 0) {
|
||||||
|
|
|
@ -25,6 +25,8 @@ import net.osmand.plus.FavouritesDbHelper;
|
||||||
import net.osmand.plus.GPXUtilities;
|
import net.osmand.plus.GPXUtilities;
|
||||||
import net.osmand.plus.GPXUtilities.GPXFile;
|
import net.osmand.plus.GPXUtilities.GPXFile;
|
||||||
import net.osmand.plus.GPXUtilities.WptPt;
|
import net.osmand.plus.GPXUtilities.WptPt;
|
||||||
|
import net.osmand.plus.sherpafy.TourSelectionFragment;
|
||||||
|
import net.osmand.plus.sherpafy.TourCommonActivity.TabsAdapter;
|
||||||
import net.osmand.plus.OsmAndFormatter;
|
import net.osmand.plus.OsmAndFormatter;
|
||||||
import net.osmand.plus.OsmandSettings;
|
import net.osmand.plus.OsmandSettings;
|
||||||
import net.osmand.plus.R;
|
import net.osmand.plus.R;
|
||||||
|
@ -36,6 +38,7 @@ import android.content.pm.ActivityInfo;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.support.v4.view.ViewPager;
|
||||||
import android.text.Spannable;
|
import android.text.Spannable;
|
||||||
import android.text.style.ForegroundColorSpan;
|
import android.text.style.ForegroundColorSpan;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
@ -48,6 +51,7 @@ import android.widget.CheckBox;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.ExpandableListView;
|
import android.widget.ExpandableListView;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TabHost;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
@ -77,6 +81,7 @@ public class FavouritesActivity extends OsmandExpandableListActivity {
|
||||||
private Set<String> groupsToDelete = new LinkedHashSet<String>();
|
private Set<String> groupsToDelete = new LinkedHashSet<String>();
|
||||||
private Comparator<FavouritePoint> favoritesComparator;
|
private Comparator<FavouritePoint> favoritesComparator;
|
||||||
private ActionMode actionMode;
|
private ActionMode actionMode;
|
||||||
|
private TabsAdapter mTabsAdapter;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -101,6 +106,20 @@ public class FavouritesActivity extends OsmandExpandableListActivity {
|
||||||
setContentView(R.layout.favourites_list);
|
setContentView(R.layout.favourites_list);
|
||||||
getSupportActionBar().setTitle(R.string.favourites_activity);
|
getSupportActionBar().setTitle(R.string.favourites_activity);
|
||||||
setSupportProgressBarIndeterminateVisibility(false);
|
setSupportProgressBarIndeterminateVisibility(false);
|
||||||
|
TabHost tabHost = (TabHost)findViewById(android.R.id.tabhost);
|
||||||
|
tabHost.setup();
|
||||||
|
|
||||||
|
ViewPager mViewPager = (ViewPager)findViewById(R.id.pager);
|
||||||
|
mTabsAdapter = new TabsAdapter(this, tabHost, mViewPager);
|
||||||
|
mTabsAdapter.addTab(tabHost.newTabSpec(TOUR_INFO).setIndicator(getString(R.string.tab_current_tour)),
|
||||||
|
TourInformationFragment.class, null);
|
||||||
|
mTabsAdapter.addTab(tabHost.newTabSpec(TOUR_STAGE).setIndicator(getString(R.string.tab_stages)),
|
||||||
|
TourStageFragment.class, null);
|
||||||
|
mTabsAdapter.addTab(tabHost.newTabSpec(TOUR_SELECTION).setIndicator(getString(R.string.tab_tours)),
|
||||||
|
TourSelectionFragment.class, null);
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
tabHost.setCurrentTabByTag(savedInstanceState.getString("tab"));
|
||||||
|
}
|
||||||
// getSupportActionBar().setIcon(R.drawable.tab_search_favorites_icon);
|
// getSupportActionBar().setIcon(R.drawable.tab_search_favorites_icon);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,701 @@
|
||||||
|
package net.osmand.plus.activities;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.text.Collator;
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import net.londatiga.android.ActionItem;
|
||||||
|
import net.londatiga.android.QuickAction;
|
||||||
|
import net.osmand.access.AccessibleToast;
|
||||||
|
import net.osmand.data.FavouritePoint;
|
||||||
|
import net.osmand.data.LatLon;
|
||||||
|
import net.osmand.plus.FavouritesDbHelper;
|
||||||
|
import net.osmand.plus.GPXUtilities;
|
||||||
|
import net.osmand.plus.OsmAndFormatter;
|
||||||
|
import net.osmand.plus.OsmandSettings;
|
||||||
|
import net.osmand.plus.R;
|
||||||
|
import net.osmand.plus.GPXUtilities.GPXFile;
|
||||||
|
import net.osmand.plus.GPXUtilities.WptPt;
|
||||||
|
import net.osmand.plus.activities.FavouritesActivity.FavouritesAdapter;
|
||||||
|
import net.osmand.plus.sherpafy.TourSelectionFragment;
|
||||||
|
import net.osmand.plus.sherpafy.TourCommonActivity.TabsAdapter;
|
||||||
|
import net.osmand.util.MapUtils;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.app.AlertDialog.Builder;
|
||||||
|
import android.app.ListFragment;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.ActivityInfo;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.v4.view.ViewPager;
|
||||||
|
import android.text.Spannable;
|
||||||
|
import android.text.style.ForegroundColorSpan;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.View.OnClickListener;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.AutoCompleteTextView;
|
||||||
|
import android.widget.CheckBox;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ExpandableListView;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TabHost;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.actionbarsherlock.view.ActionMode;
|
||||||
|
import com.actionbarsherlock.view.Menu;
|
||||||
|
import com.actionbarsherlock.view.MenuItem;
|
||||||
|
import com.actionbarsherlock.view.Window;
|
||||||
|
import com.actionbarsherlock.view.ActionMode.Callback;
|
||||||
|
|
||||||
|
public class FavouritesTreeFragment extends Fragment{
|
||||||
|
|
||||||
|
public static final int EXPORT_ID = 0;
|
||||||
|
public static final int IMPORT_ID = 1;
|
||||||
|
public static final int DELETE_ID = 2;
|
||||||
|
public static final int DELETE_ACTION_ID = 3;
|
||||||
|
public static final int SHARE_ID = 4;
|
||||||
|
|
||||||
|
|
||||||
|
private FavouritesAdapter favouritesAdapter;
|
||||||
|
private FavouritesDbHelper helper;
|
||||||
|
|
||||||
|
private boolean selectionMode = false;
|
||||||
|
private Set<FavouritePoint> favoritesToDelete = new LinkedHashSet<FavouritePoint>();
|
||||||
|
private Set<String> groupsToDelete = new LinkedHashSet<String>();
|
||||||
|
private Comparator<FavouritePoint> favoritesComparator;
|
||||||
|
private ActionMode actionMode;
|
||||||
|
private TabsAdapter mTabsAdapter;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle icicle) {
|
||||||
|
//This has to be called before setContentView and you must use the
|
||||||
|
//class in com.actionbarsherlock.view and NOT android.view
|
||||||
|
final Collator collator = Collator.getInstance();
|
||||||
|
collator.setStrength(Collator.SECONDARY);
|
||||||
|
favoritesComparator = new Comparator<FavouritePoint>(){
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(FavouritePoint object1, FavouritePoint object2) {
|
||||||
|
return collator.compare(object1.getName(), object2.getName());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
setContentView(R.layout.favourites_list);
|
||||||
|
helper = getMyApplication().getFavorites();
|
||||||
|
favouritesAdapter = new FavouritesAdapter();
|
||||||
|
favouritesAdapter.setFavoriteGroups(helper.getFavoriteGroups());
|
||||||
|
getExpandableListView().setAdapter(favouritesAdapter);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteFavorites() {
|
||||||
|
new AsyncTask<Void, Object, String>(){
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
showProgressBar();
|
||||||
|
};
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(String result) {
|
||||||
|
hideProgressBar();
|
||||||
|
favouritesAdapter.synchronizeGroups();
|
||||||
|
favouritesAdapter.sort(favoritesComparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onProgressUpdate(Object... values) {
|
||||||
|
for(Object o : values){
|
||||||
|
if(o instanceof FavouritePoint){
|
||||||
|
favouritesAdapter.deleteFavoritePoint((FavouritePoint) o);
|
||||||
|
} else if(o instanceof String){
|
||||||
|
favouritesAdapter.deleteCategory((String) o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String doInBackground(Void... params) {
|
||||||
|
for (FavouritePoint fp : favoritesToDelete) {
|
||||||
|
helper.deleteFavourite(fp);
|
||||||
|
publishProgress(fp);
|
||||||
|
}
|
||||||
|
favoritesToDelete.clear();
|
||||||
|
for (String group : groupsToDelete) {
|
||||||
|
helper.deleteGroup(group);
|
||||||
|
publishProgress(group);
|
||||||
|
}
|
||||||
|
groupsToDelete.clear();
|
||||||
|
return getString(R.string.favourites_delete_multiple_succesful);
|
||||||
|
}
|
||||||
|
|
||||||
|
}.execute();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
// final LatLon mapLocation = getMyApplication().getSettings().getLastKnownMapLocation();
|
||||||
|
favouritesAdapter.synchronizeGroups();
|
||||||
|
|
||||||
|
// Sort Favs by distance on Search tab, but sort alphabetically here
|
||||||
|
favouritesAdapter.sort(favoritesComparator);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
|
||||||
|
if(selectionMode){
|
||||||
|
CheckBox ch = (CheckBox) v.findViewById(R.id.check_item);
|
||||||
|
FavouritePoint model = favouritesAdapter.getChild(groupPosition, childPosition);
|
||||||
|
ch.setChecked(!ch.isChecked());
|
||||||
|
if(ch.isChecked()){
|
||||||
|
favoritesToDelete.add(model);
|
||||||
|
} else {
|
||||||
|
favoritesToDelete.remove(model);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final QuickAction qa = new QuickAction(v);
|
||||||
|
final OsmandSettings settings = getMyApplication().getSettings();
|
||||||
|
final FavouritePoint point = (FavouritePoint) favouritesAdapter.getChild(groupPosition, childPosition);
|
||||||
|
String name = getString(R.string.favorite) + ": " + point.getName();
|
||||||
|
LatLon location = new LatLon(point.getLatitude(), point.getLongitude());
|
||||||
|
OnClickListener onshow = new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
settings.SHOW_FAVORITES.set(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
MapActivityActions.createDirectionsActions(qa, location, point, name, settings.getLastKnownMapZoom(), this,
|
||||||
|
true, onshow, false);
|
||||||
|
if (point.isStored()) {
|
||||||
|
ActionItem edit = new ActionItem();
|
||||||
|
edit.setIcon(getResources().getDrawable(R.drawable.ic_action_edit_light));
|
||||||
|
edit.setTitle(getString(R.string.favourites_context_menu_edit));
|
||||||
|
edit.setOnClickListener(new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
editPoint(point);
|
||||||
|
qa.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
qa.addActionItem(edit);
|
||||||
|
|
||||||
|
ActionItem delete = new ActionItem();
|
||||||
|
delete.setTitle(getString(R.string.favourites_context_menu_delete));
|
||||||
|
delete.setIcon(getResources().getDrawable(R.drawable.ic_action_delete_light));
|
||||||
|
delete.setOnClickListener(new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
deletePoint(point);
|
||||||
|
qa.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
qa.addActionItem(delete);
|
||||||
|
}
|
||||||
|
|
||||||
|
qa.show();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean editPoint(final FavouritePoint point) {
|
||||||
|
Builder builder = new AlertDialog.Builder(this);
|
||||||
|
builder.setTitle(R.string.favourites_context_menu_edit);
|
||||||
|
final View v = getLayoutInflater().inflate(R.layout.favourite_edit_dialog, getExpandableListView(), false);
|
||||||
|
final AutoCompleteTextView cat = (AutoCompleteTextView) v.findViewById(R.id.Category);
|
||||||
|
final EditText editText = (EditText) v.findViewById(R.id.Name);
|
||||||
|
builder.setView(v);
|
||||||
|
editText.setText(point.getName());
|
||||||
|
cat.setText(point.getCategory());
|
||||||
|
cat.setThreshold(1);
|
||||||
|
cat.setAdapter(new ArrayAdapter<String>(this, R.layout.list_textview, helper.getFavoriteGroups().keySet().toArray(new String[] {})));
|
||||||
|
builder.setNegativeButton(R.string.default_buttons_cancel, null);
|
||||||
|
builder.setPositiveButton(R.string.default_buttons_apply, new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
boolean editied = helper.editFavouriteName(point, editText.getText().toString().trim(), cat.getText().toString());
|
||||||
|
if (editied) {
|
||||||
|
favouritesAdapter.synchronizeGroups();
|
||||||
|
favouritesAdapter.sort(favoritesComparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.create().show();
|
||||||
|
editText.requestFocus();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean deletePoint(final FavouritePoint point) {
|
||||||
|
final Resources resources = this.getResources();
|
||||||
|
Builder builder = new AlertDialog.Builder(this);
|
||||||
|
builder.setMessage(getString(R.string.favourites_remove_dialog_msg, point.getName()));
|
||||||
|
builder.setNegativeButton(R.string.default_buttons_no, null);
|
||||||
|
builder.setPositiveButton(R.string.default_buttons_yes, new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
boolean deleted = helper.deleteFavourite(point);
|
||||||
|
if (deleted) {
|
||||||
|
AccessibleToast.makeText(FavouritesActivity.this,
|
||||||
|
MessageFormat.format(resources.getString(R.string.favourites_remove_dialog_success), point.getName()),
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
favouritesAdapter.synchronizeGroups();
|
||||||
|
favouritesAdapter.sort(favoritesComparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.create().show();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(com.actionbarsherlock.view.MenuItem item) {
|
||||||
|
if (item.getItemId() == EXPORT_ID) {
|
||||||
|
export();
|
||||||
|
return true;
|
||||||
|
} else if (item.getItemId() == IMPORT_ID) {
|
||||||
|
importFile();
|
||||||
|
return true;
|
||||||
|
} else if (item.getItemId() == SHARE_ID) {
|
||||||
|
shareFavourites();
|
||||||
|
return true;
|
||||||
|
} else if (item.getItemId() == DELETE_ID) {
|
||||||
|
enterDeleteMode();
|
||||||
|
return true;
|
||||||
|
} else if (item.getItemId() == DELETE_ACTION_ID) {
|
||||||
|
deleteFavoritesAction();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(com.actionbarsherlock.view.Menu menu) {
|
||||||
|
createMenuItem(menu, EXPORT_ID, R.string.export_fav, R.drawable.ic_action_gsave_light, R.drawable.ic_action_gsave_dark,
|
||||||
|
MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
|
||||||
|
createMenuItem(menu, SHARE_ID, R.string.share_fav, R.drawable.ic_action_gshare_light, R.drawable.ic_action_gshare_dark,
|
||||||
|
MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
|
||||||
|
createMenuItem(menu, IMPORT_ID, R.string.import_fav, R.drawable.ic_action_grefresh_light, R.drawable.ic_action_grefresh_dark,
|
||||||
|
MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
|
||||||
|
createMenuItem(menu, DELETE_ID, R.string.default_buttons_delete, R.drawable.ic_action_delete_light, R.drawable.ic_action_delete_dark,
|
||||||
|
MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT) ;
|
||||||
|
return super.onCreateOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showProgressBar(){
|
||||||
|
setSupportProgressBarIndeterminateVisibility(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hideProgressBar(){
|
||||||
|
setSupportProgressBarIndeterminateVisibility(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void enterDeleteMode() {
|
||||||
|
actionMode = startActionMode(new Callback() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||||
|
selectionMode = true;
|
||||||
|
createMenuItem(menu, DELETE_ACTION_ID, R.string.default_buttons_delete, R.drawable.ic_action_delete_light, R.drawable.ic_action_delete_dark,
|
||||||
|
MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||||
|
favoritesToDelete.clear();
|
||||||
|
groupsToDelete.clear();
|
||||||
|
favouritesAdapter.notifyDataSetInvalidated();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyActionMode(ActionMode mode) {
|
||||||
|
selectionMode = false;
|
||||||
|
favouritesAdapter.notifyDataSetInvalidated();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||||
|
if(item.getItemId() == DELETE_ACTION_ID) {
|
||||||
|
deleteFavoritesAction();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteFavoritesAction() {
|
||||||
|
if (groupsToDelete.size() + favoritesToDelete.size() > 0) {
|
||||||
|
|
||||||
|
Builder b = new AlertDialog.Builder(FavouritesActivity.this);
|
||||||
|
b.setMessage(getString(R.string.favorite_delete_multiple, favoritesToDelete.size(), groupsToDelete.size()));
|
||||||
|
b.setPositiveButton(R.string.default_buttons_delete, new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
if(actionMode != null) {
|
||||||
|
actionMode.finish();
|
||||||
|
}
|
||||||
|
deleteFavorites();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
b.setNegativeButton(R.string.default_buttons_cancel, null);
|
||||||
|
b.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void importFile() {
|
||||||
|
final File tosave = getMyApplication().getAppPath(FavouritesDbHelper.FILE_TO_SAVE);
|
||||||
|
if (!tosave.exists()) {
|
||||||
|
AccessibleToast.makeText(this, MessageFormat.format(getString(R.string.fav_file_to_load_not_found), tosave.getAbsolutePath()),
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
} else {
|
||||||
|
new AsyncTask<Void, FavouritePoint, String>() {
|
||||||
|
@Override
|
||||||
|
protected String doInBackground(Void... params) {
|
||||||
|
Set<String> existedPoints = new LinkedHashSet<String>();
|
||||||
|
if (!favouritesAdapter.isEmpty()) {
|
||||||
|
for (FavouritePoint fp : helper.getFavouritePoints()) {
|
||||||
|
existedPoints.add(fp.getName() + "_" + fp.getCategory());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GPXFile res = GPXUtilities.loadGPXFile(getMyApplication(), tosave);
|
||||||
|
if (res.warning != null) {
|
||||||
|
return res.warning;
|
||||||
|
}
|
||||||
|
for (WptPt p : res.points) {
|
||||||
|
if (existedPoints.contains(p.name) || existedPoints.contains(p.name + "_" + p.category)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int c;
|
||||||
|
String name = p.name;
|
||||||
|
String categoryName = p.category != null ? p.category : "";
|
||||||
|
if (name == null) {
|
||||||
|
name = "";
|
||||||
|
}
|
||||||
|
// old way to store the category, in name.
|
||||||
|
if ("".equals(categoryName.trim()) && (c = p.name.lastIndexOf('_')) != -1) {
|
||||||
|
categoryName = p.name.substring(c + 1);
|
||||||
|
name = p.name.substring(0, c);
|
||||||
|
}
|
||||||
|
FavouritePoint fp = new FavouritePoint(p.lat, p.lon, name, categoryName);
|
||||||
|
if (helper.addFavourite(fp)) {
|
||||||
|
publishProgress(fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onProgressUpdate(FavouritePoint... values) {
|
||||||
|
for (FavouritePoint p : values) {
|
||||||
|
favouritesAdapter.addFavoritePoint(p);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
showProgressBar();
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(String warning) {
|
||||||
|
hideProgressBar();
|
||||||
|
if (warning == null) {
|
||||||
|
AccessibleToast.makeText(FavouritesActivity.this, R.string.fav_imported_sucessfully, Toast.LENGTH_SHORT).show();
|
||||||
|
} else {
|
||||||
|
AccessibleToast.makeText(FavouritesActivity.this, warning, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
favouritesAdapter.synchronizeGroups();
|
||||||
|
favouritesAdapter.sort(favoritesComparator);
|
||||||
|
};
|
||||||
|
|
||||||
|
}.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shareFavourites() {
|
||||||
|
if (favouritesAdapter.isEmpty()) {
|
||||||
|
AccessibleToast.makeText(this, R.string.no_fav_to_save, Toast.LENGTH_LONG).show();
|
||||||
|
} else {
|
||||||
|
final AsyncTask<Void, Void, GPXFile> exportTask = new AsyncTask<Void, Void, GPXFile>() {
|
||||||
|
@Override
|
||||||
|
protected GPXFile doInBackground(Void... params) {
|
||||||
|
return helper.asGpxFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
showProgressBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(GPXFile gpxFile) {
|
||||||
|
hideProgressBar();
|
||||||
|
final Intent sendIntent = new Intent();
|
||||||
|
sendIntent.setAction(Intent.ACTION_SEND);
|
||||||
|
sendIntent.putExtra(Intent.EXTRA_TEXT, GPXUtilities.asString(gpxFile, getMyApplication()));
|
||||||
|
sendIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.share_fav_subject));
|
||||||
|
sendIntent.setType("application/gpx+xml");
|
||||||
|
startActivity(sendIntent);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exportTask.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void export() {
|
||||||
|
final File tosave = getMyApplication().getAppPath(FavouritesDbHelper.FILE_TO_SAVE);
|
||||||
|
if (favouritesAdapter.isEmpty()) {
|
||||||
|
AccessibleToast.makeText(this, R.string.no_fav_to_save, Toast.LENGTH_LONG).show();
|
||||||
|
} else if (!tosave.getParentFile().exists()) {
|
||||||
|
AccessibleToast.makeText(this, R.string.sd_dir_not_accessible, Toast.LENGTH_LONG).show();
|
||||||
|
} else {
|
||||||
|
final AsyncTask<Void, Void, String> exportTask = new AsyncTask<Void, Void, String>() {
|
||||||
|
@Override
|
||||||
|
protected String doInBackground(Void... params) {
|
||||||
|
return helper.exportFavorites();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
showProgressBar();
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(String warning) {
|
||||||
|
hideProgressBar();
|
||||||
|
if (warning == null) {
|
||||||
|
AccessibleToast.makeText(FavouritesActivity.this,
|
||||||
|
MessageFormat.format(getString(R.string.fav_saved_sucessfully), tosave.getAbsolutePath()),
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
} else {
|
||||||
|
AccessibleToast.makeText(FavouritesActivity.this, warning, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
if (tosave.exists()) {
|
||||||
|
Builder bld = new AlertDialog.Builder(this);
|
||||||
|
bld.setPositiveButton(R.string.default_buttons_yes, new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
exportTask.execute();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
bld.setNegativeButton(R.string.default_buttons_no, null);
|
||||||
|
bld.setMessage(R.string.fav_export_confirmation);
|
||||||
|
bld.show();
|
||||||
|
} else {
|
||||||
|
exportTask.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class FavouritesAdapter extends OsmandBaseExpandableListAdapter {
|
||||||
|
|
||||||
|
Map<String, List<FavouritePoint>> sourceFavoriteGroups;
|
||||||
|
Map<String, List<FavouritePoint>> favoriteGroups = new LinkedHashMap<String, List<FavouritePoint>>();
|
||||||
|
List<String> groups = new ArrayList<String>();
|
||||||
|
|
||||||
|
public void setFavoriteGroups(Map<String, List<FavouritePoint>> favoriteGroups) {
|
||||||
|
this.sourceFavoriteGroups = favoriteGroups;
|
||||||
|
synchronizeGroups();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sort(Comparator<FavouritePoint> comparator) {
|
||||||
|
for(List<FavouritePoint> ps : favoriteGroups.values()) {
|
||||||
|
Collections.sort(ps, comparator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addFavoritePoint(FavouritePoint p) {
|
||||||
|
if(!favoriteGroups.containsKey(p.getCategory())){
|
||||||
|
favoriteGroups.put(p.getCategory(), new ArrayList<FavouritePoint>());
|
||||||
|
groups.add(p.getCategory());
|
||||||
|
}
|
||||||
|
favoriteGroups.get(p.getCategory()).add(p);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteFavoritePoint(FavouritePoint p) {
|
||||||
|
if(favoriteGroups.containsKey(p.getCategory())){
|
||||||
|
favoriteGroups.get(p.getCategory()).remove(p);
|
||||||
|
}
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteCategory(String p) {
|
||||||
|
favoriteGroups.remove(p);
|
||||||
|
groups.remove(p);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void synchronizeGroups(){
|
||||||
|
favoriteGroups.clear();
|
||||||
|
groups.clear();
|
||||||
|
for(String key : sourceFavoriteGroups.keySet()){
|
||||||
|
groups.add(key);
|
||||||
|
favoriteGroups.put(key, new ArrayList<FavouritePoint>(sourceFavoriteGroups.get(key)));
|
||||||
|
}
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FavouritePoint getChild(int groupPosition, int childPosition) {
|
||||||
|
return favoriteGroups.get(groups.get(groupPosition)).get(childPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getChildId(int groupPosition, int childPosition) {
|
||||||
|
return groupPosition * 10000 + childPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getChildrenCount(int groupPosition) {
|
||||||
|
return favoriteGroups.get(groups.get(groupPosition)).size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getGroup(int groupPosition) {
|
||||||
|
return groups.get(groupPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getGroupCount() {
|
||||||
|
return groups.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getGroupId(int groupPosition) {
|
||||||
|
return groupPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasStableIds() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isChildSelectable(int groupPosition, int childPosition) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
|
||||||
|
View row = convertView;
|
||||||
|
if (row == null) {
|
||||||
|
LayoutInflater inflater = getLayoutInflater();
|
||||||
|
row = inflater.inflate(R.layout.expandable_list_item_category, parent, false);
|
||||||
|
fixBackgroundRepeat(row);
|
||||||
|
}
|
||||||
|
adjustIndicator(groupPosition, isExpanded, row);
|
||||||
|
TextView label = (TextView) row.findViewById(R.id.category_name);
|
||||||
|
final String model = getGroup(groupPosition);
|
||||||
|
label.setText(model);
|
||||||
|
final CheckBox ch = (CheckBox) row.findViewById(R.id.check_item);
|
||||||
|
|
||||||
|
if(selectionMode){
|
||||||
|
ch.setVisibility(View.VISIBLE);
|
||||||
|
ch.setChecked(groupsToDelete.contains(model));
|
||||||
|
|
||||||
|
ch.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
if (ch.isChecked()) {
|
||||||
|
groupsToDelete.add(model);
|
||||||
|
List<FavouritePoint> fvs = helper.getFavoriteGroups().get(model);
|
||||||
|
if (fvs != null) {
|
||||||
|
favoritesToDelete.addAll(fvs);
|
||||||
|
}
|
||||||
|
favouritesAdapter.notifyDataSetInvalidated();
|
||||||
|
} else {
|
||||||
|
groupsToDelete.remove(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ch.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
|
||||||
|
View row = convertView;
|
||||||
|
if (row == null) {
|
||||||
|
LayoutInflater inflater = getLayoutInflater();
|
||||||
|
row = inflater.inflate(R.layout.favourites_list_item, parent, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextView label = (TextView) row.findViewById(R.id.favourite_label);
|
||||||
|
ImageView icon = (ImageView) row.findViewById(R.id.favourite_icon);
|
||||||
|
final FavouritePoint model = (FavouritePoint) getChild(groupPosition, childPosition);
|
||||||
|
row.setTag(model);
|
||||||
|
if(model.isStored()){
|
||||||
|
icon.setImageResource(R.drawable.list_favorite);
|
||||||
|
} else {
|
||||||
|
icon.setImageResource(R.drawable.opened_poi);
|
||||||
|
}
|
||||||
|
LatLon lastKnownMapLocation = getMyApplication().getSettings().getLastKnownMapLocation();
|
||||||
|
int dist = (int) (MapUtils.getDistance(model.getLatitude(), model.getLongitude(),
|
||||||
|
lastKnownMapLocation.getLatitude(), lastKnownMapLocation.getLongitude()));
|
||||||
|
String distance = OsmAndFormatter.getFormattedDistance(dist, getMyApplication()) + " ";
|
||||||
|
label.setText(distance + model.getName(), TextView.BufferType.SPANNABLE);
|
||||||
|
((Spannable) label.getText()).setSpan(new ForegroundColorSpan(getResources().getColor(R.color.color_distance)), 0, distance.length() - 1, 0);
|
||||||
|
final CheckBox ch = (CheckBox) row.findViewById(R.id.check_item);
|
||||||
|
if(selectionMode && model.isStored()){
|
||||||
|
ch.setVisibility(View.VISIBLE);
|
||||||
|
ch.setChecked(favoritesToDelete.contains(model));
|
||||||
|
row.findViewById(R.id.favourite_icon).setVisibility(View.GONE);
|
||||||
|
ch.setOnClickListener(new View.OnClickListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
if(ch.isChecked()){
|
||||||
|
favoritesToDelete.add(model);
|
||||||
|
} else {
|
||||||
|
favoritesToDelete.remove(model);
|
||||||
|
if(groupsToDelete.contains(model.getCategory())){
|
||||||
|
groupsToDelete.remove(model.getCategory());
|
||||||
|
favouritesAdapter.notifyDataSetInvalidated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
row.findViewById(R.id.favourite_icon).setVisibility(View.VISIBLE);
|
||||||
|
ch.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
package net.osmand.plus.activities;
|
||||||
|
|
||||||
|
import net.osmand.plus.OsmandApplication;
|
||||||
|
import net.osmand.plus.R;
|
||||||
|
import android.app.ActionBar;
|
||||||
|
import android.graphics.Shader.TileMode;
|
||||||
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ExpandableListView;
|
||||||
|
|
||||||
|
import com.actionbarsherlock.app.SherlockExpandableListActivity;
|
||||||
|
import com.actionbarsherlock.app.SherlockFragment;
|
||||||
|
import com.actionbarsherlock.view.Menu;
|
||||||
|
import com.actionbarsherlock.view.MenuItem;
|
||||||
|
import com.actionbarsherlock.view.MenuItem.OnMenuItemClickListener;
|
||||||
|
|
||||||
|
public abstract class OsmandExpandableListFragment extends SherlockFragment {
|
||||||
|
|
||||||
|
|
||||||
|
public OsmandApplication getMyApplication() {
|
||||||
|
return (OsmandApplication)getActivity().getApplication();
|
||||||
|
}
|
||||||
|
|
||||||
|
public
|
||||||
|
public View onCreateView(android.view.LayoutInflater inflater, android.view.ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
com.android.internal.R.layout.list_content
|
||||||
|
listView = new ExpandableListView(getActivity());
|
||||||
|
return listView;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MenuItem createMenuItem(Menu m, int id, int titleRes, int iconLight, int iconDark, int menuItemType) {
|
||||||
|
int r = isLightActionBar() ? iconLight : iconDark;
|
||||||
|
MenuItem menuItem = m.add(0, id, 0, titleRes);
|
||||||
|
if (r != 0) {
|
||||||
|
menuItem.setIcon(r);
|
||||||
|
}
|
||||||
|
menuItem.setShowAsActionFlags(menuItemType).setOnMenuItemClickListener(new OnMenuItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onMenuItemClick(com.actionbarsherlock.view.MenuItem item) {
|
||||||
|
return onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return menuItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isLightActionBar() {
|
||||||
|
return ((OsmandApplication) getApplication()).getSettings().isLightActionBar();
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,9 +12,7 @@ import net.osmand.access.AccessibleToast;
|
||||||
import net.osmand.plus.ContextMenuAdapter;
|
import net.osmand.plus.ContextMenuAdapter;
|
||||||
import net.osmand.plus.GPXUtilities;
|
import net.osmand.plus.GPXUtilities;
|
||||||
import net.osmand.plus.GPXUtilities.GPXFile;
|
import net.osmand.plus.GPXUtilities.GPXFile;
|
||||||
import net.osmand.plus.GPXUtilities.Track;
|
import net.osmand.plus.GPXUtilities.GPXTrackAnalysis;
|
||||||
import net.osmand.plus.GPXUtilities.TrkSegment;
|
|
||||||
import net.osmand.plus.GPXUtilities.WptPt;
|
|
||||||
import net.osmand.plus.OsmAndFormatter;
|
import net.osmand.plus.OsmAndFormatter;
|
||||||
import net.osmand.plus.OsmandApplication;
|
import net.osmand.plus.OsmandApplication;
|
||||||
import net.osmand.plus.R;
|
import net.osmand.plus.R;
|
||||||
|
@ -42,130 +40,46 @@ import android.widget.Toast;
|
||||||
public class GpxUiHelper {
|
public class GpxUiHelper {
|
||||||
|
|
||||||
public static String getDescription(OsmandApplication app, GPXFile result, File f) {
|
public static String getDescription(OsmandApplication app, GPXFile result, File f) {
|
||||||
|
GPXTrackAnalysis analysis = result.getAnalysis(f.lastModified());
|
||||||
|
return getDescription(app, analysis);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getDescription(OsmandApplication app, GPXTrackAnalysis analysis) {
|
||||||
StringBuilder description = new StringBuilder();
|
StringBuilder description = new StringBuilder();
|
||||||
float totalDistance = 0;
|
|
||||||
int totalTracks = 0;
|
|
||||||
long startTime = Long.MAX_VALUE;
|
|
||||||
long endTime = Long.MIN_VALUE;
|
|
||||||
long timeSpan = 0;
|
|
||||||
long timeMoving = 0;
|
|
||||||
float totalDistanceMoving = 0;
|
|
||||||
|
|
||||||
double diffElevationUp = 0;
|
|
||||||
double diffElevationDown = 0;
|
|
||||||
double totalElevation = 0;
|
|
||||||
double minElevation = 99999;
|
|
||||||
double maxElevation = 0;
|
|
||||||
|
|
||||||
float maxSpeed = 0;
|
|
||||||
int speedCount = 0;
|
|
||||||
double totalSpeedSum = 0;
|
|
||||||
|
|
||||||
float[] calculations = new float[1];
|
|
||||||
|
|
||||||
int points = 0;
|
|
||||||
for(int i = 0; i< result.tracks.size() ; i++){
|
|
||||||
Track subtrack = result.tracks.get(i);
|
|
||||||
for(TrkSegment segment : subtrack.segments){
|
|
||||||
totalTracks++;
|
|
||||||
points += segment.points.size();
|
|
||||||
for (int j = 0; j < segment.points.size(); j++) {
|
|
||||||
WptPt point = segment.points.get(j);
|
|
||||||
long time = point.time;
|
|
||||||
if(time != 0){
|
|
||||||
startTime = Math.min(startTime, time);
|
|
||||||
endTime = Math.max(startTime, time);
|
|
||||||
}
|
|
||||||
|
|
||||||
double elevation = point.ele;
|
|
||||||
if (!Double.isNaN(elevation)) {
|
|
||||||
totalElevation += elevation;
|
|
||||||
minElevation = Math.min(elevation, minElevation);
|
|
||||||
maxElevation = Math.max(elevation, maxElevation);
|
|
||||||
}
|
|
||||||
|
|
||||||
float speed = (float) point.speed;
|
|
||||||
if(speed > 0){
|
|
||||||
totalSpeedSum += speed;
|
|
||||||
maxSpeed = Math.max(speed, maxSpeed);
|
|
||||||
speedCount ++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j > 0) {
|
|
||||||
WptPt prev = segment.points.get(j - 1);
|
|
||||||
|
|
||||||
if (!Double.isNaN(point.ele) && !Double.isNaN(prev.ele)) {
|
|
||||||
double diff = point.ele - prev.ele;
|
|
||||||
if (diff > 0) {
|
|
||||||
diffElevationUp += diff;
|
|
||||||
} else {
|
|
||||||
diffElevationDown -= diff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//totalDistance += MapUtils.getDistance(prev.lat, prev.lon, point.lat, point.lon);
|
|
||||||
// using ellipsoidal 'distanceBetween' instead of spherical haversine (MapUtils.getDistance) is a little more exact, also seems slightly faster:
|
|
||||||
net.osmand.Location.distanceBetween(prev.lat, prev.lon, point.lat, point.lon, calculations);
|
|
||||||
totalDistance += calculations[0];
|
|
||||||
|
|
||||||
// Averaging speed values is less exact than totalDistance/timeMoving
|
|
||||||
if(speed > 0 && point.time != 0 && prev.time != 0){
|
|
||||||
timeMoving = timeMoving + (point.time - prev.time);
|
|
||||||
totalDistanceMoving += calculations[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(startTime == Long.MAX_VALUE){
|
|
||||||
startTime = f.lastModified();
|
|
||||||
}
|
|
||||||
if(endTime == Long.MIN_VALUE){
|
|
||||||
endTime = f.lastModified();
|
|
||||||
}
|
|
||||||
|
|
||||||
// OUTPUT:
|
// OUTPUT:
|
||||||
// 1. Total distance, Start time, End time
|
// 1. Total distance, Start time, End time
|
||||||
description.append(app.getString(R.string.local_index_gpx_info, totalTracks, points,
|
description.append(app.getString(R.string.local_index_gpx_info, analysis.totalTracks, analysis.points,
|
||||||
result.points.size(), OsmAndFormatter.getFormattedDistance(totalDistance, app),
|
analysis.wptPoints, OsmAndFormatter.getFormattedDistance(analysis.totalDistance, app),
|
||||||
startTime, endTime));
|
analysis.startTime, analysis.endTime));
|
||||||
|
|
||||||
// 2. Time span
|
// 2. Time span
|
||||||
timeSpan = endTime - startTime;
|
description.append(app.getString(R.string.local_index_gpx_timespan, analysis.getTimeHours(analysis.timeSpan),
|
||||||
description.append(app.getString(R.string.local_index_gpx_timespan, (int) ((timeSpan / 1000) / 3600), (int) (((timeSpan / 1000) / 60) % 60), (int) ((timeSpan / 1000) % 60)));
|
analysis.getTimeMinutes(analysis.timeSpan), analysis.getTimeSeconds(analysis.timeSpan)));
|
||||||
|
|
||||||
// 3. Time moving, if any
|
// 3. Time moving, if any
|
||||||
if(timeMoving > 0){
|
if(analysis.isTimeMoving()){
|
||||||
description.append(
|
description.append(
|
||||||
app.getString(R.string.local_index_gpx_timemoving, (int) ((timeMoving / 1000) / 3600), (int) (((timeMoving / 1000) / 60) % 60), (int) ((timeMoving / 1000) % 60)));
|
app.getString(R.string.local_index_gpx_timemoving, analysis.getTimeHours(analysis.timeMoving),
|
||||||
|
analysis.getTimeMinutes(analysis.timeMoving), analysis.getTimeSeconds(analysis.timeMoving)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Elevation, eleUp, eleDown, if recorded
|
// 4. Elevation, eleUp, eleDown, if recorded
|
||||||
if(totalElevation != 0 || diffElevationUp != 0 || diffElevationDown != 0){
|
if(analysis.isElevationSpecified()){
|
||||||
description.append(
|
description.append(
|
||||||
app.getString(R.string.local_index_gpx_info_elevation,
|
app.getString(R.string.local_index_gpx_info_elevation,
|
||||||
OsmAndFormatter.getFormattedAlt(totalElevation / points, app),
|
OsmAndFormatter.getFormattedAlt(analysis.avgElevation, app),
|
||||||
OsmAndFormatter.getFormattedAlt(minElevation, app),
|
OsmAndFormatter.getFormattedAlt(analysis.minElevation, app),
|
||||||
OsmAndFormatter.getFormattedAlt(maxElevation, app),
|
OsmAndFormatter.getFormattedAlt(analysis.maxElevation, app),
|
||||||
OsmAndFormatter.getFormattedAlt(diffElevationUp, app),
|
OsmAndFormatter.getFormattedAlt(analysis.diffElevationUp, app),
|
||||||
OsmAndFormatter.getFormattedAlt(diffElevationDown, app)));
|
OsmAndFormatter.getFormattedAlt(analysis.diffElevationDown, app)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Max speed and Average speed, if any. Average speed is NOT overall (effective) speed, but only calculated for "moving" periods.
|
|
||||||
if(speedCount > 0){
|
if(analysis.isSpeedSpecified()){
|
||||||
if(timeMoving > 0){
|
|
||||||
description.append(
|
description.append(
|
||||||
app.getString(R.string.local_index_gpx_info_speed,
|
app.getString(R.string.local_index_gpx_info_speed,
|
||||||
OsmAndFormatter.getFormattedSpeed((float) (totalDistanceMoving / timeMoving * 1000), app),
|
OsmAndFormatter.getFormattedSpeed(analysis.avgSpeed, app),
|
||||||
OsmAndFormatter.getFormattedSpeed(maxSpeed, app)));
|
OsmAndFormatter.getFormattedSpeed(analysis.maxSpeed, app)));
|
||||||
// (Use totalDistanceMoving instead of totalDistance for av-speed to ignore effect of position fluctuations at rest)
|
|
||||||
} else {
|
|
||||||
// Averaging speed values is less exact than totalDistance/timeMoving
|
|
||||||
description.append(
|
|
||||||
app.getString(R.string.local_index_gpx_info_speed,
|
|
||||||
OsmAndFormatter.getFormattedSpeed((float) (totalSpeedSum / speedCount), app),
|
|
||||||
OsmAndFormatter.getFormattedSpeed(maxSpeed, app)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return description.toString();
|
return description.toString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package net.osmand.plus.osmo;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.osmand.Location;
|
||||||
import net.osmand.data.LatLon;
|
import net.osmand.data.LatLon;
|
||||||
import net.osmand.plus.NavigationService;
|
import net.osmand.plus.NavigationService;
|
||||||
import net.osmand.plus.OsmandApplication;
|
import net.osmand.plus.OsmandApplication;
|
||||||
|
@ -54,7 +55,16 @@ public class OsMoControlDevice implements OsMoReactor {
|
||||||
@Override
|
@Override
|
||||||
public boolean acceptCommand(String command, String id, String data, JSONObject obj, OsMoThread tread) {
|
public boolean acceptCommand(String command, String id, String data, JSONObject obj, OsMoThread tread) {
|
||||||
if(command.equals("REMOTE_CONTROL")) {
|
if(command.equals("REMOTE_CONTROL")) {
|
||||||
if(data.equals("BATTERY_INFO")) {
|
if(data.equals("PP")) {
|
||||||
|
service.pushCommand("PP");
|
||||||
|
} else if(data.equals("FF")) {
|
||||||
|
Location ll = app.getLocationProvider().getLastKnownLocation();
|
||||||
|
if(ll == null) {
|
||||||
|
service.pushCommand("FF|0");
|
||||||
|
} else {
|
||||||
|
service.pushCommand("FF|"+OsMoTracker.formatLocation(ll));
|
||||||
|
}
|
||||||
|
} else if(data.equals("BATTERY_INFO")) {
|
||||||
try {
|
try {
|
||||||
service.pushCommand("BATTERY_INFO|"+getBatteryLevel().toString());
|
service.pushCommand("BATTERY_INFO|"+getBatteryLevel().toString());
|
||||||
} catch(JSONException e) {
|
} catch(JSONException e) {
|
||||||
|
|
|
@ -92,7 +92,7 @@ public class OsMoPositionLayer extends OsmandMapLayer implements ContextMenuLaye
|
||||||
pth = new Path();
|
pth = new Path();
|
||||||
|
|
||||||
pointOuter = new Paint();
|
pointOuter = new Paint();
|
||||||
pointOuter.setColor(Color.GRAY);
|
pointOuter.setColor(0x88555555);
|
||||||
pointOuter.setAntiAlias(true);
|
pointOuter.setAntiAlias(true);
|
||||||
pointOuter.setStyle(Style.FILL_AND_STROKE);
|
pointOuter.setStyle(Style.FILL_AND_STROKE);
|
||||||
}
|
}
|
||||||
|
@ -122,34 +122,39 @@ public class OsMoPositionLayer extends OsmandMapLayer implements ContextMenuLaye
|
||||||
long treshold = System.currentTimeMillis() - 15000;
|
long treshold = System.currentTimeMillis() - 15000;
|
||||||
for (OsMoDevice t : getTrackingDevices()) {
|
for (OsMoDevice t : getTrackingDevices()) {
|
||||||
Location l = t.getLastLocation();
|
Location l = t.getLastLocation();
|
||||||
if (l != null) {
|
ConcurrentLinkedQueue<Location> plocations = t.getPreviousLocations(treshold);
|
||||||
ConcurrentLinkedQueue<Location> plocations = t.getPreviousLocations(treshold);
|
if (!plocations.isEmpty() && l != null) {
|
||||||
int x = (int) tb.getPixXFromLatLon(l.getLatitude(), l.getLongitude());
|
int x = (int) tb.getPixXFromLatLon(l.getLatitude(), l.getLongitude());
|
||||||
int y = (int) tb.getPixYFromLatLon(l.getLatitude(), l.getLongitude());
|
int y = (int) tb.getPixYFromLatLon(l.getLatitude(), l.getLongitude());
|
||||||
if (plocations.size() > 0) {
|
pth.rewind();
|
||||||
pth.rewind();
|
Iterator<Location> it = plocations.iterator();
|
||||||
Iterator<Location> it = plocations.iterator();
|
boolean f = true;
|
||||||
boolean f= true;
|
while (it.hasNext()) {
|
||||||
while (it.hasNext()) {
|
Location lo = it.next();
|
||||||
Location lo = it.next();
|
int xt = (int) tb.getPixXFromLatLon(lo.getLatitude(), lo.getLongitude());
|
||||||
int xt = (int) tb.getPixXFromLatLon(lo.getLatitude(), lo.getLongitude());
|
int yt = (int) tb.getPixYFromLatLon(lo.getLatitude(), lo.getLongitude());
|
||||||
int yt = (int) tb.getPixYFromLatLon(lo.getLatitude(), lo.getLongitude());
|
if (f) {
|
||||||
if(f) {
|
f = false;
|
||||||
f = false;
|
pth.moveTo(xt, yt);
|
||||||
pth.moveTo(xt, yt);
|
} else {
|
||||||
} else{
|
pth.lineTo(xt, yt);
|
||||||
pth.lineTo(xt, yt);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pth.lineTo(x, y);
|
|
||||||
paintPath.setColor(t.getColor());
|
|
||||||
canvas.drawPath(pth, paintPath);
|
|
||||||
}
|
}
|
||||||
|
pth.lineTo(x, y);
|
||||||
|
paintPath.setColor(t.getColor());
|
||||||
|
canvas.drawPath(pth, paintPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (OsMoDevice t : getTrackingDevices()) {
|
||||||
|
Location l = t.getLastLocation();
|
||||||
|
if (l != null) {
|
||||||
|
int x = (int) tb.getPixXFromLatLon(l.getLatitude(), l.getLongitude());
|
||||||
|
int y = (int) tb.getPixYFromLatLon(l.getLatitude(), l.getLongitude());
|
||||||
pointInnerCircle.setColor(t.getColor());
|
pointInnerCircle.setColor(t.getColor());
|
||||||
canvas.drawCircle(x, y, r + 2, pointOuter);
|
canvas.drawCircle(x, y, r + (float)Math.ceil(tb.getDensity()), pointOuter);
|
||||||
canvas.drawCircle(x, y, r - 2, pointInnerCircle);
|
canvas.drawCircle(x, y, r - (float)Math.ceil(tb.getDensity()), pointInnerCircle);
|
||||||
paintTextIcon.setTextSize(r);
|
paintTextIcon.setTextSize(r * 3 / 2);
|
||||||
canvas.drawText(t.getVisibleName().substring(0, 1), x, y, paintTextIcon);
|
canvas.drawText(t.getVisibleName().substring(0, 1).toUpperCase(), x, y - r, paintTextIcon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,29 +78,33 @@ public class OsMoTracker implements OsMoReactor {
|
||||||
if(!bufferOfLocations.isEmpty()){
|
if(!bufferOfLocations.isEmpty()){
|
||||||
Location loc = bufferOfLocations.poll();
|
Location loc = bufferOfLocations.poll();
|
||||||
lastSendLocation = loc;
|
lastSendLocation = loc;
|
||||||
StringBuilder cmd = new StringBuilder("T|");
|
|
||||||
cmd.append("L").append((float)loc.getLatitude()).append(":").append((float)loc.getLongitude());
|
|
||||||
if(loc.hasAccuracy()) {
|
|
||||||
cmd.append("H").append((float)loc.getAccuracy());
|
|
||||||
}
|
|
||||||
if(loc.hasAltitude()) {
|
|
||||||
cmd.append("A").append((float)loc.getAltitude());
|
|
||||||
}
|
|
||||||
if(loc.hasSpeed()) {
|
|
||||||
cmd.append("S").append((float)loc.getSpeed());
|
|
||||||
}
|
|
||||||
if(loc.hasBearing()) {
|
|
||||||
cmd.append("C").append((float)loc.getBearing());
|
|
||||||
}
|
|
||||||
if((System.currentTimeMillis() - loc.getTime()) > 30000 && loc.getTime() != 0) {
|
|
||||||
cmd.append("T").append(loc.getTime());
|
|
||||||
}
|
|
||||||
locationsSent ++;
|
locationsSent ++;
|
||||||
return cmd.toString();
|
return "T|"+formatLocation(loc);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String formatLocation(Location loc) {
|
||||||
|
StringBuilder cmd = new StringBuilder();
|
||||||
|
cmd.append("L").append((float)loc.getLatitude()).append(":").append((float)loc.getLongitude());
|
||||||
|
if(loc.hasAccuracy()) {
|
||||||
|
cmd.append("H").append((float)loc.getAccuracy());
|
||||||
|
}
|
||||||
|
if(loc.hasAltitude()) {
|
||||||
|
cmd.append("A").append((float)loc.getAltitude());
|
||||||
|
}
|
||||||
|
if(loc.hasSpeed()) {
|
||||||
|
cmd.append("S").append((float)loc.getSpeed());
|
||||||
|
}
|
||||||
|
if(loc.hasBearing()) {
|
||||||
|
cmd.append("C").append((float)loc.getBearing());
|
||||||
|
}
|
||||||
|
if((System.currentTimeMillis() - loc.getTime()) > 30000 && loc.getTime() != 0) {
|
||||||
|
cmd.append("T").append(loc.getTime());
|
||||||
|
}
|
||||||
|
return cmd.toString();
|
||||||
|
}
|
||||||
|
|
||||||
public Location getLastSendLocation() {
|
public Location getLastSendLocation() {
|
||||||
return lastSendLocation;
|
return lastSendLocation;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue