Start of filtering

filtering
Floens 10 years ago
parent 17705278ab
commit c98d3565d1
  1. 15
      Clover/app/src/main/java/org/floens/chan/core/database/DatabaseManager.java
  2. 47
      Clover/app/src/main/java/org/floens/chan/core/manager/FilterEngine.java
  3. 15
      Clover/app/src/main/java/org/floens/chan/core/model/Filter.java
  4. 8
      Clover/app/src/main/java/org/floens/chan/ui/controller/BoardEditController.java
  5. 230
      Clover/app/src/main/java/org/floens/chan/ui/controller/FiltersController.java
  6. 6
      Clover/app/src/main/java/org/floens/chan/ui/controller/MainSettingsController.java
  7. 2
      Clover/app/src/main/java/org/floens/chan/ui/controller/ThemeSettingsController.java
  8. 8
      Clover/app/src/main/java/org/floens/chan/ui/drawable/DropdownArrowDrawable.java
  9. 122
      Clover/app/src/main/java/org/floens/chan/ui/layout/FilterLayout.java
  10. 79
      Clover/app/src/main/res/layout/cell_filter.xml
  11. 3
      Clover/app/src/main/res/layout/cell_history.xml
  12. 2
      Clover/app/src/main/res/layout/controller_board_edit.xml
  13. 41
      Clover/app/src/main/res/layout/controller_filters.xml
  14. 2
      Clover/app/src/main/res/layout/controller_theme.xml
  15. 46
      Clover/app/src/main/res/layout/layout_filter.xml
  16. 15
      Clover/app/src/main/res/values/strings.xml

@ -25,6 +25,7 @@ import com.j256.ormlite.stmt.QueryBuilder;
import com.j256.ormlite.table.TableUtils;
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.Loadable;
import org.floens.chan.core.model.Pin;
@ -68,6 +69,8 @@ public class DatabaseManager {
private final Object historyLock = new Object();
private final HashMap<Loadable, History> historyByLoadable = new HashMap<>();
private final List<Filter> filters = new ArrayList<>();
public DatabaseManager(Context context) {
helper = new DatabaseHelper(context);
initialize();
@ -281,6 +284,18 @@ public class DatabaseManager {
return list;
}
public void addFilter(Filter filter) {
filters.add(filter);
}
public void removeFilter(Filter filter) {
filters.remove(filter);
}
public List<Filter> getFilters() {
return filters;
}
/**
* Create or updates these boards in the boards table.
*

@ -0,0 +1,47 @@
package org.floens.chan.core.manager;
import org.floens.chan.core.model.Filter;
import java.util.ArrayList;
import java.util.List;
public class FilterEngine {
public enum FilterType {
TRIPCODE(0),
NAME(1),
COMMENT(2),
ID(3),
SUBJECT(4),
FILENAME(5);
public final int id;
FilterType(int id) {
this.id = id;
}
public static FilterType forId(int id) {
for (FilterType type : values()) {
if (type.id == id) {
return type;
}
}
return null;
}
}
private static final FilterEngine instance = new FilterEngine();
public static FilterEngine getInstance() {
return instance;
}
private List<Filter> filters = new ArrayList<>();
public FilterEngine() {
}
public void add(Filter filter) {
}
}

@ -0,0 +1,15 @@
package org.floens.chan.core.model;
public class Filter {
public int id;
public boolean enabled = true;
public int type;
public String pattern;
public String boards;
public boolean hide = true;
}

@ -68,7 +68,7 @@ public class BoardEditController extends Controller implements SwipeListener.Cal
private RecyclerView recyclerView;
private BoardEditAdapter adapter;
private FloatingActionButton done;
private FloatingActionButton add;
private List<Board> boards;
@ -90,8 +90,8 @@ public class BoardEditController extends Controller implements SwipeListener.Cal
view = inflateRes(R.layout.controller_board_edit);
recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
recyclerView.setHasFixedSize(true);
done = (FloatingActionButton) view.findViewById(R.id.done);
done.setOnClickListener(this);
add = (FloatingActionButton) view.findViewById(R.id.add);
add.setOnClickListener(this);
boards = boardManager.getSavedBoards();
@ -131,7 +131,7 @@ public class BoardEditController extends Controller implements SwipeListener.Cal
@Override
public void onClick(View v) {
if (v == done) {
if (v == add) {
showAddBoardDialog();
}
}

@ -0,0 +1,230 @@
/*
* 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/>.
*/
package org.floens.chan.ui.controller;
import android.content.Context;
import android.content.DialogInterface;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import org.floens.chan.Chan;
import org.floens.chan.R;
import org.floens.chan.controller.Controller;
import org.floens.chan.core.database.DatabaseManager;
import org.floens.chan.core.model.Filter;
import org.floens.chan.ui.layout.FilterLayout;
import org.floens.chan.ui.toolbar.ToolbarMenu;
import org.floens.chan.ui.toolbar.ToolbarMenuItem;
import org.floens.chan.ui.view.FloatingMenuItem;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import static org.floens.chan.ui.theme.ThemeHelper.theme;
public class FiltersController extends Controller implements ToolbarMenuItem.ToolbarMenuItemCallback, RootNavigationController.ToolbarSearchCallback, View.OnClickListener {
private static final int SEARCH_ID = 1;
private static final int CLEAR_ID = 101;
private DatabaseManager databaseManager;
private RecyclerView recyclerView;
private FloatingActionButton add;
private FilterAdapter adapter;
public FiltersController(Context context) {
super(context);
}
@Override
public void onCreate() {
super.onCreate();
databaseManager = Chan.getDatabaseManager();
navigationItem.title = string(R.string.filters_screen);
navigationItem.menu = new ToolbarMenu(context);
navigationItem.menu.addItem(new ToolbarMenuItem(context, this, SEARCH_ID, R.drawable.ic_search_white_24dp));
view = inflateRes(R.layout.controller_filters);
recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(context));
add = (FloatingActionButton) view.findViewById(R.id.add);
add.setOnClickListener(this);
adapter = new FilterAdapter();
recyclerView.setAdapter(adapter);
adapter.load();
}
@Override
public void onClick(View v) {
if (v == add) {
showFilterDialog(new Filter());
}
}
@Override
public void onMenuItemClicked(ToolbarMenuItem item) {
if ((Integer) item.getId() == SEARCH_ID) {
navigationController.showSearch();
}
}
@Override
public void onSubMenuItemClicked(ToolbarMenuItem parent, FloatingMenuItem item) {
}
private void showFilterDialog(Filter filter) {
final FilterLayout filterLayout = (FilterLayout) LayoutInflater.from(context).inflate(R.layout.layout_filter, null);
new AlertDialog.Builder(context)
.setView(filterLayout)
.setPositiveButton(R.string.save, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
filterLayout.save();
adapter.load();
}
})
.show();
filterLayout.setFilter(filter);
}
private void deleteFilter(Filter filter) {
databaseManager.removeFilter(filter);
adapter.load();
//TODO: undo
}
@Override
public void onSearchVisibilityChanged(boolean visible) {
if (!visible) {
adapter.search(null);
}
}
@Override
public void onSearchEntered(String entered) {
adapter.search(entered);
}
private class FilterAdapter extends RecyclerView.Adapter<FilterCell> {
private List<Filter> sourceList = new ArrayList<>();
private List<Filter> displayList = new ArrayList<>();
private String searchQuery;
public FilterAdapter() {
setHasStableIds(true);
}
@Override
public FilterCell onCreateViewHolder(ViewGroup parent, int viewType) {
return new FilterCell(LayoutInflater.from(parent.getContext()).inflate(R.layout.cell_filter, parent, false));
}
@Override
public void onBindViewHolder(FilterCell holder, int position) {
Filter filter = displayList.get(position);
holder.text.setText(filter.pattern);
holder.subtext.setText(String.valueOf(filter.type));
}
@Override
public int getItemCount() {
return displayList.size();
}
@Override
public long getItemId(int position) {
return displayList.get(position).id;
}
public void search(String query) {
this.searchQuery = query;
filter();
}
private void load() {
sourceList.clear();
sourceList.addAll(databaseManager.getFilters());
filter();
}
private void filter() {
displayList.clear();
if (!TextUtils.isEmpty(searchQuery)) {
String query = searchQuery.toLowerCase(Locale.ENGLISH);
for (Filter filter : sourceList) {
displayList.add(filter);
}
} else {
displayList.addAll(sourceList);
}
notifyDataSetChanged();
}
}
private class FilterCell extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView text;
private TextView subtext;
private ImageView delete;
public FilterCell(View itemView) {
super(itemView);
text = (TextView) itemView.findViewById(R.id.text);
subtext = (TextView) itemView.findViewById(R.id.subtext);
delete = (ImageView) itemView.findViewById(R.id.delete);
theme().clearDrawable.apply(delete);
delete.setOnClickListener(this);
itemView.setOnClickListener(this);
}
@Override
public void onClick(View v) {
int position = getAdapterPosition();
if (position >= 0 && position < adapter.getItemCount()) {
Filter filter = adapter.displayList.get(position);
if (v == itemView) {
showFilterDialog(filter);
} else if (v == delete) {
deleteFilter(filter);
}
}
}
}
}

@ -159,6 +159,12 @@ public class MainSettingsController extends SettingsController implements Toolba
// Browsing group
SettingsGroup browsing = new SettingsGroup(s(R.string.settings_group_browsing));
browsing.add(new LinkSettingView(this, s(R.string.filters_screen), null, new View.OnClickListener() {
@Override
public void onClick(View v) {
navigationController.pushController(new FiltersController(context));
}
}));
browsing.add(new BooleanSettingView(this, ChanSettings.openLinkConfirmation, s(R.string.setting_open_link_confirmation), null));
browsing.add(new BooleanSettingView(this, ChanSettings.autoRefreshThread, s(R.string.setting_auto_refresh_thread), null));

@ -120,7 +120,7 @@ public class ThemeSettingsController extends Controller implements View.OnClickL
themeHelper = ThemeHelper.getInstance();
pager = (ViewPager) view.findViewById(R.id.pager);
done = (FloatingActionButton) view.findViewById(R.id.done);
done = (FloatingActionButton) view.findViewById(R.id.add);
done.setOnClickListener(this);
adapter = new Adapter();

@ -47,10 +47,10 @@ public class DropdownArrowDrawable extends Drawable {
@Override
public void draw(Canvas canvas) {
path.rewind();
path.moveTo(0, height / 2);
path.lineTo(width, height / 2);
path.lineTo(width / 2, height);
path.lineTo(0, height / 2);
path.moveTo(0, height / 4);
path.lineTo(width, height / 4);
path.lineTo(width / 2, (int) (height * 3f / 4f));
path.lineTo(0, height / 4);
path.close();
canvas.save();

@ -0,0 +1,122 @@
package org.floens.chan.ui.layout;
import android.content.Context;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.floens.chan.Chan;
import org.floens.chan.R;
import org.floens.chan.core.manager.FilterEngine;
import org.floens.chan.core.model.Filter;
import org.floens.chan.ui.drawable.DropdownArrowDrawable;
import org.floens.chan.ui.view.FloatingMenu;
import org.floens.chan.ui.view.FloatingMenuItem;
import java.util.ArrayList;
import java.util.List;
import static org.floens.chan.utils.AndroidUtils.dp;
import static org.floens.chan.utils.AndroidUtils.getAttrColor;
import static org.floens.chan.utils.AndroidUtils.getString;
public class FilterLayout extends LinearLayout implements View.OnClickListener, FloatingMenu.FloatingMenuCallback {
private TextView typeText;
private TextView boards;
private TextView pattern;
private CheckBox hide;
private Filter filter;
public FilterLayout(Context context) {
super(context);
}
public FilterLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FilterLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
typeText = (TextView) findViewById(R.id.type);
boards = (TextView) findViewById(R.id.boards);
pattern = (TextView) findViewById(R.id.pattern);
hide = (CheckBox) findViewById(R.id.hide);
typeText.setOnClickListener(this);
typeText.setCompoundDrawablesWithIntrinsicBounds(null, null, new DropdownArrowDrawable(dp(12), dp(12), true,
getAttrColor(getContext(), R.attr.dropdown_dark_color), getAttrColor(getContext(), R.attr.dropdown_dark_pressed_color)), null);
}
public void setFilter(Filter filter) {
this.filter = filter;
pattern.setText(filter.pattern);
boards.setText(filter.boards);
hide.setChecked(filter.hide);
typeText.setText(filterTypeName(FilterEngine.FilterType.forId(filter.type)));
}
public void save() {
filter.pattern = pattern.getText().toString();
filter.boards = boards.getText().toString();
filter.hide = hide.isChecked();
Chan.getDatabaseManager().addFilter(filter);
}
@Override
public void onClick(View v) {
if (v == typeText) {
List<FloatingMenuItem> menuItems = new ArrayList<>(2);
for (FilterEngine.FilterType filterType : FilterEngine.FilterType.values()) {
menuItems.add(new FloatingMenuItem(filterType, filterTypeName(filterType)));
}
FloatingMenu menu = new FloatingMenu(v.getContext());
menu.setAnchor(v, Gravity.LEFT, -dp(5), -dp(5));
menu.setPopupWidth(dp(150));
menu.setCallback(this);
menu.setItems(menuItems);
menu.show();
}
}
@Override
public void onFloatingMenuItemClicked(FloatingMenu menu, FloatingMenuItem item) {
FilterEngine.FilterType type = (FilterEngine.FilterType) item.getId();
typeText.setText(filterTypeName(type));
filter.type = type.id;
}
@Override
public void onFloatingMenuDismissed(FloatingMenu menu) {
}
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;
}
}

@ -0,0 +1,79 @@
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/item_background"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="8dp"
android:singleLine="true"
android:textColor="?text_color_primary"
android:textSize="14sp" />
<TextView
android:id="@+id/subtext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="8dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:textColor="?text_color_secondary"
android:textSize="12sp" />
</LinearLayout>
<ImageView
android:id="@+id/delete"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center_vertical"
android:paddingBottom="4dp"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:paddingTop="4dp"
tools:ignore="ContentDescription" />
</LinearLayout>
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?attr/divider_color" />
</LinearLayout>

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

@ -30,7 +30,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
android:scrollbars="vertical" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/done"
android:id="@+id/add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|bottom"

@ -0,0 +1,41 @@
<?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/>.
-->
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?backcolor">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:padding="16dp"
android:paddingBottom="72dp"
android:scrollbarStyle="outsideOverlay"
android:scrollbars="vertical" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|bottom"
android:layout_margin="16dp"
android:src="@drawable/ic_add_white_24dp" />
</android.support.design.widget.CoordinatorLayout>

@ -45,7 +45,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</LinearLayout>
<android.support.design.widget.FloatingActionButton
android:id="@+id/done"
android:id="@+id/add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|bottom"

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<org.floens.chan.ui.layout.FilterLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="8dp"
android:paddingBottom="4dp"
android:paddingLeft="4dp"
android:paddingTop="4dp" />
<EditText
android:id="@+id/boards"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:layout_weight="1"
android:hint="@string/filter_boards"
android:textSize="14sp" />
</LinearLayout>
<CheckBox
android:id="@+id/hide"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/filter_hide" />
<EditText
android:id="@+id/pattern"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/filter_pattern"
android:textSize="14sp" />
</org.floens.chan.ui.layout.FilterLayout>

@ -26,6 +26,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<string name="exit">Exit</string>
<string name="delete">Delete</string>
<string name="undo">Undo</string>
<string name="save">Save</string>
<plurals name="minutes">
<item quantity="one">%d minute</item>
@ -116,6 +117,18 @@ 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_edit_sort_a_z">Sort A-Z</string>
<string name="filter_enabled">Enabled</string>
<string name="filter_pattern">Pattern</string>
<string name="filter_boards">Boards</string>
<string name="filter_hide">Hide</string>
<string name="filter_tripcode">Tripcode</string>
<string name="filter_name">Name</string>
<string name="filter_comment">Comment</string>
<string name="filter_id">ID</string>
<string name="filter_subject">Subject</string>
<string name="filter_filename">Filename</string>
<string name="history_clear">Clear history</string>
<string name="history_clear_confirm">Clear history?</string>
<string name="history_clear_confirm_button">Clear</string>
@ -190,6 +203,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<string name="history_screen">History</string>
<string name="filters_screen">Filters</string>
<string name="settings_screen">Settings</string>
<string name="settings_group_general">General</string>
<string name="settings_board_edit">Boards</string>

Loading…
Cancel
Save