mirror of
https://github.com/MaintainTeam/LastPipeBender.git
synced 2025-03-01 22:08:22 +03:00
searchfilters: Moving DividerItem from NewPipeExtractor into NewPipe
DividerItem was inserted in the content filter framework in the NewPipeExtractor to have a section title for YoutubeMusic. But as UI releated stuff seems a bit out of place in the Extractor I came up with injecting the DividerItem aka section title in the frontend without having to change too much in the frontend.
This commit is contained in:
parent
2da5876970
commit
d7bfb0ab7a
8 changed files with 269 additions and 28 deletions
|
@ -7,6 +7,7 @@ import androidx.lifecycle.viewmodel.initializer
|
|||
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||
import org.schabi.newpipe.extractor.NewPipe
|
||||
import org.schabi.newpipe.extractor.search.filter.FilterItem
|
||||
import org.schabi.newpipe.fragments.list.search.filter.InjectFilterItem
|
||||
import org.schabi.newpipe.fragments.list.search.filter.SearchFilterLogic
|
||||
import org.schabi.newpipe.fragments.list.search.filter.SearchFilterLogic.Factory.Variant
|
||||
|
||||
|
@ -38,11 +39,16 @@ class SearchViewModel(
|
|||
val doSearchLiveData: LiveData<Boolean>
|
||||
get() = doSearchMutableLiveData
|
||||
|
||||
val searchFilterLogic = SearchFilterLogic.Factory.create(
|
||||
logicVariant, NewPipe.getService(serviceId).searchQHFactory, null
|
||||
)
|
||||
var searchFilterLogic: SearchFilterLogic
|
||||
|
||||
init {
|
||||
// inject before creating SearchFilterLogic
|
||||
InjectFilterItem.DividerBetweenYoutubeAndYoutubeMusic.run()
|
||||
|
||||
searchFilterLogic = SearchFilterLogic.Factory.create(
|
||||
logicVariant,
|
||||
NewPipe.getService(serviceId).searchQHFactory, null
|
||||
)
|
||||
searchFilterLogic.restorePreviouslySelectedFilters(
|
||||
userSelectedContentFilterList,
|
||||
userSelectedSortFilterList
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
package org.schabi.newpipe.fragments.list.search.filter;
|
||||
|
||||
import org.schabi.newpipe.App;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.search.filter.FilterContainer;
|
||||
import org.schabi.newpipe.extractor.search.filter.FilterGroup;
|
||||
import org.schabi.newpipe.extractor.search.filter.FilterItem;
|
||||
import org.schabi.newpipe.extractor.search.filter.LibraryStringIds;
|
||||
import org.schabi.newpipe.extractor.services.youtube.search.filter.YoutubeFilters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Inject a {@link FilterItem} that actually should not be a real filter.
|
||||
* <p>
|
||||
* This base class is meant to inject eg {@link DividerItem} (that inherits {@link FilterItem})
|
||||
* as Divider between {@link FilterItem}. It will be shown in the UI's.
|
||||
* <p>
|
||||
* Of course you have to handle {@link DividerItem} or whatever in the Ui's.
|
||||
* For that for example have a look at {@link SearchFilterDialogSpinnerAdapter}.
|
||||
*/
|
||||
public abstract class InjectFilterItem {
|
||||
|
||||
protected InjectFilterItem(
|
||||
@NonNull final String serviceName,
|
||||
final int injectedAfterFilterWithId,
|
||||
@NonNull final FilterItem toBeInjectedFilterItem) {
|
||||
|
||||
prepareAndInject(serviceName, injectedAfterFilterWithId, toBeInjectedFilterItem);
|
||||
}
|
||||
|
||||
// Please refer a static boolean to determine if already injected
|
||||
protected abstract boolean isAlreadyInjected();
|
||||
|
||||
// Please refer a static boolean to determine if already injected
|
||||
protected abstract void setAsInjected();
|
||||
|
||||
private void prepareAndInject(
|
||||
@NonNull final String serviceName,
|
||||
final int injectedAfterFilterWithId,
|
||||
@NonNull final FilterItem toBeInjectedFilterItem) {
|
||||
|
||||
if (isAlreadyInjected()) { // already run
|
||||
return;
|
||||
}
|
||||
|
||||
try { // using serviceName to test if we are trying to inject into the right service
|
||||
final List<FilterGroup> groups = NewPipe.getService(serviceName)
|
||||
.getSearchQHFactory().getAvailableContentFilter().getFilterGroups();
|
||||
injectFilterItemIntoGroup(
|
||||
groups,
|
||||
injectedAfterFilterWithId,
|
||||
toBeInjectedFilterItem);
|
||||
setAsInjected();
|
||||
} catch (final ExtractionException ignored) {
|
||||
// no the service we want to prepareAndInject -> so ignore
|
||||
}
|
||||
}
|
||||
|
||||
private void injectFilterItemIntoGroup(
|
||||
@NonNull final List<FilterGroup> groups,
|
||||
final int injectedAfterFilterWithId,
|
||||
@NonNull final FilterItem toBeInjectedFilterItem) {
|
||||
|
||||
int indexForFilterId = 0;
|
||||
boolean isFilterItemFound = false;
|
||||
FilterGroup groupWithTheSearchFilterItem = null;
|
||||
|
||||
for (final FilterGroup group : groups) {
|
||||
for (final FilterItem item : group.getFilterItems()) {
|
||||
if (item.getIdentifier() == injectedAfterFilterWithId) {
|
||||
isFilterItemFound = true;
|
||||
break;
|
||||
}
|
||||
indexForFilterId++;
|
||||
}
|
||||
|
||||
if (isFilterItemFound) {
|
||||
groupWithTheSearchFilterItem = group;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isFilterItemFound) {
|
||||
// we want to insert after the FilterItem we've searched
|
||||
indexForFilterId++;
|
||||
groupWithTheSearchFilterItem.getFilterItems()
|
||||
.add(indexForFilterId, toBeInjectedFilterItem);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject DividerItem between YouTube content filters and YoutubeMusic content filters.
|
||||
*/
|
||||
public static class DividerBetweenYoutubeAndYoutubeMusic extends InjectFilterItem {
|
||||
|
||||
private static boolean isYoutubeMusicDividerInjected = false;
|
||||
|
||||
protected DividerBetweenYoutubeAndYoutubeMusic() {
|
||||
super(App.getApp().getApplicationContext().getString(R.string.youtube),
|
||||
YoutubeFilters.ID_CF_MAIN_PLAYLISTS,
|
||||
new DividerItem(R.string.search_filters_youtube_music)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Have a static runner method to avoid creating unnecessary objects if already inserted.
|
||||
*/
|
||||
public static void run() {
|
||||
if (!isYoutubeMusicDividerInjected) {
|
||||
new DividerBetweenYoutubeAndYoutubeMusic();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isAlreadyInjected() {
|
||||
return isYoutubeMusicDividerInjected;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setAsInjected() {
|
||||
isYoutubeMusicDividerInjected = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to have a title divider between regular {@link FilterItem}s.
|
||||
*/
|
||||
public static class DividerItem extends FilterItem {
|
||||
|
||||
private final int resId;
|
||||
|
||||
public DividerItem(final int resId) {
|
||||
// the LibraryStringIds.. is not needed at all I just need one to satisfy FilterItem.
|
||||
super(FilterContainer.ITEM_IDENTIFIER_UNKNOWN, LibraryStringIds.SEARCH_FILTERS_ALL);
|
||||
this.resId = resId;
|
||||
}
|
||||
|
||||
public int getStringResId() {
|
||||
return this.resId;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,6 +23,8 @@ import java.util.Objects;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.collection.SparseArrayCompat;
|
||||
|
||||
import static org.schabi.newpipe.fragments.list.search.filter.InjectFilterItem.DividerItem;
|
||||
|
||||
public class SearchFilterDialogSpinnerAdapter extends BaseAdapter {
|
||||
|
||||
private final Context context;
|
||||
|
@ -48,12 +50,12 @@ public class SearchFilterDialogSpinnerAdapter extends BaseAdapter {
|
|||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return group.getFilterItems().length;
|
||||
return group.getFilterItems().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(final int position) {
|
||||
return group.getFilterItems()[position];
|
||||
return group.getFilterItems().get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -63,7 +65,7 @@ public class SearchFilterDialogSpinnerAdapter extends BaseAdapter {
|
|||
|
||||
@Override
|
||||
public View getView(final int position, final View convertView, final ViewGroup parent) {
|
||||
final FilterItem item = group.getFilterItems()[position];
|
||||
final FilterItem item = group.getFilterItems().get(position);
|
||||
final TextView view;
|
||||
|
||||
if (convertView != null) {
|
||||
|
@ -89,11 +91,12 @@ public class SearchFilterDialogSpinnerAdapter extends BaseAdapter {
|
|||
view.setVisibility(wrappedView.getVisibility());
|
||||
view.setEnabled(wrappedView.isEnabled());
|
||||
|
||||
if (item instanceof FilterItem.DividerItem) {
|
||||
if (item instanceof DividerItem) {
|
||||
final DividerItem dividerItem = (DividerItem) item;
|
||||
wrappedView.setEnabled(false);
|
||||
view.setEnabled(wrappedView.isEnabled());
|
||||
final String menuDividerTitle = ">>>"
|
||||
+ ServiceHelper.getTranslatedFilterString(item.getNameId(), context) + "<<<";
|
||||
+ context.getString(dividerItem.getStringResId()) + "<<<";
|
||||
view.setText(menuDividerTitle);
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +114,7 @@ public class SearchFilterDialogSpinnerAdapter extends BaseAdapter {
|
|||
isInitialEnabled,
|
||||
spinner);
|
||||
|
||||
if (item instanceof FilterItem.DividerItem) {
|
||||
if (item instanceof DividerItem) {
|
||||
wrappedView.setEnabled(false);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ import org.schabi.newpipe.extractor.search.filter.FilterGroup;
|
|||
import org.schabi.newpipe.extractor.search.filter.FilterItem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -185,7 +184,7 @@ public class SearchFilterLogic {
|
|||
final List<FilterGroup> sortGroups = getAllSortFilterGroups(filters);
|
||||
uiSortFilterWorker = createUiForFiltersWorker;
|
||||
|
||||
initFiltersUi(sortGroups.toArray(new FilterGroup[0]),
|
||||
initFiltersUi(sortGroups,
|
||||
sortFilterIdToUiItemMap,
|
||||
createUiForFiltersWorker);
|
||||
|
||||
|
@ -202,7 +201,7 @@ public class SearchFilterLogic {
|
|||
* @param createUiForFiltersWorker the implementation how to create the UI.
|
||||
*/
|
||||
private void initFiltersUi(
|
||||
@NonNull final FilterGroup[] filterGroups,
|
||||
@NonNull final List<FilterGroup> filterGroups,
|
||||
@NonNull final SparseArrayCompat<IUiItemWrapper> filterIdToUiItemMap,
|
||||
@NonNull final ICreateUiForFiltersWorker createUiForFiltersWorker) {
|
||||
|
||||
|
@ -235,7 +234,7 @@ public class SearchFilterLogic {
|
|||
* @param fidToSupersetSortFilterMap null possible, only for content filters relevant
|
||||
*/
|
||||
private void initFilters(
|
||||
@NonNull final FilterGroup[] filterGroups,
|
||||
@NonNull final List<FilterGroup> filterGroups,
|
||||
@NonNull final ExclusiveGroups exclusive,
|
||||
@NonNull final List<Integer> selectedFilters,
|
||||
@Nullable final SparseArrayCompat<FilterContainer> fidToSupersetSortFilterMap) {
|
||||
|
@ -290,9 +289,7 @@ public class SearchFilterLogic {
|
|||
private void initSortFilters() {
|
||||
final FilterContainer filters = searchQHFactory.getAvailableContentFilter();
|
||||
final List<FilterGroup> sortGroups = getAllSortFilterGroups(filters);
|
||||
|
||||
initFilters(sortGroups.toArray(new FilterGroup[0]), sortFilterExclusive,
|
||||
selectedSortFilters, null);
|
||||
initFilters(sortGroups, sortFilterExclusive, selectedSortFilters, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -450,7 +447,7 @@ public class SearchFilterLogic {
|
|||
for (final FilterGroup filterGroup : filters.getFilterGroups()) {
|
||||
final FilterContainer sf = filterGroup.getAllSortFilters();
|
||||
if (sf != null && sf.getFilterGroups() != null) {
|
||||
sortGroups.addAll(Arrays.asList(sf.getFilterGroups()));
|
||||
sortGroups.addAll(sf.getFilterGroups());
|
||||
}
|
||||
}
|
||||
return sortGroups;
|
||||
|
|
|
@ -23,6 +23,7 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.Nullable;
|
||||
|
||||
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
|
||||
import static org.schabi.newpipe.fragments.list.search.filter.InjectFilterItem.DividerItem;
|
||||
|
||||
public class SearchFilterOptionMenuAlikeDialogGenerator extends BaseSearchFilterUiDialogGenerator {
|
||||
private static final Integer NO_RESIZE_VIEW_TAG = 1;
|
||||
|
@ -149,7 +150,7 @@ public class SearchFilterOptionMenuAlikeDialogGenerator extends BaseSearchFilter
|
|||
for (final FilterItem item : filterGroup.getFilterItems()) {
|
||||
|
||||
final View view;
|
||||
if (item instanceof FilterItem.DividerItem) {
|
||||
if (item instanceof DividerItem) {
|
||||
view = createDividerTextView(item, getLayoutParamsViews());
|
||||
} else {
|
||||
view = createViewItemRadio(item, getLayoutParamsViews());
|
||||
|
@ -175,7 +176,7 @@ public class SearchFilterOptionMenuAlikeDialogGenerator extends BaseSearchFilter
|
|||
@NonNull final UiSelectorDelegate selectorDelegate) {
|
||||
for (final FilterItem item : filterGroup.getFilterItems()) {
|
||||
final View view;
|
||||
if (item instanceof FilterItem.DividerItem) {
|
||||
if (item instanceof DividerItem) {
|
||||
view = createDividerTextView(item, getLayoutParamsViews());
|
||||
} else {
|
||||
final CheckBox checkBox = createCheckBox(item, getLayoutParamsViews());
|
||||
|
@ -278,10 +279,11 @@ public class SearchFilterOptionMenuAlikeDialogGenerator extends BaseSearchFilter
|
|||
@NonNull
|
||||
private TextView createDividerTextView(@NonNull final FilterItem item,
|
||||
@NonNull final ViewGroup.LayoutParams layoutParams) {
|
||||
final DividerItem dividerItem = (DividerItem) item;
|
||||
final TextView view = new TextView(context);
|
||||
view.setEnabled(true);
|
||||
final String menuDividerTitle =
|
||||
ServiceHelper.getTranslatedFilterString(item.getNameId(), context);
|
||||
context.getString(dividerItem.getStringResId());
|
||||
view.setText(menuDividerTitle);
|
||||
view.setGravity(Gravity.TOP);
|
||||
view.setLayoutParams(layoutParams);
|
||||
|
|
|
@ -24,6 +24,7 @@ import androidx.appcompat.view.menu.MenuBuilder;
|
|||
import androidx.core.view.MenuCompat;
|
||||
|
||||
import static android.content.ContentValues.TAG;
|
||||
import static org.schabi.newpipe.fragments.list.search.filter.InjectFilterItem.DividerItem;
|
||||
import static org.schabi.newpipe.fragments.list.search.filter.SearchFilterLogic.ICreateUiForFiltersWorker;
|
||||
import static org.schabi.newpipe.fragments.list.search.filter.SearchFilterLogic.IUiItemWrapper;
|
||||
|
||||
|
@ -227,9 +228,10 @@ public class SearchFilterUIOptionMenu extends BaseSearchFilterUiGenerator {
|
|||
@NonNull final FilterGroup filterGroup) {
|
||||
final MenuItem item = createMenuItem(filterItem);
|
||||
|
||||
if (filterItem instanceof FilterItem.DividerItem) {
|
||||
if (filterItem instanceof DividerItem) {
|
||||
final DividerItem dividerItem = (DividerItem) filterItem;
|
||||
final String menuDividerTitle = ">>>"
|
||||
+ ServiceHelper.getTranslatedFilterString(filterItem.getNameId(), context)
|
||||
+ context.getString(dividerItem.getStringResId())
|
||||
+ "<<<";
|
||||
item.setTitle(menuDividerTitle);
|
||||
item.setEnabled(false);
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
package org.schabi.newpipe.filter;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.search.filter.FilterContainer;
|
||||
import org.schabi.newpipe.extractor.search.filter.FilterGroup;
|
||||
import org.schabi.newpipe.extractor.search.filter.FilterItem;
|
||||
import org.schabi.newpipe.extractor.services.youtube.search.filter.YoutubeFilters;
|
||||
import org.schabi.newpipe.fragments.list.search.filter.InjectFilterItem;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class InjectFilterItemTest {
|
||||
|
||||
static final String SERVICE_NAME = "YouTube";
|
||||
|
||||
@Test
|
||||
public void injectIntoFilterGroupTest() throws ExtractionException {
|
||||
final FilterContainer filterContainer = NewPipe.getService(SERVICE_NAME)
|
||||
.getSearchQHFactory().getAvailableContentFilter();
|
||||
|
||||
final AtomicInteger itemCount = new AtomicInteger();
|
||||
assertFalse(getInjectedFilterItem(filterContainer, itemCount).isPresent());
|
||||
|
||||
InjectDividerTestClass.run(SERVICE_NAME);
|
||||
|
||||
final int expectedInjectedItemPosition = 5;
|
||||
final AtomicInteger injectedItemPosition = new AtomicInteger();
|
||||
assertTrue(getInjectedFilterItem(filterContainer, injectedItemPosition).isPresent());
|
||||
assertTrue(itemCount.get() > injectedItemPosition.get());
|
||||
assertEquals(expectedInjectedItemPosition, injectedItemPosition.get());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Optional<FilterItem> getInjectedFilterItem(
|
||||
@NonNull final FilterContainer filterContainer,
|
||||
@NonNull final AtomicInteger itemCount) {
|
||||
|
||||
return filterContainer.getFilterGroups().stream()
|
||||
.map(FilterGroup::getFilterItems)
|
||||
.flatMap(Collection::stream)
|
||||
.filter(item -> {
|
||||
itemCount.getAndIncrement();
|
||||
return item instanceof InjectFilterItem.DividerItem;
|
||||
})
|
||||
.findAny();
|
||||
}
|
||||
|
||||
public static class InjectDividerTestClass extends InjectFilterItem {
|
||||
|
||||
private static boolean isDividerInjected = false;
|
||||
|
||||
protected InjectDividerTestClass(@NonNull final String serviceName) {
|
||||
super(serviceName,
|
||||
YoutubeFilters.ID_CF_MAIN_PLAYLISTS,
|
||||
new DividerItem(0)
|
||||
);
|
||||
}
|
||||
|
||||
public static void run(final String serviceName) {
|
||||
if (!isDividerInjected) {
|
||||
new InjectDividerTestClass(serviceName);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isAlreadyInjected() {
|
||||
return isDividerInjected;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setAsInjected() {
|
||||
isDividerInjected = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,7 +17,6 @@ import org.schabi.newpipe.fragments.list.search.filter.BaseSearchFilterUiGenerat
|
|||
import org.schabi.newpipe.fragments.list.search.filter.SearchFilterLogic;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -347,7 +346,7 @@ public class SearchFilterLogicAndUiGeneratorTest {
|
|||
private void expectSortFiltersToBeVisible(final int id) {
|
||||
final FilterContainer sortFilterVariant = service.getSearchQHFactory()
|
||||
.getContentFilterSortFilterVariant(id);
|
||||
assertTrue(sortFilterVariant.getFilterGroups().length > 0);
|
||||
assertTrue(!sortFilterVariant.getFilterGroups().isEmpty());
|
||||
for (final FilterGroup group : sortFilterVariant.getFilterGroups()) {
|
||||
for (final FilterItem item : group.getFilterItems()) {
|
||||
final int itemId = item.getIdentifier();
|
||||
|
@ -389,9 +388,9 @@ public class SearchFilterLogicAndUiGeneratorTest {
|
|||
final FilterContainer allSortFilters = service.getSearchQHFactory()
|
||||
.getContentFilterSortFilterVariant(PeertubeFilters.ID_CF_MAIN_ALL);
|
||||
// second way
|
||||
final Optional<FilterGroup> allSortFilters2 = Arrays.stream(service.getSearchQHFactory()
|
||||
final Optional<FilterGroup> allSortFilters2 = service.getSearchQHFactory()
|
||||
.getAvailableContentFilter()
|
||||
.getFilterGroups())
|
||||
.getFilterGroups().stream()
|
||||
.filter(filterGroup
|
||||
-> (filterGroup.getIdentifier() == PeertubeFilters.ID_CF_MAIN_GRP))
|
||||
.findFirst();
|
||||
|
@ -399,7 +398,7 @@ public class SearchFilterLogicAndUiGeneratorTest {
|
|||
assertNotNull(allSortFilters);
|
||||
assertTrue(allSortFilters2.isPresent());
|
||||
assertEquals(allSortFilters, allSortFilters2.get().getAllSortFilters());
|
||||
assertTrue(allSortFilters.getFilterGroups().length > 0);
|
||||
assertTrue(!allSortFilters.getFilterGroups().isEmpty());
|
||||
assertNotNull(sortWorker.areAnySortFiltersVisible);
|
||||
assertTrue(sortWorker.areAnySortFiltersVisible.isPresent());
|
||||
assertFalse(sortWorker.areAnySortFiltersVisible.get());
|
||||
|
|
Loading…
Add table
Reference in a new issue