mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-01-10 09:20:31 +00:00
searchfilters: common base classes for DialogFragment based UI's
This commit is contained in:
parent
651a333393
commit
3c038aaf7c
@ -0,0 +1,123 @@
|
|||||||
|
// Created by evermind-zz 2022, licensed GNU GPL version 3 or later
|
||||||
|
|
||||||
|
package org.schabi.newpipe.fragments.list.search.filter;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.extractor.search.filter.FilterGroup;
|
||||||
|
import org.schabi.newpipe.extractor.search.filter.FilterItem;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import static org.schabi.newpipe.fragments.list.search.filter.SearchFilterLogic.ICreateUiForFiltersWorker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common base for the {@link SearchFilterDialogGenerator} and
|
||||||
|
* {@link SearchFilterOptionMenuAlikeDialogGenerator}'s
|
||||||
|
* {@link ICreateUiForFiltersWorker} implementation.
|
||||||
|
*/
|
||||||
|
public abstract class BaseCreateSearchFilterUI
|
||||||
|
implements ICreateUiForFiltersWorker {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
protected final BaseSearchFilterUiDialogGenerator dialogGenBase;
|
||||||
|
@NonNull
|
||||||
|
protected final Context context;
|
||||||
|
protected final List<View> titleViewElements = new ArrayList<>();
|
||||||
|
protected final SearchFilterLogic logic;
|
||||||
|
protected int titleResId;
|
||||||
|
|
||||||
|
protected BaseCreateSearchFilterUI(
|
||||||
|
@NonNull final BaseSearchFilterUiDialogGenerator dialogGenBase,
|
||||||
|
@NonNull final SearchFilterLogic logic,
|
||||||
|
@NonNull final Context context,
|
||||||
|
final int titleResId) {
|
||||||
|
this.dialogGenBase = dialogGenBase;
|
||||||
|
this.logic = logic;
|
||||||
|
this.context = context;
|
||||||
|
this.titleResId = titleResId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createFilterItem(@NonNull final FilterItem filterItem,
|
||||||
|
@NonNull final FilterGroup filterGroup) {
|
||||||
|
// no implementation here all creation stuff is done in createFilterGroupBeforeItems
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createFilterGroupAfterItems(@NonNull final FilterGroup filterGroup) {
|
||||||
|
// no implementation here all creation stuff is done in createFilterGroupBeforeItems
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finish() {
|
||||||
|
// no implementation here all creation stuff is done in createFilterGroupBeforeItems
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used to control the visibility of the title 'sort filter' if the
|
||||||
|
* chosen content filter has no sort filters.
|
||||||
|
*
|
||||||
|
* @param areFiltersVisible true if filter visible
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void filtersVisible(final boolean areFiltersVisible) {
|
||||||
|
final int visibility = areFiltersVisible ? View.VISIBLE : View.GONE;
|
||||||
|
for (final View view : titleViewElements) {
|
||||||
|
if (view != null) {
|
||||||
|
view.setVisibility(visibility);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CreateContentFilterUI extends CreateSortFilterUI {
|
||||||
|
|
||||||
|
public CreateContentFilterUI(
|
||||||
|
@NonNull final BaseSearchFilterUiDialogGenerator dialogGenBase,
|
||||||
|
@NonNull final Context context,
|
||||||
|
@NonNull final SearchFilterLogic logic) {
|
||||||
|
super(dialogGenBase, context, logic);
|
||||||
|
this.titleResId = R.string.filter_search_content_filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createFilterGroupBeforeItems(
|
||||||
|
@NonNull final FilterGroup filterGroup) {
|
||||||
|
dialogGenBase.createFilterGroup(filterGroup,
|
||||||
|
logic::addContentFilterUiWrapperToItemMap,
|
||||||
|
logic::selectContentFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void filtersVisible(final boolean areFiltersVisible) {
|
||||||
|
// no implementation here. As content filters have to be always visible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CreateSortFilterUI extends BaseCreateSearchFilterUI {
|
||||||
|
|
||||||
|
public CreateSortFilterUI(
|
||||||
|
@NonNull final BaseSearchFilterUiDialogGenerator dialogGenBase,
|
||||||
|
@NonNull final Context context,
|
||||||
|
@NonNull final SearchFilterLogic logic) {
|
||||||
|
super(dialogGenBase, logic, context, R.string.filter_search_sort_filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void prepare() {
|
||||||
|
dialogGenBase.createTitle(context.getString(titleResId), titleViewElements);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createFilterGroupBeforeItems(@NonNull final FilterGroup filterGroup) {
|
||||||
|
dialogGenBase.createFilterGroup(filterGroup,
|
||||||
|
logic::addSortFilterUiWrapperToItemMap,
|
||||||
|
logic::selectSortFilter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
// Created by evermind-zz 2022, licensed GNU GPL version 3 or later
|
||||||
|
|
||||||
|
package org.schabi.newpipe.fragments.list.search.filter;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.extractor.search.filter.FilterItem;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
public abstract class BaseItemWrapper implements SearchFilterLogic.IUiItemWrapper {
|
||||||
|
@NonNull
|
||||||
|
protected final FilterItem item;
|
||||||
|
|
||||||
|
protected BaseItemWrapper(@NonNull final FilterItem item) {
|
||||||
|
this.item = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemId() {
|
||||||
|
return item.getIdentifier();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,116 @@
|
|||||||
|
// Created by evermind-zz 2022, licensed GNU GPL version 3 or later
|
||||||
|
|
||||||
|
package org.schabi.newpipe.fragments.list.search.filter;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.fragments.list.search.SearchViewModel;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base dialog class for {@link DialogFragment} based search filter dialogs.
|
||||||
|
*/
|
||||||
|
public abstract class BaseSearchFilterDialogFragment extends DialogFragment {
|
||||||
|
|
||||||
|
protected BaseSearchFilterUiGenerator dialogGenerator;
|
||||||
|
protected SearchViewModel searchViewModel;
|
||||||
|
|
||||||
|
private void createSearchFilterUi() {
|
||||||
|
dialogGenerator = createSearchFilterDialogGenerator();
|
||||||
|
dialogGenerator.createSearchUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void show(@NonNull final FragmentManager manager, @Nullable final String tag) {
|
||||||
|
// Avoid multiple instances of the dialog that could be triggered by multiple taps
|
||||||
|
if (manager.findFragmentByTag(tag) == null) {
|
||||||
|
super.show(manager, tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract BaseSearchFilterUiGenerator createSearchFilterDialogGenerator();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As we have different bindings we need to get this sorted in a method.
|
||||||
|
*
|
||||||
|
* @return the {@link Toolbar} null if there is no toolbar available.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
protected abstract Toolbar getToolbar();
|
||||||
|
|
||||||
|
protected abstract View getRootView(@NonNull LayoutInflater inflater,
|
||||||
|
@Nullable ViewGroup container);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// Make sure that the first parameter is pointing to instance of SearchFragment otherwise
|
||||||
|
// another SearchViewModel object will be created instead of the existing one used.
|
||||||
|
// -> the SearchViewModel is first instantiated in SearchFragment. Here we just use it.
|
||||||
|
searchViewModel =
|
||||||
|
new ViewModelProvider(requireParentFragment()).get(SearchViewModel.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull final LayoutInflater inflater,
|
||||||
|
@Nullable final ViewGroup container,
|
||||||
|
final Bundle savedInstanceState) {
|
||||||
|
final View rootView = getRootView(inflater, container);
|
||||||
|
createSearchFilterUi();
|
||||||
|
return rootView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull final View view, final Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
final Toolbar toolbar = getToolbar();
|
||||||
|
if (toolbar != null) {
|
||||||
|
initToolbar(toolbar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the toolbar.
|
||||||
|
* <p>
|
||||||
|
* This method is only called if {@link #getToolbar()} is implemented to return a toolbar.
|
||||||
|
*
|
||||||
|
* @param toolbar the actual toolbar for this dialog fragment
|
||||||
|
*/
|
||||||
|
protected void initToolbar(@NonNull final Toolbar toolbar) {
|
||||||
|
toolbar.setTitle(R.string.filter);
|
||||||
|
toolbar.setNavigationIcon(R.drawable.ic_arrow_back);
|
||||||
|
toolbar.inflateMenu(R.menu.menu_search_filter_dialog_fragment);
|
||||||
|
toolbar.setNavigationOnClickListener(v -> dismiss());
|
||||||
|
toolbar.setNavigationContentDescription(R.string.cancel);
|
||||||
|
|
||||||
|
final View okButton = toolbar.findViewById(R.id.search);
|
||||||
|
okButton.setEnabled(true);
|
||||||
|
|
||||||
|
final View resetButton = toolbar.findViewById(R.id.reset);
|
||||||
|
resetButton.setEnabled(true);
|
||||||
|
|
||||||
|
toolbar.setOnMenuItemClickListener(item -> {
|
||||||
|
if (item.getItemId() == R.id.search) {
|
||||||
|
searchViewModel.getSearchFilterLogic().prepareForSearch();
|
||||||
|
dismiss();
|
||||||
|
return true;
|
||||||
|
} else if (item.getItemId() == R.id.reset) {
|
||||||
|
searchViewModel.getSearchFilterLogic().reset();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
// Created by evermind-zz 2022, licensed GNU GPL version 3 or later
|
||||||
|
|
||||||
|
package org.schabi.newpipe.fragments.list.search.filter;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.extractor.search.filter.FilterGroup;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
|
||||||
|
import static org.schabi.newpipe.fragments.list.search.filter.SearchFilterLogic.ICreateUiForFiltersWorker;
|
||||||
|
|
||||||
|
public abstract class BaseSearchFilterUiDialogGenerator extends BaseSearchFilterUiGenerator {
|
||||||
|
private static final float FONT_SIZE_TITLE_ITEMS_IN_DIP = 20f;
|
||||||
|
|
||||||
|
protected BaseSearchFilterUiDialogGenerator(
|
||||||
|
@NonNull final SearchFilterLogic logic,
|
||||||
|
@NonNull final Context context) {
|
||||||
|
super(logic, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void createTitle(@NonNull String name,
|
||||||
|
@NonNull List<View> titleViewElements);
|
||||||
|
|
||||||
|
protected abstract void createFilterGroup(@NonNull FilterGroup filterGroup,
|
||||||
|
@NonNull UiWrapperMapDelegate wrapperDelegate,
|
||||||
|
@NonNull UiSelectorDelegate selectorDelegate);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ICreateUiForFiltersWorker createContentFilterWorker() {
|
||||||
|
return new BaseCreateSearchFilterUI.CreateContentFilterUI(this, context, logic);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ICreateUiForFiltersWorker createSortFilterWorker() {
|
||||||
|
return new BaseCreateSearchFilterUI.CreateSortFilterUI(this, context, logic);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a View that acts as a separator between two other {@link View}-Elements.
|
||||||
|
*
|
||||||
|
* @param layoutParams this layout will be modified to have the height of 1 -> to have a
|
||||||
|
* the actual separator line.
|
||||||
|
* @return the created {@link SeparatorLineView}
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
protected SeparatorLineView createSeparatorLine(
|
||||||
|
@NonNull final ViewGroup.LayoutParams layoutParams) {
|
||||||
|
final SeparatorLineView separatorLine = new SeparatorLineView(context);
|
||||||
|
separatorLine.setBackgroundColor(getSeparatorLineColorFromTheme());
|
||||||
|
layoutParams.height = 1; // always set the separator to the height of 1
|
||||||
|
separatorLine.setLayoutParams(layoutParams);
|
||||||
|
return separatorLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
protected TextView createTitleText(@NonNull final String name,
|
||||||
|
@NonNull final ViewGroup.LayoutParams layoutParams) {
|
||||||
|
final TextView title = new TextView(context);
|
||||||
|
title.setText(name);
|
||||||
|
title.setTextSize(COMPLEX_UNIT_DIP, FONT_SIZE_TITLE_ITEMS_IN_DIP);
|
||||||
|
title.setLayoutParams(layoutParams);
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A special view to separate two other {@link View}s.
|
||||||
|
* <p>
|
||||||
|
* class only needed to distinct this special view from other View based views.
|
||||||
|
* (eg. instanceof)
|
||||||
|
*/
|
||||||
|
protected static final class SeparatorLineView extends View {
|
||||||
|
|
||||||
|
private SeparatorLineView(@NonNull final Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
// Created by evermind-zz 2022, licensed GNU GPL version 3 or later
|
||||||
|
|
||||||
|
package org.schabi.newpipe.fragments.list.search.filter;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.extractor.search.filter.FilterItem;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
public abstract class BaseUiItemWrapper extends BaseItemWrapper {
|
||||||
|
@NonNull
|
||||||
|
protected final View view;
|
||||||
|
|
||||||
|
protected BaseUiItemWrapper(@NonNull final FilterItem item,
|
||||||
|
@NonNull final View view) {
|
||||||
|
super(item);
|
||||||
|
this.view = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setVisible(final boolean visible) {
|
||||||
|
if (visible) {
|
||||||
|
view.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
view.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
// Created by evermind-zz 2022, licensed GNU GPL version 3 or later
|
||||||
|
|
||||||
|
package org.schabi.newpipe.fragments.list.search.filter;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for views that are either just labels or eg. a RadioGroup container
|
||||||
|
* etc. that represent a {@link org.schabi.newpipe.extractor.search.filter.FilterGroup}.
|
||||||
|
*/
|
||||||
|
final class UiItemWrapperViews implements SearchFilterLogic.IUiItemWrapper {
|
||||||
|
|
||||||
|
private final int itemId;
|
||||||
|
private final List<View> views = new ArrayList<>();
|
||||||
|
|
||||||
|
UiItemWrapperViews(final int itemId) {
|
||||||
|
this.itemId = itemId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(@NonNull final View view) {
|
||||||
|
this.views.add(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setVisible(final boolean visible) {
|
||||||
|
for (final View view : views) {
|
||||||
|
if (visible) {
|
||||||
|
view.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
view.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemId() {
|
||||||
|
return this.itemId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isChecked() {
|
||||||
|
boolean isChecked = false;
|
||||||
|
for (final View view : views) {
|
||||||
|
if (view.isSelected()) {
|
||||||
|
isChecked = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isChecked;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setChecked(final boolean checked) {
|
||||||
|
// not relevant as here views are wrapped that are either just labels or eg. a
|
||||||
|
// RadioGroup container etc. that represent a FilterGroup.
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user