Allow cancellation of track write

This commit is contained in:
Matthew Simmons
2011-04-12 15:43:54 -04:00
parent 82adcf37f4
commit ba472d3b7b
8 changed files with 119 additions and 26 deletions
+1
View File
@@ -45,6 +45,7 @@ limitations under the License.
<string name="error_invalid_gpx_format">File does not have a valid GPX format.</string> <string name="error_invalid_gpx_format">File does not have a valid GPX format.</string>
<string name="error_generic">An error occurred. Please try again.</string> <string name="error_generic">An error occurred. Please try again.</string>
<string name="error_track_does_not_exist">Track does not exist.</string> <string name="error_track_does_not_exist">Track does not exist.</string>
<string name="error_operation_cancelled">The operation was cancelled.</string>
<string name="error_out_of_memory">This file is too big (must be less than 500 KB). Aborting.</string> <string name="error_out_of_memory">This file is too big (must be less than 500 KB). Aborting.</string>
<string name="error_unable_to_insert_marker">Unable to insert marker. No location fix available.</string> <string name="error_unable_to_insert_marker">Unable to insert marker. No location fix available.</string>
@@ -20,6 +20,7 @@ import com.google.android.maps.mytracks.R;
import android.app.Activity; import android.app.Activity;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.DialogInterface;
/** /**
* Given a {@link TrackWriter}, this class manages the process of writing the * Given a {@link TrackWriter}, this class manages the process of writing the
@@ -66,6 +67,7 @@ class WriteProgressController {
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setMessage(activity.getString(R.string.write_progress_message)); dialog.setMessage(activity.getString(R.string.write_progress_message));
dialog.setIndeterminate(true); dialog.setIndeterminate(true);
dialog.setOnCancelListener(dialogCancelListener);
writer.setOnCompletionListener(writerCompleteListener); writer.setOnCompletionListener(writerCompleteListener);
writer.setOnWriteListener(writerWriteListener); writer.setOnWriteListener(writerWriteListener);
@@ -87,6 +89,14 @@ class WriteProgressController {
return dialog; return dialog;
} }
private final DialogInterface.OnCancelListener dialogCancelListener =
new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
writer.stopWriteTrack();
}
};
private final TrackWriter.OnCompletionListener writerCompleteListener = private final TrackWriter.OnCompletionListener writerCompleteListener =
new TrackWriter.OnCompletionListener() { new TrackWriter.OnCompletionListener() {
@Override @Override
@@ -54,7 +54,7 @@ public interface TrackFormatWriter {
void prepare(Track track, OutputStream out); void prepare(Track track, OutputStream out);
/** /**
* @return The file extentsion (i.e. gpx, kml, ...) * @return The file extension (i.e. gpx, kml, ...)
*/ */
String getExtension(); String getExtension();
@@ -77,7 +77,7 @@ public interface TrackFormatWriter {
* *
* @param location the location to write * @param location the location to write
*/ */
void writeLocation(Location location); void writeLocation(Location location) throws InterruptedException;
/** /**
* Write a way point. * Write a way point.
@@ -75,6 +75,11 @@ public interface TrackWriter {
*/ */
void writeTrack(); void writeTrack();
/**
* Stop any in-progress writes
*/
void stopWriteTrack();
/** /**
* Returns true if the write completed successfully. * Returns true if the write completed successfully.
*/ */
@@ -57,6 +57,7 @@ class TrackWriterImpl implements TrackWriter {
private File file = null; private File file = null;
private OnCompletionListener onCompletionListener; private OnCompletionListener onCompletionListener;
private OnWriteListener onWriteListener; private OnWriteListener onWriteListener;
private Thread writeThread;
TrackWriterImpl(Context context, MyTracksProviderUtils providerUtils, TrackWriterImpl(Context context, MyTracksProviderUtils providerUtils,
Track track, TrackFormatWriter writer) { Track track, TrackFormatWriter writer) {
@@ -89,28 +90,60 @@ class TrackWriterImpl implements TrackWriter {
@Override @Override
public void writeTrackAsync() { public void writeTrackAsync() {
Thread t = new Thread() { writeThread = new Thread() {
@Override @Override
public void run() { public void run() {
writeTrack(); doWriteTrack();
} }
}; };
t.start(); writeThread.start();
} }
@Override @Override
public void writeTrack() { public void writeTrack() {
writeTrackAsync();
try {
writeThread.join();
} catch (InterruptedException e) {
Log.e(Constants.TAG, "Interrupted waiting for write to complete", e);
}
}
private void doWriteTrack() {
// Open the input and output // Open the input and output
success = false; success = false;
errorMessage = R.string.error_track_does_not_exist; errorMessage = R.string.error_track_does_not_exist;
if (track != null) { if (track != null) {
if (openFile()) { if (openFile()) {
writeDocument(); try {
writeDocument();
} catch (InterruptedException e) {
Log.i(Constants.TAG, "The track write was interrupted");
if (file != null) {
file.delete();
}
success = false;
errorMessage = R.string.error_operation_cancelled;
}
} }
} }
finished(); finished();
} }
public void stopWriteTrack() {
if (writeThread != null && writeThread.isAlive()) {
Log.i(Constants.TAG, "Attempting to stop track write");
writeThread.interrupt();
try {
writeThread.join();
Log.i(Constants.TAG, "Track write stopped");
} catch (InterruptedException e) {
Log.e(Constants.TAG, "Failed to wait for writer to stop", e);
}
}
}
@Override @Override
public int getErrorMessage() { public int getErrorMessage() {
return errorMessage; return errorMessage;
@@ -253,7 +286,7 @@ class TrackWriterImpl implements TrackWriter {
/** /**
* Does the actual work of writing the track to the now open file. * Does the actual work of writing the track to the now open file.
*/ */
void writeDocument() { void writeDocument() throws InterruptedException {
Log.d(Constants.TAG, "Started writing track."); Log.d(Constants.TAG, "Started writing track.");
writer.writeHeader(); writer.writeHeader();
writeWaypoints(track.getId()); writeWaypoints(track.getId());
@@ -265,7 +298,7 @@ class TrackWriterImpl implements TrackWriter {
errorMessage = R.string.io_write_finished; errorMessage = R.string.io_write_finished;
} }
private void writeLocations() { private void writeLocations() throws InterruptedException {
boolean wroteFirst = false; boolean wroteFirst = false;
boolean segmentOpen = false; boolean segmentOpen = false;
boolean isLastValid = false; boolean isLastValid = false;
@@ -303,6 +336,9 @@ class TrackWriterImpl implements TrackWriter {
int pointNumber = 0; int pointNumber = 0;
while (it.hasNext()) { while (it.hasNext()) {
Location loc = it.next(); Location loc = it.next();
if (Thread.interrupted()) {
throw new InterruptedException();
}
pointNumber++; pointNumber++;
@@ -59,6 +59,11 @@ public class MockTrackWriter implements TrackWriter {
throw new UnsupportedOperationException("not implemented"); throw new UnsupportedOperationException("not implemented");
} }
@Override
public void stopWriteTrack() {
throw new UnsupportedOperationException("not implemented");
}
@Override @Override
public boolean wasSuccess() { public boolean wasSuccess() {
return false; return false;
@@ -5,18 +5,9 @@ import com.google.android.apps.mytracks.content.MyTracksLocation;
import com.google.android.apps.mytracks.content.Sensor; import com.google.android.apps.mytracks.content.Sensor;
import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.io.file.TrackFormatWriter;
import android.test.AndroidTestCase; import android.test.AndroidTestCase;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
@@ -29,6 +20,14 @@ import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError; import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/** /**
* Base class for track format writer tests, which sets up a fake track and * Base class for track format writer tests, which sets up a fake track and
* gives auxiliary methods for verifying XML output. * gives auxiliary methods for verifying XML output.
@@ -107,7 +106,7 @@ public abstract class TrackFormatWriterTest extends AndroidTestCase {
* @param writer the writer to write to * @param writer the writer to write to
* @return the written contents * @return the written contents
*/ */
protected String writeTrack(TrackFormatWriter writer) { protected String writeTrack(TrackFormatWriter writer) throws Exception {
OutputStream output = new ByteArrayOutputStream(BUFFER_SIZE); OutputStream output = new ByteArrayOutputStream(BUFFER_SIZE);
writer.prepare(track, output); writer.prepare(track, output);
writer.writeHeader(); writer.writeHeader();
@@ -9,10 +9,9 @@ import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils.Factory; import com.google.android.apps.mytracks.content.MyTracksProviderUtils.Factory;
import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.io.file.TrackFormatWriter;
import com.google.android.apps.mytracks.io.file.TrackWriterImpl;
import com.google.android.apps.mytracks.services.TrackRecordingServiceTest.MockContext; import com.google.android.apps.mytracks.services.TrackRecordingServiceTest.MockContext;
import com.google.android.apps.mytracks.testing.TestingProviderUtilsFactory; import com.google.android.apps.mytracks.testing.TestingProviderUtilsFactory;
import com.google.android.maps.mytracks.R;
import android.content.Context; import android.content.Context;
import android.location.Location; import android.location.Location;
@@ -25,6 +24,7 @@ import java.io.FileNotFoundException;
import java.io.OutputStream; import java.io.OutputStream;
import org.easymock.EasyMock; import org.easymock.EasyMock;
import org.easymock.IAnswer;
import org.easymock.IArgumentMatcher; import org.easymock.IArgumentMatcher;
import org.easymock.IMocksControl; import org.easymock.IMocksControl;
@@ -180,6 +180,43 @@ public class TrackWriterTest extends AndroidTestCase {
mocksControl.verify(); mocksControl.verify();
} }
public void testWriteTrack_cancelled() throws Exception {
final ByteArrayOutputStream stream = new ByteArrayOutputStream();
writer = new OpenFileTrackWriter(
getContext(), providerUtils, track, formatWriter, stream, true);
formatWriter.prepare(track, stream);
final Location[] locs = {
new Location("fake0"),
new Location("fake1"),
};
fillLocations(locs);
assertEquals(locs.length, providerUtils.bulkInsertTrackPoints(locs, locs.length, TRACK_ID));
formatWriter.writeHeader();
formatWriter.writeBeginTrack(locEq(locs[0]));
formatWriter.writeOpenSegment();
formatWriter.writeLocation(locEq(locs[0]));
//EasyMock.expectLastCall().andThrow(new InterruptedException());
EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
@Override
public Object answer() throws Throwable {
throw new InterruptedException();
}
});
mocksControl.replay();
writer.writeTrack();
mocksControl.verify();
assertFalse(writer.wasSuccess());
assertEquals(R.string.error_operation_cancelled, writer.getErrorMessage());
}
public void testWriteTrack_openFails() { public void testWriteTrack_openFails() {
writer = new WriteTracksTrackWriter(getContext(), providerUtils, track, writer = new WriteTracksTrackWriter(getContext(), providerUtils, track,
formatWriter, false); formatWriter, false);
@@ -229,7 +266,7 @@ public class TrackWriterTest extends AndroidTestCase {
mocksControl.verify(); mocksControl.verify();
} }
public void testWriteDocument_emptyTrack() { public void testWriteDocument_emptyTrack() throws Exception {
writer = new TrackWriterImpl(getContext(), providerUtils, track, formatWriter); writer = new TrackWriterImpl(getContext(), providerUtils, track, formatWriter);
// Set expected mock behavior // Set expected mock behavior
@@ -244,7 +281,7 @@ public class TrackWriterTest extends AndroidTestCase {
mocksControl.verify(); mocksControl.verify();
} }
public void testWriteDocument() { public void testWriteDocument() throws Exception {
writer = new TrackWriterImpl(getContext(), providerUtils, track, formatWriter); writer = new TrackWriterImpl(getContext(), providerUtils, track, formatWriter);
final Location[] locs = { final Location[] locs = {