This commit is contained in:
Eric Kuck
2016-10-03 14:05:03 -05:00
parent 26efe8f062
commit 3769e706af
13 changed files with 412 additions and 123 deletions
@@ -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");
}
}