com.pansapiens.occyd.Search.java Source code

Java tutorial

Introduction

Here is the source code for com.pansapiens.occyd.Search.java

Source

//    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

    }
}