/*
* Copyright (C) 2012 The Serval Project
*
* This file is part of the Serval Maps Software
*
* Serval Maps Software is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this source code; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.servalproject.maps.location;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import android.content.Context;
import android.location.Location;
import android.location.LocationManager;
import android.provider.Settings;
import android.util.Log;
/**
* a runnable class that sends mock locations for testing purposes
*/
public class MockLocations implements Runnable {
/*
* private class level constants
*/
private final String TAG = "MockLocations";
private final boolean V_LOG = false;
private final String LOCATION_FILE = "mock-locations.txt";
private final String LOCATION_ZIP_FILE = "mock-locations.zip";
/*
* private class level variables
*/
private String locations = null;
private LocationManager locationManager;
private volatile boolean keepGoing = true;
/**
* create the MockLocations class and open the zip file for the required
* location data
*
* @param context the application context
*
* @throws IllegalArgumentException if the context field is null
* @throws IOException if opening the zip file fails
*/
public MockLocations(Context context) throws IOException {
if(V_LOG) {
Log.v(TAG, "opening the zip file");
}
// open the zip file and get the required file inside
ZipInputStream mZipInput = new ZipInputStream(context.getAssets().open(LOCATION_ZIP_FILE));
ZipEntry mZipEntry;
// look for the required file
while((mZipEntry = mZipInput.getNextEntry())!= null) {
if(V_LOG) {
Log.v(TAG, "ZipEntry: " + mZipEntry.getName());
}
// read the bytes from the file and convert them to a string
if(mZipEntry.getName().equals(LOCATION_FILE)) {
if(V_LOG) {
Log.v(TAG, "required file found inside zip file");
}
ByteArrayOutputStream mByteStream = new ByteArrayOutputStream();
byte[] mBuffer = new byte[1024];
int mCount;
while((mCount = mZipInput.read(mBuffer)) != -1) {
mByteStream.write(mBuffer, 0, mCount);
}
locations = new String(mByteStream.toByteArray(), "UTF-8");
}
if(V_LOG) {
Log.v(TAG, "location file successfully read");
}
mZipInput.closeEntry();
}
mZipInput.close();
// check to make sure everything was read successfully
if(locations == null) {
throw new IOException("unable to read the required file from the zip file");
}
// get an instance of the LocationManager class
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
// add a reference to our test provider
// use the standard provider name so the rest of the code still works
locationManager.addTestProvider(LocationManager.GPS_PROVIDER, false, false, false, false, false, true, true, 0, 5);
}
/**
* check to confirm that the "Allow mock locations setting is set"
* @param context a context object used to gain access to application resources
* @return true if "Allow mock locations" is set, otherwise false
*/
public static boolean isMockLocationSet(Context context) {
if (Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION).contentEquals("1")) {
return true;
}
else {
return false;
}
}
/**
* request that this thread stops
*/
public void requestStop() {
if(V_LOG) {
Log.v(TAG, "thread requested to stop");
}
keepGoing = false;
}
/*
* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
if(V_LOG) {
Log.v(TAG, "thread started");
}
String[] mParts;
int mLineCount = -1;
int mSleepTime;
double mLatitude;
double mLongitude;
Location mLocation;
// loop through each of the locations
for(String mToken : locations.split("\\n")) {
if(keepGoing == false) {
if(V_LOG) {
Log.v(TAG, "thread stopped");
}
return;
}
mLineCount++;
// only process lines that aren't comments
if(mToken.startsWith("#") == true) {
continue;
}
mParts = mToken.split("\\|");
/*
* validate the line
*/
if(mParts.length != 3) {
Log.e(TAG, "expected 3 data elements found '" + mParts.length + "' on line: " + mLineCount);
}
try {
mSleepTime = Integer.parseInt(mParts[0]);
} catch (NumberFormatException e) {
Log.e(TAG, "unable to parse the sleep time element on line: " + mLineCount);
continue;
}
try {
mLatitude = Double.parseDouble(mParts[1]);
} catch (NumberFormatException e) {
Log.e(TAG, "unable to parse the latitude element on line: " + mLineCount);
continue;
}
try {
mLongitude = Double.parseDouble(mParts[2]);
} catch (NumberFormatException e) {
Log.e(TAG, "unable to parse the longitude element on line: " + mLineCount);
continue;
}
// create the new location
mLocation = new Location(LocationManager.GPS_PROVIDER);
mLocation.setLatitude(mLatitude);
mLocation.setLongitude(mLongitude);
mLocation.setTime(System.currentTimeMillis());
// send the new location
locationManager.setTestProviderEnabled(LocationManager.GPS_PROVIDER, true);
locationManager.setTestProviderLocation(LocationManager.GPS_PROVIDER, mLocation);
if(V_LOG) {
Log.v(TAG, "new location sent");
}
// sleep the thread
try {
Thread.sleep(mSleepTime * 1000);
} catch (InterruptedException e) {
if(keepGoing == false) {
if(V_LOG) {
Log.v(TAG, "thread was interrupted and is stopping");
}
return;
} else {
Log.w(TAG, "thread was interrupted without being requested to stop", e);
}
}
}
}
}