Browse Source

Create common suggestions task that is extended by Duck and Google suggestions

Also remove old suggestions task that was unused
master
Anthony Restaino 8 years ago
parent
commit
84dd37d51d
  1. 121
      app/src/main/java/acr/browser/lightning/search/BaseSuggestionsTask.java
  2. 189
      app/src/main/java/acr/browser/lightning/search/DuckSuggestionsTask.java
  3. 190
      app/src/main/java/acr/browser/lightning/search/GoogleSuggestionsTask.java

121
app/src/main/java/acr/browser/lightning/search/RetrieveSuggestionsTask.java → app/src/main/java/acr/browser/lightning/search/BaseSuggestionsTask.java

@ -4,58 +4,53 @@ import android.app.Application; @@ -4,58 +4,53 @@ import android.app.Application;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.ref.WeakReference;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPInputStream;
import javax.net.ssl.HttpsURLConnection;
import acr.browser.lightning.R;
import acr.browser.lightning.database.HistoryItem;
import acr.browser.lightning.utils.Utils;
class RetrieveSuggestionsTask extends AsyncTask<Void, Void, List<HistoryItem>> {
abstract class BaseSuggestionsTask {
private static final String TAG = RetrieveSuggestionsTask.class.getSimpleName();
private static final String TAG = BaseSuggestionsTask.class.getSimpleName();
private static final Pattern SPACE_PATTERN = Pattern.compile(" ", Pattern.LITERAL);
private static final String CACHE_FILE_TYPE = ".sgg";
private static final String ENCODING = "ISO-8859-1";
private static final long INTERVAL_DAY = 86400000;
private static final long INTERVAL_DAY = TimeUnit.DAYS.toMillis(1);
private static final String DEFAULT_LANGUAGE = "en";
@Nullable private static XmlPullParser sXpp;
@Nullable private static String sLanguage;
@NonNull private final WeakReference<SuggestionsResult> mResultCallback;
@NonNull private final SuggestionsResult mResultCallback;
@NonNull private final Application mApplication;
@NonNull private final String mSearchSubtitle;
@NonNull private String mQuery;
public RetrieveSuggestionsTask(@NonNull String query,
@NonNull SuggestionsResult callback,
@NonNull Application application) {
protected abstract String getQueryUrl(@NonNull String query, @NonNull String language);
protected abstract void parseResults(FileInputStream inputStream, List<HistoryItem> results) throws Exception;
protected abstract String getEncoding();
BaseSuggestionsTask(@NonNull String query,
@NonNull Application application,
@NonNull SuggestionsResult callback) {
mQuery = query;
mResultCallback = new WeakReference<>(callback);
mResultCallback = callback;
mApplication = application;
mSearchSubtitle = mApplication.getString(R.string.suggestion);
}
@NonNull
@ -69,63 +64,34 @@ class RetrieveSuggestionsTask extends AsyncTask<Void, Void, List<HistoryItem>> { @@ -69,63 +64,34 @@ class RetrieveSuggestionsTask extends AsyncTask<Void, Void, List<HistoryItem>> {
return sLanguage;
}
@NonNull
private static synchronized XmlPullParser getParser() throws XmlPullParserException {
if (sXpp == null) {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
sXpp = factory.newPullParser();
}
return sXpp;
}
@NonNull
@Override
protected List<HistoryItem> doInBackground(Void... voids) {
void run() {
List<HistoryItem> filter = new ArrayList<>(5);
try {
mQuery = SPACE_PATTERN.matcher(mQuery).replaceAll("+");
URLEncoder.encode(mQuery, ENCODING);
mQuery = URLEncoder.encode(mQuery, getEncoding());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
Log.e(TAG, "Unable to encode the URL", e);
}
File cache = downloadSuggestionsForQuery(mQuery, getLanguage(), mApplication);
if (!cache.exists()) {
return filter;
post(filter);
return;
}
InputStream fileInput = null;
FileInputStream fileInput = null;
try {
fileInput = new BufferedInputStream(new FileInputStream(cache));
XmlPullParser parser = getParser();
parser.setInput(fileInput, ENCODING);
int eventType = parser.getEventType();
int counter = 0;
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG && "suggestion".equals(parser.getName())) {
String suggestion = parser.getAttributeValue(null, "data");
filter.add(new HistoryItem(mSearchSubtitle + " \"" + suggestion + '"',
suggestion, R.drawable.ic_search));
counter++;
if (counter >= 5) {
break;
}
}
eventType = parser.next();
}
fileInput = new FileInputStream(cache);
parseResults(fileInput, filter);
} catch (Exception e) {
return filter;
post(filter);
Log.e(TAG, "Unable to parse results", e);
return;
} finally {
Utils.close(fileInput);
}
return filter;
post(filter);
}
@Override
protected void onPostExecute(@NonNull List<HistoryItem> result) {
SuggestionsResult callback = mResultCallback.get();
if (callback != null) {
callback.resultReceived(result);
}
private void post(@NonNull List<HistoryItem> result) {
mResultCallback.resultReceived(result);
}
/**
@ -136,8 +102,9 @@ class RetrieveSuggestionsTask extends AsyncTask<Void, Void, List<HistoryItem>> { @@ -136,8 +102,9 @@ class RetrieveSuggestionsTask extends AsyncTask<Void, Void, List<HistoryItem>> {
* @return the cache file containing the suggestions
*/
@NonNull
private static File downloadSuggestionsForQuery(@NonNull String query, String language, @NonNull Application app) {
File cacheFile = new File(app.getCacheDir(), query.hashCode() + CACHE_FILE_TYPE);
private File downloadSuggestionsForQuery(@NonNull String query, String language, @NonNull Application app) {
String queryUrl = getQueryUrl(query, language);
File cacheFile = new File(app.getCacheDir(), queryUrl.hashCode() + Suggestions.CACHE_FILE_TYPE);
if (System.currentTimeMillis() - INTERVAL_DAY < cacheFile.lastModified()) {
return cacheFile;
}
@ -147,22 +114,22 @@ class RetrieveSuggestionsTask extends AsyncTask<Void, Void, List<HistoryItem>> { @@ -147,22 +114,22 @@ class RetrieveSuggestionsTask extends AsyncTask<Void, Void, List<HistoryItem>> {
InputStream in = null;
FileOutputStream fos = null;
try {
// Old API that doesn't support HTTPS
// http://google.com/complete/search?q= + query + &output=toolbar&hl= + language
URL url = new URL("https://suggestqueries.google.com/complete/search?output=toolbar&hl="
+ language + "&q=" + query);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
URL url = new URL(queryUrl);
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setDoInput(true);
connection.setRequestProperty("Accept-Encoding", "gzip");
connection.connect();
if (connection.getResponseCode() >= HttpURLConnection.HTTP_MULT_CHOICE ||
connection.getResponseCode() < HttpURLConnection.HTTP_OK) {
connection.getResponseCode() < HttpURLConnection.HTTP_OK) {
Log.e(TAG, "Search API Responded with code: " + connection.getResponseCode());
connection.disconnect();
return cacheFile;
}
in = connection.getInputStream();
if (in != null) {
in = new GZIPInputStream(in);
//noinspection IOResourceOpenedButNotSafelyClosed
fos = new FileOutputStream(cacheFile);
int buffer;
@ -190,12 +157,12 @@ class RetrieveSuggestionsTask extends AsyncTask<Void, Void, List<HistoryItem>> { @@ -190,12 +157,12 @@ class RetrieveSuggestionsTask extends AsyncTask<Void, Void, List<HistoryItem>> {
@Nullable
private static NetworkInfo getActiveNetworkInfo(@NonNull Context context) {
ConnectivityManager connectivity = (ConnectivityManager) context
.getApplicationContext()
.getSystemService(Context.CONNECTIVITY_SERVICE);
.getApplicationContext()
.getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivity == null) {
return null;
}
return connectivity.getActiveNetworkInfo();
}
}
}

189
app/src/main/java/acr/browser/lightning/search/DuckSuggestionsTask.java

@ -1,202 +1,55 @@ @@ -1,202 +1,55 @@
package acr.browser.lightning.search;
import android.app.Application;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPInputStream;
import javax.net.ssl.HttpsURLConnection;
import acr.browser.lightning.R;
import acr.browser.lightning.database.HistoryItem;
import acr.browser.lightning.utils.FileUtils;
import acr.browser.lightning.utils.Utils;
/**
* Copyright 9/10/2016 Anthony Restaino
* <p/>
* 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
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* 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.
*/
public final class DuckSuggestionsTask {
private static final String TAG = RetrieveSuggestionsTask.class.getSimpleName();
public final class DuckSuggestionsTask extends BaseSuggestionsTask {
private static final String ENCODING = "UTF-8";
private static final long INTERVAL_DAY = TimeUnit.DAYS.toMillis(1);
private static final String DEFAULT_LANGUAGE = "en";
@Nullable private static String sLanguage;
@NonNull private final SuggestionsResult mResultCallback;
@NonNull private final Application mApplication;
@NonNull private final String mSearchSubtitle;
@NonNull private String mQuery;
DuckSuggestionsTask(@NonNull String query,
@NonNull Application application,
@NonNull SuggestionsResult callback) {
mQuery = query;
mResultCallback = callback;
mApplication = application;
mSearchSubtitle = mApplication.getString(R.string.suggestion);
}
@NonNull
private static synchronized String getLanguage() {
if (sLanguage == null) {
sLanguage = Locale.getDefault().getLanguage();
}
if (TextUtils.isEmpty(sLanguage)) {
sLanguage = DEFAULT_LANGUAGE;
}
return sLanguage;
super(query, application, callback);
mSearchSubtitle = application.getString(R.string.suggestion);
}
@NonNull
private static String getQueryUrl(@NonNull String query) {
@Override
protected String getQueryUrl(@NonNull String query, @NonNull String language) {
return "https://duckduckgo.com/ac/?q=" + query;
}
void run() {
List<HistoryItem> filter = new ArrayList<>(5);
try {
mQuery = URLEncoder.encode(mQuery, ENCODING);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
File cache = downloadSuggestionsForQuery(mQuery, getLanguage(), mApplication);
if (!cache.exists()) {
post(filter);
return;
}
InputStream fileInput = null;
try {
fileInput = new FileInputStream(cache);
String content = FileUtils.readStringFromFile(fileInput, ENCODING);
JSONArray jsonArray = new JSONArray(content);
int counter = 0;
for (int n = 0, size = jsonArray.length(); n < size; n++) {
JSONObject object = jsonArray.getJSONObject(n);
String suggestion = object.getString("phrase");
filter.add(new HistoryItem(mSearchSubtitle + " \"" + suggestion + '"',
suggestion, R.drawable.ic_search));
counter++;
if (counter >= 5) {
break;
}
}
} catch (Exception e) {
post(filter);
e.printStackTrace();
return;
} finally {
Utils.close(fileInput);
}
post(filter);
}
private void post(@NonNull List<HistoryItem> result) {
mResultCallback.resultReceived(result);
}
/**
* This method downloads the search suggestions for the specific query.
* NOTE: This is a blocking operation, do not run on the UI thread.
*
* @param query the query to get suggestions for
* @return the cache file containing the suggestions
*/
@NonNull
private static File downloadSuggestionsForQuery(@NonNull String query, String language, @NonNull Application app) {
String queryUrl = getQueryUrl(query);
File cacheFile = new File(app.getCacheDir(), queryUrl.hashCode() + Suggestions.CACHE_FILE_TYPE);
if (System.currentTimeMillis() - INTERVAL_DAY < cacheFile.lastModified()) {
return cacheFile;
}
if (!isNetworkConnected(app)) {
return cacheFile;
}
InputStream in = null;
FileOutputStream fos = null;
try {
URL url = new URL(queryUrl);
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setDoInput(true);
connection.setRequestProperty("Accept-Encoding", "gzip");
connection.connect();
if (connection.getResponseCode() >= HttpURLConnection.HTTP_MULT_CHOICE ||
connection.getResponseCode() < HttpURLConnection.HTTP_OK) {
Log.e(TAG, "Search API Responded with code: " + connection.getResponseCode());
connection.disconnect();
return cacheFile;
@Override
protected void parseResults(FileInputStream inputStream, List<HistoryItem> results) throws Exception {
String content = FileUtils.readStringFromFile(inputStream, ENCODING);
JSONArray jsonArray = new JSONArray(content);
int counter = 0;
for (int n = 0, size = jsonArray.length(); n < size; n++) {
JSONObject object = jsonArray.getJSONObject(n);
String suggestion = object.getString("phrase");
results.add(new HistoryItem(mSearchSubtitle + " \"" + suggestion + '"',
suggestion, R.drawable.ic_search));
counter++;
if (counter >= 5) {
break;
}
in = connection.getInputStream();
if (in != null) {
in = new GZIPInputStream(in);
//noinspection IOResourceOpenedButNotSafelyClosed
fos = new FileOutputStream(cacheFile);
int buffer;
while ((buffer = in.read()) != -1) {
fos.write(buffer);
}
fos.flush();
}
connection.disconnect();
cacheFile.setLastModified(System.currentTimeMillis());
} catch (Exception e) {
Log.w(TAG, "Problem getting search suggestions", e);
} finally {
Utils.close(in);
Utils.close(fos);
}
return cacheFile;
}
private static boolean isNetworkConnected(@NonNull Context context) {
NetworkInfo networkInfo = getActiveNetworkInfo(context);
return networkInfo != null && networkInfo.isConnected();
}
@Nullable
private static NetworkInfo getActiveNetworkInfo(@NonNull Context context) {
ConnectivityManager connectivity = (ConnectivityManager) context
.getApplicationContext()
.getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivity == null) {
return null;
}
return connectivity.getActiveNetworkInfo();
@Override
protected String getEncoding() {
return ENCODING;
}
}

190
app/src/main/java/acr/browser/lightning/search/GoogleSuggestionsTask.java

@ -1,204 +1,72 @@ @@ -1,204 +1,72 @@
package acr.browser.lightning.search;
import android.app.Application;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPInputStream;
import javax.net.ssl.HttpsURLConnection;
import acr.browser.lightning.R;
import acr.browser.lightning.database.HistoryItem;
import acr.browser.lightning.utils.Utils;
public class GoogleSuggestionsTask {
private static final String TAG = RetrieveSuggestionsTask.class.getSimpleName();
public class GoogleSuggestionsTask extends BaseSuggestionsTask {
private static final String ENCODING = "ISO-8859-1";
private static final long INTERVAL_DAY = TimeUnit.DAYS.toMillis(1);
private static final String DEFAULT_LANGUAGE = "en";
@Nullable private static XmlPullParser sXpp;
@Nullable private static String sLanguage;
@NonNull private final SuggestionsResult mResultCallback;
@NonNull private final Application mApplication;
@NonNull private final String mSearchSubtitle;
@NonNull private String mQuery;
GoogleSuggestionsTask(@NonNull String query,
@NonNull Application application,
@NonNull SuggestionsResult callback) {
mQuery = query;
mResultCallback = callback;
mApplication = application;
mSearchSubtitle = mApplication.getString(R.string.suggestion);
}
@NonNull
private static synchronized String getLanguage() {
if (sLanguage == null) {
sLanguage = Locale.getDefault().getLanguage();
}
if (TextUtils.isEmpty(sLanguage)) {
sLanguage = DEFAULT_LANGUAGE;
}
return sLanguage;
super(query, application, callback);
mSearchSubtitle = application.getString(R.string.suggestion);
}
@NonNull
private static String getQueryUrl(@NonNull String query, @NonNull String language) {
protected String getQueryUrl(@NonNull String query, @NonNull String language) {
return "https://suggestqueries.google.com/complete/search?output=toolbar&hl="
+ language + "&q=" + query;
}
@NonNull
private static synchronized XmlPullParser getParser() throws XmlPullParserException {
if (sXpp == null) {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
sXpp = factory.newPullParser();
}
return sXpp;
}
void run() {
List<HistoryItem> filter = new ArrayList<>(5);
try {
mQuery = URLEncoder.encode(mQuery, ENCODING);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
File cache = downloadSuggestionsForQuery(mQuery, getLanguage(), mApplication);
if (!cache.exists()) {
post(filter);
return;
}
InputStream fileInput = null;
try {
fileInput = new BufferedInputStream(new FileInputStream(cache));
XmlPullParser parser = getParser();
parser.setInput(fileInput, ENCODING);
int eventType = parser.getEventType();
int counter = 0;
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG && "suggestion".equals(parser.getName())) {
String suggestion = parser.getAttributeValue(null, "data");
filter.add(new HistoryItem(mSearchSubtitle + " \"" + suggestion + '"',
suggestion, R.drawable.ic_search));
counter++;
if (counter >= 5) {
break;
}
@Override
protected void parseResults(FileInputStream inputStream, List<HistoryItem> results) throws Exception {
BufferedInputStream fileInput = new BufferedInputStream(inputStream);
XmlPullParser parser = getParser();
parser.setInput(fileInput, ENCODING);
int eventType = parser.getEventType();
int counter = 0;
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG && "suggestion".equals(parser.getName())) {
String suggestion = parser.getAttributeValue(null, "data");
results.add(new HistoryItem(mSearchSubtitle + " \"" + suggestion + '"',
suggestion, R.drawable.ic_search));
counter++;
if (counter >= 5) {
break;
}
eventType = parser.next();
}
} catch (Exception e) {
e.printStackTrace();
post(filter);
return;
} finally {
Utils.close(fileInput);
eventType = parser.next();
}
post(filter);
}
private void post(@NonNull List<HistoryItem> result) {
mResultCallback.resultReceived(result);
@Override
protected String getEncoding() {
return ENCODING;
}
/**
* This method downloads the search suggestions for the specific query.
* NOTE: This is a blocking operation, do not run on the UI thread.
*
* @param query the query to get suggestions for
* @return the cache file containing the suggestions
*/
@NonNull
private static File downloadSuggestionsForQuery(@NonNull String query, String language, @NonNull Application app) {
String queryUrl = getQueryUrl(query, language);
File cacheFile = new File(app.getCacheDir(), queryUrl.hashCode() + Suggestions.CACHE_FILE_TYPE);
if (System.currentTimeMillis() - INTERVAL_DAY < cacheFile.lastModified()) {
return cacheFile;
}
if (!isNetworkConnected(app)) {
return cacheFile;
}
InputStream in = null;
FileOutputStream fos = null;
try {
// Old API that doesn't support HTTPS
// http://google.com/complete/search?q= + query + &output=toolbar&hl= + language
URL url = new URL(queryUrl);
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setDoInput(true);
connection.setRequestProperty("Accept-Encoding", "gzip");
connection.connect();
if (connection.getResponseCode() >= HttpURLConnection.HTTP_MULT_CHOICE ||
connection.getResponseCode() < HttpURLConnection.HTTP_OK) {
Log.e(TAG, "Search API Responded with code: " + connection.getResponseCode());
connection.disconnect();
return cacheFile;
}
in = connection.getInputStream();
if (in != null) {
//noinspection IOResourceOpenedButNotSafelyClosed
in = new GZIPInputStream(in);
fos = new FileOutputStream(cacheFile);
int buffer;
while ((buffer = in.read()) != -1) {
fos.write(buffer);
}
fos.flush();
}
connection.disconnect();
cacheFile.setLastModified(System.currentTimeMillis());
} catch (Exception e) {
Log.w(TAG, "Problem getting search suggestions", e);
} finally {
Utils.close(in);
Utils.close(fos);
}
return cacheFile;
}
private static boolean isNetworkConnected(@NonNull Context context) {
NetworkInfo networkInfo = getActiveNetworkInfo(context);
return networkInfo != null && networkInfo.isConnected();
}
@Nullable
private static NetworkInfo getActiveNetworkInfo(@NonNull Context context) {
ConnectivityManager connectivity = (ConnectivityManager) context
.getApplicationContext()
.getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivity == null) {
return null;
private static synchronized XmlPullParser getParser() throws XmlPullParserException {
if (sXpp == null) {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
sXpp = factory.newPullParser();
}
return connectivity.getActiveNetworkInfo();
return sXpp;
}
}

Loading…
Cancel
Save