Java tutorial
/******************************************************************************* * Copyright (C) 2014 sgelb * * This program 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.github.sgelb.booket; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.AsyncTask; import android.os.Bundle; import android.text.Html; import android.util.Log; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemLongClickListener; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import com.github.sgelb.booket.R; public class SearchResultActivity extends Activity { public static ArrayList<Book> bookList; private ProgressBar downloadProgressBar; private ListView resultList; private BookResultAdapter adapter; private BooksDataSource datasource; private TextView noResults; private Bitmap defaultThumbnail; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_search_results); getActionBar().setDisplayHomeAsUpEnabled(true); noResults = (TextView) findViewById(R.id.noResultsTextView); defaultThumbnail = BitmapFactory.decodeResource(getResources(), R.drawable.ic_default_cover); bookList = new ArrayList<>(); resultList = (ListView) findViewById(R.id.searchResultsList); downloadProgressBar = (ProgressBar) findViewById(R.id.downloadProgressBar); datasource = new BooksDataSource(this); BookInfos bookInfos = new BookInfos(); bookInfos.execute(createUrl()); adapter = new BookResultAdapter(bookList, this); resultList.setAdapter(adapter); // Save book in database resultList.setOnItemLongClickListener(new OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> parent, View view, final int position, long id) { AlertDialog alert = new AlertDialog.Builder(SearchResultActivity.this).create(); alert.setTitle( Html.fromHtml(getString(R.string.dialog_save_book, bookList.get(position).getTitle()))); alert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.ok), new OnClickListener() { public void onClick(final DialogInterface dialog, final int which) { saveBook(position); } }); alert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel), new OnClickListener() { public void onClick(final DialogInterface dialog, final int which) { } }); alert.show(); return true; } }); // Expand list row resultList.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { adapter.toggleExpand(view, position); } }); } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } private String createUrl() { Intent intent = getIntent(); String author = intent.getStringExtra(BookSearchActivity.AUTHOR); String title = intent.getStringExtra(BookSearchActivity.TITLE); String isbn = intent.getStringExtra(BookSearchActivity.ISBN); String keyword = intent.getStringExtra(BookSearchActivity.KEYWORD); String language = intent.getStringExtra(BookSearchActivity.LANGUAGE); String query = ""; try { if (!isbn.isEmpty()) { query += "+isbn:" + URLEncoder.encode(isbn, "utf-8"); } else { if (!author.isEmpty()) query += "+inauthor:" + URLEncoder.encode(author, "utf-8"); if (!title.isEmpty()) query += "+intitle:" + URLEncoder.encode(title, "utf-8"); if (!keyword.isEmpty()) query += URLEncoder.encode(keyword, "utf-8"); if (!language.isEmpty()) query += "&langRestrict=" + URLEncoder.encode(language, "utf-8"); } } catch (UnsupportedEncodingException e) { } query += "&fields=items(volumeInfo,id)&maxResults=40"; Log.d("R2R", "URL: " + query); return "https://www.googleapis.com/books/v1/volumes?q=" + query; } private class BookInfos extends AsyncTask<String, Integer, String> { @Override protected void onPreExecute() { downloadProgressBar.setVisibility(View.VISIBLE); } @Override protected String doInBackground(String... urls) { Log.d("R2R", "BookInfo started"); StringBuilder builder = new StringBuilder(); URL url = null; HttpURLConnection con = null; try { url = new URL(urls[0]); con = (HttpURLConnection) url.openConnection(); if (con.getResponseCode() == HttpURLConnection.HTTP_OK) { InputStream content = new BufferedInputStream(con.getInputStream()); BufferedReader reader = new BufferedReader(new InputStreamReader(content)); String line; while ((line = reader.readLine()) != null) { builder.append(line); } } } catch (MalformedURLException e) { Log.d("R2R", "MalformedURLException: " + e.getMessage()); } catch (IOException e) { Log.d("R2R", "IOException: " + e.getMessage()); } finally { con.disconnect(); } return builder.toString(); } @Override protected void onPostExecute(String result) { Log.d("R2R", "BookInfo finished"); downloadProgressBar.setVisibility(View.GONE); processResult(result); } } private class DownloadThumbNail extends AsyncTask<String, Void, Bitmap> { private Book book; public DownloadThumbNail(Book book) { this.book = book; } @Override protected Bitmap doInBackground(String... urls) { Bitmap thumbnail = null; URL url = null; HttpURLConnection con = null; try { url = new URL(urls[0]); con = (HttpURLConnection) url.openConnection(); con.setDoInput(true); con.connect(); if (con.getResponseCode() == HttpURLConnection.HTTP_OK) { InputStream input = con.getInputStream(); thumbnail = BitmapFactory.decodeStream(input); return thumbnail; } } catch (MalformedURLException e) { Log.d("R2R", "MalformedURLException: " + e.getMessage()); } catch (IOException e) { Log.d("R2R", "IOException: " + e.getMessage()); } finally { con.disconnect(); } return thumbnail; } @Override protected void onPostExecute(Bitmap thumbnail) { // amazon returns 1x1 pixel if no cover found. if (thumbnail.getWidth() < 2) { book.setThumbnailBitmap(defaultThumbnail); } else { book.setThumbnailBitmap(thumbnail); adapter.notifyDataSetChanged(); } } } private void processResult(String result) { JSONArray items = null; try { JSONObject jsonObject = new JSONObject(result); items = jsonObject.getJSONArray("items"); } catch (JSONException e) { noResults.setVisibility(View.VISIBLE); return; } try { // iterate over items aka books Log.d("R2R", "Items: " + items.length()); for (int i = 0; i < items.length(); i++) { Log.d("R2R", "\nBook " + (i + 1)); JSONObject item = (JSONObject) items.get(i); JSONObject info = item.getJSONObject("volumeInfo"); Book book = new Book(); // add authors String authors = ""; if (info.has("authors")) { JSONArray jAuthors = info.getJSONArray("authors"); for (int j = 0; j < jAuthors.length() - 1; j++) { authors += jAuthors.getString(j) + ", "; } authors += jAuthors.getString(jAuthors.length() - 1); } else { authors = "n/a"; } book.setAuthors(authors); Log.d("R2R", "authors " + book.getAuthors()); // add title if (info.has("title")) { book.setTitle(info.getString("title")); Log.d("R2R", "title " + book.getTitle()); } // add pageCount if (info.has("pageCount")) { book.setPageCount(info.getInt("pageCount")); Log.d("R2R", "pageCount " + book.getPageCount()); } // add publisher if (info.has("publisher")) { book.setPublisher(info.getString("publisher")); Log.d("R2R", "publisher " + book.getPublisher()); } // add description if (info.has("description")) { book.setDescription(info.getString("description")); Log.d("R2R", "description " + book.getDescription()); } // add isbn if (info.has("industryIdentifiers")) { JSONArray ids = info.getJSONArray("industryIdentifiers"); for (int k = 0; k < ids.length(); k++) { JSONObject id = ids.getJSONObject(k); if (id.getString("type").equalsIgnoreCase("ISBN_13")) { book.setIsbn(id.getString("identifier")); break; } if (id.getString("type").equalsIgnoreCase("ISBN_10")) { book.setIsbn(id.getString("identifier")); } } Log.d("R2R", "isbn " + book.getIsbn()); } // add published date if (info.has("publishedDate")) { book.setYear(info.getString("publishedDate").substring(0, 4)); Log.d("R2R", "publishedDate " + book.getYear()); } // add cover thumbnail book.setThumbnailBitmap(defaultThumbnail); if (info.has("imageLinks")) { // get cover url from google JSONObject imageLinks = info.getJSONObject("imageLinks"); if (imageLinks.has("thumbnail")) { book.setThumbnailUrl(imageLinks.getString("thumbnail")); } } else if (book.getIsbn() != null) { // get cover url from amazon String amazonCoverUrl = "http://ecx.images-amazon.com/images/P/" + isbn13to10(book.getIsbn()) + ".01._SCMZZZZZZZ_.jpg"; book.setThumbnailUrl(amazonCoverUrl); } // download cover thumbnail if (book.getThumbnailUrl() != null && !book.getThumbnailUrl().isEmpty()) { new DownloadThumbNail(book).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, book.getThumbnailUrl()); Log.d("R2R", "thumbnail " + book.getThumbnailUrl()); } // add google book id if (item.has("id")) { book.setGoogleId(item.getString("id")); } bookList.add(book); } } catch (Exception e) { Log.d("R2R", "Exception: " + e.toString()); // FIXME: catch exception } } private String isbn13to10(String isbn) { if (isbn.length() == 10) { return isbn; } // cut leading 3 characters and wrong checksum from isbn13 String isbn9 = isbn.substring(3, 12); int sum = 0; for (int i = 0; i < isbn9.length(); i++) { sum += Integer.parseInt("" + isbn9.charAt(i)) * (10 - i); } // return isbn10 with checksum String checksum = "X"; if ((11 - sum % 11) < 10) { checksum = String.valueOf(11 - sum % 11); } return isbn9 + checksum; } private void saveBook(int position) { datasource.open(); boolean addedNewBook = datasource.createBook(bookList.get(position)); datasource.close(); String msg = getString(R.string.saved, bookList.get(position).getTitle()); if (!addedNewBook) { msg = getString(R.string.alreadySaved, bookList.get(position).getTitle()); } Toast.makeText(getBaseContext(), Html.fromHtml(msg), Toast.LENGTH_SHORT).show(); } }