Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 90f21d99a5 | |||
| 641e0dc43c | |||
| 91c993b005 | |||
| 39ab4723ff | |||
| 3769e706af | |||
| 26efe8f062 | |||
| 8a890644ee | |||
| b2ffa7f7f6 | |||
| 960b931744 | |||
| 47158da05e | |||
| b9c22d267d | |||
| 46e6fac6db |
@@ -20,22 +20,22 @@ Conductor is architecture-agnostic and does not try to force any design decision
|
||||
## Installation
|
||||
|
||||
```gradle
|
||||
compile 'com.bluelinelabs:conductor:2.0.2'
|
||||
compile 'com.bluelinelabs:conductor:2.0.3'
|
||||
|
||||
// If you want the components that go along with
|
||||
// Android's support libraries (currently just a PagerAdapter):
|
||||
compile 'com.bluelinelabs:conductor-support:2.0.2'
|
||||
compile 'com.bluelinelabs:conductor-support:2.0.3'
|
||||
|
||||
// If you want RxJava/RxAndroid lifecycle support:
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:2.0.2'
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:2.0.3'
|
||||
```
|
||||
|
||||
SNAPSHOT:
|
||||
|
||||
```gradle
|
||||
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'
|
||||
compile 'com.bluelinelabs:conductor:2.0.4-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor-support:2.0.4-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:2.0.4-SNAPSHOT'
|
||||
```
|
||||
|
||||
You also have to add the url to the snapshot repository:
|
||||
|
||||
@@ -28,6 +28,7 @@ dependencies {
|
||||
compile rootProject.ext.rxJava
|
||||
compile rootProject.ext.rxAndroid
|
||||
compile rootProject.ext.rxLifecycle
|
||||
compile rootProject.ext.rxLifecycleAndroid
|
||||
|
||||
compile project(':conductor')
|
||||
}
|
||||
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
package com.bluelinelabs.conductor.rxlifecycle;
|
||||
|
||||
import android.support.annotation.CheckResult;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* Interface used for RxController. Can also be used if writing your own Controller component without subclassing RxController.
|
||||
*/
|
||||
public interface ControllerLifecycleProvider {
|
||||
|
||||
/**
|
||||
* @return An observable that will have all {@link com.bluelinelabs.conductor.Controller} lifecycle events
|
||||
*/
|
||||
@NonNull
|
||||
@CheckResult
|
||||
Observable<ControllerEvent> lifecycle();
|
||||
|
||||
/**
|
||||
* Will bind the source until a specific {@link ControllerEvent} occurs.
|
||||
*
|
||||
* @param event The {@link ControllerEvent} that should cause onComplete to be called
|
||||
* @return A {@link rx.Observable.Transformer} that will call onComplete when the event occurs.
|
||||
*/
|
||||
@NonNull
|
||||
@CheckResult
|
||||
<T> Observable.Transformer<T, T> bindUntilEvent(@NonNull ControllerEvent event);
|
||||
|
||||
/**
|
||||
* Will bind the source until the next reasonable {@link ControllerEvent} occurs.
|
||||
* @return A {@link rx.Observable.Transformer} that will call onComplete when the event occurs.
|
||||
*/
|
||||
@NonNull
|
||||
@CheckResult
|
||||
<T> Observable.Transformer<T, T> bindToLifecycle();
|
||||
|
||||
}
|
||||
+5
-3
@@ -5,6 +5,8 @@ import android.support.annotation.CheckResult;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
import com.trello.rxlifecycle.LifecycleProvider;
|
||||
import com.trello.rxlifecycle.LifecycleTransformer;
|
||||
import com.trello.rxlifecycle.RxLifecycle;
|
||||
|
||||
import rx.Observable;
|
||||
@@ -13,7 +15,7 @@ import rx.subjects.BehaviorSubject;
|
||||
/**
|
||||
* A base {@link Controller} that can be used to expose lifecycle events using RxJava
|
||||
*/
|
||||
public abstract class RxController extends Controller implements ControllerLifecycleProvider {
|
||||
public abstract class RxController extends Controller implements LifecycleProvider<ControllerEvent> {
|
||||
|
||||
private final BehaviorSubject<ControllerEvent> lifecycleSubject;
|
||||
|
||||
@@ -36,14 +38,14 @@ public abstract class RxController extends Controller implements ControllerLifec
|
||||
@Override
|
||||
@NonNull
|
||||
@CheckResult
|
||||
public final <T> Observable.Transformer<T, T> bindUntilEvent(@NonNull ControllerEvent event) {
|
||||
public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull ControllerEvent event) {
|
||||
return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
@CheckResult
|
||||
public final <T> Observable.Transformer<T, T> bindToLifecycle() {
|
||||
public final <T> LifecycleTransformer<T> bindToLifecycle() {
|
||||
return RxControllerLifecycle.bindController(lifecycleSubject);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ public class RxControllerLifecycle {
|
||||
|
||||
/**
|
||||
* Binds the given source to a Controller lifecycle. This is the Controller version of
|
||||
* {@link com.trello.rxlifecycle.RxLifecycle#bindFragment(Observable)}.
|
||||
* {@link com.trello.rxlifecycle.android.RxLifecycleAndroid#bindFragment(Observable)}.
|
||||
*
|
||||
* @param lifecycle the lifecycle sequence of a Controller
|
||||
* @return a reusable {@link Observable.Transformer} that unsubscribes the source during the Controller lifecycle
|
||||
|
||||
@@ -59,7 +59,7 @@ public abstract class Controller {
|
||||
|
||||
private final Bundle args;
|
||||
|
||||
private Bundle viewState;
|
||||
Bundle viewState;
|
||||
private Bundle savedInstanceState;
|
||||
private boolean isBeingDestroyed;
|
||||
private boolean destroyed;
|
||||
@@ -647,6 +647,10 @@ public abstract class Controller {
|
||||
return false;
|
||||
}
|
||||
|
||||
final void setNeedsAttach() {
|
||||
needsAttach = true;
|
||||
}
|
||||
|
||||
final void prepareForHostDetach() {
|
||||
needsAttach = needsAttach || attached;
|
||||
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
@@ -80,6 +83,10 @@ public abstract class ControllerChangeHandler {
|
||||
return bundle;
|
||||
}
|
||||
|
||||
final ControllerChangeHandler copy() {
|
||||
return fromBundle(toBundle());
|
||||
}
|
||||
|
||||
private void ensureDefaultConstructor() {
|
||||
try {
|
||||
getClass().getConstructor();
|
||||
@@ -100,11 +107,14 @@ public abstract class ControllerChangeHandler {
|
||||
}
|
||||
}
|
||||
|
||||
public static void completePushImmediately(String controllerInstanceId) {
|
||||
static boolean completePushImmediately(String controllerInstanceId) {
|
||||
ControllerChangeHandler changeHandler = inProgressPushHandlers.get(controllerInstanceId);
|
||||
if (changeHandler != null) {
|
||||
changeHandler.completeImmediately();
|
||||
inProgressPushHandlers.remove(controllerInstanceId);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void abortPush(Controller toAbort, Controller newController, ControllerChangeHandler newChangeHandler) {
|
||||
@@ -167,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -176,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.
|
||||
*/
|
||||
|
||||
@@ -47,7 +47,8 @@ class ControllerHostedRouter extends Router {
|
||||
removeChangeListener((ControllerChangeListener)container);
|
||||
}
|
||||
|
||||
for (Controller controller : destroyingControllers) {
|
||||
final List<Controller> controllersToDestroy = new ArrayList<>(destroyingControllers);
|
||||
for (Controller controller : controllersToDestroy) {
|
||||
if (controller.getView() != null) {
|
||||
controller.detach(controller.getView(), true);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
/**
|
||||
* A simple controller subclass that changes the onCreateView signature to include a saved view state parameter.
|
||||
* This is necessary for some third party libraries like Google Maps, which require passing in a saved state
|
||||
* bundle at the time of creation.
|
||||
*/
|
||||
abstract public class RestoreViewOnCreateController extends Controller {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected final View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
return onCreateView(inflater, container, viewState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the controller is ready to display its view. A valid view must be returned. The standard body
|
||||
* for this method will be {@code return inflater.inflate(R.layout.my_layout, container, false);}, plus
|
||||
* any binding and state restoration code.
|
||||
*
|
||||
* @param inflater The LayoutInflater that should be used to inflate views
|
||||
* @param container The parent view that this Controller's view will eventually be attached to.
|
||||
* This Controller's view should NOT be added in this method. It is simply passed in
|
||||
* so that valid LayoutParams can be used during inflation.
|
||||
* @param savedViewState A bundle for the view's state, which would have been created in {@link #onSaveViewState(View, Bundle)},
|
||||
* or null if no saved state exists.
|
||||
*/
|
||||
@NonNull
|
||||
protected abstract View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container, @Nullable Bundle savedViewState);
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
@@ -345,17 +358,19 @@ public abstract class Router {
|
||||
ControllerChangeHandler handler = changeHandler != null ? changeHandler : new SimpleSwapChangeHandler();
|
||||
|
||||
Controller rootController = oldVisibleTransactions.size() > 0 ? oldVisibleTransactions.get(0).controller : null;
|
||||
performControllerChange(newVisibleTransactions.get(0).controller, rootController, true, handler);
|
||||
performControllerChange(newVisibleTransactions.get(0).controller, rootController, true, handler.copy());
|
||||
|
||||
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(): new SimpleSwapChangeHandler();
|
||||
performControllerChange(transaction.controller, newVisibleTransactions.get(i - 1).controller, true, handler);
|
||||
handler = transaction.pushChangeHandler() != null ? transaction.pushChangeHandler().copy() : new SimpleSwapChangeHandler();
|
||||
performControllerChange(transaction.controller, newVisibleTransactions.get(i - 1).controller, true, handler.copy());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -481,7 +496,9 @@ public abstract class Router {
|
||||
|
||||
public void prepareForHostDetach() {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
ControllerChangeHandler.completePushImmediately(transaction.controller.getInstanceId());
|
||||
if (ControllerChangeHandler.completePushImmediately(transaction.controller.getInstanceId())) {
|
||||
transaction.controller.setNeedsAttach();
|
||||
}
|
||||
transaction.controller.prepareForHostDetach();
|
||||
}
|
||||
}
|
||||
@@ -615,7 +632,7 @@ public abstract class Router {
|
||||
ControllerChangeHandler.executeChange(to, from, isPush, container, changeHandler, changeListeners);
|
||||
}
|
||||
|
||||
void pushToBackstack(@NonNull RouterTransaction entry) {
|
||||
private void pushToBackstack(@NonNull RouterTransaction entry) {
|
||||
backstack.push(entry);
|
||||
}
|
||||
|
||||
|
||||
+13
-11
@@ -162,18 +162,20 @@ public abstract class AnimatorChangeHandler extends ControllerChangeHandler {
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (from != null && (!isPush || removesFromViewOnPush) && !canceled) {
|
||||
container.removeView(from);
|
||||
if (!canceled && animator != null) {
|
||||
if (from != null && (!isPush || removesFromViewOnPush)) {
|
||||
container.removeView(from);
|
||||
}
|
||||
|
||||
changeListener.onChangeCompleted();
|
||||
animator.removeListener(this);
|
||||
|
||||
if (isPush && from != null) {
|
||||
resetFromView(from);
|
||||
}
|
||||
|
||||
animator = null;
|
||||
}
|
||||
|
||||
changeListener.onChangeCompleted();
|
||||
animator.removeListener(this);
|
||||
|
||||
if (isPush && from != null) {
|
||||
resetFromView(from);
|
||||
}
|
||||
|
||||
animator = null;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+39
-3
@@ -3,6 +3,7 @@ package com.bluelinelabs.conductor.changehandler;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.View;
|
||||
import android.view.View.OnAttachStateChangeListener;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
@@ -11,7 +12,7 @@ import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
/**
|
||||
* A {@link ControllerChangeHandler} that will instantly swap Views with no animations or transitions.
|
||||
*/
|
||||
public class SimpleSwapChangeHandler extends ControllerChangeHandler {
|
||||
public class SimpleSwapChangeHandler extends ControllerChangeHandler implements OnAttachStateChangeListener {
|
||||
|
||||
private static final String KEY_REMOVES_FROM_ON_PUSH = "SimpleSwapChangeHandler.removesFromViewOnPush";
|
||||
|
||||
@@ -19,6 +20,9 @@ public class SimpleSwapChangeHandler extends ControllerChangeHandler {
|
||||
|
||||
private boolean canceled;
|
||||
|
||||
private ViewGroup container;
|
||||
private ControllerChangeCompletedListener changeListener;
|
||||
|
||||
public SimpleSwapChangeHandler() {
|
||||
this(true);
|
||||
}
|
||||
@@ -47,7 +51,18 @@ public class SimpleSwapChangeHandler extends ControllerChangeHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull final ControllerChangeCompletedListener changeListener) {
|
||||
public void completeImmediately() {
|
||||
if (changeListener != null) {
|
||||
changeListener.onChangeCompleted();
|
||||
changeListener = null;
|
||||
|
||||
container.removeOnAttachStateChangeListener(this);
|
||||
container = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
if (!canceled) {
|
||||
if (from != null && (!isPush || removesFromViewOnPush)) {
|
||||
container.removeView(from);
|
||||
@@ -58,11 +73,32 @@ public class SimpleSwapChangeHandler extends ControllerChangeHandler {
|
||||
}
|
||||
}
|
||||
|
||||
changeListener.onChangeCompleted();
|
||||
if (container.getWindowToken() != null) {
|
||||
changeListener.onChangeCompleted();
|
||||
} else {
|
||||
this.changeListener = changeListener;
|
||||
this.container = container;
|
||||
container.addOnAttachStateChangeListener(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removesFromViewOnPush() {
|
||||
return removesFromViewOnPush;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewAttachedToWindow(View v) {
|
||||
v.removeOnAttachStateChangeListener(this);
|
||||
|
||||
if (changeListener != null) {
|
||||
changeListener.onChangeCompleted();
|
||||
changeListener = null;
|
||||
container = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewDetachedFromWindow(View v) { }
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.IBinder;
|
||||
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;
|
||||
|
||||
public class AttachFakingFrameLayout extends FrameLayout {
|
||||
|
||||
final IBinder fakeWindowToken = new IBinder() {
|
||||
@Override
|
||||
public String getInterfaceDescriptor() throws RemoteException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pingBinder() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBinderAlive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IInterface queryLocalInterface(String descriptor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(FileDescriptor fd, String[] args) throws RemoteException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void linkToDeath(DeathRecipient recipient, int flags) throws RemoteException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
private boolean reportAttached;
|
||||
|
||||
public AttachFakingFrameLayout(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public AttachFakingFrameLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public AttachFakingFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final IBinder getWindowToken() {
|
||||
return reportAttached ? fakeWindowToken : null;
|
||||
}
|
||||
|
||||
public void setAttached(boolean 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,11 +1,9 @@
|
||||
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;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller.LifecycleListener;
|
||||
import com.bluelinelabs.conductor.MockChangeHandler.ChangeHandlerListener;
|
||||
@@ -14,28 +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;
|
||||
FrameLayout routerContainer = new FrameLayout(activityController.get());
|
||||
routerContainer.setId(containerId);
|
||||
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,10 +3,8 @@ 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;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller.RetainViewMode;
|
||||
|
||||
@@ -14,26 +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;
|
||||
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()));
|
||||
}
|
||||
@@ -51,20 +42,20 @@ public class ControllerTests {
|
||||
// Test View getting released w/ RELEASE_DETACH
|
||||
controller.setRetainViewMode(RetainViewMode.RELEASE_DETACH);
|
||||
Assert.assertNull(controller.getView());
|
||||
View view = controller.inflate(new FrameLayout(router.getActivity()));
|
||||
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 FrameLayout(router.getActivity()));
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public class TestController extends Controller {
|
||||
@Override
|
||||
protected View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
currentCallState.createViewCalls++;
|
||||
FrameLayout view = new FrameLayout(inflater.getContext());
|
||||
FrameLayout view = new AttachFakingFrameLayout(inflater.getContext());
|
||||
view.setId(VIEW_ID);
|
||||
|
||||
FrameLayout childContainer1 = new FrameLayout(inflater.getContext());
|
||||
|
||||
@@ -9,9 +9,26 @@ import java.util.List;
|
||||
|
||||
public class ViewUtils {
|
||||
|
||||
public static void setAttached(View view, boolean attached) {
|
||||
Object listenerInfo = ReflectionHelpers.callInstanceMethod(view, "getListenerInfo");
|
||||
List<OnAttachStateChangeListener> listeners = ReflectionHelpers.getField(listenerInfo, "mOnAttachStateChangeListeners");
|
||||
public static void reportAttached(View view, boolean attached) {
|
||||
if (view instanceof AttachFakingFrameLayout) {
|
||||
((AttachFakingFrameLayout)view).setAttached(attached, false);
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -20,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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+13
-3
@@ -16,10 +16,13 @@ import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
import com.bluelinelabs.conductor.demo.util.ColorUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ParentController extends BaseController {
|
||||
|
||||
private static final int NUMBER_OF_CHILDREN = 5;
|
||||
private boolean finishing;
|
||||
private boolean hasShownAll;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
@@ -47,8 +50,12 @@ public class ParentController extends BaseController {
|
||||
childController.addLifecycleListener(new LifecycleListener() {
|
||||
@Override
|
||||
public void onChangeEnd(@NonNull Controller controller, @NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
if (changeType == ControllerChangeType.PUSH_ENTER && index < NUMBER_OF_CHILDREN - 1) {
|
||||
addChild(index + 1);
|
||||
if (changeType == ControllerChangeType.PUSH_ENTER && !hasShownAll) {
|
||||
if (index < NUMBER_OF_CHILDREN - 1) {
|
||||
addChild(index + 1);
|
||||
} else {
|
||||
hasShownAll = true;
|
||||
}
|
||||
} else if (changeType == ControllerChangeType.POP_EXIT) {
|
||||
if (index > 0) {
|
||||
removeChild(index - 1);
|
||||
@@ -66,7 +73,10 @@ public class ParentController extends BaseController {
|
||||
}
|
||||
|
||||
private void removeChild(int index) {
|
||||
removeChildRouter(getChildRouters().get(index));
|
||||
List<Router> childRouters = getChildRouters();
|
||||
if (index < childRouters.size()) {
|
||||
removeChildRouter(childRouters.get(index));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
-3
@@ -17,9 +17,10 @@ ext {
|
||||
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'
|
||||
rxLifecycle = 'com.trello:rxlifecycle:0.6.0'
|
||||
rxJava = 'io.reactivex:rxjava:1.2.0'
|
||||
rxAndroid = 'io.reactivex:rxandroid:1.2.1'
|
||||
rxLifecycle = 'com.trello:rxlifecycle:0.8.0'
|
||||
rxLifecycleAndroid = 'com.trello:rxlifecycle-android:0.8.0'
|
||||
|
||||
junit = 'junit:junit:4.11'
|
||||
roboelectric = 'org.robolectric:robolectric:3.0'
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
VERSION_NAME=2.0.3-SNAPSHOT
|
||||
VERSION_NAME=2.0.4-SNAPSHOT
|
||||
VERSION_CODE=2
|
||||
GROUP=com.bluelinelabs
|
||||
|
||||
|
||||
Reference in New Issue
Block a user