diff --git a/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseBoardManager.java b/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseBoardManager.java index ae2eb73d..b41125fc 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseBoardManager.java +++ b/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseBoardManager.java @@ -33,6 +33,17 @@ public class DatabaseBoardManager { }; } + public Callable update(final Board board) { + return new Callable() { + @Override + public Void call() throws Exception { + helper.boardsDao.update(board); + + return null; + } + }; + } + public Callable createAll(final List boards) { return new Callable() { @Override diff --git a/Clover/app/src/main/java/org/floens/chan/core/di/AppModule.java b/Clover/app/src/main/java/org/floens/chan/core/di/AppModule.java index f19f9862..5a63546a 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/di/AppModule.java +++ b/Clover/app/src/main/java/org/floens/chan/core/di/AppModule.java @@ -13,24 +13,23 @@ import org.floens.chan.core.database.DatabaseManager; import org.floens.chan.core.manager.BoardManager; import org.floens.chan.core.manager.FilterEngine; import org.floens.chan.core.manager.ReplyManager; -import org.floens.chan.core.presenter.BoardSetupPresenter; -import org.floens.chan.core.presenter.SitesSetupPresenter; -import org.floens.chan.core.site.SiteManager; import org.floens.chan.core.manager.WatchManager; import org.floens.chan.core.net.BitmapLruImageCache; +import org.floens.chan.core.presenter.BoardSetupPresenter; import org.floens.chan.core.presenter.ImageViewerPresenter; import org.floens.chan.core.presenter.ReplyPresenter; +import org.floens.chan.core.presenter.SitesSetupPresenter; import org.floens.chan.core.presenter.ThreadPresenter; import org.floens.chan.core.receiver.WatchUpdateReceiver; import org.floens.chan.core.saver.ImageSaveTask; +import org.floens.chan.core.site.SiteManager; +import org.floens.chan.core.site.common.ChanReaderRequest; import org.floens.chan.core.site.http.HttpCallManager; import org.floens.chan.core.site.sites.chan4.Chan4; -import org.floens.chan.core.site.common.ChanReaderRequest; import org.floens.chan.core.update.UpdateManager; import org.floens.chan.ui.activity.BoardActivity; import org.floens.chan.ui.adapter.DrawerAdapter; import org.floens.chan.ui.adapter.PostsFilter; -import org.floens.chan.ui.controller.BoardEditController; import org.floens.chan.ui.controller.BoardSetupController; import org.floens.chan.ui.controller.BrowseController; import org.floens.chan.ui.controller.DeveloperSettingsController; @@ -74,7 +73,6 @@ import dagger.Provides; DeveloperSettingsController.class, BoardActivity.class, ThreadPresenter.class, - BoardEditController.class, FilterEngine.class, BrowseController.class, FilterLayout.class, diff --git a/Clover/app/src/main/java/org/floens/chan/core/manager/BoardManager.java b/Clover/app/src/main/java/org/floens/chan/core/manager/BoardManager.java index 7faac521..7cf54244 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/manager/BoardManager.java +++ b/Clover/app/src/main/java/org/floens/chan/core/manager/BoardManager.java @@ -17,16 +17,21 @@ */ package org.floens.chan.core.manager; +import android.util.Pair; + import org.floens.chan.core.database.DatabaseManager; import org.floens.chan.core.model.orm.Board; import org.floens.chan.core.site.Boards; import org.floens.chan.core.site.Site; +import org.floens.chan.core.site.SiteManager; import org.floens.chan.core.site.Sites; import org.floens.chan.utils.Logger; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Observable; import javax.inject.Inject; import javax.inject.Singleton; @@ -63,14 +68,19 @@ public class BoardManager { }; private final DatabaseManager databaseManager; + private final SiteManager siteManager; private final List savedBoards = new ArrayList<>(); + private final List>> sitesWithBoards = new ArrayList<>(); + private final SavedBoards savedBoardsObservable = new SavedBoards(); + @Inject - public BoardManager(DatabaseManager databaseManager) { + public BoardManager(DatabaseManager databaseManager, SiteManager siteManager) { this.databaseManager = databaseManager; + this.siteManager = siteManager; - loadBoards(); + updateSavedBoards(); fetchLimitedSitesTheirBoards(); } @@ -92,37 +102,44 @@ public class BoardManager { return savedBoards; } + public SavedBoards getSavedBoardsObservable() { + return savedBoardsObservable; + } + public List getSiteBoards(Site site) { return databaseManager.runTaskSync( databaseManager.getDatabaseBoardManager().getSiteBoards(site)); } public List getSiteSavedBoards(Site site) { - return databaseManager.runTaskSync( + List boards = databaseManager.runTaskSync( databaseManager.getDatabaseBoardManager().getSiteSavedBoards(site)); + Collections.sort(boards, ORDER_SORT); + return boards; } public void saveBoard(Board board) { - board.saved = true; - - databaseManager.runTaskSync(databaseManager.getDatabaseBoardManager().createOrUpdate(board)); - - loadBoards(); + setSaved(board, true); } public void unsaveBoard(Board board) { - board.saved = false; - - databaseManager.runTaskSync(databaseManager.getDatabaseBoardManager().createOrUpdate(board)); - - loadBoards(); + setSaved(board, false); } - private void loadBoards() { - savedBoards.clear(); - savedBoards.addAll(databaseManager.runTaskSync(databaseManager.getDatabaseBoardManager().getSavedBoards())); + public void updateBoardOrder(Board board, int order) { + board.order = order; + databaseManager.runTask(databaseManager.getDatabaseBoardManager().update(board), new DatabaseManager.TaskResult() { + @Override + public void onComplete(Void result) { + updateSavedBoards(); + } + }); + } - EventBus.getDefault().post(new BoardsChangedMessage()); + private void setSaved(Board board, boolean saved) { + board.saved = saved; + databaseManager.runTaskSync(databaseManager.getDatabaseBoardManager().createOrUpdate(board)); + updateSavedBoards(); } private void fetchLimitedSitesTheirBoards() { @@ -145,6 +162,28 @@ public class BoardManager { databaseManager.runTask(databaseManager.getDatabaseBoardManager().createAll(boards.boards)); } + private void updateSavedBoards() { + savedBoards.clear(); + savedBoards.addAll(databaseManager.runTaskSync(databaseManager.getDatabaseBoardManager().getSavedBoards())); + + sitesWithBoards.clear(); + for (Site site : Sites.allSites()) { + List siteBoards = getSiteSavedBoards(site); + sitesWithBoards.add(new Pair<>(site, siteBoards)); + } + + // TODO: remove, use the observable + EventBus.getDefault().post(new BoardsChangedMessage()); + + savedBoardsObservable.notifyObservers(); + } + + public class SavedBoards extends Observable { + public List>> get() { + return sitesWithBoards; + } + } + /*private void appendBoards(Boards response) { List boardsToAddWs = new ArrayList<>(); List boardsToAddNws = new ArrayList<>(); diff --git a/Clover/app/src/main/java/org/floens/chan/core/presenter/BoardSetupPresenter.java b/Clover/app/src/main/java/org/floens/chan/core/presenter/BoardSetupPresenter.java index beb909a2..1f9b86c0 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/presenter/BoardSetupPresenter.java +++ b/Clover/app/src/main/java/org/floens/chan/core/presenter/BoardSetupPresenter.java @@ -63,25 +63,29 @@ public class BoardSetupPresenter { callback.setSavedBoards(savedBoards); } - public void setAddCallback(AddCallback addCallback) { + public void addClicked() { + callback.showAddDialog(); + } + + public void bindAddDialog(AddCallback addCallback) { this.addCallback = addCallback; - suggestions.clear(); - selectedSuggestions.clear(); + + searchEntered(null); } - public void addClicked() { - callback.showAddDialog(); + public void unbindAddDialog() { + this.addCallback = null; + suggestions.clear(); + selectedSuggestions.clear(); } public void onSuggestionClicked(BoardSuggestion suggestion) { suggestion.checked = !suggestion.checked; - String code = suggestion.board.code; if (suggestion.checked) { - selectedSuggestions.add(code); + selectedSuggestions.add(suggestion.getCode()); } else { - selectedSuggestions.remove(code); + selectedSuggestions.remove(suggestion.getCode()); } - addCallback.updateSuggestions(); } public List getSuggestions() { @@ -89,27 +93,62 @@ public class BoardSetupPresenter { } public void onAddDialogPositiveClicked() { - for (BoardSuggestion suggestion : suggestions) { - if (suggestion.checked) { - boardManager.saveBoard(suggestion.board); - updateSavedBoards(); - callback.setSavedBoards(savedBoards); + int count = 0; + + if (site.boardsType() == Site.BoardsType.DYNAMIC) { + for (Board board : boardManager.getSiteBoards(site)) { + if (selectedSuggestions.contains(board.code)) { + boardManager.saveBoard(board); + boardManager.updateBoardOrder(board, savedBoards.size()); + count++; + } + } + } else { + for (String suggestion : selectedSuggestions) { + // TODO(multisite) + Board board = new Board(site, suggestion, suggestion, true, true); + boardManager.saveBoard(board); + count++; } } + + updateSavedBoards(); + callback.setSavedBoards(savedBoards); + callback.boardsWereAdded(count); } public void move(int from, int to) { Board item = savedBoards.remove(from); savedBoards.add(to, item); + int min = Math.min(from, to); + int max = Math.max(from, to); + + for (int i = min; i <= max; i++) { + boardManager.updateBoardOrder(savedBoards.get(i), i); + } + callback.setSavedBoards(savedBoards); } public void remove(int position) { Board board = savedBoards.remove(position); - boardManager.unsaveBoard(board); + for (int i = position; i < savedBoards.size(); i++) { + boardManager.updateBoardOrder(savedBoards.get(i), i); + } + + updateSavedBoards(); + callback.setSavedBoards(savedBoards); + + callback.showRemovedSnackbar(board); + } + + public void undoRemoveBoard(Board board) { + boardManager.saveBoard(board); + // TODO + boardManager.updateBoardOrder(board, savedBoards.size()); updateSavedBoards(); callback.setSavedBoards(savedBoards); } @@ -118,32 +157,48 @@ public class BoardSetupPresenter { callback.finish(); } - public void searchEntered(final String query) { + public void searchEntered(String userQuery) { if (suggestionCall != null) { suggestionCall.cancel(); } + final String query = userQuery == null ? null : + userQuery.replace("/", "").replace("\\", ""); suggestionCall = BackgroundUtils.runWithExecutor(executor, new Callable>() { @Override public List call() throws Exception { List suggestions = new ArrayList<>(); if (site.boardsType() == Site.BoardsType.DYNAMIC) { List siteBoards = boardManager.getSiteBoards(site); - List toSearch = new ArrayList<>(); + List allUnsavedBoards = new ArrayList<>(); for (Board siteBoard : siteBoards) { if (!siteBoard.saved) { - toSearch.add(siteBoard); + allUnsavedBoards.add(siteBoard); } } - List search = BoardHelper.search(toSearch, query); - for (Board board : search) { + List toSuggest; + if (query == null || query.equals("")) { + toSuggest = new ArrayList<>(allUnsavedBoards.size()); + for (Board b : allUnsavedBoards) { + if (b.workSafe) toSuggest.add(b); + } + for (Board b : allUnsavedBoards) { + if (!b.workSafe) toSuggest.add(b); + } + toSuggest = allUnsavedBoards; + } else { + toSuggest = BoardHelper.search(allUnsavedBoards, query); + } + + for (Board board : toSuggest) { BoardSuggestion suggestion = new BoardSuggestion(board); suggestions.add(suggestion); } } else { - // TODO - suggestions.add(new BoardSuggestion(null)); + if (query != null && !query.equals("")) { + suggestions.add(new BoardSuggestion(query)); + } } return suggestions; @@ -167,7 +222,7 @@ public class BoardSetupPresenter { private void updateSuggestions(List suggestions) { this.suggestions = suggestions; for (BoardSuggestion suggestion : this.suggestions) { - suggestion.checked = selectedSuggestions.contains(suggestion.board.code); + suggestion.checked = selectedSuggestions.contains(suggestion.getCode()); } } @@ -176,7 +231,11 @@ public class BoardSetupPresenter { void setSavedBoards(List savedBoards); + void showRemovedSnackbar(Board board); + void finish(); + + void boardsWereAdded(int count); } public interface AddCallback { @@ -184,24 +243,47 @@ public class BoardSetupPresenter { } public static class BoardSuggestion { - public final Board board; + private final Board board; + private final String code; private boolean checked = false; - public BoardSuggestion(Board board) { + BoardSuggestion(Board board) { this.board = board; + this.code = board.code; + } + + BoardSuggestion(String code) { + this.board = null; + this.code = code; } public String getName() { - return BoardHelper.getName(board); + if (board != null) { + return BoardHelper.getName(board); + } else { + return "/" + code + "/"; + } } public String getDescription() { - return BoardHelper.getDescription(board); + if (board != null) { + return BoardHelper.getDescription(board); + } else { + return ""; + } + } + + public String getCode() { + return code; } public boolean isChecked() { return checked; } + + public long getId() { + return code.hashCode(); + } } } diff --git a/Clover/app/src/main/java/org/floens/chan/core/presenter/BrowsePresenter.java b/Clover/app/src/main/java/org/floens/chan/core/presenter/BrowsePresenter.java new file mode 100644 index 00000000..8a0b78c9 --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/core/presenter/BrowsePresenter.java @@ -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 . + */ +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> siteListPair : savedBoardsObservable.get()) { + if (!siteListPair.second.isEmpty()) { + return true; + } + } + + return false; + } + + private Board firstBoard() { + for (Pair> 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); + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/core/presenter/SitesSetupPresenter.java b/Clover/app/src/main/java/org/floens/chan/core/presenter/SitesSetupPresenter.java index dc08dd63..c9c1782b 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/presenter/SitesSetupPresenter.java +++ b/Clover/app/src/main/java/org/floens/chan/core/presenter/SitesSetupPresenter.java @@ -53,7 +53,7 @@ public class SitesSetupPresenter { } public boolean mayExit() { - return false; + return sites.size() > 0; } public void onShowDialogClicked() { diff --git a/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java b/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java index cbaed4c6..830aaacf 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java @@ -20,6 +20,7 @@ package org.floens.chan.ui.activity; import android.content.DialogInterface; import android.content.Intent; import android.content.res.Configuration; +import android.net.Uri; import android.nfc.NdefMessage; import android.nfc.NfcAdapter; import android.nfc.NfcEvent; @@ -34,6 +35,7 @@ import android.view.ViewGroup; import org.floens.chan.Chan; import org.floens.chan.R; +import org.floens.chan.chan.ChanHelper; import org.floens.chan.controller.Controller; import org.floens.chan.controller.NavigationController; import org.floens.chan.core.database.DatabaseLoadableManager; @@ -133,7 +135,7 @@ public class StartActivity extends AppCompatActivity implements NfcAdapter.Creat private void setupFromStateOrFreshLaunch(Bundle savedInstanceState) { boolean loadDefault = true; - /*if (savedInstanceState != null) { + if (savedInstanceState != null) { // Restore the activity state from the previously saved state. ChanState chanState = savedInstanceState.getParcelable(STATE_KEY); if (chanState == null) { @@ -144,7 +146,7 @@ public class StartActivity extends AppCompatActivity implements NfcAdapter.Creat if (boardThreadPair != null && boardThreadPair.first != null) { loadDefault = false; - browseController.loadBoard(boardThreadPair.first.board); + browseController.setBoard(boardThreadPair.first.board); if (boardThreadPair.second != null) { browseController.showThread(boardThreadPair.second); @@ -158,7 +160,7 @@ public class StartActivity extends AppCompatActivity implements NfcAdapter.Creat Loadable fromUri = ChanHelper.getLoadableFromStartUri(data); if (fromUri != null) { loadDefault = false; - browseController.loadBoard(fromUri.board); + browseController.setBoard(fromUri.board); if (fromUri.isThreadMode()) { browseController.showThread(fromUri, false); @@ -175,15 +177,15 @@ public class StartActivity extends AppCompatActivity implements NfcAdapter.Creat .show(); } } - }*/ + } // Not from a state or from an url, launch the setup controller if no boards are setup up yet, // otherwise load the default saved board. if (loadDefault) { - if (true || boardManager.getSavedBoards().isEmpty()) { + if (boardManager.getSavedBoards().isEmpty()) { setupWithNoBoards(); } else { - browseController.loadDefault(); + browseController.loadWithDefaultBoard(); } } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/adapter/DrawerAdapter.java b/Clover/app/src/main/java/org/floens/chan/ui/adapter/DrawerAdapter.java index b937a134..fa050a73 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/adapter/DrawerAdapter.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/adapter/DrawerAdapter.java @@ -159,11 +159,11 @@ public class DrawerAdapter extends RecyclerView.Adapter LinkHolder linkHolder = (LinkHolder) holder; switch (position) { case 0: - linkHolder.text.setText(R.string.settings_board_edit); + linkHolder.text.setText(R.string.drawer_sites); theme().listAddDrawable.apply(linkHolder.image); break; case 1: - linkHolder.text.setText(R.string.history_screen); + linkHolder.text.setText(R.string.drawer_history); theme().historyDrawable.apply(linkHolder.image); break; } @@ -400,7 +400,7 @@ public class DrawerAdapter extends RecyclerView.Adapter public void onClick(View v) { switch (getAdapterPosition()) { case 0: - callback.openBoardEditor(); + callback.openSites(); break; case 1: callback.openHistory(); @@ -452,7 +452,7 @@ public class DrawerAdapter extends RecyclerView.Adapter void onPinLongClocked(Pin pin); - void openBoardEditor(); + void openSites(); void openHistory(); } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/BoardEditController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/BoardEditController.java deleted file mode 100644 index c0f3ed90..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/BoardEditController.java +++ /dev/null @@ -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 . - */ -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 boards; - - public BoardEditController(Context context) { - super(context); - } - - @Override - public void onCreate() { - super.onCreate(); - getGraph().inject(this); - - navigationItem.setTitle(R.string.board_edit); - - List 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() { - @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 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 implements Filterable { - private List currentlyEditing; - private View autoCompleteView; - private final Filter filter; - private final List 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 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) results.values); - } else { - filtered.addAll(getBoards()); - } - - notifyDataSetChanged(); - } - }; - } - - public void setEditingList(List 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 getFiltered(String filter) { - String lowered = filter.toLowerCase(Locale.ENGLISH); - List 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 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 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 { - 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); - } - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/BoardSetupController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/BoardSetupController.java index 74599672..9e65563d 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/BoardSetupController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/BoardSetupController.java @@ -23,6 +23,7 @@ import android.content.Context; import android.content.DialogInterface; import android.graphics.drawable.Drawable; import android.support.design.widget.FloatingActionButton; +import android.support.design.widget.Snackbar; import android.support.v4.graphics.drawable.DrawableCompat; import android.support.v7.app.AlertDialog; import android.support.v7.widget.LinearLayoutManager; @@ -51,6 +52,7 @@ 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.fixSnackbarText; import static org.floens.chan.utils.AndroidUtils.getAttrColor; public class BoardSetupController extends Controller implements View.OnClickListener, BoardSetupPresenter.Callback { @@ -104,6 +106,7 @@ public class BoardSetupController extends Controller implements View.OnClickList // Navigation navigationItem.title = context.getString(R.string.setup_board_title, site.name()); + navigationItem.swipeable = false; // View binding savedBoardsRecycler = view.findViewById(R.id.boards_recycler); @@ -168,6 +171,34 @@ public class BoardSetupController extends Controller implements View.OnClickList savedAdapter.setSavedBoards(savedBoards); } + @Override + public void showRemovedSnackbar(final Board board) { + Snackbar snackbar = Snackbar.make(view, + context.getString(R.string.setup_board_removed, BoardHelper.getName(board)), + Snackbar.LENGTH_LONG); + fixSnackbarText(context, snackbar); + + snackbar.setAction(R.string.undo, new View.OnClickListener() { + @Override + public void onClick(View v) { + presenter.undoRemoveBoard(board); + } + }); + snackbar.show(); + } + + @Override + public void boardsWereAdded(int count) { + savedBoardsRecycler.smoothScrollToPosition(savedAdapter.getItemCount()); + + String boardText = context.getResources().getQuantityString(R.plurals.board, count, count); + String text = context.getString(R.string.setup_board_added, boardText); + + Snackbar snackbar = Snackbar.make(view, text, Snackbar.LENGTH_LONG); + fixSnackbarText(context, snackbar); + snackbar.show(); + } + @Override public void finish() { navigationController.popController(); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java index c2185121..438aedf9 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java @@ -20,29 +20,26 @@ package org.floens.chan.ui.controller; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; -import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; import android.view.animation.DecelerateInterpolator; -import android.widget.BaseAdapter; import android.widget.ImageView; -import android.widget.TextView; import org.floens.chan.R; import org.floens.chan.chan.ChanUrls; -import org.floens.chan.controller.Controller; -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.model.orm.Pin; +import org.floens.chan.core.presenter.BrowsePresenter; import org.floens.chan.core.presenter.ThreadPresenter; import org.floens.chan.core.settings.ChanSettings; +import org.floens.chan.core.site.Site; import org.floens.chan.ui.adapter.PostsFilter; import org.floens.chan.ui.helper.BoardHelper; +import org.floens.chan.ui.layout.BrowseBoardsFloatingMenu; import org.floens.chan.ui.layout.ThreadLayout; import org.floens.chan.ui.toolbar.ToolbarMenu; import org.floens.chan.ui.toolbar.ToolbarMenuItem; +import org.floens.chan.ui.toolbar.ToolbarMiddleMenu; import org.floens.chan.ui.view.FloatingMenu; import org.floens.chan.ui.view.FloatingMenuItem; import org.floens.chan.utils.AndroidUtils; @@ -55,7 +52,7 @@ import javax.inject.Inject; import static org.floens.chan.Chan.getGraph; import static org.floens.chan.utils.AndroidUtils.getString; -public class BrowseController extends ThreadController implements ToolbarMenuItem.ToolbarMenuItemCallback, ThreadLayout.ThreadLayoutCallback, FloatingMenu.FloatingMenuCallback { +public class BrowseController extends ThreadController implements ToolbarMenuItem.ToolbarMenuItemCallback, ThreadLayout.ThreadLayoutCallback, BrowsePresenter.Callback { private static final int SEARCH_ID = 1; private static final int REFRESH_ID = 2; private static final int REPLY_ID = 101; @@ -65,14 +62,10 @@ public class BrowseController extends ThreadController implements ToolbarMenuIte private static final int OPEN_BROWSER_ID = 106; @Inject - DatabaseManager databaseManager; - - @Inject - BoardManager boardManager; + BrowsePresenter presenter; private ChanSettings.PostViewMode postViewMode; private PostsFilter.Order order; - private List boardItems; private FloatingMenuItem viewModeMenuItem; private ToolbarMenuItem search; @@ -88,16 +81,38 @@ public class BrowseController extends ThreadController implements ToolbarMenuIte super.onCreate(); getGraph().inject(this); + // Initialization postViewMode = ChanSettings.boardViewMode.get(); order = PostsFilter.Order.find(ChanSettings.boardOrder.get()); threadLayout.setPostViewMode(postViewMode); threadLayout.getPresenter().setOrder(order); + // Navigation + initNavigation(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + + presenter.destroy(); + } + + public void setBoard(Board board) { + presenter.setBoard(board); + } + + public void loadWithDefaultBoard() { + presenter.loadWithDefaultBoard(); + } + + private void initNavigation() { + // Navigation item navigationItem.hasDrawer = true; - navigationItem.middleMenu = new FloatingMenu(context); - navigationItem.middleMenu.setCallback(this); - loadBoards(); + setupMiddleNavigation(); + + // Toolbar menu ToolbarMenu menu = new ToolbarMenu(context); navigationItem.menu = menu; navigationItem.hasBack = false; @@ -105,6 +120,7 @@ public class BrowseController extends ThreadController implements ToolbarMenuIte search = menu.addItem(new ToolbarMenuItem(context, this, SEARCH_ID, R.drawable.ic_search_white_24dp)); refresh = menu.addItem(new ToolbarMenuItem(context, this, REFRESH_ID, R.drawable.ic_refresh_white_24dp)); + // Toolbar overflow overflow = menu.createOverflow(this); List items = new ArrayList<>(); @@ -119,6 +135,32 @@ public class BrowseController extends ThreadController implements ToolbarMenuIte items.add(new FloatingMenuItem(OPEN_BROWSER_ID, R.string.action_open_browser)); overflow.setSubMenu(new FloatingMenu(context, overflow.getView(), items)); + + // Presenter + presenter.create(this); + } + + private void setupMiddleNavigation() { + navigationItem.middleMenu = new ToolbarMiddleMenu() { + @SuppressLint("InflateParams") + @Override + public void show(View anchor) { + BrowseBoardsFloatingMenu boardsFloatingMenu = + new BrowseBoardsFloatingMenu(presenter.getSavedBoardsObservable()); + boardsFloatingMenu.show(anchor, presenter.currentBoard()); + boardsFloatingMenu.setCallback(new BrowseBoardsFloatingMenu.Callback() { + @Override + public void onBoardClicked(Board item) { + presenter.onBoardsFloatingMenuBoardClicked(item); + } + + @Override + public void onSiteClicked(Site site) { + presenter.onBoardsFloatingMenuSiteClicked(site); + } + }); + } + }; } @Override @@ -152,104 +194,126 @@ public class BrowseController extends ThreadController implements ToolbarMenuIte Integer id = (Integer) item.getId(); switch (id) { case REPLY_ID: - threadLayout.openReply(true); + handleReply(); break; case SHARE_ID: case OPEN_BROWSER_ID: - if (presenter.isBound()) { - String link = ChanUrls.getCatalogUrlDesktop(presenter.getLoadable().boardCode); - - if (id == SHARE_ID) { - AndroidUtils.shareLink(link); - } else { - AndroidUtils.openLinkInBrowser((Activity) context, link); - } - } + handleShareAndOpenInBrowser(presenter, id); break; case VIEW_MODE_ID: - if (postViewMode == ChanSettings.PostViewMode.LIST) { - postViewMode = ChanSettings.PostViewMode.CARD; - } else { - postViewMode = ChanSettings.PostViewMode.LIST; - } - - ChanSettings.boardViewMode.set(postViewMode); - - viewModeMenuItem.setText(context.getString( - postViewMode == ChanSettings.PostViewMode.LIST ? R.string.action_switch_catalog : R.string.action_switch_board)); - - threadLayout.setPostViewMode(postViewMode); + handleViewMode(); break; case ORDER_ID: - List items = new ArrayList<>(); - for (PostsFilter.Order order : PostsFilter.Order.values()) { - int nameId = 0; - switch (order) { - case BUMP: - nameId = R.string.order_bump; - break; - case REPLY: - nameId = R.string.order_reply; - break; - case IMAGE: - nameId = R.string.order_image; - break; - case NEWEST: - nameId = R.string.order_newest; - break; - case OLDEST: - nameId = R.string.order_oldest; - break; - } + handleOrder(presenter); - String name = getString(nameId); - if (order == this.order) { - name = "\u2713 " + name; // Checkmark - } + break; + } + } - items.add(new FloatingMenuItem(order, name)); - } + private void handleReply() { + threadLayout.openReply(true); + } - FloatingMenu menu = new FloatingMenu(context, overflow.getView(), items); - menu.setCallback(new FloatingMenu.FloatingMenuCallback() { - @Override - public void onFloatingMenuItemClicked(FloatingMenu menu, FloatingMenuItem item) { - PostsFilter.Order order = (PostsFilter.Order) item.getId(); - ChanSettings.boardOrder.set(order.name); - BrowseController.this.order = order; - presenter.setOrder(order); - } + private void handleShareAndOpenInBrowser(ThreadPresenter presenter, Integer id) { + if (presenter.isBound()) { + String link = ChanUrls.getCatalogUrlDesktop(presenter.getLoadable().boardCode); - @Override - public void onFloatingMenuDismissed(FloatingMenu menu) { - } - }); - menu.show(); + if (id == SHARE_ID) { + AndroidUtils.shareLink(link); + } else { + AndroidUtils.openLinkInBrowser((Activity) context, link); + } + } + } - break; + private void handleViewMode() { + if (postViewMode == ChanSettings.PostViewMode.LIST) { + postViewMode = ChanSettings.PostViewMode.CARD; + } else { + postViewMode = ChanSettings.PostViewMode.LIST; } + + ChanSettings.boardViewMode.set(postViewMode); + + int viewModeText = postViewMode == ChanSettings.PostViewMode.LIST ? + R.string.action_switch_catalog : R.string.action_switch_board; + viewModeMenuItem.setText(context.getString(viewModeText)); + + threadLayout.setPostViewMode(postViewMode); } - @Override - public void onFloatingMenuItemClicked(FloatingMenu menu, FloatingMenuItem item) { - if (menu == navigationItem.middleMenu) { - if (item instanceof FloatingMenuItemBoard) { - loadBoard(((FloatingMenuItemBoard) item).board); - } else { - Controller boardEditController = new BoardSetupController(context); - if (doubleNavigationController != null) { - doubleNavigationController.pushController(boardEditController); - } else { - navigationController.pushController(boardEditController); - } - menu.dismiss(); + private void handleOrder(final ThreadPresenter presenter) { + List items = new ArrayList<>(); + for (PostsFilter.Order order : PostsFilter.Order.values()) { + int nameId = 0; + switch (order) { + case BUMP: + nameId = R.string.order_bump; + break; + case REPLY: + nameId = R.string.order_reply; + break; + case IMAGE: + nameId = R.string.order_image; + break; + case NEWEST: + nameId = R.string.order_newest; + break; + case OLDEST: + nameId = R.string.order_oldest; + break; + } + + String name = getString(nameId); + if (order == this.order) { + name = "\u2713 " + name; // Checkmark } + + items.add(new FloatingMenuItem(order, name)); } + + FloatingMenu menu = new FloatingMenu(context, overflow.getView(), items); + menu.setCallback(new FloatingMenu.FloatingMenuCallback() { + @Override + public void onFloatingMenuItemClicked(FloatingMenu menu, FloatingMenuItem item) { + PostsFilter.Order order = (PostsFilter.Order) item.getId(); + ChanSettings.boardOrder.set(order.name); + BrowseController.this.order = order; + presenter.setOrder(order); + } + + @Override + public void onFloatingMenuDismissed(FloatingMenu menu) { + } + }); + menu.show(); + } + + @Override + public void loadBoard(Loadable loadable) { + String name = BoardHelper.getName(loadable.board); + loadable.title = name; + navigationItem.title = name; + + ThreadPresenter presenter = threadLayout.getPresenter(); + presenter.unbindLoadable(); + presenter.bindLoadable(loadable); + presenter.requestData(); + + ((ToolbarNavigationController) navigationController).toolbar.updateTitle(navigationItem); } @Override - public void onFloatingMenuDismissed(FloatingMenu menu) { + public void loadSiteSetup(Site site) { + SiteSetupController siteSetupController = new SiteSetupController(context); + siteSetupController.setSite(site); + + if (doubleNavigationController != null) { + doubleNavigationController.pushController(siteSetupController); + } else { + navigationController.pushController(siteSetupController); + } } @Override @@ -316,110 +380,4 @@ public class BrowseController extends ThreadController implements ToolbarMenuIte navigationController.pushController(viewThreadController, animated); } } - - public void onEvent(BoardManager.BoardsChangedMessage event) { - loadBoards(); - } - - public void loadDefault() { - List savedBoards = boardManager.getSavedBoards(); - if (!savedBoards.isEmpty()) { - loadBoard(savedBoards.get(0)); - } - } - - public void loadBoard(Board board) { - Loadable loadable = databaseManager.getDatabaseLoadableManager().get(Loadable.forCatalog(board)); - loadable.title = BoardHelper.getName(board); - navigationItem.title = BoardHelper.getName(board); - - ThreadPresenter presenter = threadLayout.getPresenter(); - presenter.unbindLoadable(); - presenter.bindLoadable(loadable); - presenter.requestData(); - - for (FloatingMenuItem item : boardItems) { - if (((FloatingMenuItemBoard) item).board == board) { - navigationItem.middleMenu.setSelectedItem(item); - break; - } - } - ((ToolbarNavigationController) navigationController).toolbar.updateTitle(navigationItem); - } - - /** - * Load the menu with saved boards. Called on creation of this controller and when there's a - * board change event. - */ - private void loadBoards() { - List boards = boardManager.getSavedBoards(); - - boolean wasEmpty = boardItems == null || boardItems.isEmpty(); - - boardItems = new ArrayList<>(); - for (Board board : boards) { - FloatingMenuItem item = new FloatingMenuItemBoard(board); - boardItems.add(item); - } - - navigationItem.middleMenu.setItems(boardItems); - navigationItem.middleMenu.setAdapter(new BoardsAdapter(context, boardItems)); - - if (wasEmpty) { - loadDefault(); - } - } - - private static class FloatingMenuItemBoard extends FloatingMenuItem { - public Board board; - - public FloatingMenuItemBoard(Board board) { - super(board.id, BoardHelper.getName(board)); - this.board = board; - } - } - - private static class BoardsAdapter extends BaseAdapter { - private final Context context; - private List items; - - public BoardsAdapter(Context context, List items) { - this.context = context; - this.items = items; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - // No recycling, can't use itemtypes - @SuppressLint("ViewHolder") - TextView textView = (TextView) LayoutInflater.from(context).inflate(R.layout.toolbar_menu_item, parent, false); - textView.setText(getItem(position)); - if (position < items.size()) { - textView.setTypeface(AndroidUtils.ROBOTO_MEDIUM); - } else { - textView.setTypeface(AndroidUtils.ROBOTO_MEDIUM_ITALIC); - } - - return textView; - } - - @Override - public int getCount() { - return items.size() + 1; - } - - @Override - public String getItem(int position) { - if (position >= 0 && position < items.size()) { - return items.get(position).getText(); - } else { - return context.getString(R.string.thread_board_select_add); - } - } - - @Override - public long getItemId(int position) { - return position; - } - } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/DrawerController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/DrawerController.java index ab0aeebf..8888e352 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/DrawerController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/DrawerController.java @@ -224,8 +224,8 @@ public class DrawerController extends Controller implements DrawerAdapter.Callba } @Override - public void openBoardEditor() { - openController(new BoardEditController(context)); + public void openSites() { + openController(new SitesSetupController(context)); } @Override diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/MainSettingsController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/MainSettingsController.java index 576e2e0a..1efcabb3 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/MainSettingsController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/MainSettingsController.java @@ -31,10 +31,10 @@ import android.widget.Toast; import org.floens.chan.R; 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.settings.ChanSettings; import org.floens.chan.core.site.Sites; import org.floens.chan.ui.activity.StartActivity; +import org.floens.chan.ui.animation.AnimationUtils; import org.floens.chan.ui.helper.HintPopup; import org.floens.chan.ui.helper.RefreshUIMessage; import org.floens.chan.ui.settings.BooleanSettingView; @@ -48,7 +48,6 @@ 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 org.floens.chan.ui.animation.AnimationUtils; import java.util.ArrayList; import java.util.Collections; @@ -67,7 +66,6 @@ public class MainSettingsController extends SettingsController implements Toolba private ListSettingView imageAutoLoadView; private ListSettingView videoAutoLoadView; - private LinkSettingView boardEditorView; private LinkSettingView saveLocation; private LinkSettingView watchLink; private LinkSettingView passLink; @@ -154,10 +152,6 @@ public class MainSettingsController extends SettingsController implements Toolba } } - public void onEvent(BoardManager.BoardsChangedMessage message) { - updateBoardLinkDescription(); - } - public void onEvent(ChanSettings.SettingChanged setting) { if (setting.setting == ChanSettings.saveLocation) { setSaveLocationDescription(); @@ -203,14 +197,6 @@ public class MainSettingsController extends SettingsController implements Toolba private void populatePreferences() { // General group SettingsGroup general = new SettingsGroup(R.string.settings_group_general); - boardEditorView = new LinkSettingView(this, R.string.settings_board_edit, 0, new View.OnClickListener() { - @Override - public void onClick(View v) { - navigationController.pushController(new BoardEditController(context)); - } - }); - general.add(boardEditorView); - updateBoardLinkDescription(); watchLink = (LinkSettingView) general.add(new LinkSettingView(this, R.string.settings_watch, 0, new View.OnClickListener() { @Override @@ -446,11 +432,6 @@ public class MainSettingsController extends SettingsController implements Toolba groups.add(about); } - private void updateBoardLinkDescription() { - List savedBoards = boardManager.getSavedBoards(); - boardEditorView.setDescription(context.getResources().getQuantityString(R.plurals.board, savedBoards.size(), savedBoards.size())); - } - private void setSaveLocationDescription() { saveLocation.setDescription(ChanSettings.saveLocation.get()); } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/SetupController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/SetupController.java index 4b33d91a..59639c76 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/SetupController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/SetupController.java @@ -66,7 +66,9 @@ public class SetupController extends ToolbarNavigationController implements Setu @Override public void moveToSiteSetup() { - replaceController(new SitesSetupController(context), true); + SitesSetupController sitesSetupController = new SitesSetupController(context); + sitesSetupController.showDoneCheckmark(); + replaceController(sitesSetupController, true); } private void replaceController(Controller to, boolean showToolbar) { diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/SitesSetupController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/SitesSetupController.java index 0f98558a..c3aa8b57 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/SitesSetupController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/SitesSetupController.java @@ -79,11 +79,6 @@ public class SitesSetupController extends StyledToolbarNavigationController impl // Navigation navigationItem.setTitle(R.string.setup_sites_title); - navigationItem.swipeable = false; - navigationItem.menu = new ToolbarMenu(context); - doneMenuItem = navigationItem.menu.addItem( - new ToolbarMenuItem(context, this, DONE_ID, 0, R.drawable.ic_done_white_24dp)); - doneMenuItem.getView().setAlpha(0f); // View binding sitesRecyclerview = view.findViewById(R.id.sites_recycler); @@ -102,6 +97,14 @@ public class SitesSetupController extends StyledToolbarNavigationController impl presenter.create(this); } + public void showDoneCheckmark() { + navigationItem.swipeable = false; + navigationItem.menu = new ToolbarMenu(context); + doneMenuItem = navigationItem.menu.addItem( + new ToolbarMenuItem(context, this, DONE_ID, 0, R.drawable.ic_done_white_24dp)); + doneMenuItem.getView().setAlpha(0f); + } + @Override public void onMenuItemClicked(ToolbarMenuItem item) { if ((Integer) item.getId() == DONE_ID) { @@ -171,7 +174,9 @@ public class SitesSetupController extends StyledToolbarNavigationController impl @Override public void setNextAllowed(boolean nextAllowed, boolean animate) { - doneMenuItem.getView().animate().alpha(nextAllowed ? 1f : 0f).start(); + if (doneMenuItem != null) { + doneMenuItem.getView().animate().alpha(nextAllowed ? 1f : 0f).start(); + } } private void onSiteCellSettingsClicked(Site site) { diff --git a/Clover/app/src/main/java/org/floens/chan/ui/helper/BoardHelper.java b/Clover/app/src/main/java/org/floens/chan/ui/helper/BoardHelper.java index 23b1e1e1..45c85626 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/helper/BoardHelper.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/helper/BoardHelper.java @@ -20,7 +20,6 @@ package org.floens.chan.ui.helper; import android.util.Pair; import org.floens.chan.core.model.orm.Board; -import org.floens.chan.utils.Logger; import org.jsoup.parser.Parser; import java.util.ArrayList; @@ -70,8 +69,6 @@ public class BoardHelper { int name = FuzzySearch.ratio(board.name, query); int description = FuzzySearch.weightedRatio(String.valueOf(getDescription(board)), query); - Logger.d(TAG, board.code + " = code = " + code + ", name = " + name + ", desc = " + description); - return code * 4 + name * 5 + Math.max(0, description - 30) * 8; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/layout/BoardAddLayout.java b/Clover/app/src/main/java/org/floens/chan/ui/layout/BoardAddLayout.java index 4e4e9f9c..013063b0 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/layout/BoardAddLayout.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/layout/BoardAddLayout.java @@ -25,14 +25,13 @@ import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ImageView; +import android.widget.CheckBox; import android.widget.LinearLayout; import android.widget.TextView; import org.floens.chan.R; import org.floens.chan.core.presenter.BoardSetupPresenter; -import static org.floens.chan.ui.theme.ThemeHelper.theme; import static org.floens.chan.utils.AndroidUtils.getAttrColor; import static org.floens.chan.utils.AndroidUtils.getString; @@ -75,22 +74,22 @@ public class BoardAddLayout extends LinearLayout implements SearchLayout.SearchL search.setTextColor(getAttrColor(getContext(), R.attr.text_color_primary)); search.setHintColor(getAttrColor(getContext(), R.attr.text_color_hint)); search.setClearButtonImage(R.drawable.ic_clear_black_24dp); - search.openKeyboard(); suggestionsRecycler.setLayoutManager(new LinearLayoutManager(getContext())); suggestionsRecycler.setAdapter(suggestionsAdapter); + suggestionsRecycler.requestFocus(); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - presenter.setAddCallback(this); + presenter.bindAddDialog(this); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - presenter.setAddCallback(null); + presenter.unbindAddDialog(); } @Override @@ -120,6 +119,15 @@ public class BoardAddLayout extends LinearLayout implements SearchLayout.SearchL } private class SuggestionsAdapter extends RecyclerView.Adapter { + public SuggestionsAdapter() { + setHasStableIds(true); + } + + @Override + public long getItemId(int position) { + return presenter.getSuggestions().get(position).getId(); + } + @Override public int getItemCount() { return presenter.getSuggestions().size(); @@ -138,14 +146,13 @@ public class BoardAddLayout extends LinearLayout implements SearchLayout.SearchL holder.setSuggestion(boardSuggestion); holder.text.setText(boardSuggestion.getName()); holder.description.setText(boardSuggestion.getDescription()); - holder.check.setVisibility(boardSuggestion.isChecked() ? View.VISIBLE : View.INVISIBLE); } } private class SuggestionCell extends RecyclerView.ViewHolder implements OnClickListener { private TextView text; private TextView description; - private ImageView check; + private CheckBox check; private BoardSetupPresenter.BoardSuggestion suggestion; @@ -155,19 +162,20 @@ public class BoardAddLayout extends LinearLayout implements SearchLayout.SearchL text = itemView.findViewById(R.id.text); description = itemView.findViewById(R.id.description); check = itemView.findViewById(R.id.check); - theme().doneDrawable.apply(check); itemView.setOnClickListener(this); } public void setSuggestion(BoardSetupPresenter.BoardSuggestion suggestion) { this.suggestion = suggestion; + check.setChecked(suggestion.isChecked()); } @Override public void onClick(View v) { if (v == itemView) { onSuggestionClicked(suggestion); + check.setChecked(suggestion.isChecked()); } } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/layout/BrowseBoardsFloatingMenu.java b/Clover/app/src/main/java/org/floens/chan/ui/layout/BrowseBoardsFloatingMenu.java new file mode 100644 index 00000000..e63f62f5 --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/ui/layout/BrowseBoardsFloatingMenu.java @@ -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 . + */ +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 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> siteListPair : savedBoards.get()) { + count += 1; + count += siteListPair.second.size(); + } + + return count; + } + + private int resolveCurrentIndex(Board board) { + int position = 0; + for (Pair> siteListPair : savedBoards.get()) { + position += 1; + + for (Board other : siteListPair.second) { + if (board == other) { + return position; + } + position++; + } + } + + return 0; + } + + private Pair getAtPosition(int position) { + for (Pair> 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 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 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); + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/NavigationItem.java b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/NavigationItem.java index 8f538bbe..3e95122c 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/NavigationItem.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/NavigationItem.java @@ -30,10 +30,8 @@ import static org.floens.chan.utils.AndroidUtils.getString; public class NavigationItem { public String title = ""; public String subtitle = ""; - public ToolbarMenu menu; + public boolean hasBack = true; - public FloatingMenu middleMenu; - public View rightView; public boolean hasDrawer = false; public boolean handlesToolbarInset = false; public boolean swipeable = true; @@ -41,6 +39,10 @@ public class NavigationItem { boolean search = false; String searchText; + public ToolbarMenu menu; + public ToolbarMiddleMenu middleMenu; + public View rightView; + public ToolbarMenuItem createOverflow(Context context, ToolbarMenuItem.ToolbarMenuItemCallback callback, List items) { ToolbarMenuItem overflow = menu.createOverflow(callback); FloatingMenu overflowMenu = new FloatingMenu(context, overflow.getView(), items); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/Toolbar.java b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/Toolbar.java index f96c4d60..8f0badc0 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/Toolbar.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/Toolbar.java @@ -43,7 +43,6 @@ import org.floens.chan.R; import org.floens.chan.ui.drawable.ArrowMenuDrawable; import org.floens.chan.ui.drawable.DropdownArrowDrawable; import org.floens.chan.ui.layout.SearchLayout; -import org.floens.chan.ui.view.FloatingMenu; import org.floens.chan.ui.view.LoadView; import org.floens.chan.utils.AndroidUtils; @@ -479,15 +478,15 @@ public class Toolbar extends LinearLayout implements View.OnClickListener { titleView.setTextColor(0xffffffff); if (item.middleMenu != null) { - item.middleMenu.setAnchor(titleView, Gravity.LEFT, dp(5), dp(5)); - - Drawable drawable = new DropdownArrowDrawable(dp(12), dp(12), true, getAttrColor(getContext(), R.attr.dropdown_light_color), getAttrColor(getContext(), R.attr.dropdown_light_pressed_color)); + int arrowColor = getAttrColor(getContext(), R.attr.dropdown_light_color); + int arrowPressedColor = getAttrColor(getContext(), R.attr.dropdown_light_pressed_color); + Drawable drawable = new DropdownArrowDrawable(dp(12), dp(12), true, arrowColor, arrowPressedColor); titleView.setCompoundDrawablesWithIntrinsicBounds(null, null, drawable, null); titleView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - item.middleMenu.show(); + item.middleMenu.show(titleView); } }); } @@ -515,10 +514,6 @@ public class Toolbar extends LinearLayout implements View.OnClickListener { menu.addView(item.menu, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT)); } - if (item.middleMenu != null) { - item.middleMenu.setPopupWidth(FloatingMenu.POPUP_WIDTH_ANCHOR); - } - return menu; } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenu.java b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenu.java index b72cd0be..48ef772c 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenu.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenu.java @@ -34,21 +34,16 @@ public class ToolbarMenu extends LinearLayout { private List items = new ArrayList<>(); public ToolbarMenu(Context context) { - super(context); - init(); + this(context, null); } public ToolbarMenu(Context context, AttributeSet attrs) { - super(context, attrs); - init(); + this(context, attrs, 0); } - public ToolbarMenu(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(); - } + public ToolbarMenu(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); - private void init() { setOrientation(HORIZONTAL); setGravity(Gravity.CENTER_VERTICAL); } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMiddleMenu.java b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMiddleMenu.java new file mode 100644 index 00000000..739523a1 --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMiddleMenu.java @@ -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 . + */ +package org.floens.chan.ui.toolbar; + +import android.view.View; + +public interface ToolbarMiddleMenu { + void show(View anchor); +} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/FloatingMenu.java b/Clover/app/src/main/java/org/floens/chan/ui/view/FloatingMenu.java index c9dd68fc..1f5e3575 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/view/FloatingMenu.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/view/FloatingMenu.java @@ -50,9 +50,12 @@ public class FloatingMenu { private int anchorOffsetY; private int popupWidth = POPUP_WIDTH_AUTO; private int popupHeight = -1; + private boolean manageItems = true; private List items; private FloatingMenuItem selectedItem; + private int selectedPosition; private ListAdapter adapter; + private AdapterView.OnItemClickListener itemClickListener; private ViewTreeObserver.OnGlobalLayoutListener globalLayoutListener; private ListPopupWindow popupWindow; @@ -93,13 +96,20 @@ public class FloatingMenu { } public void setItems(List items) { + if (!manageItems) throw new IllegalArgumentException(); this.items = items; } public void setSelectedItem(FloatingMenuItem item) { + if (!manageItems) throw new IllegalArgumentException(); this.selectedItem = item; } + public void setSelectedPosition(int selectedPosition) { + if (manageItems) throw new IllegalArgumentException(); + this.selectedPosition = selectedPosition; + } + public void setAdapter(ListAdapter adapter) { this.adapter = adapter; if (popupWindow != null) { @@ -111,6 +121,17 @@ public class FloatingMenu { this.callback = callback; } + public void setManageItems(boolean manageItems) { + this.manageItems = manageItems; + } + + public void setOnItemClickListener(AdapterView.OnItemClickListener listener) { + this.itemClickListener = listener; + if (popupWindow != null) { + popupWindow.setOnItemClickListener(listener); + } + } + public void show() { popupWindow = new ListPopupWindow(context); popupWindow.setAnchorView(anchor); @@ -130,11 +151,15 @@ public class FloatingMenu { popupWindow.setHeight(popupHeight); } - int selectedPosition = 0; - for (int i = 0; i < items.size(); i++) { - if (items.get(i) == selectedItem) { - selectedPosition = i; + int selection = 0; + if (manageItems) { + for (int i = 0; i < items.size(); i++) { + if (items.get(i) == selectedItem) { + selection = i; + } } + } else { + selection = this.selectedPosition; } if (adapter != null) { @@ -143,20 +168,24 @@ public class FloatingMenu { popupWindow.setAdapter(new FloatingMenuArrayAdapter(context, R.layout.toolbar_menu_item, items)); } - popupWindow.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - if (position >= 0 && position < items.size()) { - FloatingMenuItem item = items.get(position); - if (item.isEnabled()) { - callback.onFloatingMenuItemClicked(FloatingMenu.this, item); - popupWindow.dismiss(); + if (manageItems) { + popupWindow.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + if (position >= 0 && position < items.size()) { + FloatingMenuItem item = items.get(position); + if (item.isEnabled()) { + callback.onFloatingMenuItemClicked(FloatingMenu.this, item); + popupWindow.dismiss(); + } + } else { + callback.onFloatingMenuItemClicked(FloatingMenu.this, null); } - } else { - callback.onFloatingMenuItemClicked(FloatingMenu.this, null); } - } - }); + }); + } else { + popupWindow.setOnItemClickListener(itemClickListener); + } globalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() { @Override @@ -186,7 +215,7 @@ public class FloatingMenu { }); popupWindow.show(); - popupWindow.setSelection(selectedPosition); + popupWindow.setSelection(selection); } public boolean isShowing() { @@ -206,6 +235,16 @@ public class FloatingMenu { void onFloatingMenuDismissed(FloatingMenu menu); } + public static class FloatingMenuCallbackAdapter implements FloatingMenuCallback { + @Override + public void onFloatingMenuItemClicked(FloatingMenu menu, FloatingMenuItem item) { + } + + @Override + public void onFloatingMenuDismissed(FloatingMenu menu) { + } + } + private static class FloatingMenuArrayAdapter extends ArrayAdapter { public FloatingMenuArrayAdapter(Context context, int resource, List objects) { super(context, resource, objects); diff --git a/Clover/app/src/main/res/layout/cell_board_edit.xml b/Clover/app/src/main/res/layout/cell_board_edit.xml deleted file mode 100644 index bee95b43..00000000 --- a/Clover/app/src/main/res/layout/cell_board_edit.xml +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - - - - diff --git a/Clover/app/src/main/res/layout/cell_board_suggestion.xml b/Clover/app/src/main/res/layout/cell_board_suggestion.xml index 94b8edd2..fd8a3360 100644 --- a/Clover/app/src/main/res/layout/cell_board_suggestion.xml +++ b/Clover/app/src/main/res/layout/cell_board_suggestion.xml @@ -22,11 +22,18 @@ along with this program. If not, see . android:background="?attr/selectableItemBackground" android:orientation="horizontal"> + + + android:orientation="vertical" + android:paddingLeft="8dp"> . - - diff --git a/Clover/app/src/main/res/layout/cell_board_edit_header.xml b/Clover/app/src/main/res/layout/cell_browse_board.xml similarity index 59% rename from Clover/app/src/main/res/layout/cell_board_edit_header.xml rename to Clover/app/src/main/res/layout/cell_browse_board.xml index 7b616df0..ab107a76 100644 --- a/Clover/app/src/main/res/layout/cell_board_edit_header.xml +++ b/Clover/app/src/main/res/layout/cell_browse_board.xml @@ -15,20 +15,14 @@ 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 . --> - - - - - + android:layout_height="48dp" + android:ellipsize="end" + android:gravity="center_vertical" + android:lines="1" + android:paddingLeft="16dp" + android:paddingRight="16dp" + android:singleLine="true" + android:textColor="?text_color_primary" + android:textSize="15sp" /> diff --git a/Clover/app/src/main/res/layout/cell_browse_site.xml b/Clover/app/src/main/res/layout/cell_browse_site.xml new file mode 100644 index 00000000..b20379cd --- /dev/null +++ b/Clover/app/src/main/res/layout/cell_browse_site.xml @@ -0,0 +1,55 @@ + + + + + + + + + + diff --git a/Clover/app/src/main/res/values/strings.xml b/Clover/app/src/main/res/values/strings.xml index 2dec92f2..210e12f8 100644 --- a/Clover/app/src/main/res/values/strings.xml +++ b/Clover/app/src/main/res/values/strings.xml @@ -181,22 +181,8 @@ Re-enable this permission in the app settings if you permanently disabled it."Configure boards of %s Add board - - Your boards - "Change your saved boards. These will appear in a list at the top." - Board code - lit - - Board editor - Add, remove and reorder your boards here.\nThe topmost board will be loaded automatically. - Add board - Board code e.g. lit - Added board - Board already added - Unknown board code - The board with code %1$s is not known. - Sort A-Z - Removed board \'%1$s\' + Removed \'%s\' + %s added all boards Enabled @@ -359,9 +345,11 @@ Re-enable this permission in the app settings if you permanently disabled it."Saving image failed Cannot make save directory + Sites + History + Settings General - Boards Thread watcher 4chan pass Try the advanced settings!