Merge chart changes.

This commit is contained in:
Sandor Dornbush
2011-02-08 07:28:23 -08:00
2 changed files with 98 additions and 99 deletions
@@ -60,6 +60,7 @@ import java.util.ArrayList;
public class ChartActivity extends Activity implements public class ChartActivity extends Activity implements
SharedPreferences.OnSharedPreferenceChangeListener { SharedPreferences.OnSharedPreferenceChangeListener {
private final static int BUFFER_SIZE = 1024;
private double profileLength = 0; private double profileLength = 0;
private boolean metricUnits = true; private boolean metricUnits = true;
@@ -101,7 +102,7 @@ public class ChartActivity extends Activity implements
/* /*
* UI elements: * UI elements:
*/ */
private ChartView cv; private ChartView chartView;
private MenuItem chartSettingsMenuItem; private MenuItem chartSettingsMenuItem;
private LinearLayout busyPane; private LinearLayout busyPane;
private ZoomControls zoomControls; private ZoomControls zoomControls;
@@ -118,10 +119,10 @@ public class ChartActivity extends Activity implements
@Override @Override
public void run() { public void run() {
busyPane.setVisibility(View.GONE); busyPane.setVisibility(View.GONE);
zoomControls.setIsZoomInEnabled(cv.canZoomIn()); zoomControls.setIsZoomInEnabled(chartView.canZoomIn());
zoomControls.setIsZoomOutEnabled(cv.canZoomOut()); zoomControls.setIsZoomOutEnabled(chartView.canZoomOut());
cv.setShowPointer(selectedTrackIsRecording()); chartView.setShowPointer(selectedTrackIsRecording());
cv.invalidate(); chartView.invalidate();
} }
}; };
@@ -158,54 +159,12 @@ public class ChartActivity extends Activity implements
private Handler updateTrackHandler; private Handler updateTrackHandler;
/** /**
* A runnable that update the profile from the provider. * A runnable that updates the profile from the provider.
*/ */
private final Runnable updateTrackRunnable = new Runnable() { private final Runnable updateTrackRunnable = new Runnable() {
@Override @Override
public void run() { public void run() {
Log.d(MyTracksConstants.TAG, "MyTracks: Updating chart."); readNewTrackPoints();
Track track = providerUtils.getTrack(recordingTrackId);
if (track == null) {
Log.w(MyTracksConstants.TAG, "MyTracks: track not found");
return;
}
Cursor cursor = null;
try {
cursor = providerUtils.getLocationsCursor(recordingTrackId,
lastSeenLocationId + 1,
MyTracksConstants.MAX_DISPLAYED_TRACK_POINTS - cv.getData().size(),
true);
if (cursor != null) {
if (cursor.moveToLast()) {
final int idColumnIdx =
cursor.getColumnIndexOrThrow(TrackPointsColumns._ID);
ArrayList<double[]> data = new ArrayList<double[]>();
// Need two locations so we can keep track of the last location.
Location location = new MyTracksLocation("");
do {
lastSeenLocationId = cursor.getLong(idColumnIdx);
providerUtils.fillLocation(cursor, location);
if (MyTracksUtils.isValidLocation(location)) {
double[] point = new double[6];
location = getDataPoint(location, track, point);
data.add(point);
}
} while (cursor.moveToPrevious());
cv.addDataPoints(data);
}
}
} catch (RuntimeException e) {
Log.w(MyTracksConstants.TAG, "Caught an unexpected exception.", e);
} finally {
if (cursor != null) {
cursor.close();
}
uiHandler.post(new Runnable() {
public void run() {
cv.invalidate();
}
});
}
} }
}; };
@@ -222,13 +181,13 @@ public class ChartActivity extends Activity implements
metricUnits = metricUnits =
sharedPreferences.getBoolean(getString(R.string.metric_units_key), sharedPreferences.getBoolean(getString(R.string.metric_units_key),
true); true);
cv.setMetricUnits(metricUnits); chartView.setMetricUnits(metricUnits);
readProfileAsync(); readProfileAsync();
} else if (key.equals(getString(R.string.report_speed_key))) { } else if (key.equals(getString(R.string.report_speed_key))) {
reportSpeed = reportSpeed =
sharedPreferences.getBoolean(getString(R.string.report_speed_key), sharedPreferences.getBoolean(getString(R.string.report_speed_key),
true); true);
cv.setReportSpeed(reportSpeed, this); chartView.setReportSpeed(reportSpeed, this);
readProfileAsync(); readProfileAsync();
} else if (key.equals(getString(R.string.recording_track_key))) { } else if (key.equals(getString(R.string.recording_track_key))) {
recordingTrackId = recordingTrackId =
@@ -254,11 +213,11 @@ public class ChartActivity extends Activity implements
requestWindowFeature(Window.FEATURE_NO_TITLE); requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.mytracks_charts); setContentView(R.layout.mytracks_charts);
ViewGroup layout = (ViewGroup) findViewById(R.id.elevation_chart); ViewGroup layout = (ViewGroup) findViewById(R.id.elevation_chart);
cv = new ChartView(this); chartView = new ChartView(this);
cv.setMode(this.mode); chartView.setMode(this.mode);
LayoutParams params = LayoutParams params =
new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
layout.addView(cv, params); layout.addView(chartView, params);
SharedPreferences preferences = SharedPreferences preferences =
getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0); getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
@@ -269,10 +228,10 @@ public class ChartActivity extends Activity implements
preferences.getLong(getString(R.string.recording_track_key), -1); preferences.getLong(getString(R.string.recording_track_key), -1);
metricUnits = preferences.getBoolean(getString(R.string.metric_units_key), metricUnits = preferences.getBoolean(getString(R.string.metric_units_key),
true); true);
cv.setMetricUnits(metricUnits); chartView.setMetricUnits(metricUnits);
reportSpeed = preferences.getBoolean(getString(R.string.report_speed_key), reportSpeed = preferences.getBoolean(getString(R.string.report_speed_key),
true); true);
cv.setReportSpeed(reportSpeed, this); chartView.setReportSpeed(reportSpeed, this);
preferences.registerOnSharedPreferenceChangeListener(this); preferences.registerOnSharedPreferenceChangeListener(this);
} }
@@ -332,7 +291,7 @@ public class ChartActivity extends Activity implements
ChartActivity.this.runOnUiThread(new Runnable() { ChartActivity.this.runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
cv.invalidate(); chartView.invalidate();
} }
}); });
} }
@@ -384,21 +343,21 @@ public class ChartActivity extends Activity implements
} }
private void zoomIn() { private void zoomIn() {
cv.zoomIn(); chartView.zoomIn();
zoomControls.setIsZoomInEnabled(cv.canZoomIn()); zoomControls.setIsZoomInEnabled(chartView.canZoomIn());
zoomControls.setIsZoomOutEnabled(cv.canZoomOut()); zoomControls.setIsZoomOutEnabled(chartView.canZoomOut());
} }
private void zoomOut() { private void zoomOut() {
cv.zoomOut(); chartView.zoomOut();
zoomControls.setIsZoomInEnabled(cv.canZoomIn()); zoomControls.setIsZoomInEnabled(chartView.canZoomIn());
zoomControls.setIsZoomOutEnabled(cv.canZoomOut()); zoomControls.setIsZoomOutEnabled(chartView.canZoomOut());
} }
public void setMode(Mode newMode) { public void setMode(Mode newMode) {
if (this.mode != newMode) { if (this.mode != newMode) {
this.mode = newMode; this.mode = newMode;
cv.setMode(this.mode); chartView.setMode(this.mode);
readProfileAsync(); readProfileAsync();
} }
} }
@@ -408,12 +367,12 @@ public class ChartActivity extends Activity implements
} }
public void setSeriesEnabled(int index, boolean enabled) { public void setSeriesEnabled(int index, boolean enabled) {
cv.getChartValueSeries(index).setEnabled(enabled); chartView.getChartValueSeries(index).setEnabled(enabled);
runOnUiThread(updateChart); runOnUiThread(updateChart);
} }
public boolean isSeriesEnabled(int index) { public boolean isSeriesEnabled(int index) {
return cv.getChartValueSeries(index).isEnabled(); return chartView.getChartValueSeries(index).isEnabled();
} }
private void readWaypoints() { private void readWaypoints() {
@@ -421,7 +380,7 @@ public class ChartActivity extends Activity implements
return; return;
} }
Cursor cursor = null; Cursor cursor = null;
cv.clearWaypoints(); chartView.clearWaypoints();
try { try {
// We will silently drop extra waypoints to make the app responsive. // We will silently drop extra waypoints to make the app responsive.
cursor = cursor =
@@ -431,7 +390,7 @@ public class ChartActivity extends Activity implements
if (cursor.moveToFirst()) { if (cursor.moveToFirst()) {
do { do {
Waypoint wpt = providerUtils.createWaypoint(cursor); Waypoint wpt = providerUtils.createWaypoint(cursor);
cv.addWaypoint(wpt); chartView.addWaypoint(wpt);
} while (cursor.moveToNext()); } while (cursor.moveToNext());
} }
} }
@@ -575,7 +534,7 @@ public class ChartActivity extends Activity implements
* Sets the chart data points reading from the provider. This is non-blocking. * Sets the chart data points reading from the provider. This is non-blocking.
*/ */
private void readProfileAsync() { private void readProfileAsync() {
cv.reset(); chartView.reset();
updateTrackHandler.post(new Runnable() { updateTrackHandler.post(new Runnable() {
public void run() { public void run() {
runOnUiThread(showSpinner); runOnUiThread(showSpinner);
@@ -594,7 +553,6 @@ public class ChartActivity extends Activity implements
profileLength = 0; profileLength = 0;
lastLocation = null; lastLocation = null;
startTime = -1; startTime = -1;
Cursor cursor = null;
if (selectedTrackId < 0) { if (selectedTrackId < 0) {
return; return;
} }
@@ -602,26 +560,70 @@ public class ChartActivity extends Activity implements
if (track == null) { if (track == null) {
return; return;
} }
long lastLocationRead = track.getStartId(); lastSeenLocationId = track.getStartId();
long totalLocations = track.getStopId() - track.getStartId(); final ArrayList<double[]> theData = readPointsToList(track);
runOnUiThread(new Runnable() {
public void run() {
chartView.setDataPoints(theData);
}
});
}
// Limit the number of chart readings. Ideally we would want around 1024. /**
int chartSamplingFrequency = Math.max(1, (int) (totalLocations / 1024.0)); * Read all new track points.
int bufferSize = 1024; */
private void readNewTrackPoints() {
Log.i(MyTracksConstants.TAG, "MyTracks: Updating chart last seen: " + lastSeenLocationId);
Track track = providerUtils.getTrack(recordingTrackId);
if (track == null) {
Log.w(MyTracksConstants.TAG, "MyTracks: track not found");
return;
}
chartView.addDataPoints(readPointsToList(track));
uiHandler.post(new Runnable() {
public void run() {
chartView.invalidate();
}
});
Log.i(MyTracksConstants.TAG, "MyTracks: Updated chart last seen: " + lastSeenLocationId);
}
/**
* Get the frequency at which points should be displayed.
* Limit the number of chart readings. Ideally we would want around 1024.
* @param track The track which will be displayed.
* @return The inverse of the frequency of points to be displayed.
*/
private int getSamplingFrequency(Track track) {
long totalLocations = track.getStopId() - track.getStartId();
return Math.max(1, (int) (totalLocations / 1024.0));
}
private Cursor getLocationsCursor(long lastLocationRead) {
return providerUtils.getLocationsCursor(selectedTrackId, lastLocationRead, BUFFER_SIZE, false);
}
/**
* Read all of the points to a list.
* @param track The track which will be displayed.
* @return
*/
private ArrayList<double[]> readPointsToList(Track track) {
Cursor cursor = null;
long lastLocationRead = lastSeenLocationId;
int points = 0;
int chartSamplingFrequency = getSamplingFrequency(track);
ArrayList<double[]> result = new ArrayList<double[]>();
// Need two locations so we can keep track of the last location.
Location location = new MyTracksLocation("");
try { try {
final ArrayList<double[]> theData = new ArrayList<double[]>(); while (lastSeenLocationId < track.getStopId()) {
int points = 0; cursor = getLocationsCursor(lastLocationRead);
// Need two locations so we can keep track of the last location.
Location location = new MyTracksLocation("");
while (lastLocationRead < track.getStopId()) {
cursor = providerUtils.getLocationsCursor(
selectedTrackId, lastLocationRead, bufferSize, false);
if (cursor != null) { if (cursor != null) {
elevationBuffer.reset(); elevationBuffer.reset();
speedBuffer.reset(); speedBuffer.reset();
if (cursor.moveToFirst()) { if (cursor.moveToFirst()) {
final int idColumnIdx = final int idColumnIdx = cursor.getColumnIndexOrThrow(TrackPointsColumns._ID);
cursor.getColumnIndexOrThrow(TrackPointsColumns._ID);
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
points++; points++;
providerUtils.fillLocation(cursor, location); providerUtils.fillLocation(cursor, location);
@@ -632,26 +634,20 @@ public class ChartActivity extends Activity implements
double[] point = new double[6]; double[] point = new double[6];
location = getDataPoint(location, track, point); location = getDataPoint(location, track, point);
if (points % chartSamplingFrequency == 0) { if (points % chartSamplingFrequency == 0) {
theData.add(point); result.add(point);
} }
} }
} }
} else { } else {
lastLocationRead += bufferSize; lastLocationRead += BUFFER_SIZE;
} }
} else { } else {
lastLocationRead += bufferSize; lastLocationRead += BUFFER_SIZE;
} }
cursor.close(); cursor.close();
cursor = null; cursor = null;
} }
runOnUiThread(new Runnable() { return result;
public void run() {
cv.setDataPoints(theData);
}
});
} catch (RuntimeException e) {
Log.w(MyTracksConstants.TAG, "Caught unexpected exception.", e);
} finally { } finally {
if (cursor != null) { if (cursor != null) {
cursor.close(); cursor.close();
@@ -216,7 +216,7 @@ public class ChartView extends View {
R.color.speed_fill, R.color.speed_fill,
R.color.speed_border, R.color.speed_border,
new ZoomSettings(MAX_INTERVALS, 0, Integer.MIN_VALUE, new ZoomSettings(MAX_INTERVALS, 0, Integer.MIN_VALUE,
new int[] {5, 10, 20, 50}), new int[] {1, 5, 10, 20, 50}),
R.string.speed); R.string.speed);
series[POWER_SERIES] = series[POWER_SERIES] =
new ChartValueSeries(context, new ChartValueSeries(context,
@@ -610,7 +610,8 @@ public class ChartView extends View {
c.translate(getScrollX(), 0); c.translate(getScrollX(), 0);
drawYAxis(c); drawYAxis(c);
final int spacer = 5; float density = getContext().getResources().getDisplayMetrics().density;
final int spacer = (int) (5 * density);
int x = leftBorder - spacer; int x = leftBorder - spacer;
for (ChartValueSeries cvs : series) { for (ChartValueSeries cvs : series) {
if (cvs.isEnabled() && cvs.hasData()) { if (cvs.isEnabled() && cvs.hasData()) {
@@ -726,9 +727,10 @@ public class ChartView extends View {
maxLength += cvs.getMaxLabelLength(); maxLength += cvs.getMaxLabelLength();
} }
} }
leftBorder = 4 + 8 * maxLength;
effectiveWidth = w - leftBorder - RIGHT_BORDER;
float density = getContext().getResources().getDisplayMetrics().density; float density = getContext().getResources().getDisplayMetrics().density;
// TODO: This should be function of the number of variables displayed.
leftBorder = (int) (density * (4 + 8 * maxLength));
effectiveWidth = w - leftBorder - RIGHT_BORDER;
bottomBorder = (int) (density * BOTTOM_BORDER); bottomBorder = (int) (density * BOTTOM_BORDER);
topBorder = (int) (density * TOP_BORDER); topBorder = (int) (density * TOP_BORDER);
effectiveHeight = h - topBorder - bottomBorder; effectiveHeight = h - topBorder - bottomBorder;
@@ -739,8 +741,9 @@ public class ChartView extends View {
} }
private int getY(ChartValueSeries cvs, double y) { private int getY(ChartValueSeries cvs, double y) {
int effectiveSpread = cvs.getInterval() * MAX_INTERVALS;
return topBorder + effectiveHeight return topBorder + effectiveHeight
- (int) ((y - cvs.getMin()) * effectiveHeight / cvs.getSpread()); - (int) ((y - cvs.getMin()) * effectiveHeight / effectiveSpread);
} }
private void drawXLabels(Canvas c) { private void drawXLabels(Canvas c) {