Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dc68990bff | |||
| e4f7e9e175 | |||
| 4ab99b68da | |||
| b8bd64e078 | |||
| 812d1f8911 | |||
| ac8288fece | |||
| d2dd786b72 | |||
| 7cf30b820c | |||
| 1120896438 | |||
| 093238cc52 | |||
| c153e29273 | |||
| 2757c7a4b6 | |||
| 46091a7c99 | |||
| b0a5da2b82 | |||
| 0026566ba0 | |||
| 9f640380bf | |||
| a3faaede61 | |||
| 94d8add220 | |||
| 6d4b5a5ef6 | |||
| ae27c5e453 | |||
| e4d23a7c74 | |||
| 778cdcfd58 | |||
| 5e730947aa | |||
| 71b10c7365 | |||
| 8c323b9613 | |||
| af45aae110 | |||
| d4c7e5791e | |||
| dc4c3a9709 | |||
| ef84fbd547 | |||
| 431569763e | |||
| 7b5bab3681 |
Regular → Executable
+1
-5
@@ -14,14 +14,10 @@ jdk:
|
||||
- oraclejdk8
|
||||
|
||||
branches:
|
||||
only
|
||||
- v2
|
||||
only:
|
||||
- develop
|
||||
- master
|
||||
|
||||
before_install:
|
||||
- chmod +x .buildscript/deploy_snapshot.sh
|
||||
|
||||
after_success:
|
||||
- .buildscript/deploy_snapshot.sh
|
||||
|
||||
|
||||
@@ -20,22 +20,33 @@ Conductor is architecture-agnostic and does not try to force any design decision
|
||||
## Installation
|
||||
|
||||
```gradle
|
||||
compile 'com.bluelinelabs:conductor:2.0.0'
|
||||
compile 'com.bluelinelabs:conductor:2.0.2'
|
||||
|
||||
// If you want the components that go along with
|
||||
// Android's support libraries (currently just a PagerAdapter):
|
||||
compile 'com.bluelinelabs:conductor-support:2.0.0'
|
||||
compile 'com.bluelinelabs:conductor-support:2.0.2'
|
||||
|
||||
// If you want RxJava/RxAndroid lifecycle support:
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:2.0.0'
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:2.0.2'
|
||||
```
|
||||
|
||||
SNAPSHOT:
|
||||
|
||||
```gradle
|
||||
compile 'com.bluelinelabs:conductor:2.0.1-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor-support:2.0.1-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:2.0.1-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor:2.0.3-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor-support:2.0.3-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:2.0.3-SNAPSHOT'
|
||||
```
|
||||
|
||||
You also have to add the url to the snapshot repository:
|
||||
|
||||
```gradle
|
||||
allprojects {
|
||||
repositories {
|
||||
...
|
||||
|
||||
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
|
||||
}
|
||||
```
|
||||
|
||||
## Components to Know
|
||||
@@ -114,7 +125,7 @@ The lifecycle of a Controller is significantly simpler to understand than that o
|
||||
`ControllerChangeHandler` can be subclassed in order to perform different functions when changing between two `Controllers`. Two convenience `ControllerChangeHandler` subclasses are included to cover most basic needs: `AnimatorChangeHandler`, which will use an `Animator` object to transition between two views, and `TransitionChangeHandler`, which will use Lollipop's `Transition` framework for transitioning between views.
|
||||
|
||||
### Child Routers & Controllers
|
||||
`getChildController` can be called on a `Controller` in order to get a nested `Router` into which child `Controller`s can be pushed. This enables creating advanced layouts, such as Master/Detail.
|
||||
`getChildRouter` can be called on a `Controller` in order to get a nested `Router` into which child `Controller`s can be pushed. This enables creating advanced layouts, such as Master/Detail.
|
||||
|
||||
### RxJava Lifecycle
|
||||
If the RxLifecycle dependency has been added, there is an `RxController` available that can be used along with the standard [RxLifecycle library](https://github.com/trello/RxLifecycle). There is also a `ControllerLifecycleProvider` available if you do not wish to use this subclass.
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.1.2'
|
||||
classpath 'com.android.tools.build:gradle:2.2.0'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -92,4 +92,9 @@ public class ActivityHostedRouter extends Router {
|
||||
List<Router> getSiblingRouters() {
|
||||
return lifecycleHandler.getRouters();
|
||||
}
|
||||
|
||||
@Override
|
||||
Router getRootRouter() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.bluelinelabs.conductor.ControllerChangeHandler.ControllerChangeListen
|
||||
import com.bluelinelabs.conductor.internal.ClassUtils;
|
||||
import com.bluelinelabs.conductor.internal.RouterRequiringFunc;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
@@ -54,6 +55,7 @@ public abstract class Controller {
|
||||
private static final String KEY_OVERRIDDEN_POP_HANDLER = "Controller.overriddenPopHandler";
|
||||
private static final String KEY_VIEW_STATE_HIERARCHY = "Controller.viewState.hierarchy";
|
||||
private static final String KEY_VIEW_STATE_BUNDLE = "Controller.viewState.bundle";
|
||||
private static final String KEY_RETAIN_VIEW_MODE = "Controller.retainViewMode";
|
||||
|
||||
private final Bundle args;
|
||||
|
||||
@@ -83,6 +85,7 @@ public abstract class Controller {
|
||||
private final ArrayList<String> requestedPermissions = new ArrayList<>();
|
||||
private final ArrayList<RouterRequiringFunc> onRouterSetListeners = new ArrayList<>();
|
||||
private final Deque<Controller> childBackstack = new ArrayDeque<>();
|
||||
private WeakReference<View> destroyedView;
|
||||
|
||||
private final ControllerChangeListener childRouterChangeListener = new ControllerChangeListener() {
|
||||
@Override
|
||||
@@ -312,7 +315,10 @@ public abstract class Controller {
|
||||
* @return This Controller's target
|
||||
*/
|
||||
public final Controller getTargetController() {
|
||||
return targetInstanceId != null ? router.getControllerWithInstanceId(targetInstanceId) : null;
|
||||
if (targetInstanceId != null) {
|
||||
return router.getRootRouter().getControllerWithInstanceId(targetInstanceId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -692,6 +698,8 @@ public abstract class Controller {
|
||||
final void activityResumed(Activity activity) {
|
||||
if (!attached && view != null && viewIsAttached) {
|
||||
attach(view);
|
||||
} else if (attached) {
|
||||
needsAttach = false;
|
||||
}
|
||||
|
||||
onActivityResumed(activity);
|
||||
@@ -734,7 +742,7 @@ public abstract class Controller {
|
||||
}
|
||||
}
|
||||
|
||||
private void detach(@NonNull View view, boolean forceViewRefRemoval) {
|
||||
void detach(@NonNull View view, boolean forceViewRefRemoval) {
|
||||
for (ControllerHostedRouter router : childRouters) {
|
||||
router.prepareForHostDetach();
|
||||
}
|
||||
@@ -777,6 +785,10 @@ public abstract class Controller {
|
||||
|
||||
view.removeOnAttachStateChangeListener(onAttachStateChangeListener);
|
||||
viewIsAttached = false;
|
||||
|
||||
if (isBeingDestroyed) {
|
||||
destroyedView = new WeakReference<>(view);
|
||||
}
|
||||
view = null;
|
||||
|
||||
for (LifecycleListener lifecycleListener : lifecycleListeners) {
|
||||
@@ -839,7 +851,7 @@ public abstract class Controller {
|
||||
return view;
|
||||
}
|
||||
|
||||
final void performDestroy() {
|
||||
private void performDestroy() {
|
||||
if (!destroyed) {
|
||||
for (LifecycleListener lifecycleListener : lifecycleListeners) {
|
||||
lifecycleListener.preDestroy(this);
|
||||
@@ -847,10 +859,6 @@ public abstract class Controller {
|
||||
|
||||
destroyed = true;
|
||||
|
||||
if (router != null) {
|
||||
router.unregisterForActivityResults(instanceId);
|
||||
}
|
||||
|
||||
onDestroy();
|
||||
|
||||
parentController = null;
|
||||
@@ -865,9 +873,13 @@ public abstract class Controller {
|
||||
destroy(false);
|
||||
}
|
||||
|
||||
final void destroy(boolean removeViews) {
|
||||
private void destroy(boolean removeViews) {
|
||||
isBeingDestroyed = true;
|
||||
|
||||
if (router != null) {
|
||||
router.unregisterForActivityResults(instanceId);
|
||||
}
|
||||
|
||||
for (ControllerHostedRouter childRouter : childRouters) {
|
||||
childRouter.destroy();
|
||||
}
|
||||
@@ -879,7 +891,7 @@ public abstract class Controller {
|
||||
}
|
||||
}
|
||||
|
||||
final void saveViewState(@NonNull View view) {
|
||||
private void saveViewState(@NonNull View view) {
|
||||
hasSavedViewState = true;
|
||||
|
||||
viewState = new Bundle();
|
||||
@@ -897,7 +909,7 @@ public abstract class Controller {
|
||||
}
|
||||
}
|
||||
|
||||
final void restoreViewState(@NonNull View view) {
|
||||
private void restoreViewState(@NonNull View view) {
|
||||
if (viewState != null) {
|
||||
view.restoreHierarchyState(viewState.getSparseParcelableArray(KEY_VIEW_STATE_HIERARCHY));
|
||||
onRestoreViewState(view, viewState.getBundle(KEY_VIEW_STATE_BUNDLE));
|
||||
@@ -908,6 +920,7 @@ public abstract class Controller {
|
||||
|
||||
if (containerView != null && containerView instanceof ViewGroup) {
|
||||
childRouter.setHost(this, (ViewGroup)containerView);
|
||||
monitorChildRouter(childRouter);
|
||||
childRouter.rebindIfNeeded();
|
||||
}
|
||||
}
|
||||
@@ -932,6 +945,7 @@ public abstract class Controller {
|
||||
outState.putString(KEY_TARGET_INSTANCE_ID, targetInstanceId);
|
||||
outState.putStringArrayList(KEY_REQUESTED_PERMISSIONS, requestedPermissions);
|
||||
outState.putBoolean(KEY_NEEDS_ATTACH, needsAttach || attached);
|
||||
outState.putInt(KEY_RETAIN_VIEW_MODE, retainViewMode.ordinal());
|
||||
|
||||
if (overriddenPushHandler != null) {
|
||||
outState.putBundle(KEY_OVERRIDDEN_PUSH_HANDLER, overriddenPushHandler.toBundle());
|
||||
@@ -968,6 +982,7 @@ public abstract class Controller {
|
||||
overriddenPushHandler = ControllerChangeHandler.fromBundle(savedInstanceState.getBundle(KEY_OVERRIDDEN_PUSH_HANDLER));
|
||||
overriddenPopHandler = ControllerChangeHandler.fromBundle(savedInstanceState.getBundle(KEY_OVERRIDDEN_POP_HANDLER));
|
||||
needsAttach = savedInstanceState.getBoolean(KEY_NEEDS_ATTACH);
|
||||
retainViewMode = RetainViewMode.values()[savedInstanceState.getInt(KEY_RETAIN_VIEW_MODE, 0)];
|
||||
|
||||
List<Bundle> childBundles = savedInstanceState.getParcelableArrayList(KEY_CHILD_ROUTERS);
|
||||
for (Bundle childBundle : childBundles) {
|
||||
@@ -1019,6 +1034,13 @@ public abstract class Controller {
|
||||
for (LifecycleListener lifecycleListener : lifecycleListeners) {
|
||||
lifecycleListener.onChangeEnd(this, changeHandler, changeType);
|
||||
}
|
||||
|
||||
if (isBeingDestroyed && !viewIsAttached && !attached && destroyedView != null) {
|
||||
View view = destroyedView.get();
|
||||
if (router.container != null && view.getParent() == router.container) {
|
||||
router.container.removeView(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final void setDetachFrozen(boolean frozen) {
|
||||
@@ -1036,28 +1058,19 @@ public abstract class Controller {
|
||||
}
|
||||
|
||||
final void createOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
if (attached) {
|
||||
if (hasOptionsMenu && !optionsMenuHidden) {
|
||||
onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
if (attached && hasOptionsMenu && !optionsMenuHidden) {
|
||||
onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
}
|
||||
|
||||
final void prepareOptionsMenu(Menu menu) {
|
||||
if (attached) {
|
||||
if (hasOptionsMenu && !optionsMenuHidden) {
|
||||
onPrepareOptionsMenu(menu);
|
||||
}
|
||||
if (attached && hasOptionsMenu && !optionsMenuHidden) {
|
||||
onPrepareOptionsMenu(menu);
|
||||
}
|
||||
}
|
||||
|
||||
final boolean optionsItemSelected(MenuItem item) {
|
||||
if (attached) {
|
||||
if (hasOptionsMenu && !optionsMenuHidden && onOptionsItemSelected(item)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return attached && hasOptionsMenu && !optionsMenuHidden && onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void monitorChildRouter(ControllerHostedRouter childRouter) {
|
||||
@@ -1065,13 +1078,15 @@ public abstract class Controller {
|
||||
}
|
||||
|
||||
private void onChildControllerPushed(Controller controller) {
|
||||
childBackstack.add(controller);
|
||||
controller.addLifecycleListener(new LifecycleListener() {
|
||||
@Override
|
||||
public void postDestroy(@NonNull Controller controller) {
|
||||
childBackstack.remove(controller);
|
||||
}
|
||||
});
|
||||
if (!childBackstack.contains(controller)) {
|
||||
childBackstack.add(controller);
|
||||
controller.addLifecycleListener(new LifecycleListener() {
|
||||
@Override
|
||||
public void postDestroy(@NonNull Controller controller) {
|
||||
childBackstack.remove(controller);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
final void setParentController(Controller controller) {
|
||||
@@ -1137,6 +1152,7 @@ public abstract class Controller {
|
||||
|
||||
public void onSaveViewState(@NonNull Controller controller, @NonNull Bundle outState) { }
|
||||
public void onRestoreViewState(@NonNull Controller controller, @NonNull Bundle savedViewState) { }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,10 +28,10 @@ public abstract class ControllerChangeHandler {
|
||||
/**
|
||||
* Responsible for swapping Views from one Controller to another.
|
||||
*
|
||||
* @param container The container these Views are hosted in.
|
||||
* @param from The previous View in the container, if any.
|
||||
* @param to The next View that should be put in the container, if any.
|
||||
* @param isPush True if this is a push transaction, false if it's a pop.
|
||||
* @param container The container these Views are hosted in.
|
||||
* @param from The previous View in the container, if any.
|
||||
* @param to The next View that should be put in the container, if any.
|
||||
* @param isPush True if this is a push transaction, false if it's a pop.
|
||||
* @param changeListener This listener must be called when any transitions or animations are completed.
|
||||
*/
|
||||
public abstract void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener);
|
||||
@@ -59,10 +59,16 @@ public abstract class ControllerChangeHandler {
|
||||
* popped before it has completed.
|
||||
*
|
||||
* @param newHandler the change handler that has caused this push to be aborted
|
||||
* @param newTop the controller that will now be at the top of the backstack
|
||||
* @param newTop the controller that will now be at the top of the backstack
|
||||
*/
|
||||
public void onAbortPush(@NonNull ControllerChangeHandler newHandler, Controller newTop) { }
|
||||
|
||||
/**
|
||||
* Will be called on change handlers that push a controller if the controller being pushed is
|
||||
* needs to be attached immediately, without any animations or transitions.
|
||||
*/
|
||||
public void completeImmediately() { }
|
||||
|
||||
final Bundle toBundle() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(KEY_CLASS_NAME, getClass().getName());
|
||||
@@ -94,6 +100,21 @@ public abstract class ControllerChangeHandler {
|
||||
}
|
||||
}
|
||||
|
||||
public static void completePushImmediately(String controllerInstanceId) {
|
||||
ControllerChangeHandler changeHandler = inProgressPushHandlers.get(controllerInstanceId);
|
||||
if (changeHandler != null) {
|
||||
changeHandler.completeImmediately();
|
||||
}
|
||||
}
|
||||
|
||||
public static void abortPush(Controller toAbort, Controller newController, ControllerChangeHandler newChangeHandler) {
|
||||
ControllerChangeHandler handlerForPush = inProgressPushHandlers.get(toAbort.getInstanceId());
|
||||
if (handlerForPush != null) {
|
||||
handlerForPush.onAbortPush(newChangeHandler, newController);
|
||||
inProgressPushHandlers.remove(toAbort.getInstanceId());
|
||||
}
|
||||
}
|
||||
|
||||
public static void executeChange(final Controller to, final Controller from, boolean isPush, ViewGroup container, ControllerChangeHandler inHandler) {
|
||||
executeChange(to, from, isPush, container, inHandler, new ArrayList<ControllerChangeListener>());
|
||||
}
|
||||
@@ -102,14 +123,10 @@ public abstract class ControllerChangeHandler {
|
||||
if (container != null) {
|
||||
final ControllerChangeHandler handler = inHandler != null ? inHandler : new SimpleSwapChangeHandler();
|
||||
|
||||
if (isPush) {
|
||||
if (isPush && to != null) {
|
||||
inProgressPushHandlers.put(to.getInstanceId(), handler);
|
||||
} else if (from != null) {
|
||||
ControllerChangeHandler handlerForPush = inProgressPushHandlers.get(from.getInstanceId());
|
||||
if (handlerForPush != null) {
|
||||
handlerForPush.onAbortPush(handler, to);
|
||||
inProgressPushHandlers.remove(from.getInstanceId());
|
||||
}
|
||||
} else if (!isPush && from != null) {
|
||||
abortPush(from, to, handler);
|
||||
}
|
||||
|
||||
for (ControllerChangeListener listener : listeners) {
|
||||
@@ -138,7 +155,6 @@ public abstract class ControllerChangeHandler {
|
||||
handler.performChange(container, fromView, toView, isPush, new ControllerChangeCompletedListener() {
|
||||
@Override
|
||||
public void onChangeCompleted() {
|
||||
|
||||
if (from != null) {
|
||||
from.changeEnded(handler, fromChangeType);
|
||||
}
|
||||
@@ -156,6 +172,10 @@ public abstract class ControllerChangeHandler {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean removesFromViewOnPush() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener interface useful for allowing external classes to be notified of change events.
|
||||
*/
|
||||
@@ -163,21 +183,22 @@ public abstract class ControllerChangeHandler {
|
||||
/**
|
||||
* Called when a {@link ControllerChangeHandler} has started changing {@link Controller}s
|
||||
*
|
||||
* @param to The new Controller
|
||||
* @param from The old Controller
|
||||
* @param isPush True if this is a push operation, or false if it's a pop.
|
||||
* @param to The new Controller
|
||||
* @param from The old Controller
|
||||
* @param isPush True if this is a push operation, or false if it's a pop.
|
||||
* @param container The containing ViewGroup
|
||||
* @param handler The change handler being used.
|
||||
* @param handler The change handler being used.
|
||||
*/
|
||||
void onChangeStarted(Controller to, Controller from, boolean isPush, ViewGroup container, ControllerChangeHandler handler);
|
||||
|
||||
/**
|
||||
* Called when a {@link ControllerChangeHandler} has completed changing {@link Controller}s
|
||||
* @param to The new Controller
|
||||
* @param from The old Controller
|
||||
* @param isPush True if this was a push operation, or false if it's a pop.
|
||||
*
|
||||
* @param to The new Controller
|
||||
* @param from The old Controller
|
||||
* @param isPush True if this was a push operation, or false if it's a pop.
|
||||
* @param container The containing ViewGroup
|
||||
* @param handler The change handler that was used.
|
||||
* @param handler The change handler that was used.
|
||||
*/
|
||||
void onChangeCompleted(Controller to, Controller from, boolean isPush, ViewGroup container, ControllerChangeHandler handler);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import com.bluelinelabs.conductor.ControllerChangeHandler.ControllerChangeListen
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ControllerHostedRouter extends Router {
|
||||
class ControllerHostedRouter extends Router {
|
||||
|
||||
private final String KEY_HOST_ID = "ControllerHostedRouter.hostId";
|
||||
private final String KEY_TAG = "ControllerHostedRouter.tag";
|
||||
@@ -22,14 +22,14 @@ public class ControllerHostedRouter extends Router {
|
||||
@IdRes private int hostId;
|
||||
private String tag;
|
||||
|
||||
public ControllerHostedRouter() { }
|
||||
ControllerHostedRouter() { }
|
||||
|
||||
public ControllerHostedRouter(int hostId, String tag) {
|
||||
ControllerHostedRouter(int hostId, String tag) {
|
||||
this.hostId = hostId;
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
public final void setHost(@NonNull Controller controller, @NonNull ViewGroup container) {
|
||||
final void setHost(@NonNull Controller controller, @NonNull ViewGroup container) {
|
||||
if (hostController != controller || this.container != container) {
|
||||
removeHost();
|
||||
|
||||
@@ -42,22 +42,39 @@ public class ControllerHostedRouter extends Router {
|
||||
}
|
||||
}
|
||||
|
||||
public final void removeHost() {
|
||||
final void removeHost() {
|
||||
if (container != null && container instanceof ControllerChangeListener) {
|
||||
removeChangeListener((ControllerChangeListener)container);
|
||||
}
|
||||
|
||||
for (Controller controller : destroyingControllers) {
|
||||
if (controller.getView() != null) {
|
||||
controller.detach(controller.getView(), true);
|
||||
}
|
||||
}
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
if (transaction.controller.getView() != null) {
|
||||
transaction.controller.detach(transaction.controller.getView(), true);
|
||||
}
|
||||
}
|
||||
|
||||
prepareForContainerRemoval();
|
||||
hostController = null;
|
||||
container = null;
|
||||
}
|
||||
|
||||
public final void setDetachFrozen(boolean frozen) {
|
||||
for (RouterTransaction transaction : backStack) {
|
||||
final void setDetachFrozen(boolean frozen) {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
transaction.controller.setDetachFrozen(frozen);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void destroy() {
|
||||
setDetachFrozen(false);
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Activity getActivity() {
|
||||
return hostController != null ? hostController.getActivity() : null;
|
||||
@@ -153,11 +170,11 @@ public class ControllerHostedRouter extends Router {
|
||||
controller.setParentController(hostController);
|
||||
}
|
||||
|
||||
public int getHostId() {
|
||||
int getHostId() {
|
||||
return hostId;
|
||||
}
|
||||
|
||||
public String getTag() {
|
||||
String getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
@@ -168,4 +185,13 @@ public class ControllerHostedRouter extends Router {
|
||||
list.addAll(hostController.getRouter().getSiblingRouters());
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
Router getRootRouter() {
|
||||
if (hostController != null && hostController.getRouter() != null) {
|
||||
return hostController.getRouter().getRootRouter();
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import com.bluelinelabs.conductor.changehandler.SimpleSwapChangeHandler;
|
||||
import com.bluelinelabs.conductor.internal.NoOpControllerChangeHandler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
@@ -29,9 +30,9 @@ public abstract class Router {
|
||||
private static final String KEY_BACKSTACK = "Router.backstack";
|
||||
private static final String KEY_POPS_LAST_VIEW = "Router.popsLastView";
|
||||
|
||||
protected final Backstack backStack = new Backstack();
|
||||
protected final Backstack backstack = new Backstack();
|
||||
private final List<ControllerChangeListener> changeListeners = new ArrayList<>();
|
||||
private final List<Controller> destroyingControllers = new ArrayList<>();
|
||||
final List<Controller> destroyingControllers = new ArrayList<>();
|
||||
|
||||
private boolean popsLastView = false;
|
||||
|
||||
@@ -71,10 +72,12 @@ public abstract class Router {
|
||||
/**
|
||||
* This should be called by the host Activity when its onBackPressed method is called. The call will be forwarded
|
||||
* to its top {@link Controller}. If that controller doesn't handle it, then it will be popped.
|
||||
*
|
||||
* @return Whether or not a back action was handled by the Router
|
||||
*/
|
||||
public boolean handleBack() {
|
||||
if (!backStack.isEmpty()) {
|
||||
if (backStack.peek().controller.handleBack()) {
|
||||
if (!backstack.isEmpty()) {
|
||||
if (backstack.peek().controller.handleBack()) {
|
||||
return true;
|
||||
} else if (popCurrentController()) {
|
||||
return true;
|
||||
@@ -90,7 +93,7 @@ public abstract class Router {
|
||||
* @return Whether or not this Router still has controllers remaining on it after popping.
|
||||
*/
|
||||
public boolean popCurrentController() {
|
||||
return popController(backStack.peek().controller);
|
||||
return popController(backstack.peek().controller);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,28 +103,28 @@ public abstract class Router {
|
||||
* @return Whether or not this Router still has controllers remaining on it after popping.
|
||||
*/
|
||||
public boolean popController(Controller controller) {
|
||||
RouterTransaction topController = backStack.peek();
|
||||
RouterTransaction topController = backstack.peek();
|
||||
boolean poppingTopController = topController != null && topController.controller == controller;
|
||||
|
||||
if (poppingTopController) {
|
||||
trackDestroyingController(backStack.pop());
|
||||
trackDestroyingController(backstack.pop());
|
||||
} else {
|
||||
for (RouterTransaction transaction : backStack) {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
if (transaction.controller == controller) {
|
||||
backStack.remove(transaction);
|
||||
backstack.remove(transaction);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (poppingTopController) {
|
||||
performControllerChange(backStack.peek(), topController, false);
|
||||
performControllerChange(backstack.peek(), topController, false);
|
||||
}
|
||||
|
||||
if (popsLastView) {
|
||||
return topController != null;
|
||||
} else {
|
||||
return !backStack.isEmpty();
|
||||
return !backstack.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +135,7 @@ public abstract class Router {
|
||||
* and its push and pop {@link ControllerChangeHandler}, and its tag.
|
||||
*/
|
||||
public void pushController(@NonNull RouterTransaction transaction) {
|
||||
RouterTransaction from = backStack.peek();
|
||||
RouterTransaction from = backstack.peek();
|
||||
pushToBackstack(transaction);
|
||||
performControllerChange(transaction, from, true);
|
||||
}
|
||||
@@ -144,9 +147,9 @@ public abstract class Router {
|
||||
* and its push and pop {@link ControllerChangeHandler}, and its tag.
|
||||
*/
|
||||
public void replaceTopController(@NonNull RouterTransaction transaction) {
|
||||
RouterTransaction topTransaction = backStack.peek();
|
||||
if (!backStack.isEmpty()) {
|
||||
trackDestroyingController(backStack.pop());
|
||||
RouterTransaction topTransaction = backstack.peek();
|
||||
if (!backstack.isEmpty()) {
|
||||
trackDestroyingController(backstack.pop());
|
||||
}
|
||||
|
||||
pushToBackstack(transaction);
|
||||
@@ -155,7 +158,7 @@ public abstract class Router {
|
||||
|
||||
void destroy() {
|
||||
popsLastView = true;
|
||||
List<RouterTransaction> poppedControllers = backStack.popAll();
|
||||
List<RouterTransaction> poppedControllers = backstack.popAll();
|
||||
|
||||
if (poppedControllers.size() > 0) {
|
||||
trackDestroyingControllers(poppedControllers);
|
||||
@@ -164,9 +167,13 @@ public abstract class Router {
|
||||
}
|
||||
}
|
||||
|
||||
public int getContainerId() {
|
||||
return container != null ? container.getId() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to true, this router will handle back presses by performing a change handler on the last controller and view
|
||||
* in the stack. This defaults to false so that the developer can either finish its containing Activity or otherwise
|
||||
* in the stack. This defaults to false so that the developer can either finish its containing Activity or otherwise
|
||||
* hide its parent view without any strange artifacting.
|
||||
*/
|
||||
public Router setPopsLastView(boolean popsLastView) {
|
||||
@@ -190,8 +197,8 @@ public abstract class Router {
|
||||
* @return Whether or not any {@link Controller}s were popped in order to get to the root transaction
|
||||
*/
|
||||
public boolean popToRoot(ControllerChangeHandler changeHandler) {
|
||||
if (backStack.size() > 1) {
|
||||
popToTransaction(backStack.root(), changeHandler);
|
||||
if (backstack.size() > 1) {
|
||||
popToTransaction(backstack.root(), changeHandler);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -216,7 +223,7 @@ public abstract class Router {
|
||||
* @return Whether or not the {@link Controller} with the passed tag is now at the top
|
||||
*/
|
||||
public boolean popToTag(@NonNull String tag, ControllerChangeHandler changeHandler) {
|
||||
for (RouterTransaction transaction : backStack) {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
if (tag.equals(transaction.tag())) {
|
||||
popToTransaction(transaction, changeHandler);
|
||||
return true;
|
||||
@@ -232,13 +239,29 @@ public abstract class Router {
|
||||
* and its push and pop {@link ControllerChangeHandler}, and its tag.
|
||||
*/
|
||||
public void setRoot(@NonNull RouterTransaction transaction) {
|
||||
RouterTransaction currentTop = backStack.peek();
|
||||
removeAllExceptTopAndUnowned();
|
||||
ControllerChangeHandler newHandler = transaction.pushChangeHandler() != null ? transaction.pushChangeHandler() : new SimpleSwapChangeHandler();
|
||||
|
||||
trackDestroyingControllers(backStack.popAll());
|
||||
List<RouterTransaction> visibleTransactions = getVisibleTransactions(backstack.iterator());
|
||||
RouterTransaction rootTransaction = visibleTransactions.size() > 0 ? visibleTransactions.get(0) : null;
|
||||
|
||||
removeAllExceptVisibleAndUnowned();
|
||||
|
||||
trackDestroyingControllers(backstack.popAll());
|
||||
|
||||
pushToBackstack(transaction);
|
||||
performControllerChange(transaction, currentTop, true);
|
||||
|
||||
for (int i = visibleTransactions.size() - 1; i > 0; i--) {
|
||||
if (visibleTransactions.get(i).controller.getView() == null) {
|
||||
ControllerChangeHandler.abortPush(visibleTransactions.get(i).controller, transaction.controller, newHandler);
|
||||
} else {
|
||||
performControllerChange(null, visibleTransactions.get(i).controller, true, newHandler);
|
||||
}
|
||||
}
|
||||
|
||||
if (rootTransaction != null && rootTransaction.controller.getView() == null) {
|
||||
ControllerChangeHandler.abortPush(rootTransaction.controller, transaction.controller, newHandler);
|
||||
}
|
||||
performControllerChange(transaction, rootTransaction, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -248,7 +271,7 @@ public abstract class Router {
|
||||
* @return The matching Controller, if one exists
|
||||
*/
|
||||
public Controller getControllerWithInstanceId(String instanceId) {
|
||||
for (RouterTransaction transaction : backStack) {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
Controller controllerWithId = transaction.controller.findController(instanceId);
|
||||
if (controllerWithId != null) {
|
||||
return controllerWithId;
|
||||
@@ -264,7 +287,7 @@ public abstract class Router {
|
||||
* @return The matching Controller, if one exists
|
||||
*/
|
||||
public Controller getControllerWithTag(String tag) {
|
||||
for (RouterTransaction transaction : backStack) {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
if (tag.equals(transaction.tag())) {
|
||||
return transaction.controller;
|
||||
}
|
||||
@@ -276,7 +299,7 @@ public abstract class Router {
|
||||
* Returns the number of {@link Controller}s currently in the backstack
|
||||
*/
|
||||
public int getBackstackSize() {
|
||||
return backStack.size();
|
||||
return backstack.size();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -284,7 +307,7 @@ public abstract class Router {
|
||||
*/
|
||||
public List<RouterTransaction> getBackstack() {
|
||||
List<RouterTransaction> list = new ArrayList<>();
|
||||
Iterator<RouterTransaction> backstackIterator = backStack.reverseIterator();
|
||||
Iterator<RouterTransaction> backstackIterator = backstack.reverseIterator();
|
||||
while (backstackIterator.hasNext()) {
|
||||
list.add(backstackIterator.next());
|
||||
}
|
||||
@@ -295,20 +318,45 @@ public abstract class Router {
|
||||
* Sets the backstack, transitioning from the current top controller to the top of the new stack (if different)
|
||||
* using the passed {@link ControllerChangeHandler}
|
||||
*
|
||||
* @param backstack The new backstack
|
||||
* @param newBackstack The new backstack
|
||||
* @param changeHandler An optional change handler to be used to handle the transition
|
||||
*/
|
||||
public void setBackstack(@NonNull List<RouterTransaction> backstack, ControllerChangeHandler changeHandler) {
|
||||
RouterTransaction oldTopTransaction = backStack.peek();
|
||||
Controller oldTop = oldTopTransaction != null ? oldTopTransaction.controller : null;
|
||||
removeAllExceptTopAndUnowned();
|
||||
public void setBackstack(@NonNull List<RouterTransaction> newBackstack, ControllerChangeHandler changeHandler) {
|
||||
List<RouterTransaction> oldVisibleTransactions = getVisibleTransactions(backstack.iterator());
|
||||
|
||||
if (backstack.size() > 0) {
|
||||
Controller newTop = backstack.get(backstack.size() - 1).controller;
|
||||
removeAllExceptVisibleAndUnowned();
|
||||
|
||||
if (newTop != oldTop) {
|
||||
if (newBackstack.size() > 0) {
|
||||
List<RouterTransaction> reverseNewBackstack = new ArrayList<>(newBackstack);
|
||||
Collections.reverse(reverseNewBackstack);
|
||||
List<RouterTransaction> newVisibleTransactions = getVisibleTransactions(reverseNewBackstack.iterator());
|
||||
|
||||
boolean visibleTransactionsChanged = newVisibleTransactions.size() != oldVisibleTransactions.size();
|
||||
if (!visibleTransactionsChanged) {
|
||||
for (int i = 0; i < oldVisibleTransactions.size(); i++) {
|
||||
if (oldVisibleTransactions.get(i).controller != newVisibleTransactions.get(i).controller) {
|
||||
visibleTransactionsChanged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (visibleTransactionsChanged) {
|
||||
ControllerChangeHandler handler = changeHandler != null ? changeHandler : new SimpleSwapChangeHandler();
|
||||
performControllerChange(newTop, oldTop, true, handler);
|
||||
|
||||
Controller rootController = oldVisibleTransactions.size() > 0 ? oldVisibleTransactions.get(0).controller : null;
|
||||
performControllerChange(newVisibleTransactions.get(0).controller, rootController, true, handler);
|
||||
|
||||
for (int i = oldVisibleTransactions.size() - 1; i > 0; i--) {
|
||||
RouterTransaction transaction = oldVisibleTransactions.get(i);
|
||||
performControllerChange(null, transaction.controller, true, handler);
|
||||
}
|
||||
|
||||
for (int i = 1; i < newVisibleTransactions.size(); i++) {
|
||||
RouterTransaction transaction = newVisibleTransactions.get(i);
|
||||
handler = transaction.pushChangeHandler() != null ? transaction.pushChangeHandler(): new SimpleSwapChangeHandler();
|
||||
performControllerChange(transaction.controller, newVisibleTransactions.get(i - 1).controller, true, handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,7 +364,7 @@ public abstract class Router {
|
||||
transaction.onAttachedToRouter();
|
||||
}
|
||||
|
||||
backStack.setBackstack(backstack);
|
||||
backstack.setBackstack(newBackstack);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -350,7 +398,7 @@ public abstract class Router {
|
||||
* Attaches this Router's existing backstack to its container if one exists.
|
||||
*/
|
||||
public void rebindIfNeeded() {
|
||||
Iterator<RouterTransaction> backstackIterator = backStack.reverseIterator();
|
||||
Iterator<RouterTransaction> backstackIterator = backstack.reverseIterator();
|
||||
while (backstackIterator.hasNext()) {
|
||||
RouterTransaction transaction = backstackIterator.next();
|
||||
|
||||
@@ -368,7 +416,7 @@ public abstract class Router {
|
||||
}
|
||||
|
||||
public final void onActivityStarted(Activity activity) {
|
||||
for (RouterTransaction transaction : backStack) {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
transaction.controller.activityStarted(activity);
|
||||
|
||||
for (Router childRouter : transaction.controller.getChildRouters()) {
|
||||
@@ -378,7 +426,7 @@ public abstract class Router {
|
||||
}
|
||||
|
||||
public final void onActivityResumed(Activity activity) {
|
||||
for (RouterTransaction transaction : backStack) {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
transaction.controller.activityResumed(activity);
|
||||
|
||||
for (Router childRouter : transaction.controller.getChildRouters()) {
|
||||
@@ -388,7 +436,7 @@ public abstract class Router {
|
||||
}
|
||||
|
||||
public final void onActivityPaused(Activity activity) {
|
||||
for (RouterTransaction transaction : backStack) {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
transaction.controller.activityPaused(activity);
|
||||
|
||||
for (Router childRouter : transaction.controller.getChildRouters()) {
|
||||
@@ -398,7 +446,7 @@ public abstract class Router {
|
||||
}
|
||||
|
||||
public final void onActivityStopped(Activity activity) {
|
||||
for (RouterTransaction transaction : backStack) {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
transaction.controller.activityStopped(activity);
|
||||
|
||||
for (Router childRouter : transaction.controller.getChildRouters()) {
|
||||
@@ -411,7 +459,7 @@ public abstract class Router {
|
||||
prepareForContainerRemoval();
|
||||
changeListeners.clear();
|
||||
|
||||
for (RouterTransaction transaction : backStack) {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
transaction.controller.activityDestroyed(activity.isChangingConfigurations());
|
||||
|
||||
for (Router childRouter : transaction.controller.getChildRouters()) {
|
||||
@@ -432,7 +480,8 @@ public abstract class Router {
|
||||
}
|
||||
|
||||
public void prepareForHostDetach() {
|
||||
for (RouterTransaction transaction : backStack) {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
ControllerChangeHandler.completePushImmediately(transaction.controller.getInstanceId());
|
||||
transaction.controller.prepareForHostDetach();
|
||||
}
|
||||
}
|
||||
@@ -441,7 +490,7 @@ public abstract class Router {
|
||||
prepareForHostDetach();
|
||||
|
||||
Bundle backstackState = new Bundle();
|
||||
backStack.saveInstanceState(backstackState);
|
||||
backstack.saveInstanceState(backstackState);
|
||||
|
||||
outState.putParcelable(KEY_BACKSTACK, backstackState);
|
||||
outState.putBoolean(KEY_POPS_LAST_VIEW, popsLastView);
|
||||
@@ -449,17 +498,17 @@ public abstract class Router {
|
||||
|
||||
public void restoreInstanceState(Bundle savedInstanceState) {
|
||||
Bundle backstackBundle = savedInstanceState.getParcelable(KEY_BACKSTACK);
|
||||
backStack.restoreInstanceState(backstackBundle);
|
||||
backstack.restoreInstanceState(backstackBundle);
|
||||
popsLastView = savedInstanceState.getBoolean(KEY_POPS_LAST_VIEW);
|
||||
|
||||
Iterator<RouterTransaction> backstackIterator = backStack.reverseIterator();
|
||||
Iterator<RouterTransaction> backstackIterator = backstack.reverseIterator();
|
||||
while (backstackIterator.hasNext()) {
|
||||
setControllerRouter(backstackIterator.next().controller);
|
||||
}
|
||||
}
|
||||
|
||||
public final void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
for (RouterTransaction transaction : backStack) {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
transaction.controller.createOptionsMenu(menu, inflater);
|
||||
|
||||
for (Router childRouter : transaction.controller.getChildRouters()) {
|
||||
@@ -469,7 +518,7 @@ public abstract class Router {
|
||||
}
|
||||
|
||||
public final void onPrepareOptionsMenu(Menu menu) {
|
||||
for (RouterTransaction transaction : backStack) {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
transaction.controller.prepareOptionsMenu(menu);
|
||||
|
||||
for (Router childRouter : transaction.controller.getChildRouters()) {
|
||||
@@ -479,7 +528,7 @@ public abstract class Router {
|
||||
}
|
||||
|
||||
public final boolean onOptionsItemSelected(MenuItem item) {
|
||||
for (RouterTransaction transaction : backStack) {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
if (transaction.controller.optionsItemSelected(item)) {
|
||||
return true;
|
||||
}
|
||||
@@ -494,8 +543,8 @@ public abstract class Router {
|
||||
}
|
||||
|
||||
private void popToTransaction(@NonNull RouterTransaction transaction, ControllerChangeHandler changeHandler) {
|
||||
RouterTransaction topTransaction = backStack.peek();
|
||||
List<RouterTransaction> poppedTransactions = backStack.popTo(transaction);
|
||||
RouterTransaction topTransaction = backstack.peek();
|
||||
List<RouterTransaction> poppedTransactions = backstack.popTo(transaction);
|
||||
trackDestroyingControllers(poppedTransactions);
|
||||
|
||||
if (poppedTransactions.size() > 0) {
|
||||
@@ -503,7 +552,7 @@ public abstract class Router {
|
||||
changeHandler = topTransaction.popChangeHandler();
|
||||
}
|
||||
|
||||
performControllerChange(backStack.peek().controller, topTransaction.controller, false, changeHandler);
|
||||
performControllerChange(backstack.peek().controller, topTransaction.controller, false, changeHandler);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,7 +565,7 @@ public abstract class Router {
|
||||
final List<Controller> getControllers() {
|
||||
List<Controller> controllers = new ArrayList<>();
|
||||
|
||||
Iterator<RouterTransaction> backstackIterator = backStack.reverseIterator();
|
||||
Iterator<RouterTransaction> backstackIterator = backstack.reverseIterator();
|
||||
while (backstackIterator.hasNext()) {
|
||||
controllers.add(backstackIterator.next().controller);
|
||||
}
|
||||
@@ -525,7 +574,7 @@ public abstract class Router {
|
||||
}
|
||||
|
||||
public final Boolean handleRequestedPermission(@NonNull String permission) {
|
||||
for (RouterTransaction transaction : backStack) {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
if (transaction.controller.didRequestPermission(permission)) {
|
||||
return transaction.controller.shouldShowRequestPermissionRationale(permission);
|
||||
}
|
||||
@@ -557,7 +606,7 @@ public abstract class Router {
|
||||
private void performControllerChange(final Controller to, final Controller from, boolean isPush, @NonNull ControllerChangeHandler changeHandler) {
|
||||
if (to != null) {
|
||||
setControllerRouter(to);
|
||||
} else if (backStack.size() == 0 && !popsLastView) {
|
||||
} else if (backstack.size() == 0 && !popsLastView) {
|
||||
// We're emptying out the backstack. Views get weird if you transition them out, so just no-op it. The hosting
|
||||
// Activity should be handling this by finishing or at least hiding this view.
|
||||
changeHandler = new NoOpControllerChangeHandler();
|
||||
@@ -567,7 +616,7 @@ public abstract class Router {
|
||||
}
|
||||
|
||||
void pushToBackstack(@NonNull RouterTransaction entry) {
|
||||
backStack.push(entry);
|
||||
backstack.push(entry);
|
||||
}
|
||||
|
||||
private void trackDestroyingController(RouterTransaction transaction) {
|
||||
@@ -589,13 +638,13 @@ public abstract class Router {
|
||||
}
|
||||
}
|
||||
|
||||
private void removeAllExceptTopAndUnowned() {
|
||||
private void removeAllExceptVisibleAndUnowned() {
|
||||
List<View> views = new ArrayList<>();
|
||||
|
||||
RouterTransaction topTransaction = backStack.peek();
|
||||
final View topView = topTransaction != null ? topTransaction.controller.getView() : null;
|
||||
if (topView != null) {
|
||||
views.add(topView);
|
||||
for (RouterTransaction transaction : getVisibleTransactions(backstack.iterator())) {
|
||||
if (transaction.controller.getView() != null) {
|
||||
views.add(transaction.controller.getView());
|
||||
}
|
||||
}
|
||||
|
||||
for (Router router : getSiblingRouters()) {
|
||||
@@ -625,6 +674,21 @@ public abstract class Router {
|
||||
}
|
||||
}
|
||||
|
||||
private List<RouterTransaction> getVisibleTransactions(Iterator<RouterTransaction> backstackIterator) {
|
||||
List<RouterTransaction> transactions = new ArrayList<>();
|
||||
while (backstackIterator.hasNext()) {
|
||||
RouterTransaction transaction = backstackIterator.next();
|
||||
transactions.add(transaction);
|
||||
|
||||
if (transaction.pushChangeHandler() == null || transaction.pushChangeHandler().removesFromViewOnPush()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Collections.reverse(transactions);
|
||||
return transactions;
|
||||
}
|
||||
|
||||
void setControllerRouter(Controller controller) {
|
||||
controller.setRouter(this);
|
||||
}
|
||||
@@ -638,4 +702,5 @@ public abstract class Router {
|
||||
abstract void requestPermissions(String instanceId, String[] permissions, int requestCode);
|
||||
abstract boolean hasHost();
|
||||
abstract List<Router> getSiblingRouters();
|
||||
abstract Router getRootRouter();
|
||||
}
|
||||
|
||||
+30
-8
@@ -24,6 +24,7 @@ public abstract class AnimatorChangeHandler extends ControllerChangeHandler {
|
||||
private long animationDuration;
|
||||
private boolean removesFromViewOnPush;
|
||||
private boolean canceled;
|
||||
private boolean needsImmediateCompletion;
|
||||
private Animator animator;
|
||||
|
||||
public AnimatorChangeHandler() {
|
||||
@@ -67,10 +68,21 @@ public abstract class AnimatorChangeHandler extends ControllerChangeHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void completeImmediately() {
|
||||
super.completeImmediately();
|
||||
|
||||
needsImmediateCompletion = true;
|
||||
if (animator != null) {
|
||||
animator.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
public long getAnimationDuration() {
|
||||
return animationDuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removesFromViewOnPush() {
|
||||
return removesFromViewOnPush;
|
||||
}
|
||||
@@ -99,7 +111,7 @@ public abstract class AnimatorChangeHandler extends ControllerChangeHandler {
|
||||
if (addingToView) {
|
||||
if (isPush || from == null) {
|
||||
container.addView(to);
|
||||
} else {
|
||||
} else if (to.getParent() == null) {
|
||||
container.addView(to, container.indexOfChild(from));
|
||||
}
|
||||
|
||||
@@ -124,7 +136,7 @@ public abstract class AnimatorChangeHandler extends ControllerChangeHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private void performAnimation(@NonNull final ViewGroup container, final View from, View to, final boolean isPush, final boolean toAddedToContainer, @NonNull final ControllerChangeCompletedListener changeListener) {
|
||||
private void performAnimation(@NonNull final ViewGroup container, final View from, final View to, final boolean isPush, final boolean toAddedToContainer, @NonNull final ControllerChangeCompletedListener changeListener) {
|
||||
if (canceled) {
|
||||
changeListener.onChangeCompleted();
|
||||
return;
|
||||
@@ -139,22 +151,32 @@ public abstract class AnimatorChangeHandler extends ControllerChangeHandler {
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
changeListener.onChangeCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (from != null && (!isPush || removesFromViewOnPush)) {
|
||||
if (from != null && (!isPush || removesFromViewOnPush) && needsImmediateCompletion) {
|
||||
container.removeView(from);
|
||||
}
|
||||
|
||||
changeListener.onChangeCompleted();
|
||||
animator.removeListener(this);
|
||||
animator = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (from != null && (!isPush || removesFromViewOnPush) && !canceled) {
|
||||
container.removeView(from);
|
||||
}
|
||||
|
||||
changeListener.onChangeCompleted();
|
||||
animator.removeListener(this);
|
||||
|
||||
if (isPush && from != null) {
|
||||
resetFromView(from);
|
||||
}
|
||||
|
||||
animator = null;
|
||||
}
|
||||
});
|
||||
|
||||
animator.start();
|
||||
}
|
||||
|
||||
|
||||
+23
-7
@@ -5,6 +5,7 @@ import android.support.annotation.NonNull;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
|
||||
/**
|
||||
@@ -16,6 +17,8 @@ public class SimpleSwapChangeHandler extends ControllerChangeHandler {
|
||||
|
||||
private boolean removesFromViewOnPush;
|
||||
|
||||
private boolean canceled;
|
||||
|
||||
public SimpleSwapChangeHandler() {
|
||||
this(true);
|
||||
}
|
||||
@@ -37,16 +40,29 @@ public class SimpleSwapChangeHandler extends ControllerChangeHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull final ControllerChangeCompletedListener changeListener) {
|
||||
if (from != null && (!isPush || removesFromViewOnPush)) {
|
||||
container.removeView(from);
|
||||
}
|
||||
public void onAbortPush(@NonNull ControllerChangeHandler newHandler, Controller newTop) {
|
||||
super.onAbortPush(newHandler, newTop);
|
||||
|
||||
if (to != null && to.getParent() == null) {
|
||||
container.addView(to);
|
||||
canceled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull final ControllerChangeCompletedListener changeListener) {
|
||||
if (!canceled) {
|
||||
if (from != null && (!isPush || removesFromViewOnPush)) {
|
||||
container.removeView(from);
|
||||
}
|
||||
|
||||
if (to != null && to.getParent() == null) {
|
||||
container.addView(to);
|
||||
}
|
||||
}
|
||||
|
||||
changeListener.onChangeCompleted();
|
||||
}
|
||||
|
||||
}
|
||||
@Override
|
||||
public boolean removesFromViewOnPush() {
|
||||
return removesFromViewOnPush;
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -76,4 +76,8 @@ public abstract class TransitionChangeHandler extends ControllerChangeHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean removesFromViewOnPush() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+8
@@ -77,4 +77,12 @@ public class TransitionChangeHandlerCompat extends ControllerChangeHandler {
|
||||
fallbackChangeHandler.restoreFromBundle(bundle.getBundle(KEY_FALLBACK_HANDLER_STATE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removesFromViewOnPush() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
return transitionChangeHandler.removesFromViewOnPush();
|
||||
} else {
|
||||
return fallbackChangeHandler.removesFromViewOnPush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
|
||||
private static final String KEY_PERMISSION_REQUEST_CODES = "LifecycleHandler.permissionRequests";
|
||||
private static final String KEY_ACTIVITY_REQUEST_CODES = "LifecycleHandler.activityRequests";
|
||||
private static final String KEY_ROUTER_STATE_PREFIX = "LifecycleHandler.routerState";
|
||||
|
||||
private Activity activity;
|
||||
private boolean hasRegisteredCallbacks;
|
||||
@@ -69,7 +70,10 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
router.setHost(this, container);
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
router.restoreInstanceState(savedInstanceState);
|
||||
Bundle routerSavedState = savedInstanceState.getBundle(KEY_ROUTER_STATE_PREFIX + router.getContainerId());
|
||||
if (routerSavedState != null) {
|
||||
router.restoreInstanceState(routerSavedState);
|
||||
}
|
||||
}
|
||||
routerMap.put(getRouterHashKey(container), router);
|
||||
} else {
|
||||
@@ -303,7 +307,9 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
|
||||
if (this.activity == activity) {
|
||||
for (Router router : routerMap.values()) {
|
||||
router.saveInstanceState(outState);
|
||||
Bundle bundle = new Bundle();
|
||||
router.saveInstanceState(bundle);
|
||||
outState.putBundle(KEY_ROUTER_STATE_PREFIX + router.getContainerId(), bundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller.LifecycleListener;
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler.ControllerChangeCompletedListener;
|
||||
import com.bluelinelabs.conductor.MockChangeHandler.ChangeHandlerListener;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
@@ -377,22 +377,8 @@ public class ControllerLifecycleTests {
|
||||
});
|
||||
|
||||
router.pushController(RouterTransaction.with(testController)
|
||||
.pushChangeHandler(new ChangeHandler(new ChangeHandlerListener() {
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
container.addView(to);
|
||||
ViewUtils.setAttached(to, true);
|
||||
changeListener.onChangeCompleted();
|
||||
}
|
||||
}))
|
||||
.popChangeHandler(new ChangeHandler(new ChangeHandlerListener() {
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
container.removeView(from);
|
||||
ViewUtils.setAttached(from, false);
|
||||
changeListener.onChangeCompleted();
|
||||
}
|
||||
})));
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
|
||||
router.popController(testController);
|
||||
|
||||
@@ -407,14 +393,7 @@ public class ControllerLifecycleTests {
|
||||
public void testChildLifecycle() {
|
||||
Controller parent = new TestController();
|
||||
router.pushController(RouterTransaction.with(parent)
|
||||
.pushChangeHandler(new ChangeHandler(new ChangeHandlerListener() {
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
container.addView(to);
|
||||
ViewUtils.setAttached(to, true);
|
||||
changeListener.onChangeCompleted();
|
||||
}
|
||||
})));
|
||||
.pushChangeHandler(new MockChangeHandler()));
|
||||
|
||||
TestController child = new TestController();
|
||||
attachLifecycleListener(child);
|
||||
@@ -440,22 +419,8 @@ public class ControllerLifecycleTests {
|
||||
public void testChildLifecycle2() {
|
||||
Controller parent = new TestController();
|
||||
router.pushController(RouterTransaction.with(parent)
|
||||
.pushChangeHandler(new ChangeHandler(new ChangeHandlerListener() {
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
container.addView(to);
|
||||
ViewUtils.setAttached(to, true);
|
||||
changeListener.onChangeCompleted();
|
||||
}
|
||||
}))
|
||||
.popChangeHandler(new ChangeHandler(new ChangeHandlerListener() {
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
container.removeView(from);
|
||||
ViewUtils.setAttached(from, false);
|
||||
changeListener.onChangeCompleted();
|
||||
}
|
||||
})));
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
|
||||
TestController child = new TestController();
|
||||
attachLifecycleListener(child);
|
||||
@@ -477,44 +442,47 @@ public class ControllerLifecycleTests {
|
||||
assertCalls(expectedCallState, child);
|
||||
}
|
||||
|
||||
private ChangeHandler getPushHandler(final CallState expectedCallState, final TestController controller) {
|
||||
return new ChangeHandler(new ChangeHandlerListener() {
|
||||
private MockChangeHandler getPushHandler(final CallState expectedCallState, final TestController controller) {
|
||||
return new MockChangeHandler(new ChangeHandlerListener() {
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
void willStartChange() {
|
||||
expectedCallState.changeStartCalls++;
|
||||
expectedCallState.createViewCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
|
||||
container.addView(to);
|
||||
ViewUtils.setAttached(to, true);
|
||||
|
||||
@Override
|
||||
void didAttachOrDetach() {
|
||||
expectedCallState.attachCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
|
||||
changeListener.onChangeCompleted();
|
||||
|
||||
@Override
|
||||
void didEndChange() {
|
||||
expectedCallState.changeEndCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private ChangeHandler getPopHandler(final CallState expectedCallState, final TestController controller) {
|
||||
return new ChangeHandler(new ChangeHandlerListener() {
|
||||
private MockChangeHandler getPopHandler(final CallState expectedCallState, final TestController controller) {
|
||||
return new MockChangeHandler(new ChangeHandlerListener() {
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
void willStartChange() {
|
||||
expectedCallState.changeStartCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
container.removeView(from);
|
||||
ViewUtils.setAttached(from, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
void didAttachOrDetach() {
|
||||
expectedCallState.destroyViewCalls++;
|
||||
expectedCallState.detachCalls++;
|
||||
expectedCallState.destroyCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
|
||||
changeListener.onChangeCompleted();
|
||||
|
||||
@Override
|
||||
void didEndChange() {
|
||||
expectedCallState.changeEndCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
@@ -585,24 +553,4 @@ public class ControllerLifecycleTests {
|
||||
});
|
||||
}
|
||||
|
||||
interface ChangeHandlerListener {
|
||||
void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener);
|
||||
}
|
||||
|
||||
public static class ChangeHandler extends ControllerChangeHandler {
|
||||
|
||||
private ChangeHandlerListener listener;
|
||||
|
||||
public ChangeHandler() { }
|
||||
|
||||
public ChangeHandler(ChangeHandlerListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
listener.performChange(container, from, to, isPush, changeListener);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
public class MockChangeHandler extends ControllerChangeHandler {
|
||||
|
||||
static class ChangeHandlerListener {
|
||||
void willStartChange() { }
|
||||
void didAttachOrDetach() { }
|
||||
void didEndChange() { }
|
||||
}
|
||||
|
||||
final ChangeHandlerListener listener;
|
||||
|
||||
public MockChangeHandler() {
|
||||
this(new ChangeHandlerListener() { });
|
||||
}
|
||||
|
||||
public MockChangeHandler(@NonNull ChangeHandlerListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
listener.willStartChange();
|
||||
|
||||
if (isPush) {
|
||||
container.addView(to);
|
||||
ViewUtils.setAttached(to, true);
|
||||
|
||||
listener.didAttachOrDetach();
|
||||
|
||||
if (from != null) {
|
||||
container.removeView(from);
|
||||
ViewUtils.setAttached(from, false);
|
||||
}
|
||||
} else {
|
||||
container.removeView(from);
|
||||
ViewUtils.setAttached(from, false);
|
||||
|
||||
listener.didAttachOrDetach();
|
||||
|
||||
if (to != null) {
|
||||
container.addView(to);
|
||||
ViewUtils.setAttached(to, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
changeListener.onChangeCompleted();
|
||||
listener.didEndChange();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.IdRes;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.util.ActivityController;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(manifest = Config.NONE)
|
||||
public class ReattachCaseTests {
|
||||
|
||||
private ActivityController<TestActivity> activityController;
|
||||
private Router router;
|
||||
|
||||
public void createActivityController(Bundle savedInstanceState) {
|
||||
activityController = Robolectric.buildActivity(TestActivity.class).create(savedInstanceState).start();
|
||||
|
||||
@IdRes int containerId = 4;
|
||||
FrameLayout routerContainer = new FrameLayout(activityController.get());
|
||||
routerContainer.setId(containerId);
|
||||
|
||||
router = Conductor.attachRouter(activityController.get(), routerContainer, savedInstanceState);
|
||||
if (!router.hasRootController()) {
|
||||
router.setRoot(RouterTransaction.with(new TestController()));
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
createActivityController(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNeedsAttachingOnPauseAndOrientation() {
|
||||
final TestController controllerA = new TestController();
|
||||
final TestController controllerB = new TestController();
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerA)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
|
||||
Assert.assertTrue(controllerA.isAttached());
|
||||
Assert.assertFalse(controllerB.isAttached());
|
||||
|
||||
sleepWakeDevice();
|
||||
|
||||
Assert.assertTrue(controllerA.isAttached());
|
||||
Assert.assertFalse(controllerB.isAttached());
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerB)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
|
||||
rotateDevice();
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildNeedsAttachOnPauseAndOrientation() {
|
||||
final TestController controllerA = new TestController();
|
||||
final TestController childController = new TestController();
|
||||
final TestController controllerB = new TestController();
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerA)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
|
||||
Router childRouter = controllerA.getChildRouter((ViewGroup)controllerA.getView().findViewById(TestController.VIEW_ID), null);
|
||||
childRouter.pushController(RouterTransaction.with(childController)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
|
||||
Assert.assertTrue(controllerA.isAttached());
|
||||
Assert.assertTrue(childController.isAttached());
|
||||
Assert.assertFalse(controllerB.isAttached());
|
||||
|
||||
sleepWakeDevice();
|
||||
|
||||
Assert.assertTrue(controllerA.isAttached());
|
||||
Assert.assertTrue(childController.isAttached());
|
||||
Assert.assertFalse(controllerB.isAttached());
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerB)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertFalse(childController.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
|
||||
rotateDevice();
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertFalse(childController.isAttached());
|
||||
Assert.assertTrue(childController.getNeedsAttach());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildHandleBackOnOrientation() {
|
||||
final TestController controllerA = new TestController();
|
||||
final TestController controllerB = new TestController();
|
||||
final TestController childController = new TestController();
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerA)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
|
||||
Assert.assertTrue(controllerA.isAttached());
|
||||
Assert.assertFalse(controllerB.isAttached());
|
||||
Assert.assertFalse(childController.isAttached());
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerB)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
|
||||
Router childRouter = controllerB.getChildRouter((ViewGroup)controllerB.getView().findViewById(TestController.VIEW_ID), null);
|
||||
childRouter.setPopsLastView(true);
|
||||
childRouter.pushController(RouterTransaction.with(childController)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
Assert.assertTrue(childController.isAttached());
|
||||
|
||||
rotateDevice();
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
Assert.assertTrue(childController.isAttached());
|
||||
|
||||
router.handleBack();
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
Assert.assertFalse(childController.isAttached());
|
||||
|
||||
router.handleBack();
|
||||
|
||||
Assert.assertTrue(controllerA.isAttached());
|
||||
Assert.assertFalse(controllerB.isAttached());
|
||||
Assert.assertFalse(childController.isAttached());
|
||||
}
|
||||
|
||||
// Attempt to test https://github.com/bluelinelabs/Conductor/issues/86#issuecomment-231381271
|
||||
@Test
|
||||
public void testReusedChildRouterHandleBackOnOrientation() {
|
||||
TestController controllerA = new TestController();
|
||||
TestController controllerB = new TestController();
|
||||
TestController childController = new TestController();
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerA)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
|
||||
Assert.assertTrue(controllerA.isAttached());
|
||||
Assert.assertFalse(controllerB.isAttached());
|
||||
Assert.assertFalse(childController.isAttached());
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerB)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
|
||||
Router childRouter = controllerB.getChildRouter((ViewGroup)controllerB.getView().findViewById(TestController.VIEW_ID), null);
|
||||
childRouter.setPopsLastView(true);
|
||||
childRouter.pushController(RouterTransaction.with(childController)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
Assert.assertTrue(childController.isAttached());
|
||||
|
||||
router.handleBack();
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
Assert.assertFalse(childController.isAttached());
|
||||
|
||||
childController = new TestController();
|
||||
childRouter.pushController(RouterTransaction.with(childController)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
Assert.assertTrue(childController.isAttached());
|
||||
|
||||
rotateDevice();
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
Assert.assertTrue(childController.isAttached());
|
||||
|
||||
router.handleBack();
|
||||
|
||||
childController = new TestController();
|
||||
childRouter.pushController(RouterTransaction.with(childController)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
Assert.assertTrue(childController.isAttached());
|
||||
|
||||
router.handleBack();
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
Assert.assertFalse(childController.isAttached());
|
||||
|
||||
router.handleBack();
|
||||
|
||||
Assert.assertTrue(controllerA.isAttached());
|
||||
Assert.assertFalse(controllerB.isAttached());
|
||||
Assert.assertFalse(childController.isAttached());
|
||||
}
|
||||
|
||||
private void sleepWakeDevice() {
|
||||
activityController.saveInstanceState(new Bundle()).pause();
|
||||
activityController.resume();
|
||||
}
|
||||
|
||||
private void rotateDevice() {
|
||||
@IdRes int containerId = 4;
|
||||
FrameLayout routerContainer = new FrameLayout(activityController.get());
|
||||
routerContainer.setId(containerId);
|
||||
|
||||
activityController.get().isChangingConfigurations = true;
|
||||
activityController.get().recreate();
|
||||
router.rebindIfNeeded();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.IdRes;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.util.ActivityController;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(manifest = Config.NONE)
|
||||
public class TargetControllerTests {
|
||||
|
||||
private ActivityController<TestActivity> activityController;
|
||||
private Router router;
|
||||
|
||||
public void createActivityController(Bundle savedInstanceState) {
|
||||
activityController = Robolectric.buildActivity(TestActivity.class).create(savedInstanceState).start();
|
||||
|
||||
@IdRes int containerId = 4;
|
||||
FrameLayout routerContainer = new FrameLayout(activityController.get());
|
||||
routerContainer.setId(containerId);
|
||||
|
||||
router = Conductor.attachRouter(activityController.get(), routerContainer, savedInstanceState);
|
||||
if (!router.hasRootController()) {
|
||||
router.setRoot(RouterTransaction.with(new TestController()));
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
createActivityController(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSiblingTarget() {
|
||||
final TestController controllerA = new TestController();
|
||||
final TestController controllerB = new TestController();
|
||||
|
||||
Assert.assertNull(controllerA.getTargetController());
|
||||
Assert.assertNull(controllerB.getTargetController());
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerA)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
|
||||
controllerB.setTargetController(controllerA);
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerB)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
|
||||
Assert.assertNull(controllerA.getTargetController());
|
||||
Assert.assertEquals(controllerA, controllerB.getTargetController());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParentChildTarget() {
|
||||
final TestController controllerA = new TestController();
|
||||
final TestController controllerB = new TestController();
|
||||
|
||||
Assert.assertNull(controllerA.getTargetController());
|
||||
Assert.assertNull(controllerB.getTargetController());
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerA)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
|
||||
controllerB.setTargetController(controllerA);
|
||||
|
||||
Router childRouter = controllerA.getChildRouter((ViewGroup)controllerA.getView().findViewById(TestController.VIEW_ID), null);
|
||||
childRouter.pushController(RouterTransaction.with(controllerB)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
|
||||
Assert.assertNull(controllerA.getTargetController());
|
||||
Assert.assertEquals(controllerA, controllerB.getTargetController());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildParentTarget() {
|
||||
final TestController controllerA = new TestController();
|
||||
final TestController controllerB = new TestController();
|
||||
|
||||
Assert.assertNull(controllerA.getTargetController());
|
||||
Assert.assertNull(controllerB.getTargetController());
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerA)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
|
||||
controllerA.setTargetController(controllerB);
|
||||
|
||||
Router childRouter = controllerA.getChildRouter((ViewGroup)controllerA.getView().findViewById(TestController.VIEW_ID), null);
|
||||
childRouter.pushController(RouterTransaction.with(controllerB)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
|
||||
Assert.assertNull(controllerB.getTargetController());
|
||||
Assert.assertEquals(controllerB, controllerA.getTargetController());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import java.util.List;
|
||||
|
||||
public class ViewUtils {
|
||||
|
||||
static void setAttached(View view, boolean attached) {
|
||||
public static void setAttached(View view, boolean attached) {
|
||||
Object listenerInfo = ReflectionHelpers.callInstanceMethod(view, "getListenerInfo");
|
||||
List<OnAttachStateChangeListener> listeners = ReflectionHelpers.getField(listenerInfo, "mOnAttachStateChangeListeners");
|
||||
|
||||
|
||||
@@ -12,22 +12,20 @@ import com.bluelinelabs.conductor.demo.controllers.HomeController;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Unbinder;
|
||||
|
||||
public class MainActivity extends AppCompatActivity implements ActionBarProvider {
|
||||
public final class MainActivity extends AppCompatActivity implements ActionBarProvider {
|
||||
|
||||
@BindView(R.id.toolbar) Toolbar toolbar;
|
||||
@BindView(R.id.controller_container) ViewGroup container;
|
||||
|
||||
private Router router;
|
||||
private Unbinder unbinder;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_main);
|
||||
unbinder = ButterKnife.bind(this);
|
||||
ButterKnife.bind(this);
|
||||
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
@@ -44,10 +42,4 @@ public class MainActivity extends AppCompatActivity implements ActionBarProvider
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
unbinder.unbind();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+10
-2
@@ -71,11 +71,19 @@ public class ParentController extends BaseController {
|
||||
|
||||
@Override
|
||||
public boolean handleBack() {
|
||||
if (!finishing) {
|
||||
int childControllers = 0;
|
||||
for (Router childRouter : getChildRouters()) {
|
||||
if (childRouter.hasRootController()) {
|
||||
childControllers++;
|
||||
}
|
||||
}
|
||||
|
||||
if (childControllers != NUMBER_OF_CHILDREN || finishing) {
|
||||
return true;
|
||||
} else {
|
||||
finishing = true;
|
||||
return super.handleBack();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-3
@@ -140,11 +140,10 @@ public class TransitionDemoController extends BaseController {
|
||||
|
||||
public static RouterTransaction getRouterTransaction(int index, Controller fromController) {
|
||||
TransitionDemoController toController = new TransitionDemoController(index);
|
||||
ControllerChangeHandler changeHandler = toController.getChangeHandler(fromController);
|
||||
|
||||
return RouterTransaction.with(toController)
|
||||
.pushChangeHandler(changeHandler)
|
||||
.popChangeHandler(changeHandler);
|
||||
.pushChangeHandler(toController.getChangeHandler(fromController))
|
||||
.popChangeHandler(toController.getChangeHandler(fromController));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
@@ -37,6 +37,7 @@ public abstract class ButterKnifeController extends RxController {
|
||||
protected void onDestroyView(View view) {
|
||||
super.onDestroyView(view);
|
||||
unbinder.unbind();
|
||||
unbinder = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+18
-1
@@ -1,7 +1,10 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers.base;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
import com.bluelinelabs.conductor.ControllerChangeType;
|
||||
import com.bluelinelabs.conductor.demo.DemoApplication;
|
||||
|
||||
public abstract class RefWatchingController extends ButterKnifeController {
|
||||
@@ -11,10 +14,24 @@ public abstract class RefWatchingController extends ButterKnifeController {
|
||||
super(args);
|
||||
}
|
||||
|
||||
private boolean hasExited;
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
DemoApplication.refWatcher.watch(this);
|
||||
|
||||
if (hasExited) {
|
||||
DemoApplication.refWatcher.watch(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onChangeEnded(@NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
super.onChangeEnded(changeHandler, changeType);
|
||||
|
||||
hasExited = !changeType.isEnter;
|
||||
if (isDestroyed()) {
|
||||
DemoApplication.refWatcher.watch(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -14,8 +14,8 @@ ext {
|
||||
|
||||
picasso = 'com.squareup.picasso:picasso:2.5.2'
|
||||
|
||||
leakCanary = 'com.squareup.leakcanary:leakcanary-android:1.4-beta2'
|
||||
leakCanaryNoOp = 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2'
|
||||
leakCanary = 'com.squareup.leakcanary:leakcanary-android:1.4'
|
||||
leakCanaryNoOp = 'com.squareup.leakcanary:leakcanary-android-no-op:1.4'
|
||||
|
||||
rxJava = 'io.reactivex:rxjava:1.1.0'
|
||||
rxAndroid = 'io.reactivex:rxandroid:1.1.0'
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
VERSION_NAME=2.0.1-SNAPSHOT
|
||||
VERSION_NAME=2.0.3-SNAPSHOT
|
||||
VERSION_CODE=2
|
||||
GROUP=com.bluelinelabs
|
||||
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
#Tue Jan 19 22:43:12 CST 2016
|
||||
#Mon Sep 05 10:45:48 CDT 2016
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
|
||||
|
||||
Reference in New Issue
Block a user