<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff></diff>
      <filename>bin/classes.dex</filename>
    </modified>
    <modified>
      <diff></diff>
      <filename>bin/occyd-android.apk</filename>
    </modified>
    <modified>
      <diff></diff>
      <filename>bin/resources.ap_</filename>
    </modified>
    <modified>
      <diff>@@ -11,6 +11,7 @@
         android:clickable=&quot;true&quot;
         android:apiKey=&quot;insert-your-google-maps-android-api-key-here-and-dont-share-it&quot; 
      /&gt;
+
     &lt;LinearLayout android:id=&quot;@+id/zoom&quot;
         android:layout_width=&quot;wrap_content&quot;
         android:layout_height=&quot;wrap_content&quot;</diff>
      <filename>res/layout/mapview.xml</filename>
    </modified>
    <modified>
      <diff>@@ -20,16 +20,21 @@ package com.pansapiens.occyd;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.net.MalformedURLException;
+import java.net.URL;
 import java.net.URLEncoder;
 
 import org.json.JSONException;
 import org.json.JSONObject;
 
 import android.app.Activity;
+import android.app.Dialog;
+import android.app.ProgressDialog;
 import android.content.Intent;
 import android.location.Location;
 import android.location.LocationManager;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
@@ -38,6 +43,8 @@ import android.widget.Button;
 import android.widget.Toast;
 
 public class NewPost extends Activity {
+	protected static final int DIALOG_POSTING = 0;
+
 	LocationManager locationManager;
 	
     @Override
@@ -90,18 +97,30 @@ public class NewPost extends Activity {
             	double lat = here.getLatitude();
             	double lon = here.getLongitude();
             	
-                Toast.makeText(NewPost.this, &quot;Posting ...&quot;,
-                        Toast.LENGTH_LONG).show();
+                //Toast.makeText(NewPost.this, &quot;Posting ...&quot;,
+                //        Toast.LENGTH_LONG).show();
+            	
             	try {
             		String BASEURL = getString(R.string.occyd_server);
-            		String url = String.format(&quot;%s/v1/posts/add?ll=%f,%f&amp;desc=%s&amp;tags=%s&amp;link=%s&quot;, BASEURL, lat, lon, desc, tags, BASEURL);
+            		String urlstr = String.format(&quot;%s/v1/posts/add?ll=%f,%f&amp;desc=%s&amp;tags=%s&amp;link=%s&quot;, BASEURL, lat, lon, desc, tags, BASEURL);
+            		final URL url = new URL(urlstr);
             		
             		TextView tx = (TextView) findViewById(R.id.new_post_title);
-            		tx.setText(url);
+            		tx.setText(url.toString());
+            		
+            		// run http fetch in a thread, with 
+            		// 'posting' dialog
+        			final Thread tr = new Thread() {
+        				@Override
+        				public void run() {
+        					handle_result.post(mDisplayPosting);
+        					UrlFetch fetcher = new UrlFetch(url, handle_result);
+        					fetcher.fetch();
+        					handle_result.post(mDismissPosting);
+        				}
+        			};
+        			tr.start();
             		
-            		UrlFetch fetcher = new UrlFetch(url);
-            		result_code = new JSONObject(fetcher.fetch())
-            							.getString(&quot;result&quot;);
             		
             	} catch (MalformedURLException e) {
                     Toast.makeText(NewPost.this, &quot;FAIL: Malformed URL.&quot;,
@@ -109,10 +128,24 @@ public class NewPost extends Activity {
         		} catch (IOException e) {
                     Toast.makeText(NewPost.this, &quot;FAIL: http fetch failed.&quot;,
                             Toast.LENGTH_LONG).show();
-        		} catch (JSONException e) {
-        			Toast.makeText(NewPost.this, &quot;FAIL: Malformed response from server.&quot;,
-                            Toast.LENGTH_LONG).show();
-				}
+        		}
+            }
+        });
+    }
+    
+    // result handler - launches / kills
+    // 'waiting' dialogs, recieves http result body
+    final Handler handle_result = new Handler() {
+		@Override
+		public void handleMessage(Message msg) {
+			
+	    	//Toast.makeText(NewPost.this, msg.obj.toString(),
+	        //        Toast.LENGTH_LONG).show();
+			
+			try {
+	    		String result_code = new JSONObject(msg.obj.toString())
+	    									.getString(&quot;result&quot;);
+	    		
         		if (result_code != null &amp;&amp; result_code.equals(&quot;done&quot;)) {
         			Toast.makeText(NewPost.this, &quot;Sighting posted !!&quot;,
                             Toast.LENGTH_LONG).show();
@@ -121,12 +154,44 @@ public class NewPost extends Activity {
                             Toast.LENGTH_LONG).show();
         		}
         		
-        		//TextView tx = (TextView) findViewById(R.id.new_post_title);
-        		//tx.setText(result_code);
+			} catch (JSONException e) {
+    			Toast.makeText(NewPost.this, &quot;FAIL: Malformed response from server.&quot;,
+                        Toast.LENGTH_LONG).show();
+			} 
+		}
+    };
+    
+	final Runnable mDisplayPosting = new Runnable(){
+		public void run(){
+			showDialog(DIALOG_POSTING);
+		}
+	};
+	
+	final Runnable mDismissPosting = new Runnable(){
+		public void run(){
+			try{
+				dismissDialog(DIALOG_POSTING);				
+			} catch(IllegalArgumentException e){
+				return;
+			}
+		}
+	};
+	
+	@Override
+    protected Dialog onCreateDialog(int id) {
+        switch (id) {
+            case DIALOG_POSTING: {
+                ProgressDialog dialog = new ProgressDialog(this);
+                dialog.setTitle(&quot;Posting ...&quot;);
+                dialog.setMessage(&quot;Posting ...&quot;);
+                dialog.setIndeterminate(true);
+                dialog.setCancelable(false);
+                return dialog;
             }
-        });
+        }
+        return null;
     }
-    
+	
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         super.onCreateOptionsMenu(menu);</diff>
      <filename>src/com/pansapiens/occyd/NewPost.java</filename>
    </modified>
    <modified>
      <diff>@@ -17,32 +17,20 @@
 
 package com.pansapiens.occyd;
 
-import java.io.BufferedReader;
-import org.apache.commons.lang.StringUtils;
 import java.io.IOException;
-import java.io.InputStreamReader;
 import java.net.MalformedURLException;
 import java.net.URL;
-import java.net.URLConnection;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.Scanner;
-
-import org.json.JSONArray;
 import org.json.JSONException;
-import org.json.JSONObject;
-import org.json.JSONTokener;
-
-
 import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.DialogInterface.OnCancelListener;
-import android.content.DialogInterface;
+import android.app.Dialog;
+import android.app.ProgressDialog;
 import android.content.Intent;
 import android.location.Location;
 import android.location.LocationManager;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
 import android.util.Log;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -54,6 +42,12 @@ import android.widget.TextView;
 import android.widget.Toast;
 
 public class Search extends Activity implements SeekBar.OnSeekBarChangeListener {
+	private static final int DIALOG_SEARCHING = 0;
+
+	// if true, switch to map view automatically
+	// after recieving search results
+	protected static boolean AUTOSHOW_MAP = true;
+	
 	LocationManager locationManager;
 	
 	EditText query_txt;
@@ -61,11 +55,12 @@ public class Search extends Activity implements SeekBar.OnSeekBarChangeListener
 	SeekBar distanceBar;
 	TextView distanceText;
 	String distance_label;
-	ArrayList&lt;Post&gt; searchResults;
-	String searchResults_json = null;
 	int distance;
 	String current_ll;
 	
+	ArrayList&lt;Post&gt; searchResults;
+	String searchResults_json = null;
+	
     /** Called when the activity is first created. */
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -95,43 +90,66 @@ public class Search extends Activity implements SeekBar.OnSeekBarChangeListener
             	final String q = query_txt.getText().toString();
             	String[] tags = q.split(&quot; &quot;);
             	
-                Toast.makeText(Search.this, &quot;Fetching results ...&quot;,
-                        Toast.LENGTH_SHORT).show();
-                
-                // query server, results returned as json
-                searchResults_json = searchByTag(tags);
-                try {
-                	searchResults = JSONdecoder.json2postArray(searchResults_json);
-                }
-                catch (JSONException e) {
-        			Toast.makeText(Search.this, &quot;FAIL: Malformed response from server.&quot;,
-                            Toast.LENGTH_LONG).show();
-                }
-                
-            	if (searchResults.size() == 0) {
-                    Toast.makeText(Search.this, &quot;Search got zero hits ...&quot;,
-                            Toast.LENGTH_LONG).show();
-            	}
-    			
-            	// TODO: Do it this way: http://code.google.com/android/kb/commontasks.html#binding
-    			// format post_results for output as a list in the TextView tx
-    			String out_string = &quot;&quot;;
-    			for (int i = 0; i &lt; searchResults.size(); i++) {
-    				out_string += searchResults.get(i).tagsToString() + &quot;\n&quot;;
-    		     }
-    			
-    	        tx.setText(out_string);
-    	        Log.i(&quot;Search&quot;, out_string);
-    	        Log.i(&quot;Search json result&quot;, searchResults_json);
-    	        
-    	        // show results on a map, search result data passed as raw json
-    	        if (searchResults.size() &gt; 0) {
-    	        	showMapResults(searchResults_json);
-    	        }
+                //Toast.makeText(Search.this, &quot;Fetching results ...&quot;,
+                //        Toast.LENGTH_SHORT).show();
+        		
+                // query server, results are returned as json
+                // to the handle results method
+                searchByTag(tags);
             }
         });
     }
     
+    // result handler - launches / kills
+    // 'waiting' dialogs, recieves http result body
+    final Handler handle_result = new Handler() {
+		
+		@Override
+		public void handleMessage(Message msg) {
+
+	    	//Toast.makeText(Search.this, msg.obj.toString(),
+	        //        Toast.LENGTH_LONG).show();
+	    	
+	    	if (msg.what == 1) {
+	    		searchResults_json = msg.obj.toString();
+	    	
+		    	try {
+	            	searchResults = JSONdecoder.json2postArray(searchResults_json);
+	            }
+	            catch (JSONException e) {
+	    			Toast.makeText(Search.this, &quot;FAIL: Malformed response from server.&quot;,
+	                        Toast.LENGTH_LONG).show();
+	            }
+	            
+	        	if (searchResults.size() == 0) {
+	                Toast.makeText(Search.this, &quot;Search got zero hits ...&quot;,
+	                        Toast.LENGTH_LONG).show();
+	        	} else {
+				
+		        	// TODO: Do it this way: http://code.google.com/android/kb/commontasks.html#binding
+					// format post_results for output as a list in the TextView tx
+					String out_string = &quot;&quot;;
+					for (int i = 0; i &lt; searchResults.size(); i++) {
+						out_string += searchResults.get(i).tagsToString() + &quot;\n&quot;;
+				     }
+					
+			        tx.setText(out_string);
+			        Log.i(&quot;Search&quot;, out_string);
+			        Log.i(&quot;Search json result&quot;, searchResults_json);
+			        
+			        // show results on a map, search result data passed as raw json
+			        if ((searchResults.size() &gt; 0) &amp;&amp; AUTOSHOW_MAP) {
+			        	showMapResults(searchResults_json);
+			        }
+	        	}
+	        	
+	    	} else {
+	    		Toast.makeText(Search.this, &quot;Search failed (IO error ?) ...&quot;,
+                        Toast.LENGTH_LONG).show();
+	    	}
+		}
+    };
+    
     void showMapResults(String json_result) {
     	// open the MapResults activity ...
     	//mapresultsI.setClassName(&quot;com.pansapiens.occyd&quot;, &quot;com.pansapiens.occyd.MapResults&quot;);
@@ -143,9 +161,25 @@ public class Search extends Activity implements SeekBar.OnSeekBarChangeListener
     	startActivity(mapresultsI);
     }
     
-    public String searchByTag(String[] taglist) {
-    	String json_txt = null;
-    	String tags = StringUtils.join(taglist, &quot;,&quot;);
+    // like the org.apache.commons.lang.StringUtils
+    // for joining strings (['a','b','c'], '-') =&gt; 'a-b-c' 
+    public String joinString(String[] strings, String joiner) {
+    	String outstr = &quot;&quot;;
+    	for (int i = 0; i &lt; strings.length; i++) {
+    		if (i != (strings.length - 1)) {
+    			outstr += strings[i] + joiner;
+    		} else { // for the final token in the list
+    			outstr += strings[i];
+    		}
+    	}
+    	return outstr;
+    }
+    
+    public void searchByTag(String[] taglist) {
+    	String tags = joinString(taglist, &quot;,&quot;);
+    	
+    	final URL url;
+    	UrlFetch fetcher;
     	/*
     	for (int i = 0; i &lt; taglist.length; i++) {
     		tags += taglist[0];
@@ -154,9 +188,9 @@ public class Search extends Activity implements SeekBar.OnSeekBarChangeListener
     	
     	try {
     		String BASEURL = getString(R.string.occyd_server);
-    		UrlFetch fetcher;
     		if (distance == 0) {
-    			fetcher = new UrlFetch(BASEURL+&quot;/v1/posts/get?tag=&quot;+tags+&quot;&amp;format=json&quot;);
+    			url = new URL(BASEURL+&quot;/v1/posts/get?tag=&quot;+tags+&quot;&amp;format=json&quot;);
+    			fetcher = new UrlFetch(url, handle_result);
     		}
     		else {
             	Location here = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
@@ -164,14 +198,27 @@ public class Search extends Activity implements SeekBar.OnSeekBarChangeListener
             	double lon = here.getLongitude();
             	
             	String current_ll = String.format(&quot;%f,%f&quot;, lat, lon);
-            	String urlstr = BASEURL+&quot;/v1/posts/get?tag=&quot;+tags+&quot;&amp;radius=&quot;+distance+&quot;&amp;near=&quot;+current_ll+&quot;&amp;format=json&quot;;
+            	url = new URL(BASEURL+&quot;/v1/posts/get?tag=&quot;+tags+&quot;&amp;radius=&quot;+distance+&quot;&amp;near=&quot;+current_ll+&quot;&amp;format=json&quot;);
                
-            	Toast.makeText(Search.this, urlstr,
+            	Toast.makeText(Search.this, url.toString(),
                         Toast.LENGTH_LONG).show();
                 
-    			fetcher = new UrlFetch(urlstr);
+    			fetcher = new UrlFetch(url, handle_result);
     		}
-    		json_txt = fetcher.fetch();
+    		
+    		// run http fetch in a thread, with 
+    		// 'searching' dialog
+			final Thread tr = new Thread() {
+				@Override
+				public void run() {
+					handle_result.post(mDisplaySearching);
+					UrlFetch fetcher = new UrlFetch(url, handle_result);
+					fetcher.fetch();
+					handle_result.post(mDismissSearching);
+				}
+			};
+			tr.start();
+    		
     	} catch (MalformedURLException e) {
             Toast.makeText(Search.this, &quot;FAIL: Malformed URL.&quot;,
                     Toast.LENGTH_LONG).show();
@@ -179,9 +226,39 @@ public class Search extends Activity implements SeekBar.OnSeekBarChangeListener
             Toast.makeText(Search.this, &quot;FAIL: http fetch failed.&quot;,
                     Toast.LENGTH_LONG).show();
 		}
-		return json_txt;
     }
     
+	final Runnable mDisplaySearching = new Runnable(){
+		public void run(){
+			showDialog(DIALOG_SEARCHING);
+		}
+	};
+	
+	final Runnable mDismissSearching = new Runnable(){
+		public void run(){
+			try{
+				dismissDialog(DIALOG_SEARCHING);				
+			} catch(IllegalArgumentException e){
+				return;
+			}
+		}
+	};
+
+	@Override
+    protected Dialog onCreateDialog(int id) {
+        switch (id) {
+            case DIALOG_SEARCHING: {
+                ProgressDialog dialog = new ProgressDialog(this);
+                dialog.setTitle(&quot;Searching ...&quot;);
+                dialog.setMessage(&quot;Searching ...&quot;);
+                dialog.setIndeterminate(true);
+                dialog.setCancelable(false);
+                return dialog;
+            }
+        }
+        return null;
+    }
+	
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         super.onCreateOptionsMenu(menu);</diff>
      <filename>src/com/pansapiens/occyd/Search.java</filename>
    </modified>
    <modified>
      <diff>@@ -20,35 +20,58 @@ package com.pansapiens.occyd;
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
-import java.net.MalformedURLException;
 import java.net.URL;
-import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.List;
 
-import android.widget.Toast;
+import org.apache.http.Header;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.params.ClientPNames;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
+
+import android.os.Handler;
+import android.os.Message;
 
 public class UrlFetch {
-		URL url;
-	public UrlFetch(String url_str) throws MalformedURLException {
-		url = new URL(url_str);
-	}
-	
-    public String fetch(String url_str) throws MalformedURLException, IOException  {
-        // grab a url
-    	url = new URL(url_str);
-    	return fetch();
-    }
-    // TODO: make fetching threaded (example: http://developerlife.com/tutorials/?p=290 ) ?
-    public String fetch() throws IOException {	
-		String response = null;
+	public String USER_AGENT = &quot;OccydAndroid/1.0&quot;;
+	private final Handler handler;
+	private URL url;
 		
-			URLConnection conn;
-			conn = url.openConnection();
-			conn.setRequestProperty(&quot;User-agent&quot;, &quot;OccydAndroid/1.0&quot;);
+	public UrlFetch(URL uri, Handler result_handler)  {
+		super();
+		url = uri;
+		handler = result_handler;
+	}
+    
+	public void fetch() {
+		String response = &quot;&quot;;
+		Message msg = Message.obtain();
+		msg.what = 0;
+		msg.obj = &quot;empty&quot;;
+		try {
 			
+			// create an http client with a get request
+			// for our url
+			HttpClient httpClient = new DefaultHttpClient();
+			HttpContext localContext = new BasicHttpContext();
+			HttpGet httpGet = new HttpGet(url.toString());
+			
+			// set the User-Agent
+			List&lt;Header&gt; headers=new ArrayList&lt;Header&gt;();
+	        headers.add(new BasicHeader(&quot;User-Agent&quot;, USER_AGENT));
+	        httpClient.getParams().setParameter(ClientPNames.DEFAULT_HEADERS, headers);
+	        
+	        // execute the request
+			HttpResponse resp = httpClient.execute(httpGet, localContext);
 			
 	        BufferedReader in = new BufferedReader(
                     new InputStreamReader(
-                    conn.getInputStream()));
+                    resp.getEntity().getContent()));
 			String inputLine;
 			
 			response = &quot;&quot;;
@@ -56,6 +79,14 @@ public class UrlFetch {
 				response += inputLine;
 			in.close();
 			
-        return response;
-    }
+			msg.what = 1;
+			msg.obj = response;
+				
+		} catch (IOException e) {
+			msg.what = 0;
+			msg.obj = &quot;io exception : &quot; + url.toString();
+		} finally {
+			handler.sendMessage(msg);
+		}
+	}
 }
\ No newline at end of file</diff>
      <filename>src/com/pansapiens/occyd/UrlFetch.java</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>baac87e3432e3c03fcd19ec8e270c50086a033b5</id>
    </parent>
    <parent>
      <id>90c56635f51028f0372e4fb536e4afc2feae0fb3</id>
    </parent>
  </parents>
  <author>
    <name>pansapiens</name>
    <email>ajperry@pansapiens.com</email>
  </author>
  <url>http://github.com/pansapiens/occyd-android/commit/5e7361d12bd6e69d029e9eb23f268bdf662717ac</url>
  <id>5e7361d12bd6e69d029e9eb23f268bdf662717ac</id>
  <committed-date>2009-03-15T02:43:51-07:00</committed-date>
  <authored-date>2009-03-15T02:43:51-07:00</authored-date>
  <message>Merge branch 'threaded', resolved conflicts.</message>
  <tree>c8ccec0c7ccaa77f3c6c5c082b69aba432cabae6</tree>
  <committer>
    <name>pansapiens</name>
    <email>ajperry@pansapiens.com</email>
  </committer>
</commit>
