mirror of https://github.com/kurisufriend/Clover
redo the browse controller middle menu to support multiple sites.multisite
parent
8a86b2d7c3
commit
99ce139bf8
@ -0,0 +1,131 @@ |
||||
/* |
||||
* 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.core.presenter; |
||||
|
||||
import android.util.Pair; |
||||
|
||||
import org.floens.chan.core.database.DatabaseManager; |
||||
import org.floens.chan.core.manager.BoardManager; |
||||
import org.floens.chan.core.model.orm.Board; |
||||
import org.floens.chan.core.model.orm.Loadable; |
||||
import org.floens.chan.core.site.Site; |
||||
|
||||
import java.util.List; |
||||
import java.util.Observable; |
||||
import java.util.Observer; |
||||
|
||||
import javax.inject.Inject; |
||||
|
||||
public class BrowsePresenter implements Observer { |
||||
private final DatabaseManager databaseManager; |
||||
private final BoardManager boardManager; |
||||
private Callback callback; |
||||
|
||||
private boolean hadBoards = false; |
||||
private Board currentBoard; |
||||
|
||||
private BoardManager.SavedBoards savedBoardsObservable; |
||||
|
||||
@Inject |
||||
public BrowsePresenter(DatabaseManager databaseManager, BoardManager boardManager) { |
||||
this.databaseManager = databaseManager; |
||||
this.boardManager = boardManager; |
||||
|
||||
savedBoardsObservable = boardManager.getSavedBoardsObservable(); |
||||
|
||||
hadBoards = hasBoards(); |
||||
} |
||||
|
||||
public void create(Callback callback) { |
||||
this.callback = callback; |
||||
|
||||
savedBoardsObservable.addObserver(this); |
||||
} |
||||
|
||||
public void destroy() { |
||||
savedBoardsObservable.deleteObserver(this); |
||||
} |
||||
|
||||
public Board currentBoard() { |
||||
return currentBoard; |
||||
} |
||||
|
||||
public void setBoard(Board board) { |
||||
loadBoard(board); |
||||
} |
||||
|
||||
public void onBoardsFloatingMenuBoardClicked(Board board) { |
||||
loadBoard(board); |
||||
} |
||||
|
||||
public void loadWithDefaultBoard() { |
||||
loadBoard(firstBoard()); |
||||
} |
||||
|
||||
public void onBoardsFloatingMenuSiteClicked(Site site) { |
||||
callback.loadSiteSetup(site); |
||||
} |
||||
|
||||
public BoardManager.SavedBoards getSavedBoardsObservable() { |
||||
return boardManager.getSavedBoardsObservable(); |
||||
} |
||||
|
||||
@Override |
||||
public void update(Observable o, Object arg) { |
||||
if (o == savedBoardsObservable) { |
||||
if (!hadBoards && hasBoards()) { |
||||
hadBoards = true; |
||||
loadWithDefaultBoard(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private boolean hasBoards() { |
||||
for (Pair<Site, List<Board>> siteListPair : savedBoardsObservable.get()) { |
||||
if (!siteListPair.second.isEmpty()) { |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
private Board firstBoard() { |
||||
for (Pair<Site, List<Board>> siteListPair : savedBoardsObservable.get()) { |
||||
if (!siteListPair.second.isEmpty()) { |
||||
return siteListPair.second.get(0); |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
private Loadable getLoadableForBoard(Board board) { |
||||
return databaseManager.getDatabaseLoadableManager().get(Loadable.forCatalog(board)); |
||||
} |
||||
|
||||
private void loadBoard(Board board) { |
||||
currentBoard = board; |
||||
callback.loadBoard(getLoadableForBoard(board)); |
||||
} |
||||
|
||||
public interface Callback { |
||||
void loadBoard(Loadable loadable); |
||||
|
||||
void loadSiteSetup(Site site); |
||||
} |
||||
} |
@ -1,478 +0,0 @@ |
||||
/* |
||||
* Clover - 4chan browser https://github.com/Floens/Clover/
|
||||
* Copyright (C) 2014 Floens |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
package org.floens.chan.ui.controller; |
||||
|
||||
import android.annotation.SuppressLint; |
||||
import android.content.Context; |
||||
import android.content.DialogInterface; |
||||
import android.support.design.widget.FloatingActionButton; |
||||
import android.support.design.widget.Snackbar; |
||||
import android.support.v7.app.AlertDialog; |
||||
import android.support.v7.widget.LinearLayoutManager; |
||||
import android.support.v7.widget.RecyclerView; |
||||
import android.support.v7.widget.helper.ItemTouchHelper; |
||||
import android.text.TextUtils; |
||||
import android.view.LayoutInflater; |
||||
import android.view.MotionEvent; |
||||
import android.view.View; |
||||
import android.view.ViewGroup; |
||||
import android.view.inputmethod.EditorInfo; |
||||
import android.widget.ArrayAdapter; |
||||
import android.widget.AutoCompleteTextView; |
||||
import android.widget.Filter; |
||||
import android.widget.Filterable; |
||||
import android.widget.ImageView; |
||||
import android.widget.LinearLayout; |
||||
import android.widget.TextView; |
||||
|
||||
import org.floens.chan.R; |
||||
import org.floens.chan.controller.Controller; |
||||
import org.floens.chan.core.manager.BoardManager; |
||||
import org.floens.chan.core.model.orm.Board; |
||||
import org.floens.chan.ui.helper.BoardHelper; |
||||
import org.floens.chan.ui.toolbar.ToolbarMenu; |
||||
import org.floens.chan.ui.toolbar.ToolbarMenuItem; |
||||
import org.floens.chan.ui.view.FloatingMenuItem; |
||||
import org.floens.chan.utils.AndroidUtils; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Collections; |
||||
import java.util.Comparator; |
||||
import java.util.List; |
||||
import java.util.Locale; |
||||
|
||||
import javax.inject.Inject; |
||||
|
||||
import static org.floens.chan.Chan.getGraph; |
||||
import static org.floens.chan.ui.theme.ThemeHelper.theme; |
||||
import static org.floens.chan.utils.AndroidUtils.dp; |
||||
import static org.floens.chan.utils.AndroidUtils.fixSnackbarText; |
||||
import static org.floens.chan.utils.AndroidUtils.getString; |
||||
|
||||
public class BoardEditController extends Controller implements View.OnClickListener, ToolbarMenuItem.ToolbarMenuItemCallback { |
||||
private static final int OPTION_SORT_A_Z = 1; |
||||
|
||||
@Inject |
||||
BoardManager boardManager; |
||||
|
||||
private RecyclerView recyclerView; |
||||
private BoardEditAdapter adapter; |
||||
private FloatingActionButton add; |
||||
private ItemTouchHelper itemTouchHelper; |
||||
|
||||
private List<Board> boards; |
||||
|
||||
public BoardEditController(Context context) { |
||||
super(context); |
||||
} |
||||
|
||||
@Override |
||||
public void onCreate() { |
||||
super.onCreate(); |
||||
getGraph().inject(this); |
||||
|
||||
navigationItem.setTitle(R.string.board_edit); |
||||
|
||||
List<FloatingMenuItem> items = new ArrayList<>(); |
||||
items.add(new FloatingMenuItem(OPTION_SORT_A_Z, R.string.board_edit_sort_a_z)); |
||||
navigationItem.menu = new ToolbarMenu(context); |
||||
navigationItem.createOverflow(context, this, items); |
||||
navigationItem.swipeable = false; |
||||
|
||||
view = inflateRes(R.layout.controller_board_edit); |
||||
recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view); |
||||
recyclerView.setHasFixedSize(true); |
||||
recyclerView.setLayoutManager(new LinearLayoutManager(context)); |
||||
add = (FloatingActionButton) view.findViewById(R.id.add); |
||||
add.setOnClickListener(this); |
||||
theme().applyFabColor(add); |
||||
|
||||
boards = boardManager.getSavedBoards(); |
||||
|
||||
adapter = new BoardEditAdapter(); |
||||
recyclerView.setAdapter(adapter); |
||||
|
||||
itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() { |
||||
@Override |
||||
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { |
||||
boolean isBoardItem = viewHolder.getAdapterPosition() > 0; |
||||
int dragFlags = isBoardItem ? ItemTouchHelper.UP | ItemTouchHelper.DOWN : 0; |
||||
int swipeFlags = isBoardItem ? ItemTouchHelper.RIGHT | ItemTouchHelper.LEFT : 0; |
||||
return makeMovementFlags(dragFlags, swipeFlags); |
||||
} |
||||
|
||||
@Override |
||||
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { |
||||
int from = viewHolder.getAdapterPosition(); |
||||
int to = target.getAdapterPosition(); |
||||
|
||||
if (to > 0) { |
||||
Board item = boards.remove(from - 1); |
||||
boards.add(to - 1, item); |
||||
adapter.notifyItemMoved(from, to); |
||||
return true; |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { |
||||
final int position = viewHolder.getAdapterPosition(); |
||||
final Board board = boards.get(position - 1); |
||||
board.saved = false; |
||||
boards.remove(position - 1); |
||||
adapter.notifyItemRemoved(position); |
||||
|
||||
Snackbar snackbar = Snackbar.make(view, context.getString(R.string.board_edit_board_removed, board.name), Snackbar.LENGTH_LONG); |
||||
fixSnackbarText(context, snackbar); |
||||
snackbar.setAction(R.string.undo, new View.OnClickListener() { |
||||
@Override |
||||
public void onClick(View v) { |
||||
board.saved = true; |
||||
boards.add(position - 1, board); |
||||
adapter.notifyDataSetChanged(); |
||||
} |
||||
}); |
||||
snackbar.show(); |
||||
} |
||||
}); |
||||
|
||||
itemTouchHelper.attachToRecyclerView(recyclerView); |
||||
} |
||||
|
||||
@Override |
||||
public void onMenuItemClicked(ToolbarMenuItem item) { |
||||
} |
||||
|
||||
@Override |
||||
public void onSubMenuItemClicked(ToolbarMenuItem parent, FloatingMenuItem item) { |
||||
if (((Integer) item.getId()) == OPTION_SORT_A_Z) { |
||||
Collections.sort(boards, new Comparator<Board>() { |
||||
@Override |
||||
public int compare(Board lhs, Board rhs) { |
||||
return lhs.code.compareTo(rhs.code); |
||||
} |
||||
}); |
||||
adapter.notifyDataSetChanged(); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void onDestroy() { |
||||
super.onDestroy(); |
||||
|
||||
for (int i = 0; i < boards.size(); i++) { |
||||
boards.get(i).order = i; |
||||
} |
||||
|
||||
// TODO(multisite)
|
||||
// boardManager.flushOrderAndSaved();
|
||||
} |
||||
|
||||
@Override |
||||
public void onClick(View v) { |
||||
if (v == add) { |
||||
showAddBoardDialog(); |
||||
} |
||||
} |
||||
|
||||
private void showAddBoardDialog() { |
||||
LinearLayout wrap = new LinearLayout(context); |
||||
wrap.setPadding(dp(16), dp(16), dp(16), 0); |
||||
final AutoCompleteTextView text = new AutoCompleteTextView(context); |
||||
text.setSingleLine(); |
||||
wrap.addView(text, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); |
||||
|
||||
FillAdapter fillAdapter = new FillAdapter(context, 0); |
||||
fillAdapter.setEditingList(boards); |
||||
fillAdapter.setAutoCompleteView(text); |
||||
text.setAdapter(fillAdapter); |
||||
text.setThreshold(1); |
||||
text.setDropDownHeight(ViewGroup.LayoutParams.WRAP_CONTENT); |
||||
text.setHint(R.string.board_add_hint); |
||||
text.setImeOptions(EditorInfo.IME_FLAG_NO_FULLSCREEN); |
||||
|
||||
AlertDialog dialog = new AlertDialog.Builder(context) |
||||
.setPositiveButton(R.string.add, new DialogInterface.OnClickListener() { |
||||
@Override |
||||
public void onClick(DialogInterface d, int which) { |
||||
String value = text.getText().toString(); |
||||
|
||||
if (!TextUtils.isEmpty(value)) { |
||||
addBoard(value.toLowerCase(Locale.ENGLISH)); |
||||
} |
||||
} |
||||
}).setNegativeButton(R.string.cancel, null) |
||||
.setTitle(R.string.board_add) |
||||
.setView(wrap) |
||||
.create(); |
||||
|
||||
AndroidUtils.requestKeyboardFocus(dialog, text); |
||||
|
||||
dialog.show(); |
||||
} |
||||
|
||||
private void addBoard(String value) { |
||||
value = value.replace(" ", ""); |
||||
value = value.replace("/", ""); |
||||
value = value.replace("\\", ""); |
||||
|
||||
// Duplicate
|
||||
for (Board board : boards) { |
||||
if (board.code.equals(value)) { |
||||
new AlertDialog.Builder(context).setMessage(R.string.board_add_duplicate).setPositiveButton(R.string.ok, null).show(); |
||||
|
||||
return; |
||||
} |
||||
} |
||||
|
||||
// Normal add
|
||||
List<Board> all = boardManager.getSavedBoards(); |
||||
for (Board board : all) { |
||||
if (board.code.equals(value)) { |
||||
board.saved = true; |
||||
boards.add(board); |
||||
adapter.notifyDataSetChanged(); |
||||
|
||||
recyclerView.smoothScrollToPosition(boards.size()); |
||||
|
||||
Snackbar snackbar = Snackbar.make(view, getString(R.string.board_add_success) + " " + board.name, Snackbar.LENGTH_LONG); |
||||
fixSnackbarText(context, snackbar); |
||||
snackbar.show(); |
||||
|
||||
return; |
||||
} |
||||
} |
||||
|
||||
// Unknown
|
||||
new AlertDialog.Builder(context) |
||||
.setTitle(R.string.board_add_unknown_title) |
||||
.setMessage(context.getString(R.string.board_add_unknown, value)) |
||||
.setPositiveButton(R.string.ok, null) |
||||
.show(); |
||||
} |
||||
|
||||
private class FillAdapter extends ArrayAdapter<String> implements Filterable { |
||||
private List<Board> currentlyEditing; |
||||
private View autoCompleteView; |
||||
private final Filter filter; |
||||
private final List<Board> filtered = new ArrayList<>(); |
||||
|
||||
public FillAdapter(Context context, int resource) { |
||||
super(context, resource); |
||||
|
||||
filter = new Filter() { |
||||
@Override |
||||
protected synchronized FilterResults performFiltering(CharSequence constraint) { |
||||
FilterResults results = new FilterResults(); |
||||
|
||||
if (TextUtils.isEmpty(constraint) || (constraint.toString().startsWith(" "))) { |
||||
results.values = null; |
||||
results.count = 0; |
||||
} else { |
||||
List<Board> keys = getFiltered(constraint.toString()); |
||||
results.values = keys; |
||||
results.count = keys.size(); |
||||
} |
||||
|
||||
return results; |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
@Override |
||||
protected void publishResults(CharSequence constraint, FilterResults results) { |
||||
filtered.clear(); |
||||
|
||||
if (results.values != null) { |
||||
filtered.addAll((List<Board>) results.values); |
||||
} else { |
||||
filtered.addAll(getBoards()); |
||||
} |
||||
|
||||
notifyDataSetChanged(); |
||||
} |
||||
}; |
||||
} |
||||
|
||||
public void setEditingList(List<Board> list) { |
||||
currentlyEditing = list; |
||||
} |
||||
|
||||
public void setAutoCompleteView(View autoCompleteView) { |
||||
this.autoCompleteView = autoCompleteView; |
||||
} |
||||
|
||||
@Override |
||||
public int getCount() { |
||||
return filtered.size(); |
||||
} |
||||
|
||||
@Override |
||||
public String getItem(int position) { |
||||
return filtered.get(position).code; |
||||
} |
||||
|
||||
@Override |
||||
public View getView(int position, View convertView, ViewGroup parent) { |
||||
@SuppressLint("ViewHolder") |
||||
TextView view = (TextView) LayoutInflater.from(parent.getContext()).inflate(android.R.layout.simple_list_item_1, parent, false); |
||||
Board b = filtered.get(position); |
||||
view.setText("/" + b.code + "/ - " + b.name); |
||||
|
||||
view.setOnTouchListener(new View.OnTouchListener() { |
||||
@Override |
||||
public boolean onTouch(View v, MotionEvent event) { |
||||
if (event.getAction() == MotionEvent.ACTION_DOWN) { |
||||
AndroidUtils.hideKeyboard(autoCompleteView); |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
}); |
||||
|
||||
return view; |
||||
} |
||||
|
||||
@Override |
||||
public Filter getFilter() { |
||||
return filter; |
||||
} |
||||
|
||||
private List<Board> getFiltered(String filter) { |
||||
String lowered = filter.toLowerCase(Locale.ENGLISH); |
||||
List<Board> list = new ArrayList<>(); |
||||
for (Board b : getBoards()) { |
||||
if ((b.name.toLowerCase(Locale.ENGLISH).contains(lowered) || b.code.toLowerCase(Locale.ENGLISH) |
||||
.contains(lowered))) { |
||||
list.add(b); |
||||
} |
||||
} |
||||
return list; |
||||
} |
||||
|
||||
private boolean haveBoard(String value) { |
||||
for (Board b : currentlyEditing) { |
||||
if (b.code.equals(value)) |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
private List<Board> getBoards() { |
||||
// Lets be cheaty here: if the user has nsfw boards in the list,
|
||||
// show them in the autofiller, hide them otherwise.
|
||||
/*boolean showUnsafe = false; |
||||
for (Board has : currentlyEditing) { |
||||
if (!has.workSafe) { |
||||
showUnsafe = true; |
||||
break; |
||||
} |
||||
}*/ |
||||
List<Board> s = new ArrayList<>(); |
||||
for (Board b : boardManager.getSavedBoards()) { |
||||
if (!haveBoard(b.code)/* && (showUnsafe || b.workSafe)*/) { |
||||
s.add(b); |
||||
} |
||||
} |
||||
return s; |
||||
} |
||||
} |
||||
|
||||
private class BoardEditAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { |
||||
private int TYPE_ITEM = 0; |
||||
private int TYPE_HEADER = 1; |
||||
|
||||
public BoardEditAdapter() { |
||||
setHasStableIds(true); |
||||
} |
||||
|
||||
@Override |
||||
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { |
||||
if (viewType == TYPE_ITEM) { |
||||
return new BoardEditItem(LayoutInflater.from(parent.getContext()).inflate(R.layout.cell_board_edit, parent, false)); |
||||
} else { |
||||
return new BoardEditHeader(LayoutInflater.from(parent.getContext()).inflate(R.layout.cell_board_edit_header, parent, false)); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { |
||||
if (getItemViewType(position) == TYPE_HEADER) { |
||||
BoardEditHeader header = (BoardEditHeader) holder; |
||||
header.text.setText(R.string.board_edit_header); |
||||
} else { |
||||
BoardEditItem item = (BoardEditItem) holder; |
||||
Board board = boards.get(position - 1); |
||||
item.text.setText(BoardHelper.getName(board)); |
||||
item.description.setText(BoardHelper.getDescription(board)); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public int getItemViewType(int position) { |
||||
return position == 0 ? TYPE_HEADER : TYPE_ITEM; |
||||
} |
||||
|
||||
@Override |
||||
public int getItemCount() { |
||||
return boards.size() + 1; |
||||
} |
||||
|
||||
@Override |
||||
public long getItemId(int position) { |
||||
if (getItemViewType(position) == TYPE_HEADER) { |
||||
return -1; |
||||
} else { |
||||
return boards.get(position - 1).id; |
||||
} |
||||
} |
||||
} |
||||
|
||||
private class BoardEditItem extends RecyclerView.ViewHolder { |
||||
private ImageView thumb; |
||||
private TextView text; |
||||
private TextView description; |
||||
|
||||
public BoardEditItem(View itemView) { |
||||
super(itemView); |
||||
thumb = (ImageView) itemView.findViewById(R.id.thumb); |
||||
text = (TextView) itemView.findViewById(R.id.text); |
||||
description = (TextView) itemView.findViewById(R.id.description); |
||||
|
||||
thumb.setOnTouchListener(new View.OnTouchListener() { |
||||
public boolean onTouch(View v, MotionEvent event) { |
||||
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { |
||||
itemTouchHelper.startDrag(BoardEditItem.this); |
||||
} |
||||
return false; |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
|
||||
private class BoardEditHeader extends RecyclerView.ViewHolder { |
||||
private TextView text; |
||||
|
||||
public BoardEditHeader(View itemView) { |
||||
super(itemView); |
||||
text = (TextView) itemView.findViewById(R.id.text); |
||||
text.setTypeface(AndroidUtils.ROBOTO_MEDIUM_ITALIC); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,237 @@ |
||||
/* |
||||
* 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.layout; |
||||
|
||||
import android.annotation.SuppressLint; |
||||
import android.graphics.drawable.Drawable; |
||||
import android.util.Pair; |
||||
import android.view.Gravity; |
||||
import android.view.LayoutInflater; |
||||
import android.view.View; |
||||
import android.view.ViewGroup; |
||||
import android.widget.AdapterView; |
||||
import android.widget.BaseAdapter; |
||||
import android.widget.ImageView; |
||||
import android.widget.TextView; |
||||
|
||||
import org.floens.chan.R; |
||||
import org.floens.chan.core.manager.BoardManager; |
||||
import org.floens.chan.core.model.orm.Board; |
||||
import org.floens.chan.core.site.Site; |
||||
import org.floens.chan.core.site.SiteIcon; |
||||
import org.floens.chan.ui.helper.BoardHelper; |
||||
import org.floens.chan.ui.view.FloatingMenu; |
||||
import org.floens.chan.utils.AndroidUtils; |
||||
|
||||
import java.util.List; |
||||
import java.util.Observable; |
||||
import java.util.Observer; |
||||
|
||||
import static org.floens.chan.utils.AndroidUtils.dp; |
||||
|
||||
public class BrowseBoardsFloatingMenu implements Observer, AdapterView.OnItemClickListener { |
||||
private FloatingMenu floatingMenu; |
||||
private BoardManager.SavedBoards savedBoards; |
||||
private BrowseBoardsAdapter adapter; |
||||
|
||||
private Callback callback; |
||||
|
||||
public BrowseBoardsFloatingMenu(BoardManager.SavedBoards savedBoards) { |
||||
this.savedBoards = savedBoards; |
||||
this.savedBoards.addObserver(this); |
||||
} |
||||
|
||||
private void onDismissed() { |
||||
savedBoards.deleteObserver(this); |
||||
} |
||||
|
||||
public void setCallback(Callback callback) { |
||||
this.callback = callback; |
||||
} |
||||
|
||||
public void show(View anchor, Board selectedBoard) { |
||||
floatingMenu = new FloatingMenu(anchor.getContext()); |
||||
floatingMenu.setCallback(new FloatingMenu.FloatingMenuCallbackAdapter() { |
||||
@Override |
||||
public void onFloatingMenuDismissed(FloatingMenu menu) { |
||||
onDismissed(); |
||||
} |
||||
}); |
||||
floatingMenu.setManageItems(false); |
||||
floatingMenu.setAnchor(anchor, Gravity.LEFT, dp(5), dp(5)); |
||||
floatingMenu.setPopupWidth(FloatingMenu.POPUP_WIDTH_ANCHOR); |
||||
adapter = new BrowseBoardsAdapter(); |
||||
floatingMenu.setAdapter(adapter); |
||||
floatingMenu.setOnItemClickListener(this); |
||||
floatingMenu.setSelectedPosition(resolveCurrentIndex(selectedBoard)); |
||||
floatingMenu.show(); |
||||
} |
||||
|
||||
@Override |
||||
public void update(Observable o, Object arg) { |
||||
if (o == savedBoards) { |
||||
adapter.notifyDataSetChanged(); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { |
||||
Pair<Site, Board> siteOrBoard = getAtPosition(position); |
||||
if (siteOrBoard.second != null) { |
||||
callback.onBoardClicked(siteOrBoard.second); |
||||
} else { |
||||
callback.onSiteClicked(siteOrBoard.first); |
||||
} |
||||
floatingMenu.dismiss(); |
||||
} |
||||
|
||||
private int getCount() { |
||||
int count = 0; |
||||
for (Pair<Site, List<Board>> siteListPair : savedBoards.get()) { |
||||
count += 1; |
||||
count += siteListPair.second.size(); |
||||
} |
||||
|
||||
return count; |
||||
} |
||||
|
||||
private int resolveCurrentIndex(Board board) { |
||||
int position = 0; |
||||
for (Pair<Site, List<Board>> siteListPair : savedBoards.get()) { |
||||
position += 1; |
||||
|
||||
for (Board other : siteListPair.second) { |
||||
if (board == other) { |
||||
return position; |
||||
} |
||||
position++; |
||||
} |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
private Pair<Site, Board> getAtPosition(int position) { |
||||
for (Pair<Site, List<Board>> siteListPair : savedBoards.get()) { |
||||
if (position == 0) { |
||||
return new Pair<>(siteListPair.first, null); |
||||
} |
||||
position -= 1; |
||||
|
||||
if (position < siteListPair.second.size()) { |
||||
return new Pair<>(null, siteListPair.second.get(position)); |
||||
} |
||||
position -= siteListPair.second.size(); |
||||
} |
||||
throw new IllegalArgumentException(); |
||||
} |
||||
|
||||
private class BrowseBoardsAdapter extends BaseAdapter { |
||||
final int TYPE_SITE = 0; |
||||
final int TYPE_BOARD = 1; |
||||
|
||||
@Override |
||||
public int getViewTypeCount() { |
||||
return 2; |
||||
} |
||||
|
||||
@Override |
||||
public int getItemViewType(int position) { |
||||
Pair<Site, Board> siteOrBoard = getAtPosition(position); |
||||
if (siteOrBoard.first != null) { |
||||
return TYPE_SITE; |
||||
} else if (siteOrBoard.second != null) { |
||||
return TYPE_BOARD; |
||||
} else { |
||||
throw new IllegalArgumentException(); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public int getCount() { |
||||
return BrowseBoardsFloatingMenu.this.getCount(); |
||||
} |
||||
|
||||
@Override |
||||
public Object getItem(int position) { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public long getItemId(int position) { |
||||
return position; |
||||
} |
||||
|
||||
@SuppressLint("ViewHolder") |
||||
@Override |
||||
public View getView(int position, View view, ViewGroup parent) { |
||||
LayoutInflater inflater = LayoutInflater.from(parent.getContext()); |
||||
|
||||
Pair<Site, Board> siteOrBoard = getAtPosition(position); |
||||
if (siteOrBoard.first != null) { |
||||
Site site = siteOrBoard.first; |
||||
|
||||
if (view == null) { |
||||
view = inflater.inflate(R.layout.cell_browse_site, parent, false); |
||||
} |
||||
|
||||
View divider = view.findViewById(R.id.divider); |
||||
final ImageView image = view.findViewById(R.id.image); |
||||
TextView text = view.findViewById(R.id.text); |
||||
|
||||
divider.setVisibility(position == 0 ? View.GONE : View.VISIBLE); |
||||
|
||||
final SiteIcon icon = site.icon(); |
||||
image.setTag(icon); |
||||
|
||||
icon.get(new SiteIcon.SiteIconResult() { |
||||
@Override |
||||
public void onSiteIcon(SiteIcon siteIcon, Drawable drawable) { |
||||
if (image.getTag() == icon) { |
||||
image.setImageDrawable(drawable); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
text.setTypeface(AndroidUtils.ROBOTO_MEDIUM); |
||||
text.setText(site.name()); |
||||
|
||||
return view; |
||||
} else { |
||||
Board board = siteOrBoard.second; |
||||
|
||||
if (view == null) { |
||||
view = inflater.inflate(R.layout.cell_browse_board, parent, false); |
||||
} |
||||
|
||||
TextView text = (TextView) view; |
||||
|
||||
text.setTypeface(AndroidUtils.ROBOTO_MEDIUM); |
||||
text.setText(BoardHelper.getName(board)); |
||||
|
||||
return text; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public interface Callback { |
||||
void onBoardClicked(Board item); |
||||
|
||||
void onSiteClicked(Site site); |
||||
} |
||||
} |
@ -0,0 +1,24 @@ |
||||
/* |
||||
* 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.toolbar; |
||||
|
||||
import android.view.View; |
||||
|
||||
public interface ToolbarMiddleMenu { |
||||
void show(View anchor); |
||||
} |
@ -1,69 +0,0 @@ |
||||
<?xml version="1.0" encoding="utf-8"?><!-- |
||||
Clover - 4chan browser https://github.com/Floens/Clover/ |
||||
Copyright (C) 2014 Floens |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
--> |
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
xmlns:tools="http://schemas.android.com/tools" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:background="?attr/selectableItemBackground" |
||||
android:orientation="horizontal"> |
||||
|
||||
<ImageView |
||||
android:id="@+id/thumb" |
||||
android:layout_width="56dp" |
||||
android:layout_height="match_parent" |
||||
android:layout_gravity="center_vertical" |
||||
android:paddingLeft="8dp" |
||||
android:paddingRight="8dp" |
||||
android:scaleType="center" |
||||
android:src="@drawable/ic_reorder_black_24dp" |
||||
tools:ignore="ContentDescription" /> |
||||
|
||||
<LinearLayout |
||||
android:layout_width="0dp" |
||||
android:layout_height="match_parent" |
||||
android:layout_weight="1" |
||||
android:orientation="vertical"> |
||||
|
||||
<TextView |
||||
android:id="@+id/text" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:ellipsize="end" |
||||
android:paddingLeft="16dp" |
||||
android:paddingRight="16dp" |
||||
android:paddingTop="8dp" |
||||
android:singleLine="true" |
||||
android:textColor="?text_color_primary" |
||||
android:textSize="14sp" |
||||
tools:text="Technology" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/description" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:ellipsize="end" |
||||
android:paddingBottom="8dp" |
||||
android:paddingLeft="16dp" |
||||
android:paddingRight="16dp" |
||||
android:textColor="?text_color_secondary" |
||||
android:textSize="12sp" |
||||
tools:text="Description is here" /> |
||||
|
||||
</LinearLayout> |
||||
|
||||
</LinearLayout> |
@ -0,0 +1,55 @@ |
||||
<?xml version="1.0" encoding="utf-8"?><!-- |
||||
Clover - 4chan browser https://github.com/Floens/Clover/ |
||||
Copyright (C) 2014 Floens |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
--> |
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
xmlns:tools="http://schemas.android.com/tools" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:orientation="horizontal" |
||||
tools:ignore="RtlHardcoded"> |
||||
|
||||
<View |
||||
android:id="@+id/divider" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="1dp" |
||||
android:background="?attr/divider_color" /> |
||||
|
||||
<ImageView |
||||
android:id="@+id/image" |
||||
android:layout_width="48dp" |
||||
android:layout_height="match_parent" |
||||
android:paddingBottom="8dp" |
||||
android:paddingLeft="16dp" |
||||
android:paddingTop="8dp" |
||||
android:scaleType="fitCenter" |
||||
tools:src="@drawable/ic_help_outline_black_24dp" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/text" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="48dp" |
||||
android:ellipsize="end" |
||||
android:gravity="center_vertical" |
||||
android:lines="1" |
||||
android:paddingLeft="56dp" |
||||
android:paddingRight="8dp" |
||||
android:singleLine="true" |
||||
android:textColor="?text_color_primary" |
||||
android:textSize="16sp" |
||||
tools:text="Site name" /> |
||||
|
||||
</FrameLayout> |
Loading…
Reference in new issue