Skip to content

Commit 0578e0e

Browse files
committed
searchfilters: 1st Ui: default dialog for search content and sort filters
1 parent 9d9f7c4 commit 0578e0e

5 files changed

Lines changed: 583 additions & 0 deletions

File tree

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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.view.LayoutInflater;
6+
import android.view.View;
7+
import android.view.ViewGroup;
8+
9+
import org.schabi.newpipe.databinding.SearchFilterDialogFragmentBinding;
10+
import org.schabi.newpipe.extractor.StreamingService;
11+
12+
import java.util.ArrayList;
13+
14+
import androidx.appcompat.widget.Toolbar;
15+
import androidx.fragment.app.DialogFragment;
16+
17+
/**
18+
* A search filter dialog that also looks like a dialog aka. 'dialog style'.
19+
*/
20+
public class SearchFilterDialogFragment extends BaseSearchFilterDialogFragment {
21+
22+
private SearchFilterDialogFragmentBinding binding;
23+
24+
public static DialogFragment newInstance(
25+
final int serviceId,
26+
final ArrayList<Integer> userSelectedContentFilter,
27+
final ArrayList<Integer> userSelectedSortFilter) {
28+
return initDialogArguments(
29+
new SearchFilterDialogFragment(),
30+
serviceId,
31+
userSelectedContentFilter,
32+
userSelectedSortFilter);
33+
}
34+
35+
@Override
36+
protected BaseSearchFilterUiGenerator createSearchFilterDialogGenerator(
37+
final StreamingService service,
38+
final SearchFilterLogic.Callback callback) {
39+
return new SearchFilterDialogGenerator(service,
40+
binding.verticalScroll, requireContext(), callback);
41+
}
42+
43+
@Override
44+
protected Toolbar getToolbar() {
45+
return binding.toolbarLayout.toolbar;
46+
}
47+
48+
@Override
49+
protected View getRootView(final LayoutInflater inflater,
50+
final ViewGroup container) {
51+
binding = SearchFilterDialogFragmentBinding
52+
.inflate(inflater, container, false);
53+
return binding.getRoot();
54+
}
55+
}
Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
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.content.Context;
6+
import android.view.Gravity;
7+
import android.view.View;
8+
import android.view.ViewGroup;
9+
import android.widget.AdapterView;
10+
import android.widget.GridLayout;
11+
import android.widget.Spinner;
12+
import android.widget.TextView;
13+
14+
import com.google.android.material.chip.Chip;
15+
import com.google.android.material.chip.ChipGroup;
16+
17+
import org.schabi.newpipe.R;
18+
import org.schabi.newpipe.extractor.StreamingService;
19+
import org.schabi.newpipe.extractor.search.filter.FilterGroup;
20+
import org.schabi.newpipe.extractor.search.filter.FilterItem;
21+
import org.schabi.newpipe.util.DeviceUtils;
22+
import org.schabi.newpipe.util.ServiceHelper;
23+
24+
import java.util.HashMap;
25+
import java.util.List;
26+
import java.util.Map;
27+
28+
import androidx.appcompat.view.ContextThemeWrapper;
29+
30+
public class SearchFilterDialogGenerator extends BaseSearchFilterUiDialogGenerator {
31+
private final GridLayout globalLayout;
32+
private final Map<Spinner, AdapterView.OnItemSelectedListener> spinners = new HashMap<>();
33+
34+
public SearchFilterDialogGenerator(final StreamingService service,
35+
final ViewGroup root,
36+
final Context context,
37+
final SearchFilterLogic.Callback callback) {
38+
super(service.getSearchQHFactory(), callback, context);
39+
this.globalLayout = createGridLayout();
40+
root.addView(globalLayout);
41+
}
42+
43+
@Override
44+
public void onResume() {
45+
for (final Map.Entry<Spinner, AdapterView.OnItemSelectedListener> spinner
46+
: spinners.entrySet()) {
47+
spinner.getKey().setOnItemSelectedListener(spinner.getValue());
48+
}
49+
super.onResume();
50+
}
51+
52+
@Override
53+
public void onPause() {
54+
for (final Map.Entry<Spinner, AdapterView.OnItemSelectedListener> spinner
55+
: spinners.entrySet()) {
56+
spinner.getKey().setOnItemSelectedListener(null);
57+
}
58+
super.onPause();
59+
}
60+
61+
@Override
62+
protected void createTitle(final String name,
63+
final List<View> titleViewElements) {
64+
final TextView titleView = createTitleText(name);
65+
final View separatorLine = createSeparatorLine();
66+
final View separatorLine2 = createSeparatorLine();
67+
68+
globalLayout.addView(separatorLine);
69+
globalLayout.addView(titleView);
70+
globalLayout.addView(separatorLine2);
71+
72+
titleViewElements.add(titleView);
73+
titleViewElements.add(separatorLine);
74+
titleViewElements.add(separatorLine2);
75+
}
76+
77+
@Override
78+
protected void createFilterGroup(final FilterGroup filterGroup,
79+
final UiWrapperMapDelegate wrapperDelegate,
80+
final UiSelectorDelegate selectorDelegate) {
81+
final GridLayout.LayoutParams layoutParams = getLayoutParamsViews();
82+
boolean doSpanDataOverMultipleCells = false;
83+
final UiItemWrapperViews viewsWrapper = new UiItemWrapperViews(
84+
filterGroup.getIdentifier());
85+
86+
if (filterGroup.getName() != null) {
87+
final TextView filterLabel = new TextView(context);
88+
89+
filterLabel.setId(filterGroup.getIdentifier());
90+
filterLabel.setText(
91+
ServiceHelper.getTranslatedFilterString(filterGroup.getName(), context));
92+
filterLabel.setGravity(Gravity.CENTER_VERTICAL);
93+
setDefaultMargin(layoutParams);
94+
setZeroPadding(filterLabel);
95+
96+
filterLabel.setLayoutParams(layoutParams);
97+
globalLayout.addView(filterLabel);
98+
viewsWrapper.add(filterLabel);
99+
} else {
100+
doSpanDataOverMultipleCells = true;
101+
}
102+
103+
if (filterGroup.isOnlyOneCheckable()) {
104+
105+
final Spinner filterDataSpinner = new Spinner(context, Spinner.MODE_DROPDOWN);
106+
107+
final GridLayout.LayoutParams spinnerLp =
108+
clipFreeRightColumnLayoutParams(doSpanDataOverMultipleCells);
109+
setDefaultMargin(spinnerLp);
110+
filterDataSpinner.setLayoutParams(spinnerLp);
111+
setZeroPadding(filterDataSpinner);
112+
113+
createUiElementsForSingleSelectableItemsFilterGroup(
114+
filterGroup, wrapperDelegate, selectorDelegate, filterDataSpinner);
115+
116+
viewsWrapper.add(filterDataSpinner);
117+
globalLayout.addView(filterDataSpinner);
118+
119+
} else { // multiple items in FilterGroup selectable
120+
final ChipGroup chipGroup = new ChipGroup(context);
121+
viewsWrapper.add(chipGroup);
122+
globalLayout.addView(chipGroup);
123+
chipGroup.setLayoutParams(
124+
clipFreeRightColumnLayoutParams(doSpanDataOverMultipleCells));
125+
chipGroup.setSingleLine(false);
126+
127+
createUiElementsForMultipleSelectableItemsFilterGroup(
128+
filterGroup, wrapperDelegate, selectorDelegate, chipGroup);
129+
}
130+
131+
wrapperDelegate.put(filterGroup.getIdentifier(), viewsWrapper);
132+
}
133+
134+
private void createUiElementsForSingleSelectableItemsFilterGroup(
135+
final FilterGroup filterGroup,
136+
final UiWrapperMapDelegate wrapperDelegate,
137+
final UiSelectorDelegate selectorDelegate,
138+
final Spinner filterDataSpinner) {
139+
filterDataSpinner.setAdapter(new SearchFilterDialogSpinnerAdapter(
140+
context, filterGroup, wrapperDelegate, filterDataSpinner));
141+
142+
final AdapterView.OnItemSelectedListener listener;
143+
listener = new AdapterView.OnItemSelectedListener() {
144+
@Override
145+
public void onItemSelected(final AdapterView<?> parent, final View view,
146+
final int position, final long id) {
147+
if (view != null) {
148+
selectorDelegate.selectFilter(view.getId());
149+
}
150+
}
151+
152+
@Override
153+
public void onNothingSelected(final AdapterView<?> parent) {
154+
// we are only interested onItemSelected() -> no implementation here
155+
}
156+
};
157+
158+
filterDataSpinner.setOnItemSelectedListener(listener);
159+
spinners.put(filterDataSpinner, listener);
160+
}
161+
162+
private void createUiElementsForMultipleSelectableItemsFilterGroup(
163+
final FilterGroup filterGroup,
164+
final UiWrapperMapDelegate wrapperDelegate,
165+
final UiSelectorDelegate selectorDelegate,
166+
final ChipGroup chipGroup) {
167+
for (final FilterItem item : filterGroup.getFilterItems()) {
168+
final Chip chip = new Chip(new ContextThemeWrapper(
169+
context, R.style.Theme_MaterialComponents_Light));
170+
chip.setText(ServiceHelper.getTranslatedFilterString(item.getName(), context));
171+
chip.setId(item.getIdentifier());
172+
chip.setCheckable(true);
173+
final View.OnClickListener listener;
174+
listener = view -> selectorDelegate.selectFilter(view.getId());
175+
176+
chip.setOnClickListener(listener);
177+
chipGroup.addView(chip);
178+
viewListeners.put(chip, listener);
179+
wrapperDelegate.put(item.getIdentifier(), new UiItemWrapperChip(
180+
item, chip, chipGroup));
181+
}
182+
}
183+
184+
private View createSeparatorLine() {
185+
return createSeparatorLine(clipFreeRightColumnLayoutParams(true));
186+
}
187+
188+
private TextView createTitleText(final String name) {
189+
final TextView title = createTitleText(name,
190+
clipFreeRightColumnLayoutParams(true));
191+
title.setGravity(Gravity.CENTER);
192+
return title;
193+
}
194+
195+
private GridLayout createGridLayout() {
196+
final GridLayout layout = new GridLayout(context);
197+
198+
layout.setColumnCount(2);
199+
200+
final GridLayout.LayoutParams layoutParams = new GridLayout.LayoutParams();
201+
layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
202+
layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
203+
setDefaultMargin(layoutParams);
204+
layout.setLayoutParams(layoutParams);
205+
206+
return layout;
207+
}
208+
209+
private GridLayout.LayoutParams clipFreeRightColumnLayoutParams(final boolean doColumnSpan) {
210+
final GridLayout.LayoutParams layoutParams = new GridLayout.LayoutParams();
211+
// https://stackoverflow.com/questions/37744672/gridlayout-children-are-being-clipped
212+
layoutParams.width = 0;
213+
layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
214+
layoutParams.setGravity(Gravity.FILL_HORIZONTAL | Gravity.CENTER_VERTICAL);
215+
setDefaultMargin(layoutParams);
216+
217+
if (doColumnSpan) {
218+
layoutParams.columnSpec = GridLayout.spec(0, 2, 1.0f);
219+
}
220+
221+
return layoutParams;
222+
}
223+
224+
private GridLayout.LayoutParams getLayoutParamsViews() {
225+
final GridLayout.LayoutParams layoutParams = new GridLayout.LayoutParams();
226+
layoutParams.setGravity(Gravity.TOP);
227+
setDefaultMargin(layoutParams);
228+
return layoutParams;
229+
}
230+
231+
private GridLayout.LayoutParams setDefaultMargin(final GridLayout.LayoutParams layoutParams) {
232+
layoutParams.setMargins(
233+
DeviceUtils.dpToPx(4, context),
234+
DeviceUtils.dpToPx(2, context),
235+
DeviceUtils.dpToPx(4, context),
236+
DeviceUtils.dpToPx(2, context)
237+
);
238+
return layoutParams;
239+
}
240+
241+
private View setZeroPadding(final View view) {
242+
view.setPadding(0, 0, 0, 0);
243+
return view;
244+
}
245+
246+
public static class UiItemWrapperChip extends BaseUiItemWrapper {
247+
248+
private final ChipGroup chipGroup;
249+
250+
public UiItemWrapperChip(final FilterItem item,
251+
final View view,
252+
final ChipGroup chipGroup) {
253+
super(item, view);
254+
this.chipGroup = chipGroup;
255+
}
256+
257+
@Override
258+
public boolean isChecked() {
259+
return ((Chip) view).isChecked();
260+
}
261+
262+
@Override
263+
public void setChecked(final boolean checked) {
264+
view.setSelected(checked);
265+
((Chip) view).setChecked(checked);
266+
267+
if (checked) {
268+
chipGroup.check(view.getId());
269+
}
270+
}
271+
}
272+
}

0 commit comments

Comments
 (0)