Add new workers

This commit is contained in:
Mauricio Colli 2017-04-26 16:25:09 -03:00
parent ee592def0c
commit a4b61bf730
7 changed files with 368 additions and 414 deletions

View file

@ -1,216 +0,0 @@
package org.schabi.newpipe.fragments.search;
import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.View;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.search.SearchEngine;
import org.schabi.newpipe.extractor.search.SearchResult;
import org.schabi.newpipe.report.ErrorActivity;
import java.io.IOException;
import java.util.EnumSet;
/**
* Created by Christian Schabesberger on 02.08.16.
*
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
* SearchWorker.java is part of NewPipe.
*
* NewPipe is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NewPipe is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
public class SearchWorker {
private static final String TAG = SearchWorker.class.toString();
public interface SearchWorkerResultListener {
void onResult(SearchResult result);
void onNothingFound(final int stringResource);
void onError(String message);
void onReCaptchaChallenge();
}
private class ResultRunnable implements Runnable {
private final SearchResult result;
private int requestId = 0;
public ResultRunnable(SearchResult result, int requestId) {
this.result = result;
this.requestId = requestId;
}
@Override
public void run() {
if(this.requestId == SearchWorker.this.requestId) {
searchWorkerResultListener.onResult(result);
}
}
}
private class SearchRunnable implements Runnable {
public static final String YOUTUBE = "Youtube";
private final String query;
private final int page;
private final EnumSet<SearchEngine.Filter> filter;
final Handler h = new Handler();
private volatile boolean runs = true;
private Activity a = null;
private int serviceId = -1;
public SearchRunnable(int serviceId,
String query,
int page,
EnumSet<SearchEngine.Filter> filter,
Activity activity,
int requestId) {
this.serviceId = serviceId;
this.query = query;
this.page = page;
this.filter = filter;
this.a = activity;
}
void terminate() {
runs = false;
}
@Override
public void run() {
final String serviceName = NewPipe.getNameOfService(serviceId);
SearchResult result = null;
SearchEngine engine = null;
try {
engine = NewPipe.getService(serviceId)
.getSearchEngineInstance();
} catch(ExtractionException e) {
ErrorActivity.reportError(h, a, e, null, null,
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
Integer.toString(serviceId), query, R.string.general_error));
return;
}
try {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(a);
String searchLanguageKey = a.getString(R.string.search_language_key);
String searchLanguage = sp.getString(searchLanguageKey,
a.getString(R.string.default_language_value));
result = SearchResult
.getSearchResult(engine, query, page, searchLanguage, filter);
if(runs) {
h.post(new ResultRunnable(result, requestId));
}
// look for errors during extraction
// soft errors:
View rootView = a.findViewById(android.R.id.content);
if(result != null &&
!result.errors.isEmpty()) {
Log.e(TAG, "OCCURRED ERRORS DURING SEARCH EXTRACTION:");
for(Throwable e : result.errors) {
e.printStackTrace();
Log.e(TAG, "------");
}
if(result.resultList.isEmpty()&& !result.errors.isEmpty()) {
// if it compleatly failes dont show snackbar, instead show error directlry
ErrorActivity.reportError(h, a, result.errors, null, null,
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
serviceName, query, R.string.parsing_error));
} else {
// if it partly show snackbar
ErrorActivity.reportError(h, a, result.errors, null, rootView,
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
serviceName, query, R.string.light_parsing_error));
}
}
// hard errors:
} catch (ReCaptchaException e) {
h.post(new Runnable() {
@Override
public void run() {
searchWorkerResultListener.onReCaptchaChallenge();
}
});
} catch(IOException e) {
h.post(new Runnable() {
@Override
public void run() {
searchWorkerResultListener.onNothingFound(R.string.network_error);
}
});
e.printStackTrace();
} catch(final SearchEngine.NothingFoundException e) {
h.post(new Runnable() {
@Override
public void run() {
searchWorkerResultListener.onError(e.getMessage());
}
});
} catch(ExtractionException e) {
ErrorActivity.reportError(h, a, e, null, null,
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
serviceName, query, R.string.parsing_error));
//postNewErrorToast(h, R.string.parsing_error);
e.printStackTrace();
} catch(Exception e) {
ErrorActivity.reportError(h, a, e, null, null,
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
/* todo: this shoudl not be assigned static */ YOUTUBE, query, R.string.general_error));
e.printStackTrace();
}
}
}
private static SearchWorker searchWorker = null;
private SearchWorkerResultListener searchWorkerResultListener = null;
private SearchRunnable runnable = null;
private int requestId = 0; //prevents running requests that have already ben expired
public static SearchWorker getInstance() {
return searchWorker == null ? (searchWorker = new SearchWorker()) : searchWorker;
}
public void setSearchWorkerResultListener(SearchWorkerResultListener listener) {
searchWorkerResultListener = listener;
}
private SearchWorker() {
}
public void search(int serviceId,
String query,
int page,
Activity a,
EnumSet<SearchEngine.Filter> filter) {
if(runnable != null) {
terminate();
}
runnable = new SearchRunnable(serviceId, query, page, filter, a, requestId);
Thread thread = new Thread(runnable);
thread.start();
}
public void terminate() {
if (runnable == null) return;
requestId++;
runnable.terminate();
}
}

View file

@ -1,105 +0,0 @@
package org.schabi.newpipe.fragments.search;
import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.widget.Toast;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.SuggestionExtractor;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.report.ErrorActivity;
import java.io.IOException;
import java.util.List;
/**
* Created by Christian Schabesberger on 02.08.16.
*
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
* SuggestionSearchRunnable.java is part of NewPipe.
*
* NewPipe is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NewPipe is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
public class SuggestionSearchRunnable implements Runnable{
/**
* Runnable to update a {@link SuggestionListAdapter}
*/
private class SuggestionResultRunnable implements Runnable{
private final List<String> suggestions;
private SuggestionResultRunnable(List<String> suggestions) {
this.suggestions = suggestions;
}
@Override
public void run() {
adapter.updateAdapter(suggestions);
}
}
private final int serviceId;
private final String query;
private final Handler h = new Handler();
private final Activity a;
private final SuggestionListAdapter adapter;
public SuggestionSearchRunnable(int serviceId, String query,
Activity activity, SuggestionListAdapter adapter) {
this.serviceId = serviceId;
this.query = query;
this.a = activity;
this.adapter = adapter;
}
@Override
public void run() {
try {
SuggestionExtractor se =
NewPipe.getService(serviceId).getSuggestionExtractorInstance();
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(a);
String searchLanguageKey = a.getString(R.string.search_language_key);
String searchLanguage = sp.getString(searchLanguageKey,
a.getString(R.string.default_language_value));
List<String> suggestions = se.suggestionList(query, searchLanguage);
h.post(new SuggestionResultRunnable(suggestions));
} catch (ExtractionException e) {
ErrorActivity.reportError(h, a, e, null, a.findViewById(android.R.id.content),
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
NewPipe.getNameOfService(serviceId), query, R.string.parsing_error));
e.printStackTrace();
} catch (IOException e) {
postNewErrorToast(h, R.string.network_error);
e.printStackTrace();
} catch (Exception e) {
ErrorActivity.reportError(h, a, e, null, a.findViewById(android.R.id.content),
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
NewPipe.getNameOfService(serviceId), query, R.string.general_error));
}
}
private void postNewErrorToast(Handler h, final int stringResource) {
h.post(new Runnable() {
@Override
public void run() {
Toast.makeText(a, a.getString(stringResource),
Toast.LENGTH_SHORT).show();
}
});
}
}

View file

@ -0,0 +1,118 @@
package org.schabi.newpipe.workers;
import android.content.Context;
import android.os.Handler;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
import java.io.InterruptedIOException;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Common properties of Workers
*
* @author mauriciocolli
*/
@SuppressWarnings("WeakerAccess")
public abstract class AbstractWorker extends Thread {
private final AtomicBoolean isRunning = new AtomicBoolean(false);
private final int serviceId;
private Context context;
private Handler handler;
private StreamingService service;
public AbstractWorker(Context context, int serviceId) {
this.context = context;
this.serviceId = serviceId;
this.handler = new Handler(context.getMainLooper());
}
@Override
public void run() {
try {
isRunning.set(true);
service = NewPipe.getService(serviceId);
doWork(serviceId);
} catch (Exception e) {
// Handle the exception only if thread is not interrupted
e.printStackTrace();
if (!isInterrupted() && !(e instanceof InterruptedIOException) && !(e.getCause() instanceof InterruptedIOException)) {
handleException(e, serviceId);
}
} finally {
isRunning.set(false);
}
}
/**
* Here is the place that the heavy work is realized
*
* @param serviceId serviceId that was passed when created this object
*
* @throws Exception these exceptions are handled by the {@link #handleException(Exception, int)}
*/
protected abstract void doWork(int serviceId) throws Exception;
/**
* Method that handle the exception thrown by the {@link #doWork(int)}.
*
* @param exception {@link Exception} that was thrown by {@link #doWork(int)}
*/
protected abstract void handleException(Exception exception, int serviceId);
/**
* Return true if the extraction is not completed yet
*
* @return the value of the AtomicBoolean {@link #isRunning}
*/
public boolean isRunning() {
return isRunning.get();
}
/**
* Cancel this ExtractorWorker, calling {@link #onDestroy()} and interrupting this thread.
* <p>
* <b>Note:</b> Any I/O that is active in the moment that this method is called will be canceled and a Exception will be thrown, because of the {@link #interrupt()}.<br>
* This is useful when you don't want the resulting {@link StreamInfo} anymore, but don't want to waste bandwidth, otherwise it'd run till it receives the StreamInfo.
*/
public void cancel() {
onDestroy();
this.interrupt();
}
/**
* Method that discards everything that doesn't need anymore.<br>
* Subclasses can override this method to destroy their garbage.
*/
protected void onDestroy() {
this.isRunning.set(false);
this.context = null;
this.handler = null;
this.service = null;
}
public Handler getHandler() {
return handler;
}
public StreamingService getService() {
return service;
}
public int getServiceId() {
return serviceId;
}
public String getServiceName() {
return service == null ? "none" : service.getServiceInfo().name;
}
public Context getContext() {
return context;
}
}

View file

@ -31,7 +31,7 @@ public class ChannelExtractorWorker extends ExtractorWorker {
* Interface which will be called for result and errors * Interface which will be called for result and errors
*/ */
public interface OnChannelInfoReceive { public interface OnChannelInfoReceive {
void onReceive(ChannelInfo info); void onReceive(ChannelInfo info, boolean onlyVideos);
void onError(int messageId); void onError(int messageId);
/** /**
* Called when an unrecoverable error has occurred. * Called when an unrecoverable error has occurred.
@ -44,12 +44,15 @@ public class ChannelExtractorWorker extends ExtractorWorker {
* @param context context for error reporting purposes * @param context context for error reporting purposes
* @param serviceId id of the request service * @param serviceId id of the request service
* @param channelUrl channelUrl of the service (e.g. https://www.youtube.com/channel/UC_aEa8K-EOJ3D6gOs7HcyNg) * @param channelUrl channelUrl of the service (e.g. https://www.youtube.com/channel/UC_aEa8K-EOJ3D6gOs7HcyNg)
* @param pageNumber which page to extract
* @param onlyVideos flag that will be send by {@link OnChannelInfoReceive#onReceive(ChannelInfo, boolean)}
* @param callback listener that will be called-back when events occur (check {@link ChannelExtractorWorker.OnChannelInfoReceive}) * @param callback listener that will be called-back when events occur (check {@link ChannelExtractorWorker.OnChannelInfoReceive})
*/ */
public ChannelExtractorWorker(Context context, int serviceId, String channelUrl, int pageNumber, OnChannelInfoReceive callback) { public ChannelExtractorWorker(Context context, int serviceId, String channelUrl, int pageNumber, boolean onlyVideos, OnChannelInfoReceive callback) {
super(context, channelUrl, serviceId); super(context, channelUrl, serviceId);
this.pageNumber = pageNumber; this.pageNumber = pageNumber;
this.callback = callback; this.callback = callback;
this.onlyVideos = onlyVideos;
} }
@Override @Override
@ -71,7 +74,7 @@ public class ChannelExtractorWorker extends ExtractorWorker {
public void run() { public void run() {
if (isInterrupted() || callback == null) return; if (isInterrupted() || callback == null) return;
callback.onReceive(channelInfo); callback.onReceive(channelInfo, onlyVideos);
onDestroy(); onDestroy();
} }
}); });
@ -107,13 +110,5 @@ public class ChannelExtractorWorker extends ExtractorWorker {
}); });
} }
} }
public boolean isOnlyVideos() {
return onlyVideos;
}
public void setOnlyVideos(boolean onlyVideos) {
this.onlyVideos = onlyVideos;
}
} }

View file

@ -2,18 +2,12 @@ package org.schabi.newpipe.workers;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.os.Handler;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.ErrorActivity;
import java.io.InterruptedIOException;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/** /**
* Common properties of ExtractorWorkers * Common properties of ExtractorWorkers
@ -21,38 +15,18 @@ import java.util.concurrent.atomic.AtomicBoolean;
* @author mauriciocolli * @author mauriciocolli
*/ */
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
public abstract class ExtractorWorker extends Thread { public abstract class ExtractorWorker extends AbstractWorker {
private final AtomicBoolean isRunning = new AtomicBoolean(false);
private final String url; private final String url;
private final int serviceId;
private Context context;
private Handler handler;
private StreamingService service;
public ExtractorWorker(Context context, String url, int serviceId) { public ExtractorWorker(Context context, String url, int serviceId) {
this.context = context; super(context, serviceId);
this.url = url; this.url = url;
this.serviceId = serviceId;
this.handler = new Handler(context.getMainLooper());
if (url.length() >= 40) setName("Thread-" + url.substring(url.length() - 11, url.length())); if (url.length() >= 40) setName("Thread-" + url.substring(url.length() - 11, url.length()));
} }
@Override @Override
public void run() { protected void doWork(int serviceId) throws Exception {
try { doWork(serviceId, url);
isRunning.set(true);
service = NewPipe.getService(serviceId);
doWork(serviceId, url);
} catch (Exception e) {
// Handle the exception only if thread is not interrupted
if (!isInterrupted() && !(e instanceof InterruptedIOException) && !(e.getCause() instanceof InterruptedIOException)) {
handleException(e, serviceId, url);
}
} finally {
isRunning.set(false);
}
} }
/** /**
@ -65,6 +39,10 @@ public abstract class ExtractorWorker extends Thread {
*/ */
protected abstract void doWork(int serviceId, String url) throws Exception; protected abstract void doWork(int serviceId, String url) throws Exception;
@Override
protected void handleException(Exception exception, int serviceId) {
handleException(exception, serviceId, url);
}
/** /**
* Method that handle the exception thrown by the {@link #doWork(int, String)}. * Method that handle the exception thrown by the {@link #doWork(int, String)}.
@ -99,63 +77,12 @@ public abstract class ExtractorWorker extends Thread {
} }
if (getContext() instanceof Activity) { if (getContext() instanceof Activity) {
View rootView = getContext() != null ? ((Activity) getContext()).findViewById(android.R.id.content) : null; View rootView = getContext() instanceof Activity ? ((Activity) getContext()).findViewById(android.R.id.content) : null;
ErrorActivity.reportError(getHandler(), getContext(), errorsList, null, rootView, ErrorActivity.ErrorInfo.make(errorUserAction, getServiceName(), url, 0 /* no message for the user */)); ErrorActivity.reportError(getHandler(), getContext(), errorsList, null, rootView, ErrorActivity.ErrorInfo.make(errorUserAction, getServiceName(), url, 0 /* no message for the user */));
} }
} }
/**
* Return true if the extraction is not completed yet
*
* @return the value of the AtomicBoolean {@link #isRunning}
*/
public boolean isRunning() {
return isRunning.get();
}
/**
* Cancel this ExtractorWorker, calling {@link #onDestroy()} and interrupting this thread.
* <p>
* <b>Note:</b> Any I/O that is active in the moment that this method is called will be canceled and a Exception will be thrown, because of the {@link #interrupt()}.<br>
* This is useful when you don't want the resulting {@link StreamInfo} anymore, but don't want to waste bandwidth, otherwise it'd run till it receives the StreamInfo.
*/
public void cancel() {
onDestroy();
this.interrupt();
}
/**
* Method that discards everything that doesn't need anymore.<br>
* Subclasses can override this method to destroy their garbage.
*/
protected void onDestroy() {
this.isRunning.set(false);
this.context = null;
this.handler = null;
this.service = null;
}
public Handler getHandler() {
return handler;
}
public String getUrl() { public String getUrl() {
return url; return url;
} }
public StreamingService getService() {
return service;
}
public int getServiceId() {
return serviceId;
}
public String getServiceName() {
return service == null ? "none" : service.getServiceInfo().name;
}
public Context getContext() {
return context;
}
} }

View file

@ -0,0 +1,127 @@
package org.schabi.newpipe.workers;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.view.View;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.search.SearchEngine;
import org.schabi.newpipe.extractor.search.SearchResult;
import org.schabi.newpipe.report.ErrorActivity;
import java.io.IOException;
import java.util.EnumSet;
/**
* Return list of results based on a query
*
* @author mauriciocolli
*/
public class SearchWorker extends AbstractWorker {
private EnumSet<SearchEngine.Filter> filter;
private String query;
private int page;
private OnSearchResult callback;
/**
* Interface which will be called for result and errors
*/
public interface OnSearchResult {
void onSearchResult(SearchResult result);
void onNothingFound(String message);
void onSearchError(int messageId);
void onReCaptchaChallenge();
}
public SearchWorker(Context context, int serviceId, String query, int page, EnumSet<SearchEngine.Filter> filter, OnSearchResult callback) {
super(context, serviceId);
this.callback = callback;
this.query = query;
this.page = page;
this.filter = filter;
}
public static SearchWorker startForQuery(Context context, int serviceId, @NonNull String query, int page, EnumSet<SearchEngine.Filter> filter, OnSearchResult callback) {
SearchWorker worker = new SearchWorker(context, serviceId, query, page, filter, callback);
worker.start();
return worker;
}
@Override
protected void onDestroy() {
super.onDestroy();
this.callback = null;
}
@Override
protected void doWork(int serviceId) throws Exception {
SearchEngine searchEngine = getService().getSearchEngineInstance();
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
String searchLanguageKey = getContext().getString(R.string.search_language_key);
String searchLanguage = sharedPreferences.getString(searchLanguageKey, getContext().getString(R.string.default_language_value));
final SearchResult searchResult = SearchResult.getSearchResult(searchEngine, query, page, searchLanguage, filter);
if (callback != null && searchResult != null && !isInterrupted()) getHandler().post(new Runnable() {
@Override
public void run() {
if (isInterrupted() || callback == null) return;
callback.onSearchResult(searchResult);
onDestroy();
}
});
}
@Override
protected void handleException(final Exception exception, int serviceId) {
if (callback == null || getHandler() == null || isInterrupted()) return;
if (exception instanceof ReCaptchaException) {
getHandler().post(new Runnable() {
@Override
public void run() {
callback.onReCaptchaChallenge();
}
});
} else if (exception instanceof IOException) {
getHandler().post(new Runnable() {
@Override
public void run() {
callback.onSearchError(R.string.network_error);
}
});
} else if (exception instanceof SearchEngine.NothingFoundException) {
getHandler().post(new Runnable() {
@Override
public void run() {
callback.onNothingFound(exception.getMessage());
}
});
} else if (exception instanceof ExtractionException) {
View rootView = getContext() instanceof Activity ? ((Activity) getContext()).findViewById(android.R.id.content) : null;
ErrorActivity.reportError(getHandler(), getContext(), exception, null, rootView, ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED, getServiceName(), query, R.string.parsing_error));
getHandler().post(new Runnable() {
@Override
public void run() {
callback.onSearchError(R.string.parsing_error);
}
});
} else {
View rootView = getContext() instanceof Activity ? ((Activity) getContext()).findViewById(android.R.id.content) : null;
ErrorActivity.reportError(getHandler(), getContext(), exception, null, rootView, ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED, getServiceName(), query, R.string.general_error));
getHandler().post(new Runnable() {
@Override
public void run() {
callback.onSearchError(R.string.general_error);
}
});
}
}
}

View file

@ -0,0 +1,108 @@
package org.schabi.newpipe.workers;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.view.View;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.SuggestionExtractor;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.report.ErrorActivity;
import java.io.IOException;
import java.util.List;
/**
* Worker that get suggestions based on the query
*
* @author mauriciocolli
*/
public class SuggestionWorker extends AbstractWorker {
private String query;
private OnSuggestionResult callback;
/**
* Interface which will be called for result and errors
*/
public interface OnSuggestionResult {
void onSuggestionResult(@NonNull List<String> suggestions);
void onSuggestionError(int messageId);
}
public SuggestionWorker(Context context, int serviceId, String query, OnSuggestionResult callback) {
super(context, serviceId);
this.callback = callback;
this.query = query;
}
public static SuggestionWorker startForQuery(Context context, int serviceId, @NonNull String query, OnSuggestionResult callback) {
SuggestionWorker worker = new SuggestionWorker(context, serviceId, query, callback);
worker.start();
return worker;
}
@Override
protected void onDestroy() {
super.onDestroy();
this.callback = null;
this.query = null;
}
@Override
protected void doWork(int serviceId) throws Exception {
SuggestionExtractor suggestionExtractor = getService().getSuggestionExtractorInstance();
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
String searchLanguageKey = getContext().getString(R.string.search_language_key);
String searchLanguage = sharedPreferences.getString(searchLanguageKey, getContext().getString(R.string.default_language_value));
final List<String> suggestions = suggestionExtractor.suggestionList(query, searchLanguage);
if (callback != null && suggestions != null && !isInterrupted()) getHandler().post(new Runnable() {
@Override
public void run() {
if (isInterrupted() || callback == null) return;
callback.onSuggestionResult(suggestions);
onDestroy();
}
});
}
@Override
protected void handleException(final Exception exception, int serviceId) {
if (callback == null || getHandler() == null || isInterrupted()) return;
if (exception instanceof ExtractionException) {
View rootView = getContext() instanceof Activity ? ((Activity) getContext()).findViewById(android.R.id.content) : null;
ErrorActivity.reportError(getHandler(), getContext(), exception, null, rootView, ErrorActivity.ErrorInfo.make(ErrorActivity.GET_SUGGESTIONS, getServiceName(), query, R.string.parsing_error));
getHandler().post(new Runnable() {
@Override
public void run() {
callback.onSuggestionError(R.string.parsing_error);
}
});
} else if (exception instanceof IOException) {
getHandler().post(new Runnable() {
@Override
public void run() {
callback.onSuggestionError(R.string.network_error);
}
});
} else {
View rootView = getContext() instanceof Activity ? ((Activity) getContext()).findViewById(android.R.id.content) : null;
ErrorActivity.reportError(getHandler(), getContext(), exception, null, rootView, ErrorActivity.ErrorInfo.make(ErrorActivity.GET_SUGGESTIONS, getServiceName(), query, R.string.general_error));
getHandler().post(new Runnable() {
@Override
public void run() {
callback.onSuggestionError(R.string.general_error);
}
});
}
}
}