mirror of
https://github.com/zxing/zxing.git
synced 2025-03-05 20:48:51 -08:00
Merge from "Barcode Scanner Plus": avoid deprecated AndroidHttpClient and use java.net for networking; better HTTP headers, better redirect handling
git-svn-id: https://zxing.googlecode.com/svn/trunk@1958 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
parent
3bd4e1e903
commit
d2ed7976ef
|
@ -1,170 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2008 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;
|
|
||||||
|
|
||||||
import org.apache.http.HttpHost;
|
|
||||||
import org.apache.http.HttpRequest;
|
|
||||||
import org.apache.http.HttpRequestInterceptor;
|
|
||||||
import org.apache.http.HttpResponse;
|
|
||||||
import org.apache.http.client.HttpClient;
|
|
||||||
import org.apache.http.client.ResponseHandler;
|
|
||||||
import org.apache.http.client.methods.HttpUriRequest;
|
|
||||||
import org.apache.http.client.params.HttpClientParams;
|
|
||||||
import org.apache.http.client.protocol.ClientContext;
|
|
||||||
import org.apache.http.conn.ClientConnectionManager;
|
|
||||||
import org.apache.http.conn.scheme.PlainSocketFactory;
|
|
||||||
import org.apache.http.conn.scheme.Scheme;
|
|
||||||
import org.apache.http.conn.scheme.SchemeRegistry;
|
|
||||||
import org.apache.http.conn.ssl.SSLSocketFactory;
|
|
||||||
import org.apache.http.impl.client.DefaultHttpClient;
|
|
||||||
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
|
|
||||||
import org.apache.http.params.BasicHttpParams;
|
|
||||||
import org.apache.http.params.HttpConnectionParams;
|
|
||||||
import org.apache.http.params.HttpParams;
|
|
||||||
import org.apache.http.params.HttpProtocolParams;
|
|
||||||
import org.apache.http.protocol.BasicHttpContext;
|
|
||||||
import org.apache.http.protocol.HttpContext;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Subclass of the Apache {@link DefaultHttpClient} that is configured with
|
|
||||||
* reasonable default settings and registered schemes for Android, and
|
|
||||||
* also lets the user add {@link HttpRequestInterceptor} classes.
|
|
||||||
* Don't create this directly, use the {@link #newInstance} factory method.</p>
|
|
||||||
* <p/>
|
|
||||||
* <p>This client processes cookies but does not retain them by default.
|
|
||||||
* To retain cookies, simply add a cookie store to the HttpContext:
|
|
||||||
* <pre>context.setAttribute(ClientContext.COOKIE_STORE, cookieStore);</pre>
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public final class AndroidHttpClient implements HttpClient {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new HttpClient with reasonable defaults (which you can update).
|
|
||||||
*
|
|
||||||
* @param userAgent to report in your HTTP requests.
|
|
||||||
* @return AndroidHttpClient for you to use for all your requests.
|
|
||||||
*/
|
|
||||||
public static AndroidHttpClient newInstance(String userAgent) {
|
|
||||||
HttpParams params = new BasicHttpParams();
|
|
||||||
|
|
||||||
// Turn off stale checking. Our connections break all the time anyway,
|
|
||||||
// and it's not worth it to pay the penalty of checking every time.
|
|
||||||
HttpConnectionParams.setStaleCheckingEnabled(params, false);
|
|
||||||
|
|
||||||
// Default connection and socket timeout of 20 seconds. Tweak to taste.
|
|
||||||
HttpConnectionParams.setConnectionTimeout(params, 20 * 1000);
|
|
||||||
HttpConnectionParams.setSoTimeout(params, 20 * 1000);
|
|
||||||
HttpConnectionParams.setSocketBufferSize(params, 8192);
|
|
||||||
|
|
||||||
// Don't handle redirects -- return them to the caller. Our code
|
|
||||||
// often wants to re-POST after a redirect, which we must do ourselves.
|
|
||||||
HttpClientParams.setRedirecting(params, false);
|
|
||||||
|
|
||||||
// Set the specified user agent and register standard protocols.
|
|
||||||
if (userAgent != null) {
|
|
||||||
HttpProtocolParams.setUserAgent(params, userAgent);
|
|
||||||
}
|
|
||||||
SchemeRegistry schemeRegistry = new SchemeRegistry();
|
|
||||||
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
|
|
||||||
schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
|
|
||||||
ClientConnectionManager manager = new ThreadSafeClientConnManager(params, schemeRegistry);
|
|
||||||
|
|
||||||
// We use a factory method to modify superclass initialization
|
|
||||||
// parameters without the funny call-a-static-method dance.
|
|
||||||
return new AndroidHttpClient(manager, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final HttpClient delegate;
|
|
||||||
|
|
||||||
|
|
||||||
private AndroidHttpClient(ClientConnectionManager ccm, HttpParams params) {
|
|
||||||
this.delegate = new DelegateHttpClient(ccm, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Release resources associated with this client. You must call this,
|
|
||||||
* or significant resources (sockets and memory) may be leaked.
|
|
||||||
*/
|
|
||||||
public void close() {
|
|
||||||
getConnectionManager().shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpParams getParams() {
|
|
||||||
return delegate.getParams();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClientConnectionManager getConnectionManager() {
|
|
||||||
return delegate.getConnectionManager();
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpResponse execute(HttpUriRequest request) throws IOException {
|
|
||||||
return delegate.execute(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpResponse execute(HttpUriRequest request, HttpContext context) throws IOException {
|
|
||||||
return delegate.execute(request, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpResponse execute(HttpHost target, HttpRequest request) throws IOException {
|
|
||||||
return delegate.execute(target, request);
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpResponse execute(HttpHost target, HttpRequest request, HttpContext context) throws IOException {
|
|
||||||
return delegate.execute(target, request, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler) throws IOException {
|
|
||||||
return delegate.execute(request, responseHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler, HttpContext context)
|
|
||||||
throws IOException {
|
|
||||||
return delegate.execute(request, responseHandler, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> T execute(HttpHost target, HttpRequest request, ResponseHandler<? extends T> responseHandler)
|
|
||||||
throws IOException {
|
|
||||||
return delegate.execute(target, request, responseHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> T execute(HttpHost target, HttpRequest request,
|
|
||||||
ResponseHandler<? extends T> responseHandler,
|
|
||||||
HttpContext context) throws IOException {
|
|
||||||
return delegate.execute(target, request, responseHandler, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class DelegateHttpClient extends DefaultHttpClient {
|
|
||||||
|
|
||||||
private DelegateHttpClient(ClientConnectionManager ccm, HttpParams params) {
|
|
||||||
super(ccm, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected HttpContext createHttpContext() {
|
|
||||||
// Same as DefaultHttpClient.createHttpContext() minus the
|
|
||||||
// cookie store.
|
|
||||||
HttpContext context = new BasicHttpContext();
|
|
||||||
context.setAttribute(ClientContext.AUTHSCHEME_REGISTRY, getAuthSchemes());
|
|
||||||
context.setAttribute(ClientContext.COOKIESPEC_REGISTRY, getCookieSpecs());
|
|
||||||
context.setAttribute(ClientContext.CREDS_PROVIDER, getCredentialsProvider());
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
168
android/src/com/google/zxing/client/android/HttpHelper.java
Normal file
168
android/src/com/google/zxing/client/android/HttpHelper.java
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2011 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;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility methods for retrieving content over HTTP using the more-supported {@code java.net} classes
|
||||||
|
* in Android.
|
||||||
|
*/
|
||||||
|
public final class HttpHelper {
|
||||||
|
|
||||||
|
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",
|
||||||
|
"SCN.BY", "su.pr", "t.co", "tinyurl.com", "tr.im"
|
||||||
|
));
|
||||||
|
|
||||||
|
private HttpHelper() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ContentType {
|
||||||
|
/** HTML-like content type, including HTML, XHTML, etc. */
|
||||||
|
HTML,
|
||||||
|
/** JSON content */
|
||||||
|
JSON,
|
||||||
|
/** Plain text content */
|
||||||
|
TEXT,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param uri URI to retrieve
|
||||||
|
* @param type expected text-like MIME type of that content
|
||||||
|
* @return content as a {@code String}
|
||||||
|
* @throws IOException if the content can't be retrieved because of a bad URI, network problem, etc.
|
||||||
|
*/
|
||||||
|
public static String downloadViaHttp(String uri, ContentType type) throws IOException {
|
||||||
|
String contentTypes;
|
||||||
|
switch (type) {
|
||||||
|
case HTML:
|
||||||
|
contentTypes = "application/xhtml+xml,text/html,text/*,*/*";
|
||||||
|
break;
|
||||||
|
case JSON:
|
||||||
|
contentTypes = "application/json,text/*,*/*";
|
||||||
|
break;
|
||||||
|
case TEXT:
|
||||||
|
default:
|
||||||
|
contentTypes = "text/*,*/*";
|
||||||
|
}
|
||||||
|
return downloadViaHttp(uri, contentTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String downloadViaHttp(String uri, String contentTypes) throws IOException {
|
||||||
|
URL url = new URL(uri);
|
||||||
|
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||||
|
connection.setRequestProperty("Accept", contentTypes);
|
||||||
|
connection.setRequestProperty("Accept-Charset", "utf-8,*");
|
||||||
|
connection.setRequestProperty("User-Agent", "ZXing (Android)");
|
||||||
|
try {
|
||||||
|
connection.connect();
|
||||||
|
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
|
||||||
|
throw new IOException("Bad HTTP response: " + connection.getResponseCode());
|
||||||
|
}
|
||||||
|
return consume(connection);
|
||||||
|
} finally {
|
||||||
|
connection.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getEncoding(HttpURLConnection connection) {
|
||||||
|
String contentTypeHeader = connection.getHeaderField("Content-Type");
|
||||||
|
if (contentTypeHeader != null) {
|
||||||
|
int charsetStart = contentTypeHeader.indexOf("charset=");
|
||||||
|
if (charsetStart >= 0) {
|
||||||
|
return contentTypeHeader.substring(charsetStart + "charset=".length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "UTF-8";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String consume(HttpURLConnection connection) throws IOException {
|
||||||
|
String encoding = getEncoding(connection);
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
InputStream in = connection.getInputStream();
|
||||||
|
try {
|
||||||
|
in = connection.getInputStream();
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = in.read(buffer)) > 0) {
|
||||||
|
out.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
in.close();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
// continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return new String(out.toByteArray(), encoding);
|
||||||
|
} catch (UnsupportedEncodingException uee) {
|
||||||
|
try {
|
||||||
|
return new String(out.toByteArray(), "UTF-8");
|
||||||
|
} catch (UnsupportedEncodingException uee2) {
|
||||||
|
// can't happen
|
||||||
|
throw new IllegalStateException(uee2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static URI unredirect(URI uri) throws IOException {
|
||||||
|
if (!REDIRECTOR_DOMAINS.contains(uri.getHost())) {
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
URL url = uri.toURL();
|
||||||
|
|
||||||
|
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||||
|
connection.setInstanceFollowRedirects(false);
|
||||||
|
connection.setDoInput(false);
|
||||||
|
connection.setRequestMethod("HEAD");
|
||||||
|
connection.setRequestProperty("User-Agent", "ZXing (Android)");
|
||||||
|
try {
|
||||||
|
connection.connect();
|
||||||
|
switch (connection.getResponseCode()) {
|
||||||
|
case HttpURLConnection.HTTP_MULT_CHOICE:
|
||||||
|
case HttpURLConnection.HTTP_MOVED_PERM:
|
||||||
|
case HttpURLConnection.HTTP_MOVED_TEMP:
|
||||||
|
case HttpURLConnection.HTTP_SEE_OTHER:
|
||||||
|
case 307: // No constant for 307 Temporary Redirect ?
|
||||||
|
String location = connection.getHeaderField("Location");
|
||||||
|
if (location != null) {
|
||||||
|
try {
|
||||||
|
return new URI(location);
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
// nevermind
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return uri;
|
||||||
|
} finally {
|
||||||
|
connection.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -31,19 +31,10 @@ 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 org.apache.http.Header;
|
|
||||||
import org.apache.http.HttpEntity;
|
|
||||||
import org.apache.http.HttpResponse;
|
|
||||||
import org.apache.http.client.methods.HttpGet;
|
|
||||||
import org.apache.http.client.methods.HttpHead;
|
|
||||||
import org.apache.http.client.methods.HttpUriRequest;
|
|
||||||
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.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URI;
|
|
||||||
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;
|
||||||
|
@ -51,7 +42,6 @@ import java.util.regex.Pattern;
|
||||||
import com.google.zxing.client.android.LocaleManager;
|
import com.google.zxing.client.android.LocaleManager;
|
||||||
import com.google.zxing.client.android.R;
|
import com.google.zxing.client.android.R;
|
||||||
import com.google.zxing.client.android.Intents;
|
import com.google.zxing.client.android.Intents;
|
||||||
import com.google.zxing.client.android.AndroidHttpClient;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uses Google Book Search to find a word or phrase in the requested book.
|
* Uses Google Book Search to find a word or phrase in the requested book.
|
||||||
|
@ -59,16 +49,16 @@ import com.google.zxing.client.android.AndroidHttpClient;
|
||||||
* @author dswitkin@google.com (Daniel Switkin)
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
*/
|
*/
|
||||||
public final class SearchBookContentsActivity extends Activity {
|
public final class SearchBookContentsActivity extends Activity {
|
||||||
|
|
||||||
private static final String TAG = SearchBookContentsActivity.class.getSimpleName();
|
private static final String TAG = SearchBookContentsActivity.class.getSimpleName();
|
||||||
|
|
||||||
private static final String USER_AGENT = "ZXing (Android)";
|
|
||||||
private static final Pattern TAG_PATTERN = Pattern.compile("\\<.*?\\>");
|
private static final Pattern TAG_PATTERN = Pattern.compile("\\<.*?\\>");
|
||||||
private static final Pattern LT_ENTITY_PATTERN = Pattern.compile("<");
|
private static final Pattern LT_ENTITY_PATTERN = Pattern.compile("<");
|
||||||
private static final Pattern GT_ENTITY_PATTERN = Pattern.compile(">");
|
private static final Pattern GT_ENTITY_PATTERN = Pattern.compile(">");
|
||||||
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 NetworkThread networkThread;
|
private Thread networkThread;
|
||||||
private String isbn;
|
private String isbn;
|
||||||
private EditText queryTextView;
|
private EditText queryTextView;
|
||||||
private Button queryButton;
|
private Button queryButton;
|
||||||
|
@ -169,7 +159,7 @@ public final class SearchBookContentsActivity extends Activity {
|
||||||
if (networkThread == null) {
|
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) {
|
||||||
networkThread = new NetworkThread(isbn, query, handler);
|
networkThread = new Thread(new NetworkWorker(isbn, query, handler));
|
||||||
networkThread.start();
|
networkThread.start();
|
||||||
headerView.setText(R.string.msg_sbc_searching_book);
|
headerView.setText(R.string.msg_sbc_searching_book);
|
||||||
resultListView.setAdapter(null);
|
resultListView.setAdapter(null);
|
||||||
|
@ -240,105 +230,4 @@ public final class SearchBookContentsActivity extends Activity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class NetworkThread extends Thread {
|
|
||||||
private final String isbn;
|
|
||||||
private final String query;
|
|
||||||
private final Handler handler;
|
|
||||||
|
|
||||||
NetworkThread(String isbn, String query, Handler handler) {
|
|
||||||
this.isbn = isbn;
|
|
||||||
this.query = query;
|
|
||||||
this.handler = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
AndroidHttpClient client = null;
|
|
||||||
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.
|
|
||||||
URI uri;
|
|
||||||
if (LocaleManager.isBookSearchUrl(isbn)) {
|
|
||||||
int equals = isbn.indexOf('=');
|
|
||||||
String volumeId = isbn.substring(equals + 1);
|
|
||||||
uri = new URI("http", null, "www.google.com", -1, "/books", "id=" + volumeId +
|
|
||||||
"&jscmd=SearchWithinVolume2&q=" + query, null);
|
|
||||||
} else {
|
|
||||||
uri = new URI("http", null, "www.google.com", -1, "/books", "vid=isbn" + isbn +
|
|
||||||
"&jscmd=SearchWithinVolume2&q=" + query, null);
|
|
||||||
}
|
|
||||||
HttpUriRequest get = new HttpGet(uri);
|
|
||||||
get.setHeader("cookie", getCookie(uri.toString()));
|
|
||||||
client = AndroidHttpClient.newInstance(USER_AGENT);
|
|
||||||
HttpResponse response = client.execute(get);
|
|
||||||
if (response.getStatusLine().getStatusCode() == 200) {
|
|
||||||
HttpEntity entity = response.getEntity();
|
|
||||||
ByteArrayOutputStream jsonHolder = new ByteArrayOutputStream();
|
|
||||||
entity.writeTo(jsonHolder);
|
|
||||||
jsonHolder.flush();
|
|
||||||
JSONObject json = new JSONObject(jsonHolder.toString(getEncoding(entity)));
|
|
||||||
jsonHolder.close();
|
|
||||||
|
|
||||||
Message message = Message.obtain(handler, R.id.search_book_contents_succeeded);
|
|
||||||
message.obj = json;
|
|
||||||
message.sendToTarget();
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "HTTP returned " + response.getStatusLine().getStatusCode() + " for " + uri);
|
|
||||||
Message message = Message.obtain(handler, R.id.search_book_contents_failed);
|
|
||||||
message.sendToTarget();
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.w(TAG, "Error accessing book search", e);
|
|
||||||
Message message = Message.obtain(handler, R.id.search_book_contents_failed);
|
|
||||||
message.sendToTarget();
|
|
||||||
} finally {
|
|
||||||
if (client != null) {
|
|
||||||
client.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Book Search requires a cookie to work, which we store persistently. If the cookie does
|
|
||||||
// not exist, this could be the first search or it has expired. Either way, do a quick HEAD
|
|
||||||
// request to fetch it, save it via the CookieSyncManager to flash, then return it.
|
|
||||||
private static String getCookie(String url) {
|
|
||||||
String cookie = CookieManager.getInstance().getCookie(url);
|
|
||||||
if (cookie == null || cookie.length() == 0) {
|
|
||||||
Log.d(TAG, "Book Search cookie was missing or expired");
|
|
||||||
HttpUriRequest head = new HttpHead(url);
|
|
||||||
AndroidHttpClient client = AndroidHttpClient.newInstance(USER_AGENT);
|
|
||||||
try {
|
|
||||||
HttpResponse response = client.execute(head);
|
|
||||||
if (response.getStatusLine().getStatusCode() == 200) {
|
|
||||||
Header[] cookies = response.getHeaders("set-cookie");
|
|
||||||
for (Header theCookie : cookies) {
|
|
||||||
CookieManager.getInstance().setCookie(url, theCookie.getValue());
|
|
||||||
}
|
|
||||||
CookieSyncManager.getInstance().sync();
|
|
||||||
cookie = CookieManager.getInstance().getCookie(url);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.w(TAG, "Error setting book search cookie", e);
|
|
||||||
}
|
|
||||||
client.close();
|
|
||||||
}
|
|
||||||
return cookie;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getEncoding(HttpEntity entity) {
|
|
||||||
// FIXME: The server is returning ISO-8859-1 but the content is actually windows-1252.
|
|
||||||
// Once Jeff fixes the HTTP response, remove this hardcoded value and go back to getting
|
|
||||||
// the encoding dynamically.
|
|
||||||
return "windows-1252";
|
|
||||||
// HeaderElement[] elements = entity.getContentType().getElements();
|
|
||||||
// if (elements != null && elements.length > 0) {
|
|
||||||
// String encoding = elements[0].getParameterByName("charset").getValue();
|
|
||||||
// if (encoding != null && encoding.length() > 0) {
|
|
||||||
// return encoding;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return "UTF-8";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import java.util.Collection;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import com.google.zxing.client.android.HttpHelper;
|
||||||
import com.google.zxing.client.android.LocaleManager;
|
import com.google.zxing.client.android.LocaleManager;
|
||||||
import com.google.zxing.client.android.R;
|
import com.google.zxing.client.android.R;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
|
@ -58,7 +59,8 @@ public final class BookResultInfoRetriever extends SupplementalInfoRetriever {
|
||||||
@Override
|
@Override
|
||||||
void retrieveSupplementalInfo() throws IOException, InterruptedException {
|
void retrieveSupplementalInfo() throws IOException, InterruptedException {
|
||||||
|
|
||||||
String contents = downloadViaHttp("https://www.googleapis.com/books/v1/volumes?q=isbn:" + isbn);
|
String contents = HttpHelper.downloadViaHttp("https://www.googleapis.com/books/v1/volumes?q=isbn:" + isbn,
|
||||||
|
HttpHelper.ContentType.JSON);
|
||||||
|
|
||||||
if (contents.length() == 0) {
|
if (contents.length() == 0) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -20,6 +20,7 @@ import android.content.Context;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import com.google.zxing.client.android.HttpHelper;
|
||||||
import com.google.zxing.client.android.R;
|
import com.google.zxing.client.android.R;
|
||||||
import com.google.zxing.client.android.history.HistoryManager;
|
import com.google.zxing.client.android.history.HistoryManager;
|
||||||
import com.google.zxing.client.android.LocaleManager;
|
import com.google.zxing.client.android.LocaleManager;
|
||||||
|
@ -56,7 +57,7 @@ final class ProductResultInfoRetriever extends SupplementalInfoRetriever {
|
||||||
|
|
||||||
String encodedProductID = URLEncoder.encode(productID, "UTF-8");
|
String encodedProductID = URLEncoder.encode(productID, "UTF-8");
|
||||||
String uri = BASE_PRODUCT_URI + encodedProductID;
|
String uri = BASE_PRODUCT_URI + encodedProductID;
|
||||||
String content = downloadViaHttp(uri);
|
String content = HttpHelper.downloadViaHttp(uri, HttpHelper.ContentType.HTML);
|
||||||
|
|
||||||
Matcher matcher = PRODUCT_NAME_PRICE_PATTERN.matcher(content);
|
Matcher matcher = PRODUCT_NAME_PRICE_PATTERN.matcher(content);
|
||||||
if (matcher.find()) {
|
if (matcher.find()) {
|
||||||
|
|
|
@ -24,21 +24,13 @@ import android.text.Spanned;
|
||||||
import android.text.method.LinkMovementMethod;
|
import android.text.method.LinkMovementMethod;
|
||||||
import android.text.style.URLSpan;
|
import android.text.style.URLSpan;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import com.google.zxing.client.android.AndroidHttpClient;
|
|
||||||
import com.google.zxing.client.result.ISBNParsedResult;
|
import com.google.zxing.client.result.ISBNParsedResult;
|
||||||
import com.google.zxing.client.result.ParsedResult;
|
import com.google.zxing.client.result.ParsedResult;
|
||||||
import com.google.zxing.client.result.ProductParsedResult;
|
import com.google.zxing.client.result.ProductParsedResult;
|
||||||
import com.google.zxing.client.result.URIParsedResult;
|
import com.google.zxing.client.result.URIParsedResult;
|
||||||
import com.google.zxing.client.android.history.HistoryManager;
|
import com.google.zxing.client.android.history.HistoryManager;
|
||||||
import org.apache.http.HttpEntity;
|
|
||||||
import org.apache.http.HttpResponse;
|
|
||||||
import org.apache.http.client.methods.HttpGet;
|
|
||||||
import org.apache.http.client.methods.HttpUriRequest;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -156,42 +148,4 @@ public abstract class SupplementalInfoRetriever implements Callable<Void> {
|
||||||
historyManager.addHistoryItemDetails(itemID, newText);
|
historyManager.addHistoryItemDetails(itemID, newText);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static String downloadViaHttp(String uri) throws IOException {
|
|
||||||
HttpUriRequest get = new HttpGet(uri);
|
|
||||||
AndroidHttpClient client = AndroidHttpClient.newInstance(null);
|
|
||||||
HttpResponse response = client.execute(get);
|
|
||||||
int status = response.getStatusLine().getStatusCode();
|
|
||||||
if (status != 200) {
|
|
||||||
throw new IOException();
|
|
||||||
}
|
|
||||||
return consume(response.getEntity());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String consume(HttpEntity entity) throws IOException {
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
InputStream in = null;
|
|
||||||
try {
|
|
||||||
in = entity.getContent();
|
|
||||||
byte[] buffer = new byte[1024];
|
|
||||||
int bytesRead;
|
|
||||||
while ((bytesRead = in.read(buffer)) > 0) {
|
|
||||||
out.write(buffer, 0, bytesRead);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (in != null) {
|
|
||||||
try {
|
|
||||||
in.close();
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
// continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return new String(out.toByteArray(), "UTF-8");
|
|
||||||
} catch (UnsupportedEncodingException uee) {
|
|
||||||
// can't happen
|
|
||||||
throw new IllegalStateException(uee);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,26 +19,18 @@ package com.google.zxing.client.android.result.supplement;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import com.google.zxing.client.android.HttpHelper;
|
||||||
import com.google.zxing.client.android.history.HistoryManager;
|
import com.google.zxing.client.android.history.HistoryManager;
|
||||||
import com.google.zxing.client.android.AndroidHttpClient;
|
|
||||||
import com.google.zxing.client.android.R;
|
import com.google.zxing.client.android.R;
|
||||||
import com.google.zxing.client.result.URIParsedResult;
|
import com.google.zxing.client.result.URIParsedResult;
|
||||||
import org.apache.http.Header;
|
|
||||||
import org.apache.http.HttpResponse;
|
|
||||||
import org.apache.http.client.methods.HttpHead;
|
|
||||||
import org.apache.http.client.methods.HttpUriRequest;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
final class URIResultInfoRetriever extends SupplementalInfoRetriever {
|
final class URIResultInfoRetriever extends SupplementalInfoRetriever {
|
||||||
|
|
||||||
private static final String[] REDIRECTOR_HOSTS = {
|
private static final int MAX_REDIRECTS = 5;
|
||||||
"http://bit.ly/",
|
|
||||||
"http://tinyurl.com/",
|
|
||||||
"http://tr.im/",
|
|
||||||
"http://goo.gl/",
|
|
||||||
"http://ow.ly/",
|
|
||||||
};
|
|
||||||
|
|
||||||
private final URIParsedResult result;
|
private final URIParsedResult result;
|
||||||
private final String redirectString;
|
private final String redirectString;
|
||||||
|
@ -55,44 +47,22 @@ final class URIResultInfoRetriever extends SupplementalInfoRetriever {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void retrieveSupplementalInfo() throws IOException, InterruptedException {
|
void retrieveSupplementalInfo() throws IOException, InterruptedException {
|
||||||
String oldURI = result.getURI();
|
URI oldURI;
|
||||||
String newURI = unredirect(oldURI);
|
try {
|
||||||
|
oldURI = new URI(result.getURI());
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
URI newURI = HttpHelper.unredirect(oldURI);
|
||||||
int count = 0;
|
int count = 0;
|
||||||
while (count < 3 && !oldURI.equals(newURI)) {
|
while (count++ < MAX_REDIRECTS && !oldURI.equals(newURI)) {
|
||||||
append(result.getDisplayResult(), null, new String[] { redirectString + " : " + newURI }, newURI);
|
append(result.getDisplayResult(),
|
||||||
count++;
|
null,
|
||||||
|
new String[] { redirectString + " : " + newURI },
|
||||||
|
newURI.toString());
|
||||||
oldURI = newURI;
|
oldURI = newURI;
|
||||||
newURI = unredirect(newURI);
|
newURI = HttpHelper.unredirect(newURI);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String unredirect(String uri) throws IOException {
|
|
||||||
if (!isRedirector(uri)) {
|
|
||||||
return uri;
|
|
||||||
}
|
|
||||||
HttpUriRequest head = new HttpHead(uri);
|
|
||||||
AndroidHttpClient client = AndroidHttpClient.newInstance(null);
|
|
||||||
HttpResponse response = client.execute(head);
|
|
||||||
int status = response.getStatusLine().getStatusCode();
|
|
||||||
if (status == 301 || status == 302) {
|
|
||||||
Header redirect = response.getFirstHeader("Location");
|
|
||||||
if (redirect != null) {
|
|
||||||
String location = redirect.getValue();
|
|
||||||
if (location != null) {
|
|
||||||
return location;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isRedirector(String uri) {
|
|
||||||
for (String redirectorHost : REDIRECTOR_HOSTS) {
|
|
||||||
if (uri.startsWith(redirectorHost)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue