Do searches and ordering through PostFilter

filtering
Floens 10 years ago
parent 797832c9e0
commit 05275eb7bf
  1. 79
      Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java
  2. 4
      Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java
  3. 81
      Clover/app/src/main/java/org/floens/chan/ui/adapter/PostAdapter.java
  4. 144
      Clover/app/src/main/java/org/floens/chan/ui/adapter/PostFilter.java
  5. 10
      Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java
  6. 9
      Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadLayout.java
  7. 20
      Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadListLayout.java
  8. 4
      Clover/app/src/main/java/org/floens/chan/ui/toolbar/Toolbar.java

@ -39,6 +39,7 @@ import org.floens.chan.core.net.LoaderPool;
import org.floens.chan.core.settings.ChanSettings;
import org.floens.chan.database.DatabaseManager;
import org.floens.chan.ui.adapter.PostAdapter;
import org.floens.chan.ui.adapter.PostFilter;
import org.floens.chan.ui.cell.PostCellInterface;
import org.floens.chan.ui.cell.ThreadStatusCell;
import org.floens.chan.ui.helper.PostHelper;
@ -49,7 +50,6 @@ import org.floens.chan.utils.AndroidUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import static org.floens.chan.utils.AndroidUtils.getString;
@ -76,7 +76,8 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt
private Loadable loadable;
private ChanLoader chanLoader;
private boolean searchOpen = false;
private Order order = Order.BUMP;
private String searchQuery;
private PostFilter.Order order = PostFilter.Order.BUMP;
public ThreadPresenter(ThreadPresenterCallback threadPresenterCallback) {
this.threadPresenterCallback = threadPresenterCallback;
@ -110,7 +111,7 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt
LoaderPool.getInstance().release(chanLoader, this);
chanLoader = null;
loadable = null;
order = Order.BUMP;
order = PostFilter.Order.BUMP;
threadPresenterCallback.showLoading();
}
@ -154,26 +155,32 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt
public void onSearchVisibilityChanged(boolean visible) {
searchOpen = visible;
threadPresenterCallback.showSearch(visible);
if (!visible) {
searchQuery = null;
}
showPosts();
}
public void onSearchEntered(String entered) {
if (chanLoader.getThread() != null) {
searchQuery = entered;
showPosts();
if (TextUtils.isEmpty(entered)) {
threadPresenterCallback.filterList(null, null, true, true, false);
threadPresenterCallback.setSearchStatus(null, true, false);
} else {
processSearch(chanLoader.getThread().posts, entered);
threadPresenterCallback.setSearchStatus(entered, false, false);
}
}
}
public void setOrder(Order order) {
public void setOrder(PostFilter.Order order) {
if (this.order != order) {
this.order = order;
if (chanLoader != null) {
ChanThread thread = chanLoader.getThread();
if (thread != null) {
threadPresenterCallback.scrollTo(0, false);
threadPresenterCallback.showPosts(thread, order);
showPosts();
}
}
}
@ -198,7 +205,7 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt
}
chanLoader.setAutoLoadMore(isWatching());
threadPresenterCallback.showPosts(result, order);
showPosts();
if (loadable.markedNo >= 0) {
Post markedPost = findPostById(loadable.markedNo);
@ -281,7 +288,9 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt
threadPresenterCallback.showThread(threadLoadable);
} else {
if (searchOpen) {
threadPresenterCallback.filterList(null, null, true, false, true);
searchQuery = null;
showPosts();
threadPresenterCallback.setSearchStatus(null, false, true);
highlightPost(post);
scrollToPost(post, false);
} else {
@ -532,56 +541,12 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt
return null;
}
private void processSearch(List<Post> all, String originalQuery) {
List<Post> filtered = new ArrayList<>();
String query = originalQuery.toLowerCase(Locale.ENGLISH);
boolean add;
for (Post item : all) {
add = false;
if (item.comment.toString().toLowerCase(Locale.ENGLISH).contains(query)) {
add = true;
} else if (item.subject.toLowerCase(Locale.ENGLISH).contains(query)) {
add = true;
} else if (item.name.toLowerCase(Locale.ENGLISH).contains(query)) {
add = true;
} else if (item.filename != null && item.filename.toLowerCase(Locale.ENGLISH).contains(query)) {
add = true;
}
if (add) {
filtered.add(item);
}
}
threadPresenterCallback.filterList(originalQuery, filtered, false, false, false);
}
public enum Order {
BUMP("bump"),
REPLY("reply"),
IMAGE("image"),
NEWEST("newest"),
OLDEST("oldest");
public String name;
Order(String storeName) {
this.name = storeName;
}
public static Order find(String name) {
for (Order mode : Order.values()) {
if (mode.name.equals(name)) {
return mode;
}
}
return null;
}
private void showPosts() {
threadPresenterCallback.showPosts(chanLoader.getThread(), new PostFilter(order, searchQuery));
}
public interface ThreadPresenterCallback {
void showPosts(ChanThread thread, Order order);
void showPosts(ChanThread thread, PostFilter filter);
void postClicked(Post post);
@ -615,7 +580,7 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt
void showSearch(boolean show);
void filterList(String query, List<Post> filter, boolean clearFilter, boolean setEmptyText, boolean hideKeyboard);
void setSearchStatus(String query, boolean setEmptyText, boolean hideKeyboard);
void quote(Post post, boolean withText);

@ -23,7 +23,7 @@ import android.os.Environment;
import org.floens.chan.Chan;
import org.floens.chan.R;
import org.floens.chan.chan.ChanUrls;
import org.floens.chan.core.presenter.ThreadPresenter;
import org.floens.chan.ui.adapter.PostFilter;
import org.floens.chan.ui.cell.PostCellInterface;
import org.floens.chan.utils.AndroidUtils;
@ -84,7 +84,7 @@ public class ChanSettings {
videoOpenExternal = new BooleanSetting(p, "preference_video_external", 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"
boardOrder = new StringSetting(p, "preference_board_order", ThreadPresenter.Order.BUMP.name);
boardOrder = new StringSetting(p, "preference_board_order", PostFilter.Order.BUMP.name);
postDefaultName = new StringSetting(p, "preference_default_name", "");
postPinThread = new BooleanSetting(p, "preference_pin_on_post", false);

@ -26,13 +26,11 @@ import org.floens.chan.R;
import org.floens.chan.core.model.ChanThread;
import org.floens.chan.core.model.Loadable;
import org.floens.chan.core.model.Post;
import org.floens.chan.core.presenter.ThreadPresenter;
import org.floens.chan.ui.cell.PostCellInterface;
import org.floens.chan.ui.cell.ThreadStatusCell;
import org.floens.chan.utils.Logger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class PostAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
@ -53,7 +51,6 @@ public class PostAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private String highlightedPostId;
private int highlightedPostNo = -1;
private String highlightedPostTripcode;
private boolean filtering = false;
private PostCellInterface.PostViewMode postViewMode;
@ -133,27 +130,27 @@ public class PostAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
}
}
public void setThread(ChanThread thread, ThreadPresenter.Order order) {
public void setThread(ChanThread thread, PostFilter filter) {
showError(null);
sourceList.clear();
sourceList.addAll(thread.posts);
orderPosts(sourceList, order);
if (!filtering) {
displayList.clear();
displayList.addAll(sourceList);
}
displayList.addAll(filter.apply(sourceList));
// Update all, recyclerview will figure out all the animations
notifyDataSetChanged();
}
public int getDisplaySize() {
return displayList.size();
}
public void cleanup() {
highlightedPost = null;
highlightedPostId = null;
highlightedPostNo = -1;
highlightedPostTripcode = null;
filtering = false;
lastPostCount = 0;
}
@ -170,33 +167,6 @@ public class PostAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
}
}
public void filterList(List<Post> filter) {
filtering = true;
displayList.clear();
for (Post item : sourceList) {
for (Post filterItem : filter) {
if (filterItem.no == item.no) {
displayList.add(item);
break;
}
}
}
notifyDataSetChanged();
}
public void clearFilter() {
if (filtering) {
filtering = false;
displayList.clear();
displayList.addAll(sourceList);
notifyDataSetChanged();
}
}
public void highlightPost(Post post) {
highlightedPost = post;
highlightedPostId = null;
@ -234,9 +204,10 @@ public class PostAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
}
private void onScrolledToBottom() {
if (!filtering && lastPostCount != sourceList.size()) {
if (lastPostCount < sourceList.size()) {
lastPostCount = sourceList.size();
postAdapterCallback.onListScrolledToBottom();
Logger.test("onScrolledToBottom");
}
}
@ -244,40 +215,6 @@ public class PostAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
return postAdapterCallback.getLoadable().isThreadMode();
}
private void orderPosts(List<Post> posts, ThreadPresenter.Order order) {
if (order != ThreadPresenter.Order.BUMP) {
if (order == ThreadPresenter.Order.IMAGE) {
Collections.sort(posts, new Comparator<Post>() {
@Override
public int compare(Post lhs, Post rhs) {
return rhs.images - lhs.images;
}
});
} else if (order == ThreadPresenter.Order.REPLY) {
Collections.sort(posts, new Comparator<Post>() {
@Override
public int compare(Post lhs, Post rhs) {
return rhs.replies - lhs.replies;
}
});
} else if (order == ThreadPresenter.Order.NEWEST) {
Collections.sort(posts, new Comparator<Post>() {
@Override
public int compare(Post lhs, Post rhs) {
return (int) (rhs.time - lhs.time);
}
});
} else if (order == ThreadPresenter.Order.OLDEST) {
Collections.sort(posts, new Comparator<Post>() {
@Override
public int compare(Post lhs, Post rhs) {
return (int) (lhs.time - rhs.time);
}
});
}
}
}
public static class PostViewHolder extends RecyclerView.ViewHolder {
private PostCellInterface postView;

@ -0,0 +1,144 @@
/*
* 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.adapter;
import android.text.TextUtils;
import org.floens.chan.core.model.Post;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
public class PostFilter {
public static final Comparator<Post> IMAGE_COMPARATOR = new Comparator<Post>() {
@Override
public int compare(Post lhs, Post rhs) {
return rhs.images - lhs.images;
}
};
public static final Comparator<Post> REPLY_COMPARATOR = new Comparator<Post>() {
@Override
public int compare(Post lhs, Post rhs) {
return rhs.replies - lhs.replies;
}
};
public static final Comparator<Post> NEWEST_COMPARATOR = new Comparator<Post>() {
@Override
public int compare(Post lhs, Post rhs) {
return (int) (rhs.time - lhs.time);
}
};
public static final Comparator<Post> OLDEST_COMPARATOR = new Comparator<Post>() {
@Override
public int compare(Post lhs, Post rhs) {
return (int) (lhs.time - rhs.time);
}
};
private Order order;
private String query;
public PostFilter(Order order, String query) {
this.order = order;
this.query = query;
}
/**
* Creates a copy of {@code original} and applies any sorting or filtering to it.
*
* @param original List of {@link Post}s to filter.
* @return a new filtered List
*/
public List<Post> apply(List<Post> original) {
List<Post> posts = new ArrayList<>(original);
// Process order
if (order != PostFilter.Order.BUMP) {
switch (order) {
case IMAGE:
Collections.sort(posts, IMAGE_COMPARATOR);
break;
case REPLY:
Collections.sort(posts, REPLY_COMPARATOR);
break;
case NEWEST:
Collections.sort(posts, NEWEST_COMPARATOR);
break;
case OLDEST:
Collections.sort(posts, OLDEST_COMPARATOR);
break;
}
}
// Process search
if (!TextUtils.isEmpty(query)) {
String lowerQuery = query.toLowerCase(Locale.ENGLISH);
boolean add;
Iterator<Post> i = posts.iterator();
while (i.hasNext()) {
Post item = i.next();
add = false;
if (item.comment.toString().toLowerCase(Locale.ENGLISH).contains(lowerQuery)) {
add = true;
} else if (item.subject.toLowerCase(Locale.ENGLISH).contains(lowerQuery)) {
add = true;
} else if (item.name.toLowerCase(Locale.ENGLISH).contains(lowerQuery)) {
add = true;
} else if (item.filename != null && item.filename.toLowerCase(Locale.ENGLISH).contains(lowerQuery)) {
add = true;
}
if (!add) {
i.remove();
}
}
}
return posts;
}
public enum Order {
BUMP("bump"),
REPLY("reply"),
IMAGE("image"),
NEWEST("newest"),
OLDEST("oldest");
public String name;
Order(String storeName) {
this.name = storeName;
}
public static Order find(String name) {
for (Order mode : Order.values()) {
if (mode.name.equals(name)) {
return mode;
}
}
return null;
}
}
}

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

@ -51,6 +51,7 @@ import org.floens.chan.core.model.PostImage;
import org.floens.chan.core.model.PostLinkable;
import org.floens.chan.core.presenter.ThreadPresenter;
import org.floens.chan.core.settings.ChanSettings;
import org.floens.chan.ui.adapter.PostFilter;
import org.floens.chan.ui.cell.PostCellInterface;
import org.floens.chan.ui.helper.PostPopupHelper;
import org.floens.chan.ui.view.LoadView;
@ -170,8 +171,8 @@ public class ThreadLayout extends CoordinatorLayout implements ThreadPresenter.T
}
@Override
public void showPosts(ChanThread thread, ThreadPresenter.Order order) {
threadListLayout.showPosts(thread, order, visible != Visible.THREAD);
public void showPosts(ChanThread thread, PostFilter filter) {
threadListLayout.showPosts(thread, filter, visible != Visible.THREAD);
switchVisible(Visible.THREAD);
callback.onShowPosts();
}
@ -303,8 +304,8 @@ public class ThreadLayout extends CoordinatorLayout implements ThreadPresenter.T
threadListLayout.showSearch(show);
}
public void filterList(String query, List<Post> filter, boolean clearFilter, boolean setEmptyText, boolean hideKeyboard) {
threadListLayout.filterList(query, filter, clearFilter, setEmptyText, hideKeyboard);
public void setSearchStatus(String query, boolean setEmptyText, boolean hideKeyboard) {
threadListLayout.setSearchStatus(query, setEmptyText, hideKeyboard);
}
@Override

@ -32,8 +32,8 @@ import org.floens.chan.core.model.Loadable;
import org.floens.chan.core.model.Post;
import org.floens.chan.core.model.PostImage;
import org.floens.chan.core.presenter.ReplyPresenter;
import org.floens.chan.core.presenter.ThreadPresenter;
import org.floens.chan.ui.adapter.PostAdapter;
import org.floens.chan.ui.adapter.PostFilter;
import org.floens.chan.ui.cell.PostCell;
import org.floens.chan.ui.cell.PostCellInterface;
import org.floens.chan.ui.cell.ThreadStatusCell;
@ -41,8 +41,6 @@ import org.floens.chan.ui.view.ThumbnailView;
import org.floens.chan.utils.AndroidUtils;
import org.floens.chan.utils.AnimationUtils;
import java.util.List;
import static org.floens.chan.utils.AndroidUtils.ROBOTO_MEDIUM;
import static org.floens.chan.utils.AndroidUtils.dp;
import static org.floens.chan.utils.AndroidUtils.getAttrColor;
@ -164,7 +162,7 @@ public class ThreadListLayout extends LinearLayout implements ReplyLayout.ReplyL
}
}
public void showPosts(ChanThread thread, ThreadPresenter.Order order, boolean initial) {
public void showPosts(ChanThread thread, PostFilter filter, boolean initial) {
showingThread = thread;
if (initial) {
reply.bindLoadable(showingThread.loadable);
@ -195,7 +193,7 @@ public class ThreadListLayout extends LinearLayout implements ReplyLayout.ReplyL
}
}
postAdapter.setThread(thread, order);
postAdapter.setThread(thread, filter);
}
public boolean onBack() {
@ -235,16 +233,10 @@ public class ThreadListLayout extends LinearLayout implements ReplyLayout.ReplyL
if (show) {
searchStatus.setText(R.string.search_empty);
} else {
postAdapter.clearFilter();
}
}
public void filterList(String query, List<Post> filter, boolean clearFilter, boolean setEmptyText, boolean hideKeyboard) {
if (clearFilter) {
postAdapter.clearFilter();
}
public void setSearchStatus(String query, boolean setEmptyText, boolean hideKeyboard) {
if (hideKeyboard) {
AndroidUtils.hideKeyboard(this);
}
@ -254,9 +246,9 @@ public class ThreadListLayout extends LinearLayout implements ReplyLayout.ReplyL
}
if (query != null) {
postAdapter.filterList(filter);
int size = postAdapter.getDisplaySize();
searchStatus.setText(getContext().getString(R.string.search_results,
getContext().getResources().getQuantityString(R.plurals.posts, filter.size(), filter.size()), query));
getContext().getResources().getQuantityString(R.plurals.posts, size, size), query));
}
}

@ -346,13 +346,13 @@ public class Toolbar extends LinearLayout implements View.OnClickListener, LoadV
if (openKeyboardAfterSearchViewCreated) {
openKeyboardAfterSearchViewCreated = false;
searchView.post(new Runnable() {
searchView.postDelayed(new Runnable() {
@Override
public void run() {
searchView.requestFocus();
AndroidUtils.requestKeyboardFocus(searchView);
}
});
}, 100);
}
return searchViewWrapper;

Loading…
Cancel
Save