2017-09-03 08:04:18 +02:00
|
|
|
package org.schabi.newpipe.fragments.list.search;
|
|
|
|
|
|
|
|
import android.app.Activity;
|
|
|
|
import android.content.Context;
|
|
|
|
import android.content.Intent;
|
|
|
|
import android.content.SharedPreferences;
|
|
|
|
import android.os.Bundle;
|
|
|
|
import android.preference.PreferenceManager;
|
|
|
|
import android.support.annotation.NonNull;
|
|
|
|
import android.support.annotation.Nullable;
|
|
|
|
import android.support.v7.app.ActionBar;
|
2017-09-28 15:06:48 +02:00
|
|
|
import android.support.v7.app.AlertDialog;
|
|
|
|
import android.support.v7.widget.RecyclerView;
|
2017-09-03 08:04:18 +02:00
|
|
|
import android.support.v7.widget.TooltipCompat;
|
|
|
|
import android.text.Editable;
|
|
|
|
import android.text.TextUtils;
|
|
|
|
import android.text.TextWatcher;
|
|
|
|
import android.util.Log;
|
|
|
|
import android.view.KeyEvent;
|
|
|
|
import android.view.LayoutInflater;
|
|
|
|
import android.view.Menu;
|
|
|
|
import android.view.MenuInflater;
|
|
|
|
import android.view.MenuItem;
|
|
|
|
import android.view.View;
|
|
|
|
import android.view.ViewGroup;
|
|
|
|
import android.view.animation.DecelerateInterpolator;
|
|
|
|
import android.view.inputmethod.EditorInfo;
|
|
|
|
import android.view.inputmethod.InputMethodManager;
|
2017-09-28 15:06:48 +02:00
|
|
|
import android.widget.EditText;
|
2017-09-03 08:04:18 +02:00
|
|
|
import android.widget.TextView;
|
|
|
|
|
|
|
|
import org.schabi.newpipe.R;
|
|
|
|
import org.schabi.newpipe.ReCaptchaActivity;
|
2017-09-28 15:06:48 +02:00
|
|
|
import org.schabi.newpipe.database.history.model.SearchHistoryEntry;
|
2017-09-03 08:04:18 +02:00
|
|
|
import org.schabi.newpipe.extractor.InfoItem;
|
|
|
|
import org.schabi.newpipe.extractor.ListExtractor;
|
|
|
|
import org.schabi.newpipe.extractor.NewPipe;
|
2017-09-15 19:27:39 +02:00
|
|
|
import org.schabi.newpipe.extractor.StreamingService;
|
2017-09-03 08:04:18 +02:00
|
|
|
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
|
|
|
import org.schabi.newpipe.extractor.search.SearchEngine;
|
2018-07-08 14:27:12 +02:00
|
|
|
import org.schabi.newpipe.extractor.search.SearchInfo;
|
2017-09-03 08:04:18 +02:00
|
|
|
import org.schabi.newpipe.extractor.search.SearchResult;
|
2018-07-08 14:27:12 +02:00
|
|
|
import org.schabi.newpipe.extractor.uih.SearchQIHandler;
|
2017-09-28 15:06:48 +02:00
|
|
|
import org.schabi.newpipe.fragments.BackPressable;
|
2017-09-03 08:04:18 +02:00
|
|
|
import org.schabi.newpipe.fragments.list.BaseListFragment;
|
2018-04-12 23:46:03 +02:00
|
|
|
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
2018-07-08 14:27:12 +02:00
|
|
|
import org.schabi.newpipe.report.ErrorActivity;
|
2017-09-03 08:04:18 +02:00
|
|
|
import org.schabi.newpipe.report.UserAction;
|
2017-10-08 21:04:37 +02:00
|
|
|
import org.schabi.newpipe.util.Constants;
|
2017-09-28 15:06:48 +02:00
|
|
|
import org.schabi.newpipe.util.AnimationUtils;
|
2017-09-03 08:04:18 +02:00
|
|
|
import org.schabi.newpipe.util.ExtractorHelper;
|
2017-09-28 15:06:48 +02:00
|
|
|
import org.schabi.newpipe.util.LayoutManagerSmoothScroller;
|
2017-09-03 08:04:18 +02:00
|
|
|
import org.schabi.newpipe.util.NavigationHelper;
|
|
|
|
|
2017-09-28 15:06:48 +02:00
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.InterruptedIOException;
|
|
|
|
import java.net.SocketException;
|
2017-09-03 08:04:18 +02:00
|
|
|
import java.util.ArrayList;
|
2017-09-28 15:06:48 +02:00
|
|
|
import java.util.Iterator;
|
2017-09-03 08:04:18 +02:00
|
|
|
import java.util.List;
|
|
|
|
import java.util.Queue;
|
2018-07-08 14:27:12 +02:00
|
|
|
import java.util.concurrent.ExecutionException;
|
2017-09-03 08:04:18 +02:00
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
|
|
import icepick.State;
|
2017-09-28 15:06:48 +02:00
|
|
|
import io.reactivex.Flowable;
|
2017-09-03 08:04:18 +02:00
|
|
|
import io.reactivex.Observable;
|
|
|
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
2017-09-15 19:27:39 +02:00
|
|
|
import io.reactivex.disposables.CompositeDisposable;
|
2017-09-03 08:04:18 +02:00
|
|
|
import io.reactivex.disposables.Disposable;
|
|
|
|
import io.reactivex.schedulers.Schedulers;
|
|
|
|
import io.reactivex.subjects.PublishSubject;
|
|
|
|
|
|
|
|
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
|
|
|
|
2018-03-18 16:37:49 +01:00
|
|
|
public class SearchFragment
|
2018-07-08 14:27:12 +02:00
|
|
|
extends BaseListFragment<SearchInfo, ListExtractor.InfoItemsPage>
|
2018-03-18 16:37:49 +01:00
|
|
|
implements BackPressable {
|
2017-09-03 08:04:18 +02:00
|
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Search
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
|
|
/**
|
2017-09-28 15:06:48 +02:00
|
|
|
* The suggestions will only be fetched from network if the query meet this threshold (>=).
|
|
|
|
* (local ones will be fetched regardless of the length)
|
2017-09-03 08:04:18 +02:00
|
|
|
*/
|
2017-09-28 15:06:48 +02:00
|
|
|
private static final int THRESHOLD_NETWORK_SUGGESTION = 1;
|
2017-09-03 08:04:18 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* How much time have to pass without emitting a item (i.e. the user stop typing) to fetch/show the suggestions, in milliseconds.
|
|
|
|
*/
|
2017-09-28 15:06:48 +02:00
|
|
|
private static final int SUGGESTIONS_DEBOUNCE = 120; //ms
|
2017-09-03 08:04:18 +02:00
|
|
|
|
|
|
|
@State
|
|
|
|
protected int filterItemCheckedId = -1;
|
|
|
|
|
|
|
|
@State
|
2017-10-08 21:04:37 +02:00
|
|
|
protected int serviceId = Constants.NO_SERVICE_ID;
|
2017-09-03 08:04:18 +02:00
|
|
|
@State
|
2018-07-08 14:27:12 +02:00
|
|
|
protected SearchQIHandler searchQuery;
|
2017-09-28 15:06:48 +02:00
|
|
|
@State
|
2018-07-08 14:27:12 +02:00
|
|
|
protected SearchQIHandler lastSearchedQuery;
|
2017-09-03 08:04:18 +02:00
|
|
|
@State
|
|
|
|
protected boolean wasSearchFocused = false;
|
2018-07-08 14:27:12 +02:00
|
|
|
@State
|
|
|
|
List<String> contentFilter;
|
2017-09-03 08:04:18 +02:00
|
|
|
|
2018-07-08 14:27:12 +02:00
|
|
|
private StreamingService service;
|
|
|
|
private String currentPageUrl;
|
|
|
|
private String nextPageUrl;
|
2018-01-09 12:25:40 +01:00
|
|
|
private String contentCountry;
|
2017-09-28 15:06:48 +02:00
|
|
|
private boolean isSuggestionsEnabled = true;
|
2017-10-30 22:04:58 +01:00
|
|
|
private boolean isSearchHistoryEnabled = true;
|
2017-09-03 08:04:18 +02:00
|
|
|
|
|
|
|
private PublishSubject<String> suggestionPublisher = PublishSubject.create();
|
|
|
|
private Disposable searchDisposable;
|
2017-09-28 15:06:48 +02:00
|
|
|
private Disposable suggestionDisposable;
|
2017-09-15 19:27:39 +02:00
|
|
|
private CompositeDisposable disposables = new CompositeDisposable();
|
2017-09-03 08:04:18 +02:00
|
|
|
|
|
|
|
private SuggestionListAdapter suggestionListAdapter;
|
2018-01-27 06:34:17 +01:00
|
|
|
private HistoryRecordManager historyRecordManager;
|
2017-09-03 08:04:18 +02:00
|
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Views
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
|
|
private View searchToolbarContainer;
|
2017-09-28 15:06:48 +02:00
|
|
|
private EditText searchEditText;
|
2017-09-03 08:04:18 +02:00
|
|
|
private View searchClear;
|
|
|
|
|
2017-09-28 15:06:48 +02:00
|
|
|
private View suggestionsPanel;
|
|
|
|
private RecyclerView suggestionsRecyclerView;
|
|
|
|
|
2017-09-03 08:04:18 +02:00
|
|
|
/*////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
2018-07-08 14:27:12 +02:00
|
|
|
public static SearchFragment getInstance(int serviceId, SearchQIHandler query) {
|
2017-09-03 08:04:18 +02:00
|
|
|
SearchFragment searchFragment = new SearchFragment();
|
|
|
|
searchFragment.setQuery(serviceId, query);
|
2017-09-28 15:06:48 +02:00
|
|
|
|
2018-07-08 14:27:12 +02:00
|
|
|
if (!TextUtils.isEmpty(query.getSearchString())) {
|
2017-09-28 15:06:48 +02:00
|
|
|
searchFragment.setSearchOnResume();
|
|
|
|
}
|
|
|
|
|
2017-09-03 08:04:18 +02:00
|
|
|
return searchFragment;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set wasLoading to true so when the fragment onResume is called, the initial search is done.
|
|
|
|
*/
|
2017-09-28 15:06:48 +02:00
|
|
|
private void setSearchOnResume() {
|
|
|
|
wasLoading.set(true);
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Fragment's LifeCycle
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAttach(Context context) {
|
|
|
|
super.onAttach(context);
|
2017-10-30 22:04:58 +01:00
|
|
|
|
2017-09-03 08:04:18 +02:00
|
|
|
suggestionListAdapter = new SuggestionListAdapter(activity);
|
2017-10-30 22:04:58 +01:00
|
|
|
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
|
|
|
|
isSearchHistoryEnabled = preferences.getBoolean(getString(R.string.enable_search_history_key), true);
|
2017-11-14 19:51:44 +01:00
|
|
|
suggestionListAdapter.setShowSuggestionHistory(isSearchHistoryEnabled);
|
2018-01-27 06:34:17 +01:00
|
|
|
|
|
|
|
historyRecordManager = new HistoryRecordManager(context);
|
2017-09-28 15:06:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onCreate(Bundle savedInstanceState) {
|
|
|
|
super.onCreate(savedInstanceState);
|
|
|
|
|
|
|
|
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
|
|
|
|
isSuggestionsEnabled = preferences.getBoolean(getString(R.string.show_search_suggestions_key), true);
|
2018-01-09 12:25:40 +01:00
|
|
|
contentCountry = preferences.getString(getString(R.string.content_country_key), getString(R.string.default_country_value));
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
|
|
|
return inflater.inflate(R.layout.fragment_search, container, false);
|
|
|
|
}
|
|
|
|
|
2017-09-28 15:06:48 +02:00
|
|
|
@Override
|
|
|
|
public void onViewCreated(View rootView, Bundle savedInstanceState) {
|
|
|
|
super.onViewCreated(rootView, savedInstanceState);
|
|
|
|
showSearchOnStart();
|
|
|
|
initSearchListeners();
|
|
|
|
}
|
|
|
|
|
2017-09-03 08:04:18 +02:00
|
|
|
@Override
|
|
|
|
public void onPause() {
|
|
|
|
super.onPause();
|
2017-09-28 15:06:48 +02:00
|
|
|
|
2017-09-03 08:04:18 +02:00
|
|
|
wasSearchFocused = searchEditText.hasFocus();
|
|
|
|
|
|
|
|
if (searchDisposable != null) searchDisposable.dispose();
|
2017-09-28 15:06:48 +02:00
|
|
|
if (suggestionDisposable != null) suggestionDisposable.dispose();
|
2017-09-15 19:27:39 +02:00
|
|
|
if (disposables != null) disposables.clear();
|
2017-09-28 15:06:48 +02:00
|
|
|
hideKeyboardSearch();
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onResume() {
|
|
|
|
if (DEBUG) Log.d(TAG, "onResume() called");
|
|
|
|
super.onResume();
|
|
|
|
|
2018-07-08 14:27:12 +02:00
|
|
|
try {
|
|
|
|
service = NewPipe.getService(serviceId);
|
|
|
|
} catch (Exception e) {
|
|
|
|
ErrorActivity.reportError(getActivity(), e, getActivity().getClass(),
|
|
|
|
getActivity().findViewById(android.R.id.content),
|
|
|
|
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
|
|
|
|
"",
|
|
|
|
"", R.string.general_error));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!TextUtils.isEmpty(searchQuery.getSearchString())) {
|
2017-09-03 08:04:18 +02:00
|
|
|
if (wasLoading.getAndSet(false)) {
|
2018-07-08 14:27:12 +02:00
|
|
|
search(searchQuery);
|
2017-09-03 08:04:18 +02:00
|
|
|
} else if (infoListAdapter.getItemsList().size() == 0) {
|
|
|
|
if (savedState == null) {
|
|
|
|
search(searchQuery);
|
|
|
|
} else if (!isLoading.get() && !wasSearchFocused) {
|
|
|
|
infoListAdapter.clearStreamItemList();
|
|
|
|
showEmptyState();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-28 15:06:48 +02:00
|
|
|
if (suggestionDisposable == null || suggestionDisposable.isDisposed()) initSuggestionObserver();
|
|
|
|
|
2018-07-08 14:27:12 +02:00
|
|
|
if (TextUtils.isEmpty(searchQuery.getSearchString()) || wasSearchFocused) {
|
2017-09-28 15:06:48 +02:00
|
|
|
showKeyboardSearch();
|
|
|
|
showSuggestionsPanel();
|
|
|
|
} else {
|
|
|
|
hideKeyboardSearch();
|
|
|
|
hideSuggestionsPanel();
|
|
|
|
}
|
|
|
|
wasSearchFocused = false;
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onDestroyView() {
|
|
|
|
if (DEBUG) Log.d(TAG, "onDestroyView() called");
|
|
|
|
unsetSearchListeners();
|
|
|
|
super.onDestroyView();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onDestroy() {
|
|
|
|
super.onDestroy();
|
|
|
|
if (searchDisposable != null) searchDisposable.dispose();
|
2017-09-28 15:06:48 +02:00
|
|
|
if (suggestionDisposable != null) suggestionDisposable.dispose();
|
2017-09-15 19:27:39 +02:00
|
|
|
if (disposables != null) disposables.clear();
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
|
|
switch (requestCode) {
|
|
|
|
case ReCaptchaActivity.RECAPTCHA_REQUEST:
|
2018-07-08 14:27:12 +02:00
|
|
|
if (resultCode == Activity.RESULT_OK
|
|
|
|
&& !TextUtils.isEmpty(searchQuery.getSearchString())) {
|
2017-09-03 08:04:18 +02:00
|
|
|
search(searchQuery);
|
|
|
|
} else Log.e(TAG, "ReCaptcha failed");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Log.e(TAG, "Request code from activity not supported [" + requestCode + "]");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-28 15:06:48 +02:00
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Init
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void initViews(View rootView, Bundle savedInstanceState) {
|
|
|
|
super.initViews(rootView, savedInstanceState);
|
|
|
|
suggestionsPanel = rootView.findViewById(R.id.suggestions_panel);
|
|
|
|
suggestionsRecyclerView = rootView.findViewById(R.id.suggestions_list);
|
|
|
|
suggestionsRecyclerView.setAdapter(suggestionListAdapter);
|
|
|
|
suggestionsRecyclerView.setLayoutManager(new LayoutManagerSmoothScroller(activity));
|
|
|
|
|
|
|
|
searchToolbarContainer = activity.findViewById(R.id.toolbar_search_container);
|
|
|
|
searchEditText = searchToolbarContainer.findViewById(R.id.toolbar_search_edit_text);
|
|
|
|
searchClear = searchToolbarContainer.findViewById(R.id.toolbar_search_clear);
|
|
|
|
}
|
|
|
|
|
2017-09-03 08:04:18 +02:00
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// State Saving
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void writeTo(Queue<Object> objectsToSave) {
|
|
|
|
super.writeTo(objectsToSave);
|
2018-07-08 14:27:12 +02:00
|
|
|
objectsToSave.add(currentPageUrl);
|
|
|
|
objectsToSave.add(nextPageUrl);
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void readFrom(@NonNull Queue<Object> savedObjects) throws Exception {
|
|
|
|
super.readFrom(savedObjects);
|
2018-07-08 14:27:12 +02:00
|
|
|
currentPageUrl = (String) savedObjects.poll();
|
|
|
|
nextPageUrl = (String) savedObjects.poll();
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onSaveInstanceState(Bundle bundle) {
|
2018-07-08 14:27:12 +02:00
|
|
|
try {
|
|
|
|
searchQuery = searchEditText != null
|
|
|
|
? NewPipe.getService(serviceId).getSearchQIHFactory().fromQuery(
|
|
|
|
searchEditText.getText().toString())
|
|
|
|
: searchQuery;
|
|
|
|
} catch (Exception e) {
|
|
|
|
ErrorActivity.reportError(getActivity(), e, getActivity().getClass(),
|
|
|
|
getActivity().findViewById(android.R.id.content),
|
|
|
|
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
|
|
|
|
"",
|
|
|
|
"", R.string.general_error));
|
|
|
|
}
|
2017-09-03 08:04:18 +02:00
|
|
|
super.onSaveInstanceState(bundle);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Init's
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void reloadContent() {
|
2018-07-08 14:27:12 +02:00
|
|
|
if (!TextUtils.isEmpty(searchQuery.getSearchString())
|
|
|
|
|| (searchEditText != null && !TextUtils.isEmpty(searchEditText.getText()))) {
|
|
|
|
search(!TextUtils.isEmpty(searchQuery.getSearchString())
|
|
|
|
? searchQuery
|
|
|
|
: getSearchQuery(searchEditText.getText().toString(),
|
|
|
|
new ArrayList<>(0), ""));
|
2017-09-03 08:04:18 +02:00
|
|
|
} else {
|
|
|
|
if (searchEditText != null) {
|
|
|
|
searchEditText.setText("");
|
2017-09-28 15:06:48 +02:00
|
|
|
showKeyboardSearch();
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
animateView(errorPanelRoot, false, 200);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Menu
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
|
|
|
super.onCreateOptionsMenu(menu, inflater);
|
|
|
|
|
|
|
|
ActionBar supportActionBar = activity.getSupportActionBar();
|
|
|
|
if (supportActionBar != null) {
|
|
|
|
supportActionBar.setDisplayShowTitleEnabled(false);
|
|
|
|
supportActionBar.setDisplayHomeAsUpEnabled(true);
|
|
|
|
}
|
|
|
|
|
2018-07-08 14:27:12 +02:00
|
|
|
for(String filter : service.getSearchQIHFactory().getAvailableContentFilter()) {
|
|
|
|
menu.add(filter);
|
|
|
|
}
|
2017-09-03 08:04:18 +02:00
|
|
|
restoreFilterChecked(menu, filterItemCheckedId);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean onOptionsItemSelected(MenuItem item) {
|
2018-07-08 14:27:12 +02:00
|
|
|
|
|
|
|
List<String> contentFilter = new ArrayList<>(1);
|
|
|
|
contentFilter.add(item.getTitle().toString());
|
|
|
|
changeContentFilter(item, contentFilter);
|
|
|
|
|
|
|
|
return true;
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private void restoreFilterChecked(Menu menu, int itemId) {
|
|
|
|
if (itemId != -1) {
|
|
|
|
MenuItem item = menu.findItem(itemId);
|
|
|
|
if (item == null) return;
|
|
|
|
|
|
|
|
item.setChecked(true);
|
2018-07-08 14:27:12 +02:00
|
|
|
contentFilter.clear();
|
|
|
|
contentFilter.add(menu.getItem(itemId).getTitle().toString());
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Search
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
|
|
private TextWatcher textWatcher;
|
|
|
|
|
2017-09-28 15:06:48 +02:00
|
|
|
private void showSearchOnStart() {
|
|
|
|
if (DEBUG) Log.d(TAG, "showSearchOnStart() called, searchQuery → " + searchQuery+", lastSearchedQuery → " + lastSearchedQuery);
|
2018-07-08 14:27:12 +02:00
|
|
|
searchEditText.setText(searchQuery.getSearchString());
|
2017-09-03 08:04:18 +02:00
|
|
|
|
2018-07-08 14:27:12 +02:00
|
|
|
if (TextUtils.isEmpty(searchQuery.getSearchString()) || TextUtils.isEmpty(searchEditText.getText())) {
|
2017-09-03 08:04:18 +02:00
|
|
|
searchToolbarContainer.setTranslationX(100);
|
|
|
|
searchToolbarContainer.setAlpha(0f);
|
|
|
|
searchToolbarContainer.setVisibility(View.VISIBLE);
|
|
|
|
searchToolbarContainer.animate().translationX(0).alpha(1f).setDuration(200).setInterpolator(new DecelerateInterpolator()).start();
|
|
|
|
} else {
|
|
|
|
searchToolbarContainer.setTranslationX(0);
|
|
|
|
searchToolbarContainer.setAlpha(1f);
|
|
|
|
searchToolbarContainer.setVisibility(View.VISIBLE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void initSearchListeners() {
|
2017-09-28 15:06:48 +02:00
|
|
|
if (DEBUG) Log.d(TAG, "initSearchListeners() called");
|
2018-07-08 14:27:12 +02:00
|
|
|
searchClear.setOnClickListener(v -> {
|
|
|
|
if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]");
|
|
|
|
if (TextUtils.isEmpty(searchEditText.getText())) {
|
|
|
|
NavigationHelper.gotoMainFragment(getFragmentManager());
|
|
|
|
return;
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
2018-07-08 14:27:12 +02:00
|
|
|
|
|
|
|
searchEditText.setText("");
|
|
|
|
suggestionListAdapter.setItems(new ArrayList<>());
|
|
|
|
showKeyboardSearch();
|
2017-09-03 08:04:18 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
TooltipCompat.setTooltipText(searchClear, getString(R.string.clear));
|
|
|
|
|
2018-07-08 14:27:12 +02:00
|
|
|
searchEditText.setOnClickListener(v -> {
|
|
|
|
if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]");
|
|
|
|
if (isSuggestionsEnabled && errorPanelRoot.getVisibility() != View.VISIBLE) {
|
|
|
|
showSuggestionsPanel();
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2018-07-08 14:27:12 +02:00
|
|
|
searchEditText.setOnFocusChangeListener((View v, boolean hasFocus) -> {
|
|
|
|
if (DEBUG) Log.d(TAG, "onFocusChange() called with: v = [" + v + "], hasFocus = [" + hasFocus + "]");
|
|
|
|
if (isSuggestionsEnabled && hasFocus && errorPanelRoot.getVisibility() != View.VISIBLE) {
|
|
|
|
showSuggestionsPanel();
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2017-09-28 15:06:48 +02:00
|
|
|
suggestionListAdapter.setListener(new SuggestionListAdapter.OnSuggestionItemSelected() {
|
2017-09-03 08:04:18 +02:00
|
|
|
@Override
|
2017-09-28 15:06:48 +02:00
|
|
|
public void onSuggestionItemSelected(SuggestionItem item) {
|
2018-07-08 14:27:12 +02:00
|
|
|
search(getSearchQuery(item.query, new ArrayList<>(), ""));
|
2017-09-28 15:06:48 +02:00
|
|
|
searchEditText.setText(item.query);
|
|
|
|
}
|
|
|
|
|
2017-11-14 19:51:44 +01:00
|
|
|
@Override
|
|
|
|
public void onSuggestionItemInserted(SuggestionItem item) {
|
|
|
|
searchEditText.setText(item.query);
|
2017-11-15 00:58:13 +01:00
|
|
|
searchEditText.setSelection(searchEditText.getText().length());
|
2017-11-14 19:51:44 +01:00
|
|
|
}
|
|
|
|
|
2017-09-28 15:06:48 +02:00
|
|
|
@Override
|
|
|
|
public void onSuggestionItemLongClick(SuggestionItem item) {
|
|
|
|
if (item.fromHistory) showDeleteSuggestionDialog(item);
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (textWatcher != null) searchEditText.removeTextChangedListener(textWatcher);
|
|
|
|
textWatcher = new TextWatcher() {
|
|
|
|
@Override
|
|
|
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void afterTextChanged(Editable s) {
|
|
|
|
String newText = searchEditText.getText().toString();
|
2017-09-28 15:06:48 +02:00
|
|
|
suggestionPublisher.onNext(newText);
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
searchEditText.addTextChangedListener(textWatcher);
|
2018-07-08 14:27:12 +02:00
|
|
|
searchEditText.setOnEditorActionListener(
|
|
|
|
(TextView v, int actionId, KeyEvent event) -> {
|
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "onEditorAction() called with: v = [" + v + "], actionId = [" + actionId + "], event = [" + event + "]");
|
|
|
|
}
|
|
|
|
if (event != null
|
|
|
|
&& (event.getKeyCode() == KeyEvent.KEYCODE_ENTER
|
|
|
|
|| event.getAction() == EditorInfo.IME_ACTION_SEARCH)) {
|
|
|
|
search(getSearchQuery(searchEditText.getText().toString(), new ArrayList<>(), ""));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
});
|
2017-09-03 08:04:18 +02:00
|
|
|
|
2018-07-08 14:27:12 +02:00
|
|
|
if (suggestionDisposable == null || suggestionDisposable.isDisposed())
|
|
|
|
initSuggestionObserver();
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private void unsetSearchListeners() {
|
2017-09-28 15:06:48 +02:00
|
|
|
if (DEBUG) Log.d(TAG, "unsetSearchListeners() called");
|
2017-09-03 08:04:18 +02:00
|
|
|
searchClear.setOnClickListener(null);
|
|
|
|
searchClear.setOnLongClickListener(null);
|
|
|
|
searchEditText.setOnClickListener(null);
|
|
|
|
searchEditText.setOnFocusChangeListener(null);
|
|
|
|
searchEditText.setOnEditorActionListener(null);
|
|
|
|
|
|
|
|
if (textWatcher != null) searchEditText.removeTextChangedListener(textWatcher);
|
|
|
|
textWatcher = null;
|
|
|
|
}
|
|
|
|
|
2017-09-28 15:06:48 +02:00
|
|
|
private void showSuggestionsPanel() {
|
|
|
|
if (DEBUG) Log.d(TAG, "showSuggestionsPanel() called");
|
|
|
|
animateView(suggestionsPanel, AnimationUtils.Type.LIGHT_SLIDE_AND_ALPHA, true, 200);
|
|
|
|
}
|
2017-09-03 08:04:18 +02:00
|
|
|
|
2017-09-28 15:06:48 +02:00
|
|
|
private void hideSuggestionsPanel() {
|
|
|
|
if (DEBUG) Log.d(TAG, "hideSuggestionsPanel() called");
|
|
|
|
animateView(suggestionsPanel, AnimationUtils.Type.LIGHT_SLIDE_AND_ALPHA, false, 200);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void showKeyboardSearch() {
|
|
|
|
if (DEBUG) Log.d(TAG, "showKeyboardSearch() called");
|
|
|
|
if (searchEditText == null) return;
|
|
|
|
|
|
|
|
if (searchEditText.requestFocus()) {
|
2018-07-08 14:27:12 +02:00
|
|
|
InputMethodManager imm = (InputMethodManager) activity.getSystemService(
|
|
|
|
Context.INPUT_METHOD_SERVICE);
|
2017-09-28 15:06:48 +02:00
|
|
|
imm.showSoftInput(searchEditText, InputMethodManager.SHOW_IMPLICIT);
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-28 15:06:48 +02:00
|
|
|
private void hideKeyboardSearch() {
|
|
|
|
if (DEBUG) Log.d(TAG, "hideKeyboardSearch() called");
|
|
|
|
if (searchEditText == null) return;
|
2017-09-03 08:04:18 +02:00
|
|
|
|
2018-07-08 14:27:12 +02:00
|
|
|
InputMethodManager imm = (InputMethodManager) activity.getSystemService(
|
|
|
|
Context.INPUT_METHOD_SERVICE);
|
|
|
|
imm.hideSoftInputFromWindow(searchEditText.getWindowToken(),
|
|
|
|
InputMethodManager.HIDE_NOT_ALWAYS);
|
2017-09-28 15:06:48 +02:00
|
|
|
|
|
|
|
searchEditText.clearFocus();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void showDeleteSuggestionDialog(final SuggestionItem item) {
|
2018-02-22 22:30:48 +01:00
|
|
|
if (activity == null || historyRecordManager == null || suggestionPublisher == null ||
|
|
|
|
searchEditText == null || disposables == null) return;
|
|
|
|
final String query = item.query;
|
2017-09-28 15:06:48 +02:00
|
|
|
new AlertDialog.Builder(activity)
|
2018-02-22 22:30:48 +01:00
|
|
|
.setTitle(query)
|
2017-09-28 15:06:48 +02:00
|
|
|
.setMessage(R.string.delete_item_search_history)
|
|
|
|
.setCancelable(true)
|
|
|
|
.setNegativeButton(R.string.cancel, null)
|
2018-02-22 22:30:48 +01:00
|
|
|
.setPositiveButton(R.string.delete, (dialog, which) -> {
|
|
|
|
final Disposable onDelete = historyRecordManager.deleteSearchHistory(query)
|
|
|
|
.observeOn(AndroidSchedulers.mainThread())
|
|
|
|
.subscribe(
|
|
|
|
howManyDeleted -> suggestionPublisher
|
|
|
|
.onNext(searchEditText.getText().toString()),
|
|
|
|
throwable -> showSnackBarError(throwable,
|
2018-04-28 16:11:37 +02:00
|
|
|
UserAction.DELETE_FROM_HISTORY, "none",
|
2018-02-22 22:30:48 +01:00
|
|
|
"Deleting item failed", R.string.general_error)
|
|
|
|
);
|
|
|
|
disposables.add(onDelete);
|
|
|
|
})
|
2018-01-27 06:34:17 +01:00
|
|
|
.show();
|
2017-09-28 15:06:48 +02:00
|
|
|
}
|
2017-09-03 08:04:18 +02:00
|
|
|
|
2017-09-28 15:06:48 +02:00
|
|
|
@Override
|
|
|
|
public boolean onBackPressed() {
|
|
|
|
if (suggestionsPanel.getVisibility() == View.VISIBLE && infoListAdapter.getItemsList().size() > 0 && !isLoading.get()) {
|
|
|
|
hideSuggestionsPanel();
|
|
|
|
hideKeyboardSearch();
|
2018-07-08 14:27:12 +02:00
|
|
|
searchEditText.setText(lastSearchedQuery.getSearchString());
|
2017-09-28 15:06:48 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void giveSearchEditTextFocus() {
|
2017-09-28 15:06:48 +02:00
|
|
|
showKeyboardSearch();
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private void initSuggestionObserver() {
|
2017-09-28 15:06:48 +02:00
|
|
|
if (DEBUG) Log.d(TAG, "initSuggestionObserver() called");
|
|
|
|
if (suggestionDisposable != null) suggestionDisposable.dispose();
|
2017-09-03 08:04:18 +02:00
|
|
|
|
2017-09-28 15:06:48 +02:00
|
|
|
final Observable<String> observable = suggestionPublisher
|
2017-09-03 08:04:18 +02:00
|
|
|
.debounce(SUGGESTIONS_DEBOUNCE, TimeUnit.MILLISECONDS)
|
2018-07-08 14:27:12 +02:00
|
|
|
.startWith(searchQuery != null
|
|
|
|
? searchQuery.getSearchString()
|
|
|
|
: "")
|
|
|
|
.filter(searchString -> isSuggestionsEnabled);
|
2017-09-28 15:06:48 +02:00
|
|
|
|
|
|
|
suggestionDisposable = observable
|
2018-01-27 06:34:17 +01:00
|
|
|
.switchMap(query -> {
|
|
|
|
final Flowable<List<SearchHistoryEntry>> flowable = historyRecordManager
|
|
|
|
.getRelatedSearches(query, 3, 25);
|
|
|
|
final Observable<List<SuggestionItem>> local = flowable.toObservable()
|
|
|
|
.map(searchHistoryEntries -> {
|
|
|
|
List<SuggestionItem> result = new ArrayList<>();
|
|
|
|
for (SearchHistoryEntry entry : searchHistoryEntries)
|
|
|
|
result.add(new SuggestionItem(true, entry.getSearch()));
|
|
|
|
return result;
|
|
|
|
});
|
2017-09-28 15:06:48 +02:00
|
|
|
|
2018-01-27 06:34:17 +01:00
|
|
|
if (query.length() < THRESHOLD_NETWORK_SUGGESTION) {
|
|
|
|
// Only pass through if the query length is equal or greater than THRESHOLD_NETWORK_SUGGESTION
|
|
|
|
return local.materialize();
|
|
|
|
}
|
|
|
|
|
|
|
|
final Observable<List<SuggestionItem>> network = ExtractorHelper
|
|
|
|
.suggestionsFor(serviceId, query, contentCountry)
|
|
|
|
.toObservable()
|
|
|
|
.map(strings -> {
|
2017-09-28 15:06:48 +02:00
|
|
|
List<SuggestionItem> result = new ArrayList<>();
|
2018-01-27 06:34:17 +01:00
|
|
|
for (String entry : strings) {
|
|
|
|
result.add(new SuggestionItem(false, entry));
|
2017-09-28 15:06:48 +02:00
|
|
|
}
|
|
|
|
return result;
|
2018-01-27 06:34:17 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
return Observable.zip(local, network, (localResult, networkResult) -> {
|
|
|
|
List<SuggestionItem> result = new ArrayList<>();
|
|
|
|
if (localResult.size() > 0) result.addAll(localResult);
|
|
|
|
|
|
|
|
// Remove duplicates
|
|
|
|
final Iterator<SuggestionItem> iterator = networkResult.iterator();
|
|
|
|
while (iterator.hasNext() && localResult.size() > 0) {
|
|
|
|
final SuggestionItem next = iterator.next();
|
|
|
|
for (SuggestionItem item : localResult) {
|
|
|
|
if (item.query.equals(next.query)) {
|
|
|
|
iterator.remove();
|
|
|
|
break;
|
|
|
|
}
|
2017-09-28 15:06:48 +02:00
|
|
|
}
|
2018-01-27 06:34:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (networkResult.size() > 0) result.addAll(networkResult);
|
|
|
|
return result;
|
|
|
|
}).materialize();
|
2017-09-03 08:04:18 +02:00
|
|
|
})
|
|
|
|
.subscribeOn(Schedulers.io())
|
|
|
|
.observeOn(AndroidSchedulers.mainThread())
|
2018-01-27 06:34:17 +01:00
|
|
|
.subscribe(listNotification -> {
|
|
|
|
if (listNotification.isOnNext()) {
|
|
|
|
handleSuggestions(listNotification.getValue());
|
|
|
|
} else if (listNotification.isOnError()) {
|
|
|
|
Throwable error = listNotification.getError();
|
|
|
|
if (!ExtractorHelper.hasAssignableCauseThrowable(error,
|
|
|
|
IOException.class, SocketException.class,
|
|
|
|
InterruptedException.class, InterruptedIOException.class)) {
|
|
|
|
onSuggestionError(error);
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void doInitialLoadLogic() {
|
|
|
|
// no-op
|
|
|
|
}
|
|
|
|
|
2018-07-08 14:27:12 +02:00
|
|
|
private void search(final SearchQIHandler query) {
|
2017-09-03 08:04:18 +02:00
|
|
|
if (DEBUG) Log.d(TAG, "search() called with: query = [" + query + "]");
|
2018-07-08 14:27:12 +02:00
|
|
|
if (query.getSearchString().isEmpty()) return;
|
2017-09-03 08:04:18 +02:00
|
|
|
|
2017-09-15 19:27:39 +02:00
|
|
|
try {
|
2018-07-08 14:27:12 +02:00
|
|
|
final StreamingService service = NewPipe.getServiceByUrl(query.getUrl());
|
2017-09-15 19:27:39 +02:00
|
|
|
if (service != null) {
|
|
|
|
showLoading();
|
|
|
|
disposables.add(Observable
|
2018-07-08 14:27:12 +02:00
|
|
|
.fromCallable(() ->
|
|
|
|
NavigationHelper.getIntentByLink(activity, service, query.getUrl()))
|
2017-09-15 19:27:39 +02:00
|
|
|
.subscribeOn(Schedulers.io())
|
|
|
|
.observeOn(AndroidSchedulers.mainThread())
|
2018-07-08 14:27:12 +02:00
|
|
|
.subscribe(intent -> {
|
|
|
|
getFragmentManager().popBackStackImmediate();
|
|
|
|
activity.startActivity(intent);
|
|
|
|
}, throwable ->
|
|
|
|
showError(getString(R.string.url_not_supported_toast), false)));
|
2017-09-15 19:27:39 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
|
|
// Exception occurred, it's not a url
|
|
|
|
}
|
|
|
|
|
2017-09-28 15:06:48 +02:00
|
|
|
lastSearchedQuery = query;
|
|
|
|
searchQuery = query;
|
2018-07-08 14:27:12 +02:00
|
|
|
currentPageUrl = "";
|
2017-09-03 08:04:18 +02:00
|
|
|
infoListAdapter.clearStreamItemList();
|
2017-09-28 15:06:48 +02:00
|
|
|
hideSuggestionsPanel();
|
|
|
|
hideKeyboardSearch();
|
2017-09-03 08:04:18 +02:00
|
|
|
|
2018-07-08 14:27:12 +02:00
|
|
|
historyRecordManager.onSearched(serviceId, query.getSearchString())
|
2018-01-27 06:34:17 +01:00
|
|
|
.observeOn(AndroidSchedulers.mainThread())
|
|
|
|
.subscribe(
|
|
|
|
ignored -> {},
|
|
|
|
error -> showSnackBarError(error, UserAction.SEARCHED,
|
2018-07-08 14:27:12 +02:00
|
|
|
NewPipe.getNameOfService(serviceId), query.getSearchString(), 0)
|
2018-01-27 06:34:17 +01:00
|
|
|
);
|
2018-07-08 14:27:12 +02:00
|
|
|
suggestionPublisher.onNext(query.getSearchString());
|
2017-09-03 08:04:18 +02:00
|
|
|
startLoading(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void startLoading(boolean forceLoad) {
|
|
|
|
super.startLoading(forceLoad);
|
2017-09-15 19:27:39 +02:00
|
|
|
if (disposables != null) disposables.clear();
|
2017-09-03 08:04:18 +02:00
|
|
|
if (searchDisposable != null) searchDisposable.dispose();
|
2018-07-08 14:27:12 +02:00
|
|
|
searchDisposable = ExtractorHelper.searchFor(serviceId,
|
|
|
|
searchQuery, currentPageUrl, contentCountry, contentFilter, "")
|
2017-09-03 08:04:18 +02:00
|
|
|
.subscribeOn(Schedulers.io())
|
|
|
|
.observeOn(AndroidSchedulers.mainThread())
|
2018-02-22 22:30:48 +01:00
|
|
|
.doOnEvent((searchResult, throwable) -> isLoading.set(false))
|
|
|
|
.subscribe(this::handleResult, this::onError);
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void loadMoreItems() {
|
|
|
|
isLoading.set(true);
|
|
|
|
showListFooter(true);
|
|
|
|
if (searchDisposable != null) searchDisposable.dispose();
|
|
|
|
currentNextPage = currentPage + 1;
|
2018-07-08 14:27:12 +02:00
|
|
|
searchDisposable = ExtractorHelper.getMoreSearchItems(serviceId,
|
|
|
|
searchQuery, currentNextPage, contentCountry, filter)
|
2017-09-03 08:04:18 +02:00
|
|
|
.subscribeOn(Schedulers.io())
|
|
|
|
.observeOn(AndroidSchedulers.mainThread())
|
2018-02-22 22:30:48 +01:00
|
|
|
.doOnEvent((nextItemsResult, throwable) -> isLoading.set(false))
|
|
|
|
.subscribe(this::handleNextItems, this::onError);
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected boolean hasMoreItems() {
|
|
|
|
// TODO: No way to tell if search has more items in the moment
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onItemSelected(InfoItem selectedItem) {
|
|
|
|
super.onItemSelected(selectedItem);
|
2017-09-28 15:06:48 +02:00
|
|
|
hideKeyboardSearch();
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Utils
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
2018-07-08 14:27:12 +02:00
|
|
|
private void changeContentFilter(MenuItem item, List<String> contentFilter) {
|
2017-09-03 08:04:18 +02:00
|
|
|
this.filterItemCheckedId = item.getItemId();
|
|
|
|
item.setChecked(true);
|
|
|
|
|
2018-07-08 14:27:12 +02:00
|
|
|
this.contentFilter = contentFilter;
|
|
|
|
searchQuery = getSearchQuery(searchQuery.getSearchString(), contentFilter, "");
|
|
|
|
|
|
|
|
if (!TextUtils.isEmpty(searchQuery.getSearchString())) {
|
2017-09-28 15:06:48 +02:00
|
|
|
search(searchQuery);
|
|
|
|
}
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
2018-07-08 14:27:12 +02:00
|
|
|
private void setQuery(int serviceId, SearchQIHandler searchQuery) {
|
2017-09-03 08:04:18 +02:00
|
|
|
this.serviceId = serviceId;
|
|
|
|
this.searchQuery = searchQuery;
|
|
|
|
}
|
|
|
|
|
2018-07-08 14:27:12 +02:00
|
|
|
private SearchQIHandler getSearchQuery(String searchString,
|
|
|
|
List<String> contentFilter,
|
|
|
|
String sortFilter) {
|
|
|
|
try {
|
|
|
|
return service.getSearchQIHFactory()
|
|
|
|
.fromQuery(searchString, contentFilter, sortFilter);
|
|
|
|
} catch (Exception e) {
|
|
|
|
ErrorActivity.reportError(getActivity(), e, getActivity().getClass(),
|
|
|
|
getActivity().findViewById(android.R.id.content),
|
|
|
|
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
|
|
|
|
"",
|
|
|
|
"", R.string.general_error));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-03 08:04:18 +02:00
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Suggestion Results
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
2017-09-28 15:06:48 +02:00
|
|
|
public void handleSuggestions(@NonNull final List<SuggestionItem> suggestions) {
|
2017-09-03 08:04:18 +02:00
|
|
|
if (DEBUG) Log.d(TAG, "handleSuggestions() called with: suggestions = [" + suggestions + "]");
|
2017-09-28 15:06:48 +02:00
|
|
|
suggestionsRecyclerView.smoothScrollToPosition(0);
|
2018-03-18 16:37:49 +01:00
|
|
|
suggestionsRecyclerView.post(() -> suggestionListAdapter.setItems(suggestions));
|
2017-09-28 15:06:48 +02:00
|
|
|
|
|
|
|
if (errorPanelRoot.getVisibility() == View.VISIBLE) {
|
|
|
|
hideLoading();
|
|
|
|
}
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void onSuggestionError(Throwable exception) {
|
|
|
|
if (DEBUG) Log.d(TAG, "onSuggestionError() called with: exception = [" + exception + "]");
|
|
|
|
if (super.onError(exception)) return;
|
|
|
|
|
2018-07-08 14:27:12 +02:00
|
|
|
int errorId = exception instanceof ParsingException
|
|
|
|
? R.string.parsing_error
|
|
|
|
: R.string.general_error;
|
|
|
|
onUnrecoverableError(exception, UserAction.GET_SUGGESTIONS,
|
|
|
|
NewPipe.getNameOfService(serviceId), searchQuery, errorId);
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Contract
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void hideLoading() {
|
|
|
|
super.hideLoading();
|
|
|
|
showListFooter(false);
|
|
|
|
}
|
|
|
|
|
2017-09-28 15:06:48 +02:00
|
|
|
@Override
|
|
|
|
public void showError(String message, boolean showRetryButton) {
|
|
|
|
super.showError(message, showRetryButton);
|
|
|
|
hideSuggestionsPanel();
|
|
|
|
hideKeyboardSearch();
|
|
|
|
}
|
|
|
|
|
2017-09-03 08:04:18 +02:00
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Search Results
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void handleResult(@NonNull SearchResult result) {
|
|
|
|
if (!result.errors.isEmpty()) {
|
2018-07-08 14:27:12 +02:00
|
|
|
showSnackBarError(result.errors, UserAction.SEARCHED,
|
|
|
|
NewPipe.getNameOfService(serviceId), searchQuery, 0);
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
2017-09-28 15:06:48 +02:00
|
|
|
lastSearchedQuery = searchQuery;
|
|
|
|
|
2017-09-03 08:04:18 +02:00
|
|
|
if (infoListAdapter.getItemsList().size() == 0) {
|
2017-12-08 15:05:08 +01:00
|
|
|
if (!result.getResults().isEmpty()) {
|
|
|
|
infoListAdapter.addInfoItemList(result.getResults());
|
2017-09-03 08:04:18 +02:00
|
|
|
} else {
|
|
|
|
infoListAdapter.clearStreamItemList();
|
|
|
|
showEmptyState();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
super.handleResult(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2018-03-18 16:37:49 +01:00
|
|
|
public void handleNextItems(ListExtractor.InfoItemsPage result) {
|
2017-09-03 08:04:18 +02:00
|
|
|
showListFooter(false);
|
2018-02-24 22:57:25 +01:00
|
|
|
currentPage = Integer.parseInt(result.getNextPageUrl());
|
2018-03-18 16:37:49 +01:00
|
|
|
infoListAdapter.addInfoItemList(result.getItems());
|
2017-09-03 08:04:18 +02:00
|
|
|
|
2017-12-08 15:05:08 +01:00
|
|
|
if (!result.getErrors().isEmpty()) {
|
2018-07-08 14:27:12 +02:00
|
|
|
showSnackBarError(result.getErrors(), UserAction.SEARCHED,
|
|
|
|
NewPipe.getNameOfService(serviceId)
|
2017-09-03 08:04:18 +02:00
|
|
|
, "\"" + searchQuery + "\" → page " + currentPage, 0);
|
|
|
|
}
|
|
|
|
super.handleNextItems(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected boolean onError(Throwable exception) {
|
|
|
|
if (super.onError(exception)) return true;
|
|
|
|
|
|
|
|
if (exception instanceof SearchEngine.NothingFoundException) {
|
|
|
|
infoListAdapter.clearStreamItemList();
|
|
|
|
showEmptyState();
|
|
|
|
} else {
|
2018-07-08 14:27:12 +02:00
|
|
|
int errorId = exception instanceof ParsingException
|
|
|
|
? R.string.parsing_error
|
|
|
|
: R.string.general_error;
|
|
|
|
onUnrecoverableError(exception, UserAction.SEARCHED,
|
|
|
|
NewPipe.getNameOfService(serviceId), searchQuery, errorId);
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|