merge heads

This commit is contained in:
Jimmy Shih
2012-02-15 10:04:18 -08:00
8 changed files with 239 additions and 170 deletions
+1
View File
@@ -293,6 +293,7 @@ limitations under the License.
<service android:name="com.google.android.apps.mytracks.services.ControlRecordingService"
android:exported="false" />
<service android:name="com.google.android.apps.mytracks.services.RemoveTempFilesService"/>
<receiver android:name="com.google.android.apps.mytracks.BootReceiver">
<intent-filter>
@@ -19,6 +19,7 @@ import static android.content.Intent.ACTION_BOOT_COMPLETED;
import static com.google.android.apps.mytracks.Constants.RESUME_TRACK_EXTRA_NAME;
import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.services.RemoveTempFilesService;
import com.google.android.apps.mytracks.services.TrackRecordingService;
import android.content.BroadcastReceiver;
@@ -50,6 +51,9 @@ public class BootReceiver extends BroadcastReceiver {
Intent startIntent = new Intent(context, TrackRecordingService.class);
startIntent.putExtra(RESUME_TRACK_EXTRA_NAME, true);
context.startService(startIntent);
Intent removeTempFilesIntent = new Intent(context, RemoveTempFilesService.class);
context.startService(removeTempFilesIntent);
} else {
Log.w(TAG, "BootReceiver: unsupported action");
}
@@ -24,7 +24,6 @@ import com.google.android.apps.mytracks.content.TracksColumns;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.content.WaypointCreationRequest;
import com.google.android.apps.mytracks.content.WaypointsColumns;
import com.google.android.apps.mytracks.io.file.TempFileCleaner;
import com.google.android.apps.mytracks.services.ITrackRecordingService;
import com.google.android.apps.mytracks.services.ServiceUtils;
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
@@ -256,14 +255,9 @@ public class MyTracks extends TabActivity implements OnTouchListener {
@Override
protected void onStop() {
Log.d(TAG, "MyTracks.onStop");
dataHub.stop();
tracker.dispatch();
tracker.stop();
// Clean up any temporary track files.
TempFileCleaner.clean();
super.onStop();
}
@@ -16,8 +16,10 @@
package com.google.android.apps.mytracks;
import com.google.android.apps.mytracks.content.TrackDataHub;
import com.google.android.apps.mytracks.services.RemoveTempFilesService;
import android.app.Application;
import android.content.Intent;
/**
* MyTracksApplication for keeping global state.
@@ -28,6 +30,11 @@ public class MyTracksApplication extends Application {
private TrackDataHub trackDataHub;
@Override
public void onCreate() {
startService(new Intent(this, RemoveTempFilesService.class));
}
/**
* Gets the application's TrackDataHub.
*
@@ -1,78 +0,0 @@
/*
* Copyright 2010 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
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.io.file;
import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.util.FileUtils;
import android.os.Environment;
import android.util.Log;
import java.io.File;
/**
* A class to clean up old temporary files.
* @author Sandor Dornbush
*/
public class TempFileCleaner {
private long currentTimeMillis;
public static void clean() {
(new TempFileCleaner(System.currentTimeMillis())).cleanImpl();
}
// @VisibleForTesting
TempFileCleaner(long time) {
currentTimeMillis = time;
}
private void cleanImpl() {
if (!Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
return; // Can't do anything now.
}
cleanTmpDirectory("csv");
cleanTmpDirectory("gpx");
cleanTmpDirectory("kml");
cleanTmpDirectory("tcx");
}
private void cleanTmpDirectory(String name) {
FileUtils fileUtils = new FileUtils();
String dirName = fileUtils.buildExternalDirectoryPath(name, "tmp");
cleanTmpDirectory(new File(dirName));
}
// @VisibleForTesting
int cleanTmpDirectory(File dir) {
if (!dir.exists()) {
return 0;
}
int count = 0;
long oldest = currentTimeMillis - 1000 * 3600;
for (File f : dir.listFiles()) {
if (f.lastModified() < oldest) {
if (!f.delete()) {
Log.w(TAG, "Failed to delete file " + f.getAbsolutePath());
}
count++;
}
}
return count;
}
}
@@ -0,0 +1,124 @@
/*
* Copyright 2012 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
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.services;
import com.google.android.apps.mytracks.io.file.TrackWriterFactory.TrackFileFormat;
import com.google.android.apps.mytracks.util.FileUtils;
import com.google.common.annotations.VisibleForTesting;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Environment;
import android.os.IBinder;
import android.util.Log;
import java.io.File;
/**
* A service to remove My Tracks temp files older than one hour on the SD card.
*
* @author Jimmy Shih
*/
public class RemoveTempFilesService extends Service {
private static final String TAG = RemoveTempFilesService.class.getSimpleName();
private static final int ONE_HOUR_IN_MILLISECONDS = 60 * 60 * 1000;
private RemoveTempFilesAsyncTask removeTempFilesAsyncTask = null;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Setup an alarm to repeatedly call this service
Intent alarmIntent = new Intent(this, RemoveTempFilesService.class);
PendingIntent pendingIntent = PendingIntent.getService(
this, 0, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + ONE_HOUR_IN_MILLISECONDS, AlarmManager.INTERVAL_HOUR,
pendingIntent);
// Invoke the AsyncTask to cleanup the temp files
if (removeTempFilesAsyncTask == null
|| removeTempFilesAsyncTask.getStatus().equals(AsyncTask.Status.FINISHED)) {
removeTempFilesAsyncTask = new RemoveTempFilesAsyncTask();
removeTempFilesAsyncTask.execute((Void[]) null);
}
return START_NOT_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
private class RemoveTempFilesAsyncTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... params) {
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
// Can't do anything
return null;
}
cleanTempDirectory(TrackFileFormat.GPX.getExtension());
cleanTempDirectory(TrackFileFormat.KML.getExtension());
cleanTempDirectory(TrackFileFormat.CSV.getExtension());
cleanTempDirectory(TrackFileFormat.TCX.getExtension());
return null;
}
@Override
protected void onPostExecute(Void result) {
stopSelf();
}
}
private void cleanTempDirectory(String name) {
FileUtils fileUtils = new FileUtils();
String dirName = fileUtils.buildExternalDirectoryPath(name, "tmp");
File dir = new File(dirName);
cleanTempDirectory(dir);
}
/**
* Removes temp files in a directory older than one hour.
*
* @param dir the directory
* @return the number of files removed.
*/
@VisibleForTesting
int cleanTempDirectory(File dir) {
if (!dir.exists()) {
return 0;
}
int count = 0;
long oneHourAgo = System.currentTimeMillis() - ONE_HOUR_IN_MILLISECONDS;
for (File f : dir.listFiles()) {
if (f.lastModified() < oneHourAgo) {
if (!f.delete()) {
Log.e(TAG, "Unable to delete file: " + f.getAbsolutePath());
} else {
count++;
}
}
}
return count;
}
}
@@ -1,86 +0,0 @@
/*
* Copyright 2010 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
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.io.file;
import static com.google.android.testing.mocking.AndroidMock.expect;
import com.google.android.apps.mytracks.io.file.TempFileCleaner;
import com.google.android.testing.mocking.AndroidMock;
import com.google.android.testing.mocking.UsesMocks;
import android.test.AndroidTestCase;
import java.io.File;
/**
* @author Sandor Dornbush
*/
public class TempFileCleanerTest extends AndroidTestCase {
@UsesMocks({
File.class,
})
public void test_noDir() {
File dir = AndroidMock.createMock(File.class, "/no_file");
TempFileCleaner cleaner = new TempFileCleaner(0);
expect(dir.exists()).andStubReturn(false);
AndroidMock.replay(dir);
assertEquals(0, cleaner.cleanTmpDirectory(dir));
AndroidMock.verify(dir);
}
public void test_emptyDir() {
File dir = AndroidMock.createMock(File.class, "/no_file");
TempFileCleaner cleaner = new TempFileCleaner(0);
expect(dir.exists()).andStubReturn(true);
expect(dir.listFiles()).andStubReturn(new File[0]);
AndroidMock.replay(dir);
assertEquals(0, cleaner.cleanTmpDirectory(dir));
AndroidMock.verify(dir);
}
public void test_newFile() {
File dir = AndroidMock.createMock(File.class, "/no_file");
long now = 100000000;
TempFileCleaner cleaner = new TempFileCleaner(now);
expect(dir.exists()).andStubReturn(true);
File file = AndroidMock.createMock(File.class, "/no_file/foo");
expect(file.lastModified()).andStubReturn(now);
File[] list = { file };
expect(dir.listFiles()).andStubReturn(list);
AndroidMock.replay(dir, file);
assertEquals(0, cleaner.cleanTmpDirectory(dir));
AndroidMock.verify(dir, file);
}
public void test_oldFile() {
File dir = AndroidMock.createMock(File.class, "/no_file");
long now = 100000000;
TempFileCleaner cleaner = new TempFileCleaner(now);
expect(dir.exists()).andStubReturn(true);
File file = AndroidMock.createMock(File.class, "/no_file/foo");
expect(file.lastModified()).andStubReturn(now - 3600001);
expect(file.delete()).andStubReturn(true);
File[] list = { file };
expect(dir.listFiles()).andStubReturn(list);
AndroidMock.replay(dir, file);
assertEquals(1, cleaner.cleanTmpDirectory(dir));
AndroidMock.verify(dir, file);
}
}
@@ -0,0 +1,103 @@
/*
* Copyright 2010 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
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.services;
import static com.google.android.testing.mocking.AndroidMock.expect;
import com.google.android.testing.mocking.AndroidMock;
import com.google.android.testing.mocking.UsesMocks;
import android.test.AndroidTestCase;
import java.io.File;
/**
* Tests {@link RemoveTempFilesService}.
*
* @author Sandor Dornbush
*/
public class RemoveTempFilesServiceTest extends AndroidTestCase {
private static final String DIR_NAME = "/tmp";
private static final String FILE_NAME = "foo";
private RemoveTempFilesService service;
@UsesMocks({ File.class, })
protected void setUp() throws Exception {
service = new RemoveTempFilesService();
};
/**
* Tests when the directory doesn't exists.
*/
public void test_noDir() {
File dir = AndroidMock.createMock(File.class, DIR_NAME);
expect(dir.exists()).andStubReturn(false);
AndroidMock.replay(dir);
assertEquals(0, service.cleanTempDirectory(dir));
AndroidMock.verify(dir);
}
/**
* Tests when the directory is empty.
*/
public void test_emptyDir() {
File dir = AndroidMock.createMock(File.class, DIR_NAME);
expect(dir.exists()).andStubReturn(true);
expect(dir.listFiles()).andStubReturn(new File[0]);
AndroidMock.replay(dir);
assertEquals(0, service.cleanTempDirectory(dir));
AndroidMock.verify(dir);
}
/**
* Tests when there is a new file and it shouldn't get deleted.
*/
public void test_newFile() {
File file = AndroidMock.createMock(File.class, DIR_NAME + FILE_NAME);
expect(file.lastModified()).andStubReturn(System.currentTimeMillis());
File dir = AndroidMock.createMock(File.class, DIR_NAME);
expect(dir.exists()).andStubReturn(true);
expect(dir.listFiles()).andStubReturn(new File[] { file });
AndroidMock.replay(dir, file);
assertEquals(0, service.cleanTempDirectory(dir));
AndroidMock.verify(dir, file);
}
/**
* Tests when there is an old file and it should get deleted.
*/
public void test_oldFile() {
File file = AndroidMock.createMock(File.class, DIR_NAME + FILE_NAME);
// qSet to one hour and 1 millisecond later than the current time
expect(file.lastModified()).andStubReturn(System.currentTimeMillis() - 3600001);
expect(file.delete()).andStubReturn(true);
File dir = AndroidMock.createMock(File.class, DIR_NAME);
expect(dir.exists()).andStubReturn(true);
expect(dir.listFiles()).andStubReturn(new File[] { file });
AndroidMock.replay(dir, file);
assertEquals(1, service.cleanTempDirectory(dir));
AndroidMock.verify(dir, file);
}
}