mirror of
https://github.com/zxing/zxing.git
synced 2025-03-05 20:48:51 -08:00
Use AsyncTask instead of Thread for cleaner async work handling
git-svn-id: https://zxing.googlecode.com/svn/trunk@2369 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
parent
9e815a73b7
commit
12c113004d
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package com.google.zxing.client.android;
|
package com.google.zxing.client.android;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -35,6 +37,8 @@ import java.util.HashSet;
|
||||||
*/
|
*/
|
||||||
public final class HttpHelper {
|
public final class HttpHelper {
|
||||||
|
|
||||||
|
private static final String TAG = HttpHelper.class.getSimpleName();
|
||||||
|
|
||||||
private static final Collection<String> REDIRECTOR_DOMAINS = new HashSet<String>(Arrays.asList(
|
private static final Collection<String> REDIRECTOR_DOMAINS = new HashSet<String>(Arrays.asList(
|
||||||
"amzn.to", "bit.ly", "bitly.com", "fb.me", "goo.gl", "is.gd", "j.mp", "lnkd.in", "ow.ly",
|
"amzn.to", "bit.ly", "bitly.com", "fb.me", "goo.gl", "is.gd", "j.mp", "lnkd.in", "ow.ly",
|
||||||
"R.BEETAGG.COM", "r.beetagg.com", "SCN.BY", "su.pr", "t.co", "tinyurl.com", "tr.im"
|
"R.BEETAGG.COM", "r.beetagg.com", "SCN.BY", "su.pr", "t.co", "tinyurl.com", "tr.im"
|
||||||
|
@ -75,6 +79,7 @@ public final class HttpHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String downloadViaHttp(String uri, String contentTypes) throws IOException {
|
private static String downloadViaHttp(String uri, String contentTypes) throws IOException {
|
||||||
|
Log.i(TAG, "Downloading " + uri);
|
||||||
URL url = new URL(uri);
|
URL url = new URL(uri);
|
||||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||||
connection.setRequestProperty("Accept", contentTypes);
|
connection.setRequestProperty("Accept", contentTypes);
|
||||||
|
|
|
@ -1,76 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2010 ZXing authors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.google.zxing.client.android.book;
|
|
||||||
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Message;
|
|
||||||
import android.util.Log;
|
|
||||||
import com.google.zxing.client.android.HttpHelper;
|
|
||||||
import com.google.zxing.client.android.LocaleManager;
|
|
||||||
import com.google.zxing.client.android.R;
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
final class NetworkWorker implements Runnable {
|
|
||||||
|
|
||||||
private static final String TAG = NetworkWorker.class.getSimpleName();
|
|
||||||
|
|
||||||
private final String isbn;
|
|
||||||
private final String query;
|
|
||||||
private final Handler handler;
|
|
||||||
|
|
||||||
NetworkWorker(String isbn, String query, Handler handler) {
|
|
||||||
this.isbn = isbn;
|
|
||||||
this.query = query;
|
|
||||||
this.handler = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
// These return a JSON result which describes if and where the query was found. This API may
|
|
||||||
// break or disappear at any time in the future. Since this is an API call rather than a
|
|
||||||
// website, we don't use LocaleManager to change the TLD.
|
|
||||||
String uri;
|
|
||||||
if (LocaleManager.isBookSearchUrl(isbn)) {
|
|
||||||
int equals = isbn.indexOf('=');
|
|
||||||
String volumeId = isbn.substring(equals + 1);
|
|
||||||
uri = "http://www.google.com/books?id=" + volumeId + "&jscmd=SearchWithinVolume2&q=" + query;
|
|
||||||
} else {
|
|
||||||
uri = "http://www.google.com/books?vid=isbn" + isbn + "&jscmd=SearchWithinVolume2&q=" + query;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
String content = HttpHelper.downloadViaHttp(uri, HttpHelper.ContentType.JSON);
|
|
||||||
JSONObject json = new JSONObject(content);
|
|
||||||
Message message = Message.obtain(handler, R.id.search_book_contents_succeeded);
|
|
||||||
message.obj = json;
|
|
||||||
message.sendToTarget();
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
Message message = Message.obtain(handler, R.id.search_book_contents_failed);
|
|
||||||
message.sendToTarget();
|
|
||||||
}
|
|
||||||
} catch (JSONException je) {
|
|
||||||
Log.w(TAG, "Error accessing book search", je);
|
|
||||||
Message message = Message.obtain(handler, R.id.search_book_contents_failed);
|
|
||||||
message.sendToTarget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -18,9 +18,8 @@ package com.google.zxing.client.android.book;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Message;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
@ -31,10 +30,12 @@ import android.widget.Button;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import com.google.zxing.client.android.HttpHelper;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
@ -58,29 +59,12 @@ public final class SearchBookContentsActivity extends Activity {
|
||||||
private static final Pattern QUOTE_ENTITY_PATTERN = Pattern.compile("'");
|
private static final Pattern QUOTE_ENTITY_PATTERN = Pattern.compile("'");
|
||||||
private static final Pattern QUOT_ENTITY_PATTERN = Pattern.compile(""");
|
private static final Pattern QUOT_ENTITY_PATTERN = Pattern.compile(""");
|
||||||
|
|
||||||
private Thread networkThread;
|
|
||||||
private String isbn;
|
private String isbn;
|
||||||
private EditText queryTextView;
|
private EditText queryTextView;
|
||||||
private Button queryButton;
|
private Button queryButton;
|
||||||
private ListView resultListView;
|
private ListView resultListView;
|
||||||
private TextView headerView;
|
private TextView headerView;
|
||||||
|
|
||||||
private final Handler handler = new Handler() {
|
|
||||||
@Override
|
|
||||||
public void handleMessage(Message message) {
|
|
||||||
switch (message.what) {
|
|
||||||
case R.id.search_book_contents_succeeded:
|
|
||||||
handleSearchResults((JSONObject) message.obj);
|
|
||||||
resetForNewQuery();
|
|
||||||
break;
|
|
||||||
case R.id.search_book_contents_failed:
|
|
||||||
resetForNewQuery();
|
|
||||||
headerView.setText(R.string.msg_sbc_failed);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final Button.OnClickListener buttonListener = new Button.OnClickListener() {
|
private final Button.OnClickListener buttonListener = new Button.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
|
@ -150,86 +134,121 @@ public final class SearchBookContentsActivity extends Activity {
|
||||||
queryTextView.selectAll();
|
queryTextView.selectAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetForNewQuery() {
|
|
||||||
networkThread = null;
|
|
||||||
queryTextView.setEnabled(true);
|
|
||||||
queryTextView.selectAll();
|
|
||||||
queryButton.setEnabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void launchSearch() {
|
private void launchSearch() {
|
||||||
if (networkThread == null) {
|
String query = queryTextView.getText().toString();
|
||||||
String query = queryTextView.getText().toString();
|
if (query != null && query.length() > 0) {
|
||||||
if (query != null && query.length() > 0) {
|
NetworkTask networkTask = new NetworkTask();
|
||||||
networkThread = new Thread(new NetworkWorker(isbn, query, handler));
|
networkTask.execute(query, isbn);
|
||||||
networkThread.start();
|
headerView.setText(R.string.msg_sbc_searching_book);
|
||||||
headerView.setText(R.string.msg_sbc_searching_book);
|
|
||||||
resultListView.setAdapter(null);
|
|
||||||
queryTextView.setEnabled(false);
|
|
||||||
queryButton.setEnabled(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Currently there is no way to distinguish between a query which had no results and a book
|
|
||||||
// which is not searchable - both return zero results.
|
|
||||||
private void handleSearchResults(JSONObject json) {
|
|
||||||
try {
|
|
||||||
int count = json.getInt("number_of_results");
|
|
||||||
headerView.setText(getString(R.string.msg_sbc_results) + " : " + count);
|
|
||||||
if (count > 0) {
|
|
||||||
JSONArray results = json.getJSONArray("search_results");
|
|
||||||
SearchBookContentsResult.setQuery(queryTextView.getText().toString());
|
|
||||||
List<SearchBookContentsResult> items = new ArrayList<SearchBookContentsResult>(count);
|
|
||||||
for (int x = 0; x < count; x++) {
|
|
||||||
items.add(parseResult(results.getJSONObject(x)));
|
|
||||||
}
|
|
||||||
resultListView.setOnItemClickListener(new BrowseBookListener(this, items));
|
|
||||||
resultListView.setAdapter(new SearchBookContentsAdapter(this, items));
|
|
||||||
} else {
|
|
||||||
String searchable = json.optString("searchable");
|
|
||||||
if ("false".equals(searchable)) {
|
|
||||||
headerView.setText(R.string.msg_sbc_book_not_searchable);
|
|
||||||
}
|
|
||||||
resultListView.setAdapter(null);
|
|
||||||
}
|
|
||||||
} catch (JSONException e) {
|
|
||||||
Log.w(TAG, "Bad JSON from book search", e);
|
|
||||||
resultListView.setAdapter(null);
|
resultListView.setAdapter(null);
|
||||||
headerView.setText(R.string.msg_sbc_failed);
|
queryTextView.setEnabled(false);
|
||||||
|
queryButton.setEnabled(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Available fields: page_id, page_number, page_url, snippet_text
|
private final class NetworkTask extends AsyncTask<String,Object,JSONObject> {
|
||||||
private SearchBookContentsResult parseResult(JSONObject json) {
|
|
||||||
try {
|
|
||||||
String pageId = json.getString("page_id");
|
|
||||||
String pageNumber = json.getString("page_number");
|
|
||||||
if (pageNumber.length() > 0) {
|
|
||||||
pageNumber = getString(R.string.msg_sbc_page) + ' ' + pageNumber;
|
|
||||||
} else {
|
|
||||||
// This can happen for text on the jacket, and possibly other reasons.
|
|
||||||
pageNumber = getString(R.string.msg_sbc_unknown_page);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove all HTML tags and encoded characters. Ideally the server would do this.
|
@Override
|
||||||
String snippet = json.optString("snippet_text");
|
protected JSONObject doInBackground(String... args) {
|
||||||
boolean valid = true;
|
try {
|
||||||
if (snippet.length() > 0) {
|
// These return a JSON result which describes if and where the query was found. This API may
|
||||||
snippet = TAG_PATTERN.matcher(snippet).replaceAll("");
|
// break or disappear at any time in the future. Since this is an API call rather than a
|
||||||
snippet = LT_ENTITY_PATTERN.matcher(snippet).replaceAll("<");
|
// website, we don't use LocaleManager to change the TLD.
|
||||||
snippet = GT_ENTITY_PATTERN.matcher(snippet).replaceAll(">");
|
String theQuery = args[0];
|
||||||
snippet = QUOTE_ENTITY_PATTERN.matcher(snippet).replaceAll("'");
|
String theIsbn = args[1];
|
||||||
snippet = QUOT_ENTITY_PATTERN.matcher(snippet).replaceAll("\"");
|
String uri;
|
||||||
} else {
|
if (LocaleManager.isBookSearchUrl(theIsbn)) {
|
||||||
snippet = '(' + getString(R.string.msg_sbc_snippet_unavailable) + ')';
|
int equals = theIsbn.indexOf('=');
|
||||||
valid = false;
|
String volumeId = theIsbn.substring(equals + 1);
|
||||||
|
uri = "http://www.google.com/books?id=" + volumeId + "&jscmd=SearchWithinVolume2&q=" + theQuery;
|
||||||
|
} else {
|
||||||
|
uri = "http://www.google.com/books?vid=isbn" + theIsbn + "&jscmd=SearchWithinVolume2&q=" + theQuery;
|
||||||
|
}
|
||||||
|
String content = HttpHelper.downloadViaHttp(uri, HttpHelper.ContentType.JSON);
|
||||||
|
return new JSONObject(content);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
Log.w(TAG, "Error accessing book search", ioe);
|
||||||
|
return null;
|
||||||
|
} catch (JSONException je) {
|
||||||
|
Log.w(TAG, "Error accessing book search", je);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
return new SearchBookContentsResult(pageId, pageNumber, snippet, valid);
|
|
||||||
} catch (JSONException e) {
|
|
||||||
// Never seen in the wild, just being complete.
|
|
||||||
return new SearchBookContentsResult(getString(R.string.msg_sbc_no_page_returned), "", "", false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(JSONObject result) {
|
||||||
|
if (result == null) {
|
||||||
|
headerView.setText(R.string.msg_sbc_failed);
|
||||||
|
} else {
|
||||||
|
handleSearchResults(result);
|
||||||
|
}
|
||||||
|
queryTextView.setEnabled(true);
|
||||||
|
queryTextView.selectAll();
|
||||||
|
queryButton.setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Currently there is no way to distinguish between a query which had no results and a book
|
||||||
|
// which is not searchable - both return zero results.
|
||||||
|
private void handleSearchResults(JSONObject json) {
|
||||||
|
try {
|
||||||
|
int count = json.getInt("number_of_results");
|
||||||
|
headerView.setText(getString(R.string.msg_sbc_results) + " : " + count);
|
||||||
|
if (count > 0) {
|
||||||
|
JSONArray results = json.getJSONArray("search_results");
|
||||||
|
SearchBookContentsResult.setQuery(queryTextView.getText().toString());
|
||||||
|
List<SearchBookContentsResult> items = new ArrayList<SearchBookContentsResult>(count);
|
||||||
|
for (int x = 0; x < count; x++) {
|
||||||
|
items.add(parseResult(results.getJSONObject(x)));
|
||||||
|
}
|
||||||
|
resultListView.setOnItemClickListener(new BrowseBookListener(SearchBookContentsActivity.this, items));
|
||||||
|
resultListView.setAdapter(new SearchBookContentsAdapter(SearchBookContentsActivity.this, items));
|
||||||
|
} else {
|
||||||
|
String searchable = json.optString("searchable");
|
||||||
|
if ("false".equals(searchable)) {
|
||||||
|
headerView.setText(R.string.msg_sbc_book_not_searchable);
|
||||||
|
}
|
||||||
|
resultListView.setAdapter(null);
|
||||||
|
}
|
||||||
|
} catch (JSONException e) {
|
||||||
|
Log.w(TAG, "Bad JSON from book search", e);
|
||||||
|
resultListView.setAdapter(null);
|
||||||
|
headerView.setText(R.string.msg_sbc_failed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Available fields: page_id, page_number, page_url, snippet_text
|
||||||
|
private SearchBookContentsResult parseResult(JSONObject json) {
|
||||||
|
try {
|
||||||
|
String pageId = json.getString("page_id");
|
||||||
|
String pageNumber = json.getString("page_number");
|
||||||
|
if (pageNumber.length() > 0) {
|
||||||
|
pageNumber = getString(R.string.msg_sbc_page) + ' ' + pageNumber;
|
||||||
|
} else {
|
||||||
|
// This can happen for text on the jacket, and possibly other reasons.
|
||||||
|
pageNumber = getString(R.string.msg_sbc_unknown_page);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all HTML tags and encoded characters. Ideally the server would do this.
|
||||||
|
String snippet = json.optString("snippet_text");
|
||||||
|
boolean valid = true;
|
||||||
|
if (snippet.length() > 0) {
|
||||||
|
snippet = TAG_PATTERN.matcher(snippet).replaceAll("");
|
||||||
|
snippet = LT_ENTITY_PATTERN.matcher(snippet).replaceAll("<");
|
||||||
|
snippet = GT_ENTITY_PATTERN.matcher(snippet).replaceAll(">");
|
||||||
|
snippet = QUOTE_ENTITY_PATTERN.matcher(snippet).replaceAll("'");
|
||||||
|
snippet = QUOT_ENTITY_PATTERN.matcher(snippet).replaceAll("\"");
|
||||||
|
} else {
|
||||||
|
snippet = '(' + getString(R.string.msg_sbc_snippet_unavailable) + ')';
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
return new SearchBookContentsResult(pageId, pageNumber, snippet, valid);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
// Never seen in the wild, just being complete.
|
||||||
|
return new SearchBookContentsResult(getString(R.string.msg_sbc_no_page_returned), "", "", false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue