IP-Geolocator Android Application


Update … IP-Geolocator has been ported as a Google Chrome extension…you may find it here

While most of 2010 was dominated by starting a new job, getting married, and experimenting with Amazons EC2 cloud service … I was able to complete/publish my first android application over the winter holiday. The application is a simple IP – Geolocation service that quickly geolocates an IP address using Google Maps and the Maxmind GeoLite City database. In a nutshell, requests are processed via an Amazon EC2 powered webservice that hosts the GeoLite City database and responses are displayed on the requesters device using Google Maps. The services GeoLite City database has an accuracy of up to 99.5% on a country level and 79% on a city level and uses Open Data License which requires me to note that “This product includes GeoLite data created by MaxMind, available from http://maxmind.com” . Lastly, for those interested in more accurate geo-location, please be sure to visit Maxmind.

Here is a screen shot:

You can find the application in the Android Market by searching for “IP Geo” or download directly via QR:


package com.ericonjava;

public interface Caller {

	public void postRequestHandler(String result);

}
package com.ericonjava;

import java.io.InputStream;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpConnectionParams;

import android.os.AsyncTask;
import android.util.Log;

public class GeoIpTranslateTask extends AsyncTask<String, Void, String> {

	private Caller caller;

	public GeoIpTranslateTask(Caller caller){

		this.caller = caller;
	}

    protected String doInBackground(String... ip) {

        return callGeoIpTranslationService(ip[0]);
    }

    protected void onPostExecute(String result) {

   	 caller.postRequestHandler(result);

    }

    private String callGeoIpTranslationService(String ip){
    	String return_str = "";

    	try{
            HttpClient hc = new DefaultHttpClient();
            HttpGet get = new HttpGet("SERVICE"+ip);
            HttpConnectionParams.setConnectionTimeout(hc.getParams(), 15000);
            HttpResponse rp = hc.execute(get);

            if(rp.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
                InputStream is = rp.getEntity().getContent();
                byte[] readBytes =new byte[512];
                is.read(readBytes);

                return_str = new String(readBytes);

                //Log.v("MYTAG", "response string="+return_str);

            }
            else{
            	return_str = "ERROR- The request failed with status code"+ rp.getStatusLine().getStatusCode();
            }
        }catch(Exception e){
        	return_str = "ERROR- The request to the GEO-IP server failed. Please ensure that a data connection is availible";
            return return_str;
        }

        return return_str;
    }

}
package com.ericonjava;

import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.json.JSONException;
import org.json.JSONObject;

import com.ericonjava.R;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.OverlayItem;

import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class MainView extends MapActivity implements Caller {

	//Regex from http://stackoverflow.com/questions/106179/regular-expression-to-match-hostname-or-ip-address
	private String ValidIpAddressRegex = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$";
	private Pattern pattern;
    private Matcher matcher;

    private Button aButton;

    private MapView  mapView;
    private MainView ref;

    private String ipaddress;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ref = this;
        setContentView(R.layout.main);

        pattern = Pattern.compile(ValidIpAddressRegex);

        mapView = (MapView) findViewById(R.id.mapview);
        mapView.setBuiltInZoomControls(true);

        aButton = (Button) findViewById(R.id.ok);
        aButton.setOnClickListener(new View.OnClickListener() {

            public void onClick(View v) {

            	EditText stxt = (EditText) findViewById(R.id.entry);
            	ipaddress = stxt.getText().toString().trim();

            	if(validateIPString(ipaddress)){
            		aButton.setEnabled(false);
            		new GeoIpTranslateTask(ref).execute(ipaddress);

            	}else{
            		Toast toast = Toast.makeText(getApplicationContext(), "Please enter a valid IP address", 30);
            		toast.show();
            	}
            } });

    }

    public boolean validateIPString(final String ip){
  	  matcher = pattern.matcher(ip);
  	  return matcher.matches();
      }

	private GeoPoint getPoint(double lat, double lon) {
		//GeoPoint gp = new GeoPoint((int)(lat * 1E6), (int)(lng * 1E6));
		return(new GeoPoint((int)(lat*1000000.0),(int)(lon*1000000.0)));
	}

	@Override
	protected boolean isRouteDisplayed() {
		return false;
	}

	public void postRequestHandler(String result){

		 aButton.setEnabled(true);

		 JSONObject json_obj;

			try{

				json_obj = new JSONObject(result);

	        String slat=json_obj.getString("latitude");
	        String slong=json_obj.getString("longitude");
	        String countryname=json_obj.getString("country_name");
	        String city=json_obj.getString("city");
	        String region=json_obj.getString("region");

	        double lat = Double.parseDouble(slat);
	        double lon = Double.parseDouble(slong);

	        GeoPoint gp = getPoint(lat,lon);
	        mapView.getController().setZoom(9);
	        mapView.getController().animateTo(gp);

	        List<Overlay> mapOverlays = mapView.getOverlays();
	        Drawable drawable = this.getResources().getDrawable(R.drawable.marker);
	        SitesOverlay itemizedoverlay = new SitesOverlay(drawable,getApplicationContext());

	        OverlayItem overlayitem = new OverlayItem(gp,ipaddress,ipaddress);

	        itemizedoverlay.addOverlay(overlayitem);
	        mapOverlays.add(itemizedoverlay);

	        TextView textView = (TextView) findViewById(R.id.display);
	        textView.setText("Longitude = "+ slat + " nLongitude = " +slong+" nCity = " +city+" nRegion = " +region+" nCountry = " +countryname);
	        textView.setHorizontalScrollBarEnabled(true);

			Toast toast = Toast.makeText(getApplicationContext(), "Request has completed succesfully", 3);
			toast.show();

			} catch (JSONException e) {
				Toast toast = Toast.makeText(getApplicationContext(),"Unable to process request...please try again", 10);
				toast.show();

			}

	}

}
package com.ericonjava;

import java.util.ArrayList;
import java.util.List;

import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.MapView;
import com.google.android.maps.OverlayItem;

import android.app.AlertDialog;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.widget.Toast;

public class SitesOverlay extends ItemizedOverlay<OverlayItem> {
private Context mContext;

	private List<OverlayItem> items=new ArrayList<OverlayItem>();

	public SitesOverlay(Drawable defaultMarker,Context context) {
		super(boundCenterBottom(defaultMarker));
		mContext = context;;

	}

	@Override
	protected OverlayItem createItem(int i) {
		return(items.get(i));
	}
	@Override
	public int size() {
	  return items.size();
	}
	@Override
	public void draw(Canvas canvas, MapView mapView,boolean shadow) {
		super.draw(canvas, mapView, shadow);

	}

	@Override
	protected boolean onTap(int index) {
	  OverlayItem item = items.get(index);
	  Toast toast = Toast.makeText(mContext,item.getTitle(), 10);
      toast.show();
	  return true;
	}

	public void addOverlay(OverlayItem overlay) {
		items.add(overlay);
	    populate();
	}

}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView
        android:id="@+id/label"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Enter IP address here: (e.g. 25.36.88.111)"/>
    <EditText
        android:id="@+id/entry"
        android:layout_width="225dip"
        android:layout_height="wrap_content"
        android:background="@android:drawable/editbox_background"
        android:layout_below="@id/label"/>
    <Button
        android:id="@+id/ok"
        android:layout_width="70dip"
        android:layout_height="50dip"
        android:layout_toRightOf="@id/entry"
        android:layout_below="@id/label"
        android:layout_marginLeft="10dip"
        android:text="OK" />

     <com.google.android.maps.MapView
        android:id="@+id/mapview"
        android:layout_marginTop="10dip"
        android:layout_below="@id/ok"
        android:layout_width="wrap_content"
        android:layout_height="200dip"
        android:clickable="true"
        android:apiKey="0Pguc-Ti7nkyeaRXxqNZUOsJeyvYplrjGuL5mHg"
    />
        <TextView
        android:id="@+id/display"
        android:layout_below="@id/mapview"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text=""/>

       <TextView
        android:id="@+id/disclaimer"
        android:layout_alignParentBottom ="true"
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"
        android:textSize="8px"
        android:text="This product includes GeoLite data created by MaxMind, available from http://www.maxmind.com/."/>

</RelativeLayout>

Complete Project / Source code is posted on Github.