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.
multisite
Floens 10 years ago
parent 81adc3cb0d
commit 444277ddbd
  1. 132
      Clover/app/src/main/java/org/floens/chan/controller/Controller.java
  2. 99
      Clover/app/src/main/java/org/floens/chan/controller/ControllerLogic.java
  3. 4
      Clover/app/src/main/java/org/floens/chan/controller/ControllerTransition.java
  4. 138
      Clover/app/src/main/java/org/floens/chan/controller/NavigationController.java
  5. 4
      Clover/app/src/main/java/org/floens/chan/controller/transition/FadeInTransition.java
  6. 4
      Clover/app/src/main/java/org/floens/chan/controller/transition/FadeOutTransition.java
  7. 4
      Clover/app/src/main/java/org/floens/chan/controller/transition/PopControllerTransition.java
  8. 3
      Clover/app/src/main/java/org/floens/chan/controller/transition/PushControllerTransition.java
  9. 310
      Clover/app/src/main/java/org/floens/chan/controller/ui/NavigationControllerContainerLayout.java
  10. 8
      Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java
  11. 7
      Clover/app/src/main/java/org/floens/chan/ui/cell/PostCell.java
  12. 9
      Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java
  13. 86
      Clover/app/src/main/java/org/floens/chan/ui/controller/DrawerController.java
  14. 14
      Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerNavigationController.java
  15. 27
      Clover/app/src/main/java/org/floens/chan/ui/controller/PopupController.java
  16. 73
      Clover/app/src/main/java/org/floens/chan/ui/controller/SplitNavigationController.java
  17. 13
      Clover/app/src/main/java/org/floens/chan/ui/controller/StyledToolbarNavigationController.java
  18. 1
      Clover/app/src/main/java/org/floens/chan/ui/controller/ThemeSettingsController.java
  19. 36
      Clover/app/src/main/java/org/floens/chan/ui/controller/ToolbarNavigationController.java
  20. 6
      Clover/app/src/main/java/org/floens/chan/ui/controller/ViewThreadController.java
  21. 18
      Clover/app/src/main/java/org/floens/chan/ui/layout/PopupControllerContainer.java
  22. 1
      Clover/app/src/main/java/org/floens/chan/ui/toolbar/NavigationItem.java
  23. 187
      Clover/app/src/main/java/org/floens/chan/ui/toolbar/Toolbar.java
  24. 6
      Clover/app/src/main/res/layout/controller_navigation_drawer.xml
  25. 2
      Clover/app/src/main/res/layout/controller_navigation_image_viewer.xml
  26. 2
      Clover/app/src/main/res/layout/controller_navigation_toolbar.xml
  27. 8
      Clover/app/src/main/res/layout/layout_controller_popup.xml

@ -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<Controller> 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) {

@ -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 <http://www.gnu.org/licenses/>.
*/
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();
}
}
}
}

@ -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) {

@ -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<Controller> 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<Controller> 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));
}
}

@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.floens.chan.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() {

@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.floens.chan.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() {

@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.floens.chan.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;

@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.floens.chan.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 {

@ -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 <http://www.gnu.org/licenses/>.
*/
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;
}
}
}

@ -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<Controller> controllers = mainNavigationController.getControllerList();
List<Controller> controllers = mainNavigationController.childControllers;
for (Controller controller : controllers) {
if (controller instanceof ViewThreadController) {
thread = ((ViewThreadController) controller).getLoadable();

@ -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);

@ -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();

@ -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;
}
}

@ -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<PostImage> 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);
}

@ -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();
}
}
}

@ -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;
}

@ -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;
}

@ -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();

@ -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);
}
}

@ -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);
}
}
}

@ -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);

@ -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;

@ -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<Animator> 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();
}
}

@ -21,12 +21,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
<org.floens.chan.ui.view.TouchBlockingFrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<LinearLayout
<org.floens.chan.ui.view.TouchBlockingLinearLayout
android:id="@+id/drawer"
android:layout_width="200dp"
android:layout_height="match_parent"
@ -87,6 +87,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</LinearLayout>
</LinearLayout>
</org.floens.chan.ui.view.TouchBlockingLinearLayout>
</android.support.v4.widget.DrawerLayout>

@ -28,7 +28,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
android:background="#87000000"
tools:ignore="UnusedAttribute" />
<FrameLayout
<org.floens.chan.controller.ui.NavigationControllerContainerLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />

@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
android:layout_height="match_parent"
android:background="?backcolor">
<FrameLayout
<org.floens.chan.controller.ui.NavigationControllerContainerLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />

@ -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 <http://www.gnu.org/licenses/>.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
<org.floens.chan.ui.layout.PopupControllerContainer xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/top_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#88000000">
<org.floens.chan.ui.layout.PopupControllerContainer
<FrameLayout
android:id="@+id/container"
android:layout_width="600dp"
android:layout_height="wrap_content"
android:layout_height="600dp"
android:layout_gravity="center"
android:background="@drawable/dialog_full_light" />
</FrameLayout>
</org.floens.chan.ui.layout.PopupControllerContainer>

Loading…
Cancel
Save