com.code.android.vibevault.SearchScreen.java Source code

Java tutorial

Introduction

Here is the source code for com.code.android.vibevault.SearchScreen.java

Source

/*
 * SearchScreen.java
 * VERSION 3.X
 * 
 * Copyright 2011 Andrew Pearson and Sanders DeNardi.
 * 
 * This file is part of Vibe Vault.
 * 
 * Vibe Vault is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * 
 */

package com.code.android.vibevault;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Calendar;
import java.util.List;

import org.apache.http.util.ByteArrayBuffer;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.os.Vibrator;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.util.TypedValue;
import android.view.ContextMenu;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.view.View.OnCreateContextMenuListener;
import android.view.View.OnKeyListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.SeekBar;
import android.widget.SlidingDrawer;
import android.widget.SlidingDrawer.OnDrawerScrollListener;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.SlidingDrawer.OnDrawerCloseListener;
import android.widget.SlidingDrawer.OnDrawerOpenListener;

import com.code.android.vibevault.R;

public class SearchScreen extends Activity {

    private static final String LOG_TAG = SearchScreen.class.getName();

    protected ListView searchList;
    protected File appRootDir;
    protected TextView labelText;

    /* SlidingDrawer Members... */
    protected SlidingDrawer searchDrawer;
    protected TextView handleText;
    protected AutoCompleteTextView artistSearchInput;
    protected EditText yearSearchInput;
    protected Spinner dateModifierSpinner;
    protected ArrayAdapter<CharSequence> spinnerAdapter;
    protected Button searchButton;
    //protected Button searchMoreButton;
    protected Button settingsButton;
    protected Button clearButton;

    private int pageNum = 1;

    private JSONQueryTask workerTask;
    private boolean dialogShown;

    private Vibrator vibrator;

    /** Persist worker Thread across orientation changes.
    *
    * Includes Thread bookkeeping to prevent not leaking Views on orientation changes.
    */
    @Override
    public Object onRetainNonConfigurationInstance() {
        workerTask.setActivity(null);
        return workerTask;
    }

    /** Dialog preparation method.
    *
    * Includes Thread bookkeeping to prevent not leaking Views on orientation changes.
    */
    @Override
    protected void onPrepareDialog(int id, Dialog dialog) {
        super.onPrepareDialog(id, dialog);
        if (id == VibeVault.SEARCHING_DIALOG_ID) {
            dialogShown = true;
        }
    }

    @Override
    public void onBackPressed() {
        if (searchDrawer != null && searchDrawer.isOpened()) {
            vibrator.vibrate(25);
            searchDrawer.close();
            return;
        } else {
            super.onBackPressed();
            return;
        }
    }

    /** Dialog creation method.
    *
    * Includes Thread bookkeeping to prevent not leaking Views on orientation changes.
    */
    @Override
    protected Dialog onCreateDialog(int id) {
        switch (id) {
        case VibeVault.SEARCHING_DIALOG_ID:

            ProgressDialog dialog = new ProgressDialog(this);
            dialog.setMessage("Finding Shows");
            return dialog;
        default:
            return super.onCreateDialog(id);
        }
    }

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.search_screen);

        this.searchList = (ListView) this.findViewById(R.id.ResultsListView);
        this.artistSearchInput = (AutoCompleteTextView) this.findViewById(R.id.ArtistSearchBox);
        this.yearSearchInput = (EditText) this.findViewById(R.id.YearSearchBox);
        this.dateModifierSpinner = (Spinner) this.findViewById(R.id.DateSearchSpinner);
        this.searchButton = (Button) this.findViewById(R.id.SearchButton);
        this.settingsButton = (Button) this.findViewById(R.id.SettingsButton);
        //this.searchMoreButton = (Button) this.findViewById(R.id.SearchMoreButton);
        this.clearButton = (Button) this.findViewById(R.id.ClearButton);
        this.searchDrawer = (SlidingDrawer) this.findViewById(R.id.SlidingDrawerSearchScreen);
        this.handleText = (TextView) this.findViewById(R.id.HandleTextView);

        vibrator = (Vibrator) this.getSystemService(VIBRATOR_SERVICE);

        searchList.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
            @Override
            public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
                menu.add(Menu.NONE, VibeVault.EMAIL_LINK, Menu.NONE, "Email Link to Show");
                menu.add(Menu.NONE, VibeVault.SHOW_INFO, Menu.NONE, "Show Info");
                menu.add(Menu.NONE, VibeVault.ADD_TO_FAVORITE_LIST, Menu.NONE, "Bookmark Show");
            }
        });

        Object retained = getLastNonConfigurationInstance();
        if (retained instanceof JSONQueryTask) {

            workerTask = (JSONQueryTask) retained;
            workerTask.setActivity(this);
        } else {
            workerTask = new JSONQueryTask(this);
        }
        if (VibeVault.searchResults.size() != 0) {
            searchButton.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.morebutton),
                    null, null, null);
            searchButton.setText("More");
            //this.clearButton.setEnabled(true);
        } else {
            searchButton.setCompoundDrawablesWithIntrinsicBounds(
                    getResources().getDrawable(R.drawable.searchbutton_plain), null, null, null);
            searchButton.setText("Search");
        }
        this.init();

        Intent intent = getIntent();
        if (intent.hasExtra("Artist")) {
            browseArtist(intent.getStringExtra("Artist"));
        }

        if (!VibeVault.db.getPref("artistUpdate").equals("2010-01-01")) {
            artistSearchInput.setAdapter(
                    new ArrayAdapter<String>(this, R.layout.artist_search_row, VibeVault.db.getArtistsStrings()));
        }

        if (VibeVault.searchPref.equals("Show/Artist Description") && artistSearchInput.getText().equals("")) {
            artistSearchInput.setHint("Search Descriptions...");
        }
    }

    public void browseArtist(String artist) {
        artistSearchInput.setText(artist);
        VibeVault.searchResults.clear();
        VibeVault.artistSearchText = artist;
        yearSearchInput.setText("");
        if (setDate()) {
            executeSearch(makeSearchURLString(1));
            pageNum = 1;
            searchDrawer.close();
        }
    }

    /** Bookkeeping method to deal with dialogs over orientation changes.
    *
    */
    private void onTaskCompleted() {
        if (dialogShown) {
            try {
                dismissDialog(VibeVault.SEARCHING_DIALOG_ID);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            }
            dialogShown = false;
        }
    }

    /** Handle user's long-click selection.
    *
    */
    @Override
    public boolean onContextItemSelected(MenuItem item) {
        AdapterContextMenuInfo menuInfo = (AdapterContextMenuInfo) item.getMenuInfo();
        if (menuInfo != null) {
            ArchiveShowObj selShow = (ArchiveShowObj) searchList.getAdapter().getItem(menuInfo.position);
            switch (item.getItemId()) {
            case VibeVault.EMAIL_LINK:
                final Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
                emailIntent.setType("plain/text");
                emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT,
                        "Great show on archive.org: " + selShow.getArtistAndTitle());
                emailIntent.putExtra(android.content.Intent.EXTRA_TEXT,
                        "Hey,\n\nYou should listen to " + selShow.getArtistAndTitle() + ".  You can find it here: "
                                + selShow.getShowURL() + "\n\nSent using VibeVault for Android.");
                startActivity(Intent.createChooser(emailIntent, "Send mail..."));
                return true;
            case VibeVault.SHOW_INFO:
                AlertDialog.Builder ad = new AlertDialog.Builder(this);
                ad.setTitle("Show Info");
                View v = LayoutInflater.from(this).inflate(R.layout.scrollable_dialog, null);
                ((TextView) v.findViewById(R.id.DialogText)).setText(selShow.getSource());
                ad.setPositiveButton("Okay.", new android.content.DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int arg1) {
                    }
                });
                ad.setView(v);
                ad.show();
                return true;
            case (VibeVault.ADD_TO_FAVORITE_LIST):
                VibeVault.db.insertFavoriteShow(selShow);
                return true;
            default:
                return false;
            }
        }
        return false;
    }

    public void onResume() {
        super.onResume();
        this.refreshSearchList();
    }

    public void onPause() {
        super.onPause();
    }

    // You can only call execute() once on an AsyncTask.
    // This makes a new AsyncTask and calls execute on it.
    private void executeSearch(String query) {
        workerTask = new JSONQueryTask(this);
        workerTask.execute(query);
    }

    private void launchSettingsDialog() {
        final SeekBar seek;
        final Spinner sortSpin;
        final Spinner searchSpin;
        final TextView seekValue;

        // Make the settings dialog.
        AlertDialog.Builder ad = new AlertDialog.Builder(this);
        ad.setTitle("Search Settings");
        View v = LayoutInflater.from(this).inflate(R.layout.scrollable_settings_dialog, null);
        ad.setPositiveButton("Okay.", new android.content.DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int arg1) {
            }
        });

        // Grab all the GUI widgets.
        seek = (SeekBar) v.findViewById(R.id.NumResultsSeekBar);
        seek.setProgress(Integer.valueOf(VibeVault.db.getPref("numResults")) - 10);
        sortSpin = (Spinner) v.findViewById(R.id.SortSpinner);
        searchSpin = (Spinner) v.findViewById(R.id.SearchSpinner);
        seekValue = (TextView) v.findViewById(R.id.SeekBarValue);
        seekValue.setText(VibeVault.db.getPref("numResults"));

        // Set the seek bar to its current value, and set up a Listener.
        seek.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                seekValue.setText(String.valueOf(progress + 10));
                VibeVault.db.updatePref("numResults", String.valueOf(progress + 10));
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
            }
        });

        // Set up the spinner, and set up it's OnItemSelectedListener.
        ArrayAdapter<String> sortAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item,
                VibeVault.sortChoices);
        sortAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        sortSpin.setAdapter(sortAdapter);
        int sortPos = 1;
        String sortOrder = VibeVault.db.getPref("sortOrder");
        for (int i = 0; i < VibeVault.sortChoices.length; i++) {
            if (VibeVault.sortChoices[i].equals(sortOrder))
                sortPos = i;
        }
        sortSpin.setSelection(sortPos);
        sortSpin.setOnItemSelectedListener(new OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> arg0, View view, int arg2, long arg3) {
                int selected = arg0.getSelectedItemPosition();
                VibeVault.db.updatePref("sortOrder", VibeVault.sortChoices[selected]);
            }

            @Override
            public void onNothingSelected(AdapterView<?> arg0) {
            }

        });

        ArrayAdapter<String> searchAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item,
                VibeVault.searchChoices);
        searchAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        searchSpin.setAdapter(searchAdapter);
        int searchPos = 1;
        String searchOrder = VibeVault.searchPref;
        for (int i = 0; i < VibeVault.searchChoices.length; i++) {
            if (VibeVault.searchChoices[i].equals(searchOrder))
                searchPos = i;
        }
        searchSpin.setSelection(searchPos);
        searchSpin.setOnItemSelectedListener(new OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> arg0, View view, int arg2, long arg3) {
                int selected = arg0.getSelectedItemPosition();
                VibeVault.searchPref = VibeVault.searchChoices[selected];
                if (VibeVault.searchPref.equals("Artist") && artistSearchInput.getText().equals("")) {
                    artistSearchInput.setHint("Search Artists...");
                } else if (VibeVault.searchPref.equals("Show/Artist Description")
                        && artistSearchInput.getText().equals("")) {
                    artistSearchInput.setHint("Search Descriptions...");
                }
            }

            @Override
            public void onNothingSelected(AdapterView<?> arg0) {
            }

        });

        // Show the settings screen.
        ad.setView(v);
        ad.show();
    }

    /** Returns true if a valid date, or no date at all, was passed.
     * Returns false if an improper date is passed.
     */
    private boolean setDate() {
        String year = yearSearchInput.getText().toString();
        if (year.equals("")) {
            VibeVault.yearSearchInt = -1;
            return true;
        }
        int yearInt = Integer.valueOf(year);
        if (yearInt >= 1800 && yearInt <= Calendar.getInstance().get(Calendar.YEAR)) {
            VibeVault.yearSearchInt = yearInt;
            return true;
        } else {
            Toast.makeText(getBaseContext(), "Year must be between now and 1800...", Toast.LENGTH_SHORT).show();
            return false;
        }
    }

    private boolean isMoreSearch(String artist, String year) {
        if (VibeVault.searchResults.size() == 0) {
            return false;
        }
        // Explanation:
        // If the artist input field is the same as the stored artist search text AND
        // The year input field is the same as the stored year search int (or if the input is blank, the int is -1),
        // Return true because you should be fetching more results.  Otherwise, false to search initially.
        if (artist.equalsIgnoreCase(VibeVault.artistSearchText)
                && ((year.equals("") && VibeVault.yearSearchInt == -1)
                        || ((!year.equals("")) && (Integer.valueOf(year) == VibeVault.yearSearchInt)))) {
            return true;
        } else {
            return false;
        }
    }

    private void init() {

        // Set up the date selection spinner.
        spinnerAdapter = ArrayAdapter.createFromResource(this, R.array.date_modifier,
                android.R.layout.simple_spinner_item);
        spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        dateModifierSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
                VibeVault.dateSearchModifierPos = pos;
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {
            }
        });
        dateModifierSpinner.setAdapter(spinnerAdapter);
        dateModifierSpinner.setSelection(VibeVault.dateSearchModifierPos);

        searchDrawer.setOnDrawerScrollListener(new OnDrawerScrollListener() {

            @Override
            public void onScrollEnded() {
            }

            @Override
            public void onScrollStarted() {
                vibrator.vibrate(50);
            }

        });
        artistSearchInput.addTextChangedListener(new TextWatcher() {

            @Override
            public void afterTextChanged(Editable s) {
                if (isMoreSearch(s.toString(), yearSearchInput.getText().toString())) {
                    searchButton.setCompoundDrawablesWithIntrinsicBounds(
                            getResources().getDrawable(R.drawable.morebutton), null, null, null);
                    searchButton.setText("More");
                } else {
                    searchButton.setCompoundDrawablesWithIntrinsicBounds(
                            getResources().getDrawable(R.drawable.searchbutton_plain), null, null, null);
                    searchButton.setText("Search");
                }
            }

            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
            }

        });
        yearSearchInput.addTextChangedListener(new TextWatcher() {

            @Override
            public void afterTextChanged(Editable s) {
                if (isMoreSearch(artistSearchInput.getText().toString(), s.toString())) {
                    searchButton.setCompoundDrawablesWithIntrinsicBounds(
                            getResources().getDrawable(R.drawable.morebutton), null, null, null);
                    searchButton.setText("More");
                } else {
                    searchButton.setCompoundDrawablesWithIntrinsicBounds(
                            getResources().getDrawable(R.drawable.searchbutton_plain), null, null, null);
                    searchButton.setText("Search");
                }
            }

            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
            }

        });
        searchDrawer.setOnDrawerOpenListener(new OnDrawerOpenListener() {
            @Override
            public void onDrawerOpened() {
                searchList.setBackgroundDrawable(getResources().getDrawable(R.drawable.backgrounddrawableblue));
                searchList.getBackground().setDither(true);
                searchList.setEnabled(false);
                handleText.setText("Search Panel");
                handleText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
                artistSearchInput.setText(VibeVault.artistSearchText);

                if (VibeVault.yearSearchInt != -1) {
                    yearSearchInput.setText(String.valueOf(VibeVault.yearSearchInt));
                } else {
                    yearSearchInput.setText("");
                }
                dateModifierSpinner.setSelection(VibeVault.dateSearchModifierPos);
            }
        });
        searchDrawer.setOnDrawerCloseListener(new OnDrawerCloseListener() {
            @Override
            public void onDrawerClosed() {
                searchList.setBackgroundColor(Color.BLACK);
                searchList.setEnabled(true);
                handleText.setText("Drag up to search...");
                handleText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 28);
            }
        });

        // Set listeners in the show details and artist search bars for the enter key.
        OnKeyListener enterListener = new OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
                    if (!(artistSearchInput.getText().toString().equals(""))) {
                        VibeVault.artistSearchText = artistSearchInput.getText().toString();
                        if (setDate()) {
                            executeSearch(makeSearchURLString(1));
                            pageNum = 1;
                            searchDrawer.close();
                            return true;
                        }
                    }
                }
                return false;
            }
        };
        this.artistSearchInput.setOnKeyListener(enterListener);

        this.searchButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {

                String query = artistSearchInput.getText().toString();
                // Blank
                if (query.equals("")) {
                    vibrator.vibrate(50);
                    Toast.makeText(getBaseContext(), "You need a query first...", Toast.LENGTH_SHORT).show();
                    return;
                }
                // Search more
                else if (isMoreSearch(artistSearchInput.getText().toString(),
                        yearSearchInput.getText().toString())) {
                    searchButton.setCompoundDrawablesWithIntrinsicBounds(
                            getResources().getDrawable(R.drawable.morebutton), null, null, null);
                    searchButton.setText("More");
                    dateModifierSpinner.setSelection(VibeVault.dateSearchModifierPos);
                    // pageNum is incremented then searched with to get the next
                    // page.
                    executeSearch(makeSearchURLString(++pageNum));
                    vibrator.vibrate(50);
                    searchDrawer.close();
                }
                // New search
                else {
                    searchButton.setCompoundDrawablesWithIntrinsicBounds(
                            getResources().getDrawable(R.drawable.searchbutton_plain), null, null, null);
                    searchButton.setText("Search");
                    VibeVault.searchResults.clear();
                    VibeVault.artistSearchText = artistSearchInput.getText().toString();
                    if (setDate()) {
                        // "1" is passed to retrieve page number 1.
                        vibrator.vibrate(50);
                        executeSearch(makeSearchURLString(1));
                        pageNum = 1;
                        searchDrawer.close();
                    }
                }

            }
        });

        this.settingsButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                launchSettingsDialog();
                vibrator.vibrate(50);
            }
        });

        this.clearButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                artistSearchInput.setText("");
                yearSearchInput.setText("");
                VibeVault.artistSearchText = "";
                VibeVault.yearSearchInt = -1;
                searchButton.setCompoundDrawablesWithIntrinsicBounds(
                        getResources().getDrawable(R.drawable.searchbutton_plain), null, null, null);
                searchButton.setText("Search");
                VibeVault.searchResults.clear();
                refreshSearchList();
                vibrator.vibrate(50);
            }
        });

        searchList.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> a, View v, int position, long id) {
                ArchiveShowObj show = (ArchiveShowObj) searchList.getItemAtPosition(position);
                Intent i = new Intent(SearchScreen.this, ShowDetailsScreen.class);
                i.putExtra("Show", show);
                startActivity(i);
            }
        });

        // Create the directory for our app if it don't exist.
        appRootDir = new File(Environment.getExternalStorageDirectory(), VibeVault.APP_DIRECTORY);
        if (!appRootDir.isFile() || !appRootDir.isDirectory()) {
            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                appRootDir.mkdirs();
            } else {
                Toast.makeText(getBaseContext(), "sdcard is unwritable...  is it mounted on the computer?",
                        Toast.LENGTH_SHORT).show();
            }
        }
        artistSearchInput.setText(VibeVault.artistSearchText);
        if (VibeVault.yearSearchInt != -1) {
            yearSearchInput.setText(String.valueOf(VibeVault.yearSearchInt));
        } else {
            yearSearchInput.setText("");
        }
        dateModifierSpinner.setSelection(VibeVault.dateSearchModifierPos);

    }

    private String makeSearchURLString(int pageNum) {
        int numResults = Integer.valueOf(VibeVault.db.getPref("numResults"));
        String sortPref = VibeVault.db.getPref("sortOrder");
        if (sortPref.equalsIgnoreCase("Date")) {
            sortPref = "date+desc";
        } else if (sortPref.equalsIgnoreCase("Rating")) {
            sortPref = "avg_rating+desc";
        }
        String queryString = null;

        try {
            String dateModifier = "";
            if (VibeVault.yearSearchInt != -1) {
                switch (VibeVault.dateSearchModifierPos) {
                case 0: //Before
                    dateModifier = "date:[1800-01-01%20TO%20" + VibeVault.yearSearchInt + "-01-01]%20AND%20";
                    break;
                case 1: //After
                    int curDate = Calendar.getInstance().get(Calendar.DATE);
                    int curMonth = Calendar.getInstance().get(Calendar.MONTH);
                    int curYear = Calendar.getInstance().get(Calendar.YEAR);
                    dateModifier = "date:[" + VibeVault.yearSearchInt + "-01-01%20TO%20" + curYear + "-"
                            + String.format("%02d", curMonth) + "-" + String.format("%02d", curDate) + "]%20AND%20";
                    break;
                case 2: // In Year.
                    dateModifier = "date:[" + VibeVault.yearSearchInt + "-01-01%20TO%20" + VibeVault.yearSearchInt
                            + "-12-31]%20AND%20";
                    break;
                }
            }
            // We search creator:(random's artist)%20OR%20creator(randoms artist) because
            // archive.org does not like apostrophes in the creator query.
            String specificSearch = "";
            if (VibeVault.searchPref.equals("Artist")) {
                specificSearch = "(creator:(" + URLEncoder.encode(VibeVault.artistSearchText, "UTF-8") + ")"
                        + "%20OR%20creator:("
                        + URLEncoder.encode(VibeVault.artistSearchText.replace("'", "").replace("\"", ""), "UTF-8")
                        + "))";
            } else if (VibeVault.searchPref.equals("Show/Artist Description")) {
                specificSearch = "(creator:(" + URLEncoder.encode(VibeVault.artistSearchText, "UTF-8") + ")"
                        + "%20OR%20description:("
                        + URLEncoder.encode(VibeVault.artistSearchText.replace("'", "").replace("\"", ""), "UTF-8")
                        + "))";
            }
            String mediaType = "mediatype:(etree)";

            queryString = "http://www.archive.org/advancedsearch.php?q=" + "(" + dateModifier + mediaType
                    + "%20AND%20(" + specificSearch + "))"
                    + "&fl[]=date&fl[]=avg_rating&fl[]=source&fl[]=format&fl[]=identifier&fl[]=mediatype&fl[]=title&sort[]="
                    + sortPref + "&sort[]=&sort[]=&rows=" + String.valueOf(numResults) + "&page="
                    + String.valueOf(pageNum) + "&output=json&callback=callback&save=yes";
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return queryString;
    }

    private void refreshSearchList() {
        searchList.setAdapter(new RatingsAdapter(this, R.layout.search_list_row, VibeVault.searchResults));
        if (VibeVault.searchResults.size() != 0) {
            searchButton.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.morebutton),
                    null, null, null);
            searchButton.setText("More");
            //clearButton.setEnabled(true);
        } else {
            searchDrawer.open();
            searchButton.setText("Search");
            searchButton.setCompoundDrawablesWithIntrinsicBounds(
                    getResources().getDrawable(R.drawable.searchbutton_plain), null, null, null);
            //clearButton.setEnabled(false);
        }
    }

    // ArrayAdapter for the ListView of shows with ratings.
    private class RatingsAdapter extends ArrayAdapter<ArchiveShowObj> {

        public RatingsAdapter(Context context, int textViewResourceId, List<ArchiveShowObj> objects) {
            super(context, textViewResourceId, objects);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ArchiveShowObj show = (ArchiveShowObj) searchList.getItemAtPosition(position);

            if (convertView == null) {
                LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                convertView = vi.inflate(R.layout.search_list_row, null);
            }
            TextView artistText = (TextView) convertView.findViewById(R.id.ArtistText);
            TextView showText = (TextView) convertView.findViewById(R.id.ShowText);
            ImageView ratingsIcon = (ImageView) convertView.findViewById(R.id.rating);
            if (show != null) {
                artistText.setText(show.getShowArtist() + " ");
                artistText.setSelected(true);
                showText.setText(show.getShowTitle());
                showText.setSelected(true);
                switch ((int) show.getRating()) {
                case 1:
                    ratingsIcon.setImageDrawable(getBaseContext().getResources().getDrawable(R.drawable.star1));
                    break;
                case 2:
                    ratingsIcon.setImageDrawable(getBaseContext().getResources().getDrawable(R.drawable.star2));
                    break;
                case 3:
                    ratingsIcon.setImageDrawable(getBaseContext().getResources().getDrawable(R.drawable.star3));
                    break;
                case 4:
                    ratingsIcon.setImageDrawable(getBaseContext().getResources().getDrawable(R.drawable.star4));
                    break;
                case 5:
                    ratingsIcon.setImageDrawable(getBaseContext().getResources().getDrawable(R.drawable.star5));
                    break;
                default:
                    ratingsIcon.setImageDrawable(getBaseContext().getResources().getDrawable(R.drawable.star0));
                    break;
                }
            }
            return convertView;
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.help_recentshows_nowplaying_options, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.nowPlaying: //Open playlist activity
            Intent np = new Intent(SearchScreen.this, NowPlayingScreen.class);
            //Intent np = new Intent(SearchScreen.this, HomeScreen.class);

            startActivity(np);
            break;
        case R.id.recentShows:
            Intent rs = new Intent(SearchScreen.this, RecentShowsScreen.class);

            startActivity(rs);
            break;
        case R.id.scrollableDialog:
            AlertDialog.Builder ad = new AlertDialog.Builder(this);
            ad.setTitle("Help!");
            View v = LayoutInflater.from(this).inflate(R.layout.scrollable_dialog, null);
            ((TextView) v.findViewById(R.id.DialogText)).setText(R.string.search_screen_help);
            ad.setPositiveButton("Okay.", new android.content.DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int arg1) {
                }
            });
            ad.setView(v);
            ad.show();
        }
        return true;
    }

    private void closeKeyboard() {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(artistSearchInput.getWindowToken(), 0);
    }

    /** Search archive.org, parse the results, update the ListView in the parent activity.
     * 
     */
    private class JSONQueryTask extends AsyncTask<String, Integer, String> {

        private SearchScreen parentScreen;
        private boolean completed;

        private JSONQueryTask(SearchScreen activity) {
            this.parentScreen = activity;
        }

        /* We query archive.org which returns JSON.  We use JSON instead
         * of XML because it's smaller in size. doInBackground() returns
         * a String to onPostExecute(), which is invoked on the UI after
         * the background computation finishes.
         */

        protected void onPreExecute() {
            parentScreen.showDialog(VibeVault.SEARCHING_DIALOG_ID);
            //Close virtual keyboard
            closeKeyboard();
        }

        @Override
        protected String doInBackground(String... queryString) {

            if (queryString[0] == null) {
                return null;
            }

            String archiveQuery = queryString[0];
            String queryResult = "";

            /* Open up an HTTP connection with the archive.org query. Grab an
             * input stream or bytes and turn it into a string. It will be of
             * the form described in JSONQueryExample.txt. We use a
             * BufferedInputStream because its read() call grabs many bytes at
             * once (behind the scenes) and puts them into an internal buffer. A
             * regular InputStream grabs one byte per read() so it has to pester
             * the OS more and is way slower.
             */
            InputStream in = null;
            try {

                URL url = new URL(archiveQuery);
                HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
                HttpURLConnection httpConn = (HttpURLConnection) urlConn;
                httpConn.setAllowUserInteraction(false);

                httpConn.connect();

                in = httpConn.getInputStream();
                BufferedInputStream bis = new BufferedInputStream(in);
                ByteArrayBuffer baf = new ByteArrayBuffer(50);
                int read = 0;
                int bufSize = 512;
                byte[] buffer = new byte[bufSize];
                while (true) {
                    read = bis.read(buffer);
                    if (read == -1) {
                        break;
                    }
                    baf.append(buffer, 0, read);
                }
                bis.close();

                queryResult = new String(baf.toByteArray());

            } catch (MalformedURLException e) {
                // DEBUG
                Log.e(LOG_TAG, "Malformed URL: " + e.toString());
            } catch (IOException e) {
                // DEBUG
                Log.e(LOG_TAG, "IO Exception: " + e.toString());
            }
            return queryResult;

        }

        protected void onPostExecute(String JSONString) {

            // This is 0 unless you are finding more results, in which case
            // we use this value later to set the ListView's position to the new results.
            int startVal = VibeVault.searchResults.size();
            if (JSONString == null) {
                Toast.makeText(getBaseContext(), "Invalid query?", Toast.LENGTH_SHORT).show();
            }
            /*
             * Parse the JSON String that we got from archive.org. If the
             * mediatype is etree, create an ArchiveShowObj which encapsulates
             * the information for a particular result from the archive.org
             * query. Populate the ArrayList which backs the ListView, and call
             * the inherited refreshSearchList().
             */
            JSONObject jObject;
            try {
                jObject = new JSONObject(JSONString.replace("callback(", "")).getJSONObject("response");
                JSONArray docsArray = jObject.getJSONArray("docs");
                int numItems = docsArray.length();
                if (numItems == 0) {
                    Toast.makeText(getBaseContext(), "Artist may not have content on archive.org...",
                            Toast.LENGTH_SHORT).show();
                }
                for (int i = 0; i < numItems; i++) {
                    if (docsArray.getJSONObject(i).optString("mediatype").equals("etree")) {
                        // Might be inefficient to keep getting size().
                        VibeVault.searchResults.add(VibeVault.searchResults.size(),
                                new ArchiveShowObj(docsArray.getJSONObject(i).optString("title"),
                                        docsArray.getJSONObject(i).optString("identifier"),
                                        docsArray.getJSONObject(i).optString("date"),
                                        docsArray.getJSONObject(i).optDouble("avg_rating"),
                                        docsArray.getJSONObject(i).optString("format"),
                                        docsArray.getJSONObject(i).optString("source")));
                    }
                }
            } catch (JSONException e) {
                if (pageNum > 1) {
                    Toast.makeText(getBaseContext(),
                            "There might not be any more results...  Try again once or twice.", Toast.LENGTH_SHORT)
                            .show();
                } else {
                    Toast.makeText(getBaseContext(), "Error from archive.org...  Try again later.",
                            Toast.LENGTH_SHORT).show();
                }
                // DEBUG
                Log.e(LOG_TAG, "JSON error: " + JSONString);
                Log.e(LOG_TAG, e.toString());
            }
            refreshSearchList();
            if (startVal != 0) {
                searchList.setSelection(startVal - 1);
            }
            completed = true;
            notifyActivityTaskCompleted();
        }

        // The parent could be null if you changed orientations
        // and this method was called before the new SearchScreen
        // could set itself as this Thread's parent.
        private void notifyActivityTaskCompleted() {
            if (parentScreen != null) {
                parentScreen.onTaskCompleted();
            }
        }

        // When a SearchScreen is reconstructed (like after an orientation change),
        // we call this method on the retained SearchScreen (if one exists) to set
        // its parent Activity as the new SearchScreen because the old one has been destroyed.
        // This prevents leaking any of the data associated with the old SearchScreen.
        private void setActivity(SearchScreen activity) {
            this.parentScreen = activity;
            if (completed) {
                notifyActivityTaskCompleted();
            }
        }
    }
}