From 29e1574b60459efcc575d249030e70d16f9df328 Mon Sep 17 00:00:00 2001 From: Floens Date: Mon, 2 Jul 2018 16:11:25 +0200 Subject: [PATCH] Add navigationitem menubuilder, convert all controllers to it. --- .../floens/chan/controller/Controller.java | 5 + .../controller/AlbumDownloadController.java | 44 ++- .../ui/controller/AlbumViewController.java | 42 +-- .../chan/ui/controller/ArchiveController.java | 17 +- .../chan/ui/controller/BrowseController.java | 186 ++++++------ .../chan/ui/controller/FiltersController.java | 26 +- .../chan/ui/controller/HistoryController.java | 81 +++-- .../ui/controller/ImageViewerController.java | 194 ++++++------ .../chan/ui/controller/LogsController.java | 34 +-- .../ui/controller/SitesSetupController.java | 30 +- .../chan/ui/controller/ThreadController.java | 2 +- .../ToolbarNavigationController.java | 1 + .../ui/controller/ViewThreadController.java | 187 ++++++------ .../controller/WatchSettingsController.java | 2 +- .../chan/ui/toolbar/NavigationItem.java | 133 +++++++-- .../org/floens/chan/ui/toolbar/Toolbar.java | 116 +------- .../chan/ui/toolbar/ToolbarContainer.java | 278 ++++++++++++++---- .../floens/chan/ui/toolbar/ToolbarMenu.java | 56 ++-- .../chan/ui/toolbar/ToolbarMenuItem.java | 170 +++++++---- .../chan/ui/toolbar/ToolbarMenuSubItem.java | 64 ++++ .../chan/ui/toolbar/ToolbarMenuView.java | 100 +++++++ .../org/floens/chan/ui/view/FloatingMenu.java | 16 +- 22 files changed, 1049 insertions(+), 735 deletions(-) create mode 100644 Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenuSubItem.java create mode 100644 Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenuView.java diff --git a/Clover/app/src/main/java/org/floens/chan/controller/Controller.java b/Clover/app/src/main/java/org/floens/chan/controller/Controller.java index 6a83e8e3..a119e7fc 100644 --- a/Clover/app/src/main/java/org/floens/chan/controller/Controller.java +++ b/Clover/app/src/main/java/org/floens/chan/controller/Controller.java @@ -29,6 +29,7 @@ import org.floens.chan.controller.transition.FadeOutTransition; import org.floens.chan.ui.activity.StartActivity; import org.floens.chan.ui.controller.DoubleNavigationController; import org.floens.chan.ui.toolbar.NavigationItem; +import org.floens.chan.ui.toolbar.Toolbar; import org.floens.chan.utils.AndroidUtils; import org.floens.chan.utils.Logger; @@ -247,6 +248,10 @@ public abstract class Controller { return (ViewGroup) LayoutInflater.from(context).inflate(resId, null); } + public Toolbar getToolbar() { + return null; + } + private void attachToView(ViewGroup parentView, boolean over) { ViewGroup.LayoutParams params = view.getLayoutParams(); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/AlbumDownloadController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/AlbumDownloadController.java index a2ff2709..9b558029 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/AlbumDownloadController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/AlbumDownloadController.java @@ -33,14 +33,12 @@ import android.widget.ImageView; import org.floens.chan.R; import org.floens.chan.controller.Controller; -import org.floens.chan.core.model.orm.Loadable; import org.floens.chan.core.model.PostImage; +import org.floens.chan.core.model.orm.Loadable; import org.floens.chan.core.saver.ImageSaveTask; import org.floens.chan.core.saver.ImageSaver; import org.floens.chan.ui.theme.ThemeHelper; -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.ui.view.GridRecyclerView; import org.floens.chan.ui.view.PostImageThumbnailView; import org.floens.chan.utils.RecyclerUtils; @@ -51,9 +49,7 @@ import java.util.List; import static org.floens.chan.ui.theme.ThemeHelper.theme; import static org.floens.chan.utils.AndroidUtils.dp; -public class AlbumDownloadController extends Controller implements ToolbarMenuItem.ToolbarMenuItemCallback, View.OnClickListener { - private static final int CHECK_ALL = 1; - +public class AlbumDownloadController extends Controller implements View.OnClickListener { private GridRecyclerView recyclerView; private GridLayoutManager gridLayoutManager; private FloatingActionButton download; @@ -79,8 +75,9 @@ public class AlbumDownloadController extends Controller implements ToolbarMenuIt updateTitle(); - navigation.menu = new ToolbarMenu(context); - navigation.menu.addItem(new ToolbarMenuItem(context, this, CHECK_ALL, R.drawable.ic_select_all_white_24dp)); + navigation.buildMenu() + .withItem(R.drawable.ic_select_all_white_24dp, this::onCheckAllClicked) + .build(); download = view.findViewById(R.id.download); download.setOnClickListener(this); @@ -134,28 +131,21 @@ public class AlbumDownloadController extends Controller implements ToolbarMenuIt } } - @Override - public void onMenuItemClicked(ToolbarMenuItem menuItem) { - if ((Integer) menuItem.getId() == CHECK_ALL) { - RecyclerUtils.clearRecyclerCache(recyclerView); - - for (int i = 0, itemsSize = items.size(); i < itemsSize; i++) { - AlbumDownloadItem item = items.get(i); - if (item.checked == allChecked) { - item.checked = !allChecked; - AlbumDownloadCell cell = (AlbumDownloadCell) recyclerView.findViewHolderForAdapterPosition(i); - if (cell != null) { - setItemChecked(cell, item.checked, true); - } + private void onCheckAllClicked(ToolbarMenuItem menuItem) { + RecyclerUtils.clearRecyclerCache(recyclerView); + + for (int i = 0, itemsSize = items.size(); i < itemsSize; i++) { + AlbumDownloadItem item = items.get(i); + if (item.checked == allChecked) { + item.checked = !allChecked; + AlbumDownloadCell cell = (AlbumDownloadCell) recyclerView.findViewHolderForAdapterPosition(i); + if (cell != null) { + setItemChecked(cell, item.checked, true); } } - updateAllChecked(); - updateTitle(); } - } - - @Override - public void onSubMenuItemClicked(ToolbarMenuItem parent, FloatingMenuItem item) { + updateAllChecked(); + updateTitle(); } public void setPostImages(Loadable loadable, List postImages) { diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/AlbumViewController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/AlbumViewController.java index 09fff978..ea445154 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/AlbumViewController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/AlbumViewController.java @@ -26,24 +26,21 @@ import android.view.ViewGroup; import org.floens.chan.R; import org.floens.chan.controller.Controller; -import org.floens.chan.core.model.orm.Loadable; import org.floens.chan.core.model.PostImage; +import org.floens.chan.core.model.orm.Loadable; import org.floens.chan.ui.cell.AlbumViewCell; -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.ui.toolbar.ToolbarMenuSubItem; import org.floens.chan.ui.view.GridRecyclerView; import org.floens.chan.ui.view.PostImageThumbnailView; import org.floens.chan.ui.view.ThumbnailView; -import java.util.ArrayList; import java.util.List; import static org.floens.chan.utils.AndroidUtils.dp; -public class AlbumViewController extends Controller implements ImageViewerController.ImageViewerCallback, ImageViewerController.GoPostCallback, ToolbarMenuItem.ToolbarMenuItemCallback { - private static final int SAVE_ALBUM_ID = 101; - +public class AlbumViewController extends Controller implements + ImageViewerController.ImageViewerCallback, + ImageViewerController.GoPostCallback { private GridRecyclerView recyclerView; private GridLayoutManager gridLayoutManager; @@ -61,13 +58,13 @@ public class AlbumViewController extends Controller implements ImageViewerContro public void onCreate() { super.onCreate(); - view = inflateRes(R.layout.controller_album_view); - - navigation.menu = new ToolbarMenu(context); - List items = new ArrayList<>(); - items.add(new FloatingMenuItem(SAVE_ALBUM_ID, R.string.action_download_album)); - navigation.createOverflow(context, this, items); + // Navigation + navigation.buildMenu().withOverflow() + .withSubItem(R.string.action_download_album, this::downloadAlbumClicked) + .build().build(); + // View setup + view = inflateRes(R.layout.controller_album_view); recyclerView = view.findViewById(R.id.recycler_view); recyclerView.setHasFixedSize(true); gridLayoutManager = new GridLayoutManager(context, 3); @@ -88,19 +85,10 @@ public class AlbumViewController extends Controller implements ImageViewerContro targetIndex = index; } - @Override - public void onMenuItemClicked(ToolbarMenuItem item) { - } - - @Override - public void onSubMenuItemClicked(ToolbarMenuItem parent, FloatingMenuItem item) { - switch ((Integer) item.getId()) { - case SAVE_ALBUM_ID: - AlbumDownloadController albumDownloadController = new AlbumDownloadController(context); - albumDownloadController.setPostImages(loadable, postImages); - navigationController.pushController(albumDownloadController); - break; - } + private void downloadAlbumClicked(ToolbarMenuSubItem item) { + AlbumDownloadController albumDownloadController = new AlbumDownloadController(context); + albumDownloadController.setPostImages(loadable, postImages); + navigationController.pushController(albumDownloadController); } @Override diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/ArchiveController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/ArchiveController.java index bbfe83fc..3ffae763 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/ArchiveController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/ArchiveController.java @@ -33,12 +33,10 @@ import org.floens.chan.core.model.orm.Board; import org.floens.chan.core.model.orm.Loadable; import org.floens.chan.core.presenter.ArchivePresenter; 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.CrossfadeView; import org.floens.chan.ui.view.DividerItemDecoration; import org.floens.chan.ui.view.FastScrollerHelper; -import org.floens.chan.ui.view.FloatingMenuItem; import java.util.ArrayList; import java.util.List; @@ -48,7 +46,6 @@ import javax.inject.Inject; import static org.floens.chan.Chan.inject; public class ArchiveController extends Controller implements ArchivePresenter.Callback, - ToolbarMenuItem.ToolbarMenuItemCallback, ToolbarNavigationController.ToolbarSearchCallback, SwipeRefreshLayout.OnRefreshListener { private static final int SEARCH_ID = 1; @@ -84,9 +81,9 @@ public class ArchiveController extends Controller implements ArchivePresenter.Ca // Navigation navigation.title = context.getString(R.string.archive_title, BoardHelper.getName(board)); - ToolbarMenu menu = new ToolbarMenu(context); - navigation.menu = menu; - menu.addItem(new ToolbarMenuItem(context, this, SEARCH_ID, R.drawable.ic_search_white_24dp)); + navigation.buildMenu() + .withItem(R.drawable.ic_search_white_24dp, this::searchClicked) + .build(); // View binding crossfadeView = view.findViewById(R.id.crossfade); @@ -112,18 +109,12 @@ public class ArchiveController extends Controller implements ArchivePresenter.Ca presenter.create(this, board); } - @Override - public void onMenuItemClicked(ToolbarMenuItem item) { + private void searchClicked(ToolbarMenuItem item) { ((ToolbarNavigationController) navigationController).showSearch(); } - @Override - public void onSubMenuItemClicked(ToolbarMenuItem parent, FloatingMenuItem item) { - } - @Override public void onSearchEntered(String entered) { - presenter.onSearchEntered(entered); } @Override 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 2f4bd885..318088b2 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 @@ -17,12 +17,13 @@ */ package org.floens.chan.ui.controller; -import android.annotation.SuppressLint; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.app.Activity; import android.content.Context; import android.view.View; +import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; -import android.widget.ImageView; import org.floens.chan.R; import org.floens.chan.core.model.orm.Board; @@ -36,9 +37,10 @@ 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.NavigationItem; 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.toolbar.ToolbarMenuSubItem; import org.floens.chan.ui.view.FloatingMenu; import org.floens.chan.ui.view.FloatingMenuItem; import org.floens.chan.utils.AndroidUtils; @@ -52,18 +54,11 @@ import static org.floens.chan.Chan.inject; import static org.floens.chan.utils.AndroidUtils.getString; public class BrowseController extends ThreadController implements - ToolbarMenuItem.ToolbarMenuItemCallback, ThreadLayout.ThreadLayoutCallback, BrowsePresenter.Callback, BrowseBoardsFloatingMenu.ClickCallback { - private static final int SEARCH_ID = 1; - private static final int REFRESH_ID = 2; - private static final int REPLY_ID = 101; - private static final int SHARE_ID = 103; - private static final int VIEW_MODE_ID = 104; - private static final int ORDER_ID = 105; - private static final int OPEN_BROWSER_ID = 106; - private static final int ARCHIVE_ID = 107; + private static final int VIEW_MODE_ID = 1; + private static final int ARCHIVE_ID = 2; @Inject BrowsePresenter presenter; @@ -71,12 +66,6 @@ public class BrowseController extends ThreadController implements private ChanSettings.PostViewMode postViewMode; private PostsFilter.Order order; - private FloatingMenuItem viewModeMenuItem; - private ToolbarMenuItem search; - private ToolbarMenuItem refresh; - private ToolbarMenuItem overflow; - private FloatingMenuItem archive; - public BrowseController(Context context) { super(context); } @@ -118,11 +107,9 @@ public class BrowseController extends ThreadController implements setupMiddleNavigation(); // Toolbar menu - ToolbarMenu menu = new ToolbarMenu(context); - navigation.menu = menu; navigation.hasBack = false; - search = menu.addItem(new ToolbarMenuItem(context, this, SEARCH_ID, R.drawable.ic_search_white_24dp)); + /*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 @@ -144,73 +131,102 @@ public class BrowseController extends ThreadController implements items.add(new FloatingMenuItem(OPEN_BROWSER_ID, R.string.action_open_browser)); items.add(new FloatingMenuItem(SHARE_ID, R.string.action_share)); - overflow.setSubMenu(new FloatingMenu(context, overflow.getView(), items)); + overflow.setSubMenu(new FloatingMenu(context, overflow.getView(), items));*/ + + NavigationItem.MenuOverflowBuilder overflowBuilder = navigation.buildMenu() + .withItem(R.drawable.ic_search_white_24dp, this::searchClicked) + .withItem(R.drawable.ic_refresh_white_24dp, this::reloadClicked) + .withOverflow(); + + if (!ChanSettings.enableReplyFab.get()) { + overflowBuilder.withSubItem(R.string.action_reply, this::replyClicked); + } + + overflowBuilder.withSubItem(VIEW_MODE_ID, + postViewMode == ChanSettings.PostViewMode.LIST ? + R.string.action_switch_catalog : R.string.action_switch_board, + this::viewModeClicked); + + overflowBuilder + .withSubItem(ARCHIVE_ID, R.string.thread_view_archive, this::archiveClicked) + .withSubItem(R.string.action_order, this::orderClicked) + .withSubItem(R.string.action_open_browser, this::openBrowserClicked) + .withSubItem(R.string.action_share, this::shareClicked) + .build() + .build(); // Presenter presenter.create(this); } - private void setupMiddleNavigation() { - navigation.middleMenu = new ToolbarMiddleMenu() { - @SuppressLint("InflateParams") - @Override - public void show(View anchor) { - BrowseBoardsFloatingMenu boardsFloatingMenu = new BrowseBoardsFloatingMenu(context); - boardsFloatingMenu.show(view, anchor, BrowseController.this, - presenter.currentBoard()); - } - }; + private void searchClicked(ToolbarMenuItem item) { + ThreadPresenter presenter = threadLayout.getPresenter(); + if (presenter.isBound()) { + View refreshView = item.getView(); + refreshView.setScaleX(1f); + refreshView.setScaleY(1f); + refreshView.animate() + .scaleX(10f) + .scaleY(10f) + .setDuration(500) + .setInterpolator(new AccelerateInterpolator(2f)) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + refreshView.setScaleX(1f); + refreshView.setScaleY(1f); + } + }); + + ((ToolbarNavigationController) navigationController).showSearch(); + } } - @Override - public void onMenuItemClicked(ToolbarMenuItem item) { + private void reloadClicked(ToolbarMenuItem item) { ThreadPresenter presenter = threadLayout.getPresenter(); - - switch ((Integer) item.getId()) { - case SEARCH_ID: - if (presenter.isBound()) { - ((ToolbarNavigationController) navigationController).showSearch(); - } - break; - case REFRESH_ID: - if (presenter.isBound()) { - presenter.requestData(); - ImageView refreshView = refresh.getView(); - refreshView.setRotation(0f); - refreshView.animate() - .rotation(360f) - .setDuration(500) - .setInterpolator(new DecelerateInterpolator(2f)); - } - break; + if (presenter.isBound()) { + presenter.requestData(); + + // Give the rotation menu item view a spin. + View refreshView = item.getView(); + refreshView.setRotation(0f); + refreshView.animate() + .rotation(360f) + .setDuration(500) + .setInterpolator(new DecelerateInterpolator(2f)); } } - @Override - public void onSubMenuItemClicked(ToolbarMenuItem parent, FloatingMenuItem item) { - final ThreadPresenter presenter = threadLayout.getPresenter(); - - Integer id = (Integer) item.getId(); - switch (id) { - case REPLY_ID: - handleReply(); - break; - case SHARE_ID: - case OPEN_BROWSER_ID: - handleShareAndOpenInBrowser(presenter, id); - break; - case VIEW_MODE_ID: - handleViewMode(); - - break; - case ORDER_ID: - handleOrder(presenter); - - break; - case ARCHIVE_ID: - openArchive(); - break; - } + private void replyClicked(ToolbarMenuSubItem item) { + threadLayout.openReply(true); + } + + private void viewModeClicked(ToolbarMenuSubItem item) { + handleViewMode(item); + } + + private void archiveClicked(ToolbarMenuSubItem item) { + openArchive(); + } + + private void orderClicked(ToolbarMenuSubItem item) { + handleOrder(threadLayout.getPresenter()); + } + + private void openBrowserClicked(ToolbarMenuSubItem item) { + handleShareAndOpenInBrowser(threadLayout.getPresenter(), false); + } + + private void shareClicked(ToolbarMenuSubItem item) { + handleShareAndOpenInBrowser(threadLayout.getPresenter(), true); + } + + private void setupMiddleNavigation() { + navigation.setMiddleMenu(anchor -> { + BrowseBoardsFloatingMenu boardsFloatingMenu = new BrowseBoardsFloatingMenu(context); + boardsFloatingMenu.show(view, anchor, BrowseController.this, + presenter.currentBoard()); + }); } @Override @@ -239,16 +255,12 @@ public class BrowseController extends ThreadController implements } } - private void handleReply() { - threadLayout.openReply(true); - } - - private void handleShareAndOpenInBrowser(ThreadPresenter presenter, Integer id) { + private void handleShareAndOpenInBrowser(ThreadPresenter presenter, boolean share) { if (presenter.isBound()) { Loadable loadable = presenter.getLoadable(); String link = loadable.site.resolvable().desktopUrl(loadable, null); - if (id == SHARE_ID) { + if (share) { AndroidUtils.shareLink(link); } else { AndroidUtils.openLinkInBrowser((Activity) context, link); @@ -256,7 +268,7 @@ public class BrowseController extends ThreadController implements } } - private void handleViewMode() { + private void handleViewMode(ToolbarMenuSubItem item) { if (postViewMode == ChanSettings.PostViewMode.LIST) { postViewMode = ChanSettings.PostViewMode.CARD; } else { @@ -267,7 +279,7 @@ public class BrowseController extends ThreadController implements int viewModeText = postViewMode == ChanSettings.PostViewMode.LIST ? R.string.action_switch_catalog : R.string.action_switch_board; - viewModeMenuItem.setText(context.getString(viewModeText)); + item.text = context.getString(viewModeText); threadLayout.setPostViewMode(postViewMode); } @@ -302,6 +314,7 @@ public class BrowseController extends ThreadController implements items.add(new FloatingMenuItem(order, name)); } + ToolbarMenuItem overflow = navigation.findItem(ToolbarMenu.OVERFLOW_ID); FloatingMenu menu = new FloatingMenu(context, overflow.getView(), items); menu.setCallback(new FloatingMenu.FloatingMenuCallback() { @Override @@ -347,7 +360,8 @@ public class BrowseController extends ThreadController implements @Override public void showArchiveOption(boolean show) { - archive.setEnabled(show); + ToolbarMenuSubItem archive = navigation.findSubItem(ARCHIVE_ID); + archive.enabled = show; } @Override diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/FiltersController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/FiltersController.java index c2d14914..226ee46e 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/FiltersController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/FiltersController.java @@ -38,9 +38,7 @@ import org.floens.chan.core.manager.FilterType; import org.floens.chan.core.model.orm.Filter; import org.floens.chan.ui.helper.RefreshUIMessage; import org.floens.chan.ui.layout.FilterLayout; -import org.floens.chan.ui.toolbar.ToolbarMenu; import org.floens.chan.ui.toolbar.ToolbarMenuItem; -import org.floens.chan.ui.view.FloatingMenuItem; import java.util.ArrayList; import java.util.List; @@ -55,10 +53,9 @@ 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; -public class FiltersController extends Controller implements ToolbarMenuItem.ToolbarMenuItemCallback, ToolbarNavigationController.ToolbarSearchCallback, View.OnClickListener { - private static final int SEARCH_ID = 1; - private static final int CLEAR_ID = 101; - +public class FiltersController extends Controller implements + ToolbarNavigationController.ToolbarSearchCallback, + View.OnClickListener { @Inject DatabaseManager databaseManager; @@ -109,8 +106,10 @@ public class FiltersController extends Controller implements ToolbarMenuItem.Too inject(this); navigation.setTitle(R.string.filters_screen); - navigation.menu = new ToolbarMenu(context); - navigation.menu.addItem(new ToolbarMenuItem(context, this, SEARCH_ID, R.drawable.ic_search_white_24dp)); + + navigation.buildMenu() + .withItem(R.drawable.ic_search_white_24dp, this::searchClicked) + .build(); view = inflateRes(R.layout.controller_filters); @@ -134,15 +133,8 @@ public class FiltersController extends Controller implements ToolbarMenuItem.Too } } - @Override - public void onMenuItemClicked(ToolbarMenuItem item) { - if ((Integer) item.getId() == SEARCH_ID) { - ((ToolbarNavigationController) navigationController).showSearch(); - } - } - - @Override - public void onSubMenuItemClicked(ToolbarMenuItem parent, FloatingMenuItem item) { + private void searchClicked(ToolbarMenuItem item) { + ((ToolbarNavigationController) navigationController).showSearch(); } public void showFilterDialog(final Filter filter) { diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/HistoryController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/HistoryController.java index 801bfdf0..d0a6d4f3 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/HistoryController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/HistoryController.java @@ -18,7 +18,6 @@ package org.floens.chan.ui.controller; import android.content.Context; -import android.content.DialogInterface; import android.support.v7.app.AlertDialog; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; @@ -41,10 +40,9 @@ import org.floens.chan.core.model.orm.Board; import org.floens.chan.core.model.orm.History; import org.floens.chan.core.settings.ChanSettings; import org.floens.chan.ui.helper.HintPopup; -import org.floens.chan.ui.toolbar.ToolbarMenu; import org.floens.chan.ui.toolbar.ToolbarMenuItem; +import org.floens.chan.ui.toolbar.ToolbarMenuSubItem; import org.floens.chan.ui.view.CrossfadeView; -import org.floens.chan.ui.view.FloatingMenuItem; import org.floens.chan.ui.view.ThumbnailView; import java.util.ArrayList; @@ -57,10 +55,10 @@ import static org.floens.chan.Chan.inject; import static org.floens.chan.ui.theme.ThemeHelper.theme; import static org.floens.chan.utils.AndroidUtils.dp; -public class HistoryController extends Controller implements CompoundButton.OnCheckedChangeListener, ToolbarMenuItem.ToolbarMenuItemCallback, ToolbarNavigationController.ToolbarSearchCallback { +public class HistoryController extends Controller implements + CompoundButton.OnCheckedChangeListener, + ToolbarNavigationController.ToolbarSearchCallback { private static final int SEARCH_ID = 1; - private static final int CLEAR_ID = 101; - private static final int SAVED_REPLY_CLEAR_ID = 102; @Inject DatabaseManager databaseManager; @@ -87,19 +85,20 @@ public class HistoryController extends Controller implements CompoundButton.OnCh databaseHistoryManager = databaseManager.getDatabaseHistoryManager(); databaseSavedReplyManager = databaseManager.getDatabaseSavedReplyManager(); + // Navigation navigation.setTitle(R.string.history_screen); - List items = new ArrayList<>(); - items.add(new FloatingMenuItem(CLEAR_ID, R.string.history_clear)); - items.add(new FloatingMenuItem(SAVED_REPLY_CLEAR_ID, R.string.saved_reply_clear)); - navigation.menu = new ToolbarMenu(context); - navigation.menu.addItem(new ToolbarMenuItem(context, this, SEARCH_ID, R.drawable.ic_search_white_24dp)); - ToolbarMenuItem overflow = navigation.createOverflow(context, this, items); - overflow.getSubMenu().setPopupWidth(dp(4 * 56)); + + navigation.buildMenu() + .withItem(R.drawable.ic_search_white_24dp, this::searchClicked) + .withOverflow() + .withSubItem(R.string.history_clear, this::clearHistoryClicked) + .withSubItem(R.string.saved_reply_clear, this::clearSavedReplyClicked) + .build().build(); SwitchCompat historyEnabledSwitch = new SwitchCompat(context); historyEnabledSwitch.setChecked(ChanSettings.historyEnabled.get()); historyEnabledSwitch.setOnCheckedChangeListener(this); - navigation.rightView = historyEnabledSwitch; + navigation.setRightView(historyEnabledSwitch); view = inflateRes(R.layout.controller_history); crossfade = view.findViewById(R.id.crossfade); @@ -116,42 +115,28 @@ public class HistoryController extends Controller implements CompoundButton.OnCh } } - @Override - public void onMenuItemClicked(ToolbarMenuItem item) { - if ((Integer) item.getId() == SEARCH_ID) { - ((ToolbarNavigationController) navigationController).showSearch(); - } + private void searchClicked(ToolbarMenuItem item) { + ((ToolbarNavigationController) navigationController).showSearch(); } - @Override - public void onSubMenuItemClicked(ToolbarMenuItem parent, FloatingMenuItem item) { - switch ((Integer) item.getId()) { - case CLEAR_ID: - new AlertDialog.Builder(context) - .setTitle(R.string.history_clear_confirm) - .setNegativeButton(R.string.cancel, null) - .setPositiveButton(R.string.history_clear_confirm_button, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - databaseManager.runTaskAsync(databaseHistoryManager.clearHistory()); - adapter.load(); - } - }) - .show(); - break; - case SAVED_REPLY_CLEAR_ID: - new AlertDialog.Builder(context) - .setTitle(R.string.saved_reply_clear_confirm) - .setNegativeButton(R.string.cancel, null) - .setPositiveButton(R.string.saved_reply_clear_confirm_button, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - databaseManager.runTaskAsync(databaseSavedReplyManager.clearSavedReplies()); - } - }) - .show(); - break; - } + private void clearHistoryClicked(ToolbarMenuSubItem item) { + new AlertDialog.Builder(context) + .setTitle(R.string.history_clear_confirm) + .setNegativeButton(R.string.cancel, null) + .setPositiveButton(R.string.history_clear_confirm_button, (dialog, which) -> { + databaseManager.runTaskAsync(databaseHistoryManager.clearHistory()); + adapter.load(); + }) + .show(); + } + + private void clearSavedReplyClicked(ToolbarMenuSubItem item) { + new AlertDialog.Builder(context) + .setTitle(R.string.saved_reply_clear_confirm) + .setNegativeButton(R.string.cancel, null) + .setPositiveButton(R.string.saved_reply_clear_confirm_button, (dialog, which) -> + databaseManager.runTaskAsync(databaseSavedReplyManager.clearSavedReplies())) + .show(); } @Override diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerController.java index aae27e12..eea5f647 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerController.java @@ -39,7 +39,6 @@ import android.view.Window; import android.view.WindowManager; import android.view.animation.DecelerateInterpolator; import android.widget.CheckBox; -import android.widget.ImageView; import android.widget.Toast; import com.android.volley.VolleyError; @@ -55,9 +54,11 @@ import org.floens.chan.core.saver.ImageSaver; import org.floens.chan.core.settings.ChanSettings; import org.floens.chan.core.site.ImageSearch; import org.floens.chan.ui.adapter.ImageViewerAdapter; +import org.floens.chan.ui.toolbar.NavigationItem; import org.floens.chan.ui.toolbar.Toolbar; import org.floens.chan.ui.toolbar.ToolbarMenu; import org.floens.chan.ui.toolbar.ToolbarMenuItem; +import org.floens.chan.ui.toolbar.ToolbarMenuSubItem; import org.floens.chan.ui.view.CustomScaleImageView; import org.floens.chan.ui.view.FloatingMenu; import org.floens.chan.ui.view.FloatingMenuItem; @@ -81,18 +82,12 @@ import static org.floens.chan.Chan.inject; import static org.floens.chan.utils.AndroidUtils.dp; import static org.floens.chan.utils.AndroidUtils.getString; -public class ImageViewerController extends Controller implements ImageViewerPresenter.Callback, ToolbarMenuItem.ToolbarMenuItemCallback { +public class ImageViewerController extends Controller implements ImageViewerPresenter.Callback { private static final String TAG = "ImageViewerController"; private static final int TRANSITION_DURATION = 300; private static final float TRANSITION_FINAL_ALPHA = 0.85f; - private static final int GO_POST_ID = 1; - private static final int VOLUME_ID = 3; - private static final int SAVE_ID = 2; - private static final int OPEN_BROWSER_ID = 103; - private static final int SHARE_ID = 104; - private static final int SEARCH_ID = 105; - private static final int SAVE_ALBUM = 106; + private static final int VOLUME_ID = 1; @Inject ImageLoader imageLoader; @@ -110,9 +105,6 @@ public class ImageViewerController extends Controller implements ImageViewerPres private OptionalSwipeViewPager pager; private LoadingBar loadingBar; - private ToolbarMenuItem overflowMenuItem; - private ToolbarMenuItem volumeMenuItem; - public ImageViewerController(Context context, Toolbar toolbar) { super(context); inject(this); @@ -126,24 +118,27 @@ public class ImageViewerController extends Controller implements ImageViewerPres public void onCreate() { super.onCreate(); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - + // Navigation navigation.subtitle = "0"; - navigation.menu = new ToolbarMenu(context); + + NavigationItem.MenuBuilder menuBuilder = navigation.buildMenu(); if (goPostCallback != null) { - navigation.menu.addItem(new ToolbarMenuItem(context, this, GO_POST_ID, R.drawable.ic_subdirectory_arrow_left_white_24dp)); + menuBuilder.withItem(R.drawable.ic_subdirectory_arrow_left_white_24dp, this::goPostClicked); } - volumeMenuItem = navigation.menu.addItem(new ToolbarMenuItem(context, - this, VOLUME_ID, R.drawable.ic_volume_off_white_24dp)); - navigation.menu.addItem(new ToolbarMenuItem(context, this, SAVE_ID, R.drawable.ic_file_download_white_24dp)); + menuBuilder.withItem(VOLUME_ID, R.drawable.ic_volume_off_white_24dp, this::volumeClicked); + menuBuilder.withItem(R.drawable.ic_file_download_white_24dp, this::saveClicked); - List items = new ArrayList<>(); - items.add(new FloatingMenuItem(OPEN_BROWSER_ID, R.string.action_open_browser)); - items.add(new FloatingMenuItem(SHARE_ID, R.string.action_share)); - items.add(new FloatingMenuItem(SEARCH_ID, R.string.action_search_image)); - items.add(new FloatingMenuItem(SAVE_ALBUM, R.string.action_download_album)); - overflowMenuItem = navigation.createOverflow(context, this, items); + NavigationItem.MenuOverflowBuilder overflowBuilder = menuBuilder.withOverflow(); + overflowBuilder.withSubItem(R.string.action_open_browser, this::openBrowserClicked); + overflowBuilder.withSubItem(R.string.action_share, this::shareClicked); + overflowBuilder.withSubItem(R.string.action_search_image, this::searchClicked); + overflowBuilder.withSubItem(R.string.action_download_album, this::downloadAlbumClicked); + + overflowBuilder.build().build(); + + // View setup + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); view = inflateRes(R.layout.controller_image_viewer); previewImage = view.findViewById(R.id.preview_image); @@ -164,84 +159,56 @@ public class ImageViewerController extends Controller implements ImageViewerPres }); } - @Override - public void onDestroy() { - super.onDestroy(); + private void goPostClicked(ToolbarMenuItem item) { + PostImage postImage = presenter.getCurrentPostImage(); + ImageViewerCallback imageViewerCallback = goPostCallback.goToPost(postImage); + if (imageViewerCallback != null) { + // hax: we need to wait for the recyclerview to do a layout before we know + // where the new thumbnails are to get the bounds from to animate to + this.imageViewerCallback = imageViewerCallback; + AndroidUtils.waitForLayout(view, view -> { + presenter.onExit(); + return false; + }); + } else { + presenter.onExit(); + } + } - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + private void volumeClicked(ToolbarMenuItem item) { + presenter.onVolumeClicked(); } - @Override - public void onMenuItemClicked(ToolbarMenuItem item) { - switch ((Integer) item.getId()) { - case GO_POST_ID: - PostImage postImage = presenter.getCurrentPostImage(); - ImageViewerCallback imageViewerCallback = goPostCallback.goToPost(postImage); - if (imageViewerCallback != null) { - // hax: we need to wait for the recyclerview to do a layout before we know - // where the new thumbnails are to get the bounds from to animate to - this.imageViewerCallback = imageViewerCallback; - AndroidUtils.waitForLayout(view, new AndroidUtils.OnMeasuredCallback() { - @Override - public boolean onMeasured(View view) { - presenter.onExit(); - return false; - } - }); - } else { - presenter.onExit(); - } - break; - case SAVE_ID: - saveShare(false, presenter.getCurrentPostImage()); - break; - case VOLUME_ID: - presenter.onVolumeClicked(); - break; - } + private void saveClicked(ToolbarMenuItem item) { + saveShare(false, presenter.getCurrentPostImage()); } - @Override - public void onSubMenuItemClicked(ToolbarMenuItem parent, FloatingMenuItem item) { + private void openBrowserClicked(ToolbarMenuSubItem item) { PostImage postImage = presenter.getCurrentPostImage(); - switch ((Integer) item.getId()) { - case SHARE_ID: - saveShare(true, postImage); - break; - case OPEN_BROWSER_ID: - AndroidUtils.openLinkInBrowser((Activity) context, postImage.imageUrl.toString()); - break; - case SEARCH_ID: - List items = new ArrayList<>(); - for (ImageSearch imageSearch : ImageSearch.engines) { - items.add(new FloatingMenuItem(imageSearch.getId(), imageSearch.getName())); - } - FloatingMenu menu = new FloatingMenu(context, overflowMenuItem.getView(), items); - menu.setCallback(new FloatingMenu.FloatingMenuCallback() { - @Override - public void onFloatingMenuItemClicked(FloatingMenu menu, FloatingMenuItem item) { - for (ImageSearch imageSearch : ImageSearch.engines) { - if (((Integer) item.getId()) == imageSearch.getId()) { - final HttpUrl searchImageUrl = getSearchImageUrl(presenter.getCurrentPostImage()); - AndroidUtils.openLinkInBrowser((Activity) context, imageSearch.getUrl(searchImageUrl.toString())); - break; - } - } - } + AndroidUtils.openLinkInBrowser((Activity) context, postImage.imageUrl.toString()); + } - @Override - public void onFloatingMenuDismissed(FloatingMenu menu) { - } - }); - menu.show(); - break; - case SAVE_ALBUM: - List all = presenter.getAllPostImages(); - AlbumDownloadController albumDownloadController = new AlbumDownloadController(context); - albumDownloadController.setPostImages(presenter.getLoadable(), all); - navigationController.pushController(albumDownloadController); - break; - } + private void shareClicked(ToolbarMenuSubItem item) { + PostImage postImage = presenter.getCurrentPostImage(); + saveShare(true, postImage); + } + + private void searchClicked(ToolbarMenuSubItem item) { + showImageSearchOptions(); + } + + private void downloadAlbumClicked(ToolbarMenuSubItem item) { + List all = presenter.getAllPostImages(); + AlbumDownloadController albumDownloadController = new AlbumDownloadController(context); + albumDownloadController.setPostImages(presenter.getLoadable(), all); + navigationController.pushController(albumDownloadController); + } + + @Override + public void onDestroy() { + super.onDestroy(); + + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } private void saveShare(boolean share, PostImage postImage) { @@ -353,10 +320,37 @@ public class ImageViewerController extends Controller implements ImageViewerPres @Override public void showVolumeMenuItem(boolean show, boolean muted) { - ImageView view = volumeMenuItem.getView(); - view.setVisibility(show ? View.VISIBLE : View.GONE); - view.setImageResource(muted ? - R.drawable.ic_volume_off_white_24dp : R.drawable.ic_volume_up_white_24dp); + ToolbarMenuItem volumeMenuItem = navigation.findItem(VOLUME_ID); + volumeMenuItem.setVisible(show); + volumeMenuItem.setImage( + muted ? R.drawable.ic_volume_off_white_24dp : R.drawable.ic_volume_up_white_24dp); + } + + private void showImageSearchOptions() { + // TODO: move to presenter + List items = new ArrayList<>(); + for (ImageSearch imageSearch : ImageSearch.engines) { + items.add(new FloatingMenuItem(imageSearch.getId(), imageSearch.getName())); + } + ToolbarMenuItem overflowMenuItem = navigation.findItem(ToolbarMenu.OVERFLOW_ID); + FloatingMenu menu = new FloatingMenu(context, overflowMenuItem.getView(), items); + menu.setCallback(new FloatingMenu.FloatingMenuCallback() { + @Override + public void onFloatingMenuItemClicked(FloatingMenu menu, FloatingMenuItem item) { + for (ImageSearch imageSearch : ImageSearch.engines) { + if (((Integer) item.getId()) == imageSearch.getId()) { + final HttpUrl searchImageUrl = getSearchImageUrl(presenter.getCurrentPostImage()); + AndroidUtils.openLinkInBrowser((Activity) context, imageSearch.getUrl(searchImageUrl.toString())); + break; + } + } + } + + @Override + public void onFloatingMenuDismissed(FloatingMenu menu) { + } + }); + menu.show(); } public void startPreviewInTransition(PostImage postImage) { diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/LogsController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/LogsController.java index df69df4e..d2462303 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/LogsController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/LogsController.java @@ -27,25 +27,19 @@ import android.widget.Toast; import org.floens.chan.R; import org.floens.chan.controller.Controller; -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.ui.toolbar.ToolbarMenuSubItem; import org.floens.chan.utils.AndroidUtils; import org.floens.chan.utils.IOUtils; import org.floens.chan.utils.Logger; import java.io.IOException; import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; import static org.floens.chan.utils.AndroidUtils.getAttrColor; -public class LogsController extends Controller implements ToolbarMenuItem.ToolbarMenuItemCallback { +public class LogsController extends Controller { private static final String TAG = "LogsController"; - private static final int COPY_ID = 101; - private TextView logTextView; private String logText; @@ -60,10 +54,9 @@ public class LogsController extends Controller implements ToolbarMenuItem.Toolba navigation.setTitle(org.floens.chan.R.string.settings_logs_screen); - navigation.menu = new ToolbarMenu(context); - List items = new ArrayList<>(); - items.add(new FloatingMenuItem(COPY_ID, R.string.settings_logs_copy)); - navigation.createOverflow(context, this, items); + navigation.buildMenu().withOverflow() + .withSubItem(R.string.settings_logs_copy, this::copyLogsClicked) + .build().build(); ScrollView container = new ScrollView(context); container.setBackgroundColor(getAttrColor(context, org.floens.chan.R.attr.backcolor)); @@ -75,18 +68,11 @@ public class LogsController extends Controller implements ToolbarMenuItem.Toolba loadLogs(); } - @Override - public void onMenuItemClicked(ToolbarMenuItem item) { - } - - @Override - public void onSubMenuItemClicked(ToolbarMenuItem parent, FloatingMenuItem item) { - if ((int) item.getId() == COPY_ID) { - ClipboardManager clipboard = (ClipboardManager) AndroidUtils.getAppContext().getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText("Logs", logText); - clipboard.setPrimaryClip(clip); - Toast.makeText(context, R.string.settings_logs_copied_to_clipboard, Toast.LENGTH_SHORT).show(); - } + private void copyLogsClicked(ToolbarMenuSubItem item) { + ClipboardManager clipboard = (ClipboardManager) AndroidUtils.getAppContext().getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText("Logs", logText); + clipboard.setPrimaryClip(clip); + Toast.makeText(context, R.string.settings_logs_copied_to_clipboard, Toast.LENGTH_SHORT).show(); } private void loadLogs() { 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 38da6ce4..d59391e6 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 @@ -44,11 +44,8 @@ import org.floens.chan.core.site.Site; import org.floens.chan.core.site.SiteIcon; import org.floens.chan.ui.helper.HintPopup; import org.floens.chan.ui.layout.SiteAddLayout; -import org.floens.chan.ui.toolbar.ToolbarMenu; -import org.floens.chan.ui.toolbar.ToolbarMenuItem; import org.floens.chan.ui.view.CrossfadeView; import org.floens.chan.ui.view.DividerItemDecoration; -import org.floens.chan.ui.view.FloatingMenuItem; import java.util.ArrayList; import java.util.List; @@ -62,15 +59,11 @@ import static org.floens.chan.utils.AndroidUtils.setRoundItemBackground; public class SitesSetupController extends StyledToolbarNavigationController implements SitesSetupPresenter.Callback, - ToolbarMenuItem.ToolbarMenuItemCallback, View.OnClickListener { - private static final int DONE_ID = 1; @Inject SitesSetupPresenter presenter; - private ToolbarMenuItem doneMenuItem; - private CrossfadeView crossfadeView; private RecyclerView sitesRecyclerview; private FloatingActionButton addButton; @@ -152,21 +145,12 @@ public class SitesSetupController extends StyledToolbarNavigationController impl public void showDoneCheckmark() { navigation.swipeable = false; - navigation.menu = new ToolbarMenu(context); - doneMenuItem = navigation.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) { - presenter.onDoneClicked(); - } - } + navigation.buildMenu() + .withItem(R.drawable.ic_done_white_24dp, (item) -> presenter.onDoneClicked()) + .build(); - @Override - public void onSubMenuItemClicked(ToolbarMenuItem parent, FloatingMenuItem item) { +// doneMenuItem.getView().setAlpha(0f); } @Override @@ -244,9 +228,9 @@ public class SitesSetupController extends StyledToolbarNavigationController impl @Override public void setNextAllowed(boolean nextAllowed) { - if (doneMenuItem != null) { - doneMenuItem.getView().animate().alpha(nextAllowed ? 1f : 0f).start(); - } +// if (doneMenuItem != null) { +// doneMenuItem.getView().animate().alpha(nextAllowed ? 1f : 0f).start(); +// } if (!nextAllowed) { navigation.swipeable = false; } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/ThreadController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/ThreadController.java index 8d4f178d..33cc8b69 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/ThreadController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/ThreadController.java @@ -231,7 +231,7 @@ public abstract class ThreadController extends Controller implements @Override public Toolbar getToolbar() { if (navigationController instanceof ToolbarNavigationController) { - return ((ToolbarNavigationController) navigationController).getToolbar(); + return navigationController.getToolbar(); } else { return null; } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/ToolbarNavigationController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/ToolbarNavigationController.java index 875e367d..dcca6676 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/ToolbarNavigationController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/ToolbarNavigationController.java @@ -35,6 +35,7 @@ public abstract class ToolbarNavigationController extends NavigationController i super(context); } + @Override public Toolbar getToolbar() { return toolbar; } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/ViewThreadController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/ViewThreadController.java index 2f1302fa..2b7f5534 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/ViewThreadController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/ViewThreadController.java @@ -20,7 +20,9 @@ package org.floens.chan.ui.controller; import android.app.Activity; import android.content.Context; import android.content.DialogInterface; +import android.graphics.drawable.Drawable; import android.support.v7.app.AlertDialog; +import android.view.View; import org.floens.chan.R; import org.floens.chan.controller.Controller; @@ -32,36 +34,25 @@ import org.floens.chan.core.presenter.ThreadPresenter; import org.floens.chan.core.settings.ChanSettings; import org.floens.chan.ui.helper.HintPopup; import org.floens.chan.ui.layout.ThreadLayout; +import org.floens.chan.ui.toolbar.NavigationItem; 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.ui.toolbar.ToolbarMenuSubItem; import org.floens.chan.utils.AndroidUtils; -import java.util.ArrayList; -import java.util.List; - import javax.inject.Inject; import static org.floens.chan.Chan.inject; import static org.floens.chan.utils.AndroidUtils.dp; import static org.floens.chan.utils.AndroidUtils.getAttrColor; -public class ViewThreadController extends ThreadController implements ThreadLayout.ThreadLayoutCallback, ToolbarMenuItem.ToolbarMenuItemCallback { - private static final int ALBUM_ID = 1; - private static final int PIN_ID = 2; - private static final int REPLY_ID = 101; - private static final int REFRESH_ID = 103; - private static final int SEARCH_ID = 104; - private static final int SHARE_ID = 105; - private static final int UP_ID = 106; - private static final int DOWN_ID = 107; - private static final int OPEN_BROWSER_ID = 108; +public class ViewThreadController extends ThreadController implements ThreadLayout.ThreadLayoutCallback { + private static final int PIN_ID = 1; @Inject WatchManager watchManager; - private ToolbarMenuItem pinItem; - private ToolbarMenuItem overflowItem; + private boolean pinItemPinned = false; private Loadable loadable; public ViewThreadController(Context context) { @@ -82,25 +73,81 @@ public class ViewThreadController extends ThreadController implements ThreadLayo view.setBackgroundColor(getAttrColor(context, R.attr.backcolor)); navigation.hasDrawer = true; - navigation.menu = new ToolbarMenu(context); - navigation.menu.addItem(new ToolbarMenuItem(context, this, ALBUM_ID, R.drawable.ic_image_white_24dp)); - pinItem = navigation.menu.addItem(new ToolbarMenuItem(context, this, PIN_ID, R.drawable.ic_bookmark_outline_white_24dp)); - List items = new ArrayList<>(); + NavigationItem.MenuOverflowBuilder menuOverflowBuilder = navigation.buildMenu() + .withItem(R.drawable.ic_image_white_24dp, this::albumClicked) + .withItem(PIN_ID, R.drawable.ic_bookmark_outline_white_24dp, this::pinClicked) + .withOverflow(); + if (!ChanSettings.enableReplyFab.get()) { - items.add(new FloatingMenuItem(REPLY_ID, context.getString(R.string.action_reply))); + menuOverflowBuilder.withSubItem(R.string.action_reply, this::replyClicked); } - items.add(new FloatingMenuItem(SEARCH_ID, R.string.action_search)); - items.add(new FloatingMenuItem(REFRESH_ID, R.string.action_reload)); - items.add(new FloatingMenuItem(OPEN_BROWSER_ID, R.string.action_open_browser)); - items.add(new FloatingMenuItem(SHARE_ID, R.string.action_share)); - items.add(new FloatingMenuItem(UP_ID, R.string.action_up)); - items.add(new FloatingMenuItem(DOWN_ID, R.string.action_down)); - overflowItem = navigation.createOverflow(context, this, items); + + menuOverflowBuilder + .withSubItem(R.string.action_search, this::searchClicked) + .withSubItem(R.string.action_reload, this::reloadClicked) + .withSubItem(R.string.action_open_browser, this::openBrowserClicked) + .withSubItem(R.string.action_share, this::shareClicked) + .withSubItem(R.string.action_up, this::upClicked) + .withSubItem(R.string.action_down, this::downClicked) + .build() + .build(); loadThread(loadable); } + private void albumClicked(ToolbarMenuItem item) { + threadLayout.getPresenter().showAlbum(); + } + + private void pinClicked(ToolbarMenuItem item) { + threadLayout.getPresenter().pin(); + setPinIconState(true); + updateDrawerHighlighting(loadable); + } + + private void searchClicked(ToolbarMenuSubItem item) { + ((ToolbarNavigationController) navigationController).showSearch(); + } + + private void replyClicked(ToolbarMenuSubItem item) { + threadLayout.openReply(true); + } + + private void reloadClicked(ToolbarMenuSubItem item) { + threadLayout.getPresenter().requestData(); + } + + private void openBrowserClicked(ToolbarMenuSubItem item) { + Loadable loadable = threadLayout.getPresenter().getLoadable(); + String link = loadable.site.resolvable().desktopUrl(loadable, null); + AndroidUtils.openLinkInBrowser((Activity) context, link); + } + + private void shareClicked(ToolbarMenuSubItem item) { + Loadable loadable = threadLayout.getPresenter().getLoadable(); + String link = loadable.site.resolvable().desktopUrl(loadable, null); + AndroidUtils.shareLink(link); + } + + private void upClicked(ToolbarMenuSubItem item) { + threadLayout.getPresenter().scrollTo(0, false); + } + + private void downClicked(ToolbarMenuSubItem item) { + threadLayout.getPresenter().scrollTo(-1, false); + } + + @Override + public void onShow() { + super.onShow(); + + ThreadPresenter presenter = threadLayout.getPresenter(); + if (presenter != null) { + setPinIconState(false); + } + } + @Override public void onDestroy() { super.onDestroy(); @@ -114,15 +161,15 @@ public class ViewThreadController extends ThreadController implements ThreadLayo } public void onEvent(WatchManager.PinAddedMessage message) { - setPinIconState(); + setPinIconState(true); } public void onEvent(WatchManager.PinRemovedMessage message) { - setPinIconState(); + setPinIconState(true); } public void onEvent(WatchManager.PinChangedMessage message) { - setPinIconState(); + setPinIconState(false); // Update title if (message.pin.loadable == loadable) { onShowPosts(); @@ -151,16 +198,18 @@ public class ViewThreadController extends ThreadController implements ThreadLayo this.loadable = presenter.getLoadable(); navigation.title = loadable.title; ((ToolbarNavigationController) navigationController).toolbar.updateTitle(navigation); - setPinIconState(presenter.isPinned()); + setPinIconState(false); updateDrawerHighlighting(loadable); updateLeftPaneHighlighting(loadable); presenter.requestInitialData(); int counter = ChanSettings.threadOpenCounter.increase(); if (counter == 2) { - HintPopup.show(context, overflowItem.getView(), context.getString(R.string.thread_up_down_hint), -dp(1), 0); + View view = navigation.findItem(ToolbarMenu.OVERFLOW_ID).getView(); + HintPopup.show(context, view, context.getString(R.string.thread_up_down_hint), -dp(1), 0); } else if (counter == 3) { - HintPopup.show(context, pinItem.getView(), context.getString(R.string.thread_pin_hint), -dp(1), 0); + View view = navigation.findItem(PIN_ID).getView(); + HintPopup.show(context, view, context.getString(R.string.thread_pin_hint), -dp(1), 0); } } } @@ -173,53 +222,6 @@ public class ViewThreadController extends ThreadController implements ThreadLayo ((ToolbarNavigationController) navigationController).toolbar.updateTitle(navigation); } - @Override - public void onMenuItemClicked(ToolbarMenuItem item) { - switch ((Integer) item.getId()) { - case ALBUM_ID: - threadLayout.getPresenter().showAlbum(); - break; - case PIN_ID: - setPinIconState(threadLayout.getPresenter().pin()); - updateDrawerHighlighting(loadable); - break; - } - } - - @Override - public void onSubMenuItemClicked(ToolbarMenuItem parent, FloatingMenuItem item) { - Integer id = (Integer) item.getId(); - - switch (id) { - case REPLY_ID: - threadLayout.openReply(true); - break; - case REFRESH_ID: - threadLayout.getPresenter().requestData(); - break; - case SEARCH_ID: - ((ToolbarNavigationController) navigationController).showSearch(); - break; - case SHARE_ID: - case OPEN_BROWSER_ID: - Loadable loadable = threadLayout.getPresenter().getLoadable(); - String link = loadable.site.resolvable().desktopUrl(loadable, null); - - if (id == SHARE_ID) { - AndroidUtils.shareLink(link); - } else { - AndroidUtils.openLinkInBrowser((Activity) context, link); - } - - break; - case UP_ID: - case DOWN_ID: - boolean up = id == UP_ID; - threadLayout.getPresenter().scrollTo(up ? 0 : -1, false); - break; - } - } - private void updateDrawerHighlighting(Loadable loadable) { Pin pin = loadable == null ? null : watchManager.findPinByLoadable(loadable); @@ -254,11 +256,26 @@ public class ViewThreadController extends ThreadController implements ThreadLayo } } - private void setPinIconState() { - setPinIconState(watchManager.findPinByLoadable(loadable) != null); + private void setPinIconState(boolean animated) { + ThreadPresenter presenter = threadLayout.getPresenter(); + if (presenter != null) { + setPinIconStateDrawable(presenter.isPinned(), animated); + } } - private void setPinIconState(boolean pinned) { - pinItem.setImage(pinned ? R.drawable.ic_bookmark_white_24dp : R.drawable.ic_bookmark_outline_white_24dp); + private void setPinIconStateDrawable(boolean pinned, boolean animated) { + if (pinned == pinItemPinned) { + return; + } + pinItemPinned = pinned; + + Drawable outline = context.getResources().getDrawable( + R.drawable.ic_bookmark_outline_white_24dp); + Drawable white = context.getResources().getDrawable( + R.drawable.ic_bookmark_white_24dp); + + Drawable drawable = pinned ? white : outline; + + navigation.findItem(PIN_ID).setImage(drawable, animated); } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/WatchSettingsController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/WatchSettingsController.java index bca5927f..3177fe51 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/WatchSettingsController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/WatchSettingsController.java @@ -62,7 +62,7 @@ public class WatchSettingsController extends SettingsController implements Compo SwitchCompat globalSwitch = new SwitchCompat(context); globalSwitch.setChecked(enabled); globalSwitch.setOnCheckedChangeListener(this); - navigation.rightView = globalSwitch; + navigation.setRightView(globalSwitch); populatePreferences(); 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 51fe5098..9db87438 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 @@ -17,16 +17,18 @@ */ package org.floens.chan.ui.toolbar; -import android.content.Context; import android.view.View; -import org.floens.chan.ui.view.FloatingMenu; -import org.floens.chan.ui.view.FloatingMenuItem; - -import java.util.List; +import org.floens.chan.R; import static org.floens.chan.utils.AndroidUtils.getString; +/** + * The navigation properties for a Controller. Controls common properties that parent controlers + * need to know, such as the title of the controller. + *

+ * This is also used to set up the toolbar menu, see {@link #buildMenu()}. + */ public class NavigationItem { public String title = ""; public String subtitle = ""; @@ -39,39 +41,110 @@ 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); - overflow.setSubMenu(overflowMenu); - return overflow; + protected ToolbarMenu menu; + protected ToolbarMiddleMenu middleMenu; + protected View rightView; + + public boolean hasArrow() { + return hasBack || search; } public void setTitle(int resId) { title = getString(resId); } - public NavigationItem copy() { - NavigationItem c = new NavigationItem(); - c.title = title; - c.subtitle = subtitle; + public MenuBuilder buildMenu() { + return new MenuBuilder(this); + } + + public void setMiddleMenu(ToolbarMiddleMenu middleMenu) { + this.middleMenu = middleMenu; + } + + public void setRightView(View view) { + rightView = view; + } + + public ToolbarMenuItem findItem(int id) { + if (menu != null) { + return menu.findItem(id); + } + return null; + } + + public ToolbarMenuSubItem findSubItem(int id) { + if (menu != null) { + return menu.findSubItem(id); + } + return null; + } + + public static class MenuBuilder { + private final NavigationItem navigationItem; + private final ToolbarMenu menu; + + public MenuBuilder(NavigationItem navigationItem) { + this.navigationItem = navigationItem; + menu = new ToolbarMenu(null); + } + + public MenuBuilder withItem(int drawable, ToolbarMenuItem.ClickCallback clicked) { + return withItem(-1, drawable, clicked); + } + + public MenuBuilder withItem(int id, int drawable, ToolbarMenuItem.ClickCallback clicked) { + return withItem(new ToolbarMenuItem(id, drawable, clicked)); + } + + public MenuBuilder withItem(ToolbarMenuItem menuItem) { + menu.addItem(menuItem); + return this; + } + + public MenuOverflowBuilder withOverflow() { + return new MenuOverflowBuilder( + this, + new ToolbarMenuItem( + ToolbarMenu.OVERFLOW_ID, + R.drawable.ic_more_vert_white_24dp, + ToolbarMenuItem::showSubmenu)); + } + + public ToolbarMenu build() { + navigationItem.menu = menu; + return menu; + } + } + + public static class MenuOverflowBuilder { + private final MenuBuilder menuBuilder; + private final ToolbarMenuItem menuItem; + + public MenuOverflowBuilder(MenuBuilder menuBuilder, ToolbarMenuItem menuItem) { + this.menuBuilder = menuBuilder; + this.menuItem = menuItem; + } + + public MenuOverflowBuilder withSubItem(int text, ToolbarMenuSubItem.ClickCallback clicked) { + return withSubItem(-1, getString(text), clicked); + } + + public MenuOverflowBuilder withSubItem(String text, ToolbarMenuSubItem.ClickCallback clicked) { + return withSubItem(-1, text, clicked); + } + + public MenuOverflowBuilder withSubItem(int id, int text, ToolbarMenuSubItem.ClickCallback clicked) { + return withSubItem(id, getString(text), clicked); + } - c.hasBack = hasBack; - c.hasDrawer = hasDrawer; - c.handlesToolbarInset = handlesToolbarInset; - c.swipeable = swipeable; + public MenuOverflowBuilder withSubItem(int id, String text, ToolbarMenuSubItem.ClickCallback clicked) { + menuItem.addSubItem(new ToolbarMenuSubItem(id, text, clicked)); - c.search = search; - c.searchText = searchText; + return this; + } -// c.menu = menu; - c.middleMenu = middleMenu; -// c.rightView = rightView; - return c; + public MenuBuilder build() { + return menuBuilder.withItem(menuItem); + } } } 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 1252d0e6..e096d3d3 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 @@ -19,40 +19,29 @@ package org.floens.chan.ui.toolbar; import android.annotation.SuppressLint; import android.content.Context; -import android.graphics.drawable.Drawable; import android.os.Build; -import android.support.annotation.NonNull; import android.support.v7.widget.RecyclerView; -import android.text.TextUtils; import android.util.AttributeSet; import android.view.Gravity; -import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; -import android.view.ViewGroup; import android.view.animation.DecelerateInterpolator; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.TextView; 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.utils.AndroidUtils; import java.util.ArrayList; import java.util.List; import static org.floens.chan.utils.AndroidUtils.dp; -import static org.floens.chan.utils.AndroidUtils.getAttrColor; import static org.floens.chan.utils.AndroidUtils.hideKeyboard; -import static org.floens.chan.utils.AndroidUtils.removeFromParentView; import static org.floens.chan.utils.AndroidUtils.setRoundItemBackground; public class Toolbar extends LinearLayout implements - View.OnClickListener, ToolbarPresenter.Callback { + View.OnClickListener, ToolbarPresenter.Callback, ToolbarContainer.Callback { public static final int TOOLBAR_COLLAPSE_HIDE = 1000000; public static final int TOOLBAR_COLLAPSE_SHOW = -1000000; @@ -234,14 +223,6 @@ public class Toolbar extends LinearLayout implements } } - public void setArrowMenuProgress(float progress) { - arrowMenuDrawable.setProgress(progress); - } - - public void setShowArrowMenu(boolean show) { - arrowMenuView.setVisibility(show ? VISIBLE : GONE); - } - public ArrowMenuDrawable getArrowMenuDrawable() { return arrowMenuDrawable; } @@ -282,6 +263,7 @@ public class Toolbar extends LinearLayout implements navigationItemContainer = new ToolbarContainer(getContext()); addView(navigationItemContainer, new LayoutParams(0, LayoutParams.MATCH_PARENT, 1f)); + navigationItemContainer.setCallback(this); navigationItemContainer.setArrowMenu(arrowMenuDrawable); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { @@ -294,19 +276,13 @@ public class Toolbar extends LinearLayout implements @Override public void showForNavigationItem( NavigationItem item, ToolbarPresenter.AnimationStyle animation) { - View view = createNavigationItemView(item); - boolean arrow = item.hasBack || item.search; - - navigationItemContainer.set(view, arrow, animation); + navigationItemContainer.set(item, animation); } @Override public void containerStartTransition( NavigationItem item, ToolbarPresenter.TransitionAnimationStyle animation) { - View view = createNavigationItemView(item); - boolean arrow = item.hasBack || item.search; - - navigationItemContainer.startTransition(view, arrow, animation); + navigationItemContainer.startTransition(item, animation); } @Override @@ -319,6 +295,16 @@ public class Toolbar extends LinearLayout implements navigationItemContainer.setTransitionProgress(progress); } + @Override + public void searchInput(String input) { + presenter.searchInput(input); + } + + @Override + public String searchHint(NavigationItem item) { + return callback.getSearchHint(item); + } + @Override public void onSearchVisibilityChanged(NavigationItem item, boolean visible) { callback.onSearchVisibilityChanged(item, visible); @@ -328,6 +314,7 @@ public class Toolbar extends LinearLayout implements } } + @Override public void onSearchInput(NavigationItem item, String input) { callback.onSearchEntered(item, input); @@ -338,79 +325,6 @@ public class Toolbar extends LinearLayout implements navigationItemContainer.update(item, current); } - private LinearLayout createNavigationItemView(final NavigationItem item) { - if (item.search) { - return createSearchLayout(item); - } else { - return createNavigationLayout(item); - } - } - - @NonNull - private LinearLayout createNavigationLayout(NavigationItem item) { - @SuppressLint("InflateParams") - LinearLayout menu = (LinearLayout) LayoutInflater.from(getContext()).inflate(R.layout.toolbar_menu, null); - menu.setGravity(Gravity.CENTER_VERTICAL); - - FrameLayout titleContainer = menu.findViewById(R.id.title_container); - - final TextView titleView = menu.findViewById(R.id.title); - titleView.setTypeface(AndroidUtils.ROBOTO_MEDIUM); - titleView.setText(item.title); - titleView.setTextColor(0xffffffff); - - if (item.middleMenu != null) { - 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(v -> item.middleMenu.show(titleView)); - } - - TextView subtitleView = menu.findViewById(R.id.subtitle); - if (!TextUtils.isEmpty(item.subtitle)) { - ViewGroup.LayoutParams titleParams = titleView.getLayoutParams(); - titleParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; - titleView.setLayoutParams(titleParams); - subtitleView.setText(item.subtitle); - subtitleView.setTextColor(0xffffffff); - titleView.setPadding(titleView.getPaddingLeft(), dp(5f), titleView.getPaddingRight(), titleView.getPaddingBottom()); - } else { - titleContainer.removeView(subtitleView); - } - - if (item.rightView != null) { - removeFromParentView(item.rightView); - item.rightView.setPadding(0, 0, dp(16), 0); - menu.addView(item.rightView, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT)); - } - - if (item.menu != null) { - removeFromParentView(item.menu); - menu.addView(item.menu, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT)); - } - - return menu; - } - - @NonNull - private LinearLayout createSearchLayout(NavigationItem item) { - SearchLayout searchLayout = new SearchLayout(getContext()); - - searchLayout.setCallback(input -> { - presenter.searchInput(input); - }); - - if (item.searchText != null) { - searchLayout.setText(item.searchText); - } - - searchLayout.setHint(callback.getSearchHint(item)); - searchLayout.setPadding(dp(16), searchLayout.getPaddingTop(), searchLayout.getPaddingRight(), searchLayout.getPaddingBottom()); - - return searchLayout; - } - public interface ToolbarCallback { void onMenuOrBackClicked(boolean isArrow); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarContainer.java b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarContainer.java index 7be8e540..00171c45 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarContainer.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarContainer.java @@ -22,22 +22,35 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; +import android.annotation.SuppressLint; import android.content.Context; +import android.graphics.drawable.Drawable; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.AttributeSet; +import android.view.Gravity; +import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.view.animation.DecelerateInterpolator; import android.view.animation.LinearInterpolator; import android.widget.FrameLayout; +import android.widget.LinearLayout; import android.widget.TextView; 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.utils.AndroidUtils; import java.util.HashMap; import java.util.Map; import static org.floens.chan.utils.AndroidUtils.dp; +import static org.floens.chan.utils.AndroidUtils.getAttrColor; +import static org.floens.chan.utils.AndroidUtils.removeFromParentView; /** * The container for the views created by the toolbar for the navigation items. @@ -57,14 +70,20 @@ import static org.floens.chan.utils.AndroidUtils.dp; * drawable. */ public class ToolbarContainer extends FrameLayout { + private Callback callback; + private ArrowMenuDrawable arrowMenu; - private View previousView; - private boolean previousArrow; - private View currentView; - private boolean currentArrow; - private View transitionView; - private boolean transitionArrow; + @Nullable + private ItemView previousView; + + @Nullable + private ItemView currentView; + + @Nullable + private ItemView transitionView; + + @Nullable private ToolbarPresenter.TransitionAnimationStyle transitionAnimationStyle; private Map animatorSet = new HashMap<>(); @@ -81,23 +100,27 @@ public class ToolbarContainer extends FrameLayout { super(context, attrs, defStyle); } + public void setCallback(Callback callback) { + this.callback = callback; + } + public void setArrowMenu(ArrowMenuDrawable arrowMenu) { this.arrowMenu = arrowMenu; } - public void set(View view, boolean arrow, ToolbarPresenter.AnimationStyle animation) { + public void set(NavigationItem item, ToolbarPresenter.AnimationStyle animation) { if (transitionView != null) { throw new IllegalStateException("Currently in transition mode"); } endAnimations(); + ItemView itemView = new ItemView(item); + previousView = currentView; - previousArrow = currentArrow; - currentView = view; - currentArrow = arrow; + currentView = itemView; - addView(view, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); + addView(itemView.view, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); if (getChildCount() > 2) { throw new IllegalArgumentException("More than 2 child views attached"); @@ -106,22 +129,24 @@ public class ToolbarContainer extends FrameLayout { // Can't run the animation if there is no previous view // Otherwise just show it without an animation. if (animation != ToolbarPresenter.AnimationStyle.NONE && previousView != null) { - setAnimation(view, previousView, animation); + setAnimation(itemView, previousView, animation); } else { if (previousView != null) { - removeView(previousView); + removeItem(previousView); previousView = null; } } if (animation == ToolbarPresenter.AnimationStyle.NONE) { - setArrowProgress(1f, !currentArrow); + setArrowProgress(1f, !currentView.item.hasArrow()); } + + itemView.attach(); } public void update(NavigationItem item, boolean current) { - // TODO: clean up - View view = current ? currentView : (previousView != null ? previousView : transitionView); + // TODO + View view = viewForItem(item); if (view != null) { TextView titleView = view.findViewById(R.id.title); if (titleView != null) { @@ -137,26 +162,49 @@ public class ToolbarContainer extends FrameLayout { } } + public View viewForItem(NavigationItem item) { + ItemView itemView = itemViewForItem(item); + if (itemView == null) { + return null; + } + return itemView.view; + } + + private ItemView itemViewForItem(NavigationItem item) { + if (currentView != null && item == currentView.item) { + return currentView; + } else if (previousView != null && item == previousView.item) { + return previousView; + } else if (transitionView != null && item == transitionView.item) { + return transitionView; + } else { + return null; + } + } + public boolean isTransitioning() { return transitionView != null || previousView != null; } public void startTransition( - View view, boolean arrow, ToolbarPresenter.TransitionAnimationStyle style) { + NavigationItem item, ToolbarPresenter.TransitionAnimationStyle style) { if (transitionView != null) { throw new IllegalStateException("Already in transition mode"); } endAnimations(); - transitionView = view; - transitionArrow = arrow; + ItemView itemView = new ItemView(item); + + transitionView = itemView; transitionAnimationStyle = style; - addView(view, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); + addView(itemView.view, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); if (getChildCount() > 2) { throw new IllegalArgumentException("More than 2 child views attached"); } + + itemView.attach(); } public void stopTransition(boolean didComplete) { @@ -165,12 +213,11 @@ public class ToolbarContainer extends FrameLayout { } if (didComplete) { - removeView(currentView); + removeItem(currentView); currentView = transitionView; - currentArrow = transitionArrow; transitionView = null; } else { - removeView(transitionView); + removeItem(transitionView); transitionView = null; } @@ -189,14 +236,14 @@ public class ToolbarContainer extends FrameLayout { private void endAnimations() { if (previousView != null) { - endAnimation(previousView); + endAnimation(previousView.view); if (previousView != null) { throw new IllegalStateException("Animation end did not remove view"); } } if (currentView != null) { - endAnimation(currentView); + endAnimation(currentView.view); } } @@ -207,7 +254,7 @@ public class ToolbarContainer extends FrameLayout { } } - private void setAnimation(View view, View previousView, + private void setAnimation(ItemView view, ItemView previousView, ToolbarPresenter.AnimationStyle animationStyle) { if (animationStyle == ToolbarPresenter.AnimationStyle.PUSH || animationStyle == ToolbarPresenter.AnimationStyle.POP) { @@ -217,69 +264,69 @@ public class ToolbarContainer extends FrameLayout { ValueAnimator previousAnimation = getShortAnimator(); previousAnimation.addUpdateListener(a -> { float value = (float) a.getAnimatedValue(); - setPreviousAnimationProgress(previousView, pushing, value); + setPreviousAnimationProgress(previousView.view, pushing, value); }); previousAnimation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - animatorSet.remove(previousView); - removeView(previousView); + animatorSet.remove(previousView.view); + removeItem(previousView); ToolbarContainer.this.previousView = null; } }); if (!pushing) previousAnimation.setStartDelay(100); - animatorSet.put(previousView, previousAnimation); + animatorSet.put(previousView.view, previousAnimation); post(previousAnimation::start); // Current animation + arrow - view.setAlpha(0f); + view.view.setAlpha(0f); ValueAnimator animation = getShortAnimator(); animation.addUpdateListener(a -> { float value = (float) a.getAnimatedValue(); - setAnimationProgress(view, pushing, value); + setAnimationProgress(view.view, pushing, value); - if (previousArrow != currentArrow) { - setArrowProgress(value, !currentArrow); + if (previousView.item.hasArrow() != currentView.item.hasArrow()) { + setArrowProgress(value, !currentView.item.hasArrow()); } }); animation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - animatorSet.remove(view); + animatorSet.remove(view.view); } }); if (!pushing) animation.setStartDelay(100); - animatorSet.put(view, animation); + animatorSet.put(view.view, animation); post(animation::start); } else if (animationStyle == ToolbarPresenter.AnimationStyle.FADE) { // Previous animation ValueAnimator previousAnimation = - ObjectAnimator.ofFloat(previousView, View.ALPHA, 1f, 0f); + ObjectAnimator.ofFloat(previousView.view, View.ALPHA, 1f, 0f); previousAnimation.setDuration(300); previousAnimation.setInterpolator(new LinearInterpolator()); previousAnimation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - animatorSet.remove(previousView); - removeView(previousView); + animatorSet.remove(previousView.view); + removeItem(previousView); ToolbarContainer.this.previousView = null; } }); - animatorSet.put(previousView, previousAnimation); + animatorSet.put(previousView.view, previousAnimation); post(previousAnimation::start); // Current animation + arrow - view.setAlpha(0f); - ValueAnimator animation = ObjectAnimator.ofFloat(view, View.ALPHA, 0f, 1f); + view.view.setAlpha(0f); + ValueAnimator animation = ObjectAnimator.ofFloat(view.view, View.ALPHA, 0f, 1f); animation.setDuration(300); animation.setInterpolator(new LinearInterpolator()); animation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - animatorSet.remove(view); + animatorSet.remove(view.view); } }); // A different animator for the arrow because that one needs the deceleration @@ -289,8 +336,8 @@ public class ToolbarContainer extends FrameLayout { arrow.setInterpolator(new DecelerateInterpolator(2f)); arrow.addUpdateListener(a -> { float value = (float) a.getAnimatedValue(); - if (previousArrow != currentArrow) { - setArrowProgress(value, !currentArrow); + if (previousView.item.hasArrow() != currentView.item.hasArrow()) { + setArrowProgress(value, !currentView.item.hasArrow()); } }); @@ -299,11 +346,11 @@ public class ToolbarContainer extends FrameLayout { animationAndArrow.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - animatorSet.remove(view); + animatorSet.remove(view.view); } }); - animatorSet.put(view, animationAndArrow); + animatorSet.put(view.view, animationAndArrow); post(animationAndArrow::start); } @@ -338,14 +385,14 @@ public class ToolbarContainer extends FrameLayout { boolean pushing = style == ToolbarPresenter.TransitionAnimationStyle.PUSH; - transitionView.setTranslationY((pushing ? offset : -offset) * (1f - progress)); - transitionView.setAlpha(progress); + transitionView.view.setTranslationY((pushing ? offset : -offset) * (1f - progress)); + transitionView.view.setAlpha(progress); - currentView.setTranslationY((pushing ? -offset : offset) * progress); - currentView.setAlpha(1f - progress); + currentView.view.setTranslationY((pushing ? -offset : offset) * progress); + currentView.view.setAlpha(1f - progress); - if (transitionArrow != currentArrow) { - setArrowProgress(progress, !transitionArrow); + if (transitionView.item.hasArrow() != currentView.item.hasArrow()) { + setArrowProgress(progress, !transitionView.item.hasArrow()); } } @@ -355,4 +402,129 @@ public class ToolbarContainer extends FrameLayout { animator.setInterpolator(new DecelerateInterpolator(2f)); return animator; } + + private void removeItem(ItemView item) { + item.remove(); + removeView(item.view); + } + + private class ItemView { + final View view; + final NavigationItem item; + + @Nullable + private ToolbarMenuView menuView; + + public ItemView(NavigationItem item) { + this.view = createNavigationItemView(item); + this.item = item; + } + + public void attach() { + if (item.menu != null) { + menuView.attach(item.menu); + } + } + + public void remove() { + if (menuView != null) { + menuView.detach(); + } + } + + private LinearLayout createNavigationItemView(final NavigationItem item) { + if (item.search) { + return createSearchLayout(item); + } else { + return createNavigationLayout(item); + } + } + + @NonNull + private LinearLayout createNavigationLayout(NavigationItem item) { + @SuppressLint("InflateParams") + LinearLayout menu = (LinearLayout) LayoutInflater.from(getContext()).inflate(R.layout.toolbar_menu, null); + menu.setGravity(Gravity.CENTER_VERTICAL); + + FrameLayout titleContainer = menu.findViewById(R.id.title_container); + + // Title + final TextView titleView = menu.findViewById(R.id.title); + titleView.setTypeface(AndroidUtils.ROBOTO_MEDIUM); + titleView.setText(item.title); + titleView.setTextColor(0xffffffff); + + // Middle title with arrow and callback + if (item.middleMenu != null) { + 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(v -> item.middleMenu.show(titleView)); + } + + // Possible subtitle. + TextView subtitleView = menu.findViewById(R.id.subtitle); + if (!TextUtils.isEmpty(item.subtitle)) { + ViewGroup.LayoutParams titleParams = titleView.getLayoutParams(); + titleParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + titleView.setLayoutParams(titleParams); + subtitleView.setText(item.subtitle); + subtitleView.setTextColor(0xffffffff); + titleView.setPadding(titleView.getPaddingLeft(), dp(5f), + titleView.getPaddingRight(), titleView.getPaddingBottom()); + } else { + titleContainer.removeView(subtitleView); + } + + // Possible view shown at the right side. + if (item.rightView != null) { + removeFromParentView(item.rightView); + item.rightView.setPadding(0, 0, dp(16), 0); + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.MATCH_PARENT); + menu.addView(item.rightView, lp); + } + + // Possible menu with items. + if (item.menu != null) { + menuView = new ToolbarMenuView(getContext()); + + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.MATCH_PARENT); + menu.addView(this.menuView, lp); + } + + return menu; + } + + @NonNull + private LinearLayout createSearchLayout(NavigationItem item) { + SearchLayout searchLayout = new SearchLayout(getContext()); + + searchLayout.setCallback(input -> { + callback.searchInput(input); + }); + + if (item.searchText != null) { + searchLayout.setText(item.searchText); + } + + searchLayout.setHint(callback.searchHint(item)); + searchLayout.setPadding(dp(16), searchLayout.getPaddingTop(), searchLayout.getPaddingRight(), searchLayout.getPaddingBottom()); + + return searchLayout; + } + } + + public interface Callback { + void searchInput(String input); + + String searchHint(NavigationItem item); + } } 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 48ef772c..ec7c943c 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 @@ -18,52 +18,42 @@ package org.floens.chan.ui.toolbar; import android.content.Context; -import android.util.AttributeSet; -import android.view.Gravity; -import android.widget.ImageView; -import android.widget.LinearLayout; - -import org.floens.chan.R; import java.util.ArrayList; import java.util.List; -import static org.floens.chan.utils.AndroidUtils.dp; - -public class ToolbarMenu extends LinearLayout { - private List items = new ArrayList<>(); - - public ToolbarMenu(Context context) { - this(context, null); - } - - public ToolbarMenu(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } +public class ToolbarMenu { + public static final int OVERFLOW_ID = 1000; - public ToolbarMenu(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); + public final List items = new ArrayList<>(); - setOrientation(HORIZONTAL); - setGravity(Gravity.CENTER_VERTICAL); + public ToolbarMenu(@Deprecated Context context) { } public ToolbarMenuItem addItem(ToolbarMenuItem item) { items.add(item); - ImageView icon = item.getView(); - if (icon != null) { - int viewIndex = Math.min(getChildCount(), item.getOrder()); - addView(icon, viewIndex); - } return item; } - public ToolbarMenuItem createOverflow(ToolbarMenuItem.ToolbarMenuItemCallback callback) { - ToolbarMenuItem overflow = addItem(new ToolbarMenuItem(getContext(), callback, 100, 100, R.drawable.ic_more_vert_white_24dp)); - ImageView overflowImage = overflow.getView(); - overflowImage.setLayoutParams(new LinearLayout.LayoutParams(dp(44), dp(54))); - overflowImage.setPadding(dp(8), 0, dp(16), 0); + public ToolbarMenuItem findItem(int id) { + for (ToolbarMenuItem item : items) { + if (item.id.equals(id)) { + return item; + } + } + return null; + } + + public ToolbarMenuSubItem findSubItem(int id) { + ToolbarMenuItem overflow = findItem(OVERFLOW_ID); + if (overflow != null) { + for (ToolbarMenuSubItem subItem : overflow.subItems) { + if (subItem.id == id) { + return subItem; + } + } + } - return overflow; + return null; } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenuItem.java b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenuItem.java index 08d63bb8..1159b12a 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenuItem.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenuItem.java @@ -17,74 +17,141 @@ */ package org.floens.chan.ui.toolbar; -import android.content.Context; import android.graphics.drawable.Drawable; +import android.graphics.drawable.TransitionDrawable; import android.view.View; import android.widget.ImageView; -import android.widget.LinearLayout; import org.floens.chan.ui.view.FloatingMenu; import org.floens.chan.ui.view.FloatingMenuItem; +import org.floens.chan.utils.Logger; -import static org.floens.chan.utils.AndroidUtils.dp; -import static org.floens.chan.utils.AndroidUtils.setRoundItemBackground; +import java.util.ArrayList; +import java.util.List; -public class ToolbarMenuItem implements View.OnClickListener, FloatingMenu.FloatingMenuCallback { - private ToolbarMenuItemCallback callback; - private Object id; - private int order; - private FloatingMenu subMenu; +import static org.floens.chan.utils.AndroidUtils.getRes; +import static org.floens.chan.utils.AndroidUtils.removeFromParentView; - private ImageView imageView; +/** + * An item for the Toolbar menu. These are ImageViews with an icon, that wehen pressed call + * some callback. Add them with the NavigationItem MenuBuilder. + */ +public class ToolbarMenuItem { + private static final String TAG = "ToolbarMenuItem"; + + public Object id; + public int order; + + public boolean overflowStyle = false; + + public boolean visible = true; + + public Drawable drawable; + + public final List subItems = new ArrayList<>(); - public ToolbarMenuItem(Context context, ToolbarMenuItem.ToolbarMenuItemCallback callback, int order, int drawable) { - this(context, callback, order, order, context.getResources().getDrawable(drawable)); + private ClickCallback clicked; + + // Views, only non-null if attached to ToolbarMenuView. + private ImageView view; + + public ToolbarMenuItem(int id, int drawable, ClickCallback clicked) { + this.id = id; + this.drawable = getRes().getDrawable(drawable); + this.clicked = clicked; } - public ToolbarMenuItem(Context context, ToolbarMenuItem.ToolbarMenuItemCallback callback, Object id, int order, int drawable) { - this(context, callback, id, order, context.getResources().getDrawable(drawable)); + public void attach(ImageView view) { + if (this.view != null) { + throw new IllegalStateException("Already attached"); + } + + this.view = view; } - public ToolbarMenuItem(Context context, ToolbarMenuItem.ToolbarMenuItemCallback callback, Object id, int order, Drawable drawable) { - this.id = id; - this.order = order; - this.callback = callback; - - if (drawable != null) { - imageView = new ImageView(context); - imageView.setOnClickListener(this); - imageView.setFocusable(true); - imageView.setScaleType(ImageView.ScaleType.CENTER); - imageView.setLayoutParams(new LinearLayout.LayoutParams(dp(50), dp(56))); - - imageView.setImageDrawable(drawable); - setRoundItemBackground(imageView); + public void detach() { + if (this.view == null) { + throw new IllegalStateException("Not attached"); } + + removeFromParentView(this.view); + this.view = null; } - public void setImage(Drawable drawable) { - imageView.setImageDrawable(drawable); + public ImageView getView() { + return view; + } + + public void addSubItem(ToolbarMenuSubItem subItem) { + subItems.add(subItem); + } + + public void setVisible(boolean visible) { + this.visible = visible; + + if (view != null) { + view.setVisibility(visible ? View.VISIBLE : View.GONE); + } } public void setImage(int drawable) { - imageView.setImageResource(drawable); + setImage(getRes().getDrawable(drawable)); } - public void setSubMenu(FloatingMenu subMenu) { - this.subMenu = subMenu; - subMenu.setCallback(this); + public void setImage(Drawable drawable) { + setImage(drawable, false); } - public FloatingMenu getSubMenu() { - return subMenu; + public void setImage(Drawable drawable, boolean animated) { + if (view == null) { + this.drawable = drawable; + return; + } + + if (!animated) { + view.setImageDrawable(drawable); + } else { + TransitionDrawable transitionDrawable = new TransitionDrawable(new Drawable[]{ + this.drawable.mutate(), drawable.mutate() + }); + + view.setImageDrawable(transitionDrawable); + + transitionDrawable.setCrossFadeEnabled(true); + transitionDrawable.startTransition(100); + } + + this.drawable = drawable; + } + + public void setSubMenu(FloatingMenu subMenu) { } - @Override - public void onClick(View v) { - if (subMenu != null && !subMenu.isShowing()) { - subMenu.show(); + public void showSubmenu() { + if (view == null) { + Logger.w(TAG, "Item not attached, can't show submenu"); + return; } - callback.onMenuItemClicked(this); + + List floatingMenuItems = new ArrayList<>(); + List subItems = new ArrayList<>(this.subItems); + for (ToolbarMenuSubItem subItem : subItems) { + floatingMenuItems.add(new FloatingMenuItem(subItem.id, subItem.text, subItem.enabled)); + } + + FloatingMenu overflowMenu = new FloatingMenu(view.getContext(), view, floatingMenuItems); + overflowMenu.setCallback(new FloatingMenu.FloatingMenuCallback() { + @Override + public void onFloatingMenuItemClicked(FloatingMenu menu, FloatingMenuItem item) { + ToolbarMenuSubItem subItem = subItems.get(floatingMenuItems.indexOf(item)); + subItem.performClick(); + } + + @Override + public void onFloatingMenuDismissed(FloatingMenu menu) { + } + }); + overflowMenu.show(); } public Object getId() { @@ -95,22 +162,13 @@ public class ToolbarMenuItem implements View.OnClickListener, FloatingMenu.Float return order; } - public ImageView getView() { - return imageView; - } - - @Override - public void onFloatingMenuItemClicked(FloatingMenu menu, FloatingMenuItem item) { - callback.onSubMenuItemClicked(this, item); - } - - @Override - public void onFloatingMenuDismissed(FloatingMenu menu) { + public void performClick() { + if (clicked != null) { + clicked.clicked(this); + } } - public interface ToolbarMenuItemCallback { - void onMenuItemClicked(ToolbarMenuItem item); - - void onSubMenuItemClicked(ToolbarMenuItem parent, FloatingMenuItem item); + public interface ClickCallback { + void clicked(ToolbarMenuItem item); } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenuSubItem.java b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenuSubItem.java new file mode 100644 index 00000000..fe74c399 --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenuSubItem.java @@ -0,0 +1,64 @@ +/* + * 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 static org.floens.chan.utils.AndroidUtils.getString; + +/** + * An item for a submenu of a ToolbarMenuItem. Most common as subitem for the overflow button. + * Add with NavigationItem MenuBuilder. + */ +public class ToolbarMenuSubItem { + public int id; + public String text; + public boolean enabled = true; + public ClickCallback clicked; + + public ToolbarMenuSubItem(int id, int text, ClickCallback clicked) { + this(id, getString(text), clicked); + } + + public ToolbarMenuSubItem(int id, int text, boolean enabled) { + this(id, getString(text), enabled, null); + } + + public ToolbarMenuSubItem(int id, String text, ClickCallback clicked) { + this(id, text, true, clicked); + } + + public ToolbarMenuSubItem(String text, ClickCallback clicked) { + this(-1, text, true, clicked); + } + + public ToolbarMenuSubItem(int id, String text, boolean enabled, ClickCallback clicked) { + this.id = id; + this.text = text; + this.enabled = enabled; + this.clicked = clicked; + } + + public void performClick() { + if (clicked != null) { + clicked.clicked(this); + } + } + + public interface ClickCallback { + void clicked(ToolbarMenuSubItem subItem); + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenuView.java b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenuView.java new file mode 100644 index 00000000..f99cadb3 --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenuView.java @@ -0,0 +1,100 @@ +/* + * 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.content.Context; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; + +import java.util.Collections; + +import static org.floens.chan.utils.AndroidUtils.dp; +import static org.floens.chan.utils.AndroidUtils.setRoundItemBackground; + +/** + * The container view for the list of ToolbarMenuItems, a list of ImageViews. + */ +public class ToolbarMenuView extends LinearLayout { + private ToolbarMenu menu; + + public ToolbarMenuView(Context context) { + this(context, null); + } + + public ToolbarMenuView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ToolbarMenuView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + setOrientation(HORIZONTAL); + setGravity(Gravity.CENTER_VERTICAL); + } + + public void attach(ToolbarMenu menu) { + this.menu = menu; + + setupMenuViews(); + } + + public void detach() { + for (ToolbarMenuItem item : menu.items) { + item.detach(); + } + } + + private void setupMenuViews() { + removeAllViews(); + + Collections.sort(menu.items, (a, b) -> a.order - b.order); + + for (ToolbarMenuItem item : menu.items) { + ImageView imageView = new ImageView(getContext()); + + imageView.setOnClickListener((v) -> { + handleClick(item); + }); + imageView.setFocusable(true); + imageView.setScaleType(ImageView.ScaleType.CENTER); + + imageView.setVisibility(item.visible ? View.VISIBLE : View.GONE); + + if (item.overflowStyle) { + imageView.setLayoutParams(new LayoutParams(dp(44), dp(56))); + imageView.setPadding(dp(8), 0, dp(16), 0); + } else { + imageView.setLayoutParams(new LinearLayout.LayoutParams(dp(50), dp(56))); + } + + imageView.setImageDrawable(item.drawable); + setRoundItemBackground(imageView); + + addView(imageView); + + item.attach(imageView); + } + } + + private void handleClick(ToolbarMenuItem item) { + item.performClick(); + } +} 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 1f5e3575..18e4181d 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 @@ -27,7 +27,6 @@ import android.view.ViewTreeObserver; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ListAdapter; -import android.widget.PopupWindow; import android.widget.TextView; import org.floens.chan.R; @@ -202,16 +201,13 @@ public class FloatingMenu { }; anchor.getViewTreeObserver().addOnGlobalLayoutListener(globalLayoutListener); - popupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() { - @Override - public void onDismiss() { - if (anchor.getViewTreeObserver().isAlive()) { - anchor.getViewTreeObserver().removeGlobalOnLayoutListener(globalLayoutListener); - } - globalLayoutListener = null; - popupWindow = null; - callback.onFloatingMenuDismissed(FloatingMenu.this); + popupWindow.setOnDismissListener(() -> { + if (anchor.getViewTreeObserver().isAlive()) { + anchor.getViewTreeObserver().removeGlobalOnLayoutListener(globalLayoutListener); } + globalLayoutListener = null; + popupWindow = null; + callback.onFloatingMenuDismissed(FloatingMenu.this); }); popupWindow.show();