diff --git a/Clover/app/src/main/java/org/floens/chan/core/manager/FilterEngine.java b/Clover/app/src/main/java/org/floens/chan/core/manager/FilterEngine.java index 1172faa5..8735aac4 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/manager/FilterEngine.java +++ b/Clover/app/src/main/java/org/floens/chan/core/manager/FilterEngine.java @@ -43,35 +43,6 @@ public class FilterEngine { return instance; } - public enum FilterType { - TRIPCODE(0, false), - NAME(1, false), - COMMENT(2, true), - ID(3, false), - SUBJECT(4, true), - FILENAME(5, true); - - public final int id; - public final boolean isRegex; - - FilterType(int id, boolean isRegex) { - this.id = id; - this.isRegex = isRegex; - } - - public static FilterType forId(int id) { - return enums[id]; - } - - private static FilterType[] enums = new FilterType[6]; - - static { - for (FilterType type : values()) { - enums[type.id] = type; - } - } - } - public enum FilterAction { HIDE(0), COLOR(1), @@ -128,37 +99,40 @@ public class FilterEngine { // threadsafe public boolean matches(Filter filter, Post post) { - // Post has not been finish()ed yet, account for invalid values - String text = null; - FilterType type = FilterType.forId(filter.type); - switch (type) { - case TRIPCODE: - text = post.tripcode; - break; - case NAME: - text = post.name; - break; - case COMMENT: - text = post.rawComment; - break; - case ID: - text = post.id; - break; - case SUBJECT: - text = post.subject; - break; - case FILENAME: - text = post.filename; - break; + if ((filter.type & FilterType.TRIPCODE.flag) != 0 && matches(filter, FilterType.TRIPCODE.isRegex, post.tripcode, false)) { + return true; } - return !TextUtils.isEmpty(text) && matches(filter, text, false); + if ((filter.type & FilterType.NAME.flag) != 0 && matches(filter, FilterType.NAME.isRegex, post.name, false)) { + return true; + } + + if ((filter.type & FilterType.COMMENT.flag) != 0 && matches(filter, FilterType.COMMENT.isRegex, post.rawComment, false)) { + return true; + } + + if ((filter.type & FilterType.ID.flag) != 0 && matches(filter, FilterType.ID.isRegex, post.id, false)) { + return true; + } + + if ((filter.type & FilterType.SUBJECT.flag) != 0 && matches(filter, FilterType.SUBJECT.isRegex, post.subject, false)) { + return true; + } + + if ((filter.type & FilterType.FILENAME.flag) != 0 && matches(filter, FilterType.FILENAME.isRegex, post.filename, false)) { + return true; + } + + return false; } // threadsafe - public boolean matches(Filter filter, String text, boolean forceCompile) { - FilterType type = FilterType.forId(filter.type); - if (type.isRegex) { + public boolean matches(Filter filter, boolean matchRegex, String text, boolean forceCompile) { + if (TextUtils.isEmpty(text)) { + return false; + } + + if (matchRegex) { Matcher matcher = null; if (!forceCompile) { matcher = filter.compiledMatcher; @@ -195,12 +169,12 @@ public class FilterEngine { // threadsafe public Pattern compile(String rawPattern) { - Pattern pattern; - if (TextUtils.isEmpty(rawPattern)) { return null; } + Pattern pattern; + Matcher isRegex = isRegexPattern.matcher(rawPattern); if (isRegex.matches()) { // This is a /Pattern/ diff --git a/Clover/app/src/main/java/org/floens/chan/core/manager/FilterType.java b/Clover/app/src/main/java/org/floens/chan/core/manager/FilterType.java new file mode 100644 index 00000000..9a1a2be7 --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/core/manager/FilterType.java @@ -0,0 +1,31 @@ +package org.floens.chan.core.manager; + +import java.util.ArrayList; +import java.util.List; + +public enum FilterType { + TRIPCODE(0x1, false), + NAME(0x2, false), + COMMENT(0x4, true), + ID(0x8, false), + SUBJECT(0x10, true), + FILENAME(0x20, true); + + public final int flag; + public final boolean isRegex; + + FilterType(int flag, boolean isRegex) { + this.flag = flag; + this.isRegex = isRegex; + } + + public static List forFlags(int flag) { + List enabledTypes = new ArrayList<>(); + for (FilterType filterType : values()) { + if ((filterType.flag & flag) != 0) { + enabledTypes.add(filterType); + } + } + return enabledTypes; + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/core/model/Filter.java b/Clover/app/src/main/java/org/floens/chan/core/model/Filter.java index 377f32ef..420375a6 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/model/Filter.java +++ b/Clover/app/src/main/java/org/floens/chan/core/model/Filter.java @@ -20,7 +20,7 @@ package org.floens.chan.core.model; import com.j256.ormlite.field.DatabaseField; import com.j256.ormlite.table.DatabaseTable; -import org.floens.chan.core.manager.FilterEngine; +import org.floens.chan.core.manager.FilterType; import java.util.regex.Matcher; @@ -32,8 +32,9 @@ public class Filter { @DatabaseField(canBeNull = false) public boolean enabled = true; + // Flags of FilterTypes that this filter is applied to @DatabaseField(canBeNull = false) - public int type = FilterEngine.FilterType.COMMENT.id; + public int type = FilterType.SUBJECT.flag | FilterType.COMMENT.flag; @DatabaseField(canBeNull = false) public String pattern; @@ -55,6 +56,10 @@ public class Filter { */ public Matcher compiledMatcher; + public boolean hasFilter(FilterType filterType) { + return (type & filterType.flag) != 0; + } + public String[] boardCodes() { return boards.split(","); } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/FiltersController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/FiltersController.java index c4e49b83..f986c62b 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/FiltersController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/FiltersController.java @@ -35,6 +35,7 @@ import org.floens.chan.R; import org.floens.chan.controller.Controller; import org.floens.chan.core.database.DatabaseManager; import org.floens.chan.core.manager.FilterEngine; +import org.floens.chan.core.manager.FilterType; import org.floens.chan.core.model.Filter; import org.floens.chan.ui.helper.RefreshUIMessage; import org.floens.chan.ui.layout.FilterLayout; @@ -66,7 +67,7 @@ public class FiltersController extends Controller implements ToolbarMenuItem.Too super(context); } - public static String filterTypeName(FilterEngine.FilterType type) { + public static String filterTypeName(FilterType type) { switch (type) { case TRIPCODE: return getString(R.string.filter_tripcode); @@ -204,9 +205,10 @@ public class FiltersController extends Controller implements ToolbarMenuItem.Too holder.text.setText(filter.pattern); holder.text.setTextColor(getAttrColor(context, filter.enabled ? R.attr.text_color_primary : R.attr.text_color_hint)); holder.subtext.setTextColor(getAttrColor(context, filter.enabled ? R.attr.text_color_secondary : R.attr.text_color_hint)); - String subText = filterTypeName(FilterEngine.FilterType.forId(filter.type)); + int types = FilterType.forFlags(filter.type).size(); + String subText = context.getResources().getQuantityString(R.plurals.type, types, types); - subText += " - "; + subText += " \u2013 "; if (filter.allBoards) { subText += context.getString(R.string.filter_summary_all_boards); } else { @@ -214,7 +216,7 @@ public class FiltersController extends Controller implements ToolbarMenuItem.Too subText += context.getResources().getQuantityString(R.plurals.board, size, size); } - subText += " - " + FiltersController.actionName(FilterEngine.FilterAction.forId(filter.action)); + subText += " \u2013 " + FiltersController.actionName(FilterEngine.FilterAction.forId(filter.action)); holder.subtext.setText(subText); } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/ThreadController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/ThreadController.java index 6774a925..4ecfbe86 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/ThreadController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/ThreadController.java @@ -30,7 +30,7 @@ import org.floens.chan.Chan; import org.floens.chan.R; import org.floens.chan.chan.ChanUrls; import org.floens.chan.controller.Controller; -import org.floens.chan.core.manager.FilterEngine; +import org.floens.chan.core.manager.FilterType; import org.floens.chan.core.model.Filter; import org.floens.chan.core.model.Loadable; import org.floens.chan.core.model.Pin; @@ -238,7 +238,7 @@ public abstract class ThreadController extends Controller implements ThreadLayou navigationController.pushController(filtersController); } Filter filter = new Filter(); - filter.type = FilterEngine.FilterType.TRIPCODE.id; + filter.type = FilterType.TRIPCODE.flag; filter.pattern = tripcode; filtersController.showFilterDialog(filter); } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/layout/FilterLayout.java b/Clover/app/src/main/java/org/floens/chan/ui/layout/FilterLayout.java index 22e2854d..3ef41d79 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/layout/FilterLayout.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/layout/FilterLayout.java @@ -42,6 +42,7 @@ import org.floens.chan.Chan; import org.floens.chan.R; import org.floens.chan.core.manager.BoardManager; import org.floens.chan.core.manager.FilterEngine; +import org.floens.chan.core.manager.FilterType; import org.floens.chan.core.model.Board; import org.floens.chan.core.model.Filter; import org.floens.chan.ui.controller.FiltersController; @@ -184,30 +185,41 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener { @Override public void onClick(View v) { if (v == typeText) { - List menuItems = new ArrayList<>(6); + @SuppressWarnings("unchecked") + final SelectLayout selectLayout = (SelectLayout) LayoutInflater.from(getContext()).inflate(R.layout.layout_select, null); + + List> items = new ArrayList<>(); + for (FilterType filterType : FilterType.values()) { + String name = FiltersController.filterTypeName(filterType); + String description = getString(filterType.isRegex ? R.string.filter_type_regex_matching : R.string.filter_type_string_matching); + boolean checked = filter.hasFilter(filterType); - for (FilterEngine.FilterType filterType : FilterEngine.FilterType.values()) { - menuItems.add(new FloatingMenuItem(filterType, FiltersController.filterTypeName(filterType))); + items.add(new SelectLayout.SelectItem<>( + filterType, filterType.flag, name, description, name, checked + )); } - FloatingMenu menu = new FloatingMenu(v.getContext()); - menu.setAnchor(v, Gravity.LEFT, -dp(5), -dp(5)); - menu.setPopupWidth(dp(150)); - menu.setCallback(new FloatingMenu.FloatingMenuCallback() { - @Override - public void onFloatingMenuItemClicked(FloatingMenu menu, FloatingMenuItem item) { - FilterEngine.FilterType type = (FilterEngine.FilterType) item.getId(); - filter.type = type.id; - updateFilterType(); - updatePatternPreview(); - } + selectLayout.setItems(items); - @Override - public void onFloatingMenuDismissed(FloatingMenu menu) { - } - }); - menu.setItems(menuItems); - menu.show(); + new AlertDialog.Builder(getContext()) + .setView(selectLayout) + .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + List> items = selectLayout.getItems(); + int flags = 0; + for (SelectLayout.SelectItem item : items) { + if (item.checked) { + flags |= item.item.flag; + } + } + + filter.type = flags; + updateFilterType(); + updatePatternPreview(); + } + }) + .show(); } else if (v == boardsSelector) { @SuppressWarnings("unchecked") final SelectLayout selectLayout = (SelectLayout) LayoutInflater.from(getContext()).inflate(R.layout.layout_select, null); @@ -326,14 +338,7 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener { } private void updateFilterValidity() { - FilterEngine.FilterType filterType = FilterEngine.FilterType.forId(filter.type); - - boolean valid; - if (filterType.isRegex) { - valid = FilterEngine.getInstance().compile(filter.pattern) != null; - } else { - valid = !TextUtils.isEmpty(filter.pattern); - } + boolean valid = !TextUtils.isEmpty(filter.pattern) && FilterEngine.getInstance().compile(filter.pattern) != null; if (valid != patternContainerErrorShowing) { patternContainerErrorShowing = valid; @@ -348,7 +353,7 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener { private void updateBoardsSummary() { String text = getString(R.string.filter_boards) + " ("; if (filter.allBoards) { - text += getString(R.string.filter_boards_all); + text += getString(R.string.filter_all); } else { text += String.valueOf(appliedBoards.size()); } @@ -371,14 +376,14 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener { } private void updateFilterType() { - FilterEngine.FilterType filterType = FilterEngine.FilterType.forId(filter.type); - typeText.setText(FiltersController.filterTypeName(filterType)); - pattern.setHint(filterType.isRegex ? R.string.filter_pattern_hint_regex : R.string.filter_pattern_hint_exact); + int types = FilterType.forFlags(filter.type).size(); + String text = getString(R.string.filter_types) + " (" + types + ")"; + typeText.setText(text); } private void updatePatternPreview() { String text = patternPreview.getText().toString(); - boolean matches = text.length() > 0 && FilterEngine.getInstance().matches(filter, text, true); + boolean matches = text.length() > 0 && FilterEngine.getInstance().matches(filter, true, text, true); patternPreviewStatus.setText(matches ? R.string.filter_matches : R.string.filter_no_matches); } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/layout/SelectLayout.java b/Clover/app/src/main/java/org/floens/chan/ui/layout/SelectLayout.java index 6c0877d6..ecb03616 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/layout/SelectLayout.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/layout/SelectLayout.java @@ -150,7 +150,7 @@ public class SelectLayout extends LinearLayout implements SearchLayout.Search @Override public BoardSelectViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - return new BoardSelectViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.cell_board_select, parent, false)); + return new BoardSelectViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.cell_select, parent, false)); } @Override @@ -158,7 +158,12 @@ public class SelectLayout extends LinearLayout implements SearchLayout.Search SelectItem item = displayList.get(position); holder.checkBox.setChecked(item.checked); holder.text.setText(item.name); - holder.description.setText(item.description); + if (item.description != null) { + holder.description.setVisibility(View.VISIBLE); + holder.description.setText(item.description); + } else { + holder.description.setVisibility(View.GONE); + } } @Override diff --git a/Clover/app/src/main/res/layout/cell_board_select.xml b/Clover/app/src/main/res/layout/cell_select.xml similarity index 100% rename from Clover/app/src/main/res/layout/cell_board_select.xml rename to Clover/app/src/main/res/layout/cell_select.xml diff --git a/Clover/app/src/main/res/layout/layout_filter.xml b/Clover/app/src/main/res/layout/layout_filter.xml index f8cbcec0..9845e2ec 100644 --- a/Clover/app/src/main/res/layout/layout_filter.xml +++ b/Clover/app/src/main/res/layout/layout_filter.xml @@ -138,6 +138,7 @@ along with this program. If not, see . android:id="@+id/pattern" android:layout_width="match_parent" android:layout_height="wrap_content" + android:hint="@string/filter_pattern_hint_regex" android:textSize="14sp" tools:ignore="TextFields" /> diff --git a/Clover/app/src/main/res/values/strings.xml b/Clover/app/src/main/res/values/strings.xml index ff397129..369ff0b6 100644 --- a/Clover/app/src/main/res/values/strings.xml +++ b/Clover/app/src/main/res/values/strings.xml @@ -98,6 +98,11 @@ along with this program. If not, see . %d boards + + %d type + %d types + + %d bookmark %d bookmarks @@ -182,10 +187,12 @@ along with this program. If not, see . Action Pattern Test pattern - Exact text Pattern Boards - all + Types + String matching + Regex matching + all Hide post Highlight post Remove post