package net.wigle.wigleandroid.background; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashSet; import java.util.Set; import net.wigle.wigleandroid.DBException; import net.wigle.wigleandroid.DatabaseHelper; import net.wigle.wigleandroid.MainActivity; import android.annotation.SuppressLint; import android.content.Context; import android.database.Cursor; import android.os.Bundle; import android.os.Environment; import android.support.v4.app.FragmentActivity; public class KmlWriter extends AbstractBackgroundTask { private final Set<String> networks; public KmlWriter( final FragmentActivity context, final DatabaseHelper dbHelper ) { this( context, dbHelper, null); } public KmlWriter( final FragmentActivity context, final DatabaseHelper dbHelper, final Set<String> networks ) { super(context, dbHelper, "KmlWriter", true); // make a safe local copy this.networks = (networks == null) ? null : new HashSet<>(networks); } @SuppressLint("SimpleDateFormat") @Override protected void subRun() throws IOException { final Bundle bundle = new Bundle(); final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); final SimpleDateFormat fileDateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); final String filename = "WigleWifi_" + fileDateFormat.format(new Date()) + ".kml"; final FileOutputStream fos = MainActivity.createFile(context, filename); // header FileUploaderTask.writeFos( fos, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<kml xmlns=\"http://www.opengis.net/kml/2.2\"><Document>" + "<Style id=\"red\"><IconStyle><Icon><href>http://maps.google.com/mapfiles/ms/icons/red-dot.png</href></Icon></IconStyle></Style>" + "<Style id=\"yellow\"><IconStyle><Icon><href>http://maps.google.com/mapfiles/ms/icons/yellow-dot.png</href></Icon></IconStyle></Style>" + "<Style id=\"green\"><IconStyle><Icon><href>http://maps.google.com/mapfiles/ms/icons/green-dot.png</href></Icon></IconStyle></Style>" + "<Folder><name>Wifi Networks</name>\n" ); // body Cursor cursor = null; Status status = null; try { if ( this.networks == null ) { cursor = dbHelper.networkIterator(); writeKmlFromCursor( fos, cursor, dateFormat, 0, dbHelper.getNetworkCount(), bundle ); } else { int count = 0; for ( String network : networks ) { // MainActivity.info( "network: " + network ); cursor = dbHelper.getSingleNetwork( network ); writeKmlFromCursor( fos, cursor, dateFormat, count, networks.size(), bundle ); cursor.close(); cursor = null; count++; } } status = Status.WRITE_SUCCESS; } catch ( final InterruptedException ex ) { MainActivity.info("Writing Kml Interrupted: " + ex); } catch ( DBException ex ) { dbHelper.deathDialog("Writing Kml", ex); status = Status.EXCEPTION; } catch ( final Exception ex ) { ex.printStackTrace(); MainActivity.error( "ex problem: " + ex, ex ); MainActivity.writeError( this, ex, context ); status = Status.EXCEPTION; bundle.putString( BackgroundGuiHandler.ERROR, "ex problem: " + ex ); } finally { if ( cursor != null ) { cursor.close(); } } // footer FileUploaderTask.writeFos( fos, "</Folder>\n</Document></kml>" ); fos.close(); bundle.putString( BackgroundGuiHandler.FILEPATH, MainActivity.getSDPath() + filename ); bundle.putString( BackgroundGuiHandler.FILENAME, filename ); MainActivity.info( "done with kml export" ); // status is null on interrupted if ( status != null ) { // tell gui sendBundledMessage( status.ordinal(), bundle ); } } private boolean writeKmlFromCursor( final OutputStream fos, final Cursor cursor, final SimpleDateFormat dateFormat, long startCount, long totalCount, final Bundle bundle ) throws IOException, InterruptedException { int lineCount = 0; for ( cursor.moveToFirst(); ! cursor.isAfterLast(); cursor.moveToNext() ) { if ( wasInterrupted() ) { throw new InterruptedException( "we were interrupted" ); } // bssid,ssid,frequency,capabilities,lasttime,lastlat,lastlon final String bssid = cursor.getString(0); final String ssid = cursor.getString(1); final int frequency = cursor.getInt(2); final String capabilities = cursor.getString(3); final long lasttime = cursor.getLong(4); final double lastlat = cursor.getDouble(5); final double lastlon = cursor.getDouble(6); final String date = dateFormat.format( new Date( lasttime ) ); String style = "green"; if (capabilities.contains("WEP")) { style = "yellow"; } if (capabilities.contains("WPA")) { style = "red"; } // not unicode. ha ha for them! byte[] ssidFiltered = ssid.getBytes( MainActivity.ENCODING ); filterIllegalXml( ssidFiltered ); FileUploaderTask.writeFos( fos, "<Placemark>\n<name><![CDATA[" ); fos.write( ssidFiltered ); FileUploaderTask.writeFos( fos, "]]></name>\n" ); FileUploaderTask.writeFos( fos, "<description><![CDATA[BSSID: <b>" + bssid + "</b><br/>" + "Capabilities: <b>" + capabilities + "</b><br/>Frequency: <b>" + frequency + "</b><br/>" + "Timestamp: <b>" + lasttime + "</b><br/>Date: <b>" + date + "</b>]]></description><styleUrl>#" + style + "</styleUrl>\n" ); FileUploaderTask.writeFos( fos, "<Point>\n" ); FileUploaderTask.writeFos( fos, "<coordinates>" + lastlon + "," + lastlat + "</coordinates>" ); FileUploaderTask.writeFos( fos, "</Point>\n</Placemark>\n" ); lineCount++; if ( (lineCount % 1000) == 0 ) { MainActivity.info("lineCount: " + lineCount + " of " + totalCount ); } // update UI if ( totalCount == 0 ) { totalCount = 1; } final int percentDone = (int)(((lineCount + startCount) * 1000) / totalCount); sendPercentTimesTen( percentDone, bundle ); } return true; } private void filterIllegalXml( byte[] data ) { for ( int i = 0; i < data.length; i++ ) { byte current = data[i]; // (0x00, 0x08), (0x0B, 0x1F), (0x7F, 0x84), (0x86, 0x9F) //noinspection ConstantConditions if ( (current >= 0x00 && current <= 0x08) || (current >= 0x0B && current <= 0x1F) || (current >= 0x7F && current <= 0x84) || (current >= 0x86 && current <= 0x9F) ) { data[i] = ' '; } } } }