mirror of
https://github.com/MaintainTeam/LastPipeBender.git
synced 2025-03-02 22:38:20 +03:00
searchfilters: common base classes for DialogFragment based UI's
This commit is contained in:
parent
5b95094a77
commit
55240193c0
5 changed files with 481 additions and 0 deletions
|
@ -0,0 +1,108 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common base for the {@link SearchFilterDialogGenerator} and
|
||||||
|
* {@link SearchFilterOptionMenuAlikeDialogGenerator}'s
|
||||||
|
* {@link SearchFilterLogic.ICreateUiForFiltersWorker} implementation.
|
||||||
|
*/
|
||||||
|
public abstract class BaseCreateSearchFilterUI
|
||||||
|
implements SearchFilterLogic.ICreateUiForFiltersWorker {
|
||||||
|
|
||||||
|
protected final BaseSearchFilterUiDialogGenerator dialogGenBase;
|
||||||
|
protected final Context context;
|
||||||
|
protected final List<View> titleViewElements = new ArrayList<>();
|
||||||
|
protected int titleResId;
|
||||||
|
|
||||||
|
protected BaseCreateSearchFilterUI(final BaseSearchFilterUiDialogGenerator dialogGenBase,
|
||||||
|
final Context context,
|
||||||
|
final int titleResId) {
|
||||||
|
this.dialogGenBase = dialogGenBase;
|
||||||
|
this.context = context;
|
||||||
|
this.titleResId = titleResId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createFilterItem(final FilterItem filterItem, final FilterGroup filterGroup) {
|
||||||
|
// no implementation here all creation stuff is done in createFilterGroupBeforeItems
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createFilterGroupAfterItems(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(final BaseSearchFilterUiDialogGenerator dialogGenBase,
|
||||||
|
final Context context) {
|
||||||
|
super(dialogGenBase, context);
|
||||||
|
this.titleResId = R.string.filter_search_content_filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createFilterGroupBeforeItems(
|
||||||
|
final FilterGroup filterGroup) {
|
||||||
|
dialogGenBase.createFilterGroup(filterGroup,
|
||||||
|
dialogGenBase::addContentFilterUiWrapperToItemMap,
|
||||||
|
dialogGenBase::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(final BaseSearchFilterUiDialogGenerator dialogGenBase,
|
||||||
|
final Context context) {
|
||||||
|
super(dialogGenBase, context, R.string.filter_search_sort_filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void prepare() {
|
||||||
|
dialogGenBase.createTitle(context.getString(titleResId), titleViewElements);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createFilterGroupBeforeItems(final FilterGroup filterGroup) {
|
||||||
|
dialogGenBase.createFilterGroup(filterGroup,
|
||||||
|
dialogGenBase::addSortFilterUiWrapperToItemMap,
|
||||||
|
dialogGenBase::selectSortFilter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,188 @@
|
||||||
|
// 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.extractor.NewPipe;
|
||||||
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
|
import org.schabi.newpipe.extractor.search.filter.FilterItem;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
import icepick.Icepick;
|
||||||
|
import icepick.State;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base dialog class for {@link DialogFragment} based search filter dialogs.
|
||||||
|
*/
|
||||||
|
public abstract class BaseSearchFilterDialogFragment extends DialogFragment {
|
||||||
|
|
||||||
|
private static final String CONTENT_FILTERS = "CONTENT_FILTERS";
|
||||||
|
private static final String SORT_FILTERS = "SORT_FILTERS";
|
||||||
|
private static final String SERVICE_ID = "SERVICE_ID";
|
||||||
|
|
||||||
|
@State
|
||||||
|
public ArrayList<Integer> userSelectedContentFilterList;
|
||||||
|
protected List<FilterItem> selectedContentFilters;
|
||||||
|
protected List<FilterItem> selectedSortFilters;
|
||||||
|
protected BaseSearchFilterUiGenerator dialogGenerator;
|
||||||
|
@State
|
||||||
|
ArrayList<Integer> userSelectedSortFilterList = null;
|
||||||
|
|
||||||
|
protected static DialogFragment initDialogArguments(
|
||||||
|
final DialogFragment dialogFragment,
|
||||||
|
final int serviceId,
|
||||||
|
final ArrayList<Integer> userSelectedContentFilter,
|
||||||
|
final ArrayList<Integer> userSelectedSortFilter) {
|
||||||
|
final Bundle bundle = new Bundle(1);
|
||||||
|
bundle.putInt(SERVICE_ID, serviceId);
|
||||||
|
bundle.putIntegerArrayList(CONTENT_FILTERS, userSelectedContentFilter);
|
||||||
|
bundle.putIntegerArrayList(SORT_FILTERS, userSelectedSortFilter);
|
||||||
|
dialogFragment.setArguments(bundle);
|
||||||
|
|
||||||
|
return dialogFragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeFilterData() {
|
||||||
|
|
||||||
|
assert getArguments() != null;
|
||||||
|
final int serviceId = getArguments().getInt(SERVICE_ID);
|
||||||
|
final ArrayList<Integer> contentFilters =
|
||||||
|
getArguments().getIntegerArrayList(CONTENT_FILTERS);
|
||||||
|
final ArrayList<Integer> sortFilters =
|
||||||
|
getArguments().getIntegerArrayList(SORT_FILTERS);
|
||||||
|
|
||||||
|
final StreamingService service;
|
||||||
|
try {
|
||||||
|
service = NewPipe.getService(serviceId);
|
||||||
|
} catch (final ExtractionException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
dialogGenerator = createSearchFilterDialogGenerator(service,
|
||||||
|
(userSelectedContentFilter, userSelectedSortFilter) -> {
|
||||||
|
selectedContentFilters = userSelectedContentFilter;
|
||||||
|
selectedSortFilters = userSelectedSortFilter;
|
||||||
|
sendDataToParentFragment();
|
||||||
|
});
|
||||||
|
|
||||||
|
userSelectedContentFilterList = contentFilters;
|
||||||
|
userSelectedSortFilterList = sortFilters;
|
||||||
|
|
||||||
|
dialogGenerator.restorePreviouslySelectedFilters(
|
||||||
|
userSelectedContentFilterList,
|
||||||
|
userSelectedSortFilterList);
|
||||||
|
|
||||||
|
dialogGenerator.createSearchUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract BaseSearchFilterUiGenerator createSearchFilterDialogGenerator(
|
||||||
|
StreamingService service,
|
||||||
|
SearchFilterLogic.Callback callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As we have different bindings we need to get this sorted in a method.
|
||||||
|
*
|
||||||
|
* @return the {@link Toolbar}
|
||||||
|
*/
|
||||||
|
protected abstract Toolbar getToolbar();
|
||||||
|
|
||||||
|
protected abstract View getRootView(LayoutInflater inflater,
|
||||||
|
ViewGroup container);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull final LayoutInflater inflater,
|
||||||
|
final ViewGroup container,
|
||||||
|
final Bundle savedInstanceState) {
|
||||||
|
final View rootView = getRootView(inflater, container);
|
||||||
|
initializeFilterData();
|
||||||
|
return rootView;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
dialogGenerator.onResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
dialogGenerator.onPause();
|
||||||
|
super.onStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull final View view, final Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
Icepick.restoreInstanceState(this, savedInstanceState);
|
||||||
|
|
||||||
|
initToolbar(getToolbar());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void initToolbar(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) {
|
||||||
|
dialogGenerator.prepareForSearch();
|
||||||
|
return true;
|
||||||
|
} else if (item.getItemId() == R.id.reset) {
|
||||||
|
dialogGenerator.reset();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(@NonNull final Bundle outState) {
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
// get data to save its state via Icepick
|
||||||
|
userSelectedContentFilterList = dialogGenerator.getSelectedContentFilters();
|
||||||
|
userSelectedSortFilterList = dialogGenerator.getSelectedSortFilters();
|
||||||
|
|
||||||
|
Icepick.saveInstanceState(this, outState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendDataToParentFragment() {
|
||||||
|
final Listener listener = (Listener) getTargetFragment();
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onFinishSearchFilterDialog(
|
||||||
|
userSelectedContentFilterList, userSelectedSortFilterList,
|
||||||
|
selectedContentFilters, selectedSortFilters);
|
||||||
|
}
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener to be implemented by the parent Fragment so it can receive data.
|
||||||
|
*/
|
||||||
|
public interface Listener {
|
||||||
|
|
||||||
|
void onFinishSearchFilterDialog(ArrayList<Integer> userSelectedContentFilterList,
|
||||||
|
ArrayList<Integer> userSelectedSortFilterList,
|
||||||
|
List<FilterItem> selectedContentFilters,
|
||||||
|
List<FilterItem> selectedSortFilters);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
// 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.linkhandler.SearchQueryHandlerFactory;
|
||||||
|
import org.schabi.newpipe.extractor.search.filter.FilterGroup;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
|
||||||
|
|
||||||
|
public abstract class BaseSearchFilterUiDialogGenerator extends BaseSearchFilterUiGenerator {
|
||||||
|
private static final float FONT_SIZE_TITLE_ITEMS_IN_DIP = 20f;
|
||||||
|
|
||||||
|
protected final Map<View, View.OnClickListener> viewListeners = new HashMap<>();
|
||||||
|
|
||||||
|
protected BaseSearchFilterUiDialogGenerator(
|
||||||
|
final SearchQueryHandlerFactory linkHandlerFactory,
|
||||||
|
final Callback callback,
|
||||||
|
final Context context) {
|
||||||
|
super(linkHandlerFactory, callback, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void createTitle(String name, List<View> titleViewElements);
|
||||||
|
|
||||||
|
protected abstract void createFilterGroup(FilterGroup filterGroup,
|
||||||
|
UiWrapperMapDelegate wrapperDelegate,
|
||||||
|
UiSelectorDelegate selectorDelegate);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ICreateUiForFiltersWorker createContentFilterWorker() {
|
||||||
|
return new BaseCreateSearchFilterUI.CreateContentFilterUI(this, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ICreateUiForFiltersWorker createSortFilterWorker() {
|
||||||
|
return new BaseCreateSearchFilterUI.CreateSortFilterUI(this, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
for (final Map.Entry<View, View.OnClickListener> view : viewListeners.entrySet()) {
|
||||||
|
view.getKey().setOnClickListener(view.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
for (final Map.Entry<View, View.OnClickListener> view : viewListeners.entrySet()) {
|
||||||
|
view.getKey().setOnClickListener(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected View createSeparatorLine(final ViewGroup.LayoutParams layoutParams) {
|
||||||
|
final View separatorLine = new View(context);
|
||||||
|
separatorLine.setBackgroundColor(getSeparatorLineColorFromTheme());
|
||||||
|
layoutParams.height = 1; // always set the separator to 1
|
||||||
|
separatorLine.setLayoutParams(layoutParams);
|
||||||
|
return separatorLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected TextView createTitleText(final String name,
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
public abstract class BaseUiItemWrapper implements SearchFilterLogic.IUiItemWrapper {
|
||||||
|
protected final FilterItem item;
|
||||||
|
protected final int groupId;
|
||||||
|
protected View view;
|
||||||
|
|
||||||
|
protected BaseUiItemWrapper(final FilterItem item,
|
||||||
|
final int groupId,
|
||||||
|
final View view) {
|
||||||
|
this.item = item;
|
||||||
|
this.groupId = groupId;
|
||||||
|
this.view = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setVisible(final boolean visible) {
|
||||||
|
if (visible) {
|
||||||
|
view.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
view.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemId() {
|
||||||
|
return item.getIdentifier();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getGroupId() {
|
||||||
|
return this.groupId;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 int groupId;
|
||||||
|
private final List<View> views = new ArrayList<>();
|
||||||
|
|
||||||
|
UiItemWrapperViews(final int itemId, final int groupId) {
|
||||||
|
this.itemId = itemId;
|
||||||
|
this.groupId = groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(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 int getGroupId() {
|
||||||
|
return this.groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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…
Add table
Reference in a new issue