Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 769d552e88 | |||
| afa4b69d7a | |||
| a9bdf0dd06 | |||
| 6ffa94ed3a | |||
| 690001ed2a | |||
| 90e015b6b3 | |||
| 44bcd0f977 | |||
| 60d0fabcf4 |
@@ -20,26 +20,26 @@ Conductor is architecture-agnostic and does not try to force any design decision
|
||||
## Installation
|
||||
|
||||
```gradle
|
||||
compile 'com.bluelinelabs:conductor:2.0.7'
|
||||
compile 'com.bluelinelabs:conductor:2.1.0'
|
||||
|
||||
// If you want the components that go along with
|
||||
// Android's support libraries (currently just a PagerAdapter):
|
||||
compile 'com.bluelinelabs:conductor-support:2.0.7'
|
||||
compile 'com.bluelinelabs:conductor-support:2.1.0'
|
||||
|
||||
// If you want RxJava lifecycle support:
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:2.0.7'
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:2.1.0'
|
||||
|
||||
// If you want RxJava2 lifecycle support:
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle2:2.0.7'
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle2:2.1.0'
|
||||
```
|
||||
|
||||
SNAPSHOT:
|
||||
|
||||
```gradle
|
||||
compile 'com.bluelinelabs:conductor:2.0.6-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor-support:2.0.6-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:2.0.6-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle2:2.0.6-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor:2.1.1-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor-support:2.1.1-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:2.1.1-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle2:2.1.1-SNAPSHOT'
|
||||
```
|
||||
|
||||
You also have to add the url to the snapshot repository:
|
||||
|
||||
+14
-4
@@ -13,8 +13,12 @@ import com.bluelinelabs.conductor.Router;
|
||||
import com.bluelinelabs.conductor.RouterTransaction;
|
||||
|
||||
/**
|
||||
* @deprecated Use RouterPagerAdapter instead! This implementation was too limited and had too many
|
||||
* gotchas associated with it.
|
||||
*
|
||||
* An adapter for ViewPagers that will handle adding and removing Controllers
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class ControllerPagerAdapter extends PagerAdapter {
|
||||
|
||||
private static final String KEY_SAVED_PAGES = "ControllerPagerAdapter.savedStates";
|
||||
@@ -53,12 +57,17 @@ public abstract class ControllerPagerAdapter extends PagerAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
final Controller controller;
|
||||
if (!router.hasRootController()) {
|
||||
Controller controller = getItem(position);
|
||||
controller = getItem(position);
|
||||
router.setRoot(RouterTransaction.with(controller).tag(name));
|
||||
visiblePageIds.put(position, controller.getInstanceId());
|
||||
} else {
|
||||
router.rebindIfNeeded();
|
||||
controller = router.getControllerWithTag(name);
|
||||
}
|
||||
|
||||
if (controller != null) {
|
||||
visiblePageIds.put(position, controller.getInstanceId());
|
||||
}
|
||||
|
||||
return router.getControllerWithTag(name);
|
||||
@@ -119,7 +128,8 @@ public abstract class ControllerPagerAdapter extends PagerAdapter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the already instantiated Controller in the specified position, if available.
|
||||
* Returns the already instantiated Controller in the specified position or {@code null} if
|
||||
* this position does not yet have a controller.
|
||||
*/
|
||||
@Nullable
|
||||
public Controller getController(int position) {
|
||||
@@ -139,4 +149,4 @@ public abstract class ControllerPagerAdapter extends PagerAdapter {
|
||||
return viewId + ":" + id;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -101,7 +101,8 @@ public abstract class RouterPagerAdapter extends PagerAdapter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the already instantiated Router in the specified position, if available.
|
||||
* Returns the already instantiated Router in the specified position or {@code null} if there
|
||||
* is no router associated with this position.
|
||||
*/
|
||||
@Nullable
|
||||
public Router getRouter(int position) {
|
||||
|
||||
@@ -9,12 +9,14 @@ import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler.ControllerChangeListener;
|
||||
import com.bluelinelabs.conductor.internal.LifecycleHandler;
|
||||
import com.bluelinelabs.conductor.internal.TransactionIndexer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ActivityHostedRouter extends Router {
|
||||
|
||||
private LifecycleHandler lifecycleHandler;
|
||||
private final TransactionIndexer transactionIndexer = new TransactionIndexer();
|
||||
|
||||
public final void setHost(@NonNull LifecycleHandler lifecycleHandler, @NonNull ViewGroup container) {
|
||||
if (this.lifecycleHandler != lifecycleHandler || this.container != container) {
|
||||
@@ -31,6 +33,20 @@ public class ActivityHostedRouter extends Router {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveInstanceState(@NonNull Bundle outState) {
|
||||
super.saveInstanceState(outState);
|
||||
|
||||
transactionIndexer.saveInstanceState(outState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreInstanceState(@NonNull Bundle savedInstanceState) {
|
||||
super.restoreInstanceState(savedInstanceState);
|
||||
|
||||
transactionIndexer.restoreInstanceState(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override @Nullable
|
||||
public Activity getActivity() {
|
||||
return lifecycleHandler != null ? lifecycleHandler.getLifecycleActivity() : null;
|
||||
@@ -98,4 +114,9 @@ public class ActivityHostedRouter extends Router {
|
||||
Router getRootRouter() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override @Nullable
|
||||
TransactionIndexer getTransactionIndexer() {
|
||||
return transactionIndexer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.Router.OnControllerPushedListener;
|
||||
import com.bluelinelabs.conductor.internal.ClassUtils;
|
||||
import com.bluelinelabs.conductor.internal.RouterRequiringFunc;
|
||||
import com.bluelinelabs.conductor.internal.ViewAttachHandler;
|
||||
@@ -30,7 +29,8 @@ import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -45,7 +45,6 @@ public abstract class Controller {
|
||||
private static final String KEY_CLASS_NAME = "Controller.className";
|
||||
private static final String KEY_VIEW_STATE = "Controller.viewState";
|
||||
private static final String KEY_CHILD_ROUTERS = "Controller.childRouters";
|
||||
private static final String KEY_CHILD_BACKSTACK = "Controller.childBackstack";
|
||||
private static final String KEY_SAVED_STATE = "Controller.savedState";
|
||||
private static final String KEY_INSTANCE_ID = "Controller.instanceId";
|
||||
private static final String KEY_TARGET_INSTANCE_ID = "Controller.target.instanceId";
|
||||
@@ -86,16 +85,8 @@ public abstract class Controller {
|
||||
private final List<LifecycleListener> lifecycleListeners = new ArrayList<>();
|
||||
private final ArrayList<String> requestedPermissions = new ArrayList<>();
|
||||
private final ArrayList<RouterRequiringFunc> onRouterSetListeners = new ArrayList<>();
|
||||
private final List<Controller> childBackstack = new LinkedList<>();
|
||||
private WeakReference<View> destroyedView;
|
||||
|
||||
private final OnControllerPushedListener onControllerPushedListener = new OnControllerPushedListener() {
|
||||
@Override
|
||||
public void onControllerPushed(Controller controller) {
|
||||
onChildControllerPushed(controller);
|
||||
}
|
||||
};
|
||||
|
||||
@NonNull
|
||||
static Controller newInstance(@NonNull Bundle bundle) {
|
||||
final String className = bundle.getString(KEY_CLASS_NAME);
|
||||
@@ -183,7 +174,7 @@ public abstract class Controller {
|
||||
* the same container unless you have a great reason to do so (ex: ViewPagers).
|
||||
*
|
||||
* @param container The ViewGroup that hosts the child Router
|
||||
* @param tag The router's tag
|
||||
* @param tag The router's tag or {@code null} if none is needed
|
||||
*/
|
||||
@NonNull
|
||||
public final Router getChildRouter(@NonNull ViewGroup container, @Nullable String tag) {
|
||||
@@ -195,10 +186,12 @@ public abstract class Controller {
|
||||
* Retrieves the child {@link Router} for the given container/tag combination. Note that multiple
|
||||
* routers should not exist in the same container unless a lot of care is taken to maintain order
|
||||
* between them. Avoid using the same container unless you have a great reason to do so (ex: ViewPagers).
|
||||
* The only time this method will return {@code null} is when the child router does not exist prior
|
||||
* to calling this method and the createIfNeeded parameter is set to false.
|
||||
*
|
||||
* @param container The ViewGroup that hosts the child Router
|
||||
* @param tag The router's tag
|
||||
* @param createIfNeeded If true, a router will be created if one does not yet exist. Else false will be returned in this case.
|
||||
* @param tag The router's tag or {@code null} if none is needed
|
||||
* @param createIfNeeded If true, a router will be created if one does not yet exist. Else {@code null} will be returned in this case.
|
||||
*/
|
||||
@Nullable
|
||||
public final Router getChildRouter(@NonNull ViewGroup container, @Nullable String tag, boolean createIfNeeded) {
|
||||
@@ -215,19 +208,23 @@ public abstract class Controller {
|
||||
if (childRouter == null) {
|
||||
if (createIfNeeded) {
|
||||
childRouter = new ControllerHostedRouter(container.getId(), tag);
|
||||
monitorChildRouter(childRouter);
|
||||
childRouter.setHost(this, container);
|
||||
childRouters.add(childRouter);
|
||||
}
|
||||
} else if (!childRouter.hasHost()) {
|
||||
childRouter.setHost(this, container);
|
||||
monitorChildRouter(childRouter);
|
||||
childRouter.rebindIfNeeded();
|
||||
}
|
||||
|
||||
return childRouter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a child {@link Router} from this Controller. When removed, all Controllers currently managed by
|
||||
* the {@link Router} will be destroyed.
|
||||
*
|
||||
* @param childRouter The router to be removed
|
||||
*/
|
||||
public final void removeChildRouter(@NonNull Router childRouter) {
|
||||
if ((childRouter instanceof ControllerHostedRouter) && childRouters.remove(childRouter)) {
|
||||
childRouter.destroy(true);
|
||||
@@ -256,7 +253,8 @@ public abstract class Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this Controller's View, if available.
|
||||
* Return this Controller's View or {@code null} if it has not yet been created or has been
|
||||
* destroyed.
|
||||
*/
|
||||
@Nullable
|
||||
public final View getView() {
|
||||
@@ -264,15 +262,17 @@ public abstract class Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the host Activity of this Controller's {@link Router}
|
||||
* Returns the host Activity of this Controller's {@link Router} or {@code null} if this
|
||||
* Controller has not yet been attached to an Activity or if the Activity has been destroyed.
|
||||
*/
|
||||
@Nullable
|
||||
public final Activity getActivity() {
|
||||
return router.getActivity();
|
||||
return router != null ? router.getActivity() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Resources from the host Activity
|
||||
* Returns the Resources from the host Activity or {@code null} if this Controller has not
|
||||
* yet been attached to an Activity or if the Activity has been destroyed.
|
||||
*/
|
||||
@Nullable
|
||||
public final Resources getResources() {
|
||||
@@ -281,7 +281,8 @@ public abstract class Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Application Context derived from the host Activity
|
||||
* Returns the Application Context derived from the host Activity or {@code null} if this Controller
|
||||
* has not yet been attached to an Activity or if the Activity has been destroyed.
|
||||
*/
|
||||
@Nullable
|
||||
public final Context getApplicationContext() {
|
||||
@@ -290,7 +291,8 @@ public abstract class Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this Controller's parent Controller if it is a child Controller.
|
||||
* Returns this Controller's parent Controller if it is a child Controller or {@code null} if
|
||||
* it has no parent.
|
||||
*/
|
||||
@Nullable
|
||||
public final Controller getParentController() {
|
||||
@@ -307,10 +309,10 @@ public abstract class Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Controller with the given instance id, if available.
|
||||
* May return the controller itself or a matching descendant
|
||||
* Returns the Controller with the given instance id or {@code null} if no such Controller
|
||||
* exists. May return the Controller itself or a matching descendant
|
||||
*
|
||||
* @param instanceId The instance ID being searched for
|
||||
* @return The matching Controller, if one exists
|
||||
*/
|
||||
@Nullable
|
||||
final Controller findController(@NonNull String instanceId) {
|
||||
@@ -355,7 +357,8 @@ public abstract class Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the target Controller that was set with the {@link #setTargetController(Controller)} method
|
||||
* Returns the target Controller that was set with the {@link #setTargetController(Controller)}
|
||||
* method or {@code null} if this Controller has no target.
|
||||
*
|
||||
* @return This Controller's target
|
||||
*/
|
||||
@@ -552,8 +555,22 @@ public abstract class Controller {
|
||||
* @return True if this Controller has consumed the back button press, otherwise false
|
||||
*/
|
||||
public boolean handleBack() {
|
||||
for (int i = childBackstack.size() - 1; i >= 0; i--) {
|
||||
Controller childController = childBackstack.get(i);
|
||||
List<RouterTransaction> childTransactions = new ArrayList<>();
|
||||
|
||||
for (ControllerHostedRouter childRouter : childRouters) {
|
||||
childTransactions.addAll(childRouter.getBackstack());
|
||||
}
|
||||
|
||||
Collections.sort(childTransactions, new Comparator<RouterTransaction>() {
|
||||
@Override
|
||||
public int compare(RouterTransaction o1, RouterTransaction o2) {
|
||||
return o2.transactionIndex - o1.transactionIndex;
|
||||
}
|
||||
});
|
||||
|
||||
for (RouterTransaction transaction : childTransactions) {
|
||||
Controller childController = transaction.controller;
|
||||
|
||||
if (childController.isAttached() && childController.getRouter().handleBack()) {
|
||||
return true;
|
||||
}
|
||||
@@ -744,6 +761,10 @@ public abstract class Controller {
|
||||
}
|
||||
|
||||
final void activityStarted(@NonNull Activity activity) {
|
||||
if (viewAttachHandler != null) {
|
||||
viewAttachHandler.onActivityStarted();
|
||||
}
|
||||
|
||||
onActivityStarted(activity);
|
||||
}
|
||||
|
||||
@@ -763,12 +784,15 @@ public abstract class Controller {
|
||||
}
|
||||
|
||||
final void activityStopped(@NonNull Activity activity) {
|
||||
if (viewAttachHandler != null) {
|
||||
viewAttachHandler.onActivityStopped();
|
||||
}
|
||||
onActivityStopped(activity);
|
||||
}
|
||||
|
||||
final void activityDestroyed(boolean isChangingConfigurations) {
|
||||
if (isChangingConfigurations) {
|
||||
detach(view, true);
|
||||
detach(view, true, false);
|
||||
} else {
|
||||
destroy(true);
|
||||
}
|
||||
@@ -802,14 +826,14 @@ public abstract class Controller {
|
||||
}
|
||||
}
|
||||
|
||||
void detach(@NonNull View view, boolean forceViewRefRemoval) {
|
||||
void detach(@NonNull View view, boolean forceViewRefRemoval, boolean blockViewRefRemoval) {
|
||||
if (!attachedToUnownedParent) {
|
||||
for (ControllerHostedRouter router : childRouters) {
|
||||
router.prepareForHostDetach();
|
||||
}
|
||||
}
|
||||
|
||||
final boolean removeViewRef = forceViewRefRemoval || retainViewMode == RetainViewMode.RELEASE_DETACH || isBeingDestroyed;
|
||||
final boolean removeViewRef = !blockViewRefRemoval && (forceViewRefRemoval || retainViewMode == RetainViewMode.RELEASE_DETACH || isBeingDestroyed);
|
||||
|
||||
if (attached) {
|
||||
List<LifecycleListener> listeners = new ArrayList<>(lifecycleListeners);
|
||||
@@ -874,7 +898,7 @@ public abstract class Controller {
|
||||
|
||||
final View inflate(@NonNull ViewGroup parent) {
|
||||
if (view != null && view.getParent() != null && view.getParent() != parent) {
|
||||
detach(view, true);
|
||||
detach(view, true, false);
|
||||
removeViewReference();
|
||||
}
|
||||
|
||||
@@ -898,21 +922,26 @@ public abstract class Controller {
|
||||
|
||||
viewAttachHandler = new ViewAttachHandler(new ViewAttachListener() {
|
||||
@Override
|
||||
public void onAttached(View v) {
|
||||
if (v == view) {
|
||||
viewIsAttached = true;
|
||||
viewWasDetached = false;
|
||||
}
|
||||
attach(v);
|
||||
public void onAttached() {
|
||||
viewIsAttached = true;
|
||||
viewWasDetached = false;
|
||||
attach(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetached(View v) {
|
||||
public void onDetached(boolean fromActivityStop) {
|
||||
viewIsAttached = false;
|
||||
viewWasDetached = true;
|
||||
|
||||
if (!isDetachFrozen) {
|
||||
detach(v, false);
|
||||
detach(view, false, fromActivityStop);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewDetachAfterStop() {
|
||||
if (!isDetachFrozen) {
|
||||
detach(view, false, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -931,7 +960,6 @@ public abstract class Controller {
|
||||
|
||||
if (containerView != null && containerView instanceof ViewGroup) {
|
||||
childRouter.setHost(this, (ViewGroup)containerView);
|
||||
monitorChildRouter(childRouter);
|
||||
childRouter.rebindIfNeeded();
|
||||
}
|
||||
}
|
||||
@@ -976,7 +1004,7 @@ public abstract class Controller {
|
||||
if (!attached) {
|
||||
removeViewReference();
|
||||
} else if (removeViews) {
|
||||
detach(view, true);
|
||||
detach(view, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1043,12 +1071,6 @@ public abstract class Controller {
|
||||
}
|
||||
outState.putParcelableArrayList(KEY_CHILD_ROUTERS, childBundles);
|
||||
|
||||
ArrayList<String> childBackstack = new ArrayList<>();
|
||||
for (Controller controller : this.childBackstack) {
|
||||
childBackstack.add(controller.instanceId);
|
||||
}
|
||||
outState.putStringArrayList(KEY_CHILD_BACKSTACK, childBackstack);
|
||||
|
||||
Bundle savedState = new Bundle();
|
||||
onSaveInstanceState(savedState);
|
||||
|
||||
@@ -1080,18 +1102,9 @@ public abstract class Controller {
|
||||
for (Bundle childBundle : childBundles) {
|
||||
ControllerHostedRouter childRouter = new ControllerHostedRouter();
|
||||
childRouter.restoreInstanceState(childBundle);
|
||||
monitorChildRouter(childRouter);
|
||||
childRouters.add(childRouter);
|
||||
}
|
||||
|
||||
List<String> childBackstackIds = savedInstanceState.getStringArrayList(KEY_CHILD_BACKSTACK);
|
||||
for (String controllerId : childBackstackIds) {
|
||||
Controller childController = findController(controllerId);
|
||||
if (childController != null) {
|
||||
childBackstack.add(childController);
|
||||
}
|
||||
}
|
||||
|
||||
this.savedInstanceState = savedInstanceState.getBundle(KEY_SAVED_STATE);
|
||||
performOnRestoreInstanceState();
|
||||
}
|
||||
@@ -1156,7 +1169,7 @@ public abstract class Controller {
|
||||
}
|
||||
|
||||
if (!frozen && view != null && viewWasDetached) {
|
||||
detach(view, false);
|
||||
detach(view, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1177,22 +1190,6 @@ public abstract class Controller {
|
||||
return attached && hasOptionsMenu && !optionsMenuHidden && onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void monitorChildRouter(@NonNull ControllerHostedRouter childRouter) {
|
||||
childRouter.setOnControllerPushedListener(onControllerPushedListener);
|
||||
}
|
||||
|
||||
private void onChildControllerPushed(@NonNull Controller controller) {
|
||||
if (!childBackstack.contains(controller)) {
|
||||
childBackstack.add(controller);
|
||||
controller.addLifecycleListener(new LifecycleListener() {
|
||||
@Override
|
||||
public void postDestroy(@NonNull Controller controller) {
|
||||
childBackstack.remove(controller);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
final void setParentController(@Nullable Controller controller) {
|
||||
parentController = controller;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import android.view.ViewParent;
|
||||
import com.bluelinelabs.conductor.changehandler.SimpleSwapChangeHandler;
|
||||
import com.bluelinelabs.conductor.internal.ClassUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -34,8 +33,8 @@ public abstract class ControllerChangeHandler {
|
||||
* Responsible for swapping Views from one Controller to another.
|
||||
*
|
||||
* @param container The container these Views are hosted in.
|
||||
* @param from The previous View in the container, if any.
|
||||
* @param to The next View that should be put in the container, if any.
|
||||
* @param from The previous View in the container or {@code null} if there was no Controller before this transition
|
||||
* @param to The next View that should be put in the container or {@code null} if no Controller is being transitioned to
|
||||
* @param isPush True if this is a push transaction, false if it's a pop.
|
||||
* @param changeListener This listener must be called when any transitions or animations are completed.
|
||||
*/
|
||||
@@ -63,8 +62,9 @@ public abstract class ControllerChangeHandler {
|
||||
* Will be called on change handlers that push a controller if the controller being pushed is
|
||||
* popped before it has completed.
|
||||
*
|
||||
* @param newHandler the change handler that has caused this push to be aborted
|
||||
* @param newTop the controller that will now be at the top of the backstack
|
||||
* @param newHandler The change handler that has caused this push to be aborted
|
||||
* @param newTop The Controller that will now be at the top of the backstack or {@code null}
|
||||
* if there will be no new Controller at the top
|
||||
*/
|
||||
public void onAbortPush(@NonNull ControllerChangeHandler newHandler, @Nullable Controller newTop) { }
|
||||
|
||||
@@ -137,7 +137,7 @@ public abstract class ControllerChangeHandler {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void abortPush(@NonNull Controller toAbort, @Nullable Controller newController, @NonNull ControllerChangeHandler newChangeHandler) {
|
||||
static void abortPush(@NonNull Controller toAbort, @Nullable Controller newController, @NonNull ControllerChangeHandler newChangeHandler) {
|
||||
ControllerChangeHandler handlerForPush = inProgressPushHandlers.get(toAbort.getInstanceId());
|
||||
if (handlerForPush != null) {
|
||||
handlerForPush.onAbortPush(newChangeHandler, newController);
|
||||
@@ -145,11 +145,7 @@ public abstract class ControllerChangeHandler {
|
||||
}
|
||||
}
|
||||
|
||||
public static void executeChange(@Nullable final Controller to, @Nullable final Controller from, boolean isPush, @NonNull ViewGroup container, @NonNull ControllerChangeHandler inHandler) {
|
||||
executeChange(to, from, isPush, container, inHandler, new ArrayList<ControllerChangeListener>());
|
||||
}
|
||||
|
||||
public static void executeChange(@Nullable final Controller to, @Nullable final Controller from, final boolean isPush, @Nullable final ViewGroup container, @Nullable final ControllerChangeHandler inHandler, @NonNull final List<ControllerChangeListener> listeners) {
|
||||
static void executeChange(@Nullable final Controller to, @Nullable final Controller from, final boolean isPush, @Nullable final ViewGroup container, @Nullable final ControllerChangeHandler inHandler, @NonNull final List<ControllerChangeListener> listeners) {
|
||||
if (isPush && to != null && to.isDestroyed()) {
|
||||
throw new IllegalStateException("Trying to push a controller that has already been destroyed. (" + to.getClass().getSimpleName() + ")");
|
||||
}
|
||||
@@ -240,8 +236,8 @@ public abstract class ControllerChangeHandler {
|
||||
/**
|
||||
* Called when a {@link ControllerChangeHandler} has started changing {@link Controller}s
|
||||
*
|
||||
* @param to The new Controller
|
||||
* @param from The old Controller
|
||||
* @param to The new Controller or {@code null} if no Controller is being transitioned to
|
||||
* @param from The old Controller or {@code null} if there was no Controller before this transition
|
||||
* @param isPush True if this is a push operation, or false if it's a pop.
|
||||
* @param container The containing ViewGroup
|
||||
* @param handler The change handler being used.
|
||||
@@ -251,9 +247,9 @@ public abstract class ControllerChangeHandler {
|
||||
/**
|
||||
* Called when a {@link ControllerChangeHandler} has completed changing {@link Controller}s
|
||||
*
|
||||
* @param to The new Controller
|
||||
* @param from The old Controller
|
||||
* @param isPush True if this was a push operation, or false if it's a pop.
|
||||
* @param to The new Controller or {@code null} if no Controller is being transitioned to
|
||||
* @param from The old Controller or {@code null} if there was no Controller before this transition
|
||||
* @param isPush True if this was a push operation, or false if it's a pop
|
||||
* @param container The containing ViewGroup
|
||||
* @param handler The change handler that was used.
|
||||
*/
|
||||
|
||||
@@ -9,6 +9,7 @@ import android.support.annotation.Nullable;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler.ControllerChangeListener;
|
||||
import com.bluelinelabs.conductor.internal.TransactionIndexer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -51,12 +52,12 @@ class ControllerHostedRouter extends Router {
|
||||
final List<Controller> controllersToDestroy = new ArrayList<>(destroyingControllers);
|
||||
for (Controller controller : controllersToDestroy) {
|
||||
if (controller.getView() != null) {
|
||||
controller.detach(controller.getView(), true);
|
||||
controller.detach(controller.getView(), true, false);
|
||||
}
|
||||
}
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
if (transaction.controller.getView() != null) {
|
||||
transaction.controller.detach(transaction.controller.getView(), true);
|
||||
transaction.controller.detach(transaction.controller.getView(), true, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,4 +198,9 @@ class ControllerHostedRouter extends Router {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@Override @Nullable
|
||||
TransactionIndexer getTransactionIndexer() {
|
||||
return getRootRouter().getTransactionIndexer();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ abstract public class RestoreViewOnCreateController extends Controller {
|
||||
* 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.
|
||||
* or {@code null} if no saved state exists.
|
||||
*/
|
||||
@NonNull
|
||||
protected abstract View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container, @Nullable Bundle savedViewState);
|
||||
|
||||
@@ -16,6 +16,7 @@ import com.bluelinelabs.conductor.Controller.LifecycleListener;
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler.ControllerChangeListener;
|
||||
import com.bluelinelabs.conductor.changehandler.SimpleSwapChangeHandler;
|
||||
import com.bluelinelabs.conductor.internal.NoOpControllerChangeHandler;
|
||||
import com.bluelinelabs.conductor.internal.TransactionIndexer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -33,7 +34,6 @@ public abstract class Router {
|
||||
private static final String KEY_POPS_LAST_VIEW = "Router.popsLastView";
|
||||
|
||||
protected final Backstack backstack = new Backstack();
|
||||
private OnControllerPushedListener onControllerPushedListener;
|
||||
private final List<ControllerChangeListener> changeListeners = new ArrayList<>();
|
||||
final List<Controller> destroyingControllers = new ArrayList<>();
|
||||
|
||||
@@ -42,7 +42,8 @@ public abstract class Router {
|
||||
ViewGroup container;
|
||||
|
||||
/**
|
||||
* Returns this Router's host Activity
|
||||
* Returns this Router's host Activity or {@code null} if it has either not yet been attached to
|
||||
* an Activity or if the Activity has been destroyed.
|
||||
*/
|
||||
@Nullable
|
||||
public abstract Activity getActivity();
|
||||
@@ -52,8 +53,8 @@ public abstract class Router {
|
||||
* of the controller that called startActivityForResult is not known.
|
||||
*
|
||||
* @param requestCode The Activity's onActivityResult requestCode
|
||||
* @param resultCode The Activity's onActivityResult resultCode
|
||||
* @param data The Activity's onActivityResult data
|
||||
* @param resultCode The Activity's onActivityResult resultCode
|
||||
* @param data The Activity's onActivityResult data
|
||||
*/
|
||||
public abstract void onActivityResult(int requestCode, int resultCode, @Nullable Intent data);
|
||||
|
||||
@@ -61,9 +62,9 @@ public abstract class Router {
|
||||
* This should be called by the host Activity when its onRequestPermissionsResult method is called. The call will be forwarded
|
||||
* to the {@link Controller} with the instanceId passed in.
|
||||
*
|
||||
* @param instanceId The instanceId of the Controller to which this result should be forwarded
|
||||
* @param requestCode The Activity's onRequestPermissionsResult requestCode
|
||||
* @param permissions The Activity's onRequestPermissionsResult permissions
|
||||
* @param instanceId The instanceId of the Controller to which this result should be forwarded
|
||||
* @param requestCode The Activity's onRequestPermissionsResult requestCode
|
||||
* @param permissions The Activity's onRequestPermissionsResult permissions
|
||||
* @param grantResults The Activity's onRequestPermissionsResult grantResults
|
||||
*/
|
||||
public void onRequestPermissionsResult(@NonNull String instanceId, int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
@@ -172,7 +173,7 @@ public abstract class Router {
|
||||
final boolean newHandlerRemovesViews = handler == null || handler.removesFromViewOnPush();
|
||||
if (!oldHandlerRemovedViews && newHandlerRemovesViews) {
|
||||
for (RouterTransaction visibleTransaction : getVisibleTransactions(backstack.iterator())) {
|
||||
performControllerChange(null, visibleTransaction.controller, true, handler);
|
||||
performControllerChange(null, visibleTransaction, true, handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -188,11 +189,10 @@ public abstract class Router {
|
||||
void destroy(boolean popViews) {
|
||||
popsLastView = true;
|
||||
List<RouterTransaction> poppedControllers = backstack.popAll();
|
||||
trackDestroyingControllers(poppedControllers);
|
||||
|
||||
if (popViews && poppedControllers.size() > 0) {
|
||||
trackDestroyingControllers(poppedControllers);
|
||||
|
||||
performControllerChange(null, poppedControllers.get(0).controller, false, poppedControllers.get(0).popChangeHandler());
|
||||
performControllerChange(null, poppedControllers.get(0), false, poppedControllers.get(0).popChangeHandler());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +252,7 @@ public abstract class Router {
|
||||
/**
|
||||
* Pops all {@link Controller}s until the {@link Controller} with the passed tag is at the top
|
||||
*
|
||||
* @param tag The tag being popped to
|
||||
* @param tag The tag being popped to
|
||||
* @param changeHandler The {@link ControllerChangeHandler} to handle this transaction
|
||||
* @return Whether or not the {@link Controller} with the passed tag is now at the top
|
||||
*/
|
||||
@@ -281,10 +281,10 @@ public abstract class Router {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hosted Controller with the given instance id, if available.
|
||||
* Returns the hosted Controller with the given instance id or {@code null} if no such
|
||||
* Controller exists in this Router.
|
||||
*
|
||||
* @param instanceId The instance ID being searched for
|
||||
* @return The matching Controller, if one exists
|
||||
*/
|
||||
@Nullable
|
||||
public Controller getControllerWithInstanceId(@NonNull String instanceId) {
|
||||
@@ -298,10 +298,10 @@ public abstract class Router {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hosted Controller that was pushed with the given tag, if available.
|
||||
* Returns the hosted Controller that was pushed with the given tag or {@code null} if no
|
||||
* such Controller exists in this Router.
|
||||
*
|
||||
* @param tag The tag being searched for
|
||||
* @return The matching Controller, if one exists
|
||||
*/
|
||||
@Nullable
|
||||
public Controller getControllerWithTag(@NonNull String tag) {
|
||||
@@ -337,7 +337,7 @@ public abstract class Router {
|
||||
* Sets the backstack, transitioning from the current top controller to the top of the new stack (if different)
|
||||
* using the passed {@link ControllerChangeHandler}
|
||||
*
|
||||
* @param newBackstack The new backstack
|
||||
* @param newBackstack The new backstack
|
||||
* @param changeHandler An optional change handler to be used to handle the root view of transition
|
||||
*/
|
||||
@UiThread
|
||||
@@ -345,6 +345,7 @@ public abstract class Router {
|
||||
List<RouterTransaction> oldVisibleTransactions = getVisibleTransactions(backstack.iterator());
|
||||
|
||||
removeAllExceptVisibleAndUnowned();
|
||||
ensureOrderedTransactionIndices(newBackstack);
|
||||
|
||||
backstack.setBackstack(newBackstack);
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
@@ -358,19 +359,19 @@ public abstract class Router {
|
||||
|
||||
boolean visibleTransactionsChanged = !backstacksAreEqual(newVisibleTransactions, oldVisibleTransactions);
|
||||
if (visibleTransactionsChanged) {
|
||||
Controller rootController = oldVisibleTransactions.size() > 0 ? oldVisibleTransactions.get(0).controller : null;
|
||||
performControllerChange(newVisibleTransactions.get(0).controller, rootController, true, changeHandler);
|
||||
RouterTransaction rootTransaction = oldVisibleTransactions.size() > 0 ? oldVisibleTransactions.get(0) : null;
|
||||
performControllerChange(newVisibleTransactions.get(0), rootTransaction, true, changeHandler);
|
||||
|
||||
for (int i = oldVisibleTransactions.size() - 1; i > 0; i--) {
|
||||
RouterTransaction transaction = oldVisibleTransactions.get(i);
|
||||
ControllerChangeHandler localHandler = changeHandler != null ? changeHandler.copy() : new SimpleSwapChangeHandler();
|
||||
localHandler.setForceRemoveViewOnPush(true);
|
||||
performControllerChange(null, transaction.controller, true, localHandler);
|
||||
performControllerChange(null, transaction, true, localHandler);
|
||||
}
|
||||
|
||||
for (int i = 1; i < newVisibleTransactions.size(); i++) {
|
||||
RouterTransaction transaction = newVisibleTransactions.get(i);
|
||||
performControllerChange(transaction.controller, newVisibleTransactions.get(i - 1).controller, true, transaction.pushChangeHandler());
|
||||
performControllerChange(transaction, newVisibleTransactions.get(i - 1), true, transaction.pushChangeHandler());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,12 +380,6 @@ public abstract class Router {
|
||||
transaction.controller.setRouter(this);
|
||||
}
|
||||
}
|
||||
|
||||
if (onControllerPushedListener != null) {
|
||||
for (RouterTransaction transaction : newBackstack) {
|
||||
onControllerPushedListener.onControllerPushed(transaction.controller);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -424,7 +419,7 @@ public abstract class Router {
|
||||
RouterTransaction transaction = backstackIterator.next();
|
||||
|
||||
if (transaction.controller.getNeedsAttach()) {
|
||||
performControllerChange(transaction.controller, null, true, new SimpleSwapChangeHandler(false));
|
||||
performControllerChange(transaction, null, true, new SimpleSwapChangeHandler(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -575,14 +570,10 @@ public abstract class Router {
|
||||
changeHandler = topTransaction.popChangeHandler();
|
||||
}
|
||||
|
||||
performControllerChange(backstack.peek().controller, topTransaction.controller, false, changeHandler);
|
||||
performControllerChange(backstack.peek(), topTransaction, false, changeHandler);
|
||||
}
|
||||
}
|
||||
|
||||
final void setOnControllerPushedListener(OnControllerPushedListener listener) {
|
||||
onControllerPushedListener = listener;
|
||||
}
|
||||
|
||||
void prepareForContainerRemoval() {
|
||||
if (container != null) {
|
||||
container.setOnHierarchyChangeListener(null);
|
||||
@@ -626,30 +617,27 @@ public abstract class Router {
|
||||
changeHandler = null;
|
||||
}
|
||||
|
||||
performControllerChange(to, from, isPush, changeHandler);
|
||||
}
|
||||
|
||||
private void performControllerChange(@Nullable final RouterTransaction to, @Nullable final RouterTransaction from, boolean isPush, @Nullable ControllerChangeHandler changeHandler) {
|
||||
Controller toController = to != null ? to.controller : null;
|
||||
Controller fromController = from != null ? from.controller : null;
|
||||
|
||||
performControllerChange(toController, fromController, isPush, changeHandler);
|
||||
}
|
||||
|
||||
private void performControllerChange(@Nullable final Controller to, @Nullable final Controller from, boolean isPush, @Nullable ControllerChangeHandler changeHandler) {
|
||||
if (to != null) {
|
||||
setControllerRouter(to);
|
||||
to.ensureValidIndex(getTransactionIndexer());
|
||||
setControllerRouter(toController);
|
||||
} else if (backstack.size() == 0 && !popsLastView) {
|
||||
// We're emptying out the backstack. Views get weird if you transition them out, so just no-op it. The hosting
|
||||
// Activity should be handling this by finishing or at least hiding this view.
|
||||
changeHandler = new NoOpControllerChangeHandler();
|
||||
}
|
||||
|
||||
ControllerChangeHandler.executeChange(to, from, isPush, container, changeHandler, changeListeners);
|
||||
ControllerChangeHandler.executeChange(toController, fromController, isPush, container, changeHandler, changeListeners);
|
||||
}
|
||||
|
||||
private void pushToBackstack(@NonNull RouterTransaction entry) {
|
||||
backstack.push(entry);
|
||||
|
||||
if (onControllerPushedListener != null) {
|
||||
onControllerPushedListener.onControllerPushed(entry.controller);
|
||||
}
|
||||
}
|
||||
|
||||
private void trackDestroyingController(@NonNull RouterTransaction transaction) {
|
||||
@@ -695,6 +683,22 @@ public abstract class Router {
|
||||
}
|
||||
}
|
||||
|
||||
// Swap around transaction indicies to ensure they don't get thrown out of order by the
|
||||
// developer rearranging the backstack at runtime.
|
||||
private void ensureOrderedTransactionIndices(List<RouterTransaction> backstack) {
|
||||
List<Integer> indices = new ArrayList<>();
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
transaction.ensureValidIndex(getTransactionIndexer());
|
||||
indices.add(transaction.transactionIndex);
|
||||
}
|
||||
|
||||
Collections.sort(indices);
|
||||
|
||||
for (int i = 0; i < backstack.size(); i++) {
|
||||
backstack.get(i).transactionIndex = indices.get(i);
|
||||
}
|
||||
}
|
||||
|
||||
private void addRouterViewsToList(@NonNull Router router, @NonNull List<View> list) {
|
||||
for (Controller controller : router.getControllers()) {
|
||||
if (controller.getView() != null) {
|
||||
@@ -750,8 +754,6 @@ public abstract class Router {
|
||||
abstract boolean hasHost();
|
||||
@NonNull abstract List<Router> getSiblingRouters();
|
||||
@NonNull abstract Router getRootRouter();
|
||||
@Nullable abstract TransactionIndexer getTransactionIndexer();
|
||||
|
||||
interface OnControllerPushedListener {
|
||||
void onControllerPushed(Controller controller);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,15 +4,20 @@ import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.bluelinelabs.conductor.internal.TransactionIndexer;
|
||||
|
||||
/**
|
||||
* Metadata used for adding {@link Controller}s to a {@link Router}.
|
||||
*/
|
||||
public class RouterTransaction {
|
||||
|
||||
private static int INVALID_INDEX = -1;
|
||||
|
||||
private static final String KEY_VIEW_CONTROLLER_BUNDLE = "RouterTransaction.controller.bundle";
|
||||
private static final String KEY_PUSH_TRANSITION = "RouterTransaction.pushControllerChangeHandler";
|
||||
private static final String KEY_POP_TRANSITION = "RouterTransaction.popControllerChangeHandler";
|
||||
private static final String KEY_TAG = "RouterTransaction.tag";
|
||||
private static final String KEY_INDEX = "RouterTransaction.transactionIndex";
|
||||
private static final String KEY_ATTACHED_TO_ROUTER = "RouterTransaction.attachedToRouter";
|
||||
|
||||
@NonNull final Controller controller;
|
||||
@@ -21,6 +26,7 @@ public class RouterTransaction {
|
||||
private ControllerChangeHandler pushControllerChangeHandler;
|
||||
private ControllerChangeHandler popControllerChangeHandler;
|
||||
private boolean attachedToRouter;
|
||||
int transactionIndex = INVALID_INDEX;
|
||||
|
||||
@NonNull
|
||||
public static RouterTransaction with(@NonNull Controller controller) {
|
||||
@@ -36,6 +42,7 @@ public class RouterTransaction {
|
||||
pushControllerChangeHandler = ControllerChangeHandler.fromBundle(bundle.getBundle(KEY_PUSH_TRANSITION));
|
||||
popControllerChangeHandler = ControllerChangeHandler.fromBundle(bundle.getBundle(KEY_POP_TRANSITION));
|
||||
tag = bundle.getString(KEY_TAG);
|
||||
transactionIndex = bundle.getInt(KEY_INDEX);
|
||||
attachedToRouter = bundle.getBoolean(KEY_ATTACHED_TO_ROUTER);
|
||||
}
|
||||
|
||||
@@ -101,6 +108,15 @@ public class RouterTransaction {
|
||||
}
|
||||
}
|
||||
|
||||
void ensureValidIndex(@Nullable TransactionIndexer indexer) {
|
||||
if (indexer == null) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
if (transactionIndex == INVALID_INDEX && indexer != null) {
|
||||
transactionIndex = indexer.nextIndex();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to serialize this transaction into a Bundle
|
||||
*/
|
||||
@@ -118,6 +134,7 @@ public class RouterTransaction {
|
||||
}
|
||||
|
||||
bundle.putString(KEY_TAG, tag);
|
||||
bundle.putInt(KEY_INDEX, transactionIndex);
|
||||
bundle.putBoolean(KEY_ATTACHED_TO_ROUTER, attachedToRouter);
|
||||
|
||||
return bundle;
|
||||
|
||||
+3
-3
@@ -94,8 +94,8 @@ public abstract class AnimatorChangeHandler extends ControllerChangeHandler {
|
||||
* Should be overridden to return the Animator to use while replacing Views.
|
||||
*
|
||||
* @param container The container these Views are hosted in.
|
||||
* @param from The previous View in the container, if any.
|
||||
* @param to The next View that should be put in the container, if any.
|
||||
* @param from The previous View in the container or {@code null} if there was no Controller before this transition
|
||||
* @param to The next View that should be put in the container or {@code null} if no Controller is being transitioned to
|
||||
* @param isPush True if this is a push transaction, false if it's a pop.
|
||||
* @param toAddedToContainer True if the "to" view was added to the container as a part of this ChangeHandler. False if it was already in the hierarchy.
|
||||
*/
|
||||
@@ -150,7 +150,7 @@ public abstract class AnimatorChangeHandler extends ControllerChangeHandler {
|
||||
if (animatorListener != null) {
|
||||
animator.removeListener(animatorListener);
|
||||
}
|
||||
animator.end();
|
||||
animator.cancel();
|
||||
animator = null;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -32,8 +32,9 @@ public class FadeChangeHandler extends AnimatorChangeHandler {
|
||||
@Override @NonNull
|
||||
protected Animator getAnimator(@NonNull ViewGroup container, @Nullable View from, @Nullable View to, boolean isPush, boolean toAddedToContainer) {
|
||||
AnimatorSet animator = new AnimatorSet();
|
||||
if (to != null && toAddedToContainer) {
|
||||
animator.play(ObjectAnimator.ofFloat(to, View.ALPHA, 0, 1));
|
||||
if (to != null) {
|
||||
float start = toAddedToContainer ? 0 : to.getAlpha();
|
||||
animator.play(ObjectAnimator.ofFloat(to, View.ALPHA, start, 1));
|
||||
}
|
||||
|
||||
if (from != null) {
|
||||
|
||||
+3
-1
@@ -45,7 +45,9 @@ public class HorizontalChangeHandler extends AnimatorChangeHandler {
|
||||
animatorSet.play(ObjectAnimator.ofFloat(from, View.TRANSLATION_X, from.getWidth()));
|
||||
}
|
||||
if (to != null) {
|
||||
animatorSet.play(ObjectAnimator.ofFloat(to, View.TRANSLATION_X, -to.getWidth(), 0));
|
||||
// Allow this to have a nice transition when coming off an aborted push animation
|
||||
float fromLeft = from != null ? from.getX() : 0;
|
||||
animatorSet.play(ObjectAnimator.ofFloat(to, View.TRANSLATION_X, fromLeft - to.getWidth(), 0));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -24,10 +24,10 @@ public abstract class TransitionChangeHandler extends ControllerChangeHandler {
|
||||
/**
|
||||
* Should be overridden to return the Transition to use while replacing Views.
|
||||
*
|
||||
* @param container The container these Views are hosted in.
|
||||
* @param from The previous View in the container, if any.
|
||||
* @param to The next View that should be put in the container, if any.
|
||||
* @param isPush True if this is a push transaction, false if it's a pop.
|
||||
* @param container The container these Views are hosted in
|
||||
* @param from The previous View in the container or {@code null} if there was no Controller before this transition
|
||||
* @param to The next View that should be put in the container or {@code null} if no Controller is being transitioned to
|
||||
* @param isPush True if this is a push transaction, false if it's a pop
|
||||
*/
|
||||
@NonNull
|
||||
protected abstract Transition getTransition(@NonNull ViewGroup container, @Nullable View from, @Nullable View to, boolean isPush);
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.bluelinelabs.conductor.internal;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
public class TransactionIndexer {
|
||||
|
||||
private static final String KEY_INDEX = "TransactionIndexer.currentIndex";
|
||||
|
||||
private int currentIndex;
|
||||
|
||||
public int nextIndex() {
|
||||
return ++currentIndex;
|
||||
}
|
||||
|
||||
public void saveInstanceState(@NonNull Bundle outState) {
|
||||
outState.putInt(KEY_INDEX, currentIndex);
|
||||
}
|
||||
|
||||
public void restoreInstanceState(@NonNull Bundle savedInstanceState) {
|
||||
currentIndex = savedInstanceState.getInt(KEY_INDEX);
|
||||
}
|
||||
|
||||
}
|
||||
+76
-35
@@ -4,66 +4,107 @@ import android.view.View;
|
||||
import android.view.View.OnAttachStateChangeListener;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
public class ViewAttachHandler {
|
||||
public class ViewAttachHandler implements OnAttachStateChangeListener {
|
||||
|
||||
private enum ReportedState {
|
||||
VIEW_DETACHED,
|
||||
ACTIVITY_STOPPED,
|
||||
ATTACHED
|
||||
}
|
||||
|
||||
public interface ViewAttachListener {
|
||||
void onAttached(View view);
|
||||
void onDetached(View view);
|
||||
void onAttached();
|
||||
void onDetached(boolean fromActivityStop);
|
||||
void onViewDetachAfterStop();
|
||||
}
|
||||
|
||||
private interface ChildAttachListener {
|
||||
void onAttached();
|
||||
}
|
||||
|
||||
private boolean rootAttached = false;
|
||||
boolean childrenAttached = false;
|
||||
private boolean activityStopped = false;
|
||||
private ReportedState reportedState = ReportedState.VIEW_DETACHED;
|
||||
private ViewAttachListener attachListener;
|
||||
private OnAttachStateChangeListener rootOnAttachStateChangeListener = new OnAttachStateChangeListener() {
|
||||
boolean rootAttached = false;
|
||||
boolean childrenAttached = false;
|
||||
|
||||
@Override
|
||||
public void onViewAttachedToWindow(final View v) {
|
||||
if (rootAttached) {
|
||||
return;
|
||||
}
|
||||
|
||||
rootAttached = true;
|
||||
listenForDeepestChildAttach(v, new ChildAttachListener() {
|
||||
@Override
|
||||
public void onAttached() {
|
||||
childrenAttached = true;
|
||||
attachListener.onAttached(v);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewDetachedFromWindow(View v) {
|
||||
rootAttached = false;
|
||||
if (childrenAttached) {
|
||||
childrenAttached = false;
|
||||
attachListener.onDetached(v);
|
||||
}
|
||||
}
|
||||
};
|
||||
private OnAttachStateChangeListener childOnAttachStateChangeListener;
|
||||
|
||||
public ViewAttachHandler(ViewAttachListener attachListener) {
|
||||
this.attachListener = attachListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewAttachedToWindow(final View v) {
|
||||
if (rootAttached) {
|
||||
return;
|
||||
}
|
||||
|
||||
rootAttached = true;
|
||||
listenForDeepestChildAttach(v, new ChildAttachListener() {
|
||||
@Override
|
||||
public void onAttached() {
|
||||
childrenAttached = true;
|
||||
reportAttached();
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewDetachedFromWindow(View v) {
|
||||
rootAttached = false;
|
||||
if (childrenAttached) {
|
||||
childrenAttached = false;
|
||||
reportDetached();
|
||||
}
|
||||
}
|
||||
|
||||
public void listenForAttach(final View view) {
|
||||
view.addOnAttachStateChangeListener(rootOnAttachStateChangeListener);
|
||||
view.addOnAttachStateChangeListener(this);
|
||||
}
|
||||
|
||||
public void unregisterAttachListener(View view) {
|
||||
view.removeOnAttachStateChangeListener(rootOnAttachStateChangeListener);
|
||||
view.removeOnAttachStateChangeListener(this);
|
||||
|
||||
if (childOnAttachStateChangeListener != null && view instanceof ViewGroup) {
|
||||
findDeepestChild((ViewGroup)view).removeOnAttachStateChangeListener(childOnAttachStateChangeListener);
|
||||
}
|
||||
}
|
||||
|
||||
public void onActivityStarted() {
|
||||
activityStopped = false;
|
||||
reportAttached();
|
||||
}
|
||||
|
||||
public void onActivityStopped() {
|
||||
activityStopped = true;
|
||||
reportDetached();
|
||||
}
|
||||
|
||||
void reportAttached() {
|
||||
if (rootAttached && childrenAttached && !activityStopped && reportedState != ReportedState.ATTACHED) {
|
||||
reportedState = ReportedState.ATTACHED;
|
||||
attachListener.onAttached();
|
||||
}
|
||||
}
|
||||
|
||||
void reportDetached() {
|
||||
boolean wasDetachedForActivity = reportedState == ReportedState.ACTIVITY_STOPPED;
|
||||
boolean isDetachedForActivity = rootAttached;
|
||||
|
||||
if (isDetachedForActivity) {
|
||||
reportedState = ReportedState.ACTIVITY_STOPPED;
|
||||
} else {
|
||||
reportedState = ReportedState.VIEW_DETACHED;
|
||||
}
|
||||
|
||||
if (wasDetachedForActivity && !isDetachedForActivity) {
|
||||
attachListener.onViewDetachAfterStop();
|
||||
} else {
|
||||
attachListener.onDetached(isDetachedForActivity);
|
||||
}
|
||||
}
|
||||
|
||||
void listenForDeepestChildAttach(final View view, final ChildAttachListener attachListener) {
|
||||
if (!(view instanceof ViewGroup)) {
|
||||
attachListener.onAttached();
|
||||
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.util.ActivityProxy;
|
||||
import com.bluelinelabs.conductor.util.ListUtils;
|
||||
import com.bluelinelabs.conductor.util.MockChangeHandler;
|
||||
import com.bluelinelabs.conductor.util.TestController;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(manifest = Config.NONE)
|
||||
public class ControllerLifecycleActivityReferenceTests {
|
||||
|
||||
private Router router;
|
||||
|
||||
private ActivityProxy activityProxy;
|
||||
|
||||
public void createActivityController(Bundle savedInstanceState, boolean includeStartAndResume) {
|
||||
activityProxy = new ActivityProxy().create(savedInstanceState);
|
||||
|
||||
if (includeStartAndResume) {
|
||||
activityProxy.start().resume();
|
||||
}
|
||||
|
||||
router = Conductor.attachRouter(activityProxy.getActivity(), activityProxy.getView(), savedInstanceState);
|
||||
router.setPopsLastView(true);
|
||||
if (!router.hasRootController()) {
|
||||
router.setRoot(RouterTransaction.with(new TestController()));
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
createActivityController(null, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleControllerActivityOnPush() {
|
||||
Controller controller = new TestController();
|
||||
|
||||
assertNull(controller.getActivity());
|
||||
|
||||
ActivityReferencingLifecycleListener listener = new ActivityReferencingLifecycleListener();
|
||||
controller.addLifecycleListener(listener);
|
||||
|
||||
router.pushController(RouterTransaction.with(controller)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
assertEquals(ListUtils.listOf(true), listener.changeEndReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postCreateViewReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postAttachReferences);
|
||||
assertEquals(ListUtils.listOf(), listener.postDetachReferences);
|
||||
assertEquals(ListUtils.listOf(), listener.postDestroyViewReferences);
|
||||
assertEquals(ListUtils.listOf(), listener.postDestroyReferences);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildControllerActivityOnPush() {
|
||||
Controller parent = new TestController();
|
||||
router.pushController(RouterTransaction.with(parent)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
TestController child = new TestController();
|
||||
|
||||
assertNull(child.getActivity());
|
||||
|
||||
ActivityReferencingLifecycleListener listener = new ActivityReferencingLifecycleListener();
|
||||
child.addLifecycleListener(listener);
|
||||
|
||||
Router childRouter = parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouter.pushController(RouterTransaction.with(child)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
assertEquals(ListUtils.listOf(true), listener.changeEndReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postCreateViewReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postAttachReferences);
|
||||
assertEquals(ListUtils.listOf(), listener.postDetachReferences);
|
||||
assertEquals(ListUtils.listOf(), listener.postDestroyViewReferences);
|
||||
assertEquals(ListUtils.listOf(), listener.postDestroyReferences);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleControllerActivityOnPop() {
|
||||
Controller controller = new TestController();
|
||||
|
||||
ActivityReferencingLifecycleListener listener = new ActivityReferencingLifecycleListener();
|
||||
controller.addLifecycleListener(listener);
|
||||
|
||||
router.pushController(RouterTransaction.with(controller)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
router.popCurrentController();
|
||||
|
||||
assertEquals(ListUtils.listOf(true, true), listener.changeEndReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postCreateViewReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postAttachReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postDetachReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postDestroyViewReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postDestroyReferences);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildControllerActivityOnPop() {
|
||||
Controller parent = new TestController();
|
||||
|
||||
router.pushController(RouterTransaction.with(parent)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
TestController child = new TestController();
|
||||
|
||||
ActivityReferencingLifecycleListener listener = new ActivityReferencingLifecycleListener();
|
||||
child.addLifecycleListener(listener);
|
||||
|
||||
Router childRouter = parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouter.setPopsLastView(true);
|
||||
childRouter.pushController(RouterTransaction.with(child)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
childRouter.popCurrentController();
|
||||
|
||||
assertEquals(ListUtils.listOf(true, true), listener.changeEndReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postCreateViewReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postAttachReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postDetachReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postDestroyViewReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postDestroyReferences);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildControllerActivityOnParentPop() {
|
||||
Controller parent = new TestController();
|
||||
|
||||
router.pushController(RouterTransaction.with(parent)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
TestController child = new TestController();
|
||||
|
||||
ActivityReferencingLifecycleListener listener = new ActivityReferencingLifecycleListener();
|
||||
child.addLifecycleListener(listener);
|
||||
|
||||
Router childRouter = parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouter.setPopsLastView(true);
|
||||
childRouter.pushController(RouterTransaction.with(child)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
router.popCurrentController();
|
||||
|
||||
assertEquals(ListUtils.listOf(true), listener.changeEndReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postCreateViewReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postAttachReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postDetachReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postDestroyViewReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postDestroyReferences);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleControllerActivityOnDestroy() {
|
||||
Controller controller = new TestController();
|
||||
|
||||
ActivityReferencingLifecycleListener listener = new ActivityReferencingLifecycleListener();
|
||||
controller.addLifecycleListener(listener);
|
||||
|
||||
router.pushController(RouterTransaction.with(controller)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
activityProxy.pause().stop(false).destroy();
|
||||
|
||||
assertEquals(ListUtils.listOf(true), listener.changeEndReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postCreateViewReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postAttachReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postDetachReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postDestroyViewReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postDestroyReferences);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildControllerActivityOnDestroy() {
|
||||
Controller parent = new TestController();
|
||||
|
||||
router.pushController(RouterTransaction.with(parent)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
TestController child = new TestController();
|
||||
|
||||
ActivityReferencingLifecycleListener listener = new ActivityReferencingLifecycleListener();
|
||||
child.addLifecycleListener(listener);
|
||||
|
||||
Router childRouter = parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouter.setPopsLastView(true);
|
||||
childRouter.pushController(RouterTransaction.with(child)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
activityProxy.pause().stop(false).destroy();
|
||||
|
||||
assertEquals(ListUtils.listOf(true), listener.changeEndReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postCreateViewReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postAttachReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postDetachReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postDestroyViewReferences);
|
||||
assertEquals(ListUtils.listOf(true), listener.postDestroyReferences);
|
||||
}
|
||||
|
||||
static class ActivityReferencingLifecycleListener extends Controller.LifecycleListener {
|
||||
List<Boolean> changeEndReferences = new ArrayList<>();
|
||||
List<Boolean> postCreateViewReferences = new ArrayList<>();
|
||||
List<Boolean> postAttachReferences = new ArrayList<>();
|
||||
List<Boolean> postDetachReferences = new ArrayList<>();
|
||||
List<Boolean> postDestroyViewReferences = new ArrayList<>();
|
||||
List<Boolean> postDestroyReferences = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void onChangeEnd(@NonNull Controller controller, @NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
changeEndReferences.add(controller.getActivity() != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postCreateView(@NonNull Controller controller, @NonNull View view) {
|
||||
postCreateViewReferences.add(controller.getActivity() != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postAttach(@NonNull Controller controller, @NonNull View view) {
|
||||
postAttachReferences.add(controller.getActivity() != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postDetach(@NonNull Controller controller, @NonNull View view) {
|
||||
postDetachReferences.add(controller.getActivity() != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postDestroyView(@NonNull Controller controller) {
|
||||
postDestroyViewReferences.add(controller.getActivity() != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postDestroy(@NonNull Controller controller) {
|
||||
postDestroyReferences.add(controller.getActivity() != null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+36
-3
@@ -11,6 +11,7 @@ import com.bluelinelabs.conductor.util.CallState;
|
||||
import com.bluelinelabs.conductor.util.MockChangeHandler;
|
||||
import com.bluelinelabs.conductor.util.MockChangeHandler.ChangeHandlerListener;
|
||||
import com.bluelinelabs.conductor.util.TestController;
|
||||
import com.bluelinelabs.conductor.util.ViewUtils;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -19,11 +20,12 @@ import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(manifest = Config.NONE)
|
||||
public class ControllerLifecycleTests {
|
||||
public class ControllerLifecycleCallbacksTests {
|
||||
|
||||
private Router router;
|
||||
|
||||
@@ -71,6 +73,37 @@ public class ControllerLifecycleTests {
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLifecycleWithActivityStop() {
|
||||
TestController controller = new TestController();
|
||||
attachLifecycleListener(controller);
|
||||
|
||||
CallState expectedCallState = new CallState();
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
router.pushController(RouterTransaction.with(controller)
|
||||
.pushChangeHandler(getPushHandler(expectedCallState, controller)));
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityProxy.getActivity().isDestroying = true;
|
||||
activityProxy.pause();
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityProxy.stop(false);
|
||||
|
||||
expectedCallState.detachCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
assertNotNull(controller.getView());
|
||||
ViewUtils.reportAttached(controller.getView(), false);
|
||||
|
||||
expectedCallState.saveViewStateCalls++;
|
||||
expectedCallState.destroyViewCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLifecycleWithActivityDestroy() {
|
||||
TestController controller = new TestController();
|
||||
@@ -89,7 +122,7 @@ public class ControllerLifecycleTests {
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityProxy.stop();
|
||||
activityProxy.stop(true);
|
||||
|
||||
expectedCallState.saveViewStateCalls++;
|
||||
expectedCallState.detachCalls++;
|
||||
@@ -128,7 +161,7 @@ public class ControllerLifecycleTests {
|
||||
activityProxy.pause();
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityProxy.stop();
|
||||
activityProxy.stop(true);
|
||||
expectedCallState.detachCalls++;
|
||||
expectedCallState.destroyViewCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
@@ -21,6 +21,7 @@ import org.robolectric.annotation.Config;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(manifest = Config.NONE)
|
||||
@@ -362,6 +363,48 @@ public class ControllerTests {
|
||||
assertNull(child2.getParentController());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestoredChildRouterBackstack() {
|
||||
TestController parent = new TestController();
|
||||
router.pushController(RouterTransaction.with(parent));
|
||||
ViewUtils.reportAttached(parent.getView(), true);
|
||||
|
||||
RouterTransaction childTransaction1 = RouterTransaction.with(new TestController());
|
||||
RouterTransaction childTransaction2 = RouterTransaction.with(new TestController());
|
||||
|
||||
Router childRouter = parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.CHILD_VIEW_ID_1));
|
||||
childRouter.setPopsLastView(true);
|
||||
childRouter.setRoot(childTransaction1);
|
||||
childRouter.pushController(childTransaction2);
|
||||
|
||||
Bundle savedState = new Bundle();
|
||||
childRouter.saveInstanceState(savedState);
|
||||
parent.removeChildRouter(childRouter);
|
||||
|
||||
childRouter = parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.CHILD_VIEW_ID_1));
|
||||
assertEquals(0, childRouter.getBackstackSize());
|
||||
|
||||
childRouter.restoreInstanceState(savedState);
|
||||
childRouter.rebindIfNeeded();
|
||||
|
||||
assertEquals(2, childRouter.getBackstackSize());
|
||||
|
||||
RouterTransaction restoredChildTransaction1 = childRouter.getBackstack().get(0);
|
||||
RouterTransaction restoredChildTransaction2 = childRouter.getBackstack().get(1);
|
||||
|
||||
assertEquals(childTransaction1.transactionIndex, restoredChildTransaction1.transactionIndex);
|
||||
assertEquals(childTransaction1.controller.getInstanceId(), restoredChildTransaction1.controller.getInstanceId());
|
||||
assertEquals(childTransaction2.transactionIndex, restoredChildTransaction2.transactionIndex);
|
||||
assertEquals(childTransaction2.controller.getInstanceId(), restoredChildTransaction2.controller.getInstanceId());
|
||||
|
||||
assertTrue(parent.handleBack());
|
||||
assertEquals(1, childRouter.getBackstackSize());
|
||||
assertEquals(restoredChildTransaction1, childRouter.getBackstack().get(0));
|
||||
|
||||
assertTrue(parent.handleBack());
|
||||
assertEquals(0, childRouter.getBackstackSize());
|
||||
}
|
||||
|
||||
private void assertCalls(CallState callState, TestController controller) {
|
||||
assertEquals("Expected call counts and controller call counts do not match.", callState, controller.currentCallState);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.util.ActivityProxy;
|
||||
import com.bluelinelabs.conductor.util.ListUtils;
|
||||
import com.bluelinelabs.conductor.util.MockChangeHandler;
|
||||
@@ -312,4 +314,61 @@ public class RouterTests {
|
||||
assertTrue(newTopTransaction.controller.isAttached());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRearrangeTransactionBackstack() {
|
||||
RouterTransaction transaction1 = RouterTransaction.with(new TestController());
|
||||
RouterTransaction transaction2 = RouterTransaction.with(new TestController());
|
||||
|
||||
List<RouterTransaction> backstack = ListUtils.listOf(transaction1, transaction2);
|
||||
router.setBackstack(backstack, null);
|
||||
|
||||
assertEquals(1, transaction1.transactionIndex);
|
||||
assertEquals(2, transaction2.transactionIndex);
|
||||
|
||||
backstack = ListUtils.listOf(transaction2, transaction1);
|
||||
router.setBackstack(backstack, null);
|
||||
|
||||
assertEquals(1, transaction2.transactionIndex);
|
||||
assertEquals(2, transaction1.transactionIndex);
|
||||
|
||||
router.handleBack();
|
||||
|
||||
assertEquals(1, router.getBackstackSize());
|
||||
assertEquals(transaction2, router.getBackstack().get(0));
|
||||
|
||||
router.handleBack();
|
||||
assertEquals(0, router.getBackstackSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildRouterRearrangeTransactionBackstack() {
|
||||
Controller parent = new TestController();
|
||||
router.setRoot(RouterTransaction.with(parent));
|
||||
|
||||
Router childRouter = parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.CHILD_VIEW_ID_1));
|
||||
|
||||
RouterTransaction transaction1 = RouterTransaction.with(new TestController());
|
||||
RouterTransaction transaction2 = RouterTransaction.with(new TestController());
|
||||
|
||||
List<RouterTransaction> backstack = ListUtils.listOf(transaction1, transaction2);
|
||||
childRouter.setBackstack(backstack, null);
|
||||
|
||||
assertEquals(2, transaction1.transactionIndex);
|
||||
assertEquals(3, transaction2.transactionIndex);
|
||||
|
||||
backstack = ListUtils.listOf(transaction2, transaction1);
|
||||
childRouter.setBackstack(backstack, null);
|
||||
|
||||
assertEquals(2, transaction2.transactionIndex);
|
||||
assertEquals(3, transaction1.transactionIndex);
|
||||
|
||||
childRouter.handleBack();
|
||||
|
||||
assertEquals(1, childRouter.getBackstackSize());
|
||||
assertEquals(transaction2, childRouter.getBackstack().get(0));
|
||||
|
||||
childRouter.handleBack();
|
||||
assertEquals(0, childRouter.getBackstackSize());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,55 +40,107 @@ public class ViewAttachHandlerTests {
|
||||
|
||||
assertEquals(0, viewAttachListener.attaches);
|
||||
assertEquals(0, viewAttachListener.detaches);
|
||||
assertEquals(0, viewAttachListener.detachAfterStops);
|
||||
|
||||
ViewUtils.reportAttached(view, true);
|
||||
assertEquals(1, viewAttachListener.attaches);
|
||||
assertEquals(0, viewAttachListener.detaches);
|
||||
assertEquals(0, viewAttachListener.detachAfterStops);
|
||||
|
||||
ViewUtils.reportAttached(view, true);
|
||||
assertEquals(1, viewAttachListener.attaches);
|
||||
assertEquals(0, viewAttachListener.detaches);
|
||||
assertEquals(0, viewAttachListener.detachAfterStops);
|
||||
|
||||
ViewUtils.reportAttached(view, false);
|
||||
assertEquals(1, viewAttachListener.attaches);
|
||||
assertEquals(1, viewAttachListener.detaches);
|
||||
assertEquals(0, viewAttachListener.detachAfterStops);
|
||||
|
||||
ViewUtils.reportAttached(view, false);
|
||||
assertEquals(1, viewAttachListener.attaches);
|
||||
assertEquals(1, viewAttachListener.detaches);
|
||||
assertEquals(0, viewAttachListener.detachAfterStops);
|
||||
|
||||
ViewUtils.reportAttached(view, true);
|
||||
assertEquals(2, viewAttachListener.attaches);
|
||||
assertEquals(1, viewAttachListener.detaches);
|
||||
assertEquals(0, viewAttachListener.detachAfterStops);
|
||||
|
||||
viewAttachHandler.onActivityStopped();
|
||||
assertEquals(2, viewAttachListener.attaches);
|
||||
assertEquals(2, viewAttachListener.detaches);
|
||||
assertEquals(0, viewAttachListener.detachAfterStops);
|
||||
|
||||
ViewUtils.reportAttached(view, false);
|
||||
assertEquals(2, viewAttachListener.attaches);
|
||||
assertEquals(2, viewAttachListener.detaches);
|
||||
assertEquals(1, viewAttachListener.detachAfterStops);
|
||||
|
||||
ViewUtils.reportAttached(view, true);
|
||||
assertEquals(2, viewAttachListener.attaches);
|
||||
assertEquals(2, viewAttachListener.detaches);
|
||||
assertEquals(1, viewAttachListener.detachAfterStops);
|
||||
|
||||
viewAttachHandler.onActivityStarted();
|
||||
assertEquals(3, viewAttachListener.attaches);
|
||||
assertEquals(2, viewAttachListener.detaches);
|
||||
assertEquals(1, viewAttachListener.detachAfterStops);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleViewGroupAttachDetach() {
|
||||
View view = new LinearLayout(activity);
|
||||
View view = new View(activity);
|
||||
viewAttachHandler.listenForAttach(view);
|
||||
|
||||
assertEquals(0, viewAttachListener.attaches);
|
||||
assertEquals(0, viewAttachListener.detaches);
|
||||
assertEquals(0, viewAttachListener.detachAfterStops);
|
||||
|
||||
ViewUtils.reportAttached(view, true);
|
||||
assertEquals(1, viewAttachListener.attaches);
|
||||
assertEquals(0, viewAttachListener.detaches);
|
||||
assertEquals(0, viewAttachListener.detachAfterStops);
|
||||
|
||||
ViewUtils.reportAttached(view, true);
|
||||
assertEquals(1, viewAttachListener.attaches);
|
||||
assertEquals(0, viewAttachListener.detaches);
|
||||
assertEquals(0, viewAttachListener.detachAfterStops);
|
||||
|
||||
ViewUtils.reportAttached(view, false);
|
||||
assertEquals(1, viewAttachListener.attaches);
|
||||
assertEquals(1, viewAttachListener.detaches);
|
||||
assertEquals(0, viewAttachListener.detachAfterStops);
|
||||
|
||||
ViewUtils.reportAttached(view, false);
|
||||
assertEquals(1, viewAttachListener.attaches);
|
||||
assertEquals(1, viewAttachListener.detaches);
|
||||
assertEquals(0, viewAttachListener.detachAfterStops);
|
||||
|
||||
ViewUtils.reportAttached(view, true);
|
||||
assertEquals(2, viewAttachListener.attaches);
|
||||
assertEquals(1, viewAttachListener.detaches);
|
||||
assertEquals(0, viewAttachListener.detachAfterStops);
|
||||
|
||||
viewAttachHandler.onActivityStopped();
|
||||
assertEquals(2, viewAttachListener.attaches);
|
||||
assertEquals(2, viewAttachListener.detaches);
|
||||
assertEquals(0, viewAttachListener.detachAfterStops);
|
||||
|
||||
ViewUtils.reportAttached(view, false);
|
||||
assertEquals(2, viewAttachListener.attaches);
|
||||
assertEquals(2, viewAttachListener.detaches);
|
||||
assertEquals(1, viewAttachListener.detachAfterStops);
|
||||
|
||||
ViewUtils.reportAttached(view, true);
|
||||
assertEquals(2, viewAttachListener.attaches);
|
||||
assertEquals(2, viewAttachListener.detaches);
|
||||
assertEquals(1, viewAttachListener.detachAfterStops);
|
||||
|
||||
viewAttachHandler.onActivityStarted();
|
||||
assertEquals(3, viewAttachListener.attaches);
|
||||
assertEquals(2, viewAttachListener.detaches);
|
||||
assertEquals(1, viewAttachListener.detachAfterStops);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -100,50 +152,85 @@ public class ViewAttachHandlerTests {
|
||||
|
||||
assertEquals(0, viewAttachListener.attaches);
|
||||
assertEquals(0, viewAttachListener.detaches);
|
||||
assertEquals(0, viewAttachListener.detachAfterStops);
|
||||
|
||||
ViewUtils.reportAttached(view, true, false);
|
||||
assertEquals(0, viewAttachListener.attaches);
|
||||
assertEquals(0, viewAttachListener.detaches);
|
||||
assertEquals(0, viewAttachListener.detachAfterStops);
|
||||
|
||||
ViewUtils.reportAttached(child, true, false);
|
||||
assertEquals(1, viewAttachListener.attaches);
|
||||
assertEquals(0, viewAttachListener.detaches);
|
||||
assertEquals(0, viewAttachListener.detachAfterStops);
|
||||
|
||||
ViewUtils.reportAttached(view, true, false);
|
||||
ViewUtils.reportAttached(child, true, false);
|
||||
assertEquals(1, viewAttachListener.attaches);
|
||||
assertEquals(0, viewAttachListener.detaches);
|
||||
assertEquals(0, viewAttachListener.detachAfterStops);
|
||||
|
||||
ViewUtils.reportAttached(view, false, false);
|
||||
assertEquals(1, viewAttachListener.attaches);
|
||||
assertEquals(1, viewAttachListener.detaches);
|
||||
assertEquals(0, viewAttachListener.detachAfterStops);
|
||||
|
||||
ViewUtils.reportAttached(view, false, false);
|
||||
assertEquals(1, viewAttachListener.attaches);
|
||||
assertEquals(1, viewAttachListener.detaches);
|
||||
assertEquals(0, viewAttachListener.detachAfterStops);
|
||||
|
||||
ViewUtils.reportAttached(view, true, false);
|
||||
assertEquals(1, viewAttachListener.attaches);
|
||||
assertEquals(1, viewAttachListener.detaches);
|
||||
assertEquals(0, viewAttachListener.detachAfterStops);
|
||||
|
||||
ViewUtils.reportAttached(child, true, false);
|
||||
assertEquals(2, viewAttachListener.attaches);
|
||||
assertEquals(1, viewAttachListener.detaches);
|
||||
assertEquals(0, viewAttachListener.detachAfterStops);
|
||||
|
||||
viewAttachHandler.onActivityStopped();
|
||||
assertEquals(2, viewAttachListener.attaches);
|
||||
assertEquals(2, viewAttachListener.detaches);
|
||||
assertEquals(0, viewAttachListener.detachAfterStops);
|
||||
|
||||
ViewUtils.reportAttached(view, false, false);
|
||||
assertEquals(2, viewAttachListener.attaches);
|
||||
assertEquals(2, viewAttachListener.detaches);
|
||||
assertEquals(1, viewAttachListener.detachAfterStops);
|
||||
|
||||
ViewUtils.reportAttached(view, true, false);
|
||||
ViewUtils.reportAttached(child, true, false);
|
||||
assertEquals(2, viewAttachListener.attaches);
|
||||
assertEquals(2, viewAttachListener.detaches);
|
||||
assertEquals(1, viewAttachListener.detachAfterStops);
|
||||
|
||||
viewAttachHandler.onActivityStarted();
|
||||
assertEquals(3, viewAttachListener.attaches);
|
||||
assertEquals(2, viewAttachListener.detaches);
|
||||
assertEquals(1, viewAttachListener.detachAfterStops);
|
||||
}
|
||||
|
||||
private static class CountingViewAttachListener implements ViewAttachListener {
|
||||
int attaches;
|
||||
int detaches;
|
||||
int detachAfterStops;
|
||||
|
||||
@Override
|
||||
public void onAttached(View view) {
|
||||
public void onAttached() {
|
||||
attaches++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetached(View view) {
|
||||
public void onDetached(boolean fromActivityStop) {
|
||||
detaches++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewDetachAfterStop() {
|
||||
detachAfterStops++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -45,14 +45,19 @@ public class ActivityProxy {
|
||||
return this;
|
||||
}
|
||||
|
||||
public ActivityProxy stop() {
|
||||
public ActivityProxy stop(boolean detachView) {
|
||||
activityController.stop();
|
||||
view.setAttached(false);
|
||||
|
||||
if (detachView) {
|
||||
view.setAttached(false);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public ActivityProxy destroy() {
|
||||
activityController.destroy();
|
||||
view.setAttached(false);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,11 +33,11 @@ public class TestController extends Controller {
|
||||
FrameLayout view = new AttachFakingFrameLayout(inflater.getContext());
|
||||
view.setId(VIEW_ID);
|
||||
|
||||
FrameLayout childContainer1 = new FrameLayout(inflater.getContext());
|
||||
FrameLayout childContainer1 = new AttachFakingFrameLayout(inflater.getContext());
|
||||
childContainer1.setId(CHILD_VIEW_ID_1);
|
||||
view.addView(childContainer1);
|
||||
|
||||
FrameLayout childContainer2 = new FrameLayout(inflater.getContext());
|
||||
FrameLayout childContainer2 = new AttachFakingFrameLayout(inflater.getContext());
|
||||
childContainer2.setId(CHILD_VIEW_ID_2);
|
||||
view.addView(childContainer2);
|
||||
|
||||
|
||||
+3
-2
@@ -22,8 +22,9 @@ public class CircularRevealChangeHandlerCompat extends CircularRevealChangeHandl
|
||||
return super.getAnimator(container, from, to, isPush, toAddedToContainer);
|
||||
} else {
|
||||
AnimatorSet animator = new AnimatorSet();
|
||||
if (to != null && toAddedToContainer) {
|
||||
animator.play(ObjectAnimator.ofFloat(to, View.ALPHA, 0, 1));
|
||||
if (to != null) {
|
||||
float start = toAddedToContainer ? 0 : to.getAlpha();
|
||||
animator.play(ObjectAnimator.ofFloat(to, View.ALPHA, start, 1));
|
||||
}
|
||||
|
||||
if (from != null) {
|
||||
|
||||
+3
-2
@@ -18,8 +18,9 @@ public class ScaleFadeChangeHandler extends AnimatorChangeHandler {
|
||||
@Override @NonNull
|
||||
protected Animator getAnimator(@NonNull ViewGroup container, View from, View to, boolean isPush, boolean toAddedToContainer) {
|
||||
AnimatorSet animator = new AnimatorSet();
|
||||
if (to != null && toAddedToContainer) {
|
||||
animator.play(ObjectAnimator.ofFloat(to, View.ALPHA, 0, 1));
|
||||
if (to != null) {
|
||||
float start = toAddedToContainer ? 0 : to.getAlpha();
|
||||
animator.play(ObjectAnimator.ofFloat(to, View.ALPHA, start, 1));
|
||||
}
|
||||
|
||||
if (from != null) {
|
||||
|
||||
+10
-5
@@ -8,9 +8,11 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
import com.bluelinelabs.conductor.Router;
|
||||
import com.bluelinelabs.conductor.RouterTransaction;
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
import com.bluelinelabs.conductor.support.ControllerPagerAdapter;
|
||||
import com.bluelinelabs.conductor.support.RouterPagerAdapter;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@@ -23,13 +25,16 @@ public class PagerController extends BaseController {
|
||||
@BindView(R.id.tab_layout) TabLayout tabLayout;
|
||||
@BindView(R.id.view_pager) ViewPager viewPager;
|
||||
|
||||
private final ControllerPagerAdapter pagerAdapter;
|
||||
private final RouterPagerAdapter pagerAdapter;
|
||||
|
||||
public PagerController() {
|
||||
pagerAdapter = new ControllerPagerAdapter(this, false) {
|
||||
pagerAdapter = new RouterPagerAdapter(this) {
|
||||
@Override
|
||||
public Controller getItem(int position) {
|
||||
return new ChildController(String.format(Locale.getDefault(), "Child #%d (Swipe to see more)", position), PAGE_COLORS[position], true);
|
||||
public void configureRouter(Router router, int position) {
|
||||
if (!router.hasRootController()) {
|
||||
Controller page = new ChildController(String.format(Locale.getDefault(), "Child #%d (Swipe to see more)", position), PAGE_COLORS[position], true);
|
||||
router.setRoot(RouterTransaction.with(page));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 65 KiB |
+1
-1
@@ -1,4 +1,4 @@
|
||||
VERSION_NAME=2.0.8-SNAPSHOT
|
||||
VERSION_NAME=2.1.1-SNAPSHOT
|
||||
VERSION_CODE=2
|
||||
GROUP=com.bluelinelabs
|
||||
|
||||
|
||||
Reference in New Issue
Block a user