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 . --> - - - +