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)}.
*/
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
*/
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
* 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.
*
* @param v
@ -234,7 +234,7 @@ public class SimpleDragSortCursorAdapter extends ResourceDragSortCursorAdapter {
* no existing ViewBinder or if the existing ViewBinder cannot handle
* 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.
*
* @param v

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

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

@ -38,6 +38,7 @@ import android.view.ViewGroup.LayoutParams;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.FrameLayout;
import android.widget.SearchView;
import android.widget.Spinner;
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.loader.Loader;
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.Pin;
import org.floens.chan.core.model.Post;
@ -68,6 +68,8 @@ public class BoardActivity extends BaseActivity implements AdapterView.OnItemSel
private boolean ignoreNextOnItemSelected = false;
private Spinner boardSpinner;
private BoardSpinnerAdapter spinnerAdapter;
private MenuItem searchMenuItem;
private boolean searchBoard;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -186,6 +188,38 @@ public class BoardActivity extends BaseActivity implements AdapterView.OnItemSel
public boolean onCreateOptionsMenu(Menu 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;
}
@ -411,6 +445,11 @@ public class BoardActivity extends BaseActivity implements AdapterView.OnItemSel
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);
}
@ -485,6 +524,14 @@ public class BoardActivity extends BaseActivity implements AdapterView.OnItemSel
startLoadingBoard(boardLoadable);
}
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:
threadPane.openPane();
@ -517,7 +564,12 @@ public class BoardActivity extends BaseActivity implements AdapterView.OnItemSel
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();
updateActionBarState();
@ -545,6 +597,32 @@ public class BoardActivity extends BaseActivity implements AdapterView.OnItemSel
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.
*

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

@ -18,9 +18,11 @@
package org.floens.chan.ui.fragment;
import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@ -29,6 +31,7 @@ import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
@ -52,7 +55,7 @@ import org.floens.chan.utils.Utils;
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 ThreadManager threadManager;
private Loadable loadable;
@ -61,9 +64,13 @@ public class ThreadFragment extends Fragment implements ThreadManager.ThreadMana
private LoadView container;
private AbsListView listView;
private ImageView skip;
private FilterView filterView;
private SkipLogic skipLogic;
private int highlightedPost = -1;
private ThreadManager.ViewMode viewMode = ThreadManager.ViewMode.LIST;
private String lastFilter = "";
private boolean isFiltering = false;
public static ThreadFragment newInstance(BaseActivity activity) {
ThreadFragment fragment = new ThreadFragment();
@ -112,6 +119,13 @@ public class ThreadFragment extends Fragment implements ThreadManager.ThreadMana
return threadManager.getLoader();
}
public void setFilter(String filter) {
if (!filter.equals(lastFilter) && postAdapter != null) {
lastFilter = filter;
postAdapter.setFilter(filter);
}
}
@Override
public void onDestroy() {
super.onDestroy();
@ -193,10 +207,17 @@ public class ThreadFragment extends Fragment implements ThreadManager.ThreadMana
if (postAdapter == null) {
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) {
ListView list = new ListView(baseActivity);
listView = list;
postAdapter = new PostAdapter(baseActivity, threadManager, listView);
postAdapter = new PostAdapter(baseActivity, threadManager, listView, this);
listView.setAdapter(postAdapter);
list.setSelectionFromTop(loadable.listViewIndex, loadable.listViewTop);
} else if (viewMode == ThreadManager.ViewMode.GRID) {
@ -210,7 +231,7 @@ public class ThreadFragment extends Fragment implements ThreadManager.ThreadMana
grid.setVerticalSpacing(postGridSpacing);
grid.setHorizontalSpacing(postGridSpacing);
listView = grid;
postAdapter = new PostAdapter(baseActivity, threadManager, listView);
postAdapter = new PostAdapter(baseActivity, threadManager, listView, this);
listView.setAdapter(postAdapter);
listView.setSelection(loadable.listViewIndex);
}
@ -218,25 +239,35 @@ public class ThreadFragment extends Fragment implements ThreadManager.ThreadMana
listView.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (skipLogic != null) {
skipLogic.onScrollStateChanged(view, scrollState);
if (!isFiltering) {
if (skipLogic != null) {
skipLogic.onScrollStateChanged(view, scrollState);
}
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (loadable != null) {
loadable.listViewIndex = view.getFirstVisiblePosition();
View v = view.getChildAt(0);
loadable.listViewTop = (v == null) ? 0 : v.getTop();
}
if (skipLogic != null) {
skipLogic.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
if (!isFiltering) {
if (loadable != null) {
int index = view.getFirstVisiblePosition();
View v = view.getChildAt(0);
int top = v == null ? 0 : v.getTop();
if (index != 0 || top != 0) {
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()) {
skip = new ImageView(baseActivity);
@ -292,6 +323,19 @@ public class ThreadFragment extends Fragment implements ThreadManager.ThreadMana
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() {
postAdapter = null;
@ -400,4 +444,35 @@ public class ThreadFragment extends Fragment implements ThreadManager.ThreadMana
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/
Copyright (C) 2014 Floens
@ -26,10 +25,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<menu>
<item
android:id="@+id/action_reload_tablet_board"
android:title="@string/action_reload_board"/>
android:title="@string/action_reload_board" />
<item
android:id="@+id/action_reload_tablet_thread"
android:title="@string/action_reload_thread"/>
android:title="@string/action_reload_thread" />
</menu>
</item>
@ -38,21 +37,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
android:icon="@drawable/ic_action_refresh"
android:orderInCategory="1"
android:showAsAction="always"
android:title="@string/action_reload_board"/>
android:title="@string/action_reload_board" />
<item
android:id="@+id/action_pin"
android:icon="@drawable/ic_action_make_available_offline"
android:orderInCategory="2"
android:showAsAction="always"
android:title="@string/action_pin"/>
android:title="@string/action_pin" />
<item
android:id="@+id/action_reply"
android:icon="@drawable/ic_action_chat"
android:orderInCategory="4"
android:showAsAction="ifRoom"
android:title="@string/action_reply"/>
android:title="@string/action_reply" />
<item
android:id="@+id/action_reply_tablet"
@ -63,10 +62,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<menu>
<item
android:id="@+id/action_reply_board"
android:title="@string/action_reply_board"/>
android:title="@string/action_reply_board" />
<item
android:id="@+id/action_reply_thread"
android:title="@string/action_reply_thread"/>
android:title="@string/action_reply_thread" />
</menu>
</item>
@ -76,34 +75,57 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
android:icon="@drawable/ic_action_refresh"
android:orderInCategory="5"
android:showAsAction="never"
android:title="@string/action_reload"/>
android:title="@string/action_reload" />
<item
android:id="@+id/action_share"
android:id="@+id/action_search"
android:actionViewClass="android.widget.SearchView"
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:title="@string/action_share"
android:actionProviderClass="android.widget.ShareActionProvider"/>
android:title="@string/action_share" />
<item
android:id="@+id/action_open_browser"
android:orderInCategory="7"
android:orderInCategory="8"
android:showAsAction="never"
android:title="@string/action_open_browser"/>
android:title="@string/action_open_browser" />
<item
android:id="@+id/action_board_view_mode"
android:orderInCategory="8"
android:orderInCategory="9"
android:showAsAction="never"
android:title="@string/action_board_view_mode">
<menu>
<group android:checkableBehavior="single">
<item
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
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>
</menu>
</item>
@ -112,6 +134,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
android:id="@+id/action_settings"
android:orderInCategory="100"
android:showAsAction="never"
android:title="@string/action_settings"/>
android:title="@string/action_settings" />
</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_grid">Grid</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">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="one_image">image</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-array name="post_options">
<item>Quick reply</item>

Loading…
Cancel
Save