Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display distance, average and maximum speed #58

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,19 @@
import me.guillaumin.android.osmtracker.R;
import me.guillaumin.android.osmtracker.db.TrackContentProvider;
import me.guillaumin.android.osmtracker.db.TrackContentProvider.Schema;
import me.guillaumin.android.osmtracker.db.TracklistAdapter;
import me.guillaumin.android.osmtracker.db.model.Track;
import me.guillaumin.android.osmtracker.db.model.TrackStatistics;
import me.guillaumin.android.osmtracker.gpx.ExportToStorageTask;
import me.guillaumin.android.osmtracker.util.MercatorProjection;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Intent;
import android.database.Cursor;
import android.database.ContentObserver;
import android.graphics.Paint;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.view.Menu;
import android.view.MenuInflater;
Expand Down Expand Up @@ -70,10 +74,33 @@ public class TrackDetail extends TrackDetailEditor implements AdapterView.OnItem
* List with track info
*/
private ListView lv;

/**
* Adapter for the above list
*/
TrackDetailSimpleAdapter adapter = null;

/**
* Observes changes on trackpoints
*/
private ContentObserver trackpointContentObserver;

/**
* Statistics for the track
*/
private TrackStatistics trackStatistics;

/**
* Data for the track info
*/
private List<HashMap<String, String>> trackData = new ArrayList<HashMap<String, String>> ();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState, R.layout.trackdetail, getIntent().getExtras().getLong(Schema.COL_TRACK_ID));
Bundle extras = getIntent().getExtras();
super.onCreate(savedInstanceState, R.layout.trackdetail, extras.getLong(Schema.COL_TRACK_ID));

trackStatistics = new TrackStatistics(trackId, getContentResolver(), extras);

lv = (ListView) findViewById(R.id.trackdetail_list);

Expand All @@ -98,19 +125,29 @@ public void onClick(View v) {
// Do not show soft keyboard by default
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);

// Create content observer for trackpoints
trackpointContentObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
pathChanged();
}
};
getContentResolver().registerContentObserver(
TrackContentProvider.trackPointsUri(trackId),
true, trackpointContentObserver);
// further work is done in onResume.
}

@Override
protected void onResume() {
super.onResume();
/**
* Update trackData (doesn't update the list view though)
*/
private void updateTrack() {
// Query the track values
ContentResolver cr = getContentResolver();
Cursor cursor = cr.query(
ContentUris.withAppendedId(TrackContentProvider.CONTENT_URI_TRACK, trackId),
null, null, null, null);

if (! cursor.moveToFirst()) {
// This shouldn't occur, it's here just in case.
// So, don't make each language translate/localize it.
Expand All @@ -119,54 +156,69 @@ protected void onResume() {
finish();
return; // <--- Early return ---
}

trackData.clear();

// Bind WP count, TP count, start date, etc.
// Fill name-field only if empty (in case changed by user/restored by onRestoreInstanceState)
Track t = Track.build(trackId, cursor, cr, true);
trackStatistics.update();

bindTrack(t);

String from[] = new String[]{ITEM_KEY, ITEM_VALUE};
int[] to = new int[] {R.id.trackdetail_item_key, R.id.trackdetail_item_value};

// Waypoint count
final int wpCount = t.getWpCount();
trackHasWaypoints = (wpCount > 0);
List<HashMap<String, String>> data = new ArrayList<HashMap<String, String>>();

HashMap<String, String> map = new HashMap<String, String>();
map.put(ITEM_KEY, getResources().getString(R.string.trackmgr_waypoints_count));
map.put(ITEM_VALUE, Integer.toString(wpCount));
data.add(WP_COUNT_INDEX, map);
trackData.add(WP_COUNT_INDEX, map);

// Trackpoint count
map = new HashMap<String, String>();
map.put(ITEM_KEY, getResources().getString(R.string.trackmgr_trackpoints_count));
map.put(ITEM_VALUE, Integer.toString(t.getTpCount()));
data.add(map);
trackData.add(map);

// Distance
map = new HashMap<String, String>();
map.put(ITEM_KEY, getResources().getString(R.string.trackmgr_distance));
map.put(ITEM_VALUE, TracklistAdapter.distanceToString(trackStatistics.totalLength(), getResources()));
trackData.add(map);

// Speed
map = new HashMap<String, String>();
map.put(ITEM_KEY, getResources().getString(R.string.trackdetail_speed));
map.put(ITEM_VALUE, TracklistAdapter.speedToString(trackStatistics.averageSpeed(), getResources()) + " " +
getResources().getString(R.string.trackdetail_speed_average) + ", " +
TracklistAdapter.speedToString(trackStatistics.maximumSpeed(), getResources()) + " " +
getResources().getString(R.string.trackdetail_speed_max));
trackData.add(map);

// Start date
map = new HashMap<String, String>();
map.put(ITEM_KEY, getResources().getString(R.string.trackdetail_startdate));
map.put(ITEM_VALUE, t.getStartDateAsString());
data.add(map);
trackData.add(map);

// End date
map = new HashMap<String, String>();
map.put(ITEM_KEY, getResources().getString(R.string.trackdetail_enddate));
map.put(ITEM_VALUE, t.getEndDateAsString());
data.add(map);
trackData.add(map);

// Start point
map = new HashMap<String, String>();
map.put(ITEM_KEY, getResources().getString(R.string.trackdetail_startloc));
map.put(ITEM_VALUE, MercatorProjection.formatDegreesAsDMS(t.getStartLat(), true) + " " + MercatorProjection.formatDegreesAsDMS(t.getStartLong(), false));
data.add(map);
trackData.add(map);

// End point
map = new HashMap<String, String>();
map.put(ITEM_KEY, getResources().getString(R.string.trackdetail_endloc));
map.put(ITEM_VALUE, MercatorProjection.formatDegreesAsDMS(t.getEndLat(), true) + " " + MercatorProjection.formatDegreesAsDMS(t.getEndLong(), false));
data.add(map);
trackData.add(map);

// OSM Upload date
map = new HashMap<String, String>();
Expand All @@ -176,7 +228,7 @@ protected void onResume() {
} else {
map.put(ITEM_VALUE, DateFormat.getDateTimeInstance().format(new Date(cursor.getLong(cursor.getColumnIndex(Schema.COL_EXPORT_DATE)))));
}
data.add(map);
trackData.add(map);

// Exported date. Should be the last item in order to be refreshed
// if the user exports the track
Expand All @@ -187,11 +239,20 @@ protected void onResume() {
} else {
map.put(ITEM_VALUE, (DateFormat.getDateTimeInstance().format(new Date(cursor.getLong(cursor.getColumnIndex(Schema.COL_EXPORT_DATE))))));
}
data.add(map);
trackData.add(map);

cursor.close();

TrackDetailSimpleAdapter adapter = new TrackDetailSimpleAdapter(data, from, to);
}

@Override
protected void onResume() {
super.onResume();

updateTrack();

String from[] = new String[]{ITEM_KEY, ITEM_VALUE};
int[] to = new int[] {R.id.trackdetail_item_key, R.id.trackdetail_item_value};
adapter = new TrackDetailSimpleAdapter(trackData, from, to);
lv.setAdapter(adapter);

// Click on Waypoint count to see the track's WaypointList
Expand Down Expand Up @@ -262,6 +323,16 @@ public void onItemClick(AdapterView<?> parent, View view, final int position, fi
startActivity(i);
}

/**
* On track path changed, update track info
*/
private void pathChanged() {
updateTrack();
// Update the list view
if (adapter != null)
adapter.notifyDataSetChanged();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice notifyDatasetChanged()!

}

/**
* Extend SimpleAdapter so we can underline the clickable Waypoint count.
* Always uses <tt>R.layout.trackdetail_item</tt> as its list item resource.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

import java.io.File;
import java.util.Date;
import java.util.TreeMap;

import me.guillaumin.android.osmtracker.OSMTracker;
import me.guillaumin.android.osmtracker.R;
import me.guillaumin.android.osmtracker.db.DataHelper;
import me.guillaumin.android.osmtracker.db.TrackContentProvider;
import me.guillaumin.android.osmtracker.db.TrackContentProvider.Schema;
import me.guillaumin.android.osmtracker.db.TracklistAdapter;
import me.guillaumin.android.osmtracker.db.model.TrackStatisticsCollection;
import me.guillaumin.android.osmtracker.exception.CreateTrackException;
import me.guillaumin.android.osmtracker.gpx.ExportToStorageTask;
import me.guillaumin.android.osmtracker.util.FileSystemUtils;
Expand Down Expand Up @@ -57,6 +59,9 @@ public class TrackManager extends ListActivity {

/** The previous item visible, or -1; for scrolling back to its position in {@link #onResume()} */
private int prevItemVisible = -1;

/** Statistics for all existing tracks */
private TrackStatisticsCollection tracksStatistics;

@Override
protected void onCreate(Bundle savedInstanceState) {
Expand All @@ -67,6 +72,7 @@ protected void onCreate(Bundle savedInstanceState) {
if (savedInstanceState != null) {
prevItemVisible = savedInstanceState.getInt(PREV_VISIBLE, -1);
}
tracksStatistics = new TrackStatisticsCollection(getContentResolver());
}

@Override
Expand All @@ -75,7 +81,7 @@ protected void onResume() {
TrackContentProvider.CONTENT_URI_TRACK, null, null, null,
Schema.COL_START_DATE + " desc");
startManagingCursor(cursor);
setListAdapter(new TracklistAdapter(TrackManager.this, cursor));
setListAdapter(new TracklistAdapter(TrackManager.this, cursor, tracksStatistics));
getListView().setEmptyView(findViewById(R.id.trackmgr_empty)); // undo change from onPause

// Is any track active?
Expand Down Expand Up @@ -351,6 +357,7 @@ public void onClick(DialogInterface dialog, int which) {
case R.id.trackmgr_contextmenu_details:
i = new Intent(this, TrackDetail.class);
i.putExtra(Schema.COL_TRACK_ID, info.id);
i.putExtras(tracksStatistics.get(info.id).getData());
startActivity(i);
break;
}
Expand All @@ -376,6 +383,7 @@ protected void onListItemClick(ListView lv, View iv, final int position, final l
// show track info
i = new Intent(this, TrackDetail.class);
i.putExtra(Schema.COL_TRACK_ID, id);
i.putExtras(tracksStatistics.get(id).getData());
}
startActivity(i);
}
Expand Down Expand Up @@ -417,6 +425,9 @@ private void deleteTrack(long id) {
if (trackStorageDirectory.exists()) {
FileSystemUtils.delete(trackStorageDirectory, true);
}

// Delete the statistics
tracksStatistics.remove(id);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
import me.guillaumin.android.osmtracker.R;
import me.guillaumin.android.osmtracker.db.TrackContentProvider.Schema;
import me.guillaumin.android.osmtracker.db.model.Track;
import me.guillaumin.android.osmtracker.db.model.TrackStatistics;
import me.guillaumin.android.osmtracker.db.model.TrackStatisticsCollection;
import me.guillaumin.android.osmtracker.activity.TrackManager;

import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
import android.view.LayoutInflater;
import android.view.View;
Expand All @@ -20,9 +25,51 @@
*
*/
public class TracklistAdapter extends CursorAdapter {

private static final float STOP_SHOWING_DECIMALS_AFTER = 20;

private TrackStatisticsCollection tracksStatistics;

public static String distanceToString(float distance, Resources resources) {
if (distance < 100) {
// Exact meters
return resources.getString(R.string.trackmgr_distance_in_meters).replace("{0}",
String.valueOf(Math.round(distance)));
} else if (distance < 1000) {
// Round to 10 meters
return resources.getString(R.string.trackmgr_distance_in_meters).replace("{0}",
String.valueOf(10 * Math.round(distance / 10)));
} else if (distance < STOP_SHOWING_DECIMALS_AFTER*1000) {
// Kilometers with 1 decimal
return resources.getString(R.string.trackmgr_distance_in_kilometers).replace("{0}",
String.format("%.1f", distance / 1000));
} else {
// Whole kilometers
return resources.getString(R.string.trackmgr_distance_in_kilometers).replace("{0}",
String.valueOf(Math.round(distance / 1000)));
}
}

public TracklistAdapter(Context context, Cursor c) {
public static String speedToString(float speed, Resources resources) {
float kmph = (float)3.6*speed; // convert meters per second to kilometers per hour
if (kmph < 1) {
// Round to one non-zero digit
return resources.getString(R.string.trackmgr_speed_in_kmph).replace("{0}",
String.format("%.1g", kmph));
} else if (kmph < STOP_SHOWING_DECIMALS_AFTER) {
// Round to one decimal
return resources.getString(R.string.trackmgr_speed_in_kmph).replace("{0}",
String.format("%.1f", kmph));
} else {
// Round to integer
return resources.getString(R.string.trackmgr_speed_in_kmph).replace("{0}",
String.valueOf(Math.round(kmph)));
}
}

public TracklistAdapter(Context context, Cursor c, TrackStatisticsCollection statistics) {
super(context, c);
tracksStatistics = statistics;
}

@Override
Expand All @@ -36,7 +83,7 @@ public View newView(Context context, Cursor cursor, ViewGroup vg) {
vg, false);
return view;
}

/**
* Do the binding between data and item view.
*
Expand All @@ -53,6 +100,8 @@ private View bind(Cursor cursor, View v, Context context) {
TextView vNameOrStartDate = (TextView) v.findViewById(R.id.trackmgr_item_nameordate);
TextView vWps = (TextView) v.findViewById(R.id.trackmgr_item_wps);
TextView vTps = (TextView) v.findViewById(R.id.trackmgr_item_tps);
TextView vDistance = (TextView) v.findViewById(R.id.trackmgr_item_distance);
TextView vSpeed = (TextView) v.findViewById(R.id.trackmgr_item_speed);
ImageView vStatus = (ImageView) v.findViewById(R.id.trackmgr_item_statusicon);
ImageView vUploadStatus = (ImageView) v.findViewById(R.id.trackmgr_item_upload_statusicon);

Expand Down Expand Up @@ -83,8 +132,11 @@ private View bind(Cursor cursor, View v, Context context) {

// Bind WP count, TP count, name
Track t = Track.build(trackId, cursor, context.getContentResolver(), false);
TrackStatistics stat = tracksStatistics.get(trackId);
vTps.setText(Integer.toString(t.getTpCount()));
vWps.setText(Integer.toString(t.getWpCount()));
vDistance.setText(distanceToString(stat.totalLength(), context.getResources()));
vSpeed.setText(speedToString(stat.averageSpeed(), context.getResources()));
vNameOrStartDate.setText(t.getName());

return v;
Expand Down
Loading