How I spent my Saturday

Hello, World!

So, I got an Android. Heck yes.

Having played with the Recovery image, I was hooked in. So I rooted it. Then Fixed up the install to Cyanogen 6 ( Upstream 2.2 Froyo ).

Well, then I got done with that. Lame. So, I thought to myself, what’s next?

I settled on trying to program the thing. I downloaded the SDK and installed everything nice and neat. I got it fired up and figured out that the Android VM is almost identical to the Java VM, with some small ( SMALL ) differences. Not watered down Blackberry Java, but real Java. This’ll be easy!

So, because it’s Java, I decided to skip Hello, World. So, I figured I would design “GeoLoc” — a Socket-based bridge to the GPS system on my Android.

But it’s all text if there’s nothing to tie into. A quick rewind to some old code I wrote for Flight Gear, and I had myself a KDE Marble interface to my GPS location. Although it’s super late, I’d really like to thank jmho, who did some really amazing work to help me make the interface work. Thanks, jmho!

This code does is accept connections on port 20017, and watch for input. When the system on the remote end requests either “lat” or “lon” it returns data in the format “data:information until end of line”.

A simple session would be:

lat
lat:12.34567
lon
lon:12.34567

So, our GUI tickles for information every few seconds, and re-draws the map.

So, let’s get down to some ‘droid programming!

My basic class ( GeoLoc.java ) looks like this:

import java.io.IOException;
import java.net.ServerSocket;
import java.util.ArrayList;

import android.app.Activity;
import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.widget.TextView;

public class GeoLoc extends Activity {

	TextView           tv;
	MyLocationListener mlocListener;
	boolean            running;

	@Override
	public void onCreate(Bundle savedInstanceState){
		super.onCreate(savedInstanceState);
		this.running = true;
		int port = 20017;
		this.mlocListener = null;
		this.tv = new TextView(this);
		tv.setText("Waiting on port " + port );
		setContentView(tv);
		/* Use the LocationManager class to obtain GPS locations */
		LocationManager  mlocManager = (LocationManager)
				getSystemService(Context.LOCATION_SERVICE);
		try {
			this.mlocListener = new MyLocationListener( port );
			mlocManager.requestLocationUpdates(
					LocationManager.GPS_PROVIDER, 0, 0, this.mlocListener);
		} catch (IOException e) { }
	}

	public class MyLocationListener extends Thread implements LocationListener { /* Inner class */
		ServerSocket s;
		ArrayList childs;
		public MyLocationListener( int port ) throws IOException {
			this.s = new ServerSocket( port );
			this.childs = new ArrayList();
			this.start();
		}
		@Override
		public void run() {
			while ( true ) {
				try {
					// System.err.println("[ ST ] Waiting on Thread" );
					ServerThread t = new ServerThread( this.s.accept() );
					t.start();
					this.childs.add(t);
					// System.err.println("[ ST ] Thread Spawned" );
				} catch (IOException e) {
					// crap-tastic but managable.
				}
			}
		}
		@Override
		public void onLocationChanged(Location loc) {
			for ( int i = 0; i < this.childs.size(); ++i ) {
				if ( this.childs.get(i).running ) {
					this.childs.get(i).sendLoc(loc);
					//System.err.println("[ GL ] Updating Child " + i );
				}
			}
		}
		@Override
		public void onProviderDisabled(String provider) { }

		@Override
		public void onProviderEnabled(String provider) { }

		@Override
		public void onStatusChanged(String provider, int status, Bundle extras) { }

	} /* End of Class MyLocationListener */
}

This uses a server thread that looks like:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;

import android.location.Location;

public class ServerThread extends Thread {
	Socket s;
	BufferedReader     input;
	OutputStreamWriter output;
	Location location; //location
	double lat;
	double lon;
	boolean running;
	ServerThread( Socket s ) throws IOException {
		//System.err.println("[ ST ] Connection rcvd, " + s.getInetAddress() ); // REMOVE ME
		this.running = true;
		this.lat = 0;
		this.lon = 0;
		this.location = null;
		this.s = s;
		this.input    = new BufferedReader(
				new InputStreamReader(
						s.getInputStream()
				)
		);
		this.output   = new OutputStreamWriter(
				s.getOutputStream()
		);
	}
	public void pipeout( String s ) {
		try {
			this.output.write(s + "\r\n");
			this.output.flush();
		} catch ( IOException e ) {
			this.running = false;
		}
	}
	public void outputLat() {
		if ( this.location == null ) {
			this.pipeout("error:latitude being aquired");
		} else {
			String output = "lat:" + this.lat;
			this.pipeout(output);
		}
	}
	public void outputLon() {
		if ( this.location == null ) {
			this.pipeout("error:longit being aquired");
		} else {
			String output = "lon:" + this.lon;
			this.pipeout(output);
		}
	}
	public void sendLoc( Location loc ) {
		this.location = loc;
		this.location.getLatitude();
		this.location.getLongitude();
		this.lat = this.location.getLatitude();
		this.lon = this.location.getLongitude();
		//System.err.println("[ ST ] Lat: " + this.lat );
		//System.err.println("[ ST ] Lon: " + this.lon );
	}
	public void backPort( String s ) {
		if ( s != null ) {
			String input = s.trim();
			if ( input.matches("lon" ) ) {
				this.outputLon();
			} else if ( input.matches("lat" ) ) {
				this.outputLat();
			} else {
				this.pipeout("error:error working out command");
			}
		} else {
			this.running = false;
		}
	}
	public void run() {
		String s;
		while ( this.running ) {
			try {
				s = this.input.readLine();
				this.backPort( s );
			} catch (IOException e) {
				this.running = false;
			}
		}
	}
}

Remember, folks. There are some missing bits. If you want the full code, check it out on Github — here

So far, I took it out for a spin in the car, and it looks good!

Viva La Open-Source!

No Comments

Post a Comment

Your email is never shared. Required fields are marked *