Fixes #124
This commit is contained in:
@@ -4,6 +4,7 @@ import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
|
||||
import com.bluelinelabs.conductor.changehandler.SimpleSwapChangeHandler;
|
||||
import com.bluelinelabs.conductor.internal.ClassUtils;
|
||||
@@ -25,6 +26,8 @@ public abstract class ControllerChangeHandler {
|
||||
|
||||
private static final Map<String, ControllerChangeHandler> inProgressPushHandlers = new HashMap<>();
|
||||
|
||||
private boolean forceRemoveViewOnPush;
|
||||
|
||||
/**
|
||||
* Responsible for swapping Views from one Controller to another.
|
||||
*
|
||||
@@ -174,6 +177,13 @@ public abstract class ControllerChangeHandler {
|
||||
for (ControllerChangeListener listener : listeners) {
|
||||
listener.onChangeCompleted(to, from, isPush, container, inHandler);
|
||||
}
|
||||
|
||||
if (handler.forceRemoveViewOnPush && fromView != null) {
|
||||
ViewParent fromParent = fromView.getParent();
|
||||
if (fromParent != null && fromParent instanceof ViewGroup) {
|
||||
((ViewGroup)fromParent).removeView(fromView);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -183,6 +193,10 @@ public abstract class ControllerChangeHandler {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setForceRemoveViewOnPush(boolean force) {
|
||||
forceRemoveViewOnPush = force;
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener interface useful for allowing external classes to be notified of change events.
|
||||
*/
|
||||
|
||||
@@ -152,8 +152,21 @@ public abstract class Router {
|
||||
trackDestroyingController(backstack.pop());
|
||||
}
|
||||
|
||||
final ControllerChangeHandler handler = transaction.pushChangeHandler();
|
||||
final boolean oldHandlerRemovedViews = topTransaction.pushChangeHandler() == null || topTransaction.pushChangeHandler().removesFromViewOnPush();
|
||||
final boolean newHandlerRemovesViews = handler == null || handler.removesFromViewOnPush();
|
||||
if (!oldHandlerRemovedViews && newHandlerRemovesViews) {
|
||||
for (RouterTransaction visibleTransaction : getVisibleTransactions(backstack.iterator())) {
|
||||
performControllerChange(null, visibleTransaction.controller, true, handler != null ? handler.copy() : new SimpleSwapChangeHandler());
|
||||
}
|
||||
}
|
||||
|
||||
pushToBackstack(transaction);
|
||||
performControllerChange(transaction, topTransaction, true);
|
||||
|
||||
if (handler != null) {
|
||||
handler.setForceRemoveViewOnPush(true);
|
||||
}
|
||||
performControllerChange(transaction.pushChangeHandler(handler), topTransaction, true);
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
@@ -319,7 +332,7 @@ public abstract class Router {
|
||||
* using the passed {@link ControllerChangeHandler}
|
||||
*
|
||||
* @param newBackstack The new backstack
|
||||
* @param changeHandler An optional change handler to be used to handle the transition
|
||||
* @param changeHandler An optional change handler to be used to handle the root view of transition
|
||||
*/
|
||||
public void setBackstack(@NonNull List<RouterTransaction> newBackstack, ControllerChangeHandler changeHandler) {
|
||||
List<RouterTransaction> oldVisibleTransactions = getVisibleTransactions(backstack.iterator());
|
||||
@@ -349,13 +362,15 @@ public abstract class Router {
|
||||
|
||||
for (int i = oldVisibleTransactions.size() - 1; i > 0; i--) {
|
||||
RouterTransaction transaction = oldVisibleTransactions.get(i);
|
||||
performControllerChange(null, transaction.controller, true, handler);
|
||||
ControllerChangeHandler localHandler = handler.copy();
|
||||
localHandler.setForceRemoveViewOnPush(true);
|
||||
performControllerChange(null, transaction.controller, true, localHandler);
|
||||
}
|
||||
|
||||
for (int i = 1; i < newVisibleTransactions.size(); i++) {
|
||||
RouterTransaction transaction = newVisibleTransactions.get(i);
|
||||
handler = transaction.pushChangeHandler() != null ? transaction.pushChangeHandler().copy() : new SimpleSwapChangeHandler();
|
||||
performControllerChange(transaction.controller, newVisibleTransactions.get(i - 1).controller, true, handler);
|
||||
performControllerChange(transaction.controller, newVisibleTransactions.get(i - 1).controller, true, handler.copy());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.IdRes;
|
||||
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.util.ActivityController;
|
||||
|
||||
public class ActivityProxy {
|
||||
|
||||
private ActivityController<TestActivity> activityController;
|
||||
private AttachFakingFrameLayout view;
|
||||
|
||||
public ActivityProxy() {
|
||||
activityController = Robolectric.buildActivity(TestActivity.class);
|
||||
|
||||
@IdRes int containerId = 4;
|
||||
view = new AttachFakingFrameLayout(activityController.get());
|
||||
view.setId(containerId);
|
||||
}
|
||||
|
||||
public ActivityProxy create(Bundle savedInstanceState) {
|
||||
activityController.create(savedInstanceState);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ActivityProxy start() {
|
||||
activityController.start();
|
||||
view.setAttached(true);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ActivityProxy resume() {
|
||||
activityController.resume();
|
||||
return this;
|
||||
}
|
||||
|
||||
public ActivityProxy pause() {
|
||||
activityController.pause();
|
||||
return this;
|
||||
}
|
||||
|
||||
public ActivityProxy saveInstanceState(Bundle outState) {
|
||||
activityController.saveInstanceState(outState);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ActivityProxy stop() {
|
||||
activityController.stop();
|
||||
view.setAttached(false);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ActivityProxy destroy() {
|
||||
activityController.destroy();
|
||||
return this;
|
||||
}
|
||||
|
||||
public ActivityProxy rotate() {
|
||||
getActivity().isChangingConfigurations = true;
|
||||
getActivity().recreate();
|
||||
return this;
|
||||
}
|
||||
|
||||
public TestActivity getActivity() {
|
||||
return activityController.get();
|
||||
}
|
||||
|
||||
public AttachFakingFrameLayout getView() {
|
||||
return view;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import android.os.IInterface;
|
||||
import android.os.Parcel;
|
||||
import android.os.RemoteException;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
@@ -79,7 +80,34 @@ public class AttachFakingFrameLayout extends FrameLayout {
|
||||
}
|
||||
|
||||
public void setAttached(boolean attached) {
|
||||
reportAttached = attached;
|
||||
setAttached(attached, true);
|
||||
}
|
||||
|
||||
public void setAttached(boolean attached, boolean reportToViewUtils) {
|
||||
if (reportAttached != attached) {
|
||||
reportAttached = attached;
|
||||
if (reportToViewUtils) {
|
||||
ViewUtils.reportAttached(this, attached);
|
||||
}
|
||||
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
ViewUtils.reportAttached(getChildAt(i), attached);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewAdded(View child) {
|
||||
if (reportAttached) {
|
||||
ViewUtils.reportAttached(child, true);
|
||||
}
|
||||
super.onViewAdded(child);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewRemoved(View child) {
|
||||
ViewUtils.reportAttached(child, false);
|
||||
super.onViewRemoved(child);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -119,9 +119,9 @@ public class CallState implements Parcelable {
|
||||
"\n restoreInstanceStateCalls=" + restoreInstanceStateCalls +
|
||||
"\n saveViewStateCalls=" + saveViewStateCalls +
|
||||
"\n restoreViewStateCalls=" + restoreViewStateCalls +
|
||||
"\n onActivityResultCalls= " + onActivityResultCalls +
|
||||
"\n onRequestPermissionsResultCalls= " + onRequestPermissionsResultCalls +
|
||||
"\n createOptionsMenuCalls= " + createOptionsMenuCalls +
|
||||
"\n onActivityResultCalls=" + onActivityResultCalls +
|
||||
"\n onRequestPermissionsResultCalls=" + onRequestPermissionsResultCalls +
|
||||
"\n createOptionsMenuCalls=" + createOptionsMenuCalls +
|
||||
"}\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.IdRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -13,29 +12,26 @@ 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 ControllerLifecycleTests {
|
||||
|
||||
private ActivityController<TestActivity> activityController;
|
||||
private Router router;
|
||||
|
||||
private ActivityProxy activityProxy;
|
||||
private CallState currentCallState;
|
||||
|
||||
public void createActivityController(Bundle savedInstanceState) {
|
||||
activityController = Robolectric.buildActivity(TestActivity.class).create(savedInstanceState).start();
|
||||
public void createActivityController(Bundle savedInstanceState, boolean includeStartAndResume) {
|
||||
activityProxy = new ActivityProxy().create(savedInstanceState);
|
||||
|
||||
@IdRes int containerId = 4;
|
||||
AttachFakingFrameLayout routerContainer = new AttachFakingFrameLayout(activityController.get());
|
||||
routerContainer.setId(containerId);
|
||||
routerContainer.setAttached(true);
|
||||
if (includeStartAndResume) {
|
||||
activityProxy.start().resume();
|
||||
}
|
||||
|
||||
router = Conductor.attachRouter(activityController.get(), routerContainer, savedInstanceState);
|
||||
router = Conductor.attachRouter(activityProxy.getActivity(), activityProxy.getView(), savedInstanceState);
|
||||
if (!router.hasRootController()) {
|
||||
router.setRoot(RouterTransaction.with(new TestController()));
|
||||
}
|
||||
@@ -43,7 +39,7 @@ public class ControllerLifecycleTests {
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
createActivityController(null);
|
||||
createActivityController(null, true);
|
||||
|
||||
currentCallState = new CallState();
|
||||
}
|
||||
@@ -82,18 +78,20 @@ public class ControllerLifecycleTests {
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityController.pause();
|
||||
activityProxy.getActivity().isDestroying = true;
|
||||
activityProxy.pause();
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityController.stop();
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityController.destroy();
|
||||
activityProxy.stop();
|
||||
|
||||
expectedCallState.saveViewStateCalls++;
|
||||
expectedCallState.detachCalls++;
|
||||
expectedCallState.destroyViewCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityProxy.destroy();
|
||||
|
||||
expectedCallState.destroyCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
@@ -112,33 +110,32 @@ public class ControllerLifecycleTests {
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityController.get().isChangingConfigurations = true;
|
||||
activityProxy.getActivity().isChangingConfigurations = true;
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
activityController.saveInstanceState(bundle);
|
||||
activityProxy.saveInstanceState(bundle);
|
||||
|
||||
expectedCallState.saveViewStateCalls++;
|
||||
expectedCallState.saveInstanceStateCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityController.pause();
|
||||
activityProxy.pause();
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityController.stop();
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityController.destroy();
|
||||
activityProxy.stop();
|
||||
expectedCallState.detachCalls++;
|
||||
expectedCallState.destroyViewCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
createActivityController(bundle);
|
||||
activityProxy.destroy();
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
createActivityController(bundle, false);
|
||||
controller = (TestController)router.getControllerWithTag("root");
|
||||
|
||||
expectedCallState.restoreInstanceStateCalls++;
|
||||
expectedCallState.restoreViewStateCalls++;
|
||||
expectedCallState.changeStartCalls++;
|
||||
expectedCallState.changeEndCalls++;
|
||||
expectedCallState.createViewCalls++;
|
||||
|
||||
// Lifecycle listener isn't attached during restore, grab the current views from the controller for this stuff...
|
||||
@@ -147,10 +144,19 @@ public class ControllerLifecycleTests {
|
||||
currentCallState.changeStartCalls = controller.currentCallState.changeStartCalls;
|
||||
currentCallState.changeEndCalls = controller.currentCallState.changeEndCalls;
|
||||
currentCallState.createViewCalls = controller.currentCallState.createViewCalls;
|
||||
currentCallState.attachCalls = controller.currentCallState.attachCalls;
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityController.resume();
|
||||
activityProxy.start().resume();
|
||||
currentCallState.changeEndCalls = controller.currentCallState.changeEndCalls;
|
||||
currentCallState.attachCalls = controller.currentCallState.attachCalls;
|
||||
expectedCallState.changeEndCalls++;
|
||||
expectedCallState.attachCalls++;
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityProxy.resume();
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
|
||||
@@ -167,16 +173,16 @@ public class ControllerLifecycleTests {
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityController.pause();
|
||||
activityProxy.pause();
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
activityController.saveInstanceState(bundle);
|
||||
activityProxy.saveInstanceState(bundle);
|
||||
|
||||
expectedCallState.saveInstanceStateCalls++;
|
||||
expectedCallState.saveViewStateCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityController.resume();
|
||||
activityProxy.resume();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.bluelinelabs.conductor;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.IdRes;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
@@ -13,27 +12,19 @@ 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 ControllerTests {
|
||||
|
||||
private ActivityController<TestActivity> activityController;
|
||||
private ActivityProxy activityProxy;
|
||||
private Router router;
|
||||
|
||||
public void createActivityController(Bundle savedInstanceState) {
|
||||
activityController = Robolectric.buildActivity(TestActivity.class).create(savedInstanceState).start();
|
||||
|
||||
@IdRes int containerId = 4;
|
||||
AttachFakingFrameLayout routerContainer = new AttachFakingFrameLayout(activityController.get());
|
||||
routerContainer.setId(containerId);
|
||||
routerContainer.setAttached(true);
|
||||
|
||||
router = Conductor.attachRouter(activityController.get(), routerContainer, savedInstanceState);
|
||||
activityProxy = new ActivityProxy().create(savedInstanceState).start().resume();
|
||||
router = Conductor.attachRouter(activityProxy.getActivity(), activityProxy.getView(), savedInstanceState);
|
||||
if (!router.hasRootController()) {
|
||||
router.setRoot(RouterTransaction.with(new TestController()));
|
||||
}
|
||||
@@ -53,18 +44,18 @@ public class ControllerTests {
|
||||
Assert.assertNull(controller.getView());
|
||||
View view = controller.inflate(new AttachFakingFrameLayout(router.getActivity()));
|
||||
Assert.assertNotNull(controller.getView());
|
||||
ViewUtils.setAttached(view, true);
|
||||
ViewUtils.reportAttached(view, true);
|
||||
Assert.assertNotNull(controller.getView());
|
||||
ViewUtils.setAttached(view, false);
|
||||
ViewUtils.reportAttached(view, false);
|
||||
Assert.assertNull(controller.getView());
|
||||
|
||||
// Test View getting retained w/ RETAIN_DETACH
|
||||
controller.setRetainViewMode(RetainViewMode.RETAIN_DETACH);
|
||||
view = controller.inflate(new AttachFakingFrameLayout(router.getActivity()));
|
||||
Assert.assertNotNull(controller.getView());
|
||||
ViewUtils.setAttached(view, true);
|
||||
ViewUtils.reportAttached(view, true);
|
||||
Assert.assertNotNull(controller.getView());
|
||||
ViewUtils.setAttached(view, false);
|
||||
ViewUtils.reportAttached(view, false);
|
||||
Assert.assertNotNull(controller.getView());
|
||||
|
||||
// Ensure re-setting RELEASE_DETACH releases
|
||||
@@ -78,7 +69,6 @@ public class ControllerTests {
|
||||
CallState expectedCallState = new CallState(true);
|
||||
|
||||
router.pushController(RouterTransaction.with(controller));
|
||||
ViewUtils.setAttached(controller.getView(), true);
|
||||
|
||||
// Ensure that calling onActivityResult w/o requesting a result doesn't do anything
|
||||
router.onActivityResult(1, Activity.RESULT_OK, null);
|
||||
@@ -103,10 +93,8 @@ public class ControllerTests {
|
||||
TestController child = new TestController();
|
||||
|
||||
router.pushController(RouterTransaction.with(parent));
|
||||
ViewUtils.setAttached(parent.getView(), true);
|
||||
parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.VIEW_ID), null)
|
||||
.setRoot(RouterTransaction.with(child));
|
||||
ViewUtils.setAttached(child.getView(), true);
|
||||
|
||||
CallState childExpectedCallState = new CallState(true);
|
||||
CallState parentExpectedCallState = new CallState(true);
|
||||
@@ -139,7 +127,6 @@ public class ControllerTests {
|
||||
CallState expectedCallState = new CallState(true);
|
||||
|
||||
router.pushController(RouterTransaction.with(controller));
|
||||
ViewUtils.setAttached(controller.getView(), true);
|
||||
|
||||
// Ensure that calling handleRequestedPermission w/o requesting a result doesn't do anything
|
||||
router.onRequestPermissionsResult("anotherId", 1, requestedPermissions, new int[] {1});
|
||||
@@ -163,10 +150,8 @@ public class ControllerTests {
|
||||
TestController child = new TestController();
|
||||
|
||||
router.pushController(RouterTransaction.with(parent));
|
||||
ViewUtils.setAttached(parent.getView(), true);
|
||||
parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.VIEW_ID), null)
|
||||
.setRoot(RouterTransaction.with(child));
|
||||
ViewUtils.setAttached(child.getView(), true);
|
||||
|
||||
CallState childExpectedCallState = new CallState(true);
|
||||
CallState parentExpectedCallState = new CallState(true);
|
||||
@@ -193,7 +178,6 @@ public class ControllerTests {
|
||||
CallState expectedCallState = new CallState(true);
|
||||
|
||||
router.pushController(RouterTransaction.with(controller));
|
||||
ViewUtils.setAttached(controller.getView(), true);
|
||||
|
||||
// Ensure that calling onCreateOptionsMenu w/o declaring that we have one doesn't do anything
|
||||
router.onCreateOptionsMenu(null, null);
|
||||
@@ -230,10 +214,8 @@ public class ControllerTests {
|
||||
TestController child = new TestController();
|
||||
|
||||
router.pushController(RouterTransaction.with(parent));
|
||||
ViewUtils.setAttached(parent.getView(), true);
|
||||
parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.VIEW_ID), null)
|
||||
.setRoot(RouterTransaction.with(child));
|
||||
ViewUtils.setAttached(child.getView(), true);
|
||||
|
||||
CallState childExpectedCallState = new CallState(true);
|
||||
CallState parentExpectedCallState = new CallState(true);
|
||||
@@ -285,6 +267,7 @@ public class ControllerTests {
|
||||
Assert.assertNull(child2.getParentController());
|
||||
|
||||
Router childRouter = parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.VIEW_ID), null);
|
||||
childRouter.setPopsLastView(true);
|
||||
childRouter.setRoot(RouterTransaction.with(child1));
|
||||
|
||||
Assert.assertEquals(1, parent.getChildRouters().size());
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
public class MockChangeHandler extends ControllerChangeHandler {
|
||||
|
||||
private static final String KEY_REMOVES_FROM_VIEW_ON_PUSH = "MockChangeHandler.removesFromViewOnPush";
|
||||
|
||||
static class ChangeHandlerListener {
|
||||
void willStartChange() { }
|
||||
void didAttachOrDetach() { }
|
||||
@@ -14,13 +17,28 @@ public class MockChangeHandler extends ControllerChangeHandler {
|
||||
}
|
||||
|
||||
final ChangeHandlerListener listener;
|
||||
boolean removesFromViewOnPush;
|
||||
|
||||
public MockChangeHandler() {
|
||||
this(new ChangeHandlerListener() { });
|
||||
this(true, null);
|
||||
}
|
||||
|
||||
public MockChangeHandler(boolean removesViewOnPush) {
|
||||
this(removesViewOnPush, null);
|
||||
}
|
||||
|
||||
public MockChangeHandler(@NonNull ChangeHandlerListener listener) {
|
||||
this.listener = listener;
|
||||
this(true, listener);
|
||||
}
|
||||
|
||||
public MockChangeHandler(boolean removesFromViewOnPush, ChangeHandlerListener listener) {
|
||||
this.removesFromViewOnPush = removesFromViewOnPush;
|
||||
|
||||
if (listener == null) {
|
||||
this.listener = new ChangeHandlerListener() { };
|
||||
} else {
|
||||
this.listener = listener;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -29,23 +47,17 @@ public class MockChangeHandler extends ControllerChangeHandler {
|
||||
|
||||
if (isPush) {
|
||||
container.addView(to);
|
||||
ViewUtils.setAttached(to, true);
|
||||
|
||||
listener.didAttachOrDetach();
|
||||
|
||||
if (from != null) {
|
||||
if (removesFromViewOnPush && 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -54,4 +66,20 @@ public class MockChangeHandler extends ControllerChangeHandler {
|
||||
listener.didEndChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removesFromViewOnPush() {
|
||||
return removesFromViewOnPush;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveToBundle(@NonNull Bundle bundle) {
|
||||
super.saveToBundle(bundle);
|
||||
bundle.putBoolean(KEY_REMOVES_FROM_VIEW_ON_PUSH, removesFromViewOnPush);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreFromBundle(@NonNull Bundle bundle) {
|
||||
super.restoreFromBundle(bundle);
|
||||
removesFromViewOnPush = bundle.getBoolean(KEY_REMOVES_FROM_VIEW_ON_PUSH);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,25 @@
|
||||
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 ActivityProxy activityProxy;
|
||||
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);
|
||||
activityProxy = new ActivityProxy().create(savedInstanceState).start().resume();
|
||||
router = Conductor.attachRouter(activityProxy.getActivity(), activityProxy.getView(), savedInstanceState);
|
||||
if (!router.hasRootController()) {
|
||||
router.setRoot(RouterTransaction.with(new TestController()));
|
||||
}
|
||||
@@ -63,7 +54,8 @@ public class ReattachCaseTests {
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
|
||||
rotateDevice();
|
||||
activityProxy.rotate();
|
||||
router.rebindIfNeeded();
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
@@ -102,7 +94,8 @@ public class ReattachCaseTests {
|
||||
Assert.assertFalse(childController.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
|
||||
rotateDevice();
|
||||
activityProxy.rotate();
|
||||
router.rebindIfNeeded();
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertFalse(childController.isAttached());
|
||||
@@ -138,7 +131,8 @@ public class ReattachCaseTests {
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
Assert.assertTrue(childController.isAttached());
|
||||
|
||||
rotateDevice();
|
||||
activityProxy.rotate();
|
||||
router.rebindIfNeeded();
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
@@ -201,7 +195,8 @@ public class ReattachCaseTests {
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
Assert.assertTrue(childController.isAttached());
|
||||
|
||||
rotateDevice();
|
||||
activityProxy.rotate();
|
||||
router.rebindIfNeeded();
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
@@ -232,18 +227,8 @@ public class ReattachCaseTests {
|
||||
}
|
||||
|
||||
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();
|
||||
activityProxy.saveInstanceState(new Bundle()).pause();
|
||||
activityProxy.resume();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.app.Activity;
|
||||
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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(manifest = Config.NONE)
|
||||
public class RouterTests {
|
||||
@@ -19,8 +18,8 @@ public class RouterTests {
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
Activity activity = Robolectric.buildActivity(TestActivity.class).create().get();
|
||||
router = Conductor.attachRouter(activity, new FrameLayout(activity), null);
|
||||
ActivityProxy activityProxy = new ActivityProxy().create(null).start().resume();
|
||||
router = Conductor.attachRouter(activityProxy.getActivity(), activityProxy.getView(), null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -174,4 +173,149 @@ public class RouterTests {
|
||||
Assert.assertEquals(controller3, router.getControllerWithTag(controller3Tag));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetBackstack() {
|
||||
RouterTransaction rootTransaction = RouterTransaction.with(new TestController());
|
||||
RouterTransaction middleTransaction = RouterTransaction.with(new TestController());
|
||||
RouterTransaction topTransaction = RouterTransaction.with(new TestController());
|
||||
|
||||
List<RouterTransaction> backstack = new ArrayList<>();
|
||||
backstack.add(rootTransaction);
|
||||
backstack.add(middleTransaction);
|
||||
backstack.add(topTransaction);
|
||||
|
||||
router.setBackstack(backstack, null);
|
||||
|
||||
Assert.assertEquals(3, router.getBackstackSize());
|
||||
|
||||
List<RouterTransaction> fetchedBackstack = router.getBackstack();
|
||||
Assert.assertEquals(rootTransaction, fetchedBackstack.get(0));
|
||||
Assert.assertEquals(middleTransaction, fetchedBackstack.get(1));
|
||||
Assert.assertEquals(topTransaction, fetchedBackstack.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNewSetBackstack() {
|
||||
router.setRoot(RouterTransaction.with(new TestController()));
|
||||
|
||||
Assert.assertEquals(1, router.getBackstackSize());
|
||||
|
||||
RouterTransaction rootTransaction = RouterTransaction.with(new TestController());
|
||||
RouterTransaction middleTransaction = RouterTransaction.with(new TestController());
|
||||
RouterTransaction topTransaction = RouterTransaction.with(new TestController());
|
||||
|
||||
List<RouterTransaction> backstack = new ArrayList<>();
|
||||
backstack.add(rootTransaction);
|
||||
backstack.add(middleTransaction);
|
||||
backstack.add(topTransaction);
|
||||
|
||||
router.setBackstack(backstack, null);
|
||||
|
||||
Assert.assertEquals(3, router.getBackstackSize());
|
||||
|
||||
List<RouterTransaction> fetchedBackstack = router.getBackstack();
|
||||
Assert.assertEquals(rootTransaction, fetchedBackstack.get(0));
|
||||
Assert.assertEquals(middleTransaction, fetchedBackstack.get(1));
|
||||
Assert.assertEquals(topTransaction, fetchedBackstack.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNewSetBackstackWithNoRemoveViewOnPush() {
|
||||
RouterTransaction oldRootTransaction = RouterTransaction.with(new TestController());
|
||||
RouterTransaction oldTopTransaction = RouterTransaction.with(new TestController()).pushChangeHandler(new MockChangeHandler(false));
|
||||
|
||||
router.setRoot(oldRootTransaction);
|
||||
router.pushController(oldTopTransaction);
|
||||
Assert.assertEquals(2, router.getBackstackSize());
|
||||
|
||||
Assert.assertTrue(oldRootTransaction.controller.isAttached());
|
||||
Assert.assertTrue(oldTopTransaction.controller.isAttached());
|
||||
|
||||
RouterTransaction rootTransaction = RouterTransaction.with(new TestController());
|
||||
RouterTransaction middleTransaction = RouterTransaction.with(new TestController()).pushChangeHandler(new MockChangeHandler(false));
|
||||
RouterTransaction topTransaction = RouterTransaction.with(new TestController()).pushChangeHandler(new MockChangeHandler(false));
|
||||
|
||||
List<RouterTransaction> backstack = new ArrayList<>();
|
||||
backstack.add(rootTransaction);
|
||||
backstack.add(middleTransaction);
|
||||
backstack.add(topTransaction);
|
||||
|
||||
router.setBackstack(backstack, null);
|
||||
|
||||
Assert.assertEquals(3, router.getBackstackSize());
|
||||
|
||||
List<RouterTransaction> fetchedBackstack = router.getBackstack();
|
||||
Assert.assertEquals(rootTransaction, fetchedBackstack.get(0));
|
||||
Assert.assertEquals(middleTransaction, fetchedBackstack.get(1));
|
||||
Assert.assertEquals(topTransaction, fetchedBackstack.get(2));
|
||||
|
||||
Assert.assertFalse(oldRootTransaction.controller.isAttached());
|
||||
Assert.assertFalse(oldTopTransaction.controller.isAttached());
|
||||
Assert.assertTrue(rootTransaction.controller.isAttached());
|
||||
Assert.assertTrue(middleTransaction.controller.isAttached());
|
||||
Assert.assertTrue(topTransaction.controller.isAttached());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplaceTopController() {
|
||||
RouterTransaction rootTransaction = RouterTransaction.with(new TestController());
|
||||
RouterTransaction topTransaction = RouterTransaction.with(new TestController());
|
||||
|
||||
List<RouterTransaction> backstack = new ArrayList<>();
|
||||
backstack.add(rootTransaction);
|
||||
backstack.add(topTransaction);
|
||||
|
||||
router.setBackstack(backstack, null);
|
||||
|
||||
Assert.assertEquals(2, router.getBackstackSize());
|
||||
|
||||
List<RouterTransaction> fetchedBackstack = router.getBackstack();
|
||||
Assert.assertEquals(rootTransaction, fetchedBackstack.get(0));
|
||||
Assert.assertEquals(topTransaction, fetchedBackstack.get(1));
|
||||
|
||||
RouterTransaction newTopTransaction = RouterTransaction.with(new TestController());
|
||||
router.replaceTopController(newTopTransaction);
|
||||
|
||||
Assert.assertEquals(2, router.getBackstackSize());
|
||||
|
||||
fetchedBackstack = router.getBackstack();
|
||||
Assert.assertEquals(rootTransaction, fetchedBackstack.get(0));
|
||||
Assert.assertEquals(newTopTransaction, fetchedBackstack.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplaceTopControllerWithNoRemoveViewOnPush() {
|
||||
RouterTransaction rootTransaction = RouterTransaction.with(new TestController());
|
||||
RouterTransaction topTransaction = RouterTransaction.with(new TestController()).pushChangeHandler(new MockChangeHandler(false));
|
||||
|
||||
List<RouterTransaction> backstack = new ArrayList<>();
|
||||
backstack.add(rootTransaction);
|
||||
backstack.add(topTransaction);
|
||||
|
||||
router.setBackstack(backstack, null);
|
||||
|
||||
Assert.assertEquals(2, router.getBackstackSize());
|
||||
|
||||
Assert.assertTrue(rootTransaction.controller.isAttached());
|
||||
Assert.assertTrue(topTransaction.controller.isAttached());
|
||||
|
||||
List<RouterTransaction> fetchedBackstack = router.getBackstack();
|
||||
Assert.assertEquals(rootTransaction, fetchedBackstack.get(0));
|
||||
Assert.assertEquals(topTransaction, fetchedBackstack.get(1));
|
||||
|
||||
RouterTransaction newTopTransaction = RouterTransaction.with(new TestController()).pushChangeHandler(new MockChangeHandler(false));
|
||||
router.replaceTopController(newTopTransaction);
|
||||
newTopTransaction.pushChangeHandler().completeImmediately();
|
||||
|
||||
Assert.assertEquals(2, router.getBackstackSize());
|
||||
|
||||
fetchedBackstack = router.getBackstack();
|
||||
Assert.assertEquals(rootTransaction, fetchedBackstack.get(0));
|
||||
Assert.assertEquals(newTopTransaction, fetchedBackstack.get(1));
|
||||
|
||||
Assert.assertTrue(rootTransaction.controller.isAttached());
|
||||
Assert.assertFalse(topTransaction.controller.isAttached());
|
||||
Assert.assertTrue(newTopTransaction.controller.isAttached());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,34 +1,24 @@
|
||||
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);
|
||||
ActivityProxy activityProxy = new ActivityProxy().create(savedInstanceState).start().resume();
|
||||
router = Conductor.attachRouter(activityProxy.getActivity(), activityProxy.getView(), savedInstanceState);
|
||||
if (!router.hasRootController()) {
|
||||
router.setRoot(RouterTransaction.with(new TestController()));
|
||||
}
|
||||
|
||||
@@ -5,10 +5,15 @@ import android.app.Activity;
|
||||
public class TestActivity extends Activity {
|
||||
|
||||
public boolean isChangingConfigurations = false;
|
||||
public boolean isDestroying = false;
|
||||
|
||||
@Override
|
||||
public boolean isChangingConfigurations() {
|
||||
return isChangingConfigurations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDestroyed() {
|
||||
return isDestroying || super.isDestroyed();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,13 +9,26 @@ import java.util.List;
|
||||
|
||||
public class ViewUtils {
|
||||
|
||||
public static void setAttached(View view, boolean attached) {
|
||||
public static void reportAttached(View view, boolean attached) {
|
||||
if (view instanceof AttachFakingFrameLayout) {
|
||||
((AttachFakingFrameLayout)view).setAttached(attached);
|
||||
((AttachFakingFrameLayout)view).setAttached(attached, false);
|
||||
}
|
||||
|
||||
Object listenerInfo = ReflectionHelpers.callInstanceMethod(view, "getListenerInfo");
|
||||
List<OnAttachStateChangeListener> listeners = ReflectionHelpers.getField(listenerInfo, "mOnAttachStateChangeListeners");
|
||||
List<OnAttachStateChangeListener> listeners = getAttachStateListeners(view);
|
||||
|
||||
// Add, then remove an OnAttachStateChangeListener to initialize the attachStateListeners variable inside a view
|
||||
if (listeners == null) {
|
||||
OnAttachStateChangeListener tmpListener = new OnAttachStateChangeListener() {
|
||||
@Override
|
||||
public void onViewAttachedToWindow(View v) { }
|
||||
|
||||
@Override
|
||||
public void onViewDetachedFromWindow(View v) { }
|
||||
};
|
||||
view.addOnAttachStateChangeListener(tmpListener);
|
||||
view.removeOnAttachStateChangeListener(tmpListener);
|
||||
listeners = getAttachStateListeners(view);
|
||||
}
|
||||
|
||||
for (OnAttachStateChangeListener listener : listeners) {
|
||||
if (attached) {
|
||||
@@ -24,6 +37,12 @@ public class ViewUtils {
|
||||
listener.onViewDetachedFromWindow(view);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static List<OnAttachStateChangeListener> getAttachStateListeners(View view) {
|
||||
Object listenerInfo = ReflectionHelpers.callInstanceMethod(view, "getListenerInfo");
|
||||
return ReflectionHelpers.getField(listenerInfo, "mOnAttachStateChangeListeners");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user