Skip to content

Commit f810109

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 d02c322 commit f810109

1 file changed

Lines changed: 316 additions & 0 deletions

File tree

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

0 commit comments

Comments
 (0)