Java tutorial
// This file is part of the Occyd Android Client // - an Android application for geolocation tagging // Copyright (C) 2008, Andrew Perry (ajperry@pansapiens.com) // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program 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 Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. package com.pansapiens.occyd; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import org.json.JSONException; 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.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.SeekBar; 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; TextView tx; SeekBar distanceBar; TextView distanceText; String distance_label; int distance; String current_ll; ArrayList<Post> searchResults; String searchResults_json = null; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // start polling location ASAP, so it can be accurate by the time // the post button is pressed locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); LocationUpdater locupdater = new LocationUpdater(locationManager); locupdater.startUpdating(); setContentView(R.layout.search); tx = (TextView) findViewById(R.id.txt); query_txt = (EditText) findViewById(R.id.query_txt); distanceBar = (SeekBar) findViewById(R.id.distance_seek); distanceBar.setOnSeekBarChangeListener(this); distanceText = (TextView) findViewById(R.id.distance); distance = 20; distance_label = "20 m"; distanceText.setText("Within " + distance_label + " of current location."); final Button search_button = (Button) findViewById(R.id.search_button); search_button.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { // grab query from the text input box final String q = query_txt.getText().toString(); String[] tags = q.split(" "); //Toast.makeText(Search.this, "Fetching results ...", // 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, "FAIL: Malformed response from server.", Toast.LENGTH_LONG).show(); } if (searchResults.size() == 0) { Toast.makeText(Search.this, "Search got zero hits ...", 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 = ""; for (int i = 0; i < searchResults.size(); i++) { out_string += searchResults.get(i).tagsToString() + "\n"; } tx.setText(out_string); Log.i("Search", out_string); Log.i("Search json result", searchResults_json); // show results on a map, search result data passed as raw json if ((searchResults.size() > 0) && AUTOSHOW_MAP) { showMapResults(searchResults_json); } } } else { Toast.makeText(Search.this, "Search failed (IO error ?) ...", Toast.LENGTH_LONG).show(); } } }; void showMapResults(String json_result) { // open the MapResults activity ... //mapresultsI.setClassName("com.pansapiens.occyd", "com.pansapiens.occyd.MapResults"); Intent mapresultsI = new Intent(this, MapResults.class); if (json_result != null) { mapresultsI.putExtra("json", json_result); } Toast.makeText(Search.this, "Finding location, mapping.", Toast.LENGTH_SHORT).show(); startActivity(mapresultsI); } // like the org.apache.commons.lang.StringUtils // for joining strings (['a','b','c'], '-') => 'a-b-c' public String joinString(String[] strings, String joiner) { String outstr = ""; for (int i = 0; i < 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, ","); final URL url; UrlFetch fetcher; /* for (int i = 0; i < taglist.length; i++) { tags += taglist[0]; } */ try { String BASEURL = getString(R.string.occyd_server); if (distance == 0) { url = new URL(BASEURL + "/v1/posts/get?tag=" + tags + "&format=json"); fetcher = new UrlFetch(url, handle_result); } else { Location here = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); double lat = here.getLatitude(); double lon = here.getLongitude(); String current_ll = String.format("%f,%f", lat, lon); url = new URL(BASEURL + "/v1/posts/get?tag=" + tags + "&radius=" + distance + "&near=" + current_ll + "&format=json"); Toast.makeText(Search.this, url.toString(), Toast.LENGTH_LONG).show(); fetcher = new UrlFetch(url, handle_result); } // 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, "FAIL: Malformed URL.", Toast.LENGTH_LONG).show(); } catch (IOException e) { Toast.makeText(Search.this, "FAIL: http fetch failed.", Toast.LENGTH_LONG).show(); } } 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("Searching ..."); dialog.setMessage("Searching ..."); dialog.setIndeterminate(true); dialog.setCancelable(false); return dialog; } } return null; } @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); // group, id (for click handling), order, title int order = Menu.CATEGORY_CONTAINER + Menu.FLAG_ALWAYS_PERFORM_CLOSE; //menu.add(Menu.NONE, 0, order, "Search").setAlphabeticShortcut('s'); ; //menu.add(Menu.NONE, 1, order, "Post").setAlphabeticShortcut('p'); ; menu.add(Menu.NONE, 2, order, "Map").setAlphabeticShortcut('m'); menu.add(Menu.NONE, 3, order, "Settings"); menu.add(Menu.NONE, 4, order, "Help").setAlphabeticShortcut('h'); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { Intent I; switch (item.getItemId()) { case 0: I = new Intent(this, Search.class); break; case 1: I = new Intent(this, NewPost.class); break; case 2: // if we launch the map, leave this Search activity // on the history stack (ie no call of this.finish() ) // so that the back button brings us from the map back to here showMapResults(searchResults_json); return true; case 3: startActivity(new Intent(this, Prefs.class)); return true; case 4: //showAlert("Menu Item Clicked", "Help", "ok", null, false, null); return true; default: return false; } startActivity(I); this.finish(); return true; } public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) { // change labels with slider if (progress >= 10) { distance = 20; distance_label = "20 m"; } if (progress > 30) { distance = 150; distance_label = "150 m"; } if (progress > 50) { distance = 600; distance_label = "600 m"; } if (progress > 70) { distance = 4800; distance_label = "4.8 km"; } if (progress > 80) { distance = 19500; distance_label = "19.5 km"; } if (progress >= 90) { distance = 150000; distance_label = "150 km"; } if (progress >= 95) { distance = 0; distance_label = "any distance"; } distanceText.setText("Within " + distance_label + " of current location."); } public void onStartTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub } public void onStopTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub } }