Allow setting multiple types on filters

Closes #154
multisite
Floens 9 years ago
parent 9b9ce6575f
commit 8092386c24
  1. 88
      Clover/app/src/main/java/org/floens/chan/core/manager/FilterEngine.java
  2. 31
      Clover/app/src/main/java/org/floens/chan/core/manager/FilterType.java
  3. 9
      Clover/app/src/main/java/org/floens/chan/core/model/Filter.java
  4. 10
      Clover/app/src/main/java/org/floens/chan/ui/controller/FiltersController.java
  5. 4
      Clover/app/src/main/java/org/floens/chan/ui/controller/ThreadController.java
  6. 71
      Clover/app/src/main/java/org/floens/chan/ui/layout/FilterLayout.java
  7. 9
      Clover/app/src/main/java/org/floens/chan/ui/layout/SelectLayout.java
  8. 0
      Clover/app/src/main/res/layout/cell_select.xml
  9. 1
      Clover/app/src/main/res/layout/layout_filter.xml
  10. 11
      Clover/app/src/main/res/values/strings.xml

@ -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/

@ -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<FilterType> forFlags(int flag) {
List<FilterType> enabledTypes = new ArrayList<>();
for (FilterType filterType : values()) {
if ((filterType.flag & flag) != 0) {
enabledTypes.add(filterType);
}
}
return enabledTypes;
}
}

@ -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(",");
}

@ -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);
}

@ -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);
}

@ -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<FloatingMenuItem> menuItems = new ArrayList<>(6);
@SuppressWarnings("unchecked")
final SelectLayout<FilterType> selectLayout = (SelectLayout<FilterType>) LayoutInflater.from(getContext()).inflate(R.layout.layout_select, null);
List<SelectLayout.SelectItem<FilterType>> 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<SelectLayout.SelectItem<FilterType>> items = selectLayout.getItems();
int flags = 0;
for (SelectLayout.SelectItem<FilterType> item : items) {
if (item.checked) {
flags |= item.item.flag;
}
}
filter.type = flags;
updateFilterType();
updatePatternPreview();
}
})
.show();
} else if (v == boardsSelector) {
@SuppressWarnings("unchecked")
final SelectLayout<Board> selectLayout = (SelectLayout<Board>) 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);
}

@ -150,7 +150,7 @@ public class SelectLayout<T> 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<T> 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

@ -138,6 +138,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
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" />

@ -98,6 +98,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<item quantity="other">%d boards</item>
</plurals>
<plurals name="type">
<item quantity="one">%d type</item>
<item quantity="other">%d types</item>
</plurals>
<plurals name="bookmark">
<item quantity="one">%d bookmark</item>
<item quantity="other">%d bookmarks</item>
@ -182,10 +187,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<string name="filter_action">Action</string>
<string name="filter_pattern">Pattern</string>
<string name="filter_match_test">Test pattern</string>
<string name="filter_pattern_hint_exact">Exact text</string>
<string name="filter_pattern_hint_regex">Pattern</string>
<string name="filter_boards">Boards</string>
<string name="filter_boards_all">all</string>
<string name="filter_types">Types</string>
<string name="filter_type_string_matching">String matching</string>
<string name="filter_type_regex_matching">Regex matching</string>
<string name="filter_all">all</string>
<string name="filter_hide">Hide post</string>
<string name="filter_color">Highlight post</string>
<string name="filter_remove">Remove post</string>

Loading…
Cancel
Save