update filter handling for multisite

still messy, the controller desperately needs a presenter.
multisite
Floens 8 years ago
parent 9ef10f129b
commit 18d06e2f57
  1. 92
      Clover/app/src/main/java/org/floens/chan/core/manager/FilterEngine.java
  2. 11
      Clover/app/src/main/java/org/floens/chan/core/site/common/ChanReaderRequest.java
  3. 2
      Clover/app/src/main/java/org/floens/chan/ui/controller/FiltersController.java
  4. 22
      Clover/app/src/main/java/org/floens/chan/ui/helper/BoardHelper.java
  5. 103
      Clover/app/src/main/java/org/floens/chan/ui/layout/FilterLayout.java
  6. 36
      Clover/app/src/main/res/layout/layout_site_board_select.xml

@ -22,13 +22,13 @@ import android.text.TextUtils;
import org.floens.chan.core.database.DatabaseFilterManager; import org.floens.chan.core.database.DatabaseFilterManager;
import org.floens.chan.core.database.DatabaseManager; import org.floens.chan.core.database.DatabaseManager;
import org.floens.chan.core.model.orm.Filter;
import org.floens.chan.core.model.Post; import org.floens.chan.core.model.Post;
import org.floens.chan.core.site.Sites; import org.floens.chan.core.model.orm.Board;
import org.floens.chan.core.model.orm.Filter;
import org.floens.chan.ui.helper.BoardHelper;
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.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -67,21 +67,6 @@ public class FilterEngine {
} }
} }
// This is messy but required now that we can't know the Board immediately.
public static class SiteIdBoardCode {
public final int siteId;
public final String boardCode;
private SiteIdBoardCode(int site, String board) {
siteId = site;
boardCode = board;
}
public static SiteIdBoardCode fromSiteIdBoardCode(int siteId, String boardCode) {
return new SiteIdBoardCode(siteId, boardCode);
}
}
private final DatabaseManager databaseManager; private final DatabaseManager databaseManager;
private final BoardManager boardManager; private final BoardManager boardManager;
@ -116,7 +101,42 @@ public class FilterEngine {
return enabledFilters; return enabledFilters;
} }
// threadsafe @AnyThread
public boolean matchesBoard(Filter filter, Board board) {
if (filter.allBoards || TextUtils.isEmpty(filter.boards)) {
return true;
} else {
for (String uniqueId : filter.boards.split(",")) {
if (BoardHelper.matchesUniqueId(board, uniqueId)) {
return true;
}
}
return false;
}
}
public int getFilterBoardCount(Filter filter) {
if (filter.allBoards) {
return -1;
} else {
return filter.boards.split(",").length;
}
}
public void saveBoardsToFilter(List<Board> appliedBoards, boolean all, Filter filter) {
filter.allBoards = all;
if (all) {
filter.boards = "";
} else {
List<String> boardsString = new ArrayList<>(appliedBoards.size());
for (int i = 0; i < appliedBoards.size(); i++) {
boardsString.add(BoardHelper.boardUniqueId(appliedBoards.get(i)));
}
filter.boards = TextUtils.join(",", boardsString);
}
}
@AnyThread
public boolean matches(Filter filter, Post.Builder post) { public boolean matches(Filter filter, Post.Builder post) {
if ((filter.type & FilterType.TRIPCODE.flag) != 0 && matches(filter, FilterType.TRIPCODE.isRegex, post.tripcode, false)) { if ((filter.type & FilterType.TRIPCODE.flag) != 0 && matches(filter, FilterType.TRIPCODE.isRegex, post.tripcode, false)) {
return true; return true;
@ -236,40 +256,6 @@ public class FilterEngine {
return pattern; return pattern;
} }
public List<SiteIdBoardCode> getBoardsForFilter(Filter filter) {
if (filter.allBoards) {
return Collections.emptyList();
} else if (!TextUtils.isEmpty(filter.boards)) {
List<SiteIdBoardCode> appliedBoards = new ArrayList<>();
for (String value : filter.boards.split(",")) {
if (value.contains(";")) {
String[] siteAndBoard = value.split(";");
if (siteAndBoard.length == 1) {
appliedBoards.add(SiteIdBoardCode.fromSiteIdBoardCode(Integer.parseInt(siteAndBoard[0]), ""));
} else {
appliedBoards.add(SiteIdBoardCode.fromSiteIdBoardCode(Integer.parseInt(siteAndBoard[0]), siteAndBoard[1]));
}
} else {
appliedBoards.add(SiteIdBoardCode.fromSiteIdBoardCode(Sites.defaultSite().id(), value));
}
}
return appliedBoards;
} else {
return Collections.emptyList();
}
}
public void saveBoardsToFilter(List<SiteIdBoardCode> appliedBoards, Filter filter) {
filter.boards = "";
for (int i = 0; i < appliedBoards.size(); i++) {
SiteIdBoardCode siteAndBoard = appliedBoards.get(i);
filter.boards += siteAndBoard.siteId + ";" + siteAndBoard.boardCode;
if (i < appliedBoards.size() - 1) {
filter.boards += ",";
}
}
}
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 \
} }

@ -91,18 +91,9 @@ public class ChanReaderRequest extends JsonReaderRequest<ChanLoaderResponse> {
for (int i = 0; i < enabledFilters.size(); i++) { for (int i = 0; i < enabledFilters.size(); i++) {
Filter filter = enabledFilters.get(i); Filter filter = enabledFilters.get(i);
if (filter.allBoards) { if (filterEngine.matchesBoard(filter, loadable.board)) {
// copy the filter because it will get used on other threads // copy the filter because it will get used on other threads
filters.add(filter.copy()); filters.add(filter.copy());
} else {
String[] boardCodes = filter.boardCodes();
for (String code : boardCodes) {
if (code.equals(loadable.boardCode)) {
// copy the filter because it will get used on other threads
filters.add(filter.copy());
break;
}
}
} }
} }

@ -216,7 +216,7 @@ public class FiltersController extends Controller implements ToolbarMenuItem.Too
if (filter.allBoards) { if (filter.allBoards) {
subText += context.getString(R.string.filter_summary_all_boards); subText += context.getString(R.string.filter_summary_all_boards);
} else { } else {
int size = filterEngine.getBoardsForFilter(filter).size(); int size = filterEngine.getFilterBoardCount(filter);
subText += context.getResources().getQuantityString(R.plurals.board, size, size); subText += context.getResources().getQuantityString(R.plurals.board, size, size);
} }

@ -73,4 +73,26 @@ public class BoardHelper {
name * 5 + name * 5 +
Math.max(0, description - 30) * 8; Math.max(0, description - 30) * 8;
} }
public static String boardUniqueId(Board board) {
String code = board.code.replace(":", "").replace(",", "");
return board.site.id() + ":" + code;
}
public static boolean matchesUniqueId(Board board, String uniqueId) {
if (!uniqueId.contains(":")) {
return board.site.id() == 0 && board.code.equals(uniqueId);
} else {
String[] splitted = uniqueId.split(":");
if (splitted.length != 2) {
return false;
}
try {
return Integer.parseInt(splitted[0]) == board.site.id() && splitted[1].equals(board.code);
} catch (NumberFormatException ignored) {
return false;
}
}
}
} }

@ -17,6 +17,7 @@
*/ */
package org.floens.chan.ui.layout; package org.floens.chan.ui.layout;
import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.graphics.Typeface; import android.graphics.Typeface;
@ -33,29 +34,23 @@ import android.util.AttributeSet;
import android.view.Gravity; import android.view.Gravity;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.SpinnerAdapter;
import android.widget.TextView; import android.widget.TextView;
import org.floens.chan.R; import org.floens.chan.R;
import org.floens.chan.core.manager.BoardManager; 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.manager.FilterType; import org.floens.chan.core.manager.FilterType;
import org.floens.chan.core.model.orm.Board;
import org.floens.chan.core.model.orm.Filter; import org.floens.chan.core.model.orm.Filter;
import org.floens.chan.core.site.Site;
import org.floens.chan.core.site.Sites;
import org.floens.chan.ui.controller.FiltersController; 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.helper.BoardHelper;
import org.floens.chan.ui.view.FloatingMenu; import org.floens.chan.ui.view.FloatingMenu;
import org.floens.chan.ui.view.FloatingMenuItem; import org.floens.chan.ui.view.FloatingMenuItem;
import org.floens.chan.utils.Logger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -90,8 +85,6 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener {
private FilterLayoutCallback callback; private FilterLayoutCallback callback;
private Filter filter; private Filter filter;
private List<FilterEngine.SiteIdBoardCode> appliedBoards = new ArrayList<>();
public FilterLayout(Context context) { public FilterLayout(Context context) {
super(context); super(context);
} }
@ -168,8 +161,6 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener {
public void setFilter(Filter filter) { public void setFilter(Filter filter) {
this.filter = filter; this.filter = filter;
appliedBoards.clear();
appliedBoards.addAll(filterEngine.getBoardsForFilter(filter));
pattern.setText(filter.pattern); pattern.setText(filter.pattern);
@ -188,16 +179,15 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener {
public Filter getFilter() { public Filter getFilter() {
filter.enabled = enabled.isChecked(); filter.enabled = enabled.isChecked();
filterEngine.saveBoardsToFilter(appliedBoards, filter);
return filter; return filter;
} }
@Override @Override
public void onClick(View v) { public void onClick(View v) {
if (v == typeText) { if (v == typeText) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked") final SelectLayout<FilterType> selectLayout =
final SelectLayout<FilterType> selectLayout = (SelectLayout<FilterType>) LayoutInflater.from(getContext()).inflate(R.layout.layout_select, null); (SelectLayout<FilterType>) LayoutInflater.from(getContext())
.inflate(R.layout.layout_select, null);
List<SelectLayout.SelectItem<FilterType>> items = new ArrayList<>(); List<SelectLayout.SelectItem<FilterType>> items = new ArrayList<>();
for (FilterType filterType : FilterType.values()) { for (FilterType filterType : FilterType.values()) {
@ -232,75 +222,44 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener {
}) })
.show(); .show();
} else if (v == boardsSelector) { } else if (v == boardsSelector) {
// TODO(multi-site): fix this crap. @SuppressLint("InflateParams") @SuppressWarnings("unchecked") final SelectLayout<Board> selectLayout =
// we need a new proper recyclerview layout where you can individually select each site and board combination (SelectLayout<Board>) LayoutInflater.from(getContext())
// and if you don't select anything, it becomes a global filter. .inflate(R.layout.layout_select, null);
final LinearLayout selectLayout = (LinearLayout) LayoutInflater.from(getContext())
.inflate(R.layout.layout_site_board_select, null);
final Spinner spinner = (Spinner) selectLayout.findViewById(R.id.progress); List<SelectLayout.SelectItem<Board>> items = new ArrayList<>();
final List<? extends Site> allSites = Sites.allSites(); for (Board board : boardManager.getSavedBoards()) {
String name = BoardHelper.getName(board);
final Site[] selectedSite = {allSites.get(0)}; boolean checked = filterEngine.matchesBoard(filter, board);
List<String> allSitesNames = new ArrayList<>(allSites.size());
for (int i = 0; i < allSites.size(); i++) {
Site site = allSites.get(i);
allSitesNames.add(site.name());
}
SpinnerAdapter adapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_list_item_1, allSitesNames); items.add(new SelectLayout.SelectItem<>(
spinner.setAdapter(adapter); board, board.id, name, "", name, checked
));
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
Site site = allSites.get(position);
selectedSite[0] = site;
Logger.test(site.name());
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
final EditText editText = (EditText) selectLayout.findViewById(R.id.boards);
String text = "";
for (int i = 0; i < appliedBoards.size(); i++) {
FilterEngine.SiteIdBoardCode siteIdBoardCode = appliedBoards.get(i);
text += siteIdBoardCode.boardCode;
if (i < appliedBoards.size() - 1) {
text += ",";
}
} }
editText.setText(text); selectLayout.setItems(items);
new AlertDialog.Builder(getContext()) new AlertDialog.Builder(getContext())
.setView(selectLayout) .setView(selectLayout)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
Site site = selectedSite[0]; List<SelectLayout.SelectItem<Board>> items = selectLayout.getItems();
boolean all = selectLayout.areAllChecked();
appliedBoards.clear(); List<Board> boardList = new ArrayList<>(items.size());
if (!all) {
String[] codes = editText.getText().toString().split(","); for (SelectLayout.SelectItem<Board> item : items) {
if (codes.length == 0) { if (item.checked) {
filter.allBoards = true; boardList.add(item.item);
} else { }
filter.allBoards = false; }
for (String code : codes) { if (boardList.isEmpty()) {
appliedBoards.add(FilterEngine.SiteIdBoardCode.fromSiteIdBoardCode( all = true;
site.id(), code
));
} }
} }
filterEngine.saveBoardsToFilter(boardList, all, filter);
updateBoardsSummary(); updateBoardsSummary();
} }
}) })
@ -392,7 +351,7 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener {
if (filter.allBoards) { if (filter.allBoards) {
text += getString(R.string.filter_all); text += getString(R.string.filter_all);
} else { } else {
text += String.valueOf(appliedBoards.size()); text += filterEngine.getFilterBoardCount(filter);
} }
text += ")"; text += ")";
boardsSelector.setText(text); boardsSelector.setText(text);

@ -1,36 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
Clover - 4chan browser https://github.com/Floens/Clover/
Copyright (C) 2014 Floens
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="16dp"
android:paddingRight="16dp">
<Spinner
android:id="@+id/progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<EditText
android:id="@+id/boards"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
Loading…
Cancel
Save