Added search:

Added UI for search, normal SearchView + a bar at the top telling how many results were found.
Added filtering support for PostAdapter.
captchafix
Florens Douwes 11 years ago
parent eb7f854dbf
commit 0f34890000
  1. 4
      Clover/app/src/main/java/com/android/volley/RequestQueue.java
  2. 4
      Clover/app/src/main/java/com/mobeta/android/dslv/SimpleDragSortCursorAdapter.java
  3. 7
      Clover/app/src/main/java/org/floens/chan/core/loader/Loader.java
  4. 14
      Clover/app/src/main/java/org/floens/chan/ui/activity/BaseActivity.java
  5. 82
      Clover/app/src/main/java/org/floens/chan/ui/activity/BoardActivity.java
  6. 275
      Clover/app/src/main/java/org/floens/chan/ui/adapter/PostAdapter.java
  7. 101
      Clover/app/src/main/java/org/floens/chan/ui/fragment/ThreadFragment.java
  8. 60
      Clover/app/src/main/res/menu/base.xml
  9. 8
      Clover/app/src/main/res/values/strings.xml

@ -171,7 +171,7 @@ public class RequestQueue {
} }
/** /**
* A simple predicate or filter interface for Requests, for use by * A simple predicate or setFilter interface for Requests, for use by
* {@link RequestQueue#cancelAll(RequestFilter)}. * {@link RequestQueue#cancelAll(RequestFilter)}.
*/ */
public interface RequestFilter { public interface RequestFilter {
@ -179,7 +179,7 @@ public class RequestQueue {
} }
/** /**
* Cancels all requests in this queue for which the given filter applies. * Cancels all requests in this queue for which the given setFilter applies.
* @param filter The filtering function to use * @param filter The filtering function to use
*/ */
public void cancelAll(RequestFilter filter) { public void cancelAll(RequestFilter filter) {

@ -213,7 +213,7 @@ public class SimpleDragSortCursorAdapter extends ResourceDragSortCursorAdapter {
* By default, the value will be treated as an image resource. If the value * By default, the value will be treated as an image resource. If the value
* cannot be used as an image resource, the value is used as an image Uri. * cannot be used as an image resource, the value is used as an image Uri.
* *
* Intended to be overridden by Adapters that need to filter strings * Intended to be overridden by Adapters that need to setFilter strings
* retrieved from the database. * retrieved from the database.
* *
* @param v * @param v
@ -234,7 +234,7 @@ public class SimpleDragSortCursorAdapter extends ResourceDragSortCursorAdapter {
* no existing ViewBinder or if the existing ViewBinder cannot handle * no existing ViewBinder or if the existing ViewBinder cannot handle
* binding to a TextView. * binding to a TextView.
* *
* Intended to be overridden by Adapters that need to filter strings * Intended to be overridden by Adapters that need to setFilter strings
* retrieved from the database. * retrieved from the database.
* *
* @param v * @param v

@ -143,12 +143,13 @@ public class Loader {
clearTimer(); clearTimer();
if (loadable.isBoardMode()) { if (loadable.isBoardMode()) {
loadable.no++;
if (request != null) { if (request != null) {
request.cancel(); // finish the last board load first
return;
} }
loadable.no++;
request = getData(); request = getData();
} else if (loadable.isThreadMode()) { } else if (loadable.isThreadMode()) {
if (request != null) { if (request != null) {

@ -240,13 +240,13 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene
private void onPinLongPress(final Pin pin) { private void onPinLongPress(final Pin pin) {
new AlertDialog.Builder(this) new AlertDialog.Builder(this)
.setNegativeButton(R.string.drawer_pinned_delete, new DialogInterface.OnClickListener() { .setNegativeButton(R.string.drawer_pinned_delete, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
// Delete pin // Delete pin
removePin(pin); removePin(pin);
} }
}).setPositiveButton(R.string.drawer_pinned_change_title, new DialogInterface.OnClickListener() { }).setPositiveButton(R.string.drawer_pinned_change_title, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
// Change pin title // Change pin title

@ -38,6 +38,7 @@ import android.view.ViewGroup.LayoutParams;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.BaseAdapter; import android.widget.BaseAdapter;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.SearchView;
import android.widget.Spinner; import android.widget.Spinner;
import android.widget.TextView; import android.widget.TextView;
@ -47,7 +48,6 @@ import org.floens.chan.chan.ChanUrls;
import org.floens.chan.core.ChanPreferences; import org.floens.chan.core.ChanPreferences;
import org.floens.chan.core.loader.Loader; import org.floens.chan.core.loader.Loader;
import org.floens.chan.core.manager.ThreadManager; import org.floens.chan.core.manager.ThreadManager;
import org.floens.chan.core.manager.WatchManager;
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.model.Post; import org.floens.chan.core.model.Post;
@ -68,6 +68,8 @@ public class BoardActivity extends BaseActivity implements AdapterView.OnItemSel
private boolean ignoreNextOnItemSelected = false; private boolean ignoreNextOnItemSelected = false;
private Spinner boardSpinner; private Spinner boardSpinner;
private BoardSpinnerAdapter spinnerAdapter; private BoardSpinnerAdapter spinnerAdapter;
private MenuItem searchMenuItem;
private boolean searchBoard;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -186,6 +188,38 @@ public class BoardActivity extends BaseActivity implements AdapterView.OnItemSel
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu); super.onCreateOptionsMenu(menu);
searchMenuItem = menu.findItem(R.id.action_search);
SearchView searchView = (SearchView) searchMenuItem.getActionView();
searchView.setQueryHint(getString(R.string.search_hint));
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
doSearch(query);
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
doSearch(newText);
return false;
}
});
searchMenuItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
return true;
}
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
threadFragment.setFilter("");
boardFragment.setFilter("");
return true;
}
});
return true; return true;
} }
@ -411,6 +445,11 @@ public class BoardActivity extends BaseActivity implements AdapterView.OnItemSel
menu.findItem(R.id.action_board_view_mode_grid).setChecked(true); menu.findItem(R.id.action_board_view_mode_grid).setChecked(true);
} }
setMenuItemEnabled(menu.findItem(R.id.action_search), slidable);
setMenuItemEnabled(menu.findItem(R.id.action_search_tablet), !slidable);
showSearch(false);
return super.onPrepareOptionsMenu(menu); return super.onPrepareOptionsMenu(menu);
} }
@ -485,6 +524,14 @@ public class BoardActivity extends BaseActivity implements AdapterView.OnItemSel
startLoadingBoard(boardLoadable); startLoadingBoard(boardLoadable);
} }
return true; return true;
case R.id.action_search_board:
showSearch(true);
searchBoard = true;
return true;
case R.id.action_search_thread:
showSearch(true);
searchBoard = false;
return true;
case android.R.id.home: case android.R.id.home:
threadPane.openPane(); threadPane.openPane();
@ -517,7 +564,12 @@ public class BoardActivity extends BaseActivity implements AdapterView.OnItemSel
boardLoadable.mode = Loadable.Mode.BOARD; boardLoadable.mode = Loadable.Mode.BOARD;
} }
boardFragment.bindLoadable(loadable); // Force catalog mode when using grid
if (boardFragment.getViewMode() == ThreadManager.ViewMode.GRID) {
boardLoadable.mode = Loadable.Mode.CATALOG;
}
boardFragment.bindLoadable(boardLoadable);
boardFragment.requestData(); boardFragment.requestData();
updateActionBarState(); updateActionBarState();
@ -545,6 +597,32 @@ public class BoardActivity extends BaseActivity implements AdapterView.OnItemSel
updateActionBarState(); updateActionBarState();
} }
private void showSearch(boolean show) {
if (searchMenuItem != null) {
if (show) {
searchMenuItem.expandActionView();
} else {
searchMenuItem.collapseActionView();
}
}
}
private void doSearch(String filter) {
if (threadPane.isSlideable()) {
if (threadPane.isOpen()) {
boardFragment.setFilter(filter);
} else {
threadFragment.setFilter(filter);
}
} else {
if (searchBoard) {
boardFragment.setFilter(filter);
} else {
threadFragment.setFilter(filter);
}
}
}
/** /**
* Handle opening from an external url. * Handle opening from an external url.
* *

@ -19,12 +19,15 @@ package org.floens.chan.ui.adapter;
import android.content.Context; import android.content.Context;
import android.os.Handler; import android.os.Handler;
import android.text.TextUtils;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.Gravity; import android.view.Gravity;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.AbsListView; import android.widget.AbsListView;
import android.widget.BaseAdapter; import android.widget.BaseAdapter;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.TextView; import android.widget.TextView;
@ -41,29 +44,46 @@ import org.floens.chan.utils.Utils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale;
public class PostAdapter extends BaseAdapter { public class PostAdapter extends BaseAdapter implements Filterable {
private static final int VIEW_TYPE_ITEM = 0; private static final int VIEW_TYPE_ITEM = 0;
private static final int VIEW_TYPE_STATUS = 1; private static final int VIEW_TYPE_STATUS = 1;
private final Object lock = new Object();
private final Context context; private final Context context;
private final ThreadManager threadManager;
private final AbsListView listView; private final AbsListView listView;
private final ThreadManager threadManager;
private final PostAdapterListener listener;
/**
* The list with the original data
*/
private final List<Post> sourceList = new ArrayList<>();
/**
* The list that is displayed (filtered)
*/
private final List<Post> displayList = new ArrayList<>();
private boolean endOfLine; private boolean endOfLine;
private final List<Post> postList = new ArrayList<>();
private int lastPostCount = 0; private int lastPostCount = 0;
private long lastViewedTime = 0; private long lastViewedTime = 0;
private String loadMessage = null; private String loadMessage = null;
private String filter = "";
public PostAdapter(Context activity, ThreadManager threadManager, AbsListView listView) { public PostAdapter(Context activity, ThreadManager threadManager, AbsListView listView, PostAdapterListener listener) {
context = activity; context = activity;
this.threadManager = threadManager; this.threadManager = threadManager;
this.listView = listView; this.listView = listView;
this.listener = listener;
} }
@Override @Override
public int getCount() { public int getCount() {
return postList.size() + (showStatusView() ? 1 : 0); return displayList.size() + (showStatusView() ? 1 : 0);
} }
@Override @Override
@ -73,8 +93,8 @@ public class PostAdapter extends BaseAdapter {
@Override @Override
public int getItemViewType(int position) { public int getItemViewType(int position) {
if (showStatusView()) { if (position == getCount() - 1) {
return position == getCount() - 1 ? VIEW_TYPE_STATUS : VIEW_TYPE_ITEM; return showStatusView() ? VIEW_TYPE_STATUS : VIEW_TYPE_ITEM;
} else { } else {
return VIEW_TYPE_ITEM; return VIEW_TYPE_ITEM;
} }
@ -82,8 +102,9 @@ public class PostAdapter extends BaseAdapter {
@Override @Override
public Post getItem(int position) { public Post getItem(int position) {
if (position >= 0 && position < postList.size()) { int realPosition = position;
return postList.get(position); if (realPosition >= 0 && realPosition < displayList.size()) {
return displayList.get(realPosition);
} else { } else {
return null; return null;
} }
@ -102,7 +123,7 @@ public class PostAdapter extends BaseAdapter {
switch (getItemViewType(position)) { switch (getItemViewType(position)) {
case VIEW_TYPE_ITEM: { case VIEW_TYPE_ITEM: {
if (convertView == null || convertView.getTag() == null && (Integer) convertView.getTag() != VIEW_TYPE_ITEM) { if (convertView == null || convertView.getTag() == null || (Integer) convertView.getTag() != VIEW_TYPE_ITEM) {
convertView = new PostView(context); convertView = new PostView(context);
convertView.setTag(VIEW_TYPE_ITEM); convertView.setTag(VIEW_TYPE_ITEM);
} }
@ -120,50 +141,79 @@ public class PostAdapter extends BaseAdapter {
return null; return null;
} }
private void onGetBottomView() { public Filter getFilter() {
if (threadManager.getLoadable().isBoardMode() && !endOfLine) { return new Filter() {
// Try to load more posts @Override
threadManager.requestNextData(); protected FilterResults performFiltering(CharSequence constraintRaw) {
} FilterResults results = new FilterResults();
if (lastPostCount != postList.size()) { if (TextUtils.isEmpty(constraintRaw)) {
lastPostCount = postList.size(); ArrayList<Post> tmp;
lastViewedTime = Time.get(); synchronized (lock) {
} tmp = new ArrayList<>(sourceList);
}
results.values = tmp;
} else {
List<Post> all;
synchronized (lock) {
all = new ArrayList<>(sourceList);
}
if (Time.get(lastViewedTime) > 1000L) { List<Post> accepted = new ArrayList<>();
lastViewedTime = Time.get(); String constraint = constraintRaw.toString().toLowerCase(Locale.ENGLISH);
threadManager.bottomPostViewed();
}
}
private boolean showStatusView() { for (Post post : all) {
Loadable l = threadManager.getLoadable(); if (post.comment.toString().toLowerCase(Locale.ENGLISH).contains(constraint)) {
if (l != null) { accepted.add(post);
if (l.isBoardMode()) { }
return true; }
} else if (l.isThreadMode() && threadManager.shouldWatch()) {
return true; results.values = accepted;
} else { }
return false;
return results;
} }
} else {
return false; @Override
} protected void publishResults(CharSequence constraint, final FilterResults results) {
filter = constraint.toString();
synchronized (lock) {
displayList.clear();
displayList.addAll((List<Post>) results.values);
}
notifyDataSetChanged();
listener.onFilterResults(filter, ((List<Post>) results.values).size(), TextUtils.isEmpty(filter));
}
};
}
public void setFilter(String filter) {
getFilter().filter(filter);
notifyDataSetChanged();
} }
public void appendList(List<Post> list) { public void appendList(List<Post> list) {
for (Post post : list) { synchronized (lock) {
boolean flag = true; boolean flag;
for (Post own : postList) { for (Post post : list) {
if (post.no == own.no) { flag = true;
flag = false; for (Post own : sourceList) {
break; if (post.no == own.no) {
flag = false;
break;
}
}
if (flag) {
sourceList.add(post);
} }
} }
if (flag) { if (!isFiltering()) {
postList.add(post); displayList.clear();
displayList.addAll(sourceList);
} else {
setFilter(filter);
} }
} }
@ -171,14 +221,23 @@ public class PostAdapter extends BaseAdapter {
} }
public void setList(List<Post> list) { public void setList(List<Post> list) {
postList.clear(); synchronized (lock) {
postList.addAll(list); sourceList.clear();
sourceList.addAll(list);
if (!isFiltering()) {
displayList.clear();
displayList.addAll(sourceList);
} else {
setFilter(filter);
}
}
notifyDataSetChanged(); notifyDataSetChanged();
} }
public List<Post> getList() { public List<Post> getList() {
return postList; return sourceList;
} }
public void setEndOfLine(boolean endOfLine) { public void setEndOfLine(boolean endOfLine) {
@ -190,16 +249,18 @@ public class PostAdapter extends BaseAdapter {
public void scrollToPost(int no) { public void scrollToPost(int no) {
notifyDataSetChanged(); notifyDataSetChanged();
for (int i = 0; i < postList.size(); i++) { synchronized (lock) {
if (postList.get(i).no == no) { for (int i = 0; i < displayList.size(); i++) {
if (Math.abs(i - listView.getFirstVisiblePosition()) > 20 || listView.getChildCount() == 0) { if (displayList.get(i).no == no) {
listView.setSelection(i); if (Math.abs(i - listView.getFirstVisiblePosition()) > 20 || listView.getChildCount() == 0) {
} else { listView.setSelection(i);
ScrollerRunnable r = new ScrollerRunnable(listView); } else {
r.start(i); ScrollerRunnable r = new ScrollerRunnable(listView);
} r.start(i);
}
break; break;
}
} }
} }
} }
@ -212,6 +273,40 @@ public class PostAdapter extends BaseAdapter {
return loadMessage; return loadMessage;
} }
private void onGetBottomView() {
if (threadManager.getLoadable().isBoardMode() && !endOfLine) {
// Try to load more posts
threadManager.requestNextData();
}
if (lastPostCount != sourceList.size()) {
lastPostCount = sourceList.size();
lastViewedTime = Time.get();
}
if (Time.get(lastViewedTime) > 1000L) {
lastViewedTime = Time.get();
threadManager.bottomPostViewed();
}
}
private boolean showStatusView() {
Loadable l = threadManager.getLoadable();
if (l != null) {
return l.isBoardMode() || l.isThreadMode();
} else {
return false;
}
}
private boolean isFiltering() {
return !TextUtils.isEmpty(filter);
}
public interface PostAdapterListener {
public void onFilterResults(String filter, int count, boolean all);
}
public class StatusView extends LinearLayout { public class StatusView extends LinearLayout {
boolean detached = false; boolean detached = false;
@ -232,47 +327,53 @@ public class PostAdapter extends BaseAdapter {
public void init() { public void init() {
Loader loader = threadManager.getLoader(); Loader loader = threadManager.getLoader();
if (loader == null) if (loader == null || loader.getLoadable() == null)
return; return;
setGravity(Gravity.CENTER); setGravity(Gravity.CENTER);
if (threadManager.shouldWatch()) { Loadable loadable = loader.getLoadable();
String error = getErrorMessage();
if (error != null) {
setText(error);
} else {
long time = loader.getTimeUntilLoadMore() / 1000L;
if (time == 0) {
setText("Loading");
} else {
setText("Loading in " + time);
}
}
new Handler().postDelayed(new Runnable() { if (loadable.isThreadMode()) {
@Override if (threadManager.shouldWatch()) {
public void run() { String error = getErrorMessage();
if (!detached) { if (error != null) {
notifyDataSetChanged(); setText(error);
} else {
long time = loader.getTimeUntilLoadMore() / 1000L;
if (time == 0) {
setText("Loading");
} else {
setText("Loading in " + time);
} }
} }
}, 1000);
new Handler().postDelayed(new Runnable() {
setOnClickListener(new OnClickListener() { @Override
@Override public void run() {
public void onClick(View v) { if (!detached) {
Loader loader = threadManager.getLoader(); notifyDataSetChanged();
if (loader != null) { }
loader.requestMoreDataAndResetTimer();
} }
}, 1000);
notifyDataSetChanged(); setOnClickListener(new OnClickListener() {
} @Override
}); public void onClick(View v) {
Loader loader = threadManager.getLoader();
if (loader != null) {
loader.requestMoreDataAndResetTimer();
}
Utils.setPressedDrawable(this); notifyDataSetChanged();
} else { }
});
Utils.setPressedDrawable(this);
} else {
setText("");
}
} else if (loadable.isBoardMode()) {
if (endOfLine) { if (endOfLine) {
setText(context.getString(R.string.thread_load_end_of_line)); setText(context.getString(R.string.thread_load_end_of_line));
} else { } else {

@ -18,9 +18,11 @@
package org.floens.chan.ui.fragment; package org.floens.chan.ui.fragment;
import android.app.Fragment; import android.app.Fragment;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.os.Bundle; import android.os.Bundle;
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;
@ -29,6 +31,7 @@ import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener; import android.widget.AbsListView.OnScrollListener;
import android.widget.GridView; import android.widget.GridView;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView; import android.widget.ListView;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
@ -52,7 +55,7 @@ import org.floens.chan.utils.Utils;
import java.util.List; import java.util.List;
public class ThreadFragment extends Fragment implements ThreadManager.ThreadManagerListener { public class ThreadFragment extends Fragment implements ThreadManager.ThreadManagerListener, PostAdapter.PostAdapterListener {
private BaseActivity baseActivity; private BaseActivity baseActivity;
private ThreadManager threadManager; private ThreadManager threadManager;
private Loadable loadable; private Loadable loadable;
@ -61,9 +64,13 @@ public class ThreadFragment extends Fragment implements ThreadManager.ThreadMana
private LoadView container; private LoadView container;
private AbsListView listView; private AbsListView listView;
private ImageView skip; private ImageView skip;
private FilterView filterView;
private SkipLogic skipLogic; private SkipLogic skipLogic;
private int highlightedPost = -1; private int highlightedPost = -1;
private ThreadManager.ViewMode viewMode = ThreadManager.ViewMode.LIST; private ThreadManager.ViewMode viewMode = ThreadManager.ViewMode.LIST;
private String lastFilter = "";
private boolean isFiltering = false;
public static ThreadFragment newInstance(BaseActivity activity) { public static ThreadFragment newInstance(BaseActivity activity) {
ThreadFragment fragment = new ThreadFragment(); ThreadFragment fragment = new ThreadFragment();
@ -112,6 +119,13 @@ public class ThreadFragment extends Fragment implements ThreadManager.ThreadMana
return threadManager.getLoader(); return threadManager.getLoader();
} }
public void setFilter(String filter) {
if (!filter.equals(lastFilter) && postAdapter != null) {
lastFilter = filter;
postAdapter.setFilter(filter);
}
}
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
@ -193,10 +207,17 @@ public class ThreadFragment extends Fragment implements ThreadManager.ThreadMana
if (postAdapter == null) { if (postAdapter == null) {
RelativeLayout compound = new RelativeLayout(baseActivity); RelativeLayout compound = new RelativeLayout(baseActivity);
LinearLayout listViewContainer = new LinearLayout(baseActivity);
listViewContainer.setOrientation(LinearLayout.VERTICAL);
filterView = new FilterView(baseActivity);
filterView.setVisibility(View.GONE);
listViewContainer.addView(filterView, Utils.MATCH_WRAP_PARAMS);
if (viewMode == ThreadManager.ViewMode.LIST) { if (viewMode == ThreadManager.ViewMode.LIST) {
ListView list = new ListView(baseActivity); ListView list = new ListView(baseActivity);
listView = list; listView = list;
postAdapter = new PostAdapter(baseActivity, threadManager, listView); postAdapter = new PostAdapter(baseActivity, threadManager, listView, this);
listView.setAdapter(postAdapter); listView.setAdapter(postAdapter);
list.setSelectionFromTop(loadable.listViewIndex, loadable.listViewTop); list.setSelectionFromTop(loadable.listViewIndex, loadable.listViewTop);
} else if (viewMode == ThreadManager.ViewMode.GRID) { } else if (viewMode == ThreadManager.ViewMode.GRID) {
@ -210,7 +231,7 @@ public class ThreadFragment extends Fragment implements ThreadManager.ThreadMana
grid.setVerticalSpacing(postGridSpacing); grid.setVerticalSpacing(postGridSpacing);
grid.setHorizontalSpacing(postGridSpacing); grid.setHorizontalSpacing(postGridSpacing);
listView = grid; listView = grid;
postAdapter = new PostAdapter(baseActivity, threadManager, listView); postAdapter = new PostAdapter(baseActivity, threadManager, listView, this);
listView.setAdapter(postAdapter); listView.setAdapter(postAdapter);
listView.setSelection(loadable.listViewIndex); listView.setSelection(loadable.listViewIndex);
} }
@ -218,25 +239,35 @@ public class ThreadFragment extends Fragment implements ThreadManager.ThreadMana
listView.setOnScrollListener(new OnScrollListener() { listView.setOnScrollListener(new OnScrollListener() {
@Override @Override
public void onScrollStateChanged(AbsListView view, int scrollState) { public void onScrollStateChanged(AbsListView view, int scrollState) {
if (skipLogic != null) { if (!isFiltering) {
skipLogic.onScrollStateChanged(view, scrollState); if (skipLogic != null) {
skipLogic.onScrollStateChanged(view, scrollState);
}
} }
} }
@Override @Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (loadable != null) { if (!isFiltering) {
loadable.listViewIndex = view.getFirstVisiblePosition(); if (loadable != null) {
View v = view.getChildAt(0); int index = view.getFirstVisiblePosition();
loadable.listViewTop = (v == null) ? 0 : v.getTop(); View v = view.getChildAt(0);
} int top = v == null ? 0 : v.getTop();
if (skipLogic != null) { if (index != 0 || top != 0) {
skipLogic.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); loadable.listViewIndex = index;
loadable.listViewTop = top;
}
}
if (skipLogic != null) {
skipLogic.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
} }
} }
}); });
compound.addView(listView, Utils.MATCH_PARAMS); listViewContainer.addView(listView, Utils.MATCH_PARAMS);
compound.addView(listViewContainer, Utils.MATCH_PARAMS);
if (loadable.isThreadMode()) { if (loadable.isThreadMode()) {
skip = new ImageView(baseActivity); skip = new ImageView(baseActivity);
@ -292,6 +323,19 @@ public class ThreadFragment extends Fragment implements ThreadManager.ThreadMana
highlightedPost = -1; highlightedPost = -1;
} }
public void onFilterResults(String filter, int count, boolean all) {
isFiltering = !all;
if (filterView != null) {
if (all) {
filterView.setVisibility(View.GONE);
} else {
filterView.setVisibility(View.VISIBLE);
filterView.setText(filter, count);
}
}
}
private void setEmpty() { private void setEmpty() {
postAdapter = null; postAdapter = null;
@ -400,4 +444,35 @@ public class ThreadFragment extends Fragment implements ThreadManager.ThreadMana
up = false; up = false;
} }
} }
public class FilterView extends LinearLayout {
private TextView textView;
public FilterView(Context activity) {
super(activity);
init();
}
public FilterView(Context activity, AttributeSet attr) {
super(activity, attr);
init();
}
public FilterView(Context activity, AttributeSet attr, int style) {
super(activity, attr, style);
init();
}
private void init() {
textView = new TextView(getContext());
textView.setGravity(Gravity.CENTER);
addView(textView, new LayoutParams(LayoutParams.MATCH_PARENT, Utils.dp(48)));
}
private void setText(String filter, int count) {
String posts = getContext().getString(count == 1 ? R.string.one_post : R.string.multiple_posts);
String text = getContext().getString(R.string.search_results, Integer.toString(count), posts, filter);
textView.setText(text);
}
}
} }

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?><!--
<!--
Clover - 4chan browser https://github.com/Floens/Clover/ Clover - 4chan browser https://github.com/Floens/Clover/
Copyright (C) 2014 Floens Copyright (C) 2014 Floens
@ -26,10 +25,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<menu> <menu>
<item <item
android:id="@+id/action_reload_tablet_board" android:id="@+id/action_reload_tablet_board"
android:title="@string/action_reload_board"/> android:title="@string/action_reload_board" />
<item <item
android:id="@+id/action_reload_tablet_thread" android:id="@+id/action_reload_tablet_thread"
android:title="@string/action_reload_thread"/> android:title="@string/action_reload_thread" />
</menu> </menu>
</item> </item>
@ -38,21 +37,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
android:icon="@drawable/ic_action_refresh" android:icon="@drawable/ic_action_refresh"
android:orderInCategory="1" android:orderInCategory="1"
android:showAsAction="always" android:showAsAction="always"
android:title="@string/action_reload_board"/> android:title="@string/action_reload_board" />
<item <item
android:id="@+id/action_pin" android:id="@+id/action_pin"
android:icon="@drawable/ic_action_make_available_offline" android:icon="@drawable/ic_action_make_available_offline"
android:orderInCategory="2" android:orderInCategory="2"
android:showAsAction="always" android:showAsAction="always"
android:title="@string/action_pin"/> android:title="@string/action_pin" />
<item <item
android:id="@+id/action_reply" android:id="@+id/action_reply"
android:icon="@drawable/ic_action_chat" android:icon="@drawable/ic_action_chat"
android:orderInCategory="4" android:orderInCategory="4"
android:showAsAction="ifRoom" android:showAsAction="ifRoom"
android:title="@string/action_reply"/> android:title="@string/action_reply" />
<item <item
android:id="@+id/action_reply_tablet" android:id="@+id/action_reply_tablet"
@ -63,10 +62,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<menu> <menu>
<item <item
android:id="@+id/action_reply_board" android:id="@+id/action_reply_board"
android:title="@string/action_reply_board"/> android:title="@string/action_reply_board" />
<item <item
android:id="@+id/action_reply_thread" android:id="@+id/action_reply_thread"
android:title="@string/action_reply_thread"/> android:title="@string/action_reply_thread" />
</menu> </menu>
</item> </item>
@ -76,34 +75,57 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
android:icon="@drawable/ic_action_refresh" android:icon="@drawable/ic_action_refresh"
android:orderInCategory="5" android:orderInCategory="5"
android:showAsAction="never" android:showAsAction="never"
android:title="@string/action_reload"/> android:title="@string/action_reload" />
<item <item
android:id="@+id/action_share" android:id="@+id/action_search"
android:actionViewClass="android.widget.SearchView"
android:orderInCategory="6" android:orderInCategory="6"
android:showAsAction="never|collapseActionView"
android:title="@string/action_search" />
<item
android:id="@+id/action_search_tablet"
android:orderInCategory="6"
android:showAsAction="never"
android:title="@string/action_search">
<menu>
<item
android:id="@+id/action_search_board"
android:title="@string/action_search_board" />
<item
android:id="@+id/action_search_thread"
android:title="@string/action_search_thread" />
</menu>
</item>
<item
android:id="@+id/action_share"
android:actionProviderClass="android.widget.ShareActionProvider"
android:orderInCategory="7"
android:showAsAction="never" android:showAsAction="never"
android:title="@string/action_share" android:title="@string/action_share" />
android:actionProviderClass="android.widget.ShareActionProvider"/>
<item <item
android:id="@+id/action_open_browser" android:id="@+id/action_open_browser"
android:orderInCategory="7" android:orderInCategory="8"
android:showAsAction="never" android:showAsAction="never"
android:title="@string/action_open_browser"/> android:title="@string/action_open_browser" />
<item <item
android:id="@+id/action_board_view_mode" android:id="@+id/action_board_view_mode"
android:orderInCategory="8" android:orderInCategory="9"
android:showAsAction="never" android:showAsAction="never"
android:title="@string/action_board_view_mode"> android:title="@string/action_board_view_mode">
<menu> <menu>
<group android:checkableBehavior="single"> <group android:checkableBehavior="single">
<item <item
android:id="@+id/action_board_view_mode_list" android:id="@+id/action_board_view_mode_list"
android:title="@string/action_board_view_mode_list"/> android:title="@string/action_board_view_mode_list" />
<item <item
android:id="@+id/action_board_view_mode_grid" android:id="@+id/action_board_view_mode_grid"
android:title="@string/action_board_view_mode_grid"/> android:title="@string/action_board_view_mode_grid" />
</group> </group>
</menu> </menu>
</item> </item>
@ -112,6 +134,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
android:id="@+id/action_settings" android:id="@+id/action_settings"
android:orderInCategory="100" android:orderInCategory="100"
android:showAsAction="never" android:showAsAction="never"
android:title="@string/action_settings"/> android:title="@string/action_settings" />
</menu> </menu>

@ -41,6 +41,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<string name="action_board_view_mode_list">List</string> <string name="action_board_view_mode_list">List</string>
<string name="action_board_view_mode_grid">Grid</string> <string name="action_board_view_mode_grid">Grid</string>
<string name="action_settings_advanced">Advanced</string> <string name="action_settings_advanced">Advanced</string>
<string name="action_search">Search</string>
<string name="action_search_board">Search board</string>
<string name="action_search_thread">Search thread</string>
<string name="search_hint">Search posts</string>
<string name="search_results">Found %1$s %2$s for "%3$s"</string>
<string name="open_unknown_title">Unsupported link</string> <string name="open_unknown_title">Unsupported link</string>
<string name="open_unknown">Clover can\'t open this link. Opening it in your browser instead.</string> <string name="open_unknown">Clover can\'t open this link. Opening it in your browser instead.</string>
@ -81,6 +87,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<string name="multiple_replies">replies</string> <string name="multiple_replies">replies</string>
<string name="one_image">image</string> <string name="one_image">image</string>
<string name="multiple_images">images</string> <string name="multiple_images">images</string>
<string name="one_post">post</string>
<string name="multiple_posts">posts</string>
<string name="post_info">Info</string> <string name="post_info">Info</string>
<string-array name="post_options"> <string-array name="post_options">
<item>Quick reply</item> <item>Quick reply</item>

Loading…
Cancel
Save