Skip to content

Commit 5c6201c

Browse files
committed
searchfilters: 3rd Ui: action based UI (enhanched legacy menu)
This approach is more or less a hack but if all else fails. Could later be dropped or right away.
1 parent e453d13 commit 5c6201c

1 file changed

Lines changed: 318 additions & 0 deletions

File tree

Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
// Created by evermind-zz 2022, licensed GNU GPL version 3 or later
2+
3+
package org.schabi.newpipe.fragments.list.search.filter;
4+
5+
import android.annotation.SuppressLint;
6+
import android.content.Context;
7+
import android.util.Log;
8+
import android.view.Menu;
9+
import android.view.MenuItem;
10+
import android.view.View;
11+
12+
import org.schabi.newpipe.R;
13+
import org.schabi.newpipe.extractor.StreamingService;
14+
import org.schabi.newpipe.extractor.search.filter.FilterContainer;
15+
import org.schabi.newpipe.extractor.search.filter.FilterGroup;
16+
import org.schabi.newpipe.extractor.search.filter.FilterItem;
17+
import org.schabi.newpipe.util.ServiceHelper;
18+
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
22+
import androidx.annotation.NonNull;
23+
import androidx.appcompat.view.menu.MenuBuilder;
24+
import androidx.core.view.MenuCompat;
25+
26+
import static android.content.ContentValues.TAG;
27+
28+
public class SearchFilterUIOptionMenu extends SearchFilterUiGeneratorBase {
29+
30+
// Menu groups identifier
31+
private static final int MENU_GROUP_SEARCH_RESET_BUTTONS = 0;
32+
// give them negative ids to not conflict with the ids of the filters
33+
private static final int MENU_ID_SEARCH_BUTTON = -100;
34+
private static final int MENU_ID_RESET_BUTTON = -101;
35+
private final Context context;
36+
private Menu menu = null;
37+
// initialize with first group id -> next group after the search/reset buttons group
38+
private int newLastUsedGroupId = MENU_GROUP_SEARCH_RESET_BUTTONS + 1;
39+
private int firstSortFilterGroupId;
40+
41+
public SearchFilterUIOptionMenu(final StreamingService service,
42+
final Callback callback, final Context context) {
43+
super(service.getSearchQHFactory(), callback);
44+
this.context = context;
45+
}
46+
47+
int getLastUsedGroupIdThanIncrement() {
48+
return newLastUsedGroupId++;
49+
}
50+
51+
private void addSortFilterUiToItemMap(final int id,
52+
final MenuItem item) {
53+
addSortFilterUiWrapperToItemMap(id, new MenuItemUiWrapper(item));
54+
}
55+
56+
@Override
57+
protected void handleIdInNonExclusiveGroup(final int id,
58+
final IUiItemWrapper uiItemWrapper,
59+
final List<Integer> selectedFilter) {
60+
uiItemWrapper.setChecked(!uiItemWrapper.isChecked()); // toggle
61+
super.handleIdInNonExclusiveGroup(id, uiItemWrapper, selectedFilter);
62+
}
63+
64+
@SuppressLint("RestrictedApi")
65+
private void alwaysShowMenuItemIcon(final Menu theMenu) {
66+
// always show icons
67+
if (theMenu instanceof MenuBuilder) {
68+
final MenuBuilder builder = ((MenuBuilder) theMenu);
69+
builder.setOptionalIconsVisible(true);
70+
}
71+
}
72+
73+
public void createSearchUI(@NonNull final Menu theMenu) {
74+
this.menu = theMenu;
75+
alwaysShowMenuItemIcon(theMenu);
76+
77+
createSearchUI();
78+
79+
MenuCompat.setGroupDividerEnabled(theMenu, true);
80+
}
81+
82+
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
83+
if (item.getGroupId() == MENU_GROUP_SEARCH_RESET_BUTTONS
84+
&& item.getItemId() == MENU_ID_SEARCH_BUTTON) {
85+
prepareForSearch();
86+
} else { // all other menu groups -> reset, content filters and sort filters
87+
88+
// main part for holding onto the menu -> not closing it
89+
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
90+
item.setActionView(new View(context));
91+
item.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
92+
93+
@Override
94+
public boolean onMenuItemActionExpand(final MenuItem item) {
95+
if (item.getGroupId() == MENU_GROUP_SEARCH_RESET_BUTTONS
96+
&& item.getItemId() == MENU_ID_RESET_BUTTON) {
97+
reset();
98+
} else if (item.getGroupId() < firstSortFilterGroupId) { // content filters
99+
final int filterId = item.getItemId();
100+
selectContentFilter(filterId);
101+
} else { // the sort filters
102+
Log.d(TAG, "onMenuItemActionExpand: sort filters are here");
103+
selectSortFilter(item.getItemId());
104+
}
105+
106+
return false;
107+
}
108+
109+
@Override
110+
public boolean onMenuItemActionCollapse(final MenuItem item) {
111+
return false;
112+
}
113+
});
114+
}
115+
116+
return false;
117+
}
118+
119+
@Override
120+
protected ICreateUiForFiltersWorker createSortFilterWorker() {
121+
return new CreateSortFilterUI();
122+
}
123+
124+
@Override
125+
protected ICreateUiForFiltersWorker createContentFilterWorker() {
126+
return new CreateContentFilterUI();
127+
}
128+
129+
@Override
130+
public void onResume() {
131+
// Menu does not need a implementation here
132+
}
133+
134+
@Override
135+
public void onPause() {
136+
// Menu does not need a implementation here
137+
}
138+
139+
private static class MenuItemUiWrapper implements IUiItemWrapper {
140+
141+
private final MenuItem item;
142+
143+
MenuItemUiWrapper(final MenuItem item) {
144+
this.item = item;
145+
}
146+
147+
@Override
148+
public void setVisible(final boolean visible) {
149+
item.setVisible(visible);
150+
}
151+
152+
@Override
153+
public int getItemId() {
154+
return item.getItemId();
155+
}
156+
157+
@Override
158+
public int getGroupId() {
159+
return item.getGroupId();
160+
}
161+
162+
@Override
163+
public boolean isChecked() {
164+
return item.isChecked();
165+
}
166+
167+
@Override
168+
public void setChecked(final boolean checked) {
169+
item.setChecked(checked);
170+
}
171+
}
172+
173+
private class CreateContentFilterUI implements SearchFilterLogic.ICreateUiForFiltersWorker {
174+
175+
/**
176+
* MenuItem's that should not be checkable.
177+
*/
178+
List<MenuItem> nonCheckableMenuItems = new ArrayList<>();
179+
180+
/**
181+
* {@link Menu#setGroupCheckable(int, boolean, boolean)} makes all {@link MenuItem}
182+
* checkable.
183+
* <p>
184+
* We do not want a group header or a group divider to be checkable. Therefore this method
185+
* calls above mentioned method and afterwards makes all items uncheckable that are placed
186+
* inside {@link #nonCheckableMenuItems}.
187+
*
188+
* @param isOnlyOneCheckable is in group only one selection allowed.
189+
* @param groupId which group should be affected
190+
*/
191+
private void makeAllowedMenuItemInGroupCheckable(final boolean isOnlyOneCheckable,
192+
final int groupId) {
193+
// this method makes all MenuItem's checkable
194+
menu.setGroupCheckable(groupId, true, isOnlyOneCheckable);
195+
// uncheckable unwanted
196+
for (final MenuItem uncheckableItem : nonCheckableMenuItems) {
197+
if (uncheckableItem != null) {
198+
uncheckableItem.setCheckable(false);
199+
}
200+
}
201+
nonCheckableMenuItems.clear();
202+
}
203+
204+
@Override
205+
public void prepare() {
206+
// create the search button
207+
menu.add(MENU_GROUP_SEARCH_RESET_BUTTONS,
208+
MENU_ID_SEARCH_BUTTON,
209+
0,
210+
ServiceHelper.getTranslatedFilterString("search", context))
211+
.setEnabled(true)
212+
.setCheckable(false)
213+
.setIcon(R.drawable.ic_search);
214+
215+
menu.add(MENU_GROUP_SEARCH_RESET_BUTTONS,
216+
MENU_ID_RESET_BUTTON,
217+
0,
218+
ServiceHelper.getTranslatedFilterString("reset", context))
219+
.setEnabled(true)
220+
.setCheckable(false)
221+
.setIcon(R.drawable.ic_settings_backup_restore);
222+
}
223+
224+
@Override
225+
public void createFilterGroupBeforeItems(
226+
final FilterGroup filterGroup) {
227+
if (filterGroup.getName() != null) {
228+
createNotEnabledAndUncheckableGroupTitleMenuItem(
229+
FilterContainer.ITEM_IDENTIFIER_UNKNOWN, filterGroup.getName());
230+
}
231+
}
232+
233+
protected MenuItem createNotEnabledAndUncheckableGroupTitleMenuItem(final int identifier,
234+
final String name) {
235+
final MenuItem item = menu.add(
236+
newLastUsedGroupId,
237+
identifier,
238+
0,
239+
ServiceHelper.getTranslatedFilterString(name, context));
240+
item.setEnabled(false);
241+
242+
nonCheckableMenuItems.add(item);
243+
244+
return item;
245+
246+
}
247+
248+
@Override
249+
public void createFilterItem(final FilterItem filterItem,
250+
final FilterGroup filterGroup) {
251+
final MenuItem item = createMenuItem(filterItem);
252+
253+
if (filterItem instanceof FilterItem.DividerItem) {
254+
final String menuDividerTitle = ">>>"
255+
+ ServiceHelper.getTranslatedFilterString(filterItem.getName(), context)
256+
+ "<<<";
257+
item.setTitle(menuDividerTitle);
258+
item.setEnabled(false);
259+
nonCheckableMenuItems.add(item);
260+
}
261+
262+
addContentFilterUiWrapperToItemMap(filterItem.getIdentifier(),
263+
new MenuItemUiWrapper(item));
264+
}
265+
266+
protected MenuItem createMenuItem(final FilterItem filterItem) {
267+
final MenuItem item = menu.add(newLastUsedGroupId,
268+
filterItem.getIdentifier(),
269+
0,
270+
ServiceHelper.getTranslatedFilterString(filterItem.getName(), context));
271+
return item;
272+
}
273+
274+
@Override
275+
public void createFilterGroupAfterItems(final FilterGroup filterGroup) {
276+
makeAllowedMenuItemInGroupCheckable(filterGroup.isOnlyOneCheckable(),
277+
getLastUsedGroupIdThanIncrement());
278+
}
279+
280+
@Override
281+
public void finish() {
282+
firstSortFilterGroupId = newLastUsedGroupId;
283+
}
284+
285+
@Override
286+
public void filtersVisible(final boolean areFiltersVisible) {
287+
}
288+
}
289+
290+
private class CreateSortFilterUI extends CreateContentFilterUI {
291+
292+
@Override
293+
public void prepare() {
294+
firstSortFilterGroupId = newLastUsedGroupId;
295+
}
296+
297+
@Override
298+
public void createFilterGroupBeforeItems(
299+
final FilterGroup filterGroup) {
300+
if (filterGroup.getName() != null) {
301+
final MenuItem item = createNotEnabledAndUncheckableGroupTitleMenuItem(
302+
filterGroup.getIdentifier(), filterGroup.getName());
303+
addSortFilterUiToItemMap(filterGroup.getIdentifier(), item);
304+
}
305+
}
306+
307+
@Override
308+
public void createFilterItem(final FilterItem filterItem,
309+
final FilterGroup filterGroup) {
310+
final MenuItem item = createMenuItem(filterItem);
311+
addSortFilterUiToItemMap(filterItem.getIdentifier(), item);
312+
}
313+
314+
@Override
315+
public void finish() {
316+
}
317+
}
318+
}

0 commit comments

Comments
 (0)