diff --git a/Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java b/Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java index 3247a81e..37f6a086 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java +++ b/Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.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 all, String originalQuery) { - List 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 filter, boolean clearFilter, boolean setEmptyText, boolean hideKeyboard); + void setSearchStatus(String query, boolean setEmptyText, boolean hideKeyboard); void quote(Post post, boolean withText); diff --git a/Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java b/Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java index a5336aa3..aa949380 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java +++ b/Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java @@ -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); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/adapter/PostAdapter.java b/Clover/app/src/main/java/org/floens/chan/ui/adapter/PostAdapter.java index c81a83e9..4d53f67f 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/adapter/PostAdapter.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/adapter/PostAdapter.java @@ -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 { @@ -53,7 +51,6 @@ public class PostAdapter extends RecyclerView.Adapter { 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 { } } - 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.clear(); + 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 { } } - public void filterList(List 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 { } 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 { return postAdapterCallback.getLoadable().isThreadMode(); } - private void orderPosts(List posts, ThreadPresenter.Order order) { - if (order != ThreadPresenter.Order.BUMP) { - if (order == ThreadPresenter.Order.IMAGE) { - Collections.sort(posts, new Comparator() { - @Override - public int compare(Post lhs, Post rhs) { - return rhs.images - lhs.images; - } - }); - } else if (order == ThreadPresenter.Order.REPLY) { - Collections.sort(posts, new Comparator() { - @Override - public int compare(Post lhs, Post rhs) { - return rhs.replies - lhs.replies; - } - }); - } else if (order == ThreadPresenter.Order.NEWEST) { - Collections.sort(posts, new Comparator() { - @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() { - @Override - public int compare(Post lhs, Post rhs) { - return (int) (lhs.time - rhs.time); - } - }); - } - } - } - public static class PostViewHolder extends RecyclerView.ViewHolder { private PostCellInterface postView; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/adapter/PostFilter.java b/Clover/app/src/main/java/org/floens/chan/ui/adapter/PostFilter.java new file mode 100644 index 00000000..9e9d1267 --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/ui/adapter/PostFilter.java @@ -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 . + */ +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 IMAGE_COMPARATOR = new Comparator() { + @Override + public int compare(Post lhs, Post rhs) { + return rhs.images - lhs.images; + } + }; + + public static final Comparator REPLY_COMPARATOR = new Comparator() { + @Override + public int compare(Post lhs, Post rhs) { + return rhs.replies - lhs.replies; + } + }; + + public static final Comparator NEWEST_COMPARATOR = new Comparator() { + @Override + public int compare(Post lhs, Post rhs) { + return (int) (rhs.time - lhs.time); + } + }; + + public static final Comparator OLDEST_COMPARATOR = new Comparator() { + @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 apply(List original) { + List 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 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; + } + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java index 0504bf2d..646edd3a 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java @@ -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 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 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); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadLayout.java b/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadLayout.java index f2983e2e..7db1db84 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadLayout.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadLayout.java @@ -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 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 diff --git a/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadListLayout.java b/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadListLayout.java index 4f47e5a8..e4c102f8 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadListLayout.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadListLayout.java @@ -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 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)); } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/Toolbar.java b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/Toolbar.java index fb919b13..9c9d1fc6 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/Toolbar.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/Toolbar.java @@ -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;