Merge to head (non-trivial merge)

This commit is contained in:
Rodrigo Damazio
2011-06-14 20:42:57 -03:00
17 changed files with 227 additions and 99 deletions
@@ -145,6 +145,7 @@ public class ChartActivity extends Activity implements TrackDataListener {
dataHub = TrackDataHub.getStartedInstance();
dataHub.registerTrackDataListener(this, EnumSet.of(
ListenerDataType.SELECTED_TRACK_CHANGED,
ListenerDataType.TRACK_UPDATES,
ListenerDataType.POINT_UPDATES,
ListenerDataType.SAMPLED_OUT_POINT_UPDATES,
ListenerDataType.WAYPOINT_UPDATES,
@@ -1,12 +1,12 @@
/*
* Copyright 2009 Google Inc.
*
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -133,7 +133,7 @@ public class ExportAllTracks {
Log.i(Constants.TAG, "ExportAllTracks: Releasing wake lock.");
}
Log.i(Constants.TAG, "ExportAllTracks: Done");
Toast.makeText(activity, R.string.export_done, Toast.LENGTH_SHORT).show();
showToast(R.string.export_done, Toast.LENGTH_SHORT);
}
private void makeProgressDialog(final int trackCount) {
@@ -189,12 +189,16 @@ public class ExportAllTracks {
Log.i(Constants.TAG, "ExportAllTracks: exporting: " + id);
TrackWriter writer =
TrackWriterFactory.newWriter(activity, providerUtils, id, format);
if (writer == null) {
showToast(R.string.error_track_does_not_exist, Toast.LENGTH_LONG);
return;
}
writer.writeTrack();
if (!writer.wasSuccess()) {
// Abort the whole export on the first error.
int error = writer.getErrorMessage();
Toast.makeText(activity, error, Toast.LENGTH_LONG).show();
showToast(writer.getErrorMessage(), Toast.LENGTH_LONG);
return;
}
}
@@ -210,4 +214,13 @@ public class ExportAllTracks {
}
}
}
private void showToast(final int messageId, final int length) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(activity, messageId, length).show();
}
});
}
}
@@ -53,6 +53,7 @@ import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import java.util.EnumSet;
@@ -480,21 +481,21 @@ public class MapActivity extends com.google.android.maps.MapActivity
@Override
public void onProviderStateChange(ProviderState state) {
final int messageId;
final boolean bindClick;
final boolean isGpsDisabled;
switch (state) {
case DISABLED:
messageId = R.string.status_enable_gps;
bindClick = true;
isGpsDisabled = true;
break;
case NO_FIX:
case BAD_FIX:
messageId = R.string.wait_for_fix;
bindClick = false;
isGpsDisabled = false;
break;
case GOOD_FIX:
// Nothing to show.
messageId = -1;
bindClick = false;
isGpsDisabled = false;
break;
default:
throw new IllegalArgumentException("Unexpected state: " + state);
@@ -507,7 +508,13 @@ public class MapActivity extends com.google.android.maps.MapActivity
messageText.setText(messageId);
messagePane.setVisibility(View.VISIBLE);
if (bindClick) {
if (isGpsDisabled) {
// Give a warning about this state.
Toast.makeText(MapActivity.this,
R.string.error_no_gps_location_provider,
Toast.LENGTH_LONG).show();
// Make clicking take the user to the location settings.
messagePane.setOnClickListener(MapActivity.this);
} else {
messagePane.setOnClickListener(null);
@@ -17,6 +17,7 @@ package com.google.android.apps.mytracks;
import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.analytics.GoogleAnalyticsTracker;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.TrackDataHub;
import com.google.android.apps.mytracks.content.TracksColumns;
@@ -79,6 +80,11 @@ public class MyTracks extends TabActivity implements OnTouchListener {
*/
private MyTracksProviderUtils providerUtils;
/**
* Google Analytics tracker
*/
private GoogleAnalyticsTracker tracker;
private TrackRecordingServiceBinder serviceBinder;
/*
@@ -125,6 +131,13 @@ public class MyTracks extends TabActivity implements OnTouchListener {
apiFeatures.getApiPlatformAdapter().enableStrictMode();
}
tracker = GoogleAnalyticsTracker.getInstance();
// Start the tracker in manual dispatch mode...
tracker.start(getString(R.string.google_analytics_id), getApplicationContext());
tracker.setProductVersion("android-mytracks", SystemUtils.getMyTracksVersion(this));
tracker.trackPageView("/appstart");
tracker.dispatch();
providerUtils = MyTracksProviderUtils.Factory.get(this);
preferences = getSharedPreferences(Constants.SETTINGS_NAME, 0);
dataHub = TrackDataHub.newInstance(this);
@@ -179,6 +192,9 @@ public class MyTracks extends TabActivity implements OnTouchListener {
dataHub.stop();
tracker.dispatch();
tracker.stop();
// Clean up any temporary track files.
TempFileCleaner.clean();
super.onStop();
@@ -464,10 +480,10 @@ public class MyTracks extends TabActivity implements OnTouchListener {
startActivity(intent);
}
}
serviceBinder.stopService();
}
void clearSelectedTrack() {
dataHub.unloadCurrentTrack();
}
@@ -1,12 +1,12 @@
/*
* Copyright 2009 Google Inc.
*
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -76,15 +76,18 @@ public class WaypointsList extends ListActivity
contextPosition = info.position;
waypointId = WaypointsList.this.listView.getAdapter()
.getItemId(contextPosition);
int type = providerUtils.getWaypoint(info.id).getType();
menu.add(0, Constants.MENU_SHOW, 0,
R.string.waypointslist_show_waypoint);
menu.add(0, Constants.MENU_EDIT, 0,
R.string.waypointslist_edit_waypoint);
menu.add(0, Constants.MENU_DELETE, 0,
R.string.waypointslist_delete_waypoint).setEnabled(
recordingTrackId < 0 || type == Waypoint.TYPE_WAYPOINT ||
info.id != providerUtils.getLastWaypointId(recordingTrackId));
Waypoint waypoint = providerUtils.getWaypoint(info.id);
if (waypoint != null) {
int type = waypoint.getType();
menu.add(0, Constants.MENU_SHOW, 0,
R.string.waypointslist_show_waypoint);
menu.add(0, Constants.MENU_EDIT, 0,
R.string.waypointslist_edit_waypoint);
menu.add(0, Constants.MENU_DELETE, 0,
R.string.waypointslist_delete_waypoint).setEnabled(
recordingTrackId < 0 || type == Waypoint.TYPE_WAYPOINT ||
info.id != providerUtils.getLastWaypointId(recordingTrackId));
}
}
};
@@ -162,7 +165,7 @@ public class WaypointsList extends ListActivity
trackId = -1;
}
final long firstWaypointId = providerUtils.getFirstWaypointId(trackId);
final long firstWaypointId = providerUtils.getFirstWaypointId(trackId);
waypointsCursor = getContentResolver().query(
WaypointsColumns.CONTENT_URI, null,
WaypointsColumns.TRACKID + "=" + trackId + " AND "
@@ -226,7 +229,7 @@ public class WaypointsList extends ListActivity
new String[] { WaypointsColumns.NAME, WaypointsColumns.TIME,
WaypointsColumns.CATEGORY, WaypointsColumns.TYPE },
new int[] { R.id.waypointslist_item_name,
R.id.waypointslist_item_time,
R.id.waypointslist_item_time,
R.id.waypointslist_item_category,
R.id.waypointslist_item_icon });
@@ -236,7 +239,7 @@ public class WaypointsList extends ListActivity
waypointsCursor.getColumnIndexOrThrow(WaypointsColumns.TYPE);
adapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
@Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
if (columnIndex == timeIdx) {
long time = cursor.getLong(timeIdx);
TextView textView = (TextView) view;
@@ -41,7 +41,6 @@ import android.location.LocationManager;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.widget.Toast;
import java.util.Collections;
import java.util.EnumSet;
@@ -684,9 +683,6 @@ public class TrackDataHub {
final TrackDataListener.ProviderState state;
if (!hasProviderEnabled) {
state = ProviderState.DISABLED;
// Give a global warning about this state.
Toast.makeText(context, R.string.error_no_gps_location_provider, Toast.LENGTH_LONG).show();
} else if (!hasFix) {
state = ProviderState.NO_FIX;
} else if (!hasGoodFix) {
@@ -824,10 +820,6 @@ public class TrackDataHub {
public void run() {
for (TrackDataListener listener : listeners) {
listener.onSelectedTrackChanged(track, isRecordingSelected());
if (track != null) {
listener.onTrackUpdated(track);
}
}
}
});
@@ -180,6 +180,11 @@ public class SendToFusionTables implements Runnable {
// Get the track meta-data
Track track = providerUtils.getTrack(trackId);
if (track == null) {
Log.w(Constants.TAG, "Cannot get track.");
return;
}
String originalDescription = track.getDescription();
// Create a new table:
@@ -208,7 +213,6 @@ public class SendToFusionTables implements Runnable {
Log.d(Constants.TAG, "SendToFusionTables: Done: " + success);
progressIndicator.setProgressValue(PROGRESS_COMPLETE);
} finally {
final boolean finalSuccess = success;
final int finalStatusMessageId = statusMessageId;
context.runOnUiThread(new Runnable() {
@@ -305,7 +309,7 @@ public class SendToFusionTables implements Runnable {
Cursor locationsCursor = providerUtils.getLocationsCursor(track.getId(), 0, -1, false);
try {
if (!locationsCursor.moveToFirst()) {
if (locationsCursor == null || !locationsCursor.moveToFirst()) {
Log.w(Constants.TAG, "Unable to get any points to upload");
return false;
}
@@ -392,7 +396,9 @@ public class SendToFusionTables implements Runnable {
return true;
} finally {
locationsCursor.close();
if (locationsCursor != null) {
locationsCursor.close();
}
}
}
@@ -1,12 +1,12 @@
/*
* Copyright 2008 Google Inc.
*
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -76,7 +76,7 @@ public class SendToMyMaps implements Runnable {
public interface OnSendCompletedListener {
void onSendCompleted(String mapId, boolean success, int statusMessage);
}
public SendToMyMaps(Activity context, String mapId, AuthManager auth,
long trackId, ProgressIndicator progressIndicator,
OnSendCompletedListener onCompletion) {
@@ -103,15 +103,20 @@ public class SendToMyMaps implements Runnable {
progressIndicator.setProgressValue(1);
progressIndicator.setProgressMessage(
R.string.progress_message_reading_track);
// Get the track meta-data
Track track = providerUtils.getTrack(trackId);
if (track == null) {
Log.w(Constants.TAG, "Cannot get track.");
return;
}
String originalDescription = track.getDescription();
track.setDescription("<p>" + track.getDescription() + "</p><p>"
+ stringUtils.generateTrackDescription(track, null, null) + "</p>");
mapsClient = new MapsFacade(context, auth);
// Create a new map if necessary:
boolean isNewMap = mapId.equals(NEW_MAP_ID);
if (isNewMap) {
@@ -166,7 +171,7 @@ public class SendToMyMaps implements Runnable {
Log.w(TAG, "SendToMyMaps: upload waypoints failed.");
}
}
if (success) {
statusMessageId = isNewMap
? R.string.status_new_mymap_has_been_created
@@ -178,7 +183,7 @@ public class SendToMyMaps implements Runnable {
if (mapsClient != null) {
mapsClient.cleanUp();
}
final boolean finalSuccess = success;
final int finalStatusMessageId = statusMessageId;
context.runOnUiThread(new Runnable() {
@@ -206,17 +211,17 @@ public class SendToMyMaps implements Runnable {
Cursor locationsCursor =
providerUtils.getLocationsCursor(track.getId(), 0, -1, false);
try {
if (!locationsCursor.moveToFirst()) {
if (locationsCursor == null || !locationsCursor.moveToFirst()) {
Log.w(TAG, "Unable to get any points to upload");
return false;
}
totalLocationsRead = 0;
totalLocationsPrepared = 0;
totalLocationsUploaded = 0;
totalLocations = locationsCursor.getCount();
totalSegmentsUploaded = 0;
// Limit the number of elevation readings. Ideally we would want around 250.
int elevationSamplingFrequency =
Math.max(1, (int) (totalLocations / 250.0));
@@ -224,12 +229,12 @@ public class SendToMyMaps implements Runnable {
"Using elevation sampling factor: " + elevationSamplingFrequency
+ " on " + totalLocations);
double totalDistance = 0;
Vector<Double> distances = new Vector<Double>();
Vector<Double> elevations = new Vector<Double>();
DoubleBuffer elevationBuffer =
new DoubleBuffer(Constants.ELEVATION_SMOOTHING_FACTOR);
List<Location> locations = new ArrayList<Location>(MAX_POINTS_PER_UPLOAD);
progressIndicator.setProgressMessage(
R.string.progress_message_reading_track);
@@ -238,15 +243,15 @@ public class SendToMyMaps implements Runnable {
if (totalLocationsRead % 100 == 0) {
updateProgress();
}
Location loc = providerUtils.createLocation(locationsCursor);
locations.add(loc);
if (totalLocationsRead == 0) {
// Put a marker at the first point of the first valid segment:
mapsClient.uploadMarker(mapId, track.getName(), track.getDescription(), loc, true);
}
// Add to the elevation profile.
if (loc != null && LocationUtils.isValidLocation(loc)) {
// All points go into the smoothing buffer...
@@ -256,19 +261,19 @@ public class SendToMyMaps implements Runnable {
double dist = lastLocation.distanceTo(loc);
totalDistance += dist;
}
// ...but only a few points are really used to keep the url short.
if (totalLocationsRead % elevationSamplingFrequency == 0) {
distances.add(totalDistance);
elevations.add(elevationBuffer.getAverage());
}
}
// If the location was not valid, it's a segment split, so make sure the
// distance between the previous segment and the new one is not accounted
// for in the next iteration.
lastLocation = loc;
// Every now and then, upload the accumulated points
if (totalLocationsRead % MAX_POINTS_PER_UPLOAD
== MAX_POINTS_PER_UPLOAD - 1) {
@@ -276,28 +281,30 @@ public class SendToMyMaps implements Runnable {
return false;
}
}
totalLocationsRead++;
} while (locationsCursor.moveToNext());
// Do a final upload with what's left
if (!prepareAndUploadPoints(track, locations)) {
return false;
}
// Put an end marker at the last point of the last valid segment:
if (lastLocation != null) {
track.setDescription("<p>" + originalDescription + "</p><p>"
+ stringUtils.generateTrackDescription(
track, distances, elevations)
+ "</p>");
return mapsClient.uploadMarker(mapId, track.getName(), track.getDescription(),
return mapsClient.uploadMarker(mapId, track.getName(), track.getDescription(),
lastLocation, false);
}
return true;
} finally {
locationsCursor.close();
if (locationsCursor != null) {
locationsCursor.close();
}
}
}
@@ -306,17 +313,17 @@ public class SendToMyMaps implements Runnable {
progressIndicator.setProgressMessage(
R.string.progress_message_preparing_track);
updateProgress();
int numLocations = locations.size();
if (numLocations < 2) {
Log.d(TAG, "Not preparing/uploading too few points");
totalLocationsUploaded += numLocations;
return true;
}
// Prepare/pre-process the points
ArrayList<Track> splitTracks = prepareLocations(track, locations);
// Start uploading them
progressIndicator.setProgressMessage(
R.string.progress_message_sending_mymaps);
@@ -330,7 +337,7 @@ public class SendToMyMaps implements Runnable {
Log.d(TAG,
"SendToMyMaps: Prepared feature for upload w/ "
+ splitTrack.getLocations().size() + " points.");
// Transmit tracks via GData feed:
// -------------------------------
Log.d(TAG,
@@ -340,7 +347,7 @@ public class SendToMyMaps implements Runnable {
return false;
}
}
locations.clear();
totalLocationsUploaded += numLocations;
updateProgress();
@@ -358,7 +365,7 @@ public class SendToMyMaps implements Runnable {
private ArrayList<Track> prepareLocations(
Track track, Iterable<Location> locations) {
ArrayList<Track> splitTracks = new ArrayList<Track>();
// Create segments from each full track:
Track segment = new Track();
TripStatistics segmentStats = segment.getStatistics();
@@ -377,11 +384,11 @@ public class SendToMyMaps implements Runnable {
if (loc.getLatitude() > 90) {
startNewTrackSegment = true;
}
if (startNewTrackSegment) {
// Close up the last segment.
prepareTrackSegment(segment, splitTracks);
Log.d(TAG,
"MyTracksSendToMyMaps: Starting new track segment...");
startNewTrackSegment = false;
@@ -391,7 +398,7 @@ public class SendToMyMaps implements Runnable {
segment.setDescription(/* track.getDescription() */ "");
segment.setCategory(track.getCategory());
}
if (loc.getLatitude() <= 90) {
segment.addLocation(loc);
if (segmentStats.getStartTime() < 0) {
@@ -400,9 +407,9 @@ public class SendToMyMaps implements Runnable {
}
totalLocationsPrepared++;
}
prepareTrackSegment(segment, splitTracks);
return splitTracks;
}
@@ -425,13 +432,13 @@ public class SendToMyMaps implements Runnable {
&& segment.getLocations().size() > 0) {
segmentStats.setStopTime(segment.getLocations().size() - 1);
}
/*
* Decimate to 2 meter precision. Mapshop doesn't like too many
* points:
*/
LocationUtils.decimate(segment, 2.0);
/* It the track still has > 500 points, split it in pieces: */
if (segment.getLocations().size() > 500) {
splitTracks.addAll(LocationUtils.split(segment, 500));
@@ -187,7 +187,7 @@ public class TcxTrackWriter implements TrackFormatWriter {
&& sensorData.getCadence().getState() == Sensor.SensorState.SENDING
&& sensorData.getCadence().hasValue()) {
pw.print(" <Cadence>");
pw.print(sensorData.getCadence().getValue());
pw.print(Math.min(254, sensorData.getCadence().getValue()));
pw.println("</Cadence>");
}
}
@@ -16,10 +16,13 @@
package com.google.android.apps.mytracks.io.file;
import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.Track;
import android.content.Context;
import android.util.Log;
/**
* A factory to produce track writers for any format.
@@ -90,6 +93,11 @@ public class TrackWriterFactory {
MyTracksProviderUtils providerUtils,
long trackId, TrackFileFormat format) {
Track track = providerUtils.getTrack(trackId);
if (track == null) {
Log.w(TAG, "Trying to create a writer for an invalid track, id=" + trackId);
return null;
}
return newWriter(context, providerUtils, track, format);
}
@@ -18,6 +18,7 @@ package com.google.android.apps.mytracks.io.sendtogoogle;
import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.accounts.Account;
import com.google.android.apps.analytics.GoogleAnalyticsTracker;
import com.google.android.apps.mytracks.AccountChooser;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.DialogManager;
@@ -34,6 +35,7 @@ import com.google.android.apps.mytracks.io.SendToFusionTables.OnSendCompletedLis
import com.google.android.apps.mytracks.io.SendToMyMaps;
import com.google.android.apps.mytracks.io.mymaps.MapsFacade;
import com.google.android.apps.mytracks.io.mymaps.MyMapsConstants;
import com.google.android.apps.mytracks.util.SystemUtils;
import com.google.android.apps.mytracks.util.UriUtils;
import com.google.android.maps.mytracks.R;
@@ -69,6 +71,8 @@ public class SendActivity extends Activity implements ProgressIndicator {
private MyTracksProviderUtils providerUtils;
private SharedPreferences sharedPreferences;
private GoogleAnalyticsTracker tracker;
// Authentication
private AuthManager lastAuth;
private final HashMap<String, AuthManager> authMap =
@@ -111,6 +115,11 @@ public class SendActivity extends Activity implements ProgressIndicator {
super.onStart();
resetState();
tracker = GoogleAnalyticsTracker.getInstance();
// Start the tracker in manual dispatch mode...
tracker.start(getString(R.string.google_analytics_id), getApplicationContext());
tracker.setProductVersion("android-mytracks", SystemUtils.getMyTracksVersion(this));
Intent intent = getIntent();
String action = intent.getAction();
String type = intent.getType();
@@ -141,11 +150,19 @@ public class SendActivity extends Activity implements ProgressIndicator {
sendDialog.show();
}
@Override
protected void onStop() {
tracker.dispatch();
tracker.stop();
super.onStop();
}
/**
* Initiates the process to send tracks to google.
* This is called once the user has selected sending options via the
* SendToGoogleDialog.
*
*
* TODO: Change this whole flow to an actual state machine.
*/
private void doSend() {
@@ -193,6 +210,8 @@ public class SendActivity extends Activity implements ProgressIndicator {
}
private void sendToGoogleMaps(String mapId) {
tracker.trackPageView("/send/maps");
SendToMyMaps.OnSendCompletedListener onCompletion = new SendToMyMaps.OnSendCompletedListener() {
@Override
public void onSendCompleted(String mapId, boolean success, int statusMessage) {
@@ -247,6 +266,8 @@ public class SendActivity extends Activity implements ProgressIndicator {
}
private void sendToFusionTables() {
tracker.trackPageView("/send/fusion_tables");
OnSendCompletedListener onCompletion = new OnSendCompletedListener() {
@Override
public void onSendCompleted(String tableId, boolean success,
@@ -310,6 +331,8 @@ public class SendActivity extends Activity implements ProgressIndicator {
private void sendToGoogleDocs() {
Log.d(TAG, "Sending to Docs....");
tracker.trackPageView("/send/docs");
setProgressValue(50);
setProgressMessage(R.string.progress_message_sending_docs);
final SendToDocs sender = new SendToDocs(this,
@@ -319,7 +342,7 @@ public class SendActivity extends Activity implements ProgressIndicator {
Runnable onCompletion = new Runnable() {
public void run() {
setProgressValue(100);
// TODO: Use this message
sendToDocsMessage = sender.getStatusMessage();
sendToDocsSuccess = sender.wasSuccess();
@@ -336,6 +359,8 @@ public class SendActivity extends Activity implements ProgressIndicator {
}
private void onSendToGoogleDone() {
tracker.dispatch();
final boolean sentToMyMaps = sendDialog.getSendToMyMaps();
final boolean sentToFusionTables = sendDialog.getSendToFusionTables();
List<SendResult> results = makeSendToGoogleResults();