Add filtering logic and db and made color highlighting work

filtering
Floens 10 years ago
parent 5a564ddc32
commit 8a472097db
  1. 14
      Clover/app/src/main/java/org/floens/chan/core/database/DatabaseHelper.java
  2. 25
      Clover/app/src/main/java/org/floens/chan/core/database/DatabaseManager.java
  3. 192
      Clover/app/src/main/java/org/floens/chan/core/manager/FilterEngine.java
  4. 26
      Clover/app/src/main/java/org/floens/chan/core/model/Filter.java
  5. 2
      Clover/app/src/main/java/org/floens/chan/core/model/Post.java
  6. 28
      Clover/app/src/main/java/org/floens/chan/core/net/ChanReaderRequest.java
  7. 10
      Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java
  8. 4
      Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java
  9. 2
      Clover/app/src/main/java/org/floens/chan/ui/adapter/PostAdapter.java
  10. 11
      Clover/app/src/main/java/org/floens/chan/ui/adapter/PostsFilter.java
  11. 9
      Clover/app/src/main/java/org/floens/chan/ui/cell/CardPostCell.java
  12. 14
      Clover/app/src/main/java/org/floens/chan/ui/cell/PostCell.java
  13. 2
      Clover/app/src/main/java/org/floens/chan/ui/cell/ThreadStatusCell.java
  14. 10
      Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java
  15. 67
      Clover/app/src/main/java/org/floens/chan/ui/controller/FiltersController.java
  16. 8
      Clover/app/src/main/java/org/floens/chan/ui/controller/MainSettingsController.java
  17. 3
      Clover/app/src/main/java/org/floens/chan/ui/controller/ThreadController.java
  18. 9
      Clover/app/src/main/java/org/floens/chan/ui/helper/RefreshUIMessage.java
  19. 61
      Clover/app/src/main/java/org/floens/chan/ui/layout/FilterLayout.java
  20. 4
      Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadLayout.java
  21. 4
      Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadListLayout.java
  22. 1
      Clover/app/src/main/res/layout/cell_filter.xml
  23. 133
      Clover/app/src/main/res/layout/cell_post.xml
  24. 6
      Clover/app/src/main/res/layout/cell_post_card.xml
  25. 6
      Clover/app/src/main/res/values/strings.xml
  26. 4
      docs/database.txt

@ -26,6 +26,7 @@ import com.j256.ormlite.support.ConnectionSource;
import com.j256.ormlite.table.TableUtils; import com.j256.ormlite.table.TableUtils;
import org.floens.chan.core.model.Board; import org.floens.chan.core.model.Board;
import org.floens.chan.core.model.Filter;
import org.floens.chan.core.model.History; import org.floens.chan.core.model.History;
import org.floens.chan.core.model.Loadable; import org.floens.chan.core.model.Loadable;
import org.floens.chan.core.model.Pin; import org.floens.chan.core.model.Pin;
@ -42,7 +43,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
private static final String TAG = "DatabaseHelper"; private static final String TAG = "DatabaseHelper";
private static final String DATABASE_NAME = "ChanDB"; private static final String DATABASE_NAME = "ChanDB";
private static final int DATABASE_VERSION = 18; private static final int DATABASE_VERSION = 19;
public Dao<Pin, Integer> pinDao; public Dao<Pin, Integer> pinDao;
public Dao<Loadable, Integer> loadableDao; public Dao<Loadable, Integer> loadableDao;
@ -50,6 +51,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
public Dao<Board, Integer> boardsDao; public Dao<Board, Integer> boardsDao;
public Dao<ThreadHide, Integer> threadHideDao; public Dao<ThreadHide, Integer> threadHideDao;
public Dao<History, Integer> historyDao; public Dao<History, Integer> historyDao;
public Dao<Filter, Integer> filterDao;
private final Context context; private final Context context;
@ -65,6 +67,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
boardsDao = getDao(Board.class); boardsDao = getDao(Board.class);
threadHideDao = getDao(ThreadHide.class); threadHideDao = getDao(ThreadHide.class);
historyDao = getDao(History.class); historyDao = getDao(History.class);
filterDao = getDao(Filter.class);
} catch (SQLException e) { } catch (SQLException e) {
Logger.e(TAG, "Error creating Daos", e); Logger.e(TAG, "Error creating Daos", e);
} }
@ -79,6 +82,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
TableUtils.createTable(connectionSource, Board.class); TableUtils.createTable(connectionSource, Board.class);
TableUtils.createTable(connectionSource, ThreadHide.class); TableUtils.createTable(connectionSource, ThreadHide.class);
TableUtils.createTable(connectionSource, History.class); TableUtils.createTable(connectionSource, History.class);
TableUtils.createTable(connectionSource, Filter.class);
} catch (SQLException e) { } catch (SQLException e) {
Logger.e(TAG, "Error creating db", e); Logger.e(TAG, "Error creating db", e);
throw new RuntimeException(e); throw new RuntimeException(e);
@ -175,6 +179,14 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
Logger.e(TAG, "Error upgrading to version 18", e); Logger.e(TAG, "Error upgrading to version 18", e);
} }
} }
if (oldVersion < 19) {
try {
filterDao.executeRawNoArgs("CREATE TABLE `filter` (`action` INTEGER NOT NULL , `allBoards` SMALLINT NOT NULL , `boards` VARCHAR NOT NULL , `color` INTEGER NOT NULL , `enabled` SMALLINT NOT NULL , `id` INTEGER PRIMARY KEY AUTOINCREMENT , `pattern` VARCHAR NOT NULL , `type` INTEGER NOT NULL );");
} catch (SQLException e) {
Logger.e(TAG, "Error upgrading to version 19", e);
}
}
} }
public void reset() { public void reset() {

@ -69,8 +69,6 @@ public class DatabaseManager {
private final Object historyLock = new Object(); private final Object historyLock = new Object();
private final HashMap<Loadable, History> historyByLoadable = new HashMap<>(); private final HashMap<Loadable, History> historyByLoadable = new HashMap<>();
private final List<Filter> filters = new ArrayList<>();
public DatabaseManager(Context context) { public DatabaseManager(Context context) {
helper = new DatabaseHelper(context); helper = new DatabaseHelper(context);
initialize(); initialize();
@ -284,16 +282,31 @@ public class DatabaseManager {
return list; return list;
} }
public void addFilter(Filter filter) { public void addOrUpdateFilter(Filter filter) {
filters.add(filter); try {
helper.filterDao.createOrUpdate(filter);
} catch (SQLException e) {
Logger.e(TAG, "Error adding filter to db", e);
}
} }
public void removeFilter(Filter filter) { public void removeFilter(Filter filter) {
filters.remove(filter); try {
helper.filterDao.delete(filter);
} catch (SQLException e) {
Logger.e(TAG, "Error removing filter from db", e);
}
} }
public List<Filter> getFilters() { public List<Filter> getFilters() {
return filters; List<Filter> list = null;
try {
list = helper.filterDao.queryForAll();
} catch (SQLException e) {
Logger.e(TAG, "Error getting filters from db", e);
}
return list;
} }
/** /**

@ -19,10 +19,15 @@ package org.floens.chan.core.manager;
import android.text.TextUtils; import android.text.TextUtils;
import org.floens.chan.Chan;
import org.floens.chan.core.database.DatabaseManager;
import org.floens.chan.core.model.Board;
import org.floens.chan.core.model.Filter; import org.floens.chan.core.model.Filter;
import org.floens.chan.core.model.Post;
import org.floens.chan.utils.Logger; import org.floens.chan.utils.Logger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -31,6 +36,12 @@ import java.util.regex.PatternSyntaxException;
public class FilterEngine { public class FilterEngine {
private static final String TAG = "FilterEngine"; private static final String TAG = "FilterEngine";
private static final FilterEngine instance = new FilterEngine();
public static FilterEngine getInstance() {
return instance;
}
public enum FilterType { public enum FilterType {
TRIPCODE(0, false), TRIPCODE(0, false),
NAME(1, false), NAME(1, false),
@ -48,12 +59,15 @@ public class FilterEngine {
} }
public static FilterType forId(int id) { public static FilterType forId(int id) {
return enums[id];
}
private static FilterType[] enums = new FilterType[6];
static {
for (FilterType type : values()) { for (FilterType type : values()) {
if (type.id == id) { enums[type.id] = type;
return type;
}
} }
return null;
} }
} }
@ -68,28 +82,128 @@ public class FilterEngine {
} }
public static FilterAction forId(int id) { public static FilterAction forId(int id) {
return enums[id];
}
private static FilterAction[] enums = new FilterAction[2];
static {
for (FilterAction type : values()) { for (FilterAction type : values()) {
if (type.id == id) { enums[type.id] = type;
return type;
}
} }
return null;
} }
} }
private static final FilterEngine instance = new FilterEngine(); private final DatabaseManager databaseManager;
public static FilterEngine getInstance() { private List<Filter> filters;
return instance; private final List<Filter> enabledFilters = new ArrayList<>();
private FilterEngine() {
databaseManager = Chan.getDatabaseManager();
filters = databaseManager.getFilters();
updateEnabledFilters();
} }
private List<Filter> filters = new ArrayList<>(); /**
* Add or update a filter, thread-safe.
* The filter will be updated in the db if the {@link Filter#id} was non-null.
*
* @param filter filter too add or update.
*/
public void addOrUpdate(Filter filter) {
databaseManager.addOrUpdateFilter(filter);
filters = databaseManager.getFilters();
updateEnabledFilters();
}
public FilterEngine() { /**
* Remove a filter, thread-safe.
*
* @param filter filter to remove
*/
public void remove(Filter filter) {
databaseManager.removeFilter(filter);
filters = databaseManager.getFilters();
updateEnabledFilters();
}
/**
* Get all enabled filters, thread safe if locked on {@link #getEnabledFiltersLock()}.
*
* @return List of enabled filters
*/
public List<Filter> getEnabledFilters() {
return enabledFilters;
} }
public void add(Filter filter) { /**
* Lock for usage of {@link #getEnabledFilters()}
*
* @return Object to call synchronized on
*/
public Object getEnabledFiltersLock() {
return enabledFilters;
}
public boolean matches(Filter filter, Post post) {
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.comment.toString();
break;
case ID:
text = post.id;
break;
case SUBJECT:
text = post.subject;
break;
case FILENAME:
text = post.filename;
break;
}
return matches(filter, text, false);
}
public boolean matches(Filter filter, String text, boolean forceCompile) {
FilterType type = FilterType.forId(filter.type);
if (type.isRegex) {
Matcher matcher = null;
synchronized (filter.compiledMatcherLock) {
if (!forceCompile) {
matcher = filter.compiledMatcher;
}
if (matcher == null) {
Pattern compiledPattern = compile(filter.pattern);
matcher = filter.compiledMatcher = compiledPattern.matcher("");
Logger.d(TAG, "Resulting pattern: " + filter.compiledMatcher);
}
}
if (matcher != null) {
matcher.reset(text);
try {
return matcher.find();
} catch (IllegalArgumentException e) {
Logger.w(TAG, "matcher.find() exception", e);
return false;
}
} else {
Logger.e(TAG, "Invalid pattern");
return false;
}
} else {
return text.equals(filter.pattern);
}
} }
private static final Pattern isRegexPattern = Pattern.compile("^/(.*)/(i?)$"); private static final Pattern isRegexPattern = Pattern.compile("^/(.*)/(i?)$");
@ -139,27 +253,49 @@ public class FilterEngine {
return pattern; return pattern;
} }
public boolean matches(Filter filter, String text) { public List<Board> getBoardsForFilter(Filter filter) {
FilterType type = FilterType.forId(filter.type); if (filter.allBoards) {
if (type.isRegex) { return Chan.getBoardManager().getSavedBoards();
Pattern compiled = filter.compiledPattern; } else if (!TextUtils.isEmpty(filter.boards)) {
if (compiled == null) { List<Board> appliedBoards = new ArrayList<>();
compiled = filter.compiledPattern = compile(filter.pattern); for (String value : filter.boards.split(",")) {
Logger.test("Resulting pattern: " + filter.compiledPattern); Board boardByValue = Chan.getBoardManager().getBoardByValue(value);
if (boardByValue != null) {
appliedBoards.add(boardByValue);
}
} }
return appliedBoards;
} else {
return Collections.emptyList();
}
}
if (compiled != null) { public void saveBoardsToFilter(List<Board> appliedBoards, Filter filter) {
return compiled.matcher(text).find(); filter.boards = "";
} else { for (int i = 0; i < appliedBoards.size(); i++) {
Logger.e(TAG, "Invalid pattern"); Board board = appliedBoards.get(i);
return false; filter.boards += board.value;
if (i < appliedBoards.size() - 1) {
filter.boards += ",";
} }
} else {
return text.equals(filter.pattern);
} }
} }
private String escapeRegex(String filthy) { private String escapeRegex(String filthy) {
return filterFilthyPattern.matcher(filthy).replaceAll("\\\\$1"); // Escape regex special characters with a \ return filterFilthyPattern.matcher(filthy).replaceAll("\\\\$1"); // Escape regex special characters with a \
} }
private void updateEnabledFilters() {
List<Filter> enabled = new ArrayList<>();
for (Filter filter : filters) {
if (filter.enabled) {
enabled.add(filter);
}
}
synchronized (enabledFilters) {
enabledFilters.clear();
enabledFilters.addAll(enabled);
}
}
} }

@ -17,26 +17,46 @@
*/ */
package org.floens.chan.core.model; package org.floens.chan.core.model;
import java.util.regex.Pattern; import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
import org.floens.chan.core.manager.FilterEngine;
import java.util.regex.Matcher;
@DatabaseTable
public class Filter { public class Filter {
@DatabaseField(generatedId = true)
public int id; public int id;
@DatabaseField(canBeNull = false)
public boolean enabled = true; public boolean enabled = true;
public int type; @DatabaseField(canBeNull = false)
public int type = FilterEngine.FilterType.COMMENT.id;
@DatabaseField(canBeNull = false)
public String pattern; public String pattern;
@DatabaseField(canBeNull = false)
public boolean allBoards = true; public boolean allBoards = true;
@DatabaseField(canBeNull = false)
public String boards; public String boards;
@DatabaseField(canBeNull = false)
public int action; public int action;
@DatabaseField(canBeNull = false)
public int color; public int color;
public Pattern compiledPattern; public final Object compiledMatcherLock = new Object();
/**
* Cached version of {@link #pattern} compiled by {@link org.floens.chan.core.manager.FilterEngine#compile(String)}.
* Thread safe when synchronized on {@link #compiledMatcherLock}
*/
public Matcher compiledMatcher;
public void apply(Filter filter) { public void apply(Filter filter) {
enabled = filter.enabled; enabled = filter.enabled;

@ -66,6 +66,8 @@ public class Post {
public String rawComment; public String rawComment;
public String countryUrl; public String countryUrl;
public boolean spoiler = false; public boolean spoiler = false;
public int filterHighlightedColor = 0;
public boolean filterStub = false;
/** /**
* This post replies to the these ids. Is an unmodifiable list after finish(). * This post replies to the these ids. Is an unmodifiable list after finish().
*/ */

@ -24,6 +24,8 @@ import com.android.volley.Response.Listener;
import org.floens.chan.Chan; import org.floens.chan.Chan;
import org.floens.chan.chan.ChanUrls; import org.floens.chan.chan.ChanUrls;
import org.floens.chan.core.manager.FilterEngine;
import org.floens.chan.core.model.Filter;
import org.floens.chan.core.model.Loadable; import org.floens.chan.core.model.Loadable;
import org.floens.chan.core.model.Post; import org.floens.chan.core.model.Post;
import org.floens.chan.utils.Logger; import org.floens.chan.utils.Logger;
@ -37,9 +39,11 @@ public class ChanReaderRequest extends JsonReaderRequest<ChanReaderRequest.ChanR
private Loadable loadable; private Loadable loadable;
private List<Post> cached; private List<Post> cached;
private Post op; private Post op;
private FilterEngine filterEngine;
private ChanReaderRequest(String url, Listener<ChanReaderResponse> listener, ErrorListener errorListener) { private ChanReaderRequest(String url, Listener<ChanReaderResponse> listener, ErrorListener errorListener) {
super(url, listener, errorListener); super(url, listener, errorListener);
filterEngine = FilterEngine.getInstance();
} }
public static ChanReaderRequest newInstance(Loadable loadable, List<Post> cached, Listener<ChanReaderResponse> listener, ErrorListener errorListener) { public static ChanReaderRequest newInstance(Loadable loadable, List<Post> cached, Listener<ChanReaderResponse> listener, ErrorListener errorListener) {
@ -318,13 +322,35 @@ public class ChanReaderRequest extends JsonReaderRequest<ChanReaderRequest.ChanR
Logger.e(TAG, "Incorrect data about post received for post " + post.no); Logger.e(TAG, "Incorrect data about post received for post " + post.no);
return null; return null;
} else { } else {
processPostFilter(post);
return post; return post;
} }
} }
} }
private void processPostFilter(Post post) {
synchronized (filterEngine.getEnabledFiltersLock()) {
List<Filter> filters = filterEngine.getEnabledFilters();
int filterSize = filters.size();
for (int i = 0; i < filterSize; i++) {
Filter filter = filters.get(i);
if (filterEngine.matches(filter, post)) {
FilterEngine.FilterAction action = FilterEngine.FilterAction.forId(filter.action);
switch (action) {
case COLOR:
post.filterHighlightedColor = filter.color;
break;
case HIDE:
post.filterStub = true;
break;
}
}
}
}
}
public static class ChanReaderResponse { public static class ChanReaderResponse {
// Op Post that is created new each time.<br> // Op Post that is created new each time.
// Used to later copy members like image count to the real op on the main thread. // Used to later copy members like image count to the real op on the main thread.
public Post op; public Post op;
public List<Post> posts; public List<Post> posts;

@ -40,7 +40,7 @@ import org.floens.chan.core.model.SavedReply;
import org.floens.chan.core.net.LoaderPool; import org.floens.chan.core.net.LoaderPool;
import org.floens.chan.core.settings.ChanSettings; import org.floens.chan.core.settings.ChanSettings;
import org.floens.chan.ui.adapter.PostAdapter; import org.floens.chan.ui.adapter.PostAdapter;
import org.floens.chan.ui.adapter.PostFilter; import org.floens.chan.ui.adapter.PostsFilter;
import org.floens.chan.ui.cell.PostCellInterface; import org.floens.chan.ui.cell.PostCellInterface;
import org.floens.chan.ui.cell.ThreadStatusCell; import org.floens.chan.ui.cell.ThreadStatusCell;
import org.floens.chan.ui.helper.PostHelper; import org.floens.chan.ui.helper.PostHelper;
@ -79,7 +79,7 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt
private ChanLoader chanLoader; private ChanLoader chanLoader;
private boolean searchOpen = false; private boolean searchOpen = false;
private String searchQuery; private String searchQuery;
private PostFilter.Order order = PostFilter.Order.BUMP; private PostsFilter.Order order = PostsFilter.Order.BUMP;
private boolean historyAdded = false; private boolean historyAdded = false;
private int notificationPostCount = -1; private int notificationPostCount = -1;
@ -181,7 +181,7 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt
} }
} }
public void setOrder(PostFilter.Order order) { public void setOrder(PostsFilter.Order order) {
if (this.order != order) { if (this.order != order) {
this.order = order; this.order = order;
if (chanLoader != null) { if (chanLoader != null) {
@ -585,7 +585,7 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt
} }
private void showPosts() { private void showPosts() {
threadPresenterCallback.showPosts(chanLoader.getThread(), new PostFilter(order, searchQuery)); threadPresenterCallback.showPosts(chanLoader.getThread(), new PostsFilter(order, searchQuery));
} }
private void addHistory() { private void addHistory() {
@ -602,7 +602,7 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt
} }
public interface ThreadPresenterCallback { public interface ThreadPresenterCallback {
void showPosts(ChanThread thread, PostFilter filter); void showPosts(ChanThread thread, PostsFilter filter);
void postClicked(Post post); void postClicked(Post post);

@ -23,7 +23,7 @@ import android.os.Environment;
import org.floens.chan.Chan; import org.floens.chan.Chan;
import org.floens.chan.R; import org.floens.chan.R;
import org.floens.chan.chan.ChanUrls; import org.floens.chan.chan.ChanUrls;
import org.floens.chan.ui.adapter.PostFilter; import org.floens.chan.ui.adapter.PostsFilter;
import org.floens.chan.ui.cell.PostCellInterface; import org.floens.chan.ui.cell.PostCellInterface;
import org.floens.chan.utils.AndroidUtils; import org.floens.chan.utils.AndroidUtils;
@ -112,7 +112,7 @@ public class ChanSettings {
videoOpenExternal = new BooleanSetting(p, "preference_video_external", false); videoOpenExternal = new BooleanSetting(p, "preference_video_external", false);
videoErrorIgnore = new BooleanSetting(p, "preference_video_error_ignore", false); videoErrorIgnore = new BooleanSetting(p, "preference_video_error_ignore", false);
boardViewMode = new StringSetting(p, "preference_board_view_mode", PostCellInterface.PostViewMode.LIST.name); // "list" or "grid" boardViewMode = new StringSetting(p, "preference_board_view_mode", PostCellInterface.PostViewMode.LIST.name); // "list" or "grid"
boardOrder = new StringSetting(p, "preference_board_order", PostFilter.Order.BUMP.name); boardOrder = new StringSetting(p, "preference_board_order", PostsFilter.Order.BUMP.name);
postDefaultName = new StringSetting(p, "preference_default_name", ""); postDefaultName = new StringSetting(p, "preference_default_name", "");
postPinThread = new BooleanSetting(p, "preference_pin_on_post", false); postPinThread = new BooleanSetting(p, "preference_pin_on_post", false);

@ -128,7 +128,7 @@ public class PostAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
} }
} }
public void setThread(ChanThread thread, PostFilter filter) { public void setThread(ChanThread thread, PostsFilter filter) {
showError(null); showError(null);
sourceList.clear(); sourceList.clear();
sourceList.addAll(thread.posts); sourceList.addAll(thread.posts);

@ -20,8 +20,9 @@ package org.floens.chan.ui.adapter;
import android.text.TextUtils; import android.text.TextUtils;
import org.floens.chan.Chan; import org.floens.chan.Chan;
import org.floens.chan.core.model.Post;
import org.floens.chan.core.database.DatabaseManager; import org.floens.chan.core.database.DatabaseManager;
import org.floens.chan.core.manager.FilterEngine;
import org.floens.chan.core.model.Post;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -30,7 +31,7 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
public class PostFilter { public class PostsFilter {
public static final Comparator<Post> IMAGE_COMPARATOR = new Comparator<Post>() { public static final Comparator<Post> IMAGE_COMPARATOR = new Comparator<Post>() {
@Override @Override
public int compare(Post lhs, Post rhs) { public int compare(Post lhs, Post rhs) {
@ -60,14 +61,16 @@ public class PostFilter {
}; };
private final DatabaseManager databaseManager; private final DatabaseManager databaseManager;
private final FilterEngine filterEngine;
private Order order; private Order order;
private String query; private String query;
public PostFilter(Order order, String query) { public PostsFilter(Order order, String query) {
this.order = order; this.order = order;
this.query = query; this.query = query;
databaseManager = Chan.getDatabaseManager(); databaseManager = Chan.getDatabaseManager();
filterEngine = FilterEngine.getInstance();
} }
/** /**
@ -80,7 +83,7 @@ public class PostFilter {
List<Post> posts = new ArrayList<>(original); List<Post> posts = new ArrayList<>(original);
// Process order // Process order
if (order != PostFilter.Order.BUMP) { if (order != PostsFilter.Order.BUMP) {
switch (order) { switch (order) {
case IMAGE: case IMAGE:
Collections.sort(posts, IMAGE_COMPARATOR); Collections.sort(posts, IMAGE_COMPARATOR);

@ -55,6 +55,7 @@ public class CardPostCell extends CardView implements PostCellInterface, View.On
private FastTextView comment; private FastTextView comment;
private TextView replies; private TextView replies;
private ImageView options; private ImageView options;
private View colorLeft;
public CardPostCell(Context context) { public CardPostCell(Context context) {
super(context); super(context);
@ -79,6 +80,7 @@ public class CardPostCell extends CardView implements PostCellInterface, View.On
replies = (TextView) findViewById(R.id.replies); replies = (TextView) findViewById(R.id.replies);
options = (ImageView) findViewById(R.id.options); options = (ImageView) findViewById(R.id.options);
setRoundItemBackground(options); setRoundItemBackground(options);
colorLeft = findViewById(R.id.filter_match_color);
int textSizeSp = Integer.parseInt(ChanSettings.fontSize.get()); int textSizeSp = Integer.parseInt(ChanSettings.fontSize.get());
title.setTextSize(textSizeSp); title.setTextSize(textSizeSp);
@ -183,6 +185,13 @@ public class CardPostCell extends CardView implements PostCellInterface, View.On
thumbnailView.setUrl(null, 0, 0); thumbnailView.setUrl(null, 0, 0);
} }
if (post.filterHighlightedColor != 0) {
colorLeft.setVisibility(View.VISIBLE);
colorLeft.setBackgroundColor(post.filterHighlightedColor);
} else {
colorLeft.setVisibility(View.GONE);
}
if (!TextUtils.isEmpty(post.subjectSpan)) { if (!TextUtils.isEmpty(post.subjectSpan)) {
title.setVisibility(View.VISIBLE); title.setVisibility(View.VISIBLE);
title.setText(post.subjectSpan); title.setText(post.subjectSpan);

@ -40,6 +40,7 @@ import android.util.AttributeSet;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
@ -67,7 +68,7 @@ import static org.floens.chan.utils.AndroidUtils.getRes;
import static org.floens.chan.utils.AndroidUtils.setRoundItemBackground; import static org.floens.chan.utils.AndroidUtils.setRoundItemBackground;
import static org.floens.chan.utils.AndroidUtils.sp; import static org.floens.chan.utils.AndroidUtils.sp;
public class PostCell extends RelativeLayout implements PostCellInterface, PostLinkable.Callback { public class PostCell extends LinearLayout implements PostCellInterface, PostLinkable.Callback {
private static final int COMMENT_MAX_LENGTH_BOARD = 500; private static final int COMMENT_MAX_LENGTH_BOARD = 500;
private ThumbnailView thumbnailView; private ThumbnailView thumbnailView;
@ -77,6 +78,7 @@ public class PostCell extends RelativeLayout implements PostCellInterface, PostL
private TextView replies; private TextView replies;
private ImageView options; private ImageView options;
private View divider; private View divider;
private View colorLeft;
private boolean commentClickable = false; private boolean commentClickable = false;
private CharSequence iconsSpannable; private CharSequence iconsSpannable;
@ -129,6 +131,7 @@ public class PostCell extends RelativeLayout implements PostCellInterface, PostL
replies = (TextView) findViewById(R.id.replies); replies = (TextView) findViewById(R.id.replies);
options = (ImageView) findViewById(R.id.options); options = (ImageView) findViewById(R.id.options);
divider = findViewById(R.id.divider); divider = findViewById(R.id.divider);
colorLeft = findViewById(R.id.filter_match_color);
int textSizeSp = Integer.parseInt(ChanSettings.fontSize.get()); int textSizeSp = Integer.parseInt(ChanSettings.fontSize.get());
paddingPx = dp(textSizeSp - 6); paddingPx = dp(textSizeSp - 6);
@ -150,7 +153,7 @@ public class PostCell extends RelativeLayout implements PostCellInterface, PostL
setRoundItemBackground(replies); setRoundItemBackground(replies);
setRoundItemBackground(options); setRoundItemBackground(options);
RelativeLayout.LayoutParams dividerParams = (LayoutParams) divider.getLayoutParams(); RelativeLayout.LayoutParams dividerParams = (RelativeLayout.LayoutParams) divider.getLayoutParams();
dividerParams.leftMargin = paddingPx; dividerParams.leftMargin = paddingPx;
dividerParams.rightMargin = paddingPx; dividerParams.rightMargin = paddingPx;
divider.setLayoutParams(dividerParams); divider.setLayoutParams(dividerParams);
@ -281,6 +284,13 @@ public class PostCell extends RelativeLayout implements PostCellInterface, PostL
setBackgroundResource(R.drawable.item_background); setBackgroundResource(R.drawable.item_background);
} }
if (post.filterHighlightedColor != 0) {
colorLeft.setVisibility(View.VISIBLE);
colorLeft.setBackgroundColor(post.filterHighlightedColor);
} else {
colorLeft.setVisibility(View.GONE);
}
if (post.hasImage) { if (post.hasImage) {
thumbnailView.setVisibility(View.VISIBLE); thumbnailView.setVisibility(View.VISIBLE);
thumbnailView.setUrl(post.thumbnailUrl, thumbnailView.getLayoutParams().width, thumbnailView.getLayoutParams().height); thumbnailView.setUrl(post.thumbnailUrl, thumbnailView.getLayoutParams().width, thumbnailView.getLayoutParams().height);

@ -126,7 +126,7 @@ public class ThreadStatusCell extends LinearLayout implements View.OnClickListen
images.setSpan(new StyleSpan(Typeface.ITALIC), 0, images.length(), 0); images.setSpan(new StyleSpan(Typeface.ITALIC), 0, images.length(), 0);
} }
text.setText(TextUtils.concat(statusText, replies, " / ", images, " / ", String.valueOf(op.uniqueIps))); text.setText(TextUtils.concat(statusText, replies, " / ", images, " / ", String.valueOf(op.uniqueIps) + "P"));
} }
} }
} }

@ -34,7 +34,7 @@ import org.floens.chan.core.model.Board;
import org.floens.chan.core.model.Loadable; import org.floens.chan.core.model.Loadable;
import org.floens.chan.core.model.Pin; import org.floens.chan.core.model.Pin;
import org.floens.chan.core.settings.ChanSettings; import org.floens.chan.core.settings.ChanSettings;
import org.floens.chan.ui.adapter.PostFilter; import org.floens.chan.ui.adapter.PostsFilter;
import org.floens.chan.ui.cell.PostCellInterface; import org.floens.chan.ui.cell.PostCellInterface;
import org.floens.chan.ui.layout.ThreadLayout; import org.floens.chan.ui.layout.ThreadLayout;
import org.floens.chan.ui.toolbar.ToolbarMenu; import org.floens.chan.ui.toolbar.ToolbarMenu;
@ -54,7 +54,7 @@ public class BrowseController extends ThreadController implements ToolbarMenuIte
private static final int ORDER_ID = 104; private static final int ORDER_ID = 104;
private PostCellInterface.PostViewMode postViewMode; private PostCellInterface.PostViewMode postViewMode;
private PostFilter.Order order; private PostsFilter.Order order;
private List<FloatingMenuItem> boardItems; private List<FloatingMenuItem> boardItems;
private FloatingMenuItem viewModeMenuItem; private FloatingMenuItem viewModeMenuItem;
@ -70,7 +70,7 @@ public class BrowseController extends ThreadController implements ToolbarMenuIte
super.onCreate(); super.onCreate();
postViewMode = PostCellInterface.PostViewMode.find(ChanSettings.boardViewMode.get()); postViewMode = PostCellInterface.PostViewMode.find(ChanSettings.boardViewMode.get());
order = PostFilter.Order.find(ChanSettings.boardOrder.get()); order = PostsFilter.Order.find(ChanSettings.boardOrder.get());
threadLayout.setPostViewMode(postViewMode); threadLayout.setPostViewMode(postViewMode);
threadLayout.getPresenter().setOrder(order); threadLayout.getPresenter().setOrder(order);
@ -136,7 +136,7 @@ public class BrowseController extends ThreadController implements ToolbarMenuIte
break; break;
case ORDER_ID: case ORDER_ID:
List<FloatingMenuItem> items = new ArrayList<>(); List<FloatingMenuItem> items = new ArrayList<>();
for (PostFilter.Order order : PostFilter.Order.values()) { for (PostsFilter.Order order : PostsFilter.Order.values()) {
int nameId = 0; int nameId = 0;
switch (order) { switch (order) {
case BUMP: case BUMP:
@ -168,7 +168,7 @@ public class BrowseController extends ThreadController implements ToolbarMenuIte
menu.setCallback(new FloatingMenu.FloatingMenuCallback() { menu.setCallback(new FloatingMenu.FloatingMenuCallback() {
@Override @Override
public void onFloatingMenuItemClicked(FloatingMenu menu, FloatingMenuItem item) { public void onFloatingMenuItemClicked(FloatingMenu menu, FloatingMenuItem item) {
PostFilter.Order order = (PostFilter.Order) item.getId(); PostsFilter.Order order = (PostsFilter.Order) item.getId();
ChanSettings.boardOrder.set(order.name); ChanSettings.boardOrder.set(order.name);
BrowseController.this.order = order; BrowseController.this.order = order;
threadLayout.getPresenter().setOrder(order); threadLayout.getPresenter().setOrder(order);

@ -30,11 +30,11 @@ import android.view.ViewGroup;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import org.floens.chan.Chan;
import org.floens.chan.R; import org.floens.chan.R;
import org.floens.chan.controller.Controller; 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.model.Filter; import org.floens.chan.core.model.Filter;
import org.floens.chan.ui.helper.RefreshUIMessage;
import org.floens.chan.ui.layout.FilterLayout; import org.floens.chan.ui.layout.FilterLayout;
import org.floens.chan.ui.toolbar.ToolbarMenu; import org.floens.chan.ui.toolbar.ToolbarMenu;
import org.floens.chan.ui.toolbar.ToolbarMenuItem; import org.floens.chan.ui.toolbar.ToolbarMenuItem;
@ -44,13 +44,17 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import de.greenrobot.event.EventBus;
import static org.floens.chan.ui.theme.ThemeHelper.theme; import static org.floens.chan.ui.theme.ThemeHelper.theme;
import static org.floens.chan.utils.AndroidUtils.getAttrColor;
import static org.floens.chan.utils.AndroidUtils.getString;
public class FiltersController extends Controller implements ToolbarMenuItem.ToolbarMenuItemCallback, RootNavigationController.ToolbarSearchCallback, View.OnClickListener { public class FiltersController extends Controller implements ToolbarMenuItem.ToolbarMenuItemCallback, RootNavigationController.ToolbarSearchCallback, View.OnClickListener {
private static final int SEARCH_ID = 1; private static final int SEARCH_ID = 1;
private static final int CLEAR_ID = 101; private static final int CLEAR_ID = 101;
private DatabaseManager databaseManager; private FilterEngine filterEngine;
private RecyclerView recyclerView; private RecyclerView recyclerView;
private FloatingActionButton add; private FloatingActionButton add;
private FilterAdapter adapter; private FilterAdapter adapter;
@ -59,11 +63,39 @@ public class FiltersController extends Controller implements ToolbarMenuItem.Too
super(context); super(context);
} }
public static String filterTypeName(FilterEngine.FilterType type) {
switch (type) {
case TRIPCODE:
return getString(R.string.filter_tripcode);
case NAME:
return getString(R.string.filter_name);
case COMMENT:
return getString(R.string.filter_comment);
case ID:
return getString(R.string.filter_id);
case SUBJECT:
return getString(R.string.filter_subject);
case FILENAME:
return getString(R.string.filter_filename);
}
return null;
}
public static String actionName(FilterEngine.FilterAction action) {
switch (action) {
case HIDE:
return getString(R.string.filter_hide);
case COLOR:
return getString(R.string.filter_color);
}
return null;
}
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
databaseManager = Chan.getDatabaseManager(); filterEngine = FilterEngine.getInstance();
navigationItem.title = string(R.string.filters_screen); navigationItem.title = string(R.string.filters_screen);
navigationItem.menu = new ToolbarMenu(context); navigationItem.menu = new ToolbarMenu(context);
@ -111,8 +143,8 @@ public class FiltersController extends Controller implements ToolbarMenuItem.Too
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
Filter newFilter = filterLayout.getFilter(); Filter newFilter = filterLayout.getFilter();
newFilter.id = filter.id; newFilter.id = filter.id;
Chan.getDatabaseManager().addFilter(newFilter); filterEngine.addOrUpdate(newFilter);
EventBus.getDefault().post(new RefreshUIMessage("filters"));
adapter.load(); adapter.load();
} }
}) })
@ -128,7 +160,7 @@ public class FiltersController extends Controller implements ToolbarMenuItem.Too
} }
private void deleteFilter(Filter filter) { private void deleteFilter(Filter filter) {
databaseManager.removeFilter(filter); filterEngine.remove(filter);
adapter.load(); adapter.load();
//TODO: undo //TODO: undo
} }
@ -163,7 +195,20 @@ public class FiltersController extends Controller implements ToolbarMenuItem.Too
public void onBindViewHolder(FilterCell holder, int position) { public void onBindViewHolder(FilterCell holder, int position) {
Filter filter = displayList.get(position); Filter filter = displayList.get(position);
holder.text.setText(filter.pattern); holder.text.setText(filter.pattern);
holder.subtext.setText(String.valueOf(filter.type)); holder.text.setTextColor(getAttrColor(context, filter.enabled ? R.attr.text_color_primary : R.attr.text_color_secondary));
String subText = filterTypeName(FilterEngine.FilterType.forId(filter.type));
subText += " - ";
if (filter.allBoards) {
subText += context.getString(R.string.filter_summary_all_boards);
} else {
int size = filterEngine.getBoardsForFilter(filter).size();
subText += context.getResources().getQuantityString(R.plurals.board, size, size);
}
subText += " - " + FiltersController.actionName(FilterEngine.FilterAction.forId(filter.action));
holder.subtext.setText(subText);
} }
@Override @Override
@ -183,7 +228,7 @@ public class FiltersController extends Controller implements ToolbarMenuItem.Too
private void load() { private void load() {
sourceList.clear(); sourceList.clear();
sourceList.addAll(databaseManager.getFilters()); sourceList.addAll(filterEngine.getEnabledFilters());
filter(); filter();
} }
@ -193,7 +238,9 @@ public class FiltersController extends Controller implements ToolbarMenuItem.Too
if (!TextUtils.isEmpty(searchQuery)) { if (!TextUtils.isEmpty(searchQuery)) {
String query = searchQuery.toLowerCase(Locale.ENGLISH); String query = searchQuery.toLowerCase(Locale.ENGLISH);
for (Filter filter : sourceList) { for (Filter filter : sourceList) {
displayList.add(filter); if (filter.pattern.toLowerCase().contains(query)) {
displayList.add(filter);
}
} }
} else { } else {
displayList.addAll(sourceList); displayList.addAll(sourceList);

@ -26,6 +26,7 @@ import android.widget.Toast;
import org.floens.chan.Chan; import org.floens.chan.Chan;
import org.floens.chan.R; import org.floens.chan.R;
import org.floens.chan.core.settings.ChanSettings; import org.floens.chan.core.settings.ChanSettings;
import org.floens.chan.ui.helper.RefreshUIMessage;
import org.floens.chan.ui.settings.BooleanSettingView; import org.floens.chan.ui.settings.BooleanSettingView;
import org.floens.chan.ui.settings.LinkSettingView; import org.floens.chan.ui.settings.LinkSettingView;
import org.floens.chan.ui.settings.ListSettingView; import org.floens.chan.ui.settings.ListSettingView;
@ -268,11 +269,4 @@ public class MainSettingsController extends SettingsController implements Toolba
return string(id); return string(id);
} }
public static class RefreshUIMessage {
public String reason;
public RefreshUIMessage(String reason) {
this.reason = reason;
}
}
} }

@ -26,6 +26,7 @@ import org.floens.chan.R;
import org.floens.chan.controller.Controller; import org.floens.chan.controller.Controller;
import org.floens.chan.core.model.Loadable; import org.floens.chan.core.model.Loadable;
import org.floens.chan.core.model.PostImage; import org.floens.chan.core.model.PostImage;
import org.floens.chan.ui.helper.RefreshUIMessage;
import org.floens.chan.ui.layout.ThreadLayout; import org.floens.chan.ui.layout.ThreadLayout;
import org.floens.chan.ui.view.ThumbnailView; import org.floens.chan.ui.view.ThumbnailView;
@ -88,7 +89,7 @@ public abstract class ThreadController extends Controller implements ThreadLayou
threadLayout.getPresenter().onForegroundChanged(message.inForeground); threadLayout.getPresenter().onForegroundChanged(message.inForeground);
} }
public void onEvent(MainSettingsController.RefreshUIMessage message) { public void onEvent(RefreshUIMessage message) {
threadLayout.getPresenter().requestData(); threadLayout.getPresenter().requestData();
} }

@ -0,0 +1,9 @@
package org.floens.chan.ui.helper;
public class RefreshUIMessage {
public String reason;
public RefreshUIMessage(String reason) {
this.reason = reason;
}
}

@ -44,6 +44,7 @@ import org.floens.chan.core.manager.BoardManager;
import org.floens.chan.core.manager.FilterEngine; import org.floens.chan.core.manager.FilterEngine;
import org.floens.chan.core.model.Board; import org.floens.chan.core.model.Board;
import org.floens.chan.core.model.Filter; import org.floens.chan.core.model.Filter;
import org.floens.chan.ui.controller.FiltersController;
import org.floens.chan.ui.dialog.ColorPickerView; import org.floens.chan.ui.dialog.ColorPickerView;
import org.floens.chan.ui.drawable.DropdownArrowDrawable; import org.floens.chan.ui.drawable.DropdownArrowDrawable;
import org.floens.chan.ui.view.FloatingMenu; import org.floens.chan.ui.view.FloatingMenu;
@ -106,7 +107,6 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener {
@Override @Override
public void onTextChanged(CharSequence s, int start, int before, int count) { public void onTextChanged(CharSequence s, int start, int before, int count) {
filter.compiledPattern = null;
filter.pattern = s.toString(); filter.pattern = s.toString();
updateFilterValidity(); updateFilterValidity();
updatePatternPreview(); updatePatternPreview();
@ -156,17 +156,7 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener {
public void setFilter(Filter filter) { public void setFilter(Filter filter) {
this.filter.apply(filter); this.filter.apply(filter);
appliedBoards.clear(); appliedBoards.clear();
appliedBoards.addAll(FilterEngine.getInstance().getBoardsForFilter(filter));
if (filter.allBoards) {
appliedBoards.addAll(boardManager.getSavedBoards());
} else if (!TextUtils.isEmpty(filter.boards)) {
for (String value : filter.boards.split(",")) {
Board boardByValue = boardManager.getBoardByValue(value);
if (boardByValue != null) {
appliedBoards.add(boardByValue);
}
}
}
pattern.setText(filter.pattern); pattern.setText(filter.pattern);
@ -185,14 +175,7 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener {
public Filter getFilter() { public Filter getFilter() {
filter.enabled = enabled.isChecked(); filter.enabled = enabled.isChecked();
filter.boards = ""; FilterEngine.getInstance().saveBoardsToFilter(appliedBoards, filter);
for (int i = 0; i < appliedBoards.size(); i++) {
Board board = appliedBoards.get(i);
filter.boards += board.value;
if (i < appliedBoards.size() - 1) {
filter.boards += ",";
}
}
return filter; return filter;
} }
@ -203,7 +186,7 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener {
List<FloatingMenuItem> menuItems = new ArrayList<>(6); List<FloatingMenuItem> menuItems = new ArrayList<>(6);
for (FilterEngine.FilterType filterType : FilterEngine.FilterType.values()) { for (FilterEngine.FilterType filterType : FilterEngine.FilterType.values()) {
menuItems.add(new FloatingMenuItem(filterType, filterTypeName(filterType))); menuItems.add(new FloatingMenuItem(filterType, FiltersController.filterTypeName(filterType)));
} }
FloatingMenu menu = new FloatingMenu(v.getContext()); FloatingMenu menu = new FloatingMenu(v.getContext());
@ -245,7 +228,7 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener {
List<FloatingMenuItem> menuItems = new ArrayList<>(6); List<FloatingMenuItem> menuItems = new ArrayList<>(6);
for (FilterEngine.FilterAction action : FilterEngine.FilterAction.values()) { for (FilterEngine.FilterAction action : FilterEngine.FilterAction.values()) {
menuItems.add(new FloatingMenuItem(action, actionName(action))); menuItems.add(new FloatingMenuItem(action, FiltersController.actionName(action)));
} }
FloatingMenu menu = new FloatingMenu(v.getContext()); FloatingMenu menu = new FloatingMenu(v.getContext());
@ -347,7 +330,7 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener {
private void updateFilterAction() { private void updateFilterAction() {
FilterEngine.FilterAction action = FilterEngine.FilterAction.forId(filter.action); FilterEngine.FilterAction action = FilterEngine.FilterAction.forId(filter.action);
actionText.setText(actionName(action)); actionText.setText(FiltersController.actionName(action));
colorContainer.setVisibility(action == FilterEngine.FilterAction.COLOR ? VISIBLE : GONE); colorContainer.setVisibility(action == FilterEngine.FilterAction.COLOR ? VISIBLE : GONE);
if (filter.color == 0) { if (filter.color == 0) {
filter.color = 0xffff0000; filter.color = 0xffff0000;
@ -357,44 +340,16 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener {
private void updateFilterType() { private void updateFilterType() {
FilterEngine.FilterType filterType = FilterEngine.FilterType.forId(filter.type); FilterEngine.FilterType filterType = FilterEngine.FilterType.forId(filter.type);
typeText.setText(filterTypeName(filterType)); typeText.setText(FiltersController.filterTypeName(filterType));
pattern.setHint(filterType.isRegex ? R.string.filter_pattern_hint_regex : R.string.filter_pattern_hint_exact); pattern.setHint(filterType.isRegex ? R.string.filter_pattern_hint_regex : R.string.filter_pattern_hint_exact);
} }
private void updatePatternPreview() { private void updatePatternPreview() {
String text = patternPreview.getText().toString(); String text = patternPreview.getText().toString();
boolean matches = text.length() > 0 && FilterEngine.getInstance().matches(filter, text); boolean matches = text.length() > 0 && FilterEngine.getInstance().matches(filter, text, true);
patternPreviewStatus.setText(matches ? R.string.filter_matches : R.string.filter_no_matches); patternPreviewStatus.setText(matches ? R.string.filter_matches : R.string.filter_no_matches);
} }
private String filterTypeName(FilterEngine.FilterType type) {
switch (type) {
case TRIPCODE:
return getString(R.string.filter_tripcode);
case NAME:
return getString(R.string.filter_name);
case COMMENT:
return getString(R.string.filter_comment);
case ID:
return getString(R.string.filter_id);
case SUBJECT:
return getString(R.string.filter_subject);
case FILENAME:
return getString(R.string.filter_filename);
}
return null;
}
private String actionName(FilterEngine.FilterAction action) {
switch (action) {
case HIDE:
return getString(R.string.filter_hide);
case COLOR:
return getString(R.string.filter_color);
}
return null;
}
public interface FilterLayoutCallback { public interface FilterLayoutCallback {
void setSaveButtonEnabled(boolean enabled); void setSaveButtonEnabled(boolean enabled);
} }

@ -55,7 +55,7 @@ import org.floens.chan.core.model.PostLinkable;
import org.floens.chan.core.model.ThreadHide; import org.floens.chan.core.model.ThreadHide;
import org.floens.chan.core.presenter.ThreadPresenter; import org.floens.chan.core.presenter.ThreadPresenter;
import org.floens.chan.core.settings.ChanSettings; import org.floens.chan.core.settings.ChanSettings;
import org.floens.chan.ui.adapter.PostFilter; import org.floens.chan.ui.adapter.PostsFilter;
import org.floens.chan.ui.cell.PostCellInterface; import org.floens.chan.ui.cell.PostCellInterface;
import org.floens.chan.ui.helper.PostPopupHelper; import org.floens.chan.ui.helper.PostPopupHelper;
import org.floens.chan.ui.view.LoadView; import org.floens.chan.ui.view.LoadView;
@ -182,7 +182,7 @@ public class ThreadLayout extends CoordinatorLayout implements ThreadPresenter.T
} }
@Override @Override
public void showPosts(ChanThread thread, PostFilter filter) { public void showPosts(ChanThread thread, PostsFilter filter) {
threadListLayout.showPosts(thread, filter, visible != Visible.THREAD); threadListLayout.showPosts(thread, filter, visible != Visible.THREAD);
switchVisible(Visible.THREAD); switchVisible(Visible.THREAD);
callback.onShowPosts(); callback.onShowPosts();

@ -33,7 +33,7 @@ import org.floens.chan.core.model.Post;
import org.floens.chan.core.model.PostImage; import org.floens.chan.core.model.PostImage;
import org.floens.chan.core.presenter.ReplyPresenter; import org.floens.chan.core.presenter.ReplyPresenter;
import org.floens.chan.ui.adapter.PostAdapter; import org.floens.chan.ui.adapter.PostAdapter;
import org.floens.chan.ui.adapter.PostFilter; import org.floens.chan.ui.adapter.PostsFilter;
import org.floens.chan.ui.cell.PostCell; import org.floens.chan.ui.cell.PostCell;
import org.floens.chan.ui.cell.PostCellInterface; import org.floens.chan.ui.cell.PostCellInterface;
import org.floens.chan.ui.cell.ThreadStatusCell; import org.floens.chan.ui.cell.ThreadStatusCell;
@ -164,7 +164,7 @@ public class ThreadListLayout extends LinearLayout implements ReplyLayout.ReplyL
} }
} }
public void showPosts(ChanThread thread, PostFilter filter, boolean initial) { public void showPosts(ChanThread thread, PostsFilter filter, boolean initial) {
showingThread = thread; showingThread = thread;
if (initial) { if (initial) {
reply.bindLoadable(showingThread.loadable); reply.bindLoadable(showingThread.loadable);

@ -42,7 +42,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
android:paddingRight="16dp" android:paddingRight="16dp"
android:paddingTop="8dp" android:paddingTop="8dp"
android:singleLine="true" android:singleLine="true"
android:textColor="?text_color_primary"
android:textSize="14sp" /> android:textSize="14sp" />
<TextView <TextView

@ -17,75 +17,90 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
--> -->
<org.floens.chan.ui.cell.PostCell xmlns:android="http://schemas.android.com/apk/res/android" <org.floens.chan.ui.cell.PostCell xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/post_cell"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"
tools:ignore="RtlHardcoded,RtlSymmetry"> tools:ignore="RtlHardcoded,RtlSymmetry">
<org.floens.chan.ui.view.ThumbnailView <View
android:id="@+id/thumbnail_view" android:id="@+id/filter_match_color"
android:layout_width="@dimen/cell_post_thumbnail_size" android:layout_width="5dp"
android:layout_height="@dimen/cell_post_thumbnail_size" android:layout_height="match_parent"
android:gravity="top" /> android:visibility="gone" />
<TextView <RelativeLayout
android:id="@+id/title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content">
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_alignWithParentIfMissing="true"
android:layout_toRightOf="@id/thumbnail_view"
android:paddingRight="25dp" />
<TextView <org.floens.chan.ui.view.ThumbnailView
android:id="@+id/icons" android:id="@+id/thumbnail_view"
android:layout_width="match_parent" android:layout_width="@dimen/cell_post_thumbnail_size"
android:layout_height="wrap_content" android:layout_height="@dimen/cell_post_thumbnail_size"
android:layout_alignParentRight="true" android:layout_alignWithParentIfMissing="true"
android:layout_alignWithParentIfMissing="true" android:gravity="top" />
android:layout_below="@id/title"
android:layout_toRightOf="@id/thumbnail_view" />
<TextView <TextView
android:id="@+id/comment" android:id="@+id/title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_alignWithParentIfMissing="true" android:layout_alignParentTop="true"
android:layout_below="@id/icons" android:layout_alignWithParentIfMissing="true"
android:layout_toRightOf="@id/thumbnail_view" android:layout_toRightOf="@id/thumbnail_view"
android:textColor="?attr/text_color_primary" /> android:paddingRight="25dp" />
<TextView <TextView
android:id="@+id/replies" android:id="@+id/icons"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignWithParentIfMissing="true" android:layout_alignParentRight="true"
android:layout_below="@id/comment" android:layout_alignWithParentIfMissing="true"
android:layout_toRightOf="@id/thumbnail_view" android:layout_below="@id/title"
android:singleLine="true" android:layout_toRightOf="@id/thumbnail_view" />
android:textColor="?attr/text_color_secondary" />
<View <TextView
android:id="@+id/divider" android:id="@+id/comment"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dp" android:layout_height="wrap_content"
android:layout_alignParentBottom="true" android:layout_alignParentRight="true"
android:layout_alignParentLeft="true" android:layout_alignWithParentIfMissing="true"
android:layout_alignParentRight="true" android:layout_below="@id/icons"
android:background="?attr/divider_color" /> android:layout_toRightOf="@id/thumbnail_view"
android:textColor="?attr/text_color_primary" />
<TextView
android:id="@+id/replies"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignWithParentIfMissing="true"
android:layout_below="@id/comment"
android:layout_toRightOf="@id/thumbnail_view"
android:singleLine="true"
android:textColor="?attr/text_color_secondary" />
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:background="?attr/divider_color" />
<ImageView
android:id="@+id/options"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:paddingBottom="15dp"
android:paddingLeft="15dp"
android:paddingRight="5dp"
android:paddingTop="5dp"
android:src="?post_options_drawable"
tools:ignore="ContentDescription" />
<ImageView </RelativeLayout>
android:id="@+id/options"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:paddingBottom="15dp"
android:paddingLeft="15dp"
android:paddingRight="5dp"
android:paddingTop="5dp"
android:src="?post_options_drawable"
tools:ignore="ContentDescription" />
</org.floens.chan.ui.cell.PostCell> </org.floens.chan.ui.cell.PostCell>

@ -55,6 +55,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</FrameLayout> </FrameLayout>
<View
android:id="@+id/filter_match_color"
android:layout_width="match_parent"
android:layout_height="10dp"
android:visibility="gone" />
<TextView <TextView
android:id="@+id/title" android:id="@+id/title"
android:layout_width="match_parent" android:layout_width="match_parent"

@ -48,6 +48,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<item quantity="other">%d images</item> <item quantity="other">%d images</item>
</plurals> </plurals>
<plurals name="board">
<item quantity="one">%d board</item>
<item quantity="other">%d boards</item>
</plurals>
<string name="card_stats">%1$dR %2$dI</string> <string name="card_stats">%1$dR %2$dI</string>
<string name="action_reload">Reload</string> <string name="action_reload">Reload</string>
@ -117,6 +122,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<string name="board_add_unknown">The board with code %1$s is not known.</string> <string name="board_add_unknown">The board with code %1$s is not known.</string>
<string name="board_edit_sort_a_z">Sort A-Z</string> <string name="board_edit_sort_a_z">Sort A-Z</string>
<string name="filter_summary_all_boards">all boards</string>
<string name="filter_enabled">Enabled</string> <string name="filter_enabled">Enabled</string>
<string name="filter_filter">Filter</string> <string name="filter_filter">Filter</string>
<string name="filter_action">Action</string> <string name="filter_action">Action</string>

@ -52,3 +52,7 @@ ALTER TABLE board ADD COLUMN description TEXT;
Changes in version 18: Changes in version 18:
CREATE TABLE `history` (`date` BIGINT , `id` INTEGER PRIMARY KEY AUTOINCREMENT , `loadable_id` INTEGER NOT NULL , `thumbnailUrl` VARCHAR ) CREATE TABLE `history` (`date` BIGINT , `id` INTEGER PRIMARY KEY AUTOINCREMENT , `loadable_id` INTEGER NOT NULL , `thumbnailUrl` VARCHAR )
Changes in version 19:
CREATE TABLE `filter` (`action` INTEGER NOT NULL , `allBoards` SMALLINT NOT NULL , `boards` VARCHAR NOT NULL , `color` INTEGER NOT NULL , `enabled` SMALLINT NOT NULL , `id` INTEGER PRIMARY KEY AUTOINCREMENT , `pattern` VARCHAR NOT NULL , `type` INTEGER NOT NULL )

Loading…
Cancel
Save