Create common suggestions task that is extended by Duck and Google suggestions
Also remove old suggestions task that was unused
This commit is contained in:
parent
9c8281f56c
commit
84dd37d51d
@ -4,58 +4,53 @@ import android.app.Application;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.NetworkInfo;
|
import android.net.NetworkInfo;
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
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.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
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.database.HistoryItem;
|
||||||
import acr.browser.lightning.utils.Utils;
|
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 long INTERVAL_DAY = TimeUnit.DAYS.toMillis(1);
|
||||||
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 String DEFAULT_LANGUAGE = "en";
|
private static final String DEFAULT_LANGUAGE = "en";
|
||||||
@Nullable private static XmlPullParser sXpp;
|
|
||||||
@Nullable private static String sLanguage;
|
@Nullable private static String sLanguage;
|
||||||
@NonNull private final WeakReference<SuggestionsResult> mResultCallback;
|
@NonNull private final SuggestionsResult mResultCallback;
|
||||||
@NonNull private final Application mApplication;
|
@NonNull private final Application mApplication;
|
||||||
@NonNull private final String mSearchSubtitle;
|
|
||||||
@NonNull private String mQuery;
|
@NonNull private String mQuery;
|
||||||
|
|
||||||
public RetrieveSuggestionsTask(@NonNull String query,
|
protected abstract String getQueryUrl(@NonNull String query, @NonNull String language);
|
||||||
@NonNull SuggestionsResult callback,
|
|
||||||
@NonNull Application application) {
|
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;
|
mQuery = query;
|
||||||
mResultCallback = new WeakReference<>(callback);
|
mResultCallback = callback;
|
||||||
mApplication = application;
|
mApplication = application;
|
||||||
mSearchSubtitle = mApplication.getString(R.string.suggestion);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@ -69,63 +64,34 @@ class RetrieveSuggestionsTask extends AsyncTask<Void, Void, List<HistoryItem>> {
|
|||||||
return sLanguage;
|
return sLanguage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
void run() {
|
||||||
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) {
|
|
||||||
List<HistoryItem> filter = new ArrayList<>(5);
|
List<HistoryItem> filter = new ArrayList<>(5);
|
||||||
try {
|
try {
|
||||||
mQuery = SPACE_PATTERN.matcher(mQuery).replaceAll("+");
|
mQuery = URLEncoder.encode(mQuery, getEncoding());
|
||||||
URLEncoder.encode(mQuery, ENCODING);
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
e.printStackTrace();
|
Log.e(TAG, "Unable to encode the URL", e);
|
||||||
}
|
}
|
||||||
File cache = downloadSuggestionsForQuery(mQuery, getLanguage(), mApplication);
|
File cache = downloadSuggestionsForQuery(mQuery, getLanguage(), mApplication);
|
||||||
if (!cache.exists()) {
|
if (!cache.exists()) {
|
||||||
return filter;
|
post(filter);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
InputStream fileInput = null;
|
FileInputStream fileInput = null;
|
||||||
try {
|
try {
|
||||||
fileInput = new BufferedInputStream(new FileInputStream(cache));
|
fileInput = new FileInputStream(cache);
|
||||||
XmlPullParser parser = getParser();
|
parseResults(fileInput, filter);
|
||||||
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();
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return filter;
|
post(filter);
|
||||||
|
Log.e(TAG, "Unable to parse results", e);
|
||||||
|
return;
|
||||||
} finally {
|
} finally {
|
||||||
Utils.close(fileInput);
|
Utils.close(fileInput);
|
||||||
}
|
}
|
||||||
return filter;
|
post(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void post(@NonNull List<HistoryItem> result) {
|
||||||
protected void onPostExecute(@NonNull List<HistoryItem> result) {
|
mResultCallback.resultReceived(result);
|
||||||
SuggestionsResult callback = mResultCallback.get();
|
|
||||||
if (callback != null) {
|
|
||||||
callback.resultReceived(result);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -136,8 +102,9 @@ class RetrieveSuggestionsTask extends AsyncTask<Void, Void, List<HistoryItem>> {
|
|||||||
* @return the cache file containing the suggestions
|
* @return the cache file containing the suggestions
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
private static File downloadSuggestionsForQuery(@NonNull String query, String language, @NonNull Application app) {
|
private File downloadSuggestionsForQuery(@NonNull String query, String language, @NonNull Application app) {
|
||||||
File cacheFile = new File(app.getCacheDir(), query.hashCode() + CACHE_FILE_TYPE);
|
String queryUrl = getQueryUrl(query, language);
|
||||||
|
File cacheFile = new File(app.getCacheDir(), queryUrl.hashCode() + Suggestions.CACHE_FILE_TYPE);
|
||||||
if (System.currentTimeMillis() - INTERVAL_DAY < cacheFile.lastModified()) {
|
if (System.currentTimeMillis() - INTERVAL_DAY < cacheFile.lastModified()) {
|
||||||
return cacheFile;
|
return cacheFile;
|
||||||
}
|
}
|
||||||
@ -147,22 +114,22 @@ class RetrieveSuggestionsTask extends AsyncTask<Void, Void, List<HistoryItem>> {
|
|||||||
InputStream in = null;
|
InputStream in = null;
|
||||||
FileOutputStream fos = null;
|
FileOutputStream fos = null;
|
||||||
try {
|
try {
|
||||||
// Old API that doesn't support HTTPS
|
URL url = new URL(queryUrl);
|
||||||
// http://google.com/complete/search?q= + query + &output=toolbar&hl= + language
|
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
|
||||||
URL url = new URL("https://suggestqueries.google.com/complete/search?output=toolbar&hl="
|
|
||||||
+ language + "&q=" + query);
|
|
||||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
|
||||||
connection.setDoInput(true);
|
connection.setDoInput(true);
|
||||||
|
connection.setRequestProperty("Accept-Encoding", "gzip");
|
||||||
connection.connect();
|
connection.connect();
|
||||||
if (connection.getResponseCode() >= HttpURLConnection.HTTP_MULT_CHOICE ||
|
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());
|
Log.e(TAG, "Search API Responded with code: " + connection.getResponseCode());
|
||||||
connection.disconnect();
|
connection.disconnect();
|
||||||
return cacheFile;
|
return cacheFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
in = connection.getInputStream();
|
in = connection.getInputStream();
|
||||||
|
|
||||||
if (in != null) {
|
if (in != null) {
|
||||||
|
in = new GZIPInputStream(in);
|
||||||
//noinspection IOResourceOpenedButNotSafelyClosed
|
//noinspection IOResourceOpenedButNotSafelyClosed
|
||||||
fos = new FileOutputStream(cacheFile);
|
fos = new FileOutputStream(cacheFile);
|
||||||
int buffer;
|
int buffer;
|
||||||
@ -190,12 +157,12 @@ class RetrieveSuggestionsTask extends AsyncTask<Void, Void, List<HistoryItem>> {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private static NetworkInfo getActiveNetworkInfo(@NonNull Context context) {
|
private static NetworkInfo getActiveNetworkInfo(@NonNull Context context) {
|
||||||
ConnectivityManager connectivity = (ConnectivityManager) context
|
ConnectivityManager connectivity = (ConnectivityManager) context
|
||||||
.getApplicationContext()
|
.getApplicationContext()
|
||||||
.getSystemService(Context.CONNECTIVITY_SERVICE);
|
.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
if (connectivity == null) {
|
if (connectivity == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return connectivity.getActiveNetworkInfo();
|
return connectivity.getActiveNetworkInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,202 +1,55 @@
|
|||||||
package acr.browser.lightning.search;
|
package acr.browser.lightning.search;
|
||||||
|
|
||||||
import android.app.Application;
|
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.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
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.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.R;
|
||||||
import acr.browser.lightning.database.HistoryItem;
|
import acr.browser.lightning.database.HistoryItem;
|
||||||
import acr.browser.lightning.utils.FileUtils;
|
import acr.browser.lightning.utils.FileUtils;
|
||||||
import acr.browser.lightning.utils.Utils;
|
|
||||||
|
|
||||||
/**
|
public final class DuckSuggestionsTask extends BaseSuggestionsTask {
|
||||||
* 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();
|
|
||||||
|
|
||||||
private static final String ENCODING = "UTF-8";
|
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 final String mSearchSubtitle;
|
||||||
@NonNull private String mQuery;
|
|
||||||
|
|
||||||
DuckSuggestionsTask(@NonNull String query,
|
DuckSuggestionsTask(@NonNull String query,
|
||||||
@NonNull Application application,
|
@NonNull Application application,
|
||||||
@NonNull SuggestionsResult callback) {
|
@NonNull SuggestionsResult callback) {
|
||||||
mQuery = query;
|
super(query, application, callback);
|
||||||
mResultCallback = callback;
|
mSearchSubtitle = application.getString(R.string.suggestion);
|
||||||
mApplication = application;
|
|
||||||
mSearchSubtitle = mApplication.getString(R.string.suggestion);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@Override
|
||||||
private static synchronized String getLanguage() {
|
protected String getQueryUrl(@NonNull String query, @NonNull String language) {
|
||||||
if (sLanguage == null) {
|
|
||||||
sLanguage = Locale.getDefault().getLanguage();
|
|
||||||
}
|
|
||||||
if (TextUtils.isEmpty(sLanguage)) {
|
|
||||||
sLanguage = DEFAULT_LANGUAGE;
|
|
||||||
}
|
|
||||||
return sLanguage;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private static String getQueryUrl(@NonNull String query) {
|
|
||||||
return "https://duckduckgo.com/ac/?q=" + query;
|
return "https://duckduckgo.com/ac/?q=" + query;
|
||||||
}
|
}
|
||||||
|
|
||||||
void run() {
|
@Override
|
||||||
List<HistoryItem> filter = new ArrayList<>(5);
|
protected void parseResults(FileInputStream inputStream, List<HistoryItem> results) throws Exception {
|
||||||
try {
|
String content = FileUtils.readStringFromFile(inputStream, ENCODING);
|
||||||
mQuery = URLEncoder.encode(mQuery, ENCODING);
|
JSONArray jsonArray = new JSONArray(content);
|
||||||
} catch (UnsupportedEncodingException e) {
|
int counter = 0;
|
||||||
e.printStackTrace();
|
for (int n = 0, size = jsonArray.length(); n < size; n++) {
|
||||||
}
|
JSONObject object = jsonArray.getJSONObject(n);
|
||||||
File cache = downloadSuggestionsForQuery(mQuery, getLanguage(), mApplication);
|
String suggestion = object.getString("phrase");
|
||||||
if (!cache.exists()) {
|
results.add(new HistoryItem(mSearchSubtitle + " \"" + suggestion + '"',
|
||||||
post(filter);
|
suggestion, R.drawable.ic_search));
|
||||||
return;
|
counter++;
|
||||||
}
|
if (counter >= 5) {
|
||||||
InputStream fileInput = null;
|
break;
|
||||||
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) {
|
@Override
|
||||||
mResultCallback.resultReceived(result);
|
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);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,79 +1,65 @@
|
|||||||
package acr.browser.lightning.search;
|
package acr.browser.lightning.search;
|
||||||
|
|
||||||
import android.app.Application;
|
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.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
import org.xmlpull.v1.XmlPullParserFactory;
|
import org.xmlpull.v1.XmlPullParserFactory;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
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.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.R;
|
||||||
import acr.browser.lightning.database.HistoryItem;
|
import acr.browser.lightning.database.HistoryItem;
|
||||||
import acr.browser.lightning.utils.Utils;
|
|
||||||
|
|
||||||
public class GoogleSuggestionsTask {
|
public class GoogleSuggestionsTask extends BaseSuggestionsTask {
|
||||||
|
|
||||||
private static final String TAG = RetrieveSuggestionsTask.class.getSimpleName();
|
|
||||||
|
|
||||||
private static final String ENCODING = "ISO-8859-1";
|
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 XmlPullParser sXpp;
|
||||||
@Nullable private static String sLanguage;
|
|
||||||
@NonNull private final SuggestionsResult mResultCallback;
|
|
||||||
@NonNull private final Application mApplication;
|
|
||||||
@NonNull private final String mSearchSubtitle;
|
@NonNull private final String mSearchSubtitle;
|
||||||
@NonNull private String mQuery;
|
|
||||||
|
|
||||||
GoogleSuggestionsTask(@NonNull String query,
|
GoogleSuggestionsTask(@NonNull String query,
|
||||||
@NonNull Application application,
|
@NonNull Application application,
|
||||||
@NonNull SuggestionsResult callback) {
|
@NonNull SuggestionsResult callback) {
|
||||||
mQuery = query;
|
super(query, application, callback);
|
||||||
mResultCallback = callback;
|
mSearchSubtitle = application.getString(R.string.suggestion);
|
||||||
mApplication = application;
|
|
||||||
mSearchSubtitle = mApplication.getString(R.string.suggestion);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private static synchronized String getLanguage() {
|
protected String getQueryUrl(@NonNull String query, @NonNull String language) {
|
||||||
if (sLanguage == null) {
|
|
||||||
sLanguage = Locale.getDefault().getLanguage();
|
|
||||||
}
|
|
||||||
if (TextUtils.isEmpty(sLanguage)) {
|
|
||||||
sLanguage = DEFAULT_LANGUAGE;
|
|
||||||
}
|
|
||||||
return sLanguage;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private static String getQueryUrl(@NonNull String query, @NonNull String language) {
|
|
||||||
return "https://suggestqueries.google.com/complete/search?output=toolbar&hl="
|
return "https://suggestqueries.google.com/complete/search?output=toolbar&hl="
|
||||||
+ language + "&q=" + query;
|
+ language + "&q=" + query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getEncoding() {
|
||||||
|
return ENCODING;
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private static synchronized XmlPullParser getParser() throws XmlPullParserException {
|
private static synchronized XmlPullParser getParser() throws XmlPullParserException {
|
||||||
if (sXpp == null) {
|
if (sXpp == null) {
|
||||||
@ -83,122 +69,4 @@ public class GoogleSuggestionsTask {
|
|||||||
}
|
}
|
||||||
return sXpp;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
eventType = parser.next();
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
post(filter);
|
|
||||||
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, 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;
|
|
||||||
}
|
|
||||||
return connectivity.getActiveNetworkInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user