Search code examples
androidloader

How to change URL Query Parameters from a users Edit Text input?


The goal of the code is to query the google api for a list of books that the user has chosen. The app takes the text inputted in the editText field, and when the user clicks the search button, the query parameter in the query url should be changed to the inputted text then another network request is made using a loader. I can't seem to get this to function. Below is the code:

Query URLS

private static String queryUrl = "https://www.googleapis.com/books/v1/volumes?q=android&maxResults=20&orderBy=relevance";
private static String baseUrl = "https://www.googleapis.com/books/v1/volumes?";

Search Button On Click

    //Method that takes the current URL and modifies it based on what the user searched
private void searchButtonOnClick() {
    Log.d(LOG_TAG, "searchButtonOnClick was called");

    //Get the user input
    if (TextUtils.isEmpty(editText.getText())) {
        Toast.makeText(this, "No Search Entered", Toast.LENGTH_SHORT).show();
    } else {

        Uri.Builder uriBuilder = new Uri.Builder();
        uriBuilder.scheme("https")
                .authority("www.googleapis.com")
                .appendPath("books")
                .appendPath("v1")
                .appendPath("volumes")
                .appendQueryParameter("q", editText.getText().toString())
                .appendQueryParameter("maxResults", "100")
                .appendQueryParameter("orderBy", "relevance");
        queryUrl = uriBuilder.build().toString();



        LoaderManager loaderManager = getSupportLoaderManager();
        booksListAdapter.clear();
        loaderManager.initLoader(BOOK_LOADER_ID, null, this);

    }
}

Full MainActivity Class

package com.example.booksearch;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;

import android.net.Uri.Builder;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import com.squareup.picasso.Picasso;

import java.lang.reflect.Array;
import java.net.NetworkInterface;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<List<Book>> {




    //Log tag for debugging purposes
    public static final String LOG_TAG = MainActivity.class.getSimpleName();


    //Getting title author and image, link book to where to buy when clicked
    private static String input = "";
    private static String queryUrl = "https://www.googleapis.com/books/v1/volumes?q=android&maxResults=20&orderBy=relevance";
    private static String baseUrl = "https://www.googleapis.com/books/v1/volumes?";
    private EditText editText;
    private List<Book> books = new ArrayList<>();
    private static BooksListAdapter booksListAdapter;
    private static int BOOK_LOADER_ID;
    private Button searchButton;
    private TextView emptyTextView;
    private ImageView imageView;
    private ProgressBar loadProgressIndicator;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        //Hook up search button
        searchButton = findViewById(R.id.search_button);

        //Hook up editText
        editText = findViewById(R.id.search_edit_text);

        //setting up searchButtonOnclickListener
        searchButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                searchButtonOnClick();
            }
        });


        //Get a connectivity Manager to monitor network state
        ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(this.CONNECTIVITY_SERVICE);

        //Get details about the devices network
        NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();

        //Hook up the empty textView
        emptyTextView = findViewById(R.id.empty_text_view);

        //Hook up progress indicator
        loadProgressIndicator = findViewById(R.id.loading_data_progress_indicator);

        //If there is a network connection fetch data if not, set the text on the empty text view accordingly
        if (networkInfo != null && networkInfo.isConnected()) {
            LoaderManager loaderManager = getSupportLoaderManager();
            loaderManager.initLoader(BOOK_LOADER_ID, null, this);

        } else {
            //if no internet connection display error message
            loadProgressIndicator.setVisibility(View.GONE);
            emptyTextView.setText(R.string.no_internet);
        }
        // the Book objects are added to this array list off the main thread, thats why the adapter is set to an empty array list.
        //This line of code will always be executed before the asynchonous Load is done, because it's on the main thread.
        booksListAdapter = new BooksListAdapter(this, new ArrayList<Book>());

        ListView bookListView = findViewById(R.id.book_list_view);

        bookListView.setAdapter(booksListAdapter);
        bookListView.setEmptyView(emptyTextView);

        //Set onclick listener on the list to send the user to where to buy the book
        bookListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Book currentBook = booksListAdapter.getItem(position);

                //Check to see if there is a place to buy the book before sending the user off
                if (currentBook.getWhereToBuyBook().isEmpty()) {
                    Toast.makeText(MainActivity.this, "Cannot find where to purchase", Toast.LENGTH_SHORT).show();

                } else {
                    Uri bookURi = Uri.parse(currentBook.getWhereToBuyBook());
                    //Creating an intent to send the user to a website
                    Intent websiteIntent = new Intent(Intent.ACTION_VIEW, bookURi);

                    //send the intent to another app that can handle it
                    startActivity(websiteIntent);

                }

            }

        });
    }

    @NonNull
    @Override
    public Loader<List<Book>> onCreateLoader(int id, @Nullable Bundle args) {
        return new BookLoader(this, queryUrl);
    }

    @Override
    public void onLoadFinished(@NonNull Loader<List<Book>> loader, List<Book> data) {
        //After the first results load check for internet connectivity to avoid error message loading at app startup
        ConnectivityManager cm = (ConnectivityManager) this.getSystemService(CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = cm.getActiveNetworkInfo();
        if (networkInfo == null) {
            emptyTextView.setText(R.string.no_internet);
        } else if (networkInfo != null && networkInfo.isConnected()) {
            //display when there is internet but there were no results
            emptyTextView.setText(R.string.no_books_found);
        }

        booksListAdapter.clear();

        //If there is a valid list of books, add them to the dataset, this will trigger the listview to update
        if (data != null && !data.isEmpty()) {
            booksListAdapter.addAll(data);
        } else {
            emptyTextView.setText(R.string.no_books_found);
        }

        loadProgressIndicator.setVisibility(View.GONE);


    }

    @Override
    public void onLoaderReset(@NonNull Loader<List<Book>> loader) {
        booksListAdapter.clear();
    }


    //Method that takes the current URL and modifies it based on what the user searched
    private void searchButtonOnClick() {
        Log.d(LOG_TAG, "searchButtonOnClick was called");

        //Get the user input
        if (TextUtils.isEmpty(editText.getText())) {
            Toast.makeText(this, "No Search Entered", Toast.LENGTH_SHORT).show();
        } else {
            Uri buildUri = Uri.parse(baseUrl);
            Uri.Builder uriBuilder = new Uri.Builder();
            buildUri.buildUpon();

            uriBuilder.scheme("https")
                    .authority("www.googleapis.com")
                    .appendPath("books")
                    .appendPath("v1")
                    .appendPath("volumes")
                    .appendQueryParameter("q", editText.getText().toString())
                    .appendQueryParameter("maxResults", "100")
                    .appendQueryParameter("orderBy", "relevance");
            queryUrl = uriBuilder.build().toString();


            booksListAdapter.clear();
            LoaderManager loaderManager = getSupportLoaderManager();
            loaderManager.restartLoader(BOOK_LOADER_ID, null, this);
            loaderManager.initLoader(BOOK_LOADER_ID, null, this).forceLoad();

        }
    }

}


Solution

  • As per the code snippet you have shared, .appendQueryParameter("q", editText.getText().toString()) is taking the query param already. Can you explain a bit more, what exactly is the issue you are facing?

    Update: Change your query to --

    private static String baseUrl = "https://www.googleapis.com/books/v1/volumes?maxResults=20&orderBy=relevance&q=";
    

    Change your else block to this -

    else {
            queryUrl = baseUrl + editText.getText().toString();
            LoaderManager loaderManager = getSupportLoaderManager();
            booksListAdapter.clear();
            loaderManager.initLoader(BOOK_LOADER_ID, null, this);
    }