Skip to content

Commit 3c038aa

Browse files
committed
searchfilters: common base classes for DialogFragment based UI's
1 parent 651a333 commit 3c038aa

6 files changed

Lines changed: 435 additions & 0 deletions

File tree

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
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.View;
7+
8+
import org.schabi.newpipe.R;
9+
import org.schabi.newpipe.extractor.search.filter.FilterGroup;
10+
import org.schabi.newpipe.extractor.search.filter.FilterItem;
11+
12+
import java.util.ArrayList;
13+
import java.util.List;
14+
15+
import androidx.annotation.NonNull;
16+
17+
import static org.schabi.newpipe.fragments.list.search.filter.SearchFilterLogic.ICreateUiForFiltersWorker;
18+
19+
/**
20+
* Common base for the {@link SearchFilterDialogGenerator} and
21+
* {@link SearchFilterOptionMenuAlikeDialogGenerator}'s
22+
* {@link ICreateUiForFiltersWorker} implementation.
23+
*/
24+
public abstract class BaseCreateSearchFilterUI
25+
implements ICreateUiForFiltersWorker {
26+
27+
@NonNull
28+
protected final BaseSearchFilterUiDialogGenerator dialogGenBase;
29+
@NonNull
30+
protected final Context context;
31+
protected final List<View> titleViewElements = new ArrayList<>();
32+
protected final SearchFilterLogic logic;
33+
protected int titleResId;
34+
35+
protected BaseCreateSearchFilterUI(
36+
@NonNull final BaseSearchFilterUiDialogGenerator dialogGenBase,
37+
@NonNull final SearchFilterLogic logic,
38+
@NonNull final Context context,
39+
final int titleResId) {
40+
this.dialogGenBase = dialogGenBase;
41+
this.logic = logic;
42+
this.context = context;
43+
this.titleResId = titleResId;
44+
}
45+
46+
@Override
47+
public void createFilterItem(@NonNull final FilterItem filterItem,
48+
@NonNull final FilterGroup filterGroup) {
49+
// no implementation here all creation stuff is done in createFilterGroupBeforeItems
50+
}
51+
52+
@Override
53+
public void createFilterGroupAfterItems(@NonNull final FilterGroup filterGroup) {
54+
// no implementation here all creation stuff is done in createFilterGroupBeforeItems
55+
}
56+
57+
@Override
58+
public void finish() {
59+
// no implementation here all creation stuff is done in createFilterGroupBeforeItems
60+
}
61+
62+
/**
63+
* This method is used to control the visibility of the title 'sort filter' if the
64+
* chosen content filter has no sort filters.
65+
*
66+
* @param areFiltersVisible true if filter visible
67+
*/
68+
@Override
69+
public void filtersVisible(final boolean areFiltersVisible) {
70+
final int visibility = areFiltersVisible ? View.VISIBLE : View.GONE;
71+
for (final View view : titleViewElements) {
72+
if (view != null) {
73+
view.setVisibility(visibility);
74+
}
75+
}
76+
}
77+
78+
public static class CreateContentFilterUI extends CreateSortFilterUI {
79+
80+
public CreateContentFilterUI(
81+
@NonNull final BaseSearchFilterUiDialogGenerator dialogGenBase,
82+
@NonNull final Context context,
83+
@NonNull final SearchFilterLogic logic) {
84+
super(dialogGenBase, context, logic);
85+
this.titleResId = R.string.filter_search_content_filters;
86+
}
87+
88+
@Override
89+
public void createFilterGroupBeforeItems(
90+
@NonNull final FilterGroup filterGroup) {
91+
dialogGenBase.createFilterGroup(filterGroup,
92+
logic::addContentFilterUiWrapperToItemMap,
93+
logic::selectContentFilter);
94+
}
95+
96+
@Override
97+
public void filtersVisible(final boolean areFiltersVisible) {
98+
// no implementation here. As content filters have to be always visible
99+
}
100+
}
101+
102+
public static class CreateSortFilterUI extends BaseCreateSearchFilterUI {
103+
104+
public CreateSortFilterUI(
105+
@NonNull final BaseSearchFilterUiDialogGenerator dialogGenBase,
106+
@NonNull final Context context,
107+
@NonNull final SearchFilterLogic logic) {
108+
super(dialogGenBase, logic, context, R.string.filter_search_sort_filters);
109+
}
110+
111+
@Override
112+
public void prepare() {
113+
dialogGenBase.createTitle(context.getString(titleResId), titleViewElements);
114+
}
115+
116+
@Override
117+
public void createFilterGroupBeforeItems(@NonNull final FilterGroup filterGroup) {
118+
dialogGenBase.createFilterGroup(filterGroup,
119+
logic::addSortFilterUiWrapperToItemMap,
120+
logic::selectSortFilter);
121+
}
122+
}
123+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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 org.schabi.newpipe.extractor.search.filter.FilterItem;
6+
7+
import androidx.annotation.NonNull;
8+
9+
public abstract class BaseItemWrapper implements SearchFilterLogic.IUiItemWrapper {
10+
@NonNull
11+
protected final FilterItem item;
12+
13+
protected BaseItemWrapper(@NonNull final FilterItem item) {
14+
this.item = item;
15+
}
16+
17+
@Override
18+
public int getItemId() {
19+
return item.getIdentifier();
20+
}
21+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
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.os.Bundle;
6+
import android.view.LayoutInflater;
7+
import android.view.View;
8+
import android.view.ViewGroup;
9+
10+
import org.schabi.newpipe.R;
11+
import org.schabi.newpipe.fragments.list.search.SearchViewModel;
12+
13+
import androidx.annotation.NonNull;
14+
import androidx.annotation.Nullable;
15+
import androidx.appcompat.widget.Toolbar;
16+
import androidx.fragment.app.DialogFragment;
17+
import androidx.fragment.app.FragmentManager;
18+
import androidx.lifecycle.ViewModelProvider;
19+
20+
/**
21+
* Base dialog class for {@link DialogFragment} based search filter dialogs.
22+
*/
23+
public abstract class BaseSearchFilterDialogFragment extends DialogFragment {
24+
25+
protected BaseSearchFilterUiGenerator dialogGenerator;
26+
protected SearchViewModel searchViewModel;
27+
28+
private void createSearchFilterUi() {
29+
dialogGenerator = createSearchFilterDialogGenerator();
30+
dialogGenerator.createSearchUI();
31+
}
32+
33+
@Override
34+
public void show(@NonNull final FragmentManager manager, @Nullable final String tag) {
35+
// Avoid multiple instances of the dialog that could be triggered by multiple taps
36+
if (manager.findFragmentByTag(tag) == null) {
37+
super.show(manager, tag);
38+
}
39+
}
40+
41+
protected abstract BaseSearchFilterUiGenerator createSearchFilterDialogGenerator();
42+
43+
/**
44+
* As we have different bindings we need to get this sorted in a method.
45+
*
46+
* @return the {@link Toolbar} null if there is no toolbar available.
47+
*/
48+
@Nullable
49+
protected abstract Toolbar getToolbar();
50+
51+
protected abstract View getRootView(@NonNull LayoutInflater inflater,
52+
@Nullable ViewGroup container);
53+
54+
@Override
55+
public void onCreate(@Nullable final Bundle savedInstanceState) {
56+
super.onCreate(savedInstanceState);
57+
58+
// Make sure that the first parameter is pointing to instance of SearchFragment otherwise
59+
// another SearchViewModel object will be created instead of the existing one used.
60+
// -> the SearchViewModel is first instantiated in SearchFragment. Here we just use it.
61+
searchViewModel =
62+
new ViewModelProvider(requireParentFragment()).get(SearchViewModel.class);
63+
}
64+
65+
@Override
66+
public View onCreateView(@NonNull final LayoutInflater inflater,
67+
@Nullable final ViewGroup container,
68+
final Bundle savedInstanceState) {
69+
final View rootView = getRootView(inflater, container);
70+
createSearchFilterUi();
71+
return rootView;
72+
}
73+
74+
@Override
75+
public void onViewCreated(@NonNull final View view, final Bundle savedInstanceState) {
76+
super.onViewCreated(view, savedInstanceState);
77+
78+
final Toolbar toolbar = getToolbar();
79+
if (toolbar != null) {
80+
initToolbar(toolbar);
81+
}
82+
}
83+
84+
/**
85+
* Initialize the toolbar.
86+
* <p>
87+
* This method is only called if {@link #getToolbar()} is implemented to return a toolbar.
88+
*
89+
* @param toolbar the actual toolbar for this dialog fragment
90+
*/
91+
protected void initToolbar(@NonNull final Toolbar toolbar) {
92+
toolbar.setTitle(R.string.filter);
93+
toolbar.setNavigationIcon(R.drawable.ic_arrow_back);
94+
toolbar.inflateMenu(R.menu.menu_search_filter_dialog_fragment);
95+
toolbar.setNavigationOnClickListener(v -> dismiss());
96+
toolbar.setNavigationContentDescription(R.string.cancel);
97+
98+
final View okButton = toolbar.findViewById(R.id.search);
99+
okButton.setEnabled(true);
100+
101+
final View resetButton = toolbar.findViewById(R.id.reset);
102+
resetButton.setEnabled(true);
103+
104+
toolbar.setOnMenuItemClickListener(item -> {
105+
if (item.getItemId() == R.id.search) {
106+
searchViewModel.getSearchFilterLogic().prepareForSearch();
107+
dismiss();
108+
return true;
109+
} else if (item.getItemId() == R.id.reset) {
110+
searchViewModel.getSearchFilterLogic().reset();
111+
return true;
112+
}
113+
return false;
114+
});
115+
}
116+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
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.View;
7+
import android.view.ViewGroup;
8+
import android.widget.TextView;
9+
10+
import org.schabi.newpipe.extractor.search.filter.FilterGroup;
11+
12+
import java.util.List;
13+
14+
import androidx.annotation.NonNull;
15+
16+
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
17+
import static org.schabi.newpipe.fragments.list.search.filter.SearchFilterLogic.ICreateUiForFiltersWorker;
18+
19+
public abstract class BaseSearchFilterUiDialogGenerator extends BaseSearchFilterUiGenerator {
20+
private static final float FONT_SIZE_TITLE_ITEMS_IN_DIP = 20f;
21+
22+
protected BaseSearchFilterUiDialogGenerator(
23+
@NonNull final SearchFilterLogic logic,
24+
@NonNull final Context context) {
25+
super(logic, context);
26+
}
27+
28+
protected abstract void createTitle(@NonNull String name,
29+
@NonNull List<View> titleViewElements);
30+
31+
protected abstract void createFilterGroup(@NonNull FilterGroup filterGroup,
32+
@NonNull UiWrapperMapDelegate wrapperDelegate,
33+
@NonNull UiSelectorDelegate selectorDelegate);
34+
35+
@Override
36+
protected ICreateUiForFiltersWorker createContentFilterWorker() {
37+
return new BaseCreateSearchFilterUI.CreateContentFilterUI(this, context, logic);
38+
}
39+
40+
@Override
41+
protected ICreateUiForFiltersWorker createSortFilterWorker() {
42+
return new BaseCreateSearchFilterUI.CreateSortFilterUI(this, context, logic);
43+
}
44+
45+
/**
46+
* Create a View that acts as a separator between two other {@link View}-Elements.
47+
*
48+
* @param layoutParams this layout will be modified to have the height of 1 -> to have a
49+
* the actual separator line.
50+
* @return the created {@link SeparatorLineView}
51+
*/
52+
@NonNull
53+
protected SeparatorLineView createSeparatorLine(
54+
@NonNull final ViewGroup.LayoutParams layoutParams) {
55+
final SeparatorLineView separatorLine = new SeparatorLineView(context);
56+
separatorLine.setBackgroundColor(getSeparatorLineColorFromTheme());
57+
layoutParams.height = 1; // always set the separator to the height of 1
58+
separatorLine.setLayoutParams(layoutParams);
59+
return separatorLine;
60+
}
61+
62+
@NonNull
63+
protected TextView createTitleText(@NonNull final String name,
64+
@NonNull final ViewGroup.LayoutParams layoutParams) {
65+
final TextView title = new TextView(context);
66+
title.setText(name);
67+
title.setTextSize(COMPLEX_UNIT_DIP, FONT_SIZE_TITLE_ITEMS_IN_DIP);
68+
title.setLayoutParams(layoutParams);
69+
return title;
70+
}
71+
72+
/**
73+
* A special view to separate two other {@link View}s.
74+
* <p>
75+
* class only needed to distinct this special view from other View based views.
76+
* (eg. instanceof)
77+
*/
78+
protected static final class SeparatorLineView extends View {
79+
80+
private SeparatorLineView(@NonNull final Context context) {
81+
super(context);
82+
}
83+
}
84+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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.View;
6+
7+
import org.schabi.newpipe.extractor.search.filter.FilterItem;
8+
9+
import androidx.annotation.NonNull;
10+
11+
public abstract class BaseUiItemWrapper extends BaseItemWrapper {
12+
@NonNull
13+
protected final View view;
14+
15+
protected BaseUiItemWrapper(@NonNull final FilterItem item,
16+
@NonNull final View view) {
17+
super(item);
18+
this.view = view;
19+
}
20+
21+
@Override
22+
public void setVisible(final boolean visible) {
23+
if (visible) {
24+
view.setVisibility(View.VISIBLE);
25+
} else {
26+
view.setVisibility(View.GONE);
27+
}
28+
}
29+
}

0 commit comments

Comments
 (0)