mirror of
https://github.com/MaintainTeam/LastPipeBender.git
synced 2025-03-01 22:08:22 +03:00
searchfilters: replace old filter interaction and integrate new dialog into SearchFragment
This commit is contained in:
parent
891e9b64ed
commit
f3d13be5fe
3 changed files with 118 additions and 95 deletions
|
@ -3,7 +3,6 @@ package org.schabi.newpipe.fragments.list.search;
|
|||
import static androidx.recyclerview.widget.ItemTouchHelper.Callback.makeMovementFlags;
|
||||
import static org.schabi.newpipe.ktx.ViewUtils.animate;
|
||||
import static org.schabi.newpipe.util.ExtractorHelper.showMetaInfoInTextView;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
|
@ -34,10 +33,13 @@ import androidx.appcompat.app.ActionBar;
|
|||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.TooltipCompat;
|
||||
import androidx.core.text.HtmlCompat;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.schabi.newpipe.App;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.databinding.FragmentSearchBinding;
|
||||
import org.schabi.newpipe.error.ErrorInfo;
|
||||
|
@ -50,12 +52,15 @@ import org.schabi.newpipe.extractor.MetaInfo;
|
|||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.Page;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.search.SearchExtractor;
|
||||
import org.schabi.newpipe.extractor.search.SearchInfo;
|
||||
import org.schabi.newpipe.extractor.services.peertube.linkHandler.PeertubeSearchQueryHandlerFactory;
|
||||
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory;
|
||||
import org.schabi.newpipe.extractor.search.filter.FilterItem;
|
||||
import org.schabi.newpipe.fragments.BackPressable;
|
||||
import org.schabi.newpipe.fragments.list.BaseListFragment;
|
||||
import org.schabi.newpipe.fragments.list.search.filter.BaseSearchFilterDialogFragment;
|
||||
import org.schabi.newpipe.fragments.list.search.filter.SearchFilterDialogFragment;
|
||||
import org.schabi.newpipe.fragments.list.search.filter.SearchFilterLogic;
|
||||
import org.schabi.newpipe.ktx.AnimationType;
|
||||
import org.schabi.newpipe.ktx.ExceptionUtils;
|
||||
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||
|
@ -65,14 +70,11 @@ import org.schabi.newpipe.util.DeviceUtils;
|
|||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
import org.schabi.newpipe.util.KeyboardUtil;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.ServiceHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -87,7 +89,8 @@ import io.reactivex.rxjava3.schedulers.Schedulers;
|
|||
import io.reactivex.rxjava3.subjects.PublishSubject;
|
||||
|
||||
public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.InfoItemsPage<?>>
|
||||
implements BackPressable {
|
||||
implements BackPressable, SearchFilterLogic.Callback,
|
||||
BaseSearchFilterDialogFragment.Listener {
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Search
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
@ -105,9 +108,6 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
private static final int SUGGESTIONS_DEBOUNCE = 120; //ms
|
||||
private final PublishSubject<String> suggestionPublisher = PublishSubject.create();
|
||||
|
||||
@State
|
||||
int filterItemCheckedId = -1;
|
||||
|
||||
@State
|
||||
protected int serviceId = Constants.NO_SERVICE_ID;
|
||||
|
||||
|
@ -115,15 +115,9 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
@State
|
||||
String searchString;
|
||||
|
||||
/**
|
||||
* No content filter should add like contentFilter = all
|
||||
* be aware of this when implementing an extractor.
|
||||
*/
|
||||
@State
|
||||
String[] contentFilter = new String[0];
|
||||
List<FilterItem> selectedContentFilter = new ArrayList<>();
|
||||
|
||||
@State
|
||||
String sortFilter;
|
||||
List<FilterItem> selectedSortFilter = new ArrayList<>();
|
||||
|
||||
// these represents the last search
|
||||
@State
|
||||
|
@ -141,7 +135,6 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
@State
|
||||
boolean wasSearchFocused = false;
|
||||
|
||||
@Nullable private Map<Integer, String> menuItemToFilterName = null;
|
||||
private StreamingService service;
|
||||
private Page nextPage;
|
||||
private boolean showLocalSuggestions = true;
|
||||
|
@ -170,9 +163,15 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
|
||||
private TextWatcher textWatcher;
|
||||
|
||||
@State
|
||||
public ArrayList<Integer> userSelectedContentFilterList;
|
||||
|
||||
@State
|
||||
ArrayList<Integer> userSelectedSortFilterList = null;
|
||||
|
||||
public static SearchFragment getInstance(final int serviceId, final String searchString) {
|
||||
final SearchFragment searchFragment = new SearchFragment();
|
||||
searchFragment.setQuery(serviceId, searchString, new String[0], "");
|
||||
searchFragment.setQuery(serviceId, searchString, null, null);
|
||||
|
||||
if (!TextUtils.isEmpty(searchString)) {
|
||||
searchFragment.setSearchOnResume();
|
||||
|
@ -205,11 +204,44 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container,
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater,
|
||||
@Nullable final ViewGroup container,
|
||||
@Nullable final Bundle savedInstanceState) {
|
||||
|
||||
if (userSelectedContentFilterList == null) {
|
||||
userSelectedContentFilterList = new ArrayList<>();
|
||||
}
|
||||
|
||||
if (userSelectedSortFilterList == null) {
|
||||
userSelectedSortFilterList = new ArrayList<>();
|
||||
}
|
||||
initializeFilterData();
|
||||
updateService();
|
||||
return inflater.inflate(R.layout.fragment_search, container, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* initialize the initial content and sort filter Lists with defaults
|
||||
* or previously with Icepick stored data.
|
||||
*/
|
||||
protected void initializeFilterData() {
|
||||
try {
|
||||
final SearchFilterLogic logic =
|
||||
new SearchFilterLogic(NewPipe.getService(serviceId).getSearchQHFactory(), null);
|
||||
logic.restorePreviouslySelectedFilters(
|
||||
userSelectedContentFilterList,
|
||||
userSelectedSortFilterList);
|
||||
|
||||
userSelectedContentFilterList = logic.getSelectedContentFilters();
|
||||
userSelectedSortFilterList = logic.getSelectedSortFilters();
|
||||
selectedContentFilter = logic.getSelectedContentFilterItems();
|
||||
selectedSortFilter = logic.getSelectedSortFiltersItems();
|
||||
} catch (final ExtractionException e) {
|
||||
ErrorUtil.showUiErrorSnackbar(this,
|
||||
"No filtering possible. Getting service for id " + serviceId, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull final View rootView, final Bundle savedInstanceState) {
|
||||
searchBinding = FragmentSearchBinding.bind(rootView);
|
||||
|
@ -265,11 +297,11 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
|
||||
if (!TextUtils.isEmpty(searchString)) {
|
||||
if (wasLoading.getAndSet(false)) {
|
||||
search(searchString, contentFilter, sortFilter);
|
||||
search(searchString, selectedContentFilter, selectedSortFilter);
|
||||
return;
|
||||
} else if (infoListAdapter.getItemsList().isEmpty()) {
|
||||
if (savedState == null) {
|
||||
search(searchString, contentFilter, sortFilter);
|
||||
search(searchString, selectedContentFilter, selectedSortFilter);
|
||||
return;
|
||||
} else if (!isLoading.get() && !wasSearchFocused && lastPanelError == null) {
|
||||
infoListAdapter.clearStreamItemList();
|
||||
|
@ -322,7 +354,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
if (requestCode == ReCaptchaActivity.RECAPTCHA_REQUEST) {
|
||||
if (resultCode == Activity.RESULT_OK
|
||||
&& !TextUtils.isEmpty(searchString)) {
|
||||
search(searchString, contentFilter, sortFilter);
|
||||
search(searchString, selectedContentFilter, selectedSortFilter);
|
||||
} else {
|
||||
Log.e(TAG, "ReCaptcha failed");
|
||||
}
|
||||
|
@ -388,6 +420,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
searchString = searchEditText != null
|
||||
? searchEditText.getText().toString()
|
||||
: searchString;
|
||||
|
||||
super.onSaveInstanceState(bundle);
|
||||
}
|
||||
|
||||
|
@ -401,7 +434,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
|| (searchEditText != null && !TextUtils.isEmpty(searchEditText.getText()))) {
|
||||
search(!TextUtils.isEmpty(searchString)
|
||||
? searchString
|
||||
: searchEditText.getText().toString(), this.contentFilter, "");
|
||||
: searchEditText.getText().toString(), this.selectedContentFilter, null);
|
||||
} else {
|
||||
if (searchEditText != null) {
|
||||
searchEditText.setText("");
|
||||
|
@ -426,67 +459,29 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
supportActionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
menuItemToFilterName = new HashMap<>();
|
||||
|
||||
int itemId = 0;
|
||||
boolean isFirstItem = true;
|
||||
final Context c = getContext();
|
||||
|
||||
if (service == null) {
|
||||
Log.w(TAG, "onCreateOptionsMenu() called with null service");
|
||||
updateService();
|
||||
}
|
||||
|
||||
for (final String filter : service.getSearchQHFactory().getAvailableContentFilter()) {
|
||||
if (filter.equals(YoutubeSearchQueryHandlerFactory.MUSIC_SONGS)) {
|
||||
final MenuItem musicItem = menu.add(2,
|
||||
itemId++,
|
||||
0,
|
||||
"YouTube Music");
|
||||
musicItem.setEnabled(false);
|
||||
} else if (filter.equals(PeertubeSearchQueryHandlerFactory.SEPIA_VIDEOS)) {
|
||||
final MenuItem sepiaItem = menu.add(2,
|
||||
itemId++,
|
||||
0,
|
||||
"Sepia Search");
|
||||
sepiaItem.setEnabled(false);
|
||||
createMenu(menu, inflater);
|
||||
}
|
||||
menuItemToFilterName.put(itemId, filter);
|
||||
final MenuItem item = menu.add(1,
|
||||
itemId++,
|
||||
0,
|
||||
ServiceHelper.getTranslatedFilterString(filter, c));
|
||||
if (isFirstItem) {
|
||||
item.setChecked(true);
|
||||
isFirstItem = false;
|
||||
}
|
||||
}
|
||||
menu.setGroupCheckable(1, true, true);
|
||||
|
||||
restoreFilterChecked(menu, filterItemCheckedId);
|
||||
protected void createMenu(@NonNull final Menu menu,
|
||||
@NonNull final MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.menu_search_fragment, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
|
||||
if (menuItemToFilterName != null) {
|
||||
final List<String> cf = new ArrayList<>(1);
|
||||
cf.add(menuItemToFilterName.get(item.getItemId()));
|
||||
changeContentFilter(item, cf);
|
||||
if (item.getItemId() == R.id.action_filter) {
|
||||
hideKeyboardSearch();
|
||||
showSelectFiltersDialog();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void restoreFilterChecked(final Menu menu, final int itemId) {
|
||||
if (itemId != -1) {
|
||||
final MenuItem item = menu.findItem(itemId);
|
||||
if (item == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
item.setChecked(true);
|
||||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Search
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
@ -564,7 +559,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
suggestionListAdapter.setListener(new SuggestionListAdapter.OnSuggestionItemSelected() {
|
||||
@Override
|
||||
public void onSuggestionItemSelected(final SuggestionItem item) {
|
||||
search(item.query, new String[0], "");
|
||||
search(item.query, null, null);
|
||||
searchEditText.setText(item.query);
|
||||
}
|
||||
|
||||
|
@ -619,7 +614,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
} else if (event != null
|
||||
&& (event.getKeyCode() == KeyEvent.KEYCODE_ENTER
|
||||
|| event.getAction() == EditorInfo.IME_ACTION_SEARCH)) {
|
||||
search(searchEditText.getText().toString(), new String[0], "");
|
||||
search(searchEditText.getText().toString(), null, null);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -806,8 +801,8 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
}
|
||||
|
||||
private void search(final String theSearchString,
|
||||
final String[] theContentFilter,
|
||||
final String theSortFilter) {
|
||||
final List<FilterItem> theContentFilter,
|
||||
final List<FilterItem> theSortFilter) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "search() called with: query = [" + theSearchString + "]");
|
||||
}
|
||||
|
@ -862,13 +857,12 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
}
|
||||
searchDisposable = ExtractorHelper.searchFor(serviceId,
|
||||
searchString,
|
||||
Arrays.asList(contentFilter),
|
||||
sortFilter)
|
||||
selectedContentFilter,
|
||||
selectedSortFilter)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnEvent((searchResult, throwable) -> isLoading.set(false))
|
||||
.subscribe(this::handleResult, this::onItemError);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -884,8 +878,8 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
searchDisposable = ExtractorHelper.getMoreSearchItems(
|
||||
serviceId,
|
||||
searchString,
|
||||
asList(contentFilter),
|
||||
sortFilter,
|
||||
selectedContentFilter,
|
||||
selectedSortFilter,
|
||||
nextPage)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
|
@ -917,25 +911,24 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private void changeContentFilter(final MenuItem item, final List<String> theContentFilter) {
|
||||
filterItemCheckedId = item.getItemId();
|
||||
item.setChecked(true);
|
||||
@Override
|
||||
public void selectedFilters(final List<FilterItem> theSelectedContentFilter,
|
||||
final List<FilterItem> theSelectedSortFilter) {
|
||||
|
||||
contentFilter = theContentFilter.toArray(new String[0]);
|
||||
selectedContentFilter = theSelectedContentFilter;
|
||||
selectedSortFilter = theSelectedSortFilter;
|
||||
|
||||
if (!TextUtils.isEmpty(searchString)) {
|
||||
search(searchString, contentFilter, sortFilter);
|
||||
search(searchString, selectedContentFilter, selectedSortFilter);
|
||||
}
|
||||
}
|
||||
|
||||
private void setQuery(final int theServiceId,
|
||||
final String theSearchString,
|
||||
final String[] theContentFilter,
|
||||
final String theSortFilter) {
|
||||
final List<FilterItem> theContentFilter,
|
||||
final List<FilterItem> theSortFilter) {
|
||||
serviceId = theServiceId;
|
||||
searchString = theSearchString;
|
||||
contentFilter = theContentFilter;
|
||||
sortFilter = theSortFilter;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1020,7 +1013,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
|
||||
searchBinding.correctSuggestion.setOnClickListener(v -> {
|
||||
searchBinding.correctSuggestion.setVisibility(View.GONE);
|
||||
search(searchSuggestion, contentFilter, sortFilter);
|
||||
search(searchSuggestion, selectedContentFilter, selectedSortFilter);
|
||||
searchEditText.setText(searchSuggestion);
|
||||
});
|
||||
|
||||
|
@ -1085,4 +1078,23 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
UserAction.DELETE_FROM_HISTORY, "Deleting item failed")));
|
||||
disposables.add(onDelete);
|
||||
}
|
||||
|
||||
private void showSelectFiltersDialog() {
|
||||
final FragmentManager fragmentManager = getParentFragmentManager();
|
||||
final DialogFragment searchFilterUiDialog =
|
||||
SearchFilterDialogFragment.newInstance(
|
||||
serviceId, userSelectedContentFilterList, userSelectedSortFilterList);
|
||||
|
||||
searchFilterUiDialog.setTargetFragment(SearchFragment.this, 300);
|
||||
searchFilterUiDialog.show(fragmentManager, "fragment_search");
|
||||
}
|
||||
|
||||
@SuppressWarnings("checkstyle:HiddenField")
|
||||
@Override
|
||||
public void onFinishSearchFilterDialog(final ArrayList<Integer> userSelectedContentFilterList,
|
||||
final ArrayList<Integer> userSelectedSortFilterList,
|
||||
final List<FilterItem> selectedContentFilters,
|
||||
final List<FilterItem> selectedSortFilters) {
|
||||
selectedFilters(selectedContentFilters, selectedSortFilters);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@ import android.util.Log;
|
|||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.schabi.newpipe.extractor.search.filter.FilterItem;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.text.HtmlCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
@ -75,8 +77,8 @@ public final class ExtractorHelper {
|
|||
}
|
||||
|
||||
public static Single<SearchInfo> searchFor(final int serviceId, final String searchString,
|
||||
final List<String> contentFilter,
|
||||
final String sortFilter) {
|
||||
final List<FilterItem> contentFilter,
|
||||
final List<FilterItem> sortFilter) {
|
||||
checkServiceId(serviceId);
|
||||
return Single.fromCallable(() ->
|
||||
SearchInfo.getInfo(NewPipe.getService(serviceId),
|
||||
|
@ -88,8 +90,8 @@ public final class ExtractorHelper {
|
|||
public static Single<InfoItemsPage<InfoItem>> getMoreSearchItems(
|
||||
final int serviceId,
|
||||
final String searchString,
|
||||
final List<String> contentFilter,
|
||||
final String sortFilter,
|
||||
final List<FilterItem> contentFilter,
|
||||
final List<FilterItem> sortFilter,
|
||||
final Page page) {
|
||||
checkServiceId(serviceId);
|
||||
return Single.fromCallable(() ->
|
||||
|
|
9
app/src/main/res/menu/menu_search_fragment.xml
Normal file
9
app/src/main/res/menu/menu_search_fragment.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/action_filter"
|
||||
android:title="@string/filter"
|
||||
android:icon="@drawable/ic_sort"
|
||||
app:showAsAction="always" />
|
||||
</menu>
|
Loading…
Add table
Reference in a new issue