From 444277ddbd818196d2a233d172e2984a3807e6bd Mon Sep 17 00:00:00 2001 From: Floens Date: Sun, 15 Nov 2015 17:07:33 +0100 Subject: [PATCH] Made all controllers swipeable Controllers now have addChildController and removeChildController methods that manage adding and removing child controllers to the controller. This way the methods like onBack are propagated correctly. Reworked the toolbar setNavigation methods to allow the animation fraction to be set manually and to make it possible to cancel a transition. --- .../floens/chan/controller/Controller.java | 132 ++++++-- .../chan/controller/ControllerLogic.java | 99 ------ .../chan/controller/ControllerTransition.java | 4 +- .../chan/controller/NavigationController.java | 138 ++++---- .../{ => transition}/FadeInTransition.java | 4 +- .../{ => transition}/FadeOutTransition.java | 4 +- .../PopControllerTransition.java | 4 +- .../PushControllerTransition.java | 3 +- .../NavigationControllerContainerLayout.java | 310 ++++++++++++++++++ .../chan/ui/activity/StartActivity.java | 8 +- .../org/floens/chan/ui/cell/PostCell.java | 7 +- .../chan/ui/controller/BrowseController.java | 9 +- .../chan/ui/controller/DrawerController.java | 86 +++-- .../ImageViewerNavigationController.java | 14 +- .../chan/ui/controller/PopupController.java | 27 +- .../controller/SplitNavigationController.java | 73 +++-- .../StyledToolbarNavigationController.java | 13 +- .../controller/ThemeSettingsController.java | 1 + .../ToolbarNavigationController.java | 36 +- .../ui/controller/ViewThreadController.java | 6 +- .../ui/layout/PopupControllerContainer.java | 18 +- .../chan/ui/toolbar/NavigationItem.java | 1 + .../org/floens/chan/ui/toolbar/Toolbar.java | 187 ++++++----- .../layout/controller_navigation_drawer.xml | 6 +- .../controller_navigation_image_viewer.xml | 2 +- .../layout/controller_navigation_toolbar.xml | 2 +- .../res/layout/layout_controller_popup.xml | 8 +- 27 files changed, 785 insertions(+), 417 deletions(-) delete mode 100644 Clover/app/src/main/java/org/floens/chan/controller/ControllerLogic.java rename Clover/app/src/main/java/org/floens/chan/controller/{ => transition}/FadeInTransition.java (93%) rename Clover/app/src/main/java/org/floens/chan/controller/{ => transition}/FadeOutTransition.java (93%) rename Clover/app/src/main/java/org/floens/chan/controller/{ => transition}/PopControllerTransition.java (95%) rename Clover/app/src/main/java/org/floens/chan/controller/{ => transition}/PushControllerTransition.java (96%) create mode 100644 Clover/app/src/main/java/org/floens/chan/controller/ui/NavigationControllerContainerLayout.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 80b51537..09aed317 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 @@ -23,10 +23,17 @@ import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.ViewGroup; +import org.floens.chan.controller.transition.FadeInTransition; +import org.floens.chan.controller.transition.FadeOutTransition; import org.floens.chan.ui.activity.StartActivity; +import org.floens.chan.ui.controller.SplitNavigationController; import org.floens.chan.ui.toolbar.NavigationItem; +import org.floens.chan.utils.AndroidUtils; import org.floens.chan.utils.Logger; +import java.util.ArrayList; +import java.util.List; + public abstract class Controller { private static final boolean LOG_STATES = false; @@ -37,21 +44,26 @@ public abstract class Controller { public Controller parentController; + public List childControllers = new ArrayList<>(); + // NavigationControllers members public Controller previousSiblingController; public NavigationController navigationController; + public SplitNavigationController splitNavigationController; + /** * Controller that this controller is presented by. */ - public Controller presentingController; + public Controller presentingByController; /** * Controller that this controller is presenting. */ - public Controller presentedController; + public Controller presentingThisController; public boolean alive = false; + public boolean shown = false; public Controller(Context context) { this.context = context; @@ -65,15 +77,29 @@ public abstract class Controller { } public void onShow() { + shown = true; if (LOG_STATES) { Logger.test(getClass().getSimpleName() + " onShow"); } + + for (Controller controller : childControllers) { + if (!controller.shown) { + controller.onShow(); + } + } } public void onHide() { + shown = false; if (LOG_STATES) { Logger.test(getClass().getSimpleName() + " onHide"); } + + for (Controller controller : childControllers) { + if (controller.shown) { + controller.onHide(); + } + } } public void onDestroy() { @@ -81,16 +107,72 @@ public abstract class Controller { if (LOG_STATES) { Logger.test(getClass().getSimpleName() + " onDestroy"); } + + while (childControllers.size() > 0) { + removeChildController(childControllers.get(0)); + } + } + + public void addChildController(Controller controller) { + childControllers.add(controller); + controller.parentController = this; + controller.splitNavigationController = splitNavigationController; + controller.onCreate(); + } + + public boolean removeChildController(Controller controller) { + controller.onDestroy(); + return childControllers.remove(controller); + } + + public void attach(ViewGroup parentView, boolean over) { + ViewGroup.LayoutParams params = view.getLayoutParams(); + + if (params == null) { + params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + } else { + params.width = ViewGroup.LayoutParams.MATCH_PARENT; + params.height = ViewGroup.LayoutParams.MATCH_PARENT; + } + + view.setLayoutParams(params); + + if (over) { + parentView.addView(view, view.getLayoutParams()); + } else { + parentView.addView(view, 0, view.getLayoutParams()); + } + } + + public void detach() { + AndroidUtils.removeFromParentView(view); } public void onConfigurationChanged(Configuration newConfig) { + for (Controller controller : childControllers) { + controller.onConfigurationChanged(newConfig); + } } public boolean dispatchKeyEvent(KeyEvent event) { + for (int i = childControllers.size() - 1; i >= 0; i--) { + Controller controller = childControllers.get(i); + if (controller.dispatchKeyEvent(event)) { + return true; + } + } + return false; } public boolean onBack() { + for (int i = childControllers.size() - 1; i >= 0; i--) { + Controller controller = childControllers.get(i); + if (controller.onBack()) { + return true; + } + } + return false; } @@ -100,21 +182,19 @@ public abstract class Controller { public void presentController(Controller controller, boolean animated) { ViewGroup contentView = ((StartActivity) context).getContentView(); - presentedController = controller; - controller.presentingController = this; + presentingThisController = controller; + controller.presentingByController = this; + + controller.onCreate(); + controller.attach(contentView, true); + controller.onShow(); if (animated) { ControllerTransition transition = new FadeInTransition(); - transition.setCallback(new ControllerTransition.Callback() { - @Override - public void onControllerTransitionCompleted(ControllerTransition transition) { - ControllerLogic.finishTransition(transition); - } - }); - ControllerLogic.startTransition(null, controller, true, contentView, transition); - } else { - ControllerLogic.transition(null, controller, true, contentView); + transition.to = controller; + transition.perform(); } + ((StartActivity) context).addController(controller); } @@ -123,22 +203,36 @@ public abstract class Controller { } public void stopPresenting(boolean animated) { - ViewGroup contentView = ((StartActivity) context).getContentView(); - if (animated) { ControllerTransition transition = new FadeOutTransition(); + transition.from = this; + transition.perform(); transition.setCallback(new ControllerTransition.Callback() { @Override public void onControllerTransitionCompleted(ControllerTransition transition) { - ControllerLogic.finishTransition(transition); + finishPresenting(); } }); - ControllerLogic.startTransition(this, null, false, contentView, transition); } else { - ControllerLogic.transition(this, null, false, contentView); + finishPresenting(); } + ((StartActivity) context).removeController(this); - presentingController.presentedController = null; + presentingByController.presentingThisController = null; + } + + private void finishPresenting() { + onHide(); + detach(); + onDestroy(); + } + + public Controller getTop() { + if (childControllers.size() > 0) { + return childControllers.get(childControllers.size() - 1); + } else { + return null; + } } public ViewGroup inflateRes(int resId) { diff --git a/Clover/app/src/main/java/org/floens/chan/controller/ControllerLogic.java b/Clover/app/src/main/java/org/floens/chan/controller/ControllerLogic.java deleted file mode 100644 index e50d4e79..00000000 --- a/Clover/app/src/main/java/org/floens/chan/controller/ControllerLogic.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.controller; - -import android.view.ViewGroup; - -import org.floens.chan.utils.AndroidUtils; - -public class ControllerLogic { - public static void attach(Controller controller, ViewGroup view, boolean over) { - ViewGroup.LayoutParams params = controller.view.getLayoutParams(); - - if (params == null) { - params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); - } else { - params.width = ViewGroup.LayoutParams.MATCH_PARENT; - params.height = ViewGroup.LayoutParams.MATCH_PARENT; - } - - controller.view.setLayoutParams(params); - - if (over) { - view.addView(controller.view, controller.view.getLayoutParams()); - } else { - view.addView(controller.view, 0, controller.view.getLayoutParams()); - } - } - - public static void detach(Controller controller) { - AndroidUtils.removeFromParentView(controller.view); - } - - public static void transition(Controller from, Controller to, boolean pushing, ViewGroup toView) { - transition(from, to, pushing, !pushing, toView); - } - - public static void transition(Controller from, Controller to, boolean createTo, boolean destroyFrom, ViewGroup toView) { - if (to != null) { - if (createTo) { - to.onCreate(); - } - - attach(to, toView, true); - to.onShow(); - } - - if (from != null) { - from.onHide(); - detach(from); - - if (destroyFrom) { - from.onDestroy(); - } - } - } - - public static void startTransition(Controller from, Controller to, boolean pushing, ViewGroup toView, ControllerTransition transition) { - transition.destroyFrom = !pushing; - transition.from = from; - transition.to = to; - - if (to != null) { - if (pushing) { - to.onCreate(); - } - - attach(to, toView, transition.viewOver); - to.onShow(); - } - - transition.perform(); - } - - public static void finishTransition(ControllerTransition transition) { - if (transition.from != null) { - transition.from.onHide(); - detach(transition.from); - - if (transition.destroyFrom) { - transition.from.onDestroy(); - } - } - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/controller/ControllerTransition.java b/Clover/app/src/main/java/org/floens/chan/controller/ControllerTransition.java index e9e2ca95..99a85223 100644 --- a/Clover/app/src/main/java/org/floens/chan/controller/ControllerTransition.java +++ b/Clover/app/src/main/java/org/floens/chan/controller/ControllerTransition.java @@ -28,7 +28,9 @@ public abstract class ControllerTransition { public abstract void perform(); public void onCompleted() { - this.callback.onControllerTransitionCompleted(this); + if (callback != null) { + callback.onControllerTransitionCompleted(this); + } } public void setCallback(Callback callback) { diff --git a/Clover/app/src/main/java/org/floens/chan/controller/NavigationController.java b/Clover/app/src/main/java/org/floens/chan/controller/NavigationController.java index cd755a54..ae6d3eda 100644 --- a/Clover/app/src/main/java/org/floens/chan/controller/NavigationController.java +++ b/Clover/app/src/main/java/org/floens/chan/controller/NavigationController.java @@ -18,17 +18,15 @@ package org.floens.chan.controller; import android.content.Context; -import android.content.res.Configuration; import android.view.KeyEvent; -import android.view.ViewGroup; -import java.util.ArrayList; -import java.util.List; +import org.floens.chan.controller.transition.PopControllerTransition; +import org.floens.chan.controller.transition.PushControllerTransition; +import org.floens.chan.controller.ui.NavigationControllerContainerLayout; -public abstract class NavigationController extends Controller implements ControllerTransition.Callback { - protected ViewGroup container; +public abstract class NavigationController extends Controller { + protected NavigationControllerContainerLayout container; - protected List controllerList = new ArrayList<>(); protected ControllerTransition controllerTransition; protected boolean blockingInput = false; @@ -36,15 +34,6 @@ public abstract class NavigationController extends Controller implements Control super(context); } - @Override - public void onDestroy() { - super.onDestroy(); - - while (controllerList.size() > 0) { - popController(false); - } - } - public boolean pushController(final Controller to) { return pushController(to, true); } @@ -56,7 +45,7 @@ public abstract class NavigationController extends Controller implements Control public boolean pushController(final Controller to, ControllerTransition controllerTransition) { if (blockingInput) return false; - final Controller from = controllerList.size() > 0 ? controllerList.get(controllerList.size() - 1) : null; + final Controller from = getTop(); if (from == null && controllerTransition != null) { throw new IllegalArgumentException("Cannot animate push when from is null"); @@ -65,8 +54,6 @@ public abstract class NavigationController extends Controller implements Control to.navigationController = this; to.previousSiblingController = from; - controllerList.add(to); - transition(from, to, true, controllerTransition); return true; @@ -83,33 +70,80 @@ public abstract class NavigationController extends Controller implements Control public boolean popController(ControllerTransition controllerTransition) { if (blockingInput) return false; - final Controller from = controllerList.get(controllerList.size() - 1); - final Controller to = controllerList.size() > 1 ? controllerList.get(controllerList.size() - 2) : null; + final Controller from = getTop(); + final Controller to = childControllers.size() > 1 ? childControllers.get(childControllers.size() - 2) : null; transition(from, to, false, controllerTransition); return true; } - public void transition(Controller from, Controller to, boolean pushing, ControllerTransition controllerTransition) { + public boolean isBlockingInput() { + return blockingInput; + } + + public boolean beginSwipeTransition(final Controller from, final Controller to) { + if (blockingInput) return false; + + if (this.controllerTransition != null) { + throw new IllegalArgumentException("Cannot transition while another transition is in progress."); + } + + to.attach(container, false); + to.onShow(); + + return true; + } + + public void swipeTransitionProgress(float progress) { + } + + public void endSwipeTransition(final Controller from, final Controller to, boolean finish) { + if (finish) { + from.onHide(); + from.detach(); + removeChildController(from); + } else { + to.onHide(); + to.detach(); + } + + controllerTransition = null; + blockingInput = false; + } + + public void transition(final Controller from, final Controller to, final boolean pushing, ControllerTransition controllerTransition) { if (this.controllerTransition != null) { throw new IllegalArgumentException("Cannot transition while another transition is in progress."); } - if (!pushing && controllerList.size() == 0) { + if (!pushing && childControllers.size() == 0) { throw new IllegalArgumentException("Cannot pop with no controllers left"); } + if (pushing && to != null) { + addChildController(to); + } + + if (to != null) { + to.attach(container, pushing); + to.onShow(); + } + if (controllerTransition != null) { + controllerTransition.from = from; + controllerTransition.to = to; blockingInput = true; this.controllerTransition = controllerTransition; - controllerTransition.setCallback(this); - ControllerLogic.startTransition(from, to, pushing, container, controllerTransition); + controllerTransition.setCallback(new ControllerTransition.Callback() { + @Override + public void onControllerTransitionCompleted(ControllerTransition transition) { + finishTransition(from, pushing); + } + }); + controllerTransition.perform(); } else { - ControllerLogic.transition(from, to, pushing, container); - if (!pushing) { - controllerList.remove(from); - } + finishTransition(from, pushing); } if (to != null) { @@ -121,48 +155,35 @@ public abstract class NavigationController extends Controller implements Control } } - protected void controllerPushed(Controller controller) { - } - - protected void controllerPopped(Controller controller) { - } - - @Override - public void onControllerTransitionCompleted(ControllerTransition transition) { - ControllerLogic.finishTransition(transition); + private void finishTransition(Controller from, boolean pushing) { + if (from != null) { + from.onHide(); + from.detach(); + } - if (transition.destroyFrom) { - controllerList.remove(transition.from); + if (!pushing && from != null) { + removeChildController(from); } controllerTransition = null; blockingInput = false; } - public Controller getTop() { - if (controllerList.size() > 0) { - return controllerList.get(controllerList.size() - 1); - } else { - return null; - } + protected void controllerPushed(Controller controller) { } - /* - * Used to save instance state - */ - public List getControllerList() { - return controllerList; + protected void controllerPopped(Controller controller) { } public boolean onBack() { if (blockingInput) return true; - if (controllerList.size() > 0) { - Controller top = controllerList.get(controllerList.size() - 1); + if (childControllers.size() > 0) { + Controller top = getTop(); if (top.onBack()) { return true; } else { - if (controllerList.size() > 1) { + if (childControllers.size() > 1) { popController(); return true; } else { @@ -177,13 +198,6 @@ public abstract class NavigationController extends Controller implements Control @Override public boolean dispatchKeyEvent(KeyEvent event) { Controller top = getTop(); - return (top != null && top.dispatchKeyEvent(event)) || super.dispatchKeyEvent(event); - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - for (Controller controller : controllerList) { - controller.onConfigurationChanged(newConfig); - } + return (top != null && top.dispatchKeyEvent(event)); } } diff --git a/Clover/app/src/main/java/org/floens/chan/controller/FadeInTransition.java b/Clover/app/src/main/java/org/floens/chan/controller/transition/FadeInTransition.java similarity index 93% rename from Clover/app/src/main/java/org/floens/chan/controller/FadeInTransition.java rename to Clover/app/src/main/java/org/floens/chan/controller/transition/FadeInTransition.java index ec3c3329..d49c3106 100644 --- a/Clover/app/src/main/java/org/floens/chan/controller/FadeInTransition.java +++ b/Clover/app/src/main/java/org/floens/chan/controller/transition/FadeInTransition.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.floens.chan.controller; +package org.floens.chan.controller.transition; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -24,6 +24,8 @@ import android.animation.ObjectAnimator; import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; +import org.floens.chan.controller.ControllerTransition; + public class FadeInTransition extends ControllerTransition { @Override public void perform() { diff --git a/Clover/app/src/main/java/org/floens/chan/controller/FadeOutTransition.java b/Clover/app/src/main/java/org/floens/chan/controller/transition/FadeOutTransition.java similarity index 93% rename from Clover/app/src/main/java/org/floens/chan/controller/FadeOutTransition.java rename to Clover/app/src/main/java/org/floens/chan/controller/transition/FadeOutTransition.java index 30868e47..df64b68b 100644 --- a/Clover/app/src/main/java/org/floens/chan/controller/FadeOutTransition.java +++ b/Clover/app/src/main/java/org/floens/chan/controller/transition/FadeOutTransition.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.floens.chan.controller; +package org.floens.chan.controller.transition; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -24,6 +24,8 @@ import android.animation.ObjectAnimator; import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; +import org.floens.chan.controller.ControllerTransition; + public class FadeOutTransition extends ControllerTransition { @Override public void perform() { diff --git a/Clover/app/src/main/java/org/floens/chan/controller/PopControllerTransition.java b/Clover/app/src/main/java/org/floens/chan/controller/transition/PopControllerTransition.java similarity index 95% rename from Clover/app/src/main/java/org/floens/chan/controller/PopControllerTransition.java rename to Clover/app/src/main/java/org/floens/chan/controller/transition/PopControllerTransition.java index 7349fe3f..285efe13 100644 --- a/Clover/app/src/main/java/org/floens/chan/controller/PopControllerTransition.java +++ b/Clover/app/src/main/java/org/floens/chan/controller/transition/PopControllerTransition.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.floens.chan.controller; +package org.floens.chan.controller.transition; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -25,6 +25,8 @@ import android.view.View; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; +import org.floens.chan.controller.ControllerTransition; + public class PopControllerTransition extends ControllerTransition { public PopControllerTransition() { viewOver = false; diff --git a/Clover/app/src/main/java/org/floens/chan/controller/PushControllerTransition.java b/Clover/app/src/main/java/org/floens/chan/controller/transition/PushControllerTransition.java similarity index 96% rename from Clover/app/src/main/java/org/floens/chan/controller/PushControllerTransition.java rename to Clover/app/src/main/java/org/floens/chan/controller/transition/PushControllerTransition.java index 939fd705..acec102f 100644 --- a/Clover/app/src/main/java/org/floens/chan/controller/PushControllerTransition.java +++ b/Clover/app/src/main/java/org/floens/chan/controller/transition/PushControllerTransition.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.floens.chan.controller; +package org.floens.chan.controller.transition; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -25,6 +25,7 @@ import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.DecelerateInterpolator; +import org.floens.chan.controller.ControllerTransition; import org.floens.chan.utils.AndroidUtils; public class PushControllerTransition extends ControllerTransition { diff --git a/Clover/app/src/main/java/org/floens/chan/controller/ui/NavigationControllerContainerLayout.java b/Clover/app/src/main/java/org/floens/chan/controller/ui/NavigationControllerContainerLayout.java new file mode 100644 index 00000000..d8899237 --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/controller/ui/NavigationControllerContainerLayout.java @@ -0,0 +1,310 @@ +/* + * 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.controller.ui; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.support.v4.view.ViewCompat; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.ViewConfiguration; +import android.widget.FrameLayout; +import android.widget.Scroller; + +import org.floens.chan.controller.Controller; +import org.floens.chan.controller.NavigationController; + +public class NavigationControllerContainerLayout extends FrameLayout { + private NavigationController navigationController; + + private int slopPixels; + private int flingPixels; + private int maxFlingPixels; + + private boolean swipeEnabled = true; + + private MotionEvent downEvent; + private boolean dontStartSwiping = false; + private MotionEvent swipeStartEvent; + private VelocityTracker velocityTracker; + private Scroller scroller; + private boolean popAfterSwipe = false; + private Paint shadowPaint; + private Rect shadowRect = new Rect(); + private boolean drawShadow; + private int swipePosition; + private boolean swiping = false; + + private Controller swipingController; + + public NavigationControllerContainerLayout(Context context) { + super(context); + init(); + } + + public NavigationControllerContainerLayout(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public NavigationControllerContainerLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + private void init() { + ViewConfiguration viewConfiguration = ViewConfiguration.get(getContext()); + slopPixels = (int) (viewConfiguration.getScaledTouchSlop() * 0.5f); + flingPixels = viewConfiguration.getScaledMinimumFlingVelocity(); + maxFlingPixels = viewConfiguration.getScaledMaximumFlingVelocity(); + + scroller = new Scroller(getContext()); + + shadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + } + + public void setSwipeEnabled(boolean swipeEnabled) { + this.swipeEnabled = swipeEnabled; + } + + public void setNavigationController(NavigationController navigationController) { + this.navigationController = navigationController; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent event) { + if (!swipeEnabled || swiping) { + return false; + } + + int actionMasked = event.getActionMasked(); + + if (actionMasked != MotionEvent.ACTION_DOWN && downEvent == null) { + // Action down wasn't called here, ignore + return false; + } + + if (!navigationController.getTop().navigationItem.swipeable) { + return false; + } + + if (getBelowTop() == null) { + // Cannot swipe now + return false; + } + + switch (actionMasked) { + case MotionEvent.ACTION_DOWN: +// Logger.test("onInterceptTouchEvent down"); + downEvent = MotionEvent.obtain(event); + break; + case MotionEvent.ACTION_MOVE: { +// Logger.test("onInterceptTouchEvent move"); + float x = (event.getX() - downEvent.getX()); + float y = (event.getY() - downEvent.getY()); + + if (Math.abs(y) >= slopPixels) { +// Logger.test("dontStartSwiping = true"); + dontStartSwiping = true; + } + + if (!dontStartSwiping && Math.abs(x) > Math.abs(y) && x >= slopPixels && !navigationController.isBlockingInput()) { +// Logger.test("Start tracking swipe"); + downEvent.recycle(); + downEvent = null; + + swipeStartEvent = MotionEvent.obtain(event); + velocityTracker = VelocityTracker.obtain(); + velocityTracker.addMovement(event); + + swiping = true; + + return true; + } + break; + } + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: { +// Logger.test("onInterceptTouchEvent cancel/up"); + downEvent.recycle(); + downEvent = null; + dontStartSwiping = false; + break; + } + } + + return false; + } + + @Override + public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { + if (disallowIntercept) { + if (downEvent != null) { + downEvent.recycle(); + downEvent = null; + } + dontStartSwiping = false; + } + + super.requestDisallowInterceptTouchEvent(disallowIntercept); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + // This touch wasn't initiated with onInterceptTouchEvent + if (swipeStartEvent == null) { + return false; + } + + if (swipingController == null) { + // Start of swipe + +// Logger.test("Start of swipe"); + + swipingController = navigationController.getTop(); + drawShadow = true; + +// long start = Time.startTiming(); + + Controller below = getBelowTop(); + navigationController.beginSwipeTransition(swipingController, below); + +// Time.endTiming("attach", start); + } + + float translationX = Math.max(0, event.getX() - swipeStartEvent.getX()); + setTopControllerTranslation((int) translationX); + + velocityTracker.addMovement(event); + + switch (event.getActionMasked()) { + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: { +// Logger.test("onTouchEvent cancel or up"); + + scroller.forceFinished(true); + + velocityTracker.addMovement(event); + velocityTracker.computeCurrentVelocity(1000); + float velocity = velocityTracker.getXVelocity(); + + if (translationX > 0f) { + boolean isFling = false; + + if (velocity > 0f && Math.abs(velocity) > flingPixels && Math.abs(velocity) < maxFlingPixels) { + scroller.fling((int) translationX, 0, (int) velocity, 0, 0, Integer.MAX_VALUE, 0, 0); + + if (scroller.getFinalX() >= getWidth()) { + isFling = true; + } + } + + if (isFling) { +// Logger.test("Flinging with velocity = " + velocity); + popAfterSwipe = true; + ViewCompat.postOnAnimation(this, flingRunnable); + } else { +// Logger.test("Snapping back!"); + scroller.forceFinished(true); + scroller.startScroll((int) translationX, 0, -((int) translationX), 0, 300); + popAfterSwipe = false; + ViewCompat.postOnAnimation(this, flingRunnable); + } + } else { + finishSwipe(); + } + + swipeStartEvent.recycle(); + swipeStartEvent = null; + + velocityTracker.recycle(); + velocityTracker = null; + + break; + } + } + + return true; + } + + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + + if (drawShadow) { + float alpha = Math.min(1f, Math.max(0f, 0.5f - (swipePosition / (float) getWidth()) * 0.5f)); + shadowPaint.setColor(Color.argb((int) (alpha * 255f), 0, 0, 0)); + shadowRect.set(0, 0, swipePosition, getHeight()); + canvas.drawRect(shadowRect, shadowPaint); + } + } + + private void finishSwipe() { + Controller below = getBelowTop(); + + navigationController.endSwipeTransition(swipingController, below, popAfterSwipe); + swipingController = null; + drawShadow = false; + swiping = false; + } + + private Runnable flingRunnable = new Runnable() { + @Override + public void run() { + boolean finished = false; + + if (scroller.computeScrollOffset()) { + float translationX = scroller.getCurrX(); + + setTopControllerTranslation((int) translationX); + + // The view is not visible anymore. End it before the fling completely finishes. + if (translationX >= getWidth()) { + finished = true; + } + } else { + finished = true; + } + + if (!finished) { + ViewCompat.postOnAnimation(NavigationControllerContainerLayout.this, flingRunnable); + } else { + finishSwipe(); + } + } + }; + + private void setTopControllerTranslation(int translationX) { + swipePosition = translationX; + swipingController.view.setTranslationX(swipePosition); + navigationController.swipeTransitionProgress(swipePosition / (float) getWidth()); + invalidate(); + } + + private Controller getBelowTop() { + if (navigationController.childControllers.size() >= 2) { + return navigationController.childControllers.get(navigationController.childControllers.size() - 2); + } else { + return null; + } + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java b/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java index 7c390d78..a4f800a4 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java @@ -220,11 +220,11 @@ public class StartActivity extends AppCompatActivity implements NfcAdapter.Creat } else { Loadable thread = null; - if (drawerController.getChildController() instanceof SplitNavigationController) { - SplitNavigationController splitNavigationController = (SplitNavigationController) drawerController.getChildController(); + if (drawerController.childControllers.get(0) instanceof SplitNavigationController) { + SplitNavigationController splitNavigationController = (SplitNavigationController) drawerController.childControllers.get(0); if (splitNavigationController.rightController instanceof NavigationController) { NavigationController rightNavigationController = (NavigationController) splitNavigationController.rightController; - for (Controller controller : rightNavigationController.getControllerList()) { + for (Controller controller : rightNavigationController.childControllers) { if (controller instanceof ViewThreadController) { thread = ((ViewThreadController) controller).getLoadable(); break; @@ -233,7 +233,7 @@ public class StartActivity extends AppCompatActivity implements NfcAdapter.Creat } } else { - List controllers = mainNavigationController.getControllerList(); + List controllers = mainNavigationController.childControllers; for (Controller controller : controllers) { if (controller instanceof ViewThreadController) { thread = ((ViewThreadController) controller).getLoadable(); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/cell/PostCell.java b/Clover/app/src/main/java/org/floens/chan/ui/cell/PostCell.java index 05205637..e369c195 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/cell/PostCell.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/cell/PostCell.java @@ -78,7 +78,7 @@ import static org.floens.chan.utils.AndroidUtils.sp; public class PostCell extends LinearLayout implements PostCellInterface, PostLinkable.Callback { private static final String TAG = "PostCell"; - private static final int COMMENT_MAX_LENGTH_BOARD = 500; + private static final int COMMENT_MAX_LENGTH_BOARD = 350; private ThumbnailView thumbnailView; private FastTextView title; @@ -91,11 +91,9 @@ public class PostCell extends LinearLayout implements PostCellInterface, PostLin private boolean commentClickable = false; private int detailsSizePx; - private int iconsTextSize; private int countrySizePx; private int paddingPx; private boolean threadMode; -// private boolean ignoreNextOnClick; private boolean bound = false; private Theme theme; @@ -143,9 +141,8 @@ public class PostCell extends LinearLayout implements PostCellInterface, PostLin title.setTextSize(textSizeSp); title.setPadding(paddingPx, paddingPx, dp(52), 0); - iconsTextSize = sp(textSizeSp); countrySizePx = sp(textSizeSp - 3); - icons.setHeight(iconsTextSize); + icons.setHeight(sp(textSizeSp)); icons.setSpacing(dp(4)); icons.setPadding(paddingPx, dp(4), paddingPx, 0); 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 60e94fb0..84973d71 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 @@ -209,8 +209,8 @@ public class BrowseController extends ThreadController implements ToolbarMenuIte loadBoard(((FloatingMenuItemBoard) item).board); } else { BoardEditController boardEditController = new BoardEditController(context); - if (navigationController.navigationController instanceof SplitNavigationController) { - navigationController.navigationController.pushController(boardEditController); + if (splitNavigationController != null) { + splitNavigationController.pushController(boardEditController); } else { navigationController.pushController(boardEditController); } @@ -234,9 +234,7 @@ public class BrowseController extends ThreadController implements ToolbarMenuIte } public void showThread(Loadable threadLoadable, boolean animated) { - if (navigationController.navigationController instanceof SplitNavigationController) { - SplitNavigationController splitNavigationController = (SplitNavigationController) navigationController.navigationController; - + if (splitNavigationController != null) { if (splitNavigationController.rightController instanceof StyledToolbarNavigationController) { StyledToolbarNavigationController navigationController = (StyledToolbarNavigationController) splitNavigationController.rightController; @@ -274,6 +272,7 @@ public class BrowseController extends ThreadController implements ToolbarMenuIte for (FloatingMenuItem item : boardItems) { if (((FloatingMenuItemBoard) item).board == board) { navigationItem.middleMenu.setSelectedItem(item); + break; } } navigationItem.updateTitle(); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/DrawerController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/DrawerController.java index 264ae7d7..ce9d9298 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/DrawerController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/DrawerController.java @@ -28,7 +28,6 @@ import android.support.v7.widget.RecyclerView; import android.support.v7.widget.helper.ItemTouchHelper; import android.text.TextUtils; import android.view.Gravity; -import android.view.KeyEvent; import android.view.View; import android.widget.EditText; import android.widget.FrameLayout; @@ -39,7 +38,6 @@ import android.widget.TextView; import org.floens.chan.Chan; import org.floens.chan.R; import org.floens.chan.controller.Controller; -import org.floens.chan.controller.ControllerLogic; import org.floens.chan.controller.NavigationController; import org.floens.chan.core.manager.WatchManager; import org.floens.chan.core.model.Pin; @@ -65,8 +63,6 @@ public class DrawerController extends Controller implements PinAdapter.Callback, protected LinearLayout settings; protected PinAdapter pinAdapter; - private NavigationController childController; - public DrawerController(Context context) { super(context); } @@ -114,21 +110,13 @@ public class DrawerController extends Controller implements PinAdapter.Callback, public void onDestroy() { super.onDestroy(); - if (childController != null) { - childController.onDestroy(); - } - EventBus.getDefault().unregister(this); } - public void setChildController(NavigationController childController) { - childController.parentController = this; - ControllerLogic.transition(this.childController, childController, true, true, container); - this.childController = childController; - } - - public NavigationController getChildController() { - return childController; + public void setChildController(Controller childController) { + addChildController(childController); + childController.attach(container, true); + childController.onShow(); } @Override @@ -151,7 +139,9 @@ public class DrawerController extends Controller implements PinAdapter.Callback, } public void onMenuClicked() { - drawerLayout.openDrawer(drawer); + if (getStyledToolbarNavigationController().getTop().navigationItem.hasDrawer) { + drawerLayout.openDrawer(drawer); + } } @Override @@ -159,35 +149,19 @@ public class DrawerController extends Controller implements PinAdapter.Callback, if (drawerLayout.isDrawerOpen(drawer)) { drawerLayout.closeDrawer(drawer); return true; - } else if (childController != null && childController.onBack()) { - return true; } else { return super.onBack(); } } - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - return (childController != null && childController.dispatchKeyEvent(event)) || super.dispatchKeyEvent(event); - } - @Override public void onPinClicked(Pin pin) { drawerLayout.closeDrawer(Gravity.LEFT); - if (childController instanceof StyledToolbarNavigationController) { - if (childController.getTop() instanceof ThreadController) { - ((ThreadController) childController.getTop()).openPin(pin); - } - } else if (childController instanceof SplitNavigationController) { - SplitNavigationController splitNavigationController = (SplitNavigationController) childController; - if (splitNavigationController.leftController instanceof NavigationController) { - NavigationController navigationController = (NavigationController) splitNavigationController.leftController; - if (navigationController.getTop() instanceof ThreadController) { - ThreadController threadController = (ThreadController) navigationController.getTop(); - threadController.openPin(pin); - } - } + StyledToolbarNavigationController navigationController = getStyledToolbarNavigationController(); + if (navigationController.getTop() instanceof ThreadController) { + ThreadController threadController = (ThreadController) navigationController.getTop(); + threadController.openPin(pin); } } @@ -295,13 +269,8 @@ public class DrawerController extends Controller implements PinAdapter.Callback, } } - if (childController instanceof StyledToolbarNavigationController) { - ((StyledToolbarNavigationController) childController).toolbar.getArrowMenuDrawable().setBadge(count, color); - } else if (childController instanceof SplitNavigationController) { - SplitNavigationController splitNavigationController = (SplitNavigationController) childController; - if (splitNavigationController.leftController instanceof StyledToolbarNavigationController) { - ((StyledToolbarNavigationController) splitNavigationController.leftController).toolbar.getArrowMenuDrawable().setBadge(count, color); - } + if (getTop() != null) { + getStyledToolbarNavigationController().toolbar.getArrowMenuDrawable().setBadge(count, color); } } @@ -317,7 +286,34 @@ public class DrawerController extends Controller implements PinAdapter.Callback, } private void openController(Controller controller) { - childController.pushController(controller); + Controller top = getTop(); + if (top instanceof NavigationController) { + ((NavigationController) top).pushController(controller); + } else if (top instanceof SplitNavigationController) { + ((SplitNavigationController) top).pushController(controller); + } + drawerLayout.closeDrawer(Gravity.LEFT); } + + private StyledToolbarNavigationController getStyledToolbarNavigationController() { + StyledToolbarNavigationController navigationController = null; + + Controller top = getTop(); + if (top instanceof StyledToolbarNavigationController) { + navigationController = (StyledToolbarNavigationController) top; + } else if (top instanceof SplitNavigationController) { + SplitNavigationController splitNavigationController = (SplitNavigationController) top; + if (splitNavigationController.leftController instanceof StyledToolbarNavigationController) { + navigationController = (StyledToolbarNavigationController) splitNavigationController.leftController; + } + } + + if (navigationController == null) { + throw new IllegalStateException("The child controller of a DrawerController must either be StyledToolbarNavigationController" + + "or an SplitNavigationController that has a StyledToolbarNavigationController."); + } + + return navigationController; + } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerNavigationController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerNavigationController.java index 51491840..1e691a32 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerNavigationController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerNavigationController.java @@ -18,14 +18,11 @@ package org.floens.chan.ui.controller; import android.content.Context; -import android.view.ViewGroup; -import android.widget.FrameLayout; import org.floens.chan.R; -import org.floens.chan.controller.NavigationController; +import org.floens.chan.controller.ui.NavigationControllerContainerLayout; import org.floens.chan.core.model.Loadable; import org.floens.chan.core.model.PostImage; -import org.floens.chan.ui.theme.ThemeHelper; import org.floens.chan.ui.toolbar.Toolbar; import java.util.List; @@ -42,15 +39,16 @@ public class ImageViewerNavigationController extends ToolbarNavigationController super.onCreate(); view = inflateRes(R.layout.controller_navigation_image_viewer); - container = (ViewGroup) view.findViewById(R.id.container); + container = (NavigationControllerContainerLayout) view.findViewById(R.id.container); + container.setNavigationController(this); + container.setSwipeEnabled(false); toolbar = (Toolbar) view.findViewById(R.id.toolbar); toolbar.setCallback(this); - - imageViewerController = new ImageViewerController(context, toolbar); - pushController(imageViewerController, false); } public void showImages(final List images, final int index, final Loadable loadable, final ImageViewerController.PreviewCallback previewCallback) { + imageViewerController = new ImageViewerController(context, toolbar); + pushController(imageViewerController, false); imageViewerController.setPreviewCallback(previewCallback); imageViewerController.getPresenter().showImages(images, index, loadable); } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/PopupController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/PopupController.java index 5f8fc7be..58448d5f 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/PopupController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/PopupController.java @@ -23,15 +23,12 @@ import android.widget.FrameLayout; import org.floens.chan.R; import org.floens.chan.controller.Controller; -import org.floens.chan.controller.ControllerLogic; import org.floens.chan.controller.NavigationController; public class PopupController extends Controller implements View.OnClickListener { private FrameLayout topView; private FrameLayout container; - private NavigationController childController; - public PopupController(Context context) { super(context); } @@ -46,24 +43,10 @@ public class PopupController extends Controller implements View.OnClickListener container = (FrameLayout) view.findViewById(R.id.container); } - @Override - public void onDestroy() { - super.onDestroy(); - - if (childController != null) { - childController.onDestroy(); - } - } - public void setChildController(NavigationController childController) { - childController.parentController = this; - ControllerLogic.transition(this.childController, childController, true, true, container); - this.childController = childController; - } - - @Override - public boolean onBack() { - return childController != null && childController.onBack(); + addChildController(childController); + childController.attach(container, true); + childController.onShow(); } @Override @@ -72,8 +55,8 @@ public class PopupController extends Controller implements View.OnClickListener } public void dismiss() { - if (presentingController instanceof SplitNavigationController) { - ((SplitNavigationController) presentingController).popAll(); + if (presentingByController instanceof SplitNavigationController) { + ((SplitNavigationController) presentingByController).popAll(); } } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/SplitNavigationController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/SplitNavigationController.java index 2390597b..b269dd06 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/SplitNavigationController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/SplitNavigationController.java @@ -27,15 +27,15 @@ import android.widget.LinearLayout; import org.floens.chan.R; import org.floens.chan.controller.Controller; -import org.floens.chan.controller.ControllerLogic; import org.floens.chan.controller.ControllerTransition; -import org.floens.chan.controller.NavigationController; +import org.floens.chan.controller.transition.PopControllerTransition; +import org.floens.chan.controller.transition.PushControllerTransition; import org.floens.chan.utils.AndroidUtils; import static org.floens.chan.utils.AndroidUtils.dp; import static org.floens.chan.utils.AndroidUtils.getAttrColor; -public class SplitNavigationController extends NavigationController implements AndroidUtils.OnMeasuredCallback { +public class SplitNavigationController extends Controller implements AndroidUtils.OnMeasuredCallback { public Controller leftController; public Controller rightController; @@ -55,6 +55,8 @@ public class SplitNavigationController extends NavigationController implements A public void onCreate() { super.onCreate(); + splitNavigationController = this; + LinearLayout wrap = new LinearLayout(context); view = wrap; @@ -73,44 +75,56 @@ public class SplitNavigationController extends NavigationController implements A AndroidUtils.waitForMeasure(view, this); } - @Override - public void onDestroy() { - super.onDestroy(); - - if (leftController != null) { - leftController.onDestroy(); - } - if (rightController != null) { - rightController.onDestroy(); - } - } - public void setEmptyView(ViewGroup emptyView) { this.emptyView = emptyView; } public void setLeftController(Controller leftController) { - leftController.navigationController = this; - ControllerLogic.transition(this.leftController, leftController, true, true, leftControllerView); + if (this.leftController != null) { + this.leftController.onHide(); + this.leftController.detach(); + removeChildController(this.leftController); + } + this.leftController = leftController; + + if (leftController != null) { + addChildController(leftController); + leftController.attach(leftControllerView, true); + leftController.onShow(); + } else { + + } } public void setRightController(Controller rightController) { - if (rightController != null) { - rightController.navigationController = this; + if (this.rightController != null) { + this.rightController.onHide(); + this.rightController.detach(); + removeChildController(this.rightController); } else { rightControllerView.removeAllViews(); } - ControllerLogic.transition(this.rightController, rightController, true, true, rightControllerView); this.rightController = rightController; - if (rightController == null) { + if (rightController != null) { + addChildController(rightController); + rightController.attach(rightControllerView, true); + rightController.onShow(); + } else { rightControllerView.addView(emptyView); } } - @Override + public boolean pushController(final Controller to) { + return pushController(to, true); + } + + public boolean pushController(final Controller to, boolean animated) { + return pushController(to, animated ? new PushControllerTransition() : null); + } + public boolean pushController(Controller to, ControllerTransition controllerTransition) { if (popup == null) { popup = new PopupController(context); @@ -125,11 +139,18 @@ public class SplitNavigationController extends NavigationController implements A return true; } - @Override + public boolean popController() { + return popController(true); + } + + public boolean popController(boolean animated) { + return popController(animated ? new PopControllerTransition() : null); + } + public boolean popController(ControllerTransition controllerTransition) { if (popup != null) { - if (popupChild.getControllerList().size() == 1) { - presentedController.stopPresenting(); + if (popupChild.childControllers.size() == 1) { + presentingThisController.stopPresenting(); popup = null; popupChild = null; } else { @@ -143,7 +164,7 @@ public class SplitNavigationController extends NavigationController implements A public void popAll() { if (popup != null) { - presentedController.stopPresenting(); + presentingThisController.stopPresenting(); popup = null; popupChild = null; } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/StyledToolbarNavigationController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/StyledToolbarNavigationController.java index 449e7e7f..223a4d52 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/StyledToolbarNavigationController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/StyledToolbarNavigationController.java @@ -18,11 +18,11 @@ package org.floens.chan.ui.controller; import android.content.Context; -import android.view.ViewGroup; import org.floens.chan.R; import org.floens.chan.controller.Controller; import org.floens.chan.controller.ControllerTransition; +import org.floens.chan.controller.ui.NavigationControllerContainerLayout; import org.floens.chan.ui.theme.ThemeHelper; import org.floens.chan.ui.toolbar.Toolbar; @@ -36,7 +36,8 @@ public class StyledToolbarNavigationController extends ToolbarNavigationControll super.onCreate(); view = inflateRes(R.layout.controller_navigation_toolbar); - container = (ViewGroup) view.findViewById(R.id.container); + container = (NavigationControllerContainerLayout) view.findViewById(R.id.container); + container.setNavigationController(this); toolbar = (Toolbar) view.findViewById(R.id.toolbar); toolbar.setBackgroundColor(ThemeHelper.getInstance().getTheme().primaryColor.color); toolbar.setCallback(this); @@ -58,11 +59,10 @@ public class StyledToolbarNavigationController extends ToolbarNavigationControll public boolean onBack() { if (super.onBack()) { return true; - } else if (parentController instanceof PopupController && controllerList.size() == 1) { + } else if (parentController instanceof PopupController && childControllers.size() == 1) { ((PopupController) parentController).dismiss(); return true; - } else if (navigationController instanceof SplitNavigationController && controllerList.size() == 1) { - SplitNavigationController splitNavigationController = (SplitNavigationController) navigationController; + } else if (splitNavigationController != null && childControllers.size() == 1) { if (splitNavigationController.rightController == this) { splitNavigationController.setRightController(null); return true; @@ -85,8 +85,7 @@ public class StyledToolbarNavigationController extends ToolbarNavigationControll private DrawerController getDrawerController() { if (parentController instanceof DrawerController) { return (DrawerController) parentController; - } else if (navigationController instanceof SplitNavigationController) { - SplitNavigationController splitNavigationController = (SplitNavigationController) navigationController; + } else if (splitNavigationController != null) { if (splitNavigationController.parentController instanceof DrawerController) { return (DrawerController) splitNavigationController.parentController; } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/ThemeSettingsController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/ThemeSettingsController.java index 7552b494..7e8ea4fb 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/ThemeSettingsController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/ThemeSettingsController.java @@ -116,6 +116,7 @@ public class ThemeSettingsController extends Controller implements View.OnClickL super.onCreate(); navigationItem.setTitle(R.string.settings_screen_theme); + navigationItem.swipeable = false; view = inflateRes(R.layout.controller_theme); themeHelper = ThemeHelper.getInstance(); 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 3783ee11..b11e0817 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 @@ -51,6 +51,38 @@ public abstract class ToolbarNavigationController extends NavigationController i } } + @Override + public boolean beginSwipeTransition(Controller from, Controller to) { + if (!super.beginSwipeTransition(from, to)) { + return false; + } + + toolbar.processScrollCollapse(Toolbar.TOOLBAR_COLLAPSE_SHOW, true); + toolbar.beginTransition(to.navigationItem); + + return true; + } + + @Override + public void swipeTransitionProgress(float progress) { + super.swipeTransitionProgress(progress); + + toolbar.transitionProgress(progress, false); + } + + @Override + public void endSwipeTransition(Controller from, Controller to, boolean finish) { + super.endSwipeTransition(from, to, finish); + + toolbar.finishTransition(finish); + + if (finish) { + updateToolbarCollapse(to, controllerTransition != null); + } else { + updateToolbarCollapse(from, controllerTransition != null); + } + } + @Override public void onMenuOrBackClicked(boolean isArrow) { if (isArrow) { @@ -104,8 +136,4 @@ public abstract class ToolbarNavigationController extends NavigationController i void onSearchEntered(String entered); } - - public interface ToolbarMenuCallback { - void onMenuOrBackClicked(boolean isArrow); - } } 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 d0f4cd38..3611dc57 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 @@ -202,9 +202,9 @@ public class ViewThreadController extends ThreadController implements ThreadLayo if (navigationController.parentController instanceof DrawerController) { ((DrawerController) navigationController.parentController).setPinHighlighted(pin); - } else if (navigationController.navigationController instanceof SplitNavigationController) { - if (((SplitNavigationController) navigationController.navigationController).parentController instanceof DrawerController) { - ((DrawerController) ((SplitNavigationController) navigationController.navigationController).parentController).setPinHighlighted(pin); + } else if (splitNavigationController != null) { + if (splitNavigationController.parentController instanceof DrawerController) { + ((DrawerController) splitNavigationController.parentController).setPinHighlighted(pin); } } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/layout/PopupControllerContainer.java b/Clover/app/src/main/java/org/floens/chan/ui/layout/PopupControllerContainer.java index ad3249eb..def81943 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/layout/PopupControllerContainer.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/layout/PopupControllerContainer.java @@ -21,6 +21,8 @@ import android.content.Context; import android.util.AttributeSet; import android.widget.FrameLayout; +import org.floens.chan.utils.Logger; + import static org.floens.chan.utils.AndroidUtils.dp; public class PopupControllerContainer extends FrameLayout { @@ -38,10 +40,18 @@ public class PopupControllerContainer extends FrameLayout { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int maxHeight = dp(600); - - if (MeasureSpec.getSize(heightMeasureSpec) > maxHeight) { - heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.getMode(heightMeasureSpec)); + int widthMode = MeasureSpec.getMode(widthMeasureSpec); + int heightMode = MeasureSpec.getMode(heightMeasureSpec); + int widthSize = MeasureSpec.getSize(widthMeasureSpec); + int heightSize = MeasureSpec.getSize(heightMeasureSpec); + + FrameLayout.LayoutParams child = (LayoutParams) getChildAt(0).getLayoutParams(); + + Logger.test("%s %s", MeasureSpec.toString(widthMeasureSpec), MeasureSpec.toString(heightMeasureSpec)); + if (heightMode == MeasureSpec.EXACTLY && heightSize < dp(600)) { + child.height = heightSize; + } else { + child.height = dp(600); } super.onMeasure(widthMeasureSpec, heightMeasureSpec); 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 842cded1..fb86a8f9 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 @@ -37,6 +37,7 @@ public class NavigationItem { public View rightView; public boolean hasDrawer = false; public boolean collapseToolbar = false; + public boolean swipeable = true; boolean search = false; String searchText; 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 dc91d1d9..7f9d3351 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,7 +19,6 @@ package org.floens.chan.ui.toolbar; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.SuppressLint; @@ -46,9 +45,6 @@ import org.floens.chan.ui.layout.SearchLayout; import org.floens.chan.ui.view.LoadView; 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.setRoundItemBackground; @@ -83,11 +79,13 @@ public class Toolbar extends LinearLayout implements View.OnClickListener, LoadV private LoadView navigationItemContainer; private ToolbarCallback callback; - private NavigationItem navigationItem; private boolean openKeyboardAfterSearchViewCreated = false; private int lastScrollDeltaOffset; private int scrollOffset; + private NavigationItem fromItem; + private NavigationItem toItem; + public Toolbar(Context context) { super(context); init(); @@ -136,13 +134,13 @@ public class Toolbar extends LinearLayout implements View.OnClickListener, LoadV recyclerView.removeOnScrollListener(recyclerViewOnScrollListener); } - public void updateNavigation() { - closeSearchInternal(); - setNavigationItem(false, false, navigationItem); - } +// public void updateNavigation() { +// closeSearchInternal(); +// setNavigationItem(false, false, toItem); +// } public NavigationItem getNavigationItem() { - return navigationItem; + return toItem; } public boolean openSearch() { @@ -157,6 +155,44 @@ public class Toolbar extends LinearLayout implements View.OnClickListener, LoadV setNavigationItemInternal(animate, pushing, item); } + public void beginTransition(NavigationItem newItem) { + attachNavigationItem(newItem); + + navigationItemContainer.addView(toItem.view, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + } + + public void transitionProgress(float progress, boolean pushing) { + final int offset = dp(16); + + toItem.view.setTranslationY((pushing ? offset : -offset) * (1f - progress)); + toItem.view.setAlpha(progress); + + if (fromItem != null) { + fromItem.view.setTranslationY((pushing ? -offset : offset) * progress); + fromItem.view.setAlpha(1f - progress); + } + + float arrowEnd = toItem.hasBack || toItem.search ? 1f : 0f; + if (arrowMenuDrawable.getProgress() != arrowEnd) { + arrowMenuDrawable.setProgress(toItem.hasBack || toItem.search ? progress : 1f - progress); + } + } + + public void finishTransition(boolean finished) { + if (finished) { + if (fromItem != null) { + removeNavigationItem(fromItem); + } + setArrowMenuProgress(toItem.hasBack || toItem.search ? 1f : 0f); + } else { + removeNavigationItem(toItem); + setArrowMenuProgress(toItem.hasBack || toItem.search ? 1f : 0f); + toItem = fromItem; + } + + fromItem = null; + } + public void setCallback(ToolbarCallback callback) { this.callback = callback; } @@ -178,7 +214,7 @@ public class Toolbar extends LinearLayout implements View.OnClickListener, LoadV @Override public void onLoadViewRemoved(View view) { - // TODO: this is kinda a hack + // Remove the menu from the navigation item if (view instanceof ViewGroup) { ((ViewGroup) view).removeAllViews(); } @@ -227,10 +263,10 @@ public class Toolbar extends LinearLayout implements View.OnClickListener, LoadV } private boolean openSearchInternal() { - if (navigationItem != null && !navigationItem.search) { - navigationItem.search = true; + if (toItem != null && !toItem.search) { + toItem.search = true; openKeyboardAfterSearchViewCreated = true; - setNavigationItemInternal(true, false, navigationItem); + setNavigationItemInternal(true, false, toItem); callback.onSearchVisibilityChanged(true); return true; } else { @@ -239,11 +275,10 @@ public class Toolbar extends LinearLayout implements View.OnClickListener, LoadV } private boolean closeSearchInternal() { - if (navigationItem != null && navigationItem.search) { - navigationItem.search = false; - navigationItem.searchText = null; - setNavigationItemInternal(true, false, navigationItem); - AndroidUtils.hideKeyboard(navigationItemContainer); + if (toItem != null && toItem.search) { + toItem.search = false; + toItem.searchText = null; + setNavigationItemInternal(true, false, toItem); callback.onSearchVisibilityChanged(false); return true; } else { @@ -251,88 +286,61 @@ public class Toolbar extends LinearLayout implements View.OnClickListener, LoadV } } - private void setNavigationItemInternal(boolean animate, boolean pushing, NavigationItem toItem) { - final NavigationItem fromItem = navigationItem; - - boolean same = toItem == navigationItem; - - if (!toItem.search) { - AndroidUtils.hideKeyboard(navigationItemContainer); - } - - if (fromItem != null) { - fromItem.toolbar = null; - } - - toItem.toolbar = this; - - if (!animate) { - if (fromItem != null) { - removeNavigationItem(fromItem); - } - setArrowMenuProgress(toItem.hasBack ? 1f : 0f); - } + private void setNavigationItemInternal(boolean animate, final boolean pushing, NavigationItem newItem) { + attachNavigationItem(newItem); - toItem.view = createNavigationItemView(toItem); - - // use the LoadView animation when from a search - if (same) { + if (fromItem == toItem) { + // Search toggled navigationItemContainer.setView(toItem.view, animate); + + animateArrow(toItem.hasBack || toItem.search); } else { navigationItemContainer.addView(toItem.view, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); - } - final int duration = 300; - final int offset = dp(16); - final int delay = pushing ? 0 : 100; - - if (animate) { - animateArrow(toItem.hasBack || toItem.search, toItem.search ? 0 : delay); - } - - // Use the LoadView animation when from a search - if (animate && !same) { - toItem.view.setAlpha(0f); - - List animations = new ArrayList<>(5); + if (animate) { + toItem.view.setAlpha(0f); - Animator toYAnimation = ObjectAnimator.ofFloat(toItem.view, View.TRANSLATION_Y, pushing ? offset : -offset, 0f); - toYAnimation.setDuration(duration); - toYAnimation.setInterpolator(new DecelerateInterpolator(2f)); - toYAnimation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (fromItem != null) { - removeNavigationItem(fromItem); + ValueAnimator animator = ObjectAnimator.ofFloat(0f, 1f); + animator.setDuration(300); + animator.setInterpolator(new DecelerateInterpolator(2f)); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + finishTransition(true); } + }); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + transitionProgress((float) animation.getAnimatedValue(), pushing); + } + }); + if (!pushing) { + animator.setStartDelay(100); } - }); - animations.add(toYAnimation); + animator.start(); + } else { + arrowMenuDrawable.setProgress(toItem.hasBack || toItem.search ? 1f : 0f); + finishTransition(true); + } + } + } - Animator toAlphaAnimation = ObjectAnimator.ofFloat(toItem.view, View.ALPHA, 0f, 1f); - toAlphaAnimation.setDuration(duration); - toAlphaAnimation.setInterpolator(new DecelerateInterpolator(2f)); - animations.add(toAlphaAnimation); + private void attachNavigationItem(NavigationItem newItem) { + fromItem = toItem; + toItem = newItem; - if (fromItem != null) { - Animator fromYAnimation = ObjectAnimator.ofFloat(fromItem.view, View.TRANSLATION_Y, 0f, pushing ? -offset : offset); - fromYAnimation.setDuration(duration); - fromYAnimation.setInterpolator(new DecelerateInterpolator(2f)); - animations.add(fromYAnimation); - - Animator fromAlphaAnimation = ObjectAnimator.ofFloat(fromItem.view, View.ALPHA, 1f, 0f); - fromAlphaAnimation.setDuration(duration); - fromAlphaAnimation.setInterpolator(new DecelerateInterpolator(2f)); - animations.add(fromAlphaAnimation); - } + if (!toItem.search) { + AndroidUtils.hideKeyboard(navigationItemContainer); + } - AnimatorSet set = new AnimatorSet(); - set.setStartDelay(delay); - set.playTogether(animations); - set.start(); + if (fromItem != null) { + fromItem.toolbar = null; } - navigationItem = toItem; + toItem.toolbar = this; + + toItem.view = createNavigationItemView(toItem); } private void removeNavigationItem(NavigationItem item) { @@ -428,7 +436,7 @@ public class Toolbar extends LinearLayout implements View.OnClickListener, LoadV } } - private void animateArrow(boolean toArrow, long delay) { + private void animateArrow(boolean toArrow) { float to = toArrow ? 1f : 0f; if (to != arrowMenuDrawable.getProgress()) { ValueAnimator arrowAnimation = ValueAnimator.ofFloat(arrowMenuDrawable.getProgress(), to); @@ -440,7 +448,6 @@ public class Toolbar extends LinearLayout implements View.OnClickListener, LoadV setArrowMenuProgress((float) animation.getAnimatedValue()); } }); - arrowAnimation.setStartDelay(delay); arrowAnimation.start(); } } diff --git a/Clover/app/src/main/res/layout/controller_navigation_drawer.xml b/Clover/app/src/main/res/layout/controller_navigation_drawer.xml index 77db5b6d..d8ef7582 100644 --- a/Clover/app/src/main/res/layout/controller_navigation_drawer.xml +++ b/Clover/app/src/main/res/layout/controller_navigation_drawer.xml @@ -21,12 +21,12 @@ along with this program. If not, see . android:layout_width="match_parent" android:layout_height="match_parent"> - - . - + diff --git a/Clover/app/src/main/res/layout/controller_navigation_image_viewer.xml b/Clover/app/src/main/res/layout/controller_navigation_image_viewer.xml index 405e7f13..e757827f 100644 --- a/Clover/app/src/main/res/layout/controller_navigation_image_viewer.xml +++ b/Clover/app/src/main/res/layout/controller_navigation_image_viewer.xml @@ -28,7 +28,7 @@ along with this program. If not, see . android:background="#87000000" tools:ignore="UnusedAttribute" /> - diff --git a/Clover/app/src/main/res/layout/controller_navigation_toolbar.xml b/Clover/app/src/main/res/layout/controller_navigation_toolbar.xml index 24a4cca4..e08944dc 100644 --- a/Clover/app/src/main/res/layout/controller_navigation_toolbar.xml +++ b/Clover/app/src/main/res/layout/controller_navigation_toolbar.xml @@ -20,7 +20,7 @@ along with this program. If not, see . android:layout_height="match_parent" android:background="?backcolor"> - diff --git a/Clover/app/src/main/res/layout/layout_controller_popup.xml b/Clover/app/src/main/res/layout/layout_controller_popup.xml index 708d4e71..6bab3ae7 100644 --- a/Clover/app/src/main/res/layout/layout_controller_popup.xml +++ b/Clover/app/src/main/res/layout/layout_controller_popup.xml @@ -15,17 +15,17 @@ 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 . --> - - - +