Skip to content

Commit d7bfb0a

Browse files
committed
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.
1 parent 2da5876 commit d7bfb0a

8 files changed

Lines changed: 269 additions & 28 deletions

File tree

app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchViewModel.kt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import androidx.lifecycle.viewmodel.initializer
77
import androidx.lifecycle.viewmodel.viewModelFactory
88
import org.schabi.newpipe.extractor.NewPipe
99
import org.schabi.newpipe.extractor.search.filter.FilterItem
10+
import org.schabi.newpipe.fragments.list.search.filter.InjectFilterItem
1011
import org.schabi.newpipe.fragments.list.search.filter.SearchFilterLogic
1112
import org.schabi.newpipe.fragments.list.search.filter.SearchFilterLogic.Factory.Variant
1213

@@ -38,11 +39,16 @@ class SearchViewModel(
3839
val doSearchLiveData: LiveData<Boolean>
3940
get() = doSearchMutableLiveData
4041

41-
val searchFilterLogic = SearchFilterLogic.Factory.create(
42-
logicVariant, NewPipe.getService(serviceId).searchQHFactory, null
43-
)
42+
var searchFilterLogic: SearchFilterLogic
4443

4544
init {
45+
// inject before creating SearchFilterLogic
46+
InjectFilterItem.DividerBetweenYoutubeAndYoutubeMusic.run()
47+
48+
searchFilterLogic = SearchFilterLogic.Factory.create(
49+
logicVariant,
50+
NewPipe.getService(serviceId).searchQHFactory, null
51+
)
4652
searchFilterLogic.restorePreviouslySelectedFilters(
4753
userSelectedContentFilterList,
4854
userSelectedSortFilterList
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package org.schabi.newpipe.fragments.list.search.filter;
2+
3+
import org.schabi.newpipe.App;
4+
import org.schabi.newpipe.R;
5+
import org.schabi.newpipe.extractor.NewPipe;
6+
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
7+
import org.schabi.newpipe.extractor.search.filter.FilterContainer;
8+
import org.schabi.newpipe.extractor.search.filter.FilterGroup;
9+
import org.schabi.newpipe.extractor.search.filter.FilterItem;
10+
import org.schabi.newpipe.extractor.search.filter.LibraryStringIds;
11+
import org.schabi.newpipe.extractor.services.youtube.search.filter.YoutubeFilters;
12+
13+
import java.util.List;
14+
15+
import androidx.annotation.NonNull;
16+
17+
/**
18+
* Inject a {@link FilterItem} that actually should not be a real filter.
19+
* <p>
20+
* This base class is meant to inject eg {@link DividerItem} (that inherits {@link FilterItem})
21+
* as Divider between {@link FilterItem}. It will be shown in the UI's.
22+
* <p>
23+
* Of course you have to handle {@link DividerItem} or whatever in the Ui's.
24+
* For that for example have a look at {@link SearchFilterDialogSpinnerAdapter}.
25+
*/
26+
public abstract class InjectFilterItem {
27+
28+
protected InjectFilterItem(
29+
@NonNull final String serviceName,
30+
final int injectedAfterFilterWithId,
31+
@NonNull final FilterItem toBeInjectedFilterItem) {
32+
33+
prepareAndInject(serviceName, injectedAfterFilterWithId, toBeInjectedFilterItem);
34+
}
35+
36+
// Please refer a static boolean to determine if already injected
37+
protected abstract boolean isAlreadyInjected();
38+
39+
// Please refer a static boolean to determine if already injected
40+
protected abstract void setAsInjected();
41+
42+
private void prepareAndInject(
43+
@NonNull final String serviceName,
44+
final int injectedAfterFilterWithId,
45+
@NonNull final FilterItem toBeInjectedFilterItem) {
46+
47+
if (isAlreadyInjected()) { // already run
48+
return;
49+
}
50+
51+
try { // using serviceName to test if we are trying to inject into the right service
52+
final List<FilterGroup> groups = NewPipe.getService(serviceName)
53+
.getSearchQHFactory().getAvailableContentFilter().getFilterGroups();
54+
injectFilterItemIntoGroup(
55+
groups,
56+
injectedAfterFilterWithId,
57+
toBeInjectedFilterItem);
58+
setAsInjected();
59+
} catch (final ExtractionException ignored) {
60+
// no the service we want to prepareAndInject -> so ignore
61+
}
62+
}
63+
64+
private void injectFilterItemIntoGroup(
65+
@NonNull final List<FilterGroup> groups,
66+
final int injectedAfterFilterWithId,
67+
@NonNull final FilterItem toBeInjectedFilterItem) {
68+
69+
int indexForFilterId = 0;
70+
boolean isFilterItemFound = false;
71+
FilterGroup groupWithTheSearchFilterItem = null;
72+
73+
for (final FilterGroup group : groups) {
74+
for (final FilterItem item : group.getFilterItems()) {
75+
if (item.getIdentifier() == injectedAfterFilterWithId) {
76+
isFilterItemFound = true;
77+
break;
78+
}
79+
indexForFilterId++;
80+
}
81+
82+
if (isFilterItemFound) {
83+
groupWithTheSearchFilterItem = group;
84+
break;
85+
}
86+
}
87+
88+
if (isFilterItemFound) {
89+
// we want to insert after the FilterItem we've searched
90+
indexForFilterId++;
91+
groupWithTheSearchFilterItem.getFilterItems()
92+
.add(indexForFilterId, toBeInjectedFilterItem);
93+
}
94+
}
95+
96+
/**
97+
* Inject DividerItem between YouTube content filters and YoutubeMusic content filters.
98+
*/
99+
public static class DividerBetweenYoutubeAndYoutubeMusic extends InjectFilterItem {
100+
101+
private static boolean isYoutubeMusicDividerInjected = false;
102+
103+
protected DividerBetweenYoutubeAndYoutubeMusic() {
104+
super(App.getApp().getApplicationContext().getString(R.string.youtube),
105+
YoutubeFilters.ID_CF_MAIN_PLAYLISTS,
106+
new DividerItem(R.string.search_filters_youtube_music)
107+
);
108+
}
109+
110+
/**
111+
* Have a static runner method to avoid creating unnecessary objects if already inserted.
112+
*/
113+
public static void run() {
114+
if (!isYoutubeMusicDividerInjected) {
115+
new DividerBetweenYoutubeAndYoutubeMusic();
116+
}
117+
}
118+
119+
@Override
120+
protected boolean isAlreadyInjected() {
121+
return isYoutubeMusicDividerInjected;
122+
}
123+
124+
@Override
125+
protected void setAsInjected() {
126+
isYoutubeMusicDividerInjected = true;
127+
}
128+
}
129+
130+
/**
131+
* Used to have a title divider between regular {@link FilterItem}s.
132+
*/
133+
public static class DividerItem extends FilterItem {
134+
135+
private final int resId;
136+
137+
public DividerItem(final int resId) {
138+
// the LibraryStringIds.. is not needed at all I just need one to satisfy FilterItem.
139+
super(FilterContainer.ITEM_IDENTIFIER_UNKNOWN, LibraryStringIds.SEARCH_FILTERS_ALL);
140+
this.resId = resId;
141+
}
142+
143+
public int getStringResId() {
144+
return this.resId;
145+
}
146+
}
147+
}

app/src/main/java/org/schabi/newpipe/fragments/list/search/filter/SearchFilterDialogSpinnerAdapter.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import androidx.annotation.NonNull;
2424
import androidx.collection.SparseArrayCompat;
2525

26+
import static org.schabi.newpipe.fragments.list.search.filter.InjectFilterItem.DividerItem;
27+
2628
public class SearchFilterDialogSpinnerAdapter extends BaseAdapter {
2729

2830
private final Context context;
@@ -48,12 +50,12 @@ public SearchFilterDialogSpinnerAdapter(
4850

4951
@Override
5052
public int getCount() {
51-
return group.getFilterItems().length;
53+
return group.getFilterItems().size();
5254
}
5355

5456
@Override
5557
public Object getItem(final int position) {
56-
return group.getFilterItems()[position];
58+
return group.getFilterItems().get(position);
5759
}
5860

5961
@Override
@@ -63,7 +65,7 @@ public long getItemId(final int position) {
6365

6466
@Override
6567
public View getView(final int position, final View convertView, final ViewGroup parent) {
66-
final FilterItem item = group.getFilterItems()[position];
68+
final FilterItem item = group.getFilterItems().get(position);
6769
final TextView view;
6870

6971
if (convertView != null) {
@@ -89,11 +91,12 @@ private void initViewWithData(final int position,
8991
view.setVisibility(wrappedView.getVisibility());
9092
view.setEnabled(wrappedView.isEnabled());
9193

92-
if (item instanceof FilterItem.DividerItem) {
94+
if (item instanceof DividerItem) {
95+
final DividerItem dividerItem = (DividerItem) item;
9396
wrappedView.setEnabled(false);
9497
view.setEnabled(wrappedView.isEnabled());
9598
final String menuDividerTitle = ">>>"
96-
+ ServiceHelper.getTranslatedFilterString(item.getNameId(), context) + "<<<";
99+
+ context.getString(dividerItem.getStringResId()) + "<<<";
97100
view.setText(menuDividerTitle);
98101
}
99102
}
@@ -111,7 +114,7 @@ private void createViewWrappers() {
111114
isInitialEnabled,
112115
spinner);
113116

114-
if (item instanceof FilterItem.DividerItem) {
117+
if (item instanceof DividerItem) {
115118
wrappedView.setEnabled(false);
116119
}
117120

app/src/main/java/org/schabi/newpipe/fragments/list/search/filter/SearchFilterLogic.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import org.schabi.newpipe.extractor.search.filter.FilterItem;
99

1010
import java.util.ArrayList;
11-
import java.util.Arrays;
1211
import java.util.Collections;
1312
import java.util.HashSet;
1413
import java.util.List;
@@ -185,7 +184,7 @@ public void initSortFiltersUi(
185184
final List<FilterGroup> sortGroups = getAllSortFilterGroups(filters);
186185
uiSortFilterWorker = createUiForFiltersWorker;
187186

188-
initFiltersUi(sortGroups.toArray(new FilterGroup[0]),
187+
initFiltersUi(sortGroups,
189188
sortFilterIdToUiItemMap,
190189
createUiForFiltersWorker);
191190

@@ -202,7 +201,7 @@ public void initSortFiltersUi(
202201
* @param createUiForFiltersWorker the implementation how to create the UI.
203202
*/
204203
private void initFiltersUi(
205-
@NonNull final FilterGroup[] filterGroups,
204+
@NonNull final List<FilterGroup> filterGroups,
206205
@NonNull final SparseArrayCompat<IUiItemWrapper> filterIdToUiItemMap,
207206
@NonNull final ICreateUiForFiltersWorker createUiForFiltersWorker) {
208207

@@ -235,7 +234,7 @@ private void initFiltersUi(
235234
* @param fidToSupersetSortFilterMap null possible, only for content filters relevant
236235
*/
237236
private void initFilters(
238-
@NonNull final FilterGroup[] filterGroups,
237+
@NonNull final List<FilterGroup> filterGroups,
239238
@NonNull final ExclusiveGroups exclusive,
240239
@NonNull final List<Integer> selectedFilters,
241240
@Nullable final SparseArrayCompat<FilterContainer> fidToSupersetSortFilterMap) {
@@ -290,9 +289,7 @@ private void initContentFilters() {
290289
private void initSortFilters() {
291290
final FilterContainer filters = searchQHFactory.getAvailableContentFilter();
292291
final List<FilterGroup> sortGroups = getAllSortFilterGroups(filters);
293-
294-
initFilters(sortGroups.toArray(new FilterGroup[0]), sortFilterExclusive,
295-
selectedSortFilters, null);
292+
initFilters(sortGroups, sortFilterExclusive, selectedSortFilters, null);
296293
}
297294

298295
/**
@@ -450,7 +447,7 @@ private List<FilterGroup> getAllSortFilterGroups(@Nullable final FilterContainer
450447
for (final FilterGroup filterGroup : filters.getFilterGroups()) {
451448
final FilterContainer sf = filterGroup.getAllSortFilters();
452449
if (sf != null && sf.getFilterGroups() != null) {
453-
sortGroups.addAll(Arrays.asList(sf.getFilterGroups()));
450+
sortGroups.addAll(sf.getFilterGroups());
454451
}
455452
}
456453
return sortGroups;

app/src/main/java/org/schabi/newpipe/fragments/list/search/filter/SearchFilterOptionMenuAlikeDialogGenerator.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import androidx.annotation.Nullable;
2424

2525
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
26+
import static org.schabi.newpipe.fragments.list.search.filter.InjectFilterItem.DividerItem;
2627

2728
public class SearchFilterOptionMenuAlikeDialogGenerator extends BaseSearchFilterUiDialogGenerator {
2829
private static final Integer NO_RESIZE_VIEW_TAG = 1;
@@ -149,7 +150,7 @@ private void createUiElementsForSingleSelectableItemsFilterGroup(
149150
for (final FilterItem item : filterGroup.getFilterItems()) {
150151

151152
final View view;
152-
if (item instanceof FilterItem.DividerItem) {
153+
if (item instanceof DividerItem) {
153154
view = createDividerTextView(item, getLayoutParamsViews());
154155
} else {
155156
view = createViewItemRadio(item, getLayoutParamsViews());
@@ -175,7 +176,7 @@ private void createUiElementsForMultipleSelectableItemsFilterGroup(
175176
@NonNull final UiSelectorDelegate selectorDelegate) {
176177
for (final FilterItem item : filterGroup.getFilterItems()) {
177178
final View view;
178-
if (item instanceof FilterItem.DividerItem) {
179+
if (item instanceof DividerItem) {
179180
view = createDividerTextView(item, getLayoutParamsViews());
180181
} else {
181182
final CheckBox checkBox = createCheckBox(item, getLayoutParamsViews());
@@ -278,10 +279,11 @@ private CheckBox createCheckBox(@NonNull final FilterItem item,
278279
@NonNull
279280
private TextView createDividerTextView(@NonNull final FilterItem item,
280281
@NonNull final ViewGroup.LayoutParams layoutParams) {
282+
final DividerItem dividerItem = (DividerItem) item;
281283
final TextView view = new TextView(context);
282284
view.setEnabled(true);
283285
final String menuDividerTitle =
284-
ServiceHelper.getTranslatedFilterString(item.getNameId(), context);
286+
context.getString(dividerItem.getStringResId());
285287
view.setText(menuDividerTitle);
286288
view.setGravity(Gravity.TOP);
287289
view.setLayoutParams(layoutParams);

app/src/main/java/org/schabi/newpipe/fragments/list/search/filter/SearchFilterUIOptionMenu.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import androidx.core.view.MenuCompat;
2525

2626
import static android.content.ContentValues.TAG;
27+
import static org.schabi.newpipe.fragments.list.search.filter.InjectFilterItem.DividerItem;
2728
import static org.schabi.newpipe.fragments.list.search.filter.SearchFilterLogic.ICreateUiForFiltersWorker;
2829
import static org.schabi.newpipe.fragments.list.search.filter.SearchFilterLogic.IUiItemWrapper;
2930

@@ -227,9 +228,10 @@ public void createFilterItem(@NonNull final FilterItem filterItem,
227228
@NonNull final FilterGroup filterGroup) {
228229
final MenuItem item = createMenuItem(filterItem);
229230

230-
if (filterItem instanceof FilterItem.DividerItem) {
231+
if (filterItem instanceof DividerItem) {
232+
final DividerItem dividerItem = (DividerItem) filterItem;
231233
final String menuDividerTitle = ">>>"
232-
+ ServiceHelper.getTranslatedFilterString(filterItem.getNameId(), context)
234+
+ context.getString(dividerItem.getStringResId())
233235
+ "<<<";
234236
item.setTitle(menuDividerTitle);
235237
item.setEnabled(false);

0 commit comments

Comments
 (0)