Compare commits
170 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 55e8aebb87 | |||
| 272c507f91 | |||
| 8e945cde52 | |||
| c17287db67 | |||
| 625a3fcfd6 | |||
| dd2ecf2f83 | |||
| 0efc2b3daf | |||
| 4b4c3a82b8 | |||
| 7d0599fc5d | |||
| 0adea89d34 | |||
| 044363517c | |||
| a2f11d5d51 | |||
| bcd8ddbfb5 | |||
| 479e3f0474 | |||
| c298ca905c | |||
| aebb19effa | |||
| e64fe1c610 | |||
| 2357297531 | |||
| 926f7da6ce | |||
| d260dfcf12 | |||
| a84b2fb4c4 | |||
| 6aca3f578a | |||
| 45ab54b5d7 | |||
| ca84419e0c | |||
| a04dec7ec1 | |||
| 81a499d121 | |||
| ff8ab621bc | |||
| cdb5e5a978 | |||
| effa410eae | |||
| df27bfaa3d | |||
| a865c210b6 | |||
| 7820748cce | |||
| 2147b2aa5e | |||
| 19418617dd | |||
| e1924bf8a7 | |||
| 6d3faaebe3 | |||
| 17639129b9 | |||
| 1c809095ec | |||
| 3bc563de38 | |||
| 7beb94f8cc | |||
| 2b32a30c1a | |||
| 893ffc0461 | |||
| 0df11e3224 | |||
| 75ad389424 | |||
| 00577823dc | |||
| 671a117b96 | |||
| 3d1c2d392c | |||
| b7611e1a1b | |||
| d13af316d3 | |||
| 7d5cc26ea4 | |||
| c4d881ac47 | |||
| 0a53b9f07a | |||
| c2ad655af2 | |||
| a888073e1b | |||
| df68655b13 | |||
| 86227ae3b3 | |||
| 97878b1ad6 | |||
| 314ee2b456 | |||
| f4ef47c2d2 | |||
| 769d552e88 | |||
| afa4b69d7a | |||
| a9bdf0dd06 | |||
| 6ffa94ed3a | |||
| 690001ed2a | |||
| 90e015b6b3 | |||
| 44bcd0f977 | |||
| 60d0fabcf4 | |||
| 10a1c8af3e | |||
| 6834df73e6 | |||
| 04d40a5b90 | |||
| be40900e1e | |||
| f16f7b6d2c | |||
| f74f8391b6 | |||
| 2ce8c0a45d | |||
| 77ad6b4512 | |||
| efbdf913bd | |||
| dfb01389f7 | |||
| e390261b53 | |||
| 27f5275172 | |||
| d15f2b68ab | |||
| 44ed19858b | |||
| e7c195d910 | |||
| 23a4dbbb60 | |||
| 0ac81767dd | |||
| 54cdc51557 | |||
| 09ce640d9c | |||
| 01df673a34 | |||
| 553bae0be5 | |||
| 48dc4abcbe | |||
| 4a814afb5f | |||
| 9cd225e704 | |||
| c8640af1ac | |||
| 43c825f7c2 | |||
| 7334ed5300 | |||
| 7ea4872ff8 | |||
| 9655170bd2 | |||
| acce9b1702 | |||
| 95baa8baa3 | |||
| 2388fa2d06 | |||
| 285eb59da0 | |||
| ae42ee1674 | |||
| 638b2ad311 | |||
| 96e068d348 | |||
| db359d906b | |||
| 11185458b3 | |||
| 977db6b5bf | |||
| 803c20e093 | |||
| 9948cb4652 | |||
| 07a579b939 | |||
| 104d96e6e2 | |||
| e0f40a9fce | |||
| bc8e0c5b2c | |||
| 2b6e41f895 | |||
| b633523d0e | |||
| c5eb7fc89e | |||
| cf6837a41a | |||
| e297242264 | |||
| 550e7e0aa1 | |||
| 90f21d99a5 | |||
| 641e0dc43c | |||
| 91c993b005 | |||
| 39ab4723ff | |||
| 3769e706af | |||
| 26efe8f062 | |||
| 8a890644ee | |||
| b2ffa7f7f6 | |||
| 960b931744 | |||
| 47158da05e | |||
| b9c22d267d | |||
| 46e6fac6db | |||
| dc68990bff | |||
| e4f7e9e175 | |||
| 4ab99b68da | |||
| b8bd64e078 | |||
| 812d1f8911 | |||
| ac8288fece | |||
| d2dd786b72 | |||
| 7cf30b820c | |||
| 1120896438 | |||
| 093238cc52 | |||
| c153e29273 | |||
| 2757c7a4b6 | |||
| 46091a7c99 | |||
| b0a5da2b82 | |||
| 0026566ba0 | |||
| 9f640380bf | |||
| a3faaede61 | |||
| 94d8add220 | |||
| 6d4b5a5ef6 | |||
| ae27c5e453 | |||
| e4d23a7c74 | |||
| 778cdcfd58 | |||
| 5e730947aa | |||
| 71b10c7365 | |||
| 8c323b9613 | |||
| af45aae110 | |||
| d4c7e5791e | |||
| dc4c3a9709 | |||
| ef84fbd547 | |||
| 431569763e | |||
| 7b5bab3681 | |||
| 86b32afdcb | |||
| 557e4c2122 | |||
| 8b29c1cd56 | |||
| 30ef5a187b | |||
| ebe69bf98b | |||
| a907263ab8 | |||
| 7528437f65 | |||
| e2e8260876 | |||
| 6dfc5839cc |
Regular → Executable
Executable → Regular
+7
-9
@@ -3,9 +3,11 @@ language: android
|
||||
android:
|
||||
components:
|
||||
- tools
|
||||
- build-tools-23.0.2
|
||||
- android-23
|
||||
- build-tools-25.0.3
|
||||
- android-25
|
||||
- extra-android-m2repository
|
||||
licenses:
|
||||
- '.+'
|
||||
|
||||
script:
|
||||
- ./gradlew test
|
||||
@@ -14,13 +16,9 @@ jdk:
|
||||
- oraclejdk8
|
||||
|
||||
branches:
|
||||
except:
|
||||
- v2
|
||||
- feature/nested_controllers
|
||||
- feature/save_restore_lifecycle
|
||||
|
||||
before_install:
|
||||
- chmod +x .buildscript/deploy_snapshot.sh
|
||||
only:
|
||||
- develop
|
||||
- master
|
||||
|
||||
after_success:
|
||||
- .buildscript/deploy_snapshot.sh
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
[](https://travis-ci.org/bluelinelabs/Conductor) [](http://android-arsenal.com/details/1/3361)
|
||||
[](https://travis-ci.org/bluelinelabs/Conductor) [](http://android-arsenal.com/details/1/3361) [](http://javadoc.io/doc/com.bluelinelabs/conductor)
|
||||
|
||||
# Conductor
|
||||
|
||||
A small, yet full-featured framework that allows building View-based Android applications. Conductor provides a light-weight wrapper around standard Android Views that does just about everything you'd want:
|
||||
|
||||
| Conductor
|
||||
------|------------------------------
|
||||
| | Conductor |
|
||||
|-----------|-------------|
|
||||
:tada: | Easy integration
|
||||
:point_up: | Single Activity apps without using Fragments
|
||||
:recycle: | Simple but powerful lifecycle management
|
||||
@@ -20,26 +20,41 @@ Conductor is architecture-agnostic and does not try to force any design decision
|
||||
## Installation
|
||||
|
||||
```gradle
|
||||
compile 'com.bluelinelabs:conductor:1.1.5'
|
||||
compile 'com.bluelinelabs:conductor:2.1.4'
|
||||
|
||||
// If you want the components that go along with
|
||||
// Android's support libraries (currently just a PagerAdapter):
|
||||
compile 'com.bluelinelabs:conductor-support:1.1.5'
|
||||
compile 'com.bluelinelabs:conductor-support:2.1.4'
|
||||
|
||||
// If you want RxJava/RxAndroid lifecycle support:
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:1.1.5'
|
||||
// If you want RxJava lifecycle support:
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:2.1.4'
|
||||
|
||||
// If you want RxJava2 lifecycle support:
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle2:2.1.4'
|
||||
|
||||
// If you want RxJava2 Autodispose support:
|
||||
compile 'com.bluelinelabs:conductor-autodispose:2.1.4'
|
||||
|
||||
// If you want Controllers that are Lifecycle-aware (architecture components):
|
||||
compile 'com.bluelinelabs:conductor-arch-components-lifecycle:0.1.1'
|
||||
```
|
||||
|
||||
SNAPSHOT:
|
||||
|
||||
Just use `2.1.5-SNAPSHOT` as your version number in any of the above dependencies and add the url to the snapshot repository:
|
||||
|
||||
```gradle
|
||||
compile 'com.bluelinelabs:conductor:1.1.6-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor-support:1.1.6-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:1.1.6-SNAPSHOT'
|
||||
allprojects {
|
||||
repositories {
|
||||
...
|
||||
|
||||
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
|
||||
}
|
||||
```
|
||||
|
||||
## Components to Know
|
||||
|
||||
| Conductor Components
|
||||
| | Conductor Components |
|
||||
------|------------------------------
|
||||
__Controller__ | The Controller is the View wrapper that will give you all of your lifecycle management features. Think of it as a lighter-weight and more predictable Fragment alternative with an easier to manage lifecycle.
|
||||
__Router__ | A Router implements navigation and backstack handling for Controllers. Router objects are attached to Activity/containing ViewGroup pairs. Routers do not directly render or push Views to the container ViewGroup, but instead defer this responsibility to the ControllerChangeHandler specified in a given transaction.
|
||||
@@ -53,7 +68,7 @@ __ControllerTransaction__ | Transactions are used to define data about adding Co
|
||||
```java
|
||||
public class MainActivity extends Activity {
|
||||
|
||||
private Router mRouter;
|
||||
private Router router;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@@ -61,17 +76,17 @@ public class MainActivity extends Activity {
|
||||
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
ViewGroup container = (ViewGroup)findViewById(R.id.controller_container)
|
||||
ViewGroup container = (ViewGroup)findViewById(R.id.controller_container);
|
||||
|
||||
mRouter = Conductor.attachRouter(this, container, savedInstanceState);
|
||||
if (!mRouter.hasRootController()) {
|
||||
mRouter.setRoot(new HomeController());
|
||||
router = Conductor.attachRouter(this, container, savedInstanceState);
|
||||
if (!router.hasRootController()) {
|
||||
router.setRoot(RouterTransaction.with(new HomeController()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (!mRouter.handleBack()) {
|
||||
if (!router.handleBack()) {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
@@ -102,7 +117,7 @@ public class HomeController extends Controller {
|
||||
|
||||
The lifecycle of a Controller is significantly simpler to understand than that of a Fragment. A lifecycle diagram is shown below:
|
||||
|
||||

|
||||

|
||||
|
||||
## Advanced Topics
|
||||
|
||||
@@ -112,11 +127,11 @@ The lifecycle of a Controller is significantly simpler to understand than that o
|
||||
### Custom Change Handlers
|
||||
`ControllerChangeHandler` can be subclassed in order to perform different functions when changing between two `Controllers`. Two convenience `ControllerChangeHandler` subclasses are included to cover most basic needs: `AnimatorChangeHandler`, which will use an `Animator` object to transition between two views, and `TransitionChangeHandler`, which will use Lollipop's `Transition` framework for transitioning between views.
|
||||
|
||||
### Child Controllers
|
||||
`addChildController` can be called on a `Controller` in order to add nested `Controller`s. Child `Controller`s will receive all lifecycle callbacks that parents get.
|
||||
### Child Routers & Controllers
|
||||
`getChildRouter` can be called on a `Controller` in order to get a nested `Router` into which child `Controller`s can be pushed. This enables creating advanced layouts, such as Master/Detail.
|
||||
|
||||
### RxJava Lifecycle
|
||||
If the RxLifecycle dependency has been added, there is an `RxController` available that can be used along with the standard [RxLifecycle library](https://github.com/trello/RxLifecycle). There is also a `ControllerLifecycleProvider` available if you do not wish to use this subclass.
|
||||
If the RxLifecycle dependency has been added, there is an `RxController` available that can be used along with the standard [RxLifecycle library](https://github.com/trello/RxLifecycle). There is also a `ControllerLifecycleProvider` available if you do not wish to use this subclass.
|
||||
|
||||
## License
|
||||
```
|
||||
|
||||
Executable → Regular
+3
-6
@@ -3,19 +3,16 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.1.0'
|
||||
classpath 'com.android.tools.build:gradle:2.3.2'
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
maven { url 'https://maven.google.com' }
|
||||
}
|
||||
}
|
||||
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion = '2.10'
|
||||
}
|
||||
|
||||
apply from: rootProject.file('dependencies.gradle')
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
apply plugin: 'java'
|
||||
|
||||
targetCompatibility = JavaVersion.VERSION_1_7
|
||||
sourceCompatibility = JavaVersion.VERSION_1_7
|
||||
|
||||
configurations {
|
||||
lintChecks
|
||||
}
|
||||
@@ -8,6 +11,9 @@ dependencies {
|
||||
compile rootProject.ext.lintapi
|
||||
compile rootProject.ext.lintchecks
|
||||
|
||||
testCompile rootProject.ext.lint
|
||||
testCompile rootProject.ext.lintTests
|
||||
|
||||
lintChecks files(jar)
|
||||
}
|
||||
|
||||
|
||||
+23
-54
@@ -1,7 +1,6 @@
|
||||
package com.bluelinelabs.conductor.lint;
|
||||
|
||||
import com.android.annotations.NonNull;
|
||||
import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
|
||||
import com.android.tools.lint.client.api.JavaEvaluator;
|
||||
import com.android.tools.lint.detector.api.Category;
|
||||
import com.android.tools.lint.detector.api.Detector;
|
||||
import com.android.tools.lint.detector.api.Implementation;
|
||||
@@ -9,21 +8,13 @@ import com.android.tools.lint.detector.api.Issue;
|
||||
import com.android.tools.lint.detector.api.JavaContext;
|
||||
import com.android.tools.lint.detector.api.Scope;
|
||||
import com.android.tools.lint.detector.api.Severity;
|
||||
import com.android.tools.lint.detector.api.Speed;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.ast.ClassDeclaration;
|
||||
import lombok.ast.ConstructorDeclaration;
|
||||
import lombok.ast.Node;
|
||||
import lombok.ast.NormalTypeBody;
|
||||
import lombok.ast.StrictListAccessor;
|
||||
import lombok.ast.TypeMember;
|
||||
import lombok.ast.VariableDefinition;
|
||||
|
||||
public final class ControllerChangeHandlerIssueDetector extends Detector implements Detector.JavaScanner, Detector.ClassScanner {
|
||||
public final class ControllerChangeHandlerIssueDetector extends Detector implements Detector.JavaPsiScanner {
|
||||
|
||||
public static final Issue ISSUE =
|
||||
Issue.create("ValidControllerChangeHandler", "ControllerChangeHandler not instantiatable",
|
||||
@@ -34,67 +25,45 @@ public final class ControllerChangeHandlerIssueDetector extends Detector impleme
|
||||
|
||||
public ControllerChangeHandlerIssueDetector() { }
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Speed getSpeed() {
|
||||
return Speed.FAST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> applicableSuperClasses() {
|
||||
return Collections.singletonList("com.bluelinelabs.conductor.ControllerChangeHandler");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkClass(@NonNull JavaContext context, ClassDeclaration node,
|
||||
@NonNull Node declarationOrAnonymous, @NonNull ResolvedClass cls) {
|
||||
|
||||
if (node == null) {
|
||||
public void checkClass(JavaContext context, PsiClass declaration) {
|
||||
final JavaEvaluator evaluator = context.getEvaluator();
|
||||
if (evaluator.isAbstract(declaration)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int flags = node.astModifiers().getEffectiveModifierFlags();
|
||||
if ((flags & Modifier.ABSTRACT) != 0) {
|
||||
if (!evaluator.isPublic(declaration)) {
|
||||
String message = String.format("This ControllerChangeHandler class should be public (%1$s)", declaration.getQualifiedName());
|
||||
context.report(ISSUE, declaration, context.getLocation(declaration), message);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((flags & Modifier.PUBLIC) == 0) {
|
||||
String message = String.format("This ControllerChangeHandler class should be public (%1$s)", cls.getName());
|
||||
context.report(ISSUE, node, context.getLocation(node.astName()), message);
|
||||
if (declaration.getContainingClass() != null && !evaluator.isStatic(declaration)) {
|
||||
String message = String.format("This ControllerChangeHandler inner class should be static (%1$s)", declaration.getQualifiedName());
|
||||
context.report(ISSUE, declaration, context.getLocation(declaration), message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cls.getContainingClass() != null && (flags & Modifier.STATIC) == 0) {
|
||||
String message = String.format("This ControllerChangeHandler inner class should be static (%1$s)", cls.getName());
|
||||
context.report(ISSUE, node, context.getLocation(node.astName()), message);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean hasConstructor = false;
|
||||
boolean hasDefaultConstructor = false;
|
||||
NormalTypeBody body = node.astBody();
|
||||
if (body != null) {
|
||||
for (TypeMember member : body.astMembers()) {
|
||||
if (member instanceof ConstructorDeclaration) {
|
||||
hasConstructor = true;
|
||||
ConstructorDeclaration constructor = (ConstructorDeclaration)member;
|
||||
|
||||
if (constructor.astModifiers().isPublic()) {
|
||||
StrictListAccessor<VariableDefinition, ConstructorDeclaration> params = constructor.astParameters();
|
||||
if (params.isEmpty()) {
|
||||
hasDefaultConstructor = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
PsiMethod[] constructors = declaration.getConstructors();
|
||||
for (PsiMethod constructor : constructors) {
|
||||
if (evaluator.isPublic(constructor)) {
|
||||
if (constructor.getParameterList().getParametersCount() == 0) {
|
||||
hasDefaultConstructor = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasConstructor && !hasDefaultConstructor) {
|
||||
if (constructors.length > 0 && !hasDefaultConstructor) {
|
||||
String message = String.format(
|
||||
"This ControllerChangeHandler needs to have a public default constructor (`%1$s`)",
|
||||
cls.getName());
|
||||
context.report(ISSUE, node, context.getLocation(node.astName()), message);
|
||||
"This ControllerChangeHandler needs to have a public default constructor (`%1$s`)", declaration.getQualifiedName());
|
||||
context.report(ISSUE, declaration, context.getLocation(declaration), message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+30
-56
@@ -1,8 +1,7 @@
|
||||
package com.bluelinelabs.conductor.lint;
|
||||
|
||||
import com.android.SdkConstants;
|
||||
import com.android.annotations.NonNull;
|
||||
import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
|
||||
import com.android.tools.lint.client.api.JavaEvaluator;
|
||||
import com.android.tools.lint.detector.api.Category;
|
||||
import com.android.tools.lint.detector.api.Detector;
|
||||
import com.android.tools.lint.detector.api.Implementation;
|
||||
@@ -10,21 +9,14 @@ import com.android.tools.lint.detector.api.Issue;
|
||||
import com.android.tools.lint.detector.api.JavaContext;
|
||||
import com.android.tools.lint.detector.api.Scope;
|
||||
import com.android.tools.lint.detector.api.Severity;
|
||||
import com.android.tools.lint.detector.api.Speed;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.PsiParameter;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.ast.ClassDeclaration;
|
||||
import lombok.ast.ConstructorDeclaration;
|
||||
import lombok.ast.Node;
|
||||
import lombok.ast.NormalTypeBody;
|
||||
import lombok.ast.StrictListAccessor;
|
||||
import lombok.ast.TypeMember;
|
||||
import lombok.ast.VariableDefinition;
|
||||
|
||||
public final class ControllerIssueDetector extends Detector implements Detector.JavaScanner, Detector.ClassScanner {
|
||||
public final class ControllerIssueDetector extends Detector implements Detector.JavaPsiScanner {
|
||||
|
||||
public static final Issue ISSUE =
|
||||
Issue.create("ValidController", "Controller not instantiatable",
|
||||
@@ -35,74 +27,56 @@ public final class ControllerIssueDetector extends Detector implements Detector.
|
||||
|
||||
public ControllerIssueDetector() { }
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Speed getSpeed() {
|
||||
return Speed.FAST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> applicableSuperClasses() {
|
||||
return Collections.singletonList("com.bluelinelabs.conductor.Controller");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkClass(@NonNull JavaContext context, ClassDeclaration node,
|
||||
@NonNull Node declarationOrAnonymous, @NonNull ResolvedClass cls) {
|
||||
|
||||
if (node == null) {
|
||||
public void checkClass(JavaContext context, PsiClass declaration) {
|
||||
final JavaEvaluator evaluator = context.getEvaluator();
|
||||
if (evaluator.isAbstract(declaration)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int flags = node.astModifiers().getEffectiveModifierFlags();
|
||||
if ((flags & Modifier.ABSTRACT) != 0) {
|
||||
if (!evaluator.isPublic(declaration)) {
|
||||
String message = String.format("This Controller class should be public (%1$s)", declaration.getQualifiedName());
|
||||
context.report(ISSUE, declaration, context.getLocation(declaration), message);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((flags & Modifier.PUBLIC) == 0) {
|
||||
String message = String.format("This Controller class should be public (%1$s)", cls.getName());
|
||||
context.report(ISSUE, node, context.getLocation(node.astName()), message);
|
||||
if (declaration.getContainingClass() != null && !evaluator.isStatic(declaration)) {
|
||||
String message = String.format("This Controller inner class should be static (%1$s)", declaration.getQualifiedName());
|
||||
context.report(ISSUE, declaration, context.getLocation(declaration), message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cls.getContainingClass() != null && (flags & Modifier.STATIC) == 0) {
|
||||
String message = String.format("This Controller inner class should be static (%1$s)", cls.getName());
|
||||
context.report(ISSUE, node, context.getLocation(node.astName()), message);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean hasConstructor = false;
|
||||
boolean hasDefaultConstructor = false;
|
||||
boolean hasBundleConstructor = false;
|
||||
NormalTypeBody body = node.astBody();
|
||||
if (body != null) {
|
||||
for (TypeMember member : body.astMembers()) {
|
||||
if (member instanceof ConstructorDeclaration) {
|
||||
hasConstructor = true;
|
||||
ConstructorDeclaration constructor = (ConstructorDeclaration)member;
|
||||
PsiMethod[] constructors = declaration.getConstructors();
|
||||
for (PsiMethod constructor : constructors) {
|
||||
if (evaluator.isPublic(constructor)) {
|
||||
PsiParameter[] parameters = constructor.getParameterList().getParameters();
|
||||
|
||||
if (constructor.astModifiers().isPublic()) {
|
||||
StrictListAccessor<VariableDefinition, ConstructorDeclaration> params = constructor.astParameters();
|
||||
if (params.isEmpty()) {
|
||||
hasDefaultConstructor = true;
|
||||
break;
|
||||
} else if (params.size() == 1 &&
|
||||
(params.first().astTypeReference().getTypeName().equals(SdkConstants.CLASS_BUNDLE)) ||
|
||||
params.first().astTypeReference().getTypeName().equals("Bundle")) {
|
||||
hasBundleConstructor = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (parameters.length == 0) {
|
||||
hasDefaultConstructor = true;
|
||||
break;
|
||||
} else if (parameters.length == 1 &&
|
||||
parameters[0].getType().equalsToText(SdkConstants.CLASS_BUNDLE) ||
|
||||
parameters[0].getType().equalsToText("Bundle")) {
|
||||
hasBundleConstructor = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasConstructor && !hasDefaultConstructor && !hasBundleConstructor) {
|
||||
if (constructors.length > 0 && !hasDefaultConstructor && !hasBundleConstructor) {
|
||||
String message = String.format(
|
||||
"This Controller needs to have either a public default constructor or a" +
|
||||
" public single-argument constructor that takes a Bundle. (`%1$s`)",
|
||||
cls.getName());
|
||||
context.report(ISSUE, node, context.getLocation(node.astName()), message);
|
||||
declaration.getQualifiedName());
|
||||
context.report(ISSUE, declaration, context.getLocation(declaration), message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,4 +11,4 @@ public final class IssueRegistry extends com.android.tools.lint.client.api.Issue
|
||||
ControllerIssueDetector.ISSUE,
|
||||
ControllerChangeHandlerIssueDetector.ISSUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
package com.bluelinelabs.conductor.lint;
|
||||
|
||||
import com.android.tools.lint.checks.infrastructure.LintDetectorTest;
|
||||
import com.android.tools.lint.detector.api.Detector;
|
||||
import com.android.tools.lint.detector.api.Issue;
|
||||
|
||||
import org.intellij.lang.annotations.Language;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
public class ControllerChangeHandlerDetectorTest extends LintDetectorTest {
|
||||
|
||||
private static final String NO_WARNINGS = "No warnings.";
|
||||
private static final String CONSTRUCTOR =
|
||||
"src/test/SampleHandler.java:2: Error: This ControllerChangeHandler needs to have a public default constructor (test.SampleHandler) [ValidControllerChangeHandler]\n"
|
||||
+ "public class SampleHandler extends com.bluelinelabs.conductor.ControllerChangeHandler {\n"
|
||||
+ "^\n"
|
||||
+ "1 errors, 0 warnings\n";
|
||||
private static final String PRIVATE_CLASS_ERROR =
|
||||
"src/test/SampleHandler.java:2: Error: This ControllerChangeHandler class should be public (test.SampleHandler) [ValidControllerChangeHandler]\n"
|
||||
+ "private class SampleHandler extends com.bluelinelabs.conductor.ControllerChangeHandler {\n"
|
||||
+ "^\n"
|
||||
+ "1 errors, 0 warnings\n";
|
||||
|
||||
public void testWithNoConstructor() throws Exception {
|
||||
@Language("JAVA") String source = ""
|
||||
+ "package test;\n"
|
||||
+ "public class SampleHandler extends com.bluelinelabs.conductor.ControllerChangeHandler {\n"
|
||||
+ "}";
|
||||
assertThat(lintProject(java(source))).isEqualTo(NO_WARNINGS);
|
||||
}
|
||||
|
||||
public void testWithEmptyConstructor() throws Exception {
|
||||
@Language("JAVA") String source = ""
|
||||
+ "package test;\n"
|
||||
+ "public class SampleHandler extends com.bluelinelabs.conductor.ControllerChangeHandler {\n"
|
||||
+ " public SampleHandler() { }\n"
|
||||
+ "}";
|
||||
assertThat(lintProject(java(source))).isEqualTo(NO_WARNINGS);
|
||||
}
|
||||
|
||||
public void testWithInvalidConstructor() throws Exception {
|
||||
@Language("JAVA") String source = ""
|
||||
+ "package test;\n"
|
||||
+ "public class SampleHandler extends com.bluelinelabs.conductor.ControllerChangeHandler {\n"
|
||||
+ " public SampleHandler(int number) { }\n"
|
||||
+ "}";
|
||||
assertThat(lintProject(java(source))).isEqualTo(CONSTRUCTOR);
|
||||
}
|
||||
|
||||
public void testWithEmptyAndInvalidConstructor() throws Exception {
|
||||
@Language("JAVA") String source = ""
|
||||
+ "package test;\n"
|
||||
+ "public class SampleHandler extends com.bluelinelabs.conductor.ControllerChangeHandler {\n"
|
||||
+ " public SampleHandler() { }\n"
|
||||
+ " public SampleHandler(int number) { }\n"
|
||||
+ "}";
|
||||
assertThat(lintProject(java(source))).isEqualTo(NO_WARNINGS);
|
||||
}
|
||||
|
||||
public void testWithPrivateConstructor() throws Exception {
|
||||
@Language("JAVA") String source = ""
|
||||
+ "package test;\n"
|
||||
+ "public class SampleHandler extends com.bluelinelabs.conductor.ControllerChangeHandler {\n"
|
||||
+ " private SampleHandler() { }\n"
|
||||
+ "}";
|
||||
assertThat(lintProject(java(source))).isEqualTo(CONSTRUCTOR);
|
||||
}
|
||||
|
||||
public void testWithPrivateClass() throws Exception {
|
||||
@Language("JAVA") String source = ""
|
||||
+ "package test;\n"
|
||||
+ "private class SampleHandler extends com.bluelinelabs.conductor.ControllerChangeHandler {\n"
|
||||
+ " public SampleHandler() { }\n"
|
||||
+ "}";
|
||||
assertThat(lintProject(java(source))).isEqualTo(PRIVATE_CLASS_ERROR);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Detector getDetector() {
|
||||
return new ControllerChangeHandlerIssueDetector();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Issue> getIssues() {
|
||||
return Collections.singletonList(ControllerChangeHandlerIssueDetector.ISSUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean allowCompilationErrors() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
package com.bluelinelabs.conductor.lint;
|
||||
|
||||
import com.android.tools.lint.checks.infrastructure.LintDetectorTest;
|
||||
import com.android.tools.lint.detector.api.Detector;
|
||||
import com.android.tools.lint.detector.api.Issue;
|
||||
|
||||
import org.intellij.lang.annotations.Language;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
public class ControllerDetectorTest extends LintDetectorTest {
|
||||
|
||||
private static final String NO_WARNINGS = "No warnings.";
|
||||
private static final String CONSTRUCTOR_ERROR =
|
||||
"src/test/SampleController.java:2: Error: This Controller needs to have either a public default constructor or a public single-argument constructor that takes a Bundle. (test.SampleController) [ValidController]\n"
|
||||
+ "public class SampleController extends com.bluelinelabs.conductor.Controller {\n"
|
||||
+ "^\n"
|
||||
+ "1 errors, 0 warnings\n";
|
||||
private static final String CLASS_ERROR =
|
||||
"src/test/SampleController.java:2: Error: This Controller class should be public (test.SampleController) [ValidController]\n"
|
||||
+ "private class SampleController extends com.bluelinelabs.conductor.Controller {\n"
|
||||
+ "^\n"
|
||||
+ "1 errors, 0 warnings\n";
|
||||
|
||||
public void testWithNoConstructor() throws Exception {
|
||||
@Language("JAVA") String source = ""
|
||||
+ "package test;\n"
|
||||
+ "public class SampleController extends com.bluelinelabs.conductor.Controller {\n"
|
||||
+ "}";
|
||||
assertThat(lintProject(java(source))).isEqualTo(NO_WARNINGS);
|
||||
}
|
||||
|
||||
public void testWithEmptyConstructor() throws Exception {
|
||||
@Language("JAVA") String source = ""
|
||||
+ "package test;\n"
|
||||
+ "public class SampleController extends com.bluelinelabs.conductor.Controller {\n"
|
||||
+ " public SampleController() { }\n"
|
||||
+ "}";
|
||||
assertThat(lintProject(java(source))).isEqualTo(NO_WARNINGS);
|
||||
}
|
||||
|
||||
public void testWithInvalidConstructor() throws Exception {
|
||||
@Language("JAVA") String source = ""
|
||||
+ "package test;\n"
|
||||
+ "public class SampleController extends com.bluelinelabs.conductor.Controller {\n"
|
||||
+ " public SampleController(int number) { }\n"
|
||||
+ "}";
|
||||
assertThat(lintProject(java(source))).isEqualTo(CONSTRUCTOR_ERROR);
|
||||
}
|
||||
|
||||
public void testWithEmptyAndInvalidConstructor() throws Exception {
|
||||
@Language("JAVA") String source = ""
|
||||
+ "package test;\n"
|
||||
+ "public class SampleController extends com.bluelinelabs.conductor.Controller {\n"
|
||||
+ " public SampleController() { }\n"
|
||||
+ " public SampleController(int number) { }\n"
|
||||
+ "}";
|
||||
assertThat(lintProject(java(source))).isEqualTo(NO_WARNINGS);
|
||||
}
|
||||
|
||||
public void testWithPrivateConstructor() throws Exception {
|
||||
@Language("JAVA") String source = ""
|
||||
+ "package test;\n"
|
||||
+ "public class SampleController extends com.bluelinelabs.conductor.Controller {\n"
|
||||
+ " private SampleController() { }\n"
|
||||
+ "}";
|
||||
assertThat(lintProject(java(source))).isEqualTo(CONSTRUCTOR_ERROR);
|
||||
}
|
||||
|
||||
public void testWithPrivateClass() throws Exception {
|
||||
@Language("JAVA") String source = ""
|
||||
+ "package test;\n"
|
||||
+ "private class SampleController extends com.bluelinelabs.conductor.Controller {\n"
|
||||
+ " public SampleController() { }\n"
|
||||
+ "}";
|
||||
assertThat(lintProject(java(source))).isEqualTo(CLASS_ERROR);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Detector getDetector() {
|
||||
return new ControllerIssueDetector();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Issue> getIssues() {
|
||||
return Collections.singletonList(ControllerIssueDetector.ISSUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean allowCompilationErrors() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Executable → Regular
+3
-7
@@ -7,10 +7,6 @@ android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
@@ -25,9 +21,9 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile rootProject.ext.supportAppCompat
|
||||
compile rootProject.ext.archComopnentsLifecycle
|
||||
|
||||
compile project(':conductor')
|
||||
}
|
||||
|
||||
ext.artifactId = 'conductor-support'
|
||||
|
||||
ext.artifactId = 'conductor-arch-components-lifecycle'
|
||||
@@ -0,0 +1,4 @@
|
||||
POM_NAME=Conductor Architecture Components Lifecycle Extensions
|
||||
POM_ARTIFACT_ID=conductor-archlifecycle
|
||||
POM_PACKAGING=aar
|
||||
VERSION_NAME=0.1.2-SNAPSHOT
|
||||
@@ -0,0 +1,3 @@
|
||||
<manifest package="com.bluelinelabs.conductor.archlifecycle">
|
||||
<application />
|
||||
</manifest>
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package com.bluelinelabs.conductor.archlifecycle;
|
||||
|
||||
import android.arch.lifecycle.Lifecycle.Event;
|
||||
import android.arch.lifecycle.Lifecycle.State;
|
||||
import android.arch.lifecycle.LifecycleRegistry;
|
||||
import android.arch.lifecycle.LifecycleRegistryOwner;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.View;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
import com.bluelinelabs.conductor.Controller.LifecycleListener;
|
||||
|
||||
public class ControllerLifecycleRegistryOwner extends LifecycleListener implements LifecycleRegistryOwner {
|
||||
|
||||
private final LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);
|
||||
|
||||
public ControllerLifecycleRegistryOwner(Controller controller) {
|
||||
lifecycleRegistry.handleLifecycleEvent(Event.ON_CREATE);
|
||||
lifecycleRegistry.markState(State.CREATED);
|
||||
|
||||
controller.addLifecycleListener(new LifecycleListener() {
|
||||
@Override
|
||||
public void preCreateView(@NonNull Controller controller) {
|
||||
lifecycleRegistry.handleLifecycleEvent(Event.ON_START);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postCreateView(@NonNull Controller controller, @NonNull View view) {
|
||||
lifecycleRegistry.markState(State.STARTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preAttach(@NonNull Controller controller, @NonNull View view) {
|
||||
lifecycleRegistry.handleLifecycleEvent(Event.ON_RESUME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postAttach(@NonNull Controller controller, @NonNull View view) {
|
||||
lifecycleRegistry.markState(State.RESUMED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDetach(@NonNull Controller controller, @NonNull View view) {
|
||||
lifecycleRegistry.handleLifecycleEvent(Event.ON_PAUSE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postDetach(@NonNull Controller controller, @NonNull View view) {
|
||||
lifecycleRegistry.markState(State.STARTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestroyView(@NonNull Controller controller, @NonNull View view) {
|
||||
lifecycleRegistry.handleLifecycleEvent(Event.ON_STOP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postDestroyView(@NonNull Controller controller) {
|
||||
lifecycleRegistry.markState(State.CREATED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestroy(@NonNull Controller controller) {
|
||||
lifecycleRegistry.handleLifecycleEvent(Event.ON_DESTROY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postDestroy(@NonNull Controller controller) {
|
||||
lifecycleRegistry.markState(State.DESTROYED);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public LifecycleRegistry getLifecycle() {
|
||||
return lifecycleRegistry;
|
||||
}
|
||||
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.bluelinelabs.conductor.archlifecycle;
|
||||
|
||||
import android.arch.lifecycle.LifecycleRegistry;
|
||||
import android.arch.lifecycle.LifecycleRegistryOwner;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
|
||||
public abstract class LifecycleController extends Controller implements LifecycleRegistryOwner {
|
||||
|
||||
private final ControllerLifecycleRegistryOwner lifecycleRegistryOwner = new ControllerLifecycleRegistryOwner(this);
|
||||
|
||||
@Override
|
||||
public LifecycleRegistry getLifecycle() {
|
||||
return lifecycleRegistryOwner.getLifecycle();
|
||||
}
|
||||
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.bluelinelabs.conductor.archlifecycle;
|
||||
|
||||
import android.arch.lifecycle.LifecycleRegistry;
|
||||
import android.arch.lifecycle.LifecycleRegistryOwner;
|
||||
|
||||
import com.bluelinelabs.conductor.RestoreViewOnCreateController;
|
||||
|
||||
public abstract class LifecycleRestoreViewOnCreateController extends RestoreViewOnCreateController implements LifecycleRegistryOwner {
|
||||
|
||||
private final ControllerLifecycleRegistryOwner lifecycleRegistryOwner = new ControllerLifecycleRegistryOwner(this);
|
||||
|
||||
@Override
|
||||
public LifecycleRegistry getLifecycle() {
|
||||
return lifecycleRegistryOwner.getLifecycle();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
apply from: rootProject.file('dependencies.gradle')
|
||||
apply from: rootProject.file('gradle-mvn-push.gradle')
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode Integer.parseInt(project.VERSION_CODE)
|
||||
versionName project.VERSION_NAME
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile rootProject.ext.rxJava2
|
||||
compile rootProject.ext.autodispose
|
||||
|
||||
compile project(':conductor')
|
||||
}
|
||||
|
||||
ext.artifactId = 'conductor-autodispose'
|
||||
@@ -0,0 +1,3 @@
|
||||
POM_NAME=Conductor AutoDispose Extensions
|
||||
POM_ARTIFACT_ID=conductor-autodispose
|
||||
POM_PACKAGING=aar
|
||||
@@ -0,0 +1,3 @@
|
||||
<manifest package="com.bluelinelabs.conductor.autodispose">
|
||||
<application />
|
||||
</manifest>
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.bluelinelabs.conductor.autodispose;
|
||||
|
||||
public enum ControllerEvent {
|
||||
|
||||
CREATE,
|
||||
CONTEXT_AVAILABLE,
|
||||
CREATE_VIEW,
|
||||
ATTACH,
|
||||
DETACH,
|
||||
DESTROY_VIEW,
|
||||
CONTEXT_UNAVAILABLE,
|
||||
DESTROY
|
||||
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package com.bluelinelabs.conductor.autodispose;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.View;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
import com.uber.autodispose.OutsideLifecycleException;
|
||||
|
||||
import io.reactivex.subjects.BehaviorSubject;
|
||||
|
||||
public class ControllerLifecycleSubjectHelper {
|
||||
private ControllerLifecycleSubjectHelper() { }
|
||||
|
||||
@NonNull
|
||||
public static BehaviorSubject<ControllerEvent> create(Controller controller) {
|
||||
ControllerEvent initialState;
|
||||
if (controller.isBeingDestroyed() || controller.isDestroyed()) {
|
||||
throw new OutsideLifecycleException("Cannot bind to Controller lifecycle when outside of it.");
|
||||
} else if (controller.isAttached()) {
|
||||
initialState = ControllerEvent.ATTACH;
|
||||
} else if (controller.getView() != null) {
|
||||
initialState = ControllerEvent.CREATE_VIEW;
|
||||
} else if (controller.getActivity() != null) {
|
||||
initialState = ControllerEvent.CONTEXT_AVAILABLE;
|
||||
} else {
|
||||
initialState = ControllerEvent.CREATE;
|
||||
}
|
||||
|
||||
final BehaviorSubject<ControllerEvent> subject = BehaviorSubject.createDefault(initialState);
|
||||
|
||||
controller.addLifecycleListener(new Controller.LifecycleListener() {
|
||||
@Override
|
||||
public void preContextAvailable(@NonNull Controller controller) {
|
||||
subject.onNext(ControllerEvent.CONTEXT_AVAILABLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preCreateView(@NonNull Controller controller) {
|
||||
subject.onNext(ControllerEvent.CREATE_VIEW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preAttach(@NonNull Controller controller, @NonNull View view) {
|
||||
subject.onNext(ControllerEvent.ATTACH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDetach(@NonNull Controller controller, @NonNull View view) {
|
||||
subject.onNext(ControllerEvent.DETACH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestroyView(@NonNull Controller controller, @NonNull View view) {
|
||||
subject.onNext(ControllerEvent.DESTROY_VIEW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preContextUnavailable(@NonNull Controller controller, @NonNull Context context) {
|
||||
subject.onNext(ControllerEvent.CONTEXT_UNAVAILABLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestroy(@NonNull Controller controller) {
|
||||
subject.onNext(ControllerEvent.DESTROY);
|
||||
}
|
||||
});
|
||||
|
||||
return subject;
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package com.bluelinelabs.conductor.autodispose;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
import com.uber.autodispose.LifecycleScopeProvider;
|
||||
import com.uber.autodispose.OutsideLifecycleException;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.functions.Function;
|
||||
import io.reactivex.subjects.BehaviorSubject;
|
||||
|
||||
public class ControllerScopeProvider implements LifecycleScopeProvider<ControllerEvent> {
|
||||
private static final Function<ControllerEvent, ControllerEvent> CORRESPONDING_EVENTS =
|
||||
new Function<ControllerEvent, ControllerEvent>() {
|
||||
@Override
|
||||
public ControllerEvent apply(ControllerEvent lastEvent) throws Exception {
|
||||
switch (lastEvent) {
|
||||
case CREATE:
|
||||
return ControllerEvent.DESTROY;
|
||||
case CONTEXT_AVAILABLE:
|
||||
return ControllerEvent.CONTEXT_UNAVAILABLE;
|
||||
case CREATE_VIEW:
|
||||
return ControllerEvent.DESTROY_VIEW;
|
||||
case ATTACH:
|
||||
return ControllerEvent.DETACH;
|
||||
case DETACH:
|
||||
return ControllerEvent.DESTROY;
|
||||
default:
|
||||
throw new OutsideLifecycleException("Cannot bind to Controller lifecycle when outside of it.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@NonNull private final BehaviorSubject<ControllerEvent> lifecycleSubject;
|
||||
|
||||
public static ControllerScopeProvider from(@NonNull Controller controller) {
|
||||
return new ControllerScopeProvider(controller);
|
||||
}
|
||||
|
||||
private ControllerScopeProvider(@NonNull Controller controller) {
|
||||
lifecycleSubject = ControllerLifecycleSubjectHelper.create(controller);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Observable<ControllerEvent> lifecycle() {
|
||||
return lifecycleSubject.hide();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<ControllerEvent, ControllerEvent> correspondingEvents() {
|
||||
return CORRESPONDING_EVENTS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControllerEvent peekLifecycle() {
|
||||
return lifecycleSubject.getValue();
|
||||
}
|
||||
}
|
||||
Executable → Regular
+1
-6
@@ -7,10 +7,6 @@ android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
@@ -26,11 +22,10 @@ android {
|
||||
|
||||
dependencies {
|
||||
compile rootProject.ext.rxJava
|
||||
compile rootProject.ext.rxAndroid
|
||||
compile rootProject.ext.rxLifecycle
|
||||
compile rootProject.ext.rxLifecycleAndroid
|
||||
|
||||
compile project(':conductor')
|
||||
}
|
||||
|
||||
ext.artifactId = 'conductor-rxlifecycle'
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
POM_NAME=Conductor RxLifecycle Extensions
|
||||
POM_ARTIFACT_ID=conductor-rxlifecycle
|
||||
POM_PACKAGING=jar
|
||||
POM_PACKAGING=aar
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
<manifest package="com.bluelinelabs.conductor.rxlifecycle">
|
||||
<application />
|
||||
</manifest>
|
||||
</manifest>
|
||||
+2
@@ -3,10 +3,12 @@ package com.bluelinelabs.conductor.rxlifecycle;
|
||||
public enum ControllerEvent {
|
||||
|
||||
CREATE,
|
||||
CONTEXT_AVAILABLE,
|
||||
CREATE_VIEW,
|
||||
ATTACH,
|
||||
DETACH,
|
||||
DESTROY_VIEW,
|
||||
CONTEXT_UNAVAILABLE,
|
||||
DESTROY
|
||||
|
||||
}
|
||||
+26
-1
@@ -1,10 +1,12 @@
|
||||
package com.bluelinelabs.conductor.rxlifecycle;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.View;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
import com.bluelinelabs.conductor.Controller.LifecycleListener;
|
||||
import com.trello.rxlifecycle.OutsideLifecycleException;
|
||||
|
||||
import rx.subjects.BehaviorSubject;
|
||||
|
||||
@@ -17,9 +19,27 @@ public class ControllerLifecycleSubjectHelper {
|
||||
private ControllerLifecycleSubjectHelper() { }
|
||||
|
||||
public static BehaviorSubject<ControllerEvent> create(Controller controller) {
|
||||
final BehaviorSubject<ControllerEvent> subject = BehaviorSubject.create(ControllerEvent.CREATE);
|
||||
ControllerEvent initialState;
|
||||
if (controller.isBeingDestroyed() || controller.isDestroyed()) {
|
||||
throw new OutsideLifecycleException("Cannot bind to Controller lifecycle when outside of it.");
|
||||
} else if (controller.isAttached()) {
|
||||
initialState = ControllerEvent.ATTACH;
|
||||
} else if (controller.getView() != null) {
|
||||
initialState = ControllerEvent.CREATE_VIEW;
|
||||
} else if (controller.getActivity() != null) {
|
||||
initialState = ControllerEvent.CONTEXT_AVAILABLE;
|
||||
} else {
|
||||
initialState = ControllerEvent.CREATE;
|
||||
}
|
||||
|
||||
final BehaviorSubject<ControllerEvent> subject = BehaviorSubject.create(initialState);
|
||||
|
||||
controller.addLifecycleListener(new LifecycleListener() {
|
||||
@Override
|
||||
public void preContextAvailable(@NonNull Controller controller) {
|
||||
subject.onNext(ControllerEvent.CONTEXT_AVAILABLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preCreateView(@NonNull Controller controller) {
|
||||
subject.onNext(ControllerEvent.CREATE_VIEW);
|
||||
@@ -40,6 +60,11 @@ public class ControllerLifecycleSubjectHelper {
|
||||
subject.onNext(ControllerEvent.DESTROY_VIEW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preContextUnavailable(@NonNull Controller controller, @NonNull Context context) {
|
||||
subject.onNext(ControllerEvent.CONTEXT_UNAVAILABLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestroy(@NonNull Controller controller) {
|
||||
subject.onNext(ControllerEvent.DESTROY);
|
||||
+11
-9
@@ -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,9 +15,9 @@ 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> mLifecycleSubject;
|
||||
private final BehaviorSubject<ControllerEvent> lifecycleSubject;
|
||||
|
||||
public RxController() {
|
||||
this(null);
|
||||
@@ -23,28 +25,28 @@ public abstract class RxController extends Controller implements ControllerLifec
|
||||
|
||||
public RxController(Bundle args) {
|
||||
super(args);
|
||||
mLifecycleSubject = ControllerLifecycleSubjectHelper.create(this);
|
||||
lifecycleSubject = ControllerLifecycleSubjectHelper.create(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
@CheckResult
|
||||
public final Observable<ControllerEvent> lifecycle() {
|
||||
return mLifecycleSubject.asObservable();
|
||||
return lifecycleSubject.asObservable();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
@CheckResult
|
||||
public final <T> Observable.Transformer<T, T> bindUntilEvent(@NonNull ControllerEvent event) {
|
||||
return RxLifecycle.bindUntilEvent(mLifecycleSubject, 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() {
|
||||
return RxControllerLifecycle.bindController(mLifecycleSubject);
|
||||
public final <T> LifecycleTransformer<T> bindToLifecycle() {
|
||||
return RxControllerLifecycle.bindController(lifecycleSubject);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -14,10 +14,10 @@ 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
|
||||
* @return a reusable {@link rx.Observable.Transformer} that unsubscribes the source during the Controller lifecycle
|
||||
*/
|
||||
@NonNull
|
||||
@CheckResult
|
||||
@@ -32,6 +32,8 @@ public class RxControllerLifecycle {
|
||||
switch (lastEvent) {
|
||||
case CREATE:
|
||||
return ControllerEvent.DESTROY;
|
||||
case CONTEXT_AVAILABLE:
|
||||
return ControllerEvent.CONTEXT_UNAVAILABLE;
|
||||
case ATTACH:
|
||||
return ControllerEvent.DETACH;
|
||||
case CREATE_VIEW:
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package com.bluelinelabs.conductor.rxlifecycle;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.CheckResult;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.bluelinelabs.conductor.RestoreViewOnCreateController;
|
||||
import com.trello.rxlifecycle.LifecycleProvider;
|
||||
import com.trello.rxlifecycle.LifecycleTransformer;
|
||||
import com.trello.rxlifecycle.RxLifecycle;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.subjects.BehaviorSubject;
|
||||
|
||||
/**
|
||||
* A base {@link RestoreViewOnCreateController} that can be used to expose lifecycle events using RxJava
|
||||
*/
|
||||
public abstract class RxRestoreViewOnCreateController extends RestoreViewOnCreateController implements LifecycleProvider<ControllerEvent> {
|
||||
|
||||
private final BehaviorSubject<ControllerEvent> lifecycleSubject;
|
||||
|
||||
public RxRestoreViewOnCreateController() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public RxRestoreViewOnCreateController(Bundle args) {
|
||||
super(args);
|
||||
lifecycleSubject = ControllerLifecycleSubjectHelper.create(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
@CheckResult
|
||||
public final Observable<ControllerEvent> lifecycle() {
|
||||
return lifecycleSubject.asObservable();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
@CheckResult
|
||||
public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull ControllerEvent event) {
|
||||
return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
@CheckResult
|
||||
public final <T> LifecycleTransformer<T> bindToLifecycle() {
|
||||
return RxControllerLifecycle.bindController(lifecycleSubject);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
apply from: rootProject.file('dependencies.gradle')
|
||||
apply from: rootProject.file('gradle-mvn-push.gradle')
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode Integer.parseInt(project.VERSION_CODE)
|
||||
versionName project.VERSION_NAME
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile rootProject.ext.rxJava2
|
||||
compile rootProject.ext.rxLifecycle2
|
||||
compile rootProject.ext.rxLifecycleAndroid2
|
||||
|
||||
compile project(':conductor')
|
||||
}
|
||||
|
||||
ext.artifactId = 'conductor-rxlifecycle2'
|
||||
@@ -0,0 +1,3 @@
|
||||
POM_NAME=Conductor RxLifecycle2 Extensions
|
||||
POM_ARTIFACT_ID=conductor-rxlifecycle2
|
||||
POM_PACKAGING=aar
|
||||
@@ -0,0 +1,3 @@
|
||||
<manifest package="com.bluelinelabs.conductor.rxlifecycle2">
|
||||
<application />
|
||||
</manifest>
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.bluelinelabs.conductor.rxlifecycle2;
|
||||
|
||||
public enum ControllerEvent {
|
||||
|
||||
CREATE,
|
||||
CONTEXT_AVAILABLE,
|
||||
CREATE_VIEW,
|
||||
ATTACH,
|
||||
DETACH,
|
||||
DESTROY_VIEW,
|
||||
CONTEXT_UNAVAILABLE,
|
||||
DESTROY
|
||||
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package com.bluelinelabs.conductor.rxlifecycle2;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.View;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
import com.trello.rxlifecycle2.OutsideLifecycleException;
|
||||
|
||||
import io.reactivex.subjects.BehaviorSubject;
|
||||
|
||||
public class ControllerLifecycleSubjectHelper {
|
||||
private ControllerLifecycleSubjectHelper() {
|
||||
}
|
||||
|
||||
public static BehaviorSubject<ControllerEvent> create(Controller controller) {
|
||||
ControllerEvent initialState;
|
||||
if (controller.isBeingDestroyed() || controller.isDestroyed()) {
|
||||
throw new OutsideLifecycleException("Cannot bind to Controller lifecycle when outside of it.");
|
||||
} else if (controller.isAttached()) {
|
||||
initialState = ControllerEvent.ATTACH;
|
||||
} else if (controller.getView() != null) {
|
||||
initialState = ControllerEvent.CREATE_VIEW;
|
||||
} else if (controller.getActivity() != null) {
|
||||
initialState = ControllerEvent.CONTEXT_AVAILABLE;
|
||||
} else {
|
||||
initialState = ControllerEvent.CREATE;
|
||||
}
|
||||
|
||||
final BehaviorSubject<ControllerEvent> subject = BehaviorSubject.createDefault(initialState);
|
||||
|
||||
controller.addLifecycleListener(new Controller.LifecycleListener() {
|
||||
@Override
|
||||
public void preContextAvailable(@NonNull Controller controller) {
|
||||
subject.onNext(ControllerEvent.CONTEXT_AVAILABLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preCreateView(@NonNull Controller controller) {
|
||||
subject.onNext(ControllerEvent.CREATE_VIEW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preAttach(@NonNull Controller controller, @NonNull View view) {
|
||||
subject.onNext(ControllerEvent.ATTACH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDetach(@NonNull Controller controller, @NonNull View view) {
|
||||
subject.onNext(ControllerEvent.DETACH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestroyView(@NonNull Controller controller, @NonNull View view) {
|
||||
subject.onNext(ControllerEvent.DESTROY_VIEW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preContextUnavailable(@NonNull Controller controller, @NonNull Context context) {
|
||||
subject.onNext(ControllerEvent.CONTEXT_UNAVAILABLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestroy(@NonNull Controller controller) {
|
||||
subject.onNext(ControllerEvent.DESTROY);
|
||||
}
|
||||
});
|
||||
|
||||
return subject;
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package com.bluelinelabs.conductor.rxlifecycle2;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.CheckResult;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
import com.trello.rxlifecycle2.LifecycleProvider;
|
||||
import com.trello.rxlifecycle2.LifecycleTransformer;
|
||||
import com.trello.rxlifecycle2.RxLifecycle;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.subjects.BehaviorSubject;
|
||||
|
||||
/**
|
||||
* A base {@link Controller} that can be used to expose lifecycle events using RxJava
|
||||
*/
|
||||
public abstract class RxController extends Controller implements LifecycleProvider<ControllerEvent> {
|
||||
private final BehaviorSubject<ControllerEvent> lifecycleSubject;
|
||||
|
||||
public RxController(){
|
||||
this(null);
|
||||
}
|
||||
|
||||
public RxController(@Nullable Bundle args) {
|
||||
super(args);
|
||||
lifecycleSubject = ControllerLifecycleSubjectHelper.create(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
@CheckResult
|
||||
public final Observable<ControllerEvent> lifecycle() {
|
||||
return lifecycleSubject.hide();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
@CheckResult
|
||||
public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull ControllerEvent event) {
|
||||
return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
@CheckResult
|
||||
public final <T> LifecycleTransformer<T> bindToLifecycle() {
|
||||
return RxControllerLifecycle.bindController(lifecycleSubject);
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package com.bluelinelabs.conductor.rxlifecycle2;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import com.trello.rxlifecycle2.LifecycleTransformer;
|
||||
import com.trello.rxlifecycle2.OutsideLifecycleException;
|
||||
import com.trello.rxlifecycle2.RxLifecycle;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.functions.Function;
|
||||
|
||||
public class RxControllerLifecycle {
|
||||
|
||||
/**
|
||||
* Binds the given source to a Controller lifecycle. This is the Controller version of
|
||||
* {@link com.trello.rxlifecycle2.android.RxLifecycleAndroid#bindFragment(Observable)}.
|
||||
*
|
||||
* @param lifecycle the lifecycle sequence of a Controller
|
||||
* @return a reusable {@link io.reactivex.ObservableTransformer} that unsubscribes the source during the Controller lifecycle
|
||||
*/
|
||||
public static <T> LifecycleTransformer<T> bindController(@NonNull final Observable<ControllerEvent> lifecycle) {
|
||||
return RxLifecycle.bind(lifecycle, CONTROLLER_LIFECYCLE);
|
||||
}
|
||||
|
||||
private static final Function<ControllerEvent, ControllerEvent> CONTROLLER_LIFECYCLE =
|
||||
new Function<ControllerEvent, ControllerEvent>() {
|
||||
@Override
|
||||
public ControllerEvent apply(ControllerEvent lastEvent) {
|
||||
switch (lastEvent) {
|
||||
case CREATE:
|
||||
return ControllerEvent.DESTROY;
|
||||
case CONTEXT_AVAILABLE:
|
||||
return ControllerEvent.CONTEXT_UNAVAILABLE;
|
||||
case ATTACH:
|
||||
return ControllerEvent.DETACH;
|
||||
case CREATE_VIEW:
|
||||
return ControllerEvent.DESTROY_VIEW;
|
||||
case DETACH:
|
||||
return ControllerEvent.DESTROY;
|
||||
default:
|
||||
throw new OutsideLifecycleException("Cannot bind to Controller lifecycle when outside of it.");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package com.bluelinelabs.conductor.rxlifecycle2;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.CheckResult;
|
||||
import android.support.annotation.NonNull;
|
||||
import com.bluelinelabs.conductor.RestoreViewOnCreateController;
|
||||
import com.trello.rxlifecycle2.LifecycleProvider;
|
||||
import com.trello.rxlifecycle2.LifecycleTransformer;
|
||||
import com.trello.rxlifecycle2.RxLifecycle;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.subjects.BehaviorSubject;
|
||||
|
||||
public abstract class RxRestoreViewOnCreateController extends RestoreViewOnCreateController implements LifecycleProvider<ControllerEvent> {
|
||||
private final BehaviorSubject<ControllerEvent> lifecycleSubject;
|
||||
|
||||
public RxRestoreViewOnCreateController() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public RxRestoreViewOnCreateController(Bundle args) {
|
||||
super(args);
|
||||
lifecycleSubject = ControllerLifecycleSubjectHelper.create(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
@CheckResult
|
||||
public final Observable<ControllerEvent> lifecycle() {
|
||||
return lifecycleSubject.hide();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
@CheckResult
|
||||
public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull ControllerEvent event) {
|
||||
return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
@CheckResult
|
||||
public final <T> LifecycleTransformer<T> bindToLifecycle() {
|
||||
return RxControllerLifecycle.bindController(lifecycleSubject);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'de.mobilej.unmock:UnMockPlugin:0.6.0'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'de.mobilej.unmock'
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode Integer.parseInt(project.VERSION_CODE)
|
||||
versionName project.VERSION_NAME
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testCompile rootProject.ext.junit
|
||||
testCompile rootProject.ext.roboelectric
|
||||
|
||||
compile rootProject.ext.supportAppCompat
|
||||
compile project(':conductor')
|
||||
|
||||
unmock 'org.robolectric:android-all:4.3_r2-robolectric-0'
|
||||
}
|
||||
|
||||
unMock {
|
||||
keep "android.os.Bundle"
|
||||
keep "android.os.BaseBundle"
|
||||
}
|
||||
|
||||
ext.artifactId = 'conductor-support'
|
||||
|
||||
apply from: rootProject.file('dependencies.gradle')
|
||||
apply from: rootProject.file('gradle-mvn-push.gradle')
|
||||
@@ -1,3 +1,3 @@
|
||||
POM_NAME=Conductor Support Extensions
|
||||
POM_ARTIFACT_ID=conductor-support
|
||||
POM_PACKAGING=jar
|
||||
POM_PACKAGING=aar
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
<manifest package="com.bluelinelabs.conductor.support">
|
||||
<application />
|
||||
</manifest>
|
||||
</manifest>
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
package com.bluelinelabs.conductor.support;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.view.PagerAdapter;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
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";
|
||||
private static final String KEY_SAVES_STATE = "ControllerPagerAdapter.savesState";
|
||||
private static final String KEY_VISIBLE_PAGE_IDS_KEYS = "ControllerPagerAdapter.visiblePageIds.keys";
|
||||
private static final String KEY_VISIBLE_PAGE_IDS_VALUES = "ControllerPagerAdapter.visiblePageIds.values";
|
||||
|
||||
private final Controller host;
|
||||
private boolean savesState;
|
||||
private SparseArray<Bundle> savedPages = new SparseArray<>();
|
||||
private SparseArray<String> visiblePageIds = new SparseArray<>();
|
||||
|
||||
/**
|
||||
* Creates a new ControllerPagerAdapter using the passed host.
|
||||
*/
|
||||
public ControllerPagerAdapter(@NonNull Controller host, boolean saveControllerState) {
|
||||
this.host = host;
|
||||
savesState = saveControllerState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Controller associated with a specified position.
|
||||
*/
|
||||
@NonNull
|
||||
public abstract Controller getItem(int position);
|
||||
|
||||
@Override
|
||||
public Object instantiateItem(ViewGroup container, int position) {
|
||||
final String name = makeControllerName(container.getId(), getItemId(position));
|
||||
|
||||
Router router = host.getChildRouter(container, name);
|
||||
if (savesState && !router.hasRootController()) {
|
||||
Bundle routerSavedState = savedPages.get(position);
|
||||
|
||||
if (routerSavedState != null) {
|
||||
router.restoreInstanceState(routerSavedState);
|
||||
}
|
||||
}
|
||||
|
||||
final Controller controller;
|
||||
if (!router.hasRootController()) {
|
||||
controller = getItem(position);
|
||||
router.setRoot(RouterTransaction.with(controller).tag(name));
|
||||
} else {
|
||||
router.rebindIfNeeded();
|
||||
controller = router.getControllerWithTag(name);
|
||||
}
|
||||
|
||||
if (controller != null) {
|
||||
visiblePageIds.put(position, controller.getInstanceId());
|
||||
}
|
||||
|
||||
return router.getControllerWithTag(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyItem(ViewGroup container, int position, Object object) {
|
||||
Router router = ((Controller)object).getRouter();
|
||||
|
||||
if (savesState) {
|
||||
Bundle savedState = new Bundle();
|
||||
router.saveInstanceState(savedState);
|
||||
savedPages.put(position, savedState);
|
||||
}
|
||||
|
||||
visiblePageIds.remove(position);
|
||||
|
||||
host.removeChildRouter(router);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isViewFromObject(View view, Object object) {
|
||||
return ((Controller)object).getView() == view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable saveState() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putBoolean(KEY_SAVES_STATE, savesState);
|
||||
bundle.putSparseParcelableArray(KEY_SAVED_PAGES, savedPages);
|
||||
|
||||
int[] visiblePageIdsKeys = new int[visiblePageIds.size()];
|
||||
String[] visiblePageIdsValues = new String[visiblePageIds.size()];
|
||||
for (int i = 0; i < visiblePageIds.size(); i++) {
|
||||
visiblePageIdsKeys[i] = visiblePageIds.keyAt(i);
|
||||
visiblePageIdsValues[i] = visiblePageIds.valueAt(i);
|
||||
}
|
||||
bundle.putIntArray(KEY_VISIBLE_PAGE_IDS_KEYS, visiblePageIdsKeys);
|
||||
bundle.putStringArray(KEY_VISIBLE_PAGE_IDS_VALUES, visiblePageIdsValues);
|
||||
|
||||
return bundle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreState(Parcelable state, ClassLoader loader) {
|
||||
Bundle bundle = (Bundle)state;
|
||||
if (state != null) {
|
||||
savesState = bundle.getBoolean(KEY_SAVES_STATE, false);
|
||||
savedPages = bundle.getSparseParcelableArray(KEY_SAVED_PAGES);
|
||||
|
||||
int[] visiblePageIdsKeys = bundle.getIntArray(KEY_VISIBLE_PAGE_IDS_KEYS);
|
||||
String[] visiblePageIdsValues = bundle.getStringArray(KEY_VISIBLE_PAGE_IDS_VALUES);
|
||||
visiblePageIds = new SparseArray<>(visiblePageIdsKeys.length);
|
||||
for (int i = 0; i < visiblePageIdsKeys.length; i++) {
|
||||
visiblePageIds.put(visiblePageIdsKeys[i], visiblePageIdsValues[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
String instanceId = visiblePageIds.get(position);
|
||||
if (instanceId != null) {
|
||||
return host.getRouter().getControllerWithInstanceId(instanceId);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
private static String makeControllerName(int viewId, long id) {
|
||||
return viewId + ":" + id;
|
||||
}
|
||||
|
||||
}
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
package com.bluelinelabs.conductor.support;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.view.PagerAdapter;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
import com.bluelinelabs.conductor.Router;
|
||||
import com.bluelinelabs.conductor.RouterTransaction;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An adapter for ViewPagers that uses Routers as pages
|
||||
*/
|
||||
public abstract class RouterPagerAdapter extends PagerAdapter {
|
||||
|
||||
private static final String KEY_SAVED_PAGES = "RouterPagerAdapter.savedStates";
|
||||
private static final String KEY_MAX_PAGES_TO_STATE_SAVE = "RouterPagerAdapter.maxPagesToStateSave";
|
||||
private static final String KEY_SAVE_PAGE_HISTORY = "RouterPagerAdapter.savedPageHistory";
|
||||
|
||||
private final Controller host;
|
||||
private int maxPagesToStateSave = Integer.MAX_VALUE;
|
||||
private SparseArray<Bundle> savedPages = new SparseArray<>();
|
||||
private SparseArray<Router> visibleRouters = new SparseArray<>();
|
||||
private ArrayList<Integer> savedPageHistory = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Creates a new RouterPagerAdapter using the passed host.
|
||||
*/
|
||||
public RouterPagerAdapter(@NonNull Controller host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a router is instantiated. Here the router's root should be set if needed.
|
||||
*
|
||||
* @param router The router used for the page
|
||||
* @param position The page position to be instantiated.
|
||||
*/
|
||||
public abstract void configureRouter(@NonNull Router router, int position);
|
||||
|
||||
/**
|
||||
* Sets the maximum number of pages that will have their states saved. When this number is exceeded,
|
||||
* the page that was state saved least recently will have its state removed from the save data.
|
||||
*/
|
||||
public void setMaxPagesToStateSave(int maxPagesToStateSave) {
|
||||
if (maxPagesToStateSave < 0) {
|
||||
throw new IllegalArgumentException("Only positive integers may be passed for maxPagesToStateSave.");
|
||||
}
|
||||
|
||||
this.maxPagesToStateSave = maxPagesToStateSave;
|
||||
|
||||
ensurePagesSaved();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object instantiateItem(ViewGroup container, int position) {
|
||||
final String name = makeRouterName(container.getId(), getItemId(position));
|
||||
|
||||
Router router = host.getChildRouter(container, name);
|
||||
if (!router.hasRootController()) {
|
||||
Bundle routerSavedState = savedPages.get(position);
|
||||
|
||||
if (routerSavedState != null) {
|
||||
router.restoreInstanceState(routerSavedState);
|
||||
savedPages.remove(position);
|
||||
}
|
||||
}
|
||||
|
||||
router.rebindIfNeeded();
|
||||
configureRouter(router, position);
|
||||
|
||||
visibleRouters.put(position, router);
|
||||
return router;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyItem(ViewGroup container, int position, Object object) {
|
||||
Router router = (Router)object;
|
||||
|
||||
Bundle savedState = new Bundle();
|
||||
router.saveInstanceState(savedState);
|
||||
savedPages.put(position, savedState);
|
||||
|
||||
savedPageHistory.remove((Integer)position);
|
||||
savedPageHistory.add(position);
|
||||
|
||||
ensurePagesSaved();
|
||||
|
||||
host.removeChildRouter(router);
|
||||
|
||||
visibleRouters.remove(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isViewFromObject(View view, Object object) {
|
||||
Router router = (Router)object;
|
||||
final List<RouterTransaction> backstack = router.getBackstack();
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
if (transaction.controller().getView() == view) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable saveState() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putSparseParcelableArray(KEY_SAVED_PAGES, savedPages);
|
||||
bundle.putInt(KEY_MAX_PAGES_TO_STATE_SAVE, maxPagesToStateSave);
|
||||
bundle.putIntegerArrayList(KEY_SAVE_PAGE_HISTORY, savedPageHistory);
|
||||
return bundle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreState(Parcelable state, ClassLoader loader) {
|
||||
Bundle bundle = (Bundle)state;
|
||||
if (state != null) {
|
||||
savedPages = bundle.getSparseParcelableArray(KEY_SAVED_PAGES);
|
||||
maxPagesToStateSave = bundle.getInt(KEY_MAX_PAGES_TO_STATE_SAVE);
|
||||
savedPageHistory = bundle.getIntegerArrayList(KEY_SAVE_PAGE_HISTORY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
return visibleRouters.get(position);
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
SparseArray<Bundle> getSavedPages() {
|
||||
return savedPages;
|
||||
}
|
||||
|
||||
private void ensurePagesSaved() {
|
||||
while (savedPages.size() > maxPagesToStateSave) {
|
||||
int positionToRemove = savedPageHistory.remove(0);
|
||||
savedPages.remove(positionToRemove);
|
||||
}
|
||||
}
|
||||
|
||||
private static String makeRouterName(int viewId, long id) {
|
||||
return viewId + ":" + id;
|
||||
}
|
||||
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
package com.bluelinelabs.conductor.support;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.SparseArray;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.bluelinelabs.conductor.Conductor;
|
||||
import com.bluelinelabs.conductor.Router;
|
||||
import com.bluelinelabs.conductor.RouterTransaction;
|
||||
import com.bluelinelabs.conductor.support.util.FakePager;
|
||||
import com.bluelinelabs.conductor.support.util.TestController;
|
||||
|
||||
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;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(manifest = Config.NONE)
|
||||
public class StateSaveTests {
|
||||
|
||||
private FakePager pager;
|
||||
private RouterPagerAdapter pagerAdapter;
|
||||
|
||||
public void createActivityController(Bundle savedInstanceState) {
|
||||
ActivityController<Activity> activityController = Robolectric.buildActivity(Activity.class).create().start().resume();
|
||||
Router router = Conductor.attachRouter(activityController.get(), new FrameLayout(activityController.get()), savedInstanceState);
|
||||
TestController controller = new TestController();
|
||||
router.setRoot(RouterTransaction.with(controller));
|
||||
|
||||
pager = new FakePager(new FrameLayout(activityController.get()));
|
||||
pager.setOffscreenPageLimit(1);
|
||||
|
||||
pagerAdapter = new RouterPagerAdapter(controller) {
|
||||
@Override
|
||||
public void configureRouter(@NonNull Router router, int position) {
|
||||
if (!router.hasRootController()) {
|
||||
router.setRoot(RouterTransaction.with(new TestController()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return 20;
|
||||
}
|
||||
};
|
||||
|
||||
pager.setAdapter(pagerAdapter);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
createActivityController(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoMaxSaves() {
|
||||
// Load all pages
|
||||
for (int i = 0; i < pagerAdapter.getCount(); i++) {
|
||||
pager.pageTo(i);
|
||||
}
|
||||
|
||||
pager.pageTo(pagerAdapter.getCount() / 2);
|
||||
|
||||
// Ensure all non-visible pages are saved
|
||||
assertEquals(pagerAdapter.getCount() - 1 - pager.getOffscreenPageLimit() * 2, pagerAdapter.getSavedPages().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxSavedSet() {
|
||||
final int maxPages = 3;
|
||||
pagerAdapter.setMaxPagesToStateSave(maxPages);
|
||||
|
||||
// Load all pages
|
||||
for (int i = 0; i < pagerAdapter.getCount(); i++) {
|
||||
pager.pageTo(i);
|
||||
}
|
||||
|
||||
final int firstSelectedItem = pagerAdapter.getCount() / 2;
|
||||
pager.pageTo(firstSelectedItem);
|
||||
|
||||
SparseArray<Bundle> savedPages = pagerAdapter.getSavedPages();
|
||||
|
||||
// Ensure correct number of pages are saved
|
||||
assertEquals(maxPages, savedPages.size());
|
||||
|
||||
// Ensure correct pages are saved
|
||||
assertEquals(pagerAdapter.getCount() - 3, savedPages.keyAt(0));
|
||||
assertEquals(pagerAdapter.getCount() - 2, savedPages.keyAt(1));
|
||||
assertEquals(pagerAdapter.getCount() - 1, savedPages.keyAt(2));
|
||||
|
||||
final int secondSelectedItem = 1;
|
||||
pager.pageTo(secondSelectedItem);
|
||||
|
||||
savedPages = pagerAdapter.getSavedPages();
|
||||
|
||||
// Ensure correct number of pages are saved
|
||||
assertEquals(maxPages, savedPages.size());
|
||||
|
||||
// Ensure correct pages are saved
|
||||
assertEquals(firstSelectedItem - 1, savedPages.keyAt(0));
|
||||
assertEquals(firstSelectedItem, savedPages.keyAt(1));
|
||||
assertEquals(firstSelectedItem + 1, savedPages.keyAt(2));
|
||||
}
|
||||
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package com.bluelinelabs.conductor.support.util;
|
||||
|
||||
import android.util.SparseArray;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.support.RouterPagerAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class FakePager {
|
||||
|
||||
private ViewGroup container;
|
||||
private int offscreenPageLimit;
|
||||
private final SparseArray<Object> pages = new SparseArray<>();
|
||||
|
||||
private RouterPagerAdapter adapter;
|
||||
|
||||
public FakePager(ViewGroup container) {
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
public void setAdapter(RouterPagerAdapter adapter) {
|
||||
this.adapter = adapter;
|
||||
}
|
||||
|
||||
public void pageTo(int page) {
|
||||
int firstPage = Math.max(0, page - offscreenPageLimit);
|
||||
int lastPage = Math.min(adapter.getCount() - 1, page + offscreenPageLimit);
|
||||
|
||||
List<Integer> pagesI = new ArrayList<>();
|
||||
for (int i = 0; i < pages.size(); i++) {
|
||||
pagesI.add(pages.keyAt(i));
|
||||
}
|
||||
|
||||
for (int i = pages.size() - 1; i >= 0; i--) {
|
||||
int key = pages.keyAt(i);
|
||||
|
||||
if (key < firstPage || key > lastPage) {
|
||||
adapter.destroyItem(container, key, pages.get(key));
|
||||
pages.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
for (int key = firstPage; key <= lastPage; key++) {
|
||||
if (pages.get(key) == null) {
|
||||
pages.put(key, adapter.instantiateItem(container, key));
|
||||
}
|
||||
}
|
||||
|
||||
adapter.setPrimaryItem(container, page, pages.get(page));
|
||||
}
|
||||
|
||||
public int getOffscreenPageLimit() {
|
||||
return offscreenPageLimit;
|
||||
}
|
||||
|
||||
public void setOffscreenPageLimit(int offscreenPageLimit) {
|
||||
this.offscreenPageLimit = offscreenPageLimit;
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.bluelinelabs.conductor.support.util;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
|
||||
public class TestController extends Controller {
|
||||
|
||||
@NonNull @Override
|
||||
protected View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
return new FrameLayout(inflater.getContext());
|
||||
}
|
||||
|
||||
}
|
||||
-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();
|
||||
|
||||
}
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
package com.bluelinelabs.conductor.support;
|
||||
|
||||
import android.support.v4.view.PagerAdapter;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.ChildControllerTransaction;
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
|
||||
/**
|
||||
* An adapter for ViewPagers that will handle adding and removing Controllers
|
||||
*/
|
||||
public abstract class ControllerPagerAdapter extends PagerAdapter {
|
||||
|
||||
private final Controller mHost;
|
||||
|
||||
/**
|
||||
* Creates a new ControllerPagerAdapter using the passed host.
|
||||
*/
|
||||
public ControllerPagerAdapter(Controller host) {
|
||||
mHost = host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Controller associated with a specified position.
|
||||
*/
|
||||
public abstract Controller getItem(int position);
|
||||
|
||||
@Override
|
||||
public Object instantiateItem(ViewGroup container, int position) {
|
||||
final String name = makeControllerName(container.getId(), getItemId(position));
|
||||
|
||||
Controller controller = mHost.getChildController(name);
|
||||
if (controller == null) {
|
||||
controller = getItem(position);
|
||||
|
||||
mHost.addChildController(ChildControllerTransaction.builder(controller, container.getId())
|
||||
.tag(name)
|
||||
.build());
|
||||
}
|
||||
|
||||
return controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyItem(ViewGroup container, int position, Object object) {
|
||||
mHost.removeChildController((Controller)object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isViewFromObject(View view, Object object) {
|
||||
return ((Controller)object).getView() == view;
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
private static String makeControllerName(int viewId, long id) {
|
||||
return viewId + ":" + id;
|
||||
}
|
||||
|
||||
}
|
||||
Executable → Regular
+4
-7
@@ -3,7 +3,7 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'de.mobilej.unmock:UnMockPlugin:0.3.6'
|
||||
classpath 'de.mobilej.unmock:UnMockPlugin:0.6.0'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,10 +14,6 @@ android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
@@ -28,6 +24,7 @@ android {
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode Integer.parseInt(project.VERSION_CODE)
|
||||
versionName project.VERSION_NAME
|
||||
consumerProguardFiles 'proguard-rules.txt'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,11 +39,11 @@ dependencies {
|
||||
compile rootProject.ext.supportAnnotations
|
||||
|
||||
lintChecks project(path: ':conductor-lint', configuration: 'lintChecks')
|
||||
|
||||
unmock 'org.robolectric:android-all:4.3_r2-robolectric-0'
|
||||
}
|
||||
|
||||
unMock {
|
||||
downloadFrom 'https://oss.sonatype.org/content/groups/public/org/robolectric/android-all/4.3_r2-robolectric-0/android-all-4.3_r2-robolectric-0.jar'
|
||||
|
||||
keep "android.os.Bundle"
|
||||
keep "android.os.BaseBundle"
|
||||
keep "android.text.TextUtils"
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
POM_NAME=Conductor
|
||||
POM_ARTIFACT_ID=conductor
|
||||
POM_PACKAGING=jar
|
||||
POM_PACKAGING=aar
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# Retain constructor that is called by using reflection to recreate the Controller
|
||||
-keepclassmembers public class * extends com.bluelinelabs.conductor.Controller {
|
||||
public <init>();
|
||||
public <init>(android.os.Bundle);
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
<manifest package="com.bluelinelabs.conductor">
|
||||
<application />
|
||||
</manifest>
|
||||
</manifest>
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.content.IntentSender.SendIntentException;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
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) {
|
||||
if (this.container != null && this.container instanceof ControllerChangeListener) {
|
||||
removeChangeListener((ControllerChangeListener)this.container);
|
||||
}
|
||||
|
||||
if (container instanceof ControllerChangeListener) {
|
||||
addChangeListener((ControllerChangeListener)container);
|
||||
}
|
||||
|
||||
this.lifecycleHandler = lifecycleHandler;
|
||||
this.container = container;
|
||||
|
||||
watchContainerAttach();
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityDestroyed(@NonNull Activity activity) {
|
||||
super.onActivityDestroyed(activity);
|
||||
lifecycleHandler = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void invalidateOptionsMenu() {
|
||||
if (lifecycleHandler != null && lifecycleHandler.getFragmentManager() != null) {
|
||||
lifecycleHandler.getFragmentManager().invalidateOptionsMenu();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
||||
lifecycleHandler.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
void startActivity(@NonNull Intent intent) {
|
||||
lifecycleHandler.startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
void startActivityForResult(@NonNull String instanceId, @NonNull Intent intent, int requestCode) {
|
||||
lifecycleHandler.startActivityForResult(instanceId, intent, requestCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
void startActivityForResult(@NonNull String instanceId, @NonNull Intent intent, int requestCode, @Nullable Bundle options) {
|
||||
lifecycleHandler.startActivityForResult(instanceId, intent, requestCode, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
void startIntentSenderForResult(@NonNull String instanceId, @NonNull IntentSender intent, int requestCode, @Nullable Intent fillInIntent,
|
||||
int flagsMask, int flagsValues, int extraFlags, @Nullable Bundle options) throws SendIntentException {
|
||||
lifecycleHandler.startIntentSenderForResult(instanceId, intent, requestCode, fillInIntent, flagsMask, flagsValues, extraFlags, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
void registerForActivityResult(@NonNull String instanceId, int requestCode) {
|
||||
lifecycleHandler.registerForActivityResult(instanceId, requestCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
void unregisterForActivityResults(@NonNull String instanceId) {
|
||||
lifecycleHandler.unregisterForActivityResults(instanceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
void requestPermissions(@NonNull String instanceId, @NonNull String[] permissions, int requestCode) {
|
||||
lifecycleHandler.requestPermissions(instanceId, permissions, requestCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean hasHost() {
|
||||
return lifecycleHandler != null;
|
||||
}
|
||||
|
||||
@Override @NonNull
|
||||
List<Router> getSiblingRouters() {
|
||||
return lifecycleHandler.getRouters();
|
||||
}
|
||||
|
||||
@Override @NonNull
|
||||
Router getRootRouter() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override @Nullable
|
||||
TransactionIndexer getTransactionIndexer() {
|
||||
return transactionIndexer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContextAvailable() {
|
||||
super.onContextAvailable();
|
||||
}
|
||||
|
||||
}
|
||||
Executable → Regular
+64
-30
@@ -1,10 +1,13 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
@@ -12,34 +15,37 @@ class Backstack implements Iterable<RouterTransaction> {
|
||||
|
||||
private static final String KEY_ENTRIES = "Backstack.entries";
|
||||
|
||||
private final ArrayDeque<RouterTransaction> mBackStack = new ArrayDeque<>();
|
||||
private final Deque<RouterTransaction> backstack = new ArrayDeque<>();
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
public boolean isEmpty() {
|
||||
return mBackStack.isEmpty();
|
||||
boolean isEmpty() {
|
||||
return backstack.isEmpty();
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return mBackStack.size();
|
||||
int size() {
|
||||
return backstack.size();
|
||||
}
|
||||
|
||||
public RouterTransaction root() {
|
||||
return mBackStack.size() > 0 ? mBackStack.getLast() : null;
|
||||
@Nullable
|
||||
RouterTransaction root() {
|
||||
return backstack.size() > 0 ? backstack.getLast() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override @NonNull
|
||||
public Iterator<RouterTransaction> iterator() {
|
||||
return mBackStack.iterator();
|
||||
return backstack.iterator();
|
||||
}
|
||||
|
||||
public Iterator<RouterTransaction> reverseIterator() {
|
||||
return mBackStack.descendingIterator();
|
||||
@NonNull
|
||||
Iterator<RouterTransaction> reverseIterator() {
|
||||
return backstack.descendingIterator();
|
||||
}
|
||||
|
||||
public List<RouterTransaction> popTo(RouterTransaction transaction) {
|
||||
@NonNull
|
||||
List<RouterTransaction> popTo(@NonNull RouterTransaction transaction) {
|
||||
List<RouterTransaction> popped = new ArrayList<>();
|
||||
if (mBackStack.contains(transaction)) {
|
||||
while (mBackStack.peek() != transaction) {
|
||||
if (backstack.contains(transaction)) {
|
||||
while (backstack.peek() != transaction) {
|
||||
RouterTransaction poppedTransaction = pop();
|
||||
popped.add(poppedTransaction);
|
||||
}
|
||||
@@ -49,25 +55,28 @@ class Backstack implements Iterable<RouterTransaction> {
|
||||
return popped;
|
||||
}
|
||||
|
||||
public RouterTransaction pop() {
|
||||
RouterTransaction popped = mBackStack.pop();
|
||||
popped.getController().destroy();
|
||||
@NonNull
|
||||
RouterTransaction pop() {
|
||||
RouterTransaction popped = backstack.pop();
|
||||
popped.controller.destroy();
|
||||
return popped;
|
||||
}
|
||||
|
||||
public RouterTransaction peek() {
|
||||
return mBackStack.peek();
|
||||
@Nullable
|
||||
RouterTransaction peek() {
|
||||
return backstack.peek();
|
||||
}
|
||||
|
||||
public void remove(RouterTransaction transaction) {
|
||||
mBackStack.removeFirstOccurrence(transaction);
|
||||
void remove(@NonNull RouterTransaction transaction) {
|
||||
backstack.removeFirstOccurrence(transaction);
|
||||
}
|
||||
|
||||
public void push(RouterTransaction transaction) {
|
||||
mBackStack.push(transaction);
|
||||
void push(@NonNull RouterTransaction transaction) {
|
||||
backstack.push(transaction);
|
||||
}
|
||||
|
||||
public List<RouterTransaction> popAll() {
|
||||
@NonNull
|
||||
List<RouterTransaction> popAll() {
|
||||
List<RouterTransaction> list = new ArrayList<>();
|
||||
while (!isEmpty()) {
|
||||
list.add(pop());
|
||||
@@ -75,21 +84,46 @@ class Backstack implements Iterable<RouterTransaction> {
|
||||
return list;
|
||||
}
|
||||
|
||||
public void detachAndSaveInstanceState(Bundle outState) {
|
||||
ArrayList<Bundle> entryBundles = new ArrayList<>(mBackStack.size());
|
||||
for (RouterTransaction entry : mBackStack) {
|
||||
entryBundles.add(entry.detachAndSaveInstanceState());
|
||||
void setBackstack(@NonNull List<RouterTransaction> backstack) {
|
||||
for (RouterTransaction existingTransaction : this.backstack) {
|
||||
boolean contains = false;
|
||||
for (RouterTransaction newTransaction : backstack) {
|
||||
if (existingTransaction.controller == newTransaction.controller) {
|
||||
contains = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!contains) {
|
||||
existingTransaction.controller.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
this.backstack.clear();
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
this.backstack.push(transaction);
|
||||
}
|
||||
}
|
||||
|
||||
boolean contains(@NonNull RouterTransaction transaction) {
|
||||
return backstack.contains(transaction);
|
||||
}
|
||||
|
||||
void saveInstanceState(@NonNull Bundle outState) {
|
||||
ArrayList<Bundle> entryBundles = new ArrayList<>(backstack.size());
|
||||
for (RouterTransaction entry : backstack) {
|
||||
entryBundles.add(entry.saveInstanceState());
|
||||
}
|
||||
|
||||
outState.putParcelableArrayList(KEY_ENTRIES, entryBundles);
|
||||
}
|
||||
|
||||
public void restoreInstanceState(Bundle savedInstanceState) {
|
||||
void restoreInstanceState(@NonNull Bundle savedInstanceState) {
|
||||
ArrayList<Bundle> entryBundles = savedInstanceState.getParcelableArrayList(KEY_ENTRIES);
|
||||
if (entryBundles != null) {
|
||||
Collections.reverse(entryBundles);
|
||||
for (Bundle transactionBundle : entryBundles) {
|
||||
mBackStack.push(new RouterTransaction(transactionBundle));
|
||||
backstack.push(new RouterTransaction(transactionBundle));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package com.bluelinelabs.conductor;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ViewGroup;
|
||||
@@ -17,7 +19,7 @@ import com.bluelinelabs.conductor.ControllerChangeHandler.ControllerChangeListen
|
||||
*/
|
||||
public class ChangeHandlerFrameLayout extends FrameLayout implements ControllerChangeListener {
|
||||
|
||||
private int mInProgressTransactionCount;
|
||||
private int inProgressTransactionCount;
|
||||
|
||||
public ChangeHandlerFrameLayout(Context context) {
|
||||
super(context);
|
||||
@@ -38,17 +40,17 @@ public class ChangeHandlerFrameLayout extends FrameLayout implements ControllerC
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
return (mInProgressTransactionCount > 0) || super.onInterceptTouchEvent(ev);
|
||||
return (inProgressTransactionCount > 0) || super.onInterceptTouchEvent(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChangeStarted(Controller to, Controller from, boolean isPush, ViewGroup container, ControllerChangeHandler handler) {
|
||||
mInProgressTransactionCount++;
|
||||
public void onChangeStarted(@Nullable Controller to, @Nullable Controller from, boolean isPush, @NonNull ViewGroup container, @NonNull ControllerChangeHandler handler) {
|
||||
inProgressTransactionCount++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChangeCompleted(Controller to, Controller from, boolean isPush, ViewGroup container, ControllerChangeHandler handler) {
|
||||
mInProgressTransactionCount--;
|
||||
public void onChangeCompleted(@Nullable Controller to, @Nullable Controller from, boolean isPush, @NonNull ViewGroup container, @NonNull ControllerChangeHandler handler) {
|
||||
inProgressTransactionCount--;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.IdRes;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* A {@link ControllerTransaction} implementation used for adding child {@link Controller}s.
|
||||
*/
|
||||
public class ChildControllerTransaction extends ControllerTransaction {
|
||||
|
||||
private static final String KEY_CONTAINER_ID = "ChildControllerTransaction.containerId";
|
||||
private static final String KEY_ADD_TO_LOCAL_BACKSTACK = "ChildControllerTransaction.addToLocalBackstack";
|
||||
|
||||
/** The ID of the ViewGroup that the child {@link Controller} will be added to */
|
||||
public final int containerId;
|
||||
|
||||
/** If true, the hosting {@link Controller} will be responsible for reversing this transaction if the user presses the back button */
|
||||
public final boolean addToLocalBackstack;
|
||||
|
||||
ChildControllerTransaction(Builder builder) {
|
||||
super(builder);
|
||||
containerId = builder.containerId;
|
||||
addToLocalBackstack = builder.addToLocalBackstack;
|
||||
}
|
||||
|
||||
ChildControllerTransaction(@NonNull Bundle bundle) {
|
||||
super(bundle);
|
||||
containerId = bundle.getInt(KEY_CONTAINER_ID);
|
||||
addToLocalBackstack = bundle.getBoolean(KEY_ADD_TO_LOCAL_BACKSTACK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle detachAndSaveInstanceState() {
|
||||
Bundle bundle = super.detachAndSaveInstanceState();
|
||||
bundle.putInt(KEY_CONTAINER_ID, containerId);
|
||||
bundle.putBoolean(KEY_ADD_TO_LOCAL_BACKSTACK, addToLocalBackstack);
|
||||
return bundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Builder
|
||||
*
|
||||
* @param controller The Controller to add as a child
|
||||
* @param containerId The ID of the ViewGroup to which the controller's view should be added
|
||||
*/
|
||||
public static Builder builder(@NonNull Controller controller, @IdRes int containerId) {
|
||||
return new Builder(controller, containerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link ControllerTransaction.Builder} implementation used for adding child {@link Controller}s.
|
||||
*/
|
||||
public static class Builder extends ControllerTransaction.Builder<Builder> {
|
||||
|
||||
@IdRes final int containerId;
|
||||
|
||||
boolean addToLocalBackstack;
|
||||
|
||||
Builder(@NonNull Controller controller, @IdRes int containerId) {
|
||||
super(controller);
|
||||
this.containerId = containerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, the hosting {@link Controller} will be responsible for reversing this transaction if the user presses the back button.
|
||||
*/
|
||||
public Builder addToLocalBackstack(boolean addToLocalBackstack) {
|
||||
this.addToLocalBackstack = addToLocalBackstack;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Creates the transaction */
|
||||
public ChildControllerTransaction build() {
|
||||
return new ChildControllerTransaction(this);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -3,17 +3,20 @@ package com.bluelinelabs.conductor;
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.internal.LifecycleHandler;
|
||||
import com.bluelinelabs.conductor.internal.ThreadUtils;
|
||||
|
||||
/**
|
||||
* Point of initial interaction with Conductor. Used to attach a {@link Router} to your Activity.
|
||||
*/
|
||||
public final class Conductor {
|
||||
|
||||
|
||||
private Conductor() {}
|
||||
|
||||
|
||||
/**
|
||||
* Conductor will create a {@link Router} that has been initialized for your Activity and containing ViewGroup.
|
||||
* If an existing {@link Router} is already associated with this Activity/ViewGroup pair, either in memory
|
||||
@@ -26,7 +29,10 @@ public final class Conductor {
|
||||
* for restoring the Router's state if possible.
|
||||
* @return A fully configured {@link Router} instance for use with this Activity/ViewGroup pair.
|
||||
*/
|
||||
public static Router attachRouter(@NonNull Activity activity, @NonNull ViewGroup container, Bundle savedInstanceState) {
|
||||
@NonNull @UiThread
|
||||
public static Router attachRouter(@NonNull Activity activity, @NonNull ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
LifecycleHandler lifecycleHandler = LifecycleHandler.install(activity);
|
||||
|
||||
Router router = lifecycleHandler.getRouter(container, savedInstanceState);
|
||||
|
||||
Executable → Regular
+607
-402
File diff suppressed because it is too large
Load Diff
Executable → Regular
+162
-24
@@ -2,15 +2,17 @@ package com.bluelinelabs.conductor;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
|
||||
import com.bluelinelabs.conductor.ControllerTransaction.ControllerChangeType;
|
||||
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;
|
||||
|
||||
/**
|
||||
* ControllerChangeHandlers are responsible for swapping the View for one Controller to the View
|
||||
@@ -22,16 +24,21 @@ public abstract class ControllerChangeHandler {
|
||||
private static final String KEY_CLASS_NAME = "ControllerChangeHandler.className";
|
||||
private static final String KEY_SAVED_STATE = "ControllerChangeHandler.savedState";
|
||||
|
||||
private static final Map<String, ChangeHandlerData> inProgressChangeHandlers = new HashMap<>();
|
||||
|
||||
private boolean forceRemoveViewOnPush;
|
||||
private boolean hasBeenUsed;
|
||||
|
||||
/**
|
||||
* Responsible for swapping Views from one Controller to another.
|
||||
*
|
||||
* @param container The container these Views are hosted in.
|
||||
* @param from The previous View in the container, if any.
|
||||
* @param to The next View that should be put in the container, if any.
|
||||
* @param isPush True if this is a push transaction, false if it's a pop.
|
||||
* @param container The container these Views are hosted in.
|
||||
* @param from The previous View in the container 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.
|
||||
*/
|
||||
public abstract void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener);
|
||||
public abstract void performChange(@NonNull ViewGroup container, @Nullable View from, @Nullable View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener);
|
||||
|
||||
public ControllerChangeHandler() {
|
||||
ensureDefaultConstructor();
|
||||
@@ -51,6 +58,43 @@ public abstract class ControllerChangeHandler {
|
||||
*/
|
||||
public void restoreFromBundle(@NonNull Bundle bundle) { }
|
||||
|
||||
/**
|
||||
* 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 or {@code null}
|
||||
* if there will be no new Controller at the top
|
||||
*/
|
||||
public void onAbortPush(@NonNull ControllerChangeHandler newHandler, @Nullable Controller newTop) { }
|
||||
|
||||
/**
|
||||
* Will be called on change handlers that push a controller if the controller being pushed is
|
||||
* needs to be attached immediately, without any animations or transitions.
|
||||
*/
|
||||
public void completeImmediately() { }
|
||||
|
||||
/**
|
||||
* Returns a copy of this ControllerChangeHandler. This method is internally used by the library, so
|
||||
* ensure it will return an exact copy of your handler if overriding. If not overriding, the handler
|
||||
* will be saved and restored from the Bundle format.
|
||||
*/
|
||||
@NonNull
|
||||
public ControllerChangeHandler copy() {
|
||||
return fromBundle(toBundle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this is a reusable ControllerChangeHandler. Defaults to false and should
|
||||
* ONLY be overridden if there are absolutely no side effects to using this handler more than once.
|
||||
* In the case that a handler is not reusable, it will be copied using the {@link #copy()} method
|
||||
* prior to use.
|
||||
*/
|
||||
public boolean isReusable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
final Bundle toBundle() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(KEY_CLASS_NAME, getClass().getName());
|
||||
@@ -70,7 +114,8 @@ public abstract class ControllerChangeHandler {
|
||||
}
|
||||
}
|
||||
|
||||
public static ControllerChangeHandler fromBundle(Bundle bundle) {
|
||||
@Nullable
|
||||
public static ControllerChangeHandler fromBundle(@Nullable Bundle bundle) {
|
||||
if (bundle != null) {
|
||||
String className = bundle.getString(KEY_CLASS_NAME);
|
||||
ControllerChangeHandler changeHandler = ClassUtils.newInstance(className);
|
||||
@@ -82,20 +127,64 @@ public abstract class ControllerChangeHandler {
|
||||
}
|
||||
}
|
||||
|
||||
public static void executeChange(final Controller to, final Controller from, boolean isPush, ViewGroup container, ControllerChangeHandler inHandler) {
|
||||
executeChange(to, from, isPush, container, inHandler, new ArrayList<ControllerChangeListener>());
|
||||
static boolean completeHandlerImmediately(@NonNull String controllerInstanceId) {
|
||||
ChangeHandlerData changeHandlerData = inProgressChangeHandlers.get(controllerInstanceId);
|
||||
if (changeHandlerData != null) {
|
||||
changeHandlerData.changeHandler.completeImmediately();
|
||||
inProgressChangeHandlers.remove(controllerInstanceId);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void executeChange(final Controller to, final Controller from, final boolean isPush, final ViewGroup container, final ControllerChangeHandler inHandler, @NonNull final List<ControllerChangeListener> listeners) {
|
||||
static void abortOrComplete(@NonNull Controller toAbort, @Nullable Controller newController, @NonNull ControllerChangeHandler newChangeHandler) {
|
||||
ChangeHandlerData changeHandlerData = inProgressChangeHandlers.get(toAbort.getInstanceId());
|
||||
if (changeHandlerData != null) {
|
||||
if (changeHandlerData.isPush) {
|
||||
changeHandlerData.changeHandler.onAbortPush(newChangeHandler, newController);
|
||||
} else {
|
||||
changeHandlerData.changeHandler.completeImmediately();
|
||||
}
|
||||
|
||||
inProgressChangeHandlers.remove(toAbort.getInstanceId());
|
||||
}
|
||||
}
|
||||
|
||||
static void executeChange(@NonNull final ChangeTransaction transaction) {
|
||||
executeChange(transaction.to, transaction.from, transaction.isPush, transaction.container, transaction.changeHandler, transaction.listeners);
|
||||
}
|
||||
|
||||
private 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 (container != null) {
|
||||
final ControllerChangeHandler handler;
|
||||
if (inHandler == null) {
|
||||
handler = new SimpleSwapChangeHandler();
|
||||
} else if (inHandler.hasBeenUsed && !inHandler.isReusable()) {
|
||||
handler = inHandler.copy();
|
||||
} else {
|
||||
handler = inHandler;
|
||||
}
|
||||
handler.hasBeenUsed = true;
|
||||
|
||||
if (from != null) {
|
||||
if (isPush) {
|
||||
completeHandlerImmediately(from.getInstanceId());
|
||||
} else {
|
||||
abortOrComplete(from, to, handler);
|
||||
}
|
||||
}
|
||||
|
||||
if (to != null) {
|
||||
inProgressChangeHandlers.put(to.getInstanceId(), new ChangeHandlerData(handler, isPush));
|
||||
}
|
||||
|
||||
for (ControllerChangeListener listener : listeners) {
|
||||
listener.onChangeStarted(to, from, isPush, container, inHandler);
|
||||
listener.onChangeStarted(to, from, isPush, container, handler);
|
||||
}
|
||||
|
||||
final ControllerChangeType toChangeType = isPush ? ControllerChangeType.PUSH_ENTER : ControllerChangeType.POP_ENTER;
|
||||
final ControllerChangeType fromChangeType = isPush ? ControllerChangeType.PUSH_EXIT : ControllerChangeType.POP_EXIT;
|
||||
|
||||
final ControllerChangeHandler handler = inHandler != null ? inHandler : new SimpleSwapChangeHandler();
|
||||
final View toView;
|
||||
if (to != null) {
|
||||
toView = to.inflate(container);
|
||||
@@ -120,17 +209,55 @@ public abstract class ControllerChangeHandler {
|
||||
}
|
||||
|
||||
if (to != null) {
|
||||
inProgressChangeHandlers.remove(to.getInstanceId());
|
||||
to.changeEnded(handler, toChangeType);
|
||||
}
|
||||
|
||||
for (ControllerChangeListener listener : listeners) {
|
||||
listener.onChangeCompleted(to, from, isPush, container, inHandler);
|
||||
listener.onChangeCompleted(to, from, isPush, container, handler);
|
||||
}
|
||||
|
||||
if (handler.forceRemoveViewOnPush && fromView != null) {
|
||||
ViewParent fromParent = fromView.getParent();
|
||||
if (fromParent != null && fromParent instanceof ViewGroup) {
|
||||
((ViewGroup)fromParent).removeView(fromView);
|
||||
}
|
||||
}
|
||||
|
||||
if (handler.removesFromViewOnPush() && from != null) {
|
||||
from.setNeedsAttach(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public boolean removesFromViewOnPush() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setForceRemoveViewOnPush(boolean force) {
|
||||
forceRemoveViewOnPush = force;
|
||||
}
|
||||
|
||||
static class ChangeTransaction {
|
||||
@Nullable final Controller to;
|
||||
@Nullable final Controller from;
|
||||
final boolean isPush;
|
||||
@Nullable final ViewGroup container;
|
||||
@Nullable final ControllerChangeHandler changeHandler;
|
||||
@NonNull final List<ControllerChangeListener> listeners;
|
||||
|
||||
public ChangeTransaction(@Nullable Controller to, @Nullable Controller from, boolean isPush, @Nullable ViewGroup container, @Nullable ControllerChangeHandler changeHandler, @NonNull List<ControllerChangeListener> listeners) {
|
||||
this.to = to;
|
||||
this.from = from;
|
||||
this.isPush = isPush;
|
||||
this.container = container;
|
||||
this.changeHandler = changeHandler;
|
||||
this.listeners = listeners;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener interface useful for allowing external classes to be notified of change events.
|
||||
*/
|
||||
@@ -138,23 +265,24 @@ public abstract class ControllerChangeHandler {
|
||||
/**
|
||||
* Called when a {@link ControllerChangeHandler} has started changing {@link Controller}s
|
||||
*
|
||||
* @param to The new Controller
|
||||
* @param from The old Controller
|
||||
* @param isPush True if this is a push operation, or false if it's a pop.
|
||||
* @param to The new Controller 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.
|
||||
* @param handler The change handler being used.
|
||||
*/
|
||||
void onChangeStarted(Controller to, Controller from, boolean isPush, ViewGroup container, ControllerChangeHandler handler);
|
||||
void onChangeStarted(@Nullable Controller to, @Nullable Controller from, boolean isPush, @NonNull ViewGroup container, @NonNull ControllerChangeHandler handler);
|
||||
|
||||
/**
|
||||
* Called when a {@link ControllerChangeHandler} has completed changing {@link Controller}s
|
||||
* @param to The new Controller
|
||||
* @param from The old Controller
|
||||
* @param isPush True if this was a push operation, or false if it's a pop.
|
||||
*
|
||||
* @param to The new Controller 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.
|
||||
* @param handler The change handler that was used.
|
||||
*/
|
||||
void onChangeCompleted(Controller to, Controller from, boolean isPush, ViewGroup container, ControllerChangeHandler handler);
|
||||
void onChangeCompleted(@Nullable Controller to, @Nullable Controller from, boolean isPush, @NonNull ViewGroup container, @NonNull ControllerChangeHandler handler);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,4 +296,14 @@ public abstract class ControllerChangeHandler {
|
||||
void onChangeCompleted();
|
||||
}
|
||||
|
||||
private static class ChangeHandlerData {
|
||||
public final ControllerChangeHandler changeHandler;
|
||||
public final boolean isPush;
|
||||
|
||||
public ChangeHandlerData(ControllerChangeHandler changeHandler, boolean isPush) {
|
||||
this.changeHandler = changeHandler;
|
||||
this.isPush = isPush;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
/**
|
||||
* All possible types of {@link Controller} changes to be used in {@link ControllerChangeHandler}s
|
||||
*/
|
||||
public enum ControllerChangeType {
|
||||
/** The Controller is being pushed to the host container */
|
||||
PUSH_ENTER(true, true),
|
||||
|
||||
/** The Controller is being pushed to the backstack as another Controller is pushed to the host container */
|
||||
PUSH_EXIT(true, false),
|
||||
|
||||
/** The Controller is being popped from the backstack and placed in the host container as another Controller is popped */
|
||||
POP_ENTER(false, true),
|
||||
|
||||
/** The Controller is being popped from the host container */
|
||||
POP_EXIT(false, false);
|
||||
|
||||
public boolean isPush;
|
||||
public boolean isEnter;
|
||||
|
||||
ControllerChangeType(boolean isPush, boolean isEnter) {
|
||||
this.isPush = isPush;
|
||||
this.isEnter = isEnter;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.content.IntentSender.SendIntentException;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.IdRes;
|
||||
import android.support.annotation.NonNull;
|
||||
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;
|
||||
|
||||
class ControllerHostedRouter extends Router {
|
||||
|
||||
private final String KEY_HOST_ID = "ControllerHostedRouter.hostId";
|
||||
private final String KEY_TAG = "ControllerHostedRouter.tag";
|
||||
|
||||
private Controller hostController;
|
||||
|
||||
@IdRes private int hostId;
|
||||
private String tag;
|
||||
private boolean isDetachFrozen;
|
||||
|
||||
ControllerHostedRouter() { }
|
||||
|
||||
ControllerHostedRouter(int hostId, @Nullable String tag) {
|
||||
this.hostId = hostId;
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
final void setHost(@NonNull Controller controller, @NonNull ViewGroup container) {
|
||||
if (hostController != controller || this.container != container) {
|
||||
removeHost();
|
||||
|
||||
if (container instanceof ControllerChangeListener) {
|
||||
addChangeListener((ControllerChangeListener)container);
|
||||
}
|
||||
|
||||
hostController = controller;
|
||||
this.container = container;
|
||||
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
transaction.controller.setParentController(controller);
|
||||
}
|
||||
|
||||
watchContainerAttach();
|
||||
}
|
||||
}
|
||||
|
||||
final void removeHost() {
|
||||
if (container != null && container instanceof ControllerChangeListener) {
|
||||
removeChangeListener((ControllerChangeListener)container);
|
||||
}
|
||||
|
||||
final List<Controller> controllersToDestroy = new ArrayList<>(destroyingControllers);
|
||||
for (Controller controller : controllersToDestroy) {
|
||||
if (controller.getView() != null) {
|
||||
controller.detach(controller.getView(), true, false);
|
||||
}
|
||||
}
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
if (transaction.controller.getView() != null) {
|
||||
transaction.controller.detach(transaction.controller.getView(), true, false);
|
||||
}
|
||||
}
|
||||
|
||||
prepareForContainerRemoval();
|
||||
hostController = null;
|
||||
container = null;
|
||||
}
|
||||
|
||||
final void setDetachFrozen(boolean frozen) {
|
||||
isDetachFrozen = frozen;
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
transaction.controller.setDetachFrozen(frozen);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void destroy(boolean popViews) {
|
||||
setDetachFrozen(false);
|
||||
super.destroy(popViews);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void pushToBackstack(@NonNull RouterTransaction entry) {
|
||||
if (isDetachFrozen) {
|
||||
entry.controller.setDetachFrozen(true);
|
||||
}
|
||||
super.pushToBackstack(entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBackstack(@NonNull List<RouterTransaction> newBackstack, @Nullable ControllerChangeHandler changeHandler) {
|
||||
if (isDetachFrozen) {
|
||||
for (RouterTransaction transaction : newBackstack) {
|
||||
transaction.controller.setDetachFrozen(true);
|
||||
}
|
||||
}
|
||||
super.setBackstack(newBackstack, changeHandler);
|
||||
}
|
||||
|
||||
@Override @Nullable
|
||||
public Activity getActivity() {
|
||||
return hostController != null ? hostController.getActivity() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityDestroyed(@NonNull Activity activity) {
|
||||
super.onActivityDestroyed(activity);
|
||||
|
||||
removeHost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
||||
if (hostController != null && hostController.getRouter() != null) {
|
||||
hostController.getRouter().onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateOptionsMenu() {
|
||||
if (hostController != null && hostController.getRouter() != null) {
|
||||
hostController.getRouter().invalidateOptionsMenu();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void startActivity(@NonNull Intent intent) {
|
||||
if (hostController != null && hostController.getRouter() != null) {
|
||||
hostController.getRouter().startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void startActivityForResult(@NonNull String instanceId, @NonNull Intent intent, int requestCode) {
|
||||
if (hostController != null && hostController.getRouter() != null) {
|
||||
hostController.getRouter().startActivityForResult(instanceId, intent, requestCode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void startActivityForResult(@NonNull String instanceId, @NonNull Intent intent, int requestCode, @Nullable Bundle options) {
|
||||
if (hostController != null && hostController.getRouter() != null) {
|
||||
hostController.getRouter().startActivityForResult(instanceId, intent, requestCode, options);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void startIntentSenderForResult(@NonNull String instanceId, @NonNull IntentSender intent, int requestCode, @Nullable Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, @Nullable Bundle options) throws SendIntentException {
|
||||
if (hostController != null && hostController.getRouter() != null) {
|
||||
hostController.getRouter().startIntentSenderForResult(instanceId, intent, requestCode, fillInIntent, flagsMask, flagsValues, extraFlags, options);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void registerForActivityResult(@NonNull String instanceId, int requestCode) {
|
||||
if (hostController != null && hostController.getRouter() != null) {
|
||||
hostController.getRouter().registerForActivityResult(instanceId, requestCode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void unregisterForActivityResults(@NonNull String instanceId) {
|
||||
if (hostController != null && hostController.getRouter() != null) {
|
||||
hostController.getRouter().unregisterForActivityResults(instanceId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void requestPermissions(@NonNull String instanceId, @NonNull String[] permissions, int requestCode) {
|
||||
if (hostController != null && hostController.getRouter() != null) {
|
||||
hostController.getRouter().requestPermissions(instanceId, permissions, requestCode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean hasHost() {
|
||||
return hostController != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveInstanceState(@NonNull Bundle outState) {
|
||||
super.saveInstanceState(outState);
|
||||
|
||||
outState.putInt(KEY_HOST_ID, hostId);
|
||||
outState.putString(KEY_TAG, tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreInstanceState(@NonNull Bundle savedInstanceState) {
|
||||
super.restoreInstanceState(savedInstanceState);
|
||||
|
||||
hostId = savedInstanceState.getInt(KEY_HOST_ID);
|
||||
tag = savedInstanceState.getString(KEY_TAG);
|
||||
}
|
||||
|
||||
@Override
|
||||
void setControllerRouter(@NonNull Controller controller) {
|
||||
super.setControllerRouter(controller);
|
||||
controller.setParentController(hostController);
|
||||
}
|
||||
|
||||
int getHostId() {
|
||||
return hostId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
String getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Override @NonNull
|
||||
List<Router> getSiblingRouters() {
|
||||
List<Router> list = new ArrayList<>();
|
||||
list.addAll(hostController.getChildRouters());
|
||||
list.addAll(hostController.getRouter().getSiblingRouters());
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override @NonNull
|
||||
Router getRootRouter() {
|
||||
if (hostController != null && hostController.getRouter() != null) {
|
||||
return hostController.getRouter().getRootRouter();
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@Override @Nullable
|
||||
TransactionIndexer getTransactionIndexer() {
|
||||
return getRootRouter().getTransactionIndexer();
|
||||
}
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Metadata used to transition between {@link Controller}s.
|
||||
*/
|
||||
public class ControllerTransaction {
|
||||
|
||||
/**
|
||||
* All possible types of {@link Controller} changes to be used in {@link ControllerChangeHandler}s
|
||||
*/
|
||||
public enum ControllerChangeType {
|
||||
/** The Controller is being pushed to the host container */
|
||||
PUSH_ENTER(true, true),
|
||||
|
||||
/** The Controller is being pushed to the backstack as another Controller is pushed to the host container */
|
||||
PUSH_EXIT(true, false),
|
||||
|
||||
/** The Controller is being popped from the backstack and placed in the host container as another Controller is popped */
|
||||
POP_ENTER(false, true),
|
||||
|
||||
/** The Controller is being popped from the host container */
|
||||
POP_EXIT(false, false);
|
||||
|
||||
public boolean isPush;
|
||||
public boolean isEnter;
|
||||
|
||||
ControllerChangeType(boolean isPush, boolean isEnter) {
|
||||
this.isPush = isPush;
|
||||
this.isEnter = isEnter;
|
||||
}
|
||||
}
|
||||
|
||||
private static final String KEY_VIEW_CONTROLLER_BUNDLE = "ControllerTransaction.controller.bundle";
|
||||
private static final String KEY_PUSH_TRANSITION = "ControllerTransaction.pushControllerChangeHandler";
|
||||
private static final String KEY_POP_TRANSITION = "ControllerTransaction.popControllerChangeHandler";
|
||||
private static final String KEY_TAG = "ControllerTransaction.tag";
|
||||
|
||||
public final Controller controller;
|
||||
public final String tag;
|
||||
|
||||
private final ControllerChangeHandler mPushControllerChangeHandler;
|
||||
private final ControllerChangeHandler mPopControllerChangeHandler;
|
||||
|
||||
ControllerTransaction(Builder builder) {
|
||||
controller = builder.controller;
|
||||
tag = builder.tag;
|
||||
mPushControllerChangeHandler = builder.pushControllerChangeHandler;
|
||||
mPopControllerChangeHandler = builder.popControllerChangeHandler;
|
||||
}
|
||||
|
||||
ControllerTransaction(@NonNull Bundle bundle) {
|
||||
controller = Controller.newInstance(bundle.getBundle(KEY_VIEW_CONTROLLER_BUNDLE));
|
||||
mPushControllerChangeHandler = ControllerChangeHandler.fromBundle(bundle.getBundle(KEY_PUSH_TRANSITION));
|
||||
mPopControllerChangeHandler = ControllerChangeHandler.fromBundle(bundle.getBundle(KEY_POP_TRANSITION));
|
||||
tag = bundle.getString(KEY_TAG);
|
||||
}
|
||||
|
||||
public Controller getController() {
|
||||
return controller;
|
||||
}
|
||||
|
||||
public String getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
public ControllerChangeHandler getPushControllerChangeHandler() {
|
||||
ControllerChangeHandler handler = controller.getOverriddenPushHandler();
|
||||
if (handler == null) {
|
||||
handler = mPushControllerChangeHandler;
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
public ControllerChangeHandler getPopControllerChangeHandler() {
|
||||
ControllerChangeHandler handler = controller.getOverriddenPopHandler();
|
||||
if (handler == null) {
|
||||
handler = mPopControllerChangeHandler;
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to serialize this transaction into a Bundle
|
||||
*/
|
||||
public Bundle detachAndSaveInstanceState() {
|
||||
Bundle bundle = new Bundle();
|
||||
|
||||
bundle.putBundle(KEY_VIEW_CONTROLLER_BUNDLE, controller.detachAndSaveInstanceState());
|
||||
|
||||
if (mPushControllerChangeHandler != null) {
|
||||
bundle.putBundle(KEY_PUSH_TRANSITION, mPushControllerChangeHandler.toBundle());
|
||||
}
|
||||
if (mPopControllerChangeHandler != null) {
|
||||
bundle.putBundle(KEY_POP_TRANSITION, mPopControllerChangeHandler.toBundle());
|
||||
}
|
||||
|
||||
bundle.putString(KEY_TAG, tag);
|
||||
|
||||
return bundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder used to create transactions.
|
||||
*/
|
||||
public static class Builder<T extends Builder<T>> {
|
||||
|
||||
final Controller controller;
|
||||
ControllerChangeHandler pushControllerChangeHandler;
|
||||
ControllerChangeHandler popControllerChangeHandler;
|
||||
String tag;
|
||||
|
||||
public Builder(@NonNull Controller controller) {
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link ControllerChangeHandler} that will be used when the {@link Controller} is pushed
|
||||
* to the screen.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T pushChangeHandler(ControllerChangeHandler pushControllerChangeHandler) {
|
||||
this.pushControllerChangeHandler = pushControllerChangeHandler;
|
||||
return (T)this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link ControllerChangeHandler} that will be used when the {@link Controller} is popped
|
||||
* from the screen.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T popChangeHandler(ControllerChangeHandler popControllerChangeHandler) {
|
||||
this.popControllerChangeHandler = popControllerChangeHandler;
|
||||
return (T)this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The tag to use for this transaction. Tags can be used for finding transactions later on.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T tag(String tag) {
|
||||
this.tag = tag;
|
||||
return (T)this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the transaction.
|
||||
*/
|
||||
public ControllerTransaction build() {
|
||||
return new ControllerTransaction(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
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 {
|
||||
|
||||
/**
|
||||
* Convenience constructor for use when no arguments are needed.
|
||||
*/
|
||||
protected RestoreViewOnCreateController() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor that takes arguments that need to be retained across restarts.
|
||||
*
|
||||
* @param args Any arguments that need to be retained.
|
||||
*/
|
||||
protected RestoreViewOnCreateController(@Nullable Bundle args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
@Override @NonNull
|
||||
protected final View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
return onCreateView(inflater, container, viewState == null ? null : viewState.getBundle(KEY_VIEW_STATE_BUNDLE));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {@code null} if no saved state exists.
|
||||
*/
|
||||
@NonNull
|
||||
protected abstract View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container, @Nullable Bundle savedViewState);
|
||||
|
||||
}
|
||||
Executable → Regular
+580
-202
File diff suppressed because it is too large
Load Diff
@@ -2,43 +2,142 @@ package com.bluelinelabs.conductor;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.bluelinelabs.conductor.internal.TransactionIndexer;
|
||||
|
||||
/**
|
||||
* A {@link ControllerTransaction} implementation used for adding {@link Controller}s to a {@link Router}.
|
||||
* Metadata used for adding {@link Controller}s to a {@link Router}.
|
||||
*/
|
||||
public class RouterTransaction extends ControllerTransaction {
|
||||
public class RouterTransaction {
|
||||
|
||||
private RouterTransaction(Builder builder) {
|
||||
super(builder);
|
||||
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;
|
||||
private String tag;
|
||||
|
||||
private ControllerChangeHandler pushControllerChangeHandler;
|
||||
private ControllerChangeHandler popControllerChangeHandler;
|
||||
private boolean attachedToRouter;
|
||||
int transactionIndex = INVALID_INDEX;
|
||||
|
||||
@NonNull
|
||||
public static RouterTransaction with(@NonNull Controller controller) {
|
||||
return new RouterTransaction(controller);
|
||||
}
|
||||
|
||||
private RouterTransaction(@NonNull Controller controller) {
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
RouterTransaction(@NonNull Bundle bundle) {
|
||||
super(bundle);
|
||||
controller = Controller.newInstance(bundle.getBundle(KEY_VIEW_CONTROLLER_BUNDLE));
|
||||
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);
|
||||
}
|
||||
|
||||
void onAttachedToRouter() {
|
||||
attachedToRouter = true;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Controller controller() {
|
||||
return controller;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String tag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public RouterTransaction tag(@Nullable String tag) {
|
||||
if (!attachedToRouter) {
|
||||
this.tag = tag;
|
||||
return this;
|
||||
} else {
|
||||
throw new RuntimeException(getClass().getSimpleName() + "s can not be modified after being added to a Router.");
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ControllerChangeHandler pushChangeHandler() {
|
||||
ControllerChangeHandler handler = controller.getOverriddenPushHandler();
|
||||
if (handler == null) {
|
||||
handler = pushControllerChangeHandler;
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public RouterTransaction pushChangeHandler(@Nullable ControllerChangeHandler handler) {
|
||||
if (!attachedToRouter) {
|
||||
pushControllerChangeHandler = handler;
|
||||
return this;
|
||||
} else {
|
||||
throw new RuntimeException(getClass().getSimpleName() + "s can not be modified after being added to a Router.");
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ControllerChangeHandler popChangeHandler() {
|
||||
ControllerChangeHandler handler = controller.getOverriddenPopHandler();
|
||||
if (handler == null) {
|
||||
handler = popControllerChangeHandler;
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public RouterTransaction popChangeHandler(@Nullable ControllerChangeHandler handler) {
|
||||
if (!attachedToRouter) {
|
||||
popControllerChangeHandler = handler;
|
||||
return this;
|
||||
} else {
|
||||
throw new RuntimeException(getClass().getSimpleName() + "s can not be modified after being added to a Router.");
|
||||
}
|
||||
}
|
||||
|
||||
void ensureValidIndex(@Nullable TransactionIndexer indexer) {
|
||||
if (indexer == null) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
if (transactionIndex == INVALID_INDEX && indexer != null) {
|
||||
transactionIndex = indexer.nextIndex();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Builder
|
||||
*
|
||||
* @param controller The {@link Controller} to add to the {@link Router}
|
||||
* Used to serialize this transaction into a Bundle
|
||||
*/
|
||||
public static Builder builder(@NonNull Controller controller) {
|
||||
return new Builder(controller);
|
||||
}
|
||||
@NonNull
|
||||
public Bundle saveInstanceState() {
|
||||
Bundle bundle = new Bundle();
|
||||
|
||||
/**
|
||||
* A {@link ControllerTransaction.Builder} implementation used for adding {@link Controller}s to a {@link Router}.
|
||||
*/
|
||||
public static class Builder extends ControllerTransaction.Builder<Builder> {
|
||||
bundle.putBundle(KEY_VIEW_CONTROLLER_BUNDLE, controller.saveInstanceState());
|
||||
|
||||
Builder(@NonNull Controller controller) {
|
||||
super(controller);
|
||||
if (pushControllerChangeHandler != null) {
|
||||
bundle.putBundle(KEY_PUSH_TRANSITION, pushControllerChangeHandler.toBundle());
|
||||
}
|
||||
if (popControllerChangeHandler != null) {
|
||||
bundle.putBundle(KEY_POP_TRANSITION, popControllerChangeHandler.toBundle());
|
||||
}
|
||||
|
||||
/** Creates the transaction */
|
||||
public RouterTransaction build() {
|
||||
return new RouterTransaction(this);
|
||||
}
|
||||
bundle.putString(KEY_TAG, tag);
|
||||
bundle.putInt(KEY_INDEX, transactionIndex);
|
||||
bundle.putBoolean(KEY_ATTACHED_TO_ROUTER, attachedToRouter);
|
||||
|
||||
return bundle;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Executable → Regular
+147
-37
@@ -1,13 +1,16 @@
|
||||
package com.bluelinelabs.conductor.changehandler;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.Animator.AnimatorListener;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
|
||||
/**
|
||||
@@ -18,60 +21,95 @@ public abstract class AnimatorChangeHandler extends ControllerChangeHandler {
|
||||
private static final String KEY_DURATION = "AnimatorChangeHandler.duration";
|
||||
private static final String KEY_REMOVES_FROM_ON_PUSH = "AnimatorChangeHandler.removesFromViewOnPush";
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public static final long DEFAULT_ANIMATION_DURATION = -1;
|
||||
|
||||
private long mAnimationDuration;
|
||||
private boolean mRemovesFromViewOnPush;
|
||||
private long animationDuration;
|
||||
private boolean removesFromViewOnPush;
|
||||
private boolean canceled;
|
||||
private boolean needsImmediateCompletion;
|
||||
private boolean completed;
|
||||
private Animator animator;
|
||||
private OnAnimationReadyOrAbortedListener onAnimationReadyOrAbortedListener;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public AnimatorChangeHandler() {
|
||||
this(DEFAULT_ANIMATION_DURATION, true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public AnimatorChangeHandler(boolean removesFromViewOnPush) {
|
||||
this(DEFAULT_ANIMATION_DURATION, removesFromViewOnPush);
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public AnimatorChangeHandler(long duration) {
|
||||
this(duration, true);
|
||||
}
|
||||
|
||||
public AnimatorChangeHandler(long duration, boolean removesFromViewOnPush) {
|
||||
mAnimationDuration = duration;
|
||||
mRemovesFromViewOnPush = removesFromViewOnPush;
|
||||
animationDuration = duration;
|
||||
this.removesFromViewOnPush = removesFromViewOnPush;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveToBundle(@NonNull Bundle bundle) {
|
||||
super.saveToBundle(bundle);
|
||||
bundle.putLong(KEY_DURATION, mAnimationDuration);
|
||||
bundle.putBoolean(KEY_REMOVES_FROM_ON_PUSH, mRemovesFromViewOnPush);
|
||||
bundle.putLong(KEY_DURATION, animationDuration);
|
||||
bundle.putBoolean(KEY_REMOVES_FROM_ON_PUSH, removesFromViewOnPush);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreFromBundle(@NonNull Bundle bundle) {
|
||||
super.restoreFromBundle(bundle);
|
||||
mAnimationDuration = bundle.getLong(KEY_DURATION);
|
||||
mRemovesFromViewOnPush = bundle.getBoolean(KEY_REMOVES_FROM_ON_PUSH);
|
||||
animationDuration = bundle.getLong(KEY_DURATION);
|
||||
removesFromViewOnPush = bundle.getBoolean(KEY_REMOVES_FROM_ON_PUSH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAbortPush(@NonNull ControllerChangeHandler newHandler, @Nullable Controller newTop) {
|
||||
super.onAbortPush(newHandler, newTop);
|
||||
|
||||
canceled = true;
|
||||
if (animator != null) {
|
||||
animator.cancel();
|
||||
} else if (onAnimationReadyOrAbortedListener != null) {
|
||||
onAnimationReadyOrAbortedListener.onReadyOrAborted();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void completeImmediately() {
|
||||
super.completeImmediately();
|
||||
|
||||
needsImmediateCompletion = true;
|
||||
if (animator != null) {
|
||||
animator.end();
|
||||
} else if (onAnimationReadyOrAbortedListener != null) {
|
||||
onAnimationReadyOrAbortedListener.onReadyOrAborted();
|
||||
}
|
||||
}
|
||||
|
||||
public long getAnimationDuration() {
|
||||
return mAnimationDuration;
|
||||
return animationDuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removesFromViewOnPush() {
|
||||
return mRemovesFromViewOnPush;
|
||||
return removesFromViewOnPush;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
protected abstract Animator getAnimator(@NonNull ViewGroup container, View from, View to, boolean isPush, boolean toAddedToContainer);
|
||||
@NonNull
|
||||
protected abstract Animator getAnimator(@NonNull ViewGroup container, @Nullable View from, @Nullable View to, boolean isPush, boolean toAddedToContainer);
|
||||
|
||||
/**
|
||||
* Will be called after the animation is complete to reset the View that was removed to its pre-animation state.
|
||||
@@ -79,30 +117,21 @@ public abstract class AnimatorChangeHandler extends ControllerChangeHandler {
|
||||
protected abstract void resetFromView(@NonNull View from);
|
||||
|
||||
@Override
|
||||
public final void performChange(@NonNull final ViewGroup container, final View from, final View to, final boolean isPush, @NonNull final ControllerChangeCompletedListener changeListener) {
|
||||
public final void performChange(@NonNull final ViewGroup container, @Nullable final View from, @Nullable final View to, final boolean isPush, @NonNull final ControllerChangeCompletedListener changeListener) {
|
||||
boolean readyToAnimate = true;
|
||||
final boolean addingToView = to != null && to.getParent() == null;
|
||||
|
||||
if (addingToView) {
|
||||
if (isPush || from == null) {
|
||||
container.addView(to);
|
||||
} else {
|
||||
} else if (to.getParent() == null) {
|
||||
container.addView(to, container.indexOfChild(from));
|
||||
}
|
||||
|
||||
if (to.getWidth() <= 0 && to.getHeight() <= 0) {
|
||||
readyToAnimate = false;
|
||||
to.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
|
||||
@Override
|
||||
public boolean onPreDraw() {
|
||||
final ViewTreeObserver observer = to.getViewTreeObserver();
|
||||
if (observer.isAlive()) {
|
||||
observer.removeOnPreDrawListener(this);
|
||||
}
|
||||
performAnimation(container, from, to, isPush, addingToView, changeListener);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
onAnimationReadyOrAbortedListener = new OnAnimationReadyOrAbortedListener(container, from, to, isPush, true, changeListener);
|
||||
to.getViewTreeObserver().addOnPreDrawListener(onAnimationReadyOrAbortedListener);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,33 +140,114 @@ public abstract class AnimatorChangeHandler extends ControllerChangeHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private void performAnimation(@NonNull final ViewGroup container, final View from, View to, final boolean isPush, final boolean toAddedToContainer, @NonNull final ControllerChangeCompletedListener changeListener) {
|
||||
Animator animator = getAnimator(container, from, to, isPush, toAddedToContainer);
|
||||
private void complete(@NonNull ControllerChangeCompletedListener changeListener, @Nullable AnimatorListener animatorListener) {
|
||||
if (!completed) {
|
||||
completed = true;
|
||||
changeListener.onChangeCompleted();
|
||||
}
|
||||
|
||||
if (mAnimationDuration > 0) {
|
||||
animator.setDuration(mAnimationDuration);
|
||||
if (animator != null) {
|
||||
if (animatorListener != null) {
|
||||
animator.removeListener(animatorListener);
|
||||
}
|
||||
animator.cancel();
|
||||
animator = null;
|
||||
}
|
||||
|
||||
onAnimationReadyOrAbortedListener = null;
|
||||
}
|
||||
|
||||
private void performAnimation(@NonNull final ViewGroup container, @Nullable final View from, @Nullable final View to, final boolean isPush, final boolean toAddedToContainer, @NonNull final ControllerChangeCompletedListener changeListener) {
|
||||
if (canceled) {
|
||||
complete(changeListener, null);
|
||||
return;
|
||||
}
|
||||
if (needsImmediateCompletion) {
|
||||
if (from != null && (!isPush || removesFromViewOnPush)) {
|
||||
container.removeView(from);
|
||||
}
|
||||
complete(changeListener, null);
|
||||
if (isPush && from != null) {
|
||||
resetFromView(from);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
animator = getAnimator(container, from, to, isPush, toAddedToContainer);
|
||||
|
||||
if (animationDuration > 0) {
|
||||
animator.setDuration(animationDuration);
|
||||
}
|
||||
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
changeListener.onChangeCompleted();
|
||||
if (from != null && (!isPush || removesFromViewOnPush) && needsImmediateCompletion) {
|
||||
container.removeView(from);
|
||||
}
|
||||
|
||||
complete(changeListener, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (from != null && (!isPush || mRemovesFromViewOnPush)) {
|
||||
container.removeView(from);
|
||||
}
|
||||
if (!canceled && animator != null) {
|
||||
if (from != null && (!isPush || removesFromViewOnPush)) {
|
||||
container.removeView(from);
|
||||
}
|
||||
|
||||
changeListener.onChangeCompleted();
|
||||
complete(changeListener, this);
|
||||
|
||||
if (isPush && from != null) {
|
||||
resetFromView(from);
|
||||
if (isPush && from != null) {
|
||||
resetFromView(from);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
animator.start();
|
||||
}
|
||||
|
||||
private class OnAnimationReadyOrAbortedListener implements ViewTreeObserver.OnPreDrawListener {
|
||||
@NonNull final ViewGroup container;
|
||||
@Nullable final View from;
|
||||
@Nullable final View to;
|
||||
final boolean isPush;
|
||||
final boolean addingToView;
|
||||
@NonNull final ControllerChangeCompletedListener changeListener;
|
||||
private boolean hasRun;
|
||||
|
||||
OnAnimationReadyOrAbortedListener(@NonNull ViewGroup container, @Nullable View from, @Nullable View to, boolean isPush, boolean addingToView, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
this.container = container;
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.isPush = isPush;
|
||||
this.addingToView = addingToView;
|
||||
this.changeListener = changeListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreDraw() {
|
||||
onReadyOrAborted();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void onReadyOrAborted() {
|
||||
if (!hasRun) {
|
||||
hasRun = true;
|
||||
|
||||
if (to != null) {
|
||||
final ViewTreeObserver observer = to.getViewTreeObserver();
|
||||
if (observer.isAlive()) {
|
||||
observer.removeOnPreDrawListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
performAnimation(container, from, to, isPush, addingToView, changeListener);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Executable → Regular
+10
-3
@@ -3,21 +3,28 @@ package com.bluelinelabs.conductor.changehandler;
|
||||
import android.annotation.TargetApi;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.transition.AutoTransition;
|
||||
import android.transition.Transition;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
|
||||
/**
|
||||
* A change handler that will use an AutoTransition.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public class AutoTransitionChangeHandler extends TransitionChangeHandler {
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
protected Transition getTransition(@NonNull ViewGroup container, View from, View to, boolean isPush) {
|
||||
@Override @NonNull
|
||||
protected Transition getTransition(@NonNull ViewGroup container, @Nullable View from, @Nullable View to, boolean isPush) {
|
||||
return new AutoTransition();
|
||||
}
|
||||
|
||||
@Override @NonNull
|
||||
public ControllerChangeHandler copy() {
|
||||
return new AutoTransitionChangeHandler();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Executable → Regular
+15
-5
@@ -4,9 +4,12 @@ import android.animation.Animator;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
|
||||
/**
|
||||
* An {@link AnimatorChangeHandler} that will cross fade two views
|
||||
*/
|
||||
@@ -26,14 +29,15 @@ public class FadeChangeHandler extends AnimatorChangeHandler {
|
||||
super(duration, removesFromViewOnPush);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Animator getAnimator(@NonNull ViewGroup container, View from, View to, boolean isPush, boolean toAddedToContainer) {
|
||||
@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) {
|
||||
if (from != null && (!isPush || removesFromViewOnPush())) {
|
||||
animator.play(ObjectAnimator.ofFloat(from, View.ALPHA, 0));
|
||||
}
|
||||
|
||||
@@ -44,4 +48,10 @@ public class FadeChangeHandler extends AnimatorChangeHandler {
|
||||
protected void resetFromView(@NonNull View from) {
|
||||
from.setAlpha(1);
|
||||
}
|
||||
|
||||
@Override @NonNull
|
||||
public ControllerChangeHandler copy() {
|
||||
return new FadeChangeHandler(getAnimationDuration(), removesFromViewOnPush());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Executable → Regular
+15
-4
@@ -4,9 +4,12 @@ import android.animation.Animator;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
|
||||
/**
|
||||
* An {@link AnimatorChangeHandler} that will slide the views left or right, depending on if it's a push or pop.
|
||||
*/
|
||||
@@ -26,8 +29,8 @@ public class HorizontalChangeHandler extends AnimatorChangeHandler {
|
||||
super(duration, removesFromViewOnPush);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Animator getAnimator(@NonNull ViewGroup container, View from, View to, boolean isPush, boolean toAddedToContainer) {
|
||||
@Override @NonNull
|
||||
protected Animator getAnimator(@NonNull ViewGroup container, @Nullable View from, @Nullable View to, boolean isPush, boolean toAddedToContainer) {
|
||||
AnimatorSet animatorSet = new AnimatorSet();
|
||||
|
||||
if (isPush) {
|
||||
@@ -42,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.getTranslationX() : 0;
|
||||
animatorSet.play(ObjectAnimator.ofFloat(to, View.TRANSLATION_X, fromLeft - to.getWidth(), 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +56,12 @@ public class HorizontalChangeHandler extends AnimatorChangeHandler {
|
||||
|
||||
@Override
|
||||
protected void resetFromView(@NonNull View from) {
|
||||
from.setTranslationY(0);
|
||||
from.setTranslationX(0);
|
||||
}
|
||||
|
||||
@Override @NonNull
|
||||
public ControllerChangeHandler copy() {
|
||||
return new HorizontalChangeHandler(getAnimationDuration(), removesFromViewOnPush());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Executable → Regular
+78
-15
@@ -2,51 +2,114 @@ package com.bluelinelabs.conductor.changehandler;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.View;
|
||||
import android.view.View.OnAttachStateChangeListener;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
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";
|
||||
|
||||
private boolean mRemovesFromViewOnPush;
|
||||
private boolean removesFromViewOnPush;
|
||||
|
||||
private boolean canceled;
|
||||
|
||||
private ViewGroup container;
|
||||
private ControllerChangeCompletedListener changeListener;
|
||||
|
||||
public SimpleSwapChangeHandler() {
|
||||
this(true);
|
||||
}
|
||||
|
||||
public SimpleSwapChangeHandler(boolean removesFromViewOnPush) {
|
||||
mRemovesFromViewOnPush = removesFromViewOnPush;
|
||||
this.removesFromViewOnPush = removesFromViewOnPush;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveToBundle(@NonNull Bundle bundle) {
|
||||
super.saveToBundle(bundle);
|
||||
bundle.putBoolean(KEY_REMOVES_FROM_ON_PUSH, mRemovesFromViewOnPush);
|
||||
bundle.putBoolean(KEY_REMOVES_FROM_ON_PUSH, removesFromViewOnPush);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreFromBundle(@NonNull Bundle bundle) {
|
||||
super.restoreFromBundle(bundle);
|
||||
mRemovesFromViewOnPush = bundle.getBoolean(KEY_REMOVES_FROM_ON_PUSH);
|
||||
removesFromViewOnPush = bundle.getBoolean(KEY_REMOVES_FROM_ON_PUSH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull final ControllerChangeCompletedListener changeListener) {
|
||||
if (from != null && (!isPush || mRemovesFromViewOnPush)) {
|
||||
container.removeView(from);
|
||||
}
|
||||
public void onAbortPush(@NonNull ControllerChangeHandler newHandler, @Nullable Controller newTop) {
|
||||
super.onAbortPush(newHandler, newTop);
|
||||
|
||||
if (to != null && to.getParent() == null) {
|
||||
container.addView(to);
|
||||
}
|
||||
|
||||
changeListener.onChangeCompleted();
|
||||
canceled = true;
|
||||
}
|
||||
|
||||
}
|
||||
@Override
|
||||
public void completeImmediately() {
|
||||
if (changeListener != null) {
|
||||
changeListener.onChangeCompleted();
|
||||
changeListener = null;
|
||||
|
||||
container.removeOnAttachStateChangeListener(this);
|
||||
container = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, @Nullable View from, @Nullable View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
if (!canceled) {
|
||||
if (from != null && (!isPush || removesFromViewOnPush)) {
|
||||
container.removeView(from);
|
||||
}
|
||||
|
||||
if (to != null && to.getParent() == null) {
|
||||
container.addView(to);
|
||||
}
|
||||
}
|
||||
|
||||
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(@NonNull View v) {
|
||||
v.removeOnAttachStateChangeListener(this);
|
||||
|
||||
if (changeListener != null) {
|
||||
changeListener.onChangeCompleted();
|
||||
changeListener = null;
|
||||
container = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewDetachedFromWindow(@NonNull View v) { }
|
||||
|
||||
@Override @NonNull
|
||||
public ControllerChangeHandler copy() {
|
||||
return new SimpleSwapChangeHandler(removesFromViewOnPush());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReusable() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+83
-10
@@ -3,12 +3,14 @@ package com.bluelinelabs.conductor.changehandler;
|
||||
import android.annotation.TargetApi;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.transition.Transition;
|
||||
import android.transition.Transition.TransitionListener;
|
||||
import android.transition.TransitionManager;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
|
||||
/**
|
||||
@@ -17,20 +19,51 @@ import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public abstract class TransitionChangeHandler extends ControllerChangeHandler {
|
||||
|
||||
public interface OnTransitionPreparedListener {
|
||||
void onPrepared();
|
||||
}
|
||||
|
||||
private boolean canceled;
|
||||
private boolean needsImmediateCompletion;
|
||||
|
||||
/**
|
||||
* 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, View from, View to, boolean isPush);
|
||||
protected abstract Transition getTransition(@NonNull ViewGroup container, @Nullable View from, @Nullable View to, boolean isPush);
|
||||
|
||||
@Override
|
||||
public void performChange(@NonNull final ViewGroup container, View from, View to, boolean isPush, @NonNull final ControllerChangeCompletedListener changeListener) {
|
||||
Transition transition = getTransition(container, from, to, isPush);
|
||||
public void onAbortPush(@NonNull ControllerChangeHandler newHandler, @Nullable Controller newTop) {
|
||||
super.onAbortPush(newHandler, newTop);
|
||||
|
||||
canceled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void completeImmediately() {
|
||||
super.completeImmediately();
|
||||
|
||||
needsImmediateCompletion = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performChange(@NonNull final ViewGroup container, @Nullable final View from, @Nullable final View to, final boolean isPush, @NonNull final ControllerChangeCompletedListener changeListener) {
|
||||
if (canceled) {
|
||||
changeListener.onChangeCompleted();
|
||||
return;
|
||||
}
|
||||
if (needsImmediateCompletion) {
|
||||
executePropertyChanges(container, from, to, null, isPush);
|
||||
changeListener.onChangeCompleted();
|
||||
return;
|
||||
}
|
||||
|
||||
final Transition transition = getTransition(container, from, to, isPush);
|
||||
transition.addListener(new TransitionListener() {
|
||||
@Override
|
||||
public void onTransitionStart(Transition transition) { }
|
||||
@@ -52,11 +85,51 @@ public abstract class TransitionChangeHandler extends ControllerChangeHandler {
|
||||
public void onTransitionResume(Transition transition) { }
|
||||
});
|
||||
|
||||
TransitionManager.beginDelayedTransition(container, transition);
|
||||
if (from != null) {
|
||||
prepareForTransition(container, from, to, transition, isPush, new OnTransitionPreparedListener() {
|
||||
@Override
|
||||
public void onPrepared() {
|
||||
if (!canceled) {
|
||||
TransitionManager.beginDelayedTransition(container, transition);
|
||||
executePropertyChanges(container, from, to, transition, isPush);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removesFromViewOnPush() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before a transition occurs. This can be used to reorder views, set their transition names, etc. The transition will begin
|
||||
* when {@code onTransitionPreparedListener} is called.
|
||||
*
|
||||
* @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 transition The transition that is being prepared for
|
||||
* @param isPush True if this is a push transaction, false if it's a pop
|
||||
*/
|
||||
public void prepareForTransition(@NonNull ViewGroup container, @Nullable View from, @Nullable View to, @NonNull Transition transition, boolean isPush, @NonNull OnTransitionPreparedListener onTransitionPreparedListener) {
|
||||
onTransitionPreparedListener.onPrepared();
|
||||
}
|
||||
|
||||
/**
|
||||
* This should set all view properties needed for the transition to work properly. By default it removes the "from" view
|
||||
* and adds the "to" view.
|
||||
*
|
||||
* @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 transition The transition with which {@code TransitionManager.beginDelayedTransition} has been called. This will be null only if another ControllerChangeHandler immediately overrides this one.
|
||||
* @param isPush True if this is a push transaction, false if it's a pop
|
||||
*/
|
||||
public void executePropertyChanges(@NonNull ViewGroup container, @Nullable View from, @Nullable View to, @Nullable Transition transition, boolean isPush) {
|
||||
if (from != null && (removesFromViewOnPush() || !isPush) && from.getParent() == container) {
|
||||
container.removeView(from);
|
||||
}
|
||||
if (to != null) {
|
||||
if (to != null && to.getParent() == null) {
|
||||
container.addView(to);
|
||||
}
|
||||
}
|
||||
|
||||
+48
-31
@@ -3,9 +3,11 @@ package com.bluelinelabs.conductor.changehandler;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
import com.bluelinelabs.conductor.internal.ClassUtils;
|
||||
|
||||
@@ -15,13 +17,10 @@ import com.bluelinelabs.conductor.internal.ClassUtils;
|
||||
*/
|
||||
public class TransitionChangeHandlerCompat extends ControllerChangeHandler {
|
||||
|
||||
private static final String KEY_TRANSITION_HANDLER_CLASS = "TransitionChangeHandlerCompat.transitionChangeHandler.class";
|
||||
private static final String KEY_FALLBACK_HANDLER_CLASS = "TransitionChangeHandlerCompat.fallbackChangeHandler.class";
|
||||
private static final String KEY_TRANSITION_HANDLER_STATE = "TransitionChangeHandlerCompat.transitionChangeHandler.state";
|
||||
private static final String KEY_FALLBACK_HANDLER_STATE = "TransitionChangeHandlerCompat.fallbackChangeHandler.state";
|
||||
private static final String KEY_CHANGE_HANDLER_CLASS = "TransitionChangeHandlerCompat.changeHandler.class";
|
||||
private static final String KEY_HANDLER_STATE = "TransitionChangeHandlerCompat.changeHandler.state";
|
||||
|
||||
private TransitionChangeHandler mTransitionChangeHandler;
|
||||
private ControllerChangeHandler mFallbackChangeHandler;
|
||||
private ControllerChangeHandler changeHandler;
|
||||
|
||||
public TransitionChangeHandlerCompat() { }
|
||||
|
||||
@@ -32,49 +31,67 @@ public class TransitionChangeHandlerCompat extends ControllerChangeHandler {
|
||||
* @param transitionChangeHandler The change handler that will be used on API 21 and above
|
||||
* @param fallbackChangeHandler The change handler that will be used on APIs below 21
|
||||
*/
|
||||
public TransitionChangeHandlerCompat(TransitionChangeHandler transitionChangeHandler, ControllerChangeHandler fallbackChangeHandler) {
|
||||
mTransitionChangeHandler = transitionChangeHandler;
|
||||
mFallbackChangeHandler = fallbackChangeHandler;
|
||||
public TransitionChangeHandlerCompat(@NonNull TransitionChangeHandler transitionChangeHandler, @NonNull ControllerChangeHandler fallbackChangeHandler) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
changeHandler = transitionChangeHandler;
|
||||
} else {
|
||||
changeHandler = fallbackChangeHandler;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performChange(@NonNull final ViewGroup container, View from, View to, boolean isPush, @NonNull final ControllerChangeCompletedListener changeListener) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
mTransitionChangeHandler.performChange(container, from, to, isPush, changeListener);
|
||||
} else {
|
||||
mFallbackChangeHandler.performChange(container, from, to, isPush, changeListener);
|
||||
}
|
||||
public void performChange(@NonNull final ViewGroup container, @Nullable View from, @Nullable View to, boolean isPush, @NonNull final ControllerChangeCompletedListener changeListener) {
|
||||
changeHandler.performChange(container, from, to, isPush, changeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveToBundle(@NonNull Bundle bundle) {
|
||||
super.saveToBundle(bundle);
|
||||
|
||||
bundle.putString(KEY_TRANSITION_HANDLER_CLASS, mTransitionChangeHandler.getClass().getName());
|
||||
bundle.putString(KEY_FALLBACK_HANDLER_CLASS, mFallbackChangeHandler.getClass().getName());
|
||||
bundle.putString(KEY_CHANGE_HANDLER_CLASS, changeHandler.getClass().getName());
|
||||
|
||||
Bundle transitionBundle = new Bundle();
|
||||
mTransitionChangeHandler.saveToBundle(transitionBundle);
|
||||
bundle.putBundle(KEY_TRANSITION_HANDLER_STATE, transitionBundle);
|
||||
|
||||
Bundle fallbackBundle = new Bundle();
|
||||
mFallbackChangeHandler.saveToBundle(fallbackBundle);
|
||||
bundle.putBundle(KEY_FALLBACK_HANDLER_STATE, fallbackBundle);
|
||||
Bundle stateBundle = new Bundle();
|
||||
changeHandler.saveToBundle(stateBundle);
|
||||
bundle.putBundle(KEY_HANDLER_STATE, stateBundle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreFromBundle(@NonNull Bundle bundle) {
|
||||
super.restoreFromBundle(bundle);
|
||||
|
||||
String transitionClassName = bundle.getString(KEY_TRANSITION_HANDLER_CLASS);
|
||||
mTransitionChangeHandler = ClassUtils.newInstance(transitionClassName);
|
||||
String className = bundle.getString(KEY_CHANGE_HANDLER_CLASS);
|
||||
changeHandler = ClassUtils.newInstance(className);
|
||||
//noinspection ConstantConditions
|
||||
mTransitionChangeHandler.restoreFromBundle(bundle.getBundle(KEY_TRANSITION_HANDLER_STATE));
|
||||
changeHandler.restoreFromBundle(bundle.getBundle(KEY_HANDLER_STATE));
|
||||
}
|
||||
|
||||
String fallbackClassName = bundle.getString(KEY_FALLBACK_HANDLER_CLASS);
|
||||
mFallbackChangeHandler = ClassUtils.newInstance(fallbackClassName);
|
||||
//noinspection ConstantConditions
|
||||
mFallbackChangeHandler.restoreFromBundle(bundle.getBundle(KEY_FALLBACK_HANDLER_STATE));
|
||||
@Override
|
||||
public boolean removesFromViewOnPush() {
|
||||
return changeHandler.removesFromViewOnPush();
|
||||
}
|
||||
|
||||
@Override @NonNull
|
||||
public ControllerChangeHandler copy() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
return new TransitionChangeHandlerCompat((TransitionChangeHandler)changeHandler.copy(), null);
|
||||
} else {
|
||||
return new TransitionChangeHandlerCompat(null, changeHandler.copy());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAbortPush(@NonNull ControllerChangeHandler newHandler, @Nullable Controller newTop) {
|
||||
changeHandler.onAbortPush(newHandler, newTop);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void completeImmediately() {
|
||||
changeHandler.completeImmediately();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setForceRemoveViewOnPush(boolean force) {
|
||||
changeHandler.setForceRemoveViewOnPush(force);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Executable → Regular
+10
-2
@@ -4,9 +4,12 @@ import android.animation.Animator;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -30,8 +33,8 @@ public class VerticalChangeHandler extends AnimatorChangeHandler {
|
||||
super(duration, removesFromViewOnPush);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Animator getAnimator(@NonNull ViewGroup container, View from, View to, boolean isPush, boolean toAddedToContainer) {
|
||||
@Override @NonNull
|
||||
protected Animator getAnimator(@NonNull ViewGroup container, @Nullable View from, @Nullable View to, boolean isPush, boolean toAddedToContainer) {
|
||||
AnimatorSet animator = new AnimatorSet();
|
||||
List<Animator> viewAnimators = new ArrayList<>();
|
||||
|
||||
@@ -48,4 +51,9 @@ public class VerticalChangeHandler extends AnimatorChangeHandler {
|
||||
@Override
|
||||
protected void resetFromView(@NonNull View from) { }
|
||||
|
||||
@Override @NonNull
|
||||
public ControllerChangeHandler copy() {
|
||||
return new VerticalChangeHandler(getAnimationDuration(), removesFromViewOnPush());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
package com.bluelinelabs.conductor.internal;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
public class ClassUtils {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Class<? extends T> classForName(String className) {
|
||||
return classForName(className, true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Class<? extends T> classForName(String className, boolean allowEmptyName) {
|
||||
@Nullable @SuppressWarnings("unchecked")
|
||||
public static <T> Class<? extends T> classForName(@NonNull String className, boolean allowEmptyName) {
|
||||
if (allowEmptyName && TextUtils.isEmpty(className)) {
|
||||
return null;
|
||||
}
|
||||
@@ -22,10 +19,10 @@ public class ClassUtils {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T newInstance(String className) {
|
||||
@Nullable @SuppressWarnings("unchecked")
|
||||
public static <T> T newInstance(@NonNull String className) {
|
||||
try {
|
||||
Class<? extends T> cls = classForName(className);
|
||||
Class<? extends T> cls = classForName(className, true);
|
||||
return cls != null ? cls.newInstance() : null;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("An exception occurred while creating a new instance of " + className + ". " + e.getMessage());
|
||||
|
||||
+212
-64
@@ -4,50 +4,69 @@ import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.app.Application.ActivityLifecycleCallbacks;
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.ActivityHostedRouter;
|
||||
import com.bluelinelabs.conductor.Router;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class LifecycleHandler extends Fragment implements ActivityLifecycleCallbacks {
|
||||
|
||||
private static final String FRAGMENT_TAG = "LifecycleHandler";
|
||||
|
||||
private static final String KEY_PENDING_PERMISSION_REQUESTS = "LifecycleHandler.pendingPermissionRequests";
|
||||
private static final String KEY_PERMISSION_REQUEST_CODES = "LifecycleHandler.permissionRequests";
|
||||
private static final String KEY_ACTIVITY_REQUEST_CODES = "LifecycleHandler.activityRequests";
|
||||
private static final String KEY_ROUTER_STATE_PREFIX = "LifecycleHandler.routerState";
|
||||
|
||||
private Activity mActivity;
|
||||
private boolean mHasRegisteredCallbacks;
|
||||
private Activity activity;
|
||||
private boolean hasRegisteredCallbacks;
|
||||
private boolean destroyed;
|
||||
private boolean attached;
|
||||
|
||||
private SparseArray<String> mPermissionRequestMap = new SparseArray<>();
|
||||
private SparseArray<String> mActivityRequestMap = new SparseArray<>();
|
||||
private static final Map<Activity, LifecycleHandler> activeLifecycleHandlers = new HashMap<>();
|
||||
private SparseArray<String> permissionRequestMap = new SparseArray<>();
|
||||
private SparseArray<String> activityRequestMap = new SparseArray<>();
|
||||
private ArrayList<PendingPermissionRequest> pendingPermissionRequests = new ArrayList<>();
|
||||
|
||||
private final Map<Integer, Router> mRouterMap = new HashMap<>();
|
||||
private final Map<Integer, ActivityHostedRouter> routerMap = new HashMap<>();
|
||||
|
||||
public LifecycleHandler() {
|
||||
setRetainInstance(true);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
private static LifecycleHandler findInActivity(Activity activity) {
|
||||
LifecycleHandler lifecycleHandler = (LifecycleHandler)activity.getFragmentManager().findFragmentByTag(FRAGMENT_TAG);
|
||||
@Nullable
|
||||
private static LifecycleHandler findInActivity(@NonNull Activity activity) {
|
||||
LifecycleHandler lifecycleHandler = activeLifecycleHandlers.get(activity);
|
||||
if (lifecycleHandler == null) {
|
||||
lifecycleHandler = (LifecycleHandler)activity.getFragmentManager().findFragmentByTag(FRAGMENT_TAG);
|
||||
}
|
||||
if (lifecycleHandler != null) {
|
||||
lifecycleHandler.registerActivityListener(activity);
|
||||
}
|
||||
return lifecycleHandler;
|
||||
}
|
||||
|
||||
public static LifecycleHandler install(Activity activity) {
|
||||
@NonNull
|
||||
public static LifecycleHandler install(@NonNull Activity activity) {
|
||||
LifecycleHandler lifecycleHandler = findInActivity(activity);
|
||||
if (lifecycleHandler == null) {
|
||||
lifecycleHandler = new LifecycleHandler();
|
||||
@@ -57,16 +76,20 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
return lifecycleHandler;
|
||||
}
|
||||
|
||||
public Router getRouter(ViewGroup container, Bundle savedInstanceState) {
|
||||
Router router = mRouterMap.get(getRouterHashKey(container));
|
||||
@NonNull
|
||||
public Router getRouter(@NonNull ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
ActivityHostedRouter router = routerMap.get(getRouterHashKey(container));
|
||||
if (router == null) {
|
||||
router = new Router();
|
||||
router = new ActivityHostedRouter();
|
||||
router.setHost(this, container);
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
router.onRestoreInstanceState(savedInstanceState);
|
||||
Bundle routerSavedState = savedInstanceState.getBundle(KEY_ROUTER_STATE_PREFIX + router.getContainerId());
|
||||
if (routerSavedState != null) {
|
||||
router.restoreInstanceState(routerSavedState);
|
||||
}
|
||||
}
|
||||
mRouterMap.put(getRouterHashKey(container), router);
|
||||
routerMap.put(getRouterHashKey(container), router);
|
||||
} else {
|
||||
router.setHost(this, container);
|
||||
}
|
||||
@@ -74,20 +97,31 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
return router;
|
||||
}
|
||||
|
||||
public Activity getLifecycleActivity() {
|
||||
return mActivity;
|
||||
@NonNull
|
||||
public List<Router> getRouters() {
|
||||
return new ArrayList<Router>(routerMap.values());
|
||||
}
|
||||
|
||||
private static int getRouterHashKey(ViewGroup viewGroup) {
|
||||
@Nullable
|
||||
public Activity getLifecycleActivity() {
|
||||
return activity;
|
||||
}
|
||||
|
||||
private static int getRouterHashKey(@NonNull ViewGroup viewGroup) {
|
||||
return viewGroup.getId();
|
||||
}
|
||||
|
||||
private void registerActivityListener(Activity activity) {
|
||||
mActivity = activity;
|
||||
private void registerActivityListener(@NonNull Activity activity) {
|
||||
this.activity = activity;
|
||||
|
||||
if (!mHasRegisteredCallbacks) {
|
||||
mHasRegisteredCallbacks = true;
|
||||
if (!hasRegisteredCallbacks) {
|
||||
hasRegisteredCallbacks = true;
|
||||
activity.getApplication().registerActivityLifecycleCallbacks(this);
|
||||
|
||||
// Since Fragment transactions are async, we have to keep an <Activity, LifecycleHandler> map in addition
|
||||
// to trying to find the LifecycleHandler fragment in the Activity to handle the case of the developer
|
||||
// trying to immediately get > 1 router in the same Activity. See issue #299.
|
||||
activeLifecycleHandlers.put(activity, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,10 +131,13 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
StringSparseArrayParceler permissionParcel = savedInstanceState.getParcelable(KEY_PERMISSION_REQUEST_CODES);
|
||||
mPermissionRequestMap = permissionParcel != null ? permissionParcel.getStringSparseArray() : null;
|
||||
permissionRequestMap = permissionParcel != null ? permissionParcel.getStringSparseArray() : new SparseArray<String>();
|
||||
|
||||
StringSparseArrayParceler activityParcel = savedInstanceState.getParcelable(KEY_ACTIVITY_REQUEST_CODES);
|
||||
mActivityRequestMap = activityParcel != null ? activityParcel.getStringSparseArray() : null;
|
||||
activityRequestMap = activityParcel != null ? activityParcel.getStringSparseArray() : new SparseArray<String>();
|
||||
|
||||
ArrayList<PendingPermissionRequest> pendingRequests = savedInstanceState.getParcelableArrayList(KEY_PENDING_PERMISSION_REQUESTS);
|
||||
pendingPermissionRequests = pendingRequests != null ? pendingRequests : new ArrayList<PendingPermissionRequest>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,22 +145,70 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
|
||||
outState.putParcelable(KEY_PERMISSION_REQUEST_CODES, new StringSparseArrayParceler(mPermissionRequestMap));
|
||||
outState.putParcelable(KEY_ACTIVITY_REQUEST_CODES, new StringSparseArrayParceler(mActivityRequestMap));
|
||||
outState.putParcelable(KEY_PERMISSION_REQUEST_CODES, new StringSparseArrayParceler(permissionRequestMap));
|
||||
outState.putParcelable(KEY_ACTIVITY_REQUEST_CODES, new StringSparseArrayParceler(activityRequestMap));
|
||||
outState.putParcelableArrayList(KEY_PENDING_PERMISSION_REQUESTS, pendingPermissionRequests);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
if (mActivity != null) {
|
||||
mActivity.getApplication().unregisterActivityLifecycleCallbacks(this);
|
||||
if (activity != null) {
|
||||
activity.getApplication().unregisterActivityLifecycleCallbacks(this);
|
||||
activeLifecycleHandlers.remove(activity);
|
||||
destroyRouters();
|
||||
activity = null;
|
||||
}
|
||||
}
|
||||
|
||||
for (Router router : mRouterMap.values()) {
|
||||
router.onActivityDestroyed(mActivity);
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
destroyed = false;
|
||||
setAttached();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
destroyed = false;
|
||||
setAttached();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
super.onDetach();
|
||||
|
||||
attached = false;
|
||||
destroyRouters();
|
||||
}
|
||||
|
||||
private void setAttached() {
|
||||
if (!attached) {
|
||||
attached = true;
|
||||
|
||||
for (int i = pendingPermissionRequests.size() - 1; i >= 0; i--) {
|
||||
PendingPermissionRequest request = pendingPermissionRequests.remove(i);
|
||||
requestPermissions(request.instanceId, request.permissions, request.requestCode);
|
||||
}
|
||||
}
|
||||
|
||||
mActivity = null;
|
||||
for (ActivityHostedRouter router : routerMap.values()) {
|
||||
router.onContextAvailable();
|
||||
}
|
||||
}
|
||||
|
||||
private void destroyRouters() {
|
||||
if (!destroyed) {
|
||||
destroyed = true;
|
||||
|
||||
if (activity != null) {
|
||||
for (Router router : routerMap.values()) {
|
||||
router.onActivityDestroyed(activity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,9 +216,9 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
String instanceId = mActivityRequestMap.get(requestCode);
|
||||
String instanceId = activityRequestMap.get(requestCode);
|
||||
if (instanceId != null) {
|
||||
for (Router router : mRouterMap.values()) {
|
||||
for (Router router : routerMap.values()) {
|
||||
router.onActivityResult(instanceId, requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
@@ -143,9 +228,9 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
|
||||
String instanceId = mPermissionRequestMap.get(requestCode);
|
||||
String instanceId = permissionRequestMap.get(requestCode);
|
||||
if (instanceId != null) {
|
||||
for (Router router : mRouterMap.values()) {
|
||||
for (Router router : routerMap.values()) {
|
||||
router.onRequestPermissionsResult(instanceId, requestCode, permissions, grantResults);
|
||||
}
|
||||
}
|
||||
@@ -153,7 +238,7 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
|
||||
@Override
|
||||
public boolean shouldShowRequestPermissionRationale(@NonNull String permission) {
|
||||
for (Router router : mRouterMap.values()) {
|
||||
for (Router router : routerMap.values()) {
|
||||
Boolean handled = router.handleRequestedPermission(permission);
|
||||
if (handled != null) {
|
||||
return handled;
|
||||
@@ -166,7 +251,7 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
|
||||
for (Router router : mRouterMap.values()) {
|
||||
for (Router router : routerMap.values()) {
|
||||
router.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
}
|
||||
@@ -175,14 +260,14 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
public void onPrepareOptionsMenu(Menu menu) {
|
||||
super.onPrepareOptionsMenu(menu);
|
||||
|
||||
for (Router router : mRouterMap.values()) {
|
||||
for (Router router : routerMap.values()) {
|
||||
router.onPrepareOptionsMenu(menu);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
for (Router router : mRouterMap.values()) {
|
||||
for (Router router : routerMap.values()) {
|
||||
if (router.onOptionsItemSelected(item)) {
|
||||
return true;
|
||||
}
|
||||
@@ -191,45 +276,61 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
public void registerForActivityRequest(String instanceId, int requestCode) {
|
||||
mActivityRequestMap.put(requestCode, instanceId);
|
||||
public void registerForActivityResult(@NonNull String instanceId, int requestCode) {
|
||||
activityRequestMap.put(requestCode, instanceId);
|
||||
}
|
||||
|
||||
public void unregisterForActivityRequests(String instanceId) {
|
||||
for (int i = mActivityRequestMap.size() - 1; i >= 0; i--) {
|
||||
if (instanceId.equals(mActivityRequestMap.get(mActivityRequestMap.keyAt(i)))) {
|
||||
mActivityRequestMap.removeAt(i);
|
||||
public void unregisterForActivityResults(@NonNull String instanceId) {
|
||||
for (int i = activityRequestMap.size() - 1; i >= 0; i--) {
|
||||
if (instanceId.equals(activityRequestMap.get(activityRequestMap.keyAt(i)))) {
|
||||
activityRequestMap.removeAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void startActivityForResult(String instanceId, Intent intent, int requestCode) {
|
||||
registerForActivityRequest(instanceId, requestCode);
|
||||
public void startActivityForResult(@NonNull String instanceId, @NonNull Intent intent, int requestCode) {
|
||||
registerForActivityResult(instanceId, requestCode);
|
||||
startActivityForResult(intent, requestCode);
|
||||
}
|
||||
|
||||
public void startActivityForResult(String instanceId, Intent intent, int requestCode, Bundle options) {
|
||||
registerForActivityRequest(instanceId, requestCode);
|
||||
public void startActivityForResult(@NonNull String instanceId, @NonNull Intent intent, int requestCode, @Nullable Bundle options) {
|
||||
registerForActivityResult(instanceId, requestCode);
|
||||
startActivityForResult(intent, requestCode, options);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.N)
|
||||
public void startIntentSenderForResult(@NonNull String instanceId, @NonNull IntentSender intent, int requestCode,
|
||||
@Nullable Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags,
|
||||
@Nullable Bundle options) throws IntentSender.SendIntentException {
|
||||
registerForActivityResult(instanceId, requestCode);
|
||||
startIntentSenderForResult(intent, requestCode, fillInIntent, flagsMask, flagsValues, extraFlags, options);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.M)
|
||||
public void requestPermissions(String instanceId, String[] permissions, int requestCode) {
|
||||
mPermissionRequestMap.put(requestCode, instanceId);
|
||||
requestPermissions(permissions, requestCode);
|
||||
public void requestPermissions(@NonNull String instanceId, @NonNull String[] permissions, int requestCode) {
|
||||
if (attached) {
|
||||
permissionRequestMap.put(requestCode, instanceId);
|
||||
requestPermissions(permissions, requestCode);
|
||||
} else {
|
||||
pendingPermissionRequests.add(new PendingPermissionRequest(instanceId, permissions, requestCode));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
|
||||
if (mActivity == null && findInActivity(activity) == LifecycleHandler.this) {
|
||||
mActivity = activity;
|
||||
if (this.activity == null && findInActivity(activity) == LifecycleHandler.this) {
|
||||
this.activity = activity;
|
||||
|
||||
for (ActivityHostedRouter router : routerMap.values()) {
|
||||
router.onContextAvailable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStarted(Activity activity) {
|
||||
if (mActivity == activity) {
|
||||
for (Router router : mRouterMap.values()) {
|
||||
if (this.activity == activity) {
|
||||
for (Router router : routerMap.values()) {
|
||||
router.onActivityStarted(activity);
|
||||
}
|
||||
}
|
||||
@@ -237,8 +338,8 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
|
||||
@Override
|
||||
public void onActivityResumed(Activity activity) {
|
||||
if (mActivity == activity) {
|
||||
for (Router router : mRouterMap.values()) {
|
||||
if (this.activity == activity) {
|
||||
for (Router router : routerMap.values()) {
|
||||
router.onActivityResumed(activity);
|
||||
}
|
||||
}
|
||||
@@ -246,8 +347,8 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
|
||||
@Override
|
||||
public void onActivityPaused(Activity activity) {
|
||||
if (mActivity == activity) {
|
||||
for (Router router : mRouterMap.values()) {
|
||||
if (this.activity == activity) {
|
||||
for (Router router : routerMap.values()) {
|
||||
router.onActivityPaused(activity);
|
||||
}
|
||||
}
|
||||
@@ -255,8 +356,8 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
|
||||
@Override
|
||||
public void onActivityStopped(Activity activity) {
|
||||
if (mActivity == activity) {
|
||||
for (Router router : mRouterMap.values()) {
|
||||
if (this.activity == activity) {
|
||||
for (Router router : routerMap.values()) {
|
||||
router.onActivityStopped(activity);
|
||||
}
|
||||
}
|
||||
@@ -264,13 +365,60 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
|
||||
@Override
|
||||
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
|
||||
if (mActivity == activity) {
|
||||
for (Router router : mRouterMap.values()) {
|
||||
router.onActivitySaveInstanceState(activity, outState);
|
||||
if (this.activity == activity) {
|
||||
for (Router router : routerMap.values()) {
|
||||
Bundle bundle = new Bundle();
|
||||
router.saveInstanceState(bundle);
|
||||
outState.putBundle(KEY_ROUTER_STATE_PREFIX + router.getContainerId(), bundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityDestroyed(Activity activity) { }
|
||||
public void onActivityDestroyed(Activity activity) {
|
||||
activeLifecycleHandlers.remove(activity);
|
||||
}
|
||||
|
||||
private static class PendingPermissionRequest implements Parcelable {
|
||||
final String instanceId;
|
||||
final String[] permissions;
|
||||
final int requestCode;
|
||||
|
||||
PendingPermissionRequest(@NonNull String instanceId, @NonNull String[] permissions, int requestCode) {
|
||||
this.instanceId = instanceId;
|
||||
this.permissions = permissions;
|
||||
this.requestCode = requestCode;
|
||||
}
|
||||
|
||||
private PendingPermissionRequest(Parcel in) {
|
||||
instanceId = in.readString();
|
||||
permissions = in.createStringArray();
|
||||
requestCode = in.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeString(instanceId);
|
||||
out.writeStringArray(permissions);
|
||||
out.writeInt(requestCode);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<PendingPermissionRequest> CREATOR = new Parcelable.Creator<PendingPermissionRequest>() {
|
||||
@Override
|
||||
public PendingPermissionRequest createFromParcel(Parcel in) {
|
||||
return new PendingPermissionRequest(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PendingPermissionRequest[] newArray(int size) {
|
||||
return new PendingPermissionRequest[size];
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+13
-2
@@ -1,6 +1,7 @@
|
||||
package com.bluelinelabs.conductor.internal;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
@@ -9,8 +10,18 @@ import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
public class NoOpControllerChangeHandler extends ControllerChangeHandler {
|
||||
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, @NonNull View from, @NonNull View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
public void performChange(@NonNull ViewGroup container, @Nullable View from, @Nullable View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
changeListener.onChangeCompleted();
|
||||
}
|
||||
|
||||
}
|
||||
@NonNull
|
||||
@Override
|
||||
public ControllerChangeHandler copy() {
|
||||
return new NoOpControllerChangeHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReusable() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+16
-10
@@ -2,52 +2,58 @@ package com.bluelinelabs.conductor.internal;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.SparseArray;
|
||||
|
||||
public class StringSparseArrayParceler implements Parcelable {
|
||||
|
||||
private final SparseArray<String> mStringSparseArray;
|
||||
private final SparseArray<String> stringSparseArray;
|
||||
|
||||
public StringSparseArrayParceler(SparseArray<String> stringSparseArray) {
|
||||
mStringSparseArray = stringSparseArray;
|
||||
public StringSparseArrayParceler(@NonNull SparseArray<String> stringSparseArray) {
|
||||
this.stringSparseArray = stringSparseArray;
|
||||
}
|
||||
|
||||
private StringSparseArrayParceler(Parcel in) {
|
||||
mStringSparseArray = new SparseArray<>();
|
||||
private StringSparseArrayParceler(@NonNull Parcel in) {
|
||||
stringSparseArray = new SparseArray<>();
|
||||
|
||||
final int size = in.readInt();
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
mStringSparseArray.put(in.readInt(), in.readString());
|
||||
stringSparseArray.put(in.readInt(), in.readString());
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public SparseArray<String> getStringSparseArray() {
|
||||
return mStringSparseArray;
|
||||
return stringSparseArray;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
final int size = mStringSparseArray.size();
|
||||
final int size = stringSparseArray.size();
|
||||
|
||||
out.writeInt(size);
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
int key = mStringSparseArray.keyAt(i);
|
||||
int key = stringSparseArray.keyAt(i);
|
||||
|
||||
out.writeInt(key);
|
||||
out.writeString(mStringSparseArray.get(key));
|
||||
out.writeString(stringSparseArray.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<StringSparseArrayParceler> CREATOR = new Parcelable.Creator<StringSparseArrayParceler>() {
|
||||
@Override
|
||||
public StringSparseArrayParceler createFromParcel(Parcel in) {
|
||||
return new StringSparseArrayParceler(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringSparseArrayParceler[] newArray(int size) {
|
||||
return new StringSparseArrayParceler[size];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.bluelinelabs.conductor.internal;
|
||||
|
||||
import android.os.Looper;
|
||||
import android.util.AndroidRuntimeException;
|
||||
|
||||
public class ThreadUtils {
|
||||
|
||||
public static void ensureMainThread() {
|
||||
if (Looper.getMainLooper().getThread() != Thread.currentThread()) {
|
||||
throw new CalledFromWrongThreadException("Methods that affect the view hierarchy can can only be called from the main thread.");
|
||||
}
|
||||
}
|
||||
|
||||
private static final class CalledFromWrongThreadException extends AndroidRuntimeException {
|
||||
CalledFromWrongThreadException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package com.bluelinelabs.conductor.internal;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.View.OnAttachStateChangeListener;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
public class ViewAttachHandler implements OnAttachStateChangeListener {
|
||||
|
||||
private enum ReportedState {
|
||||
VIEW_DETACHED,
|
||||
ACTIVITY_STOPPED,
|
||||
ATTACHED
|
||||
}
|
||||
|
||||
public interface ViewAttachListener {
|
||||
void onAttached();
|
||||
void onDetached(boolean fromActivityStop);
|
||||
void onViewDetachAfterStop();
|
||||
}
|
||||
|
||||
private interface ChildAttachListener {
|
||||
void onAttached();
|
||||
}
|
||||
|
||||
private boolean rootAttached = false;
|
||||
private boolean childrenAttached = false;
|
||||
private boolean activityStopped = false;
|
||||
private ReportedState reportedState = ReportedState.VIEW_DETACHED;
|
||||
private ViewAttachListener attachListener;
|
||||
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(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void listenForAttach(final View view) {
|
||||
view.addOnAttachStateChangeListener(this);
|
||||
}
|
||||
|
||||
public void unregisterAttachListener(View view) {
|
||||
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(true);
|
||||
}
|
||||
|
||||
private void reportAttached() {
|
||||
if (rootAttached && childrenAttached && !activityStopped && reportedState != ReportedState.ATTACHED) {
|
||||
reportedState = ReportedState.ATTACHED;
|
||||
attachListener.onAttached();
|
||||
}
|
||||
}
|
||||
|
||||
private void reportDetached(boolean detachedForActivity) {
|
||||
boolean wasDetachedForActivity = reportedState == ReportedState.ACTIVITY_STOPPED;
|
||||
|
||||
if (detachedForActivity) {
|
||||
reportedState = ReportedState.ACTIVITY_STOPPED;
|
||||
} else {
|
||||
reportedState = ReportedState.VIEW_DETACHED;
|
||||
}
|
||||
|
||||
if (wasDetachedForActivity && !detachedForActivity) {
|
||||
attachListener.onViewDetachAfterStop();
|
||||
} else {
|
||||
attachListener.onDetached(detachedForActivity);
|
||||
}
|
||||
}
|
||||
|
||||
private void listenForDeepestChildAttach(final View view, final ChildAttachListener attachListener) {
|
||||
if (!(view instanceof ViewGroup)) {
|
||||
attachListener.onAttached();
|
||||
return;
|
||||
}
|
||||
|
||||
ViewGroup viewGroup = (ViewGroup)view;
|
||||
if (viewGroup.getChildCount() == 0) {
|
||||
attachListener.onAttached();
|
||||
return;
|
||||
}
|
||||
|
||||
childOnAttachStateChangeListener = new OnAttachStateChangeListener() {
|
||||
boolean attached = false;
|
||||
|
||||
@Override
|
||||
public void onViewAttachedToWindow(View v) {
|
||||
if (!attached) {
|
||||
attached = true;
|
||||
attachListener.onAttached();
|
||||
v.removeOnAttachStateChangeListener(this);
|
||||
childOnAttachStateChangeListener = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewDetachedFromWindow(View v) { }
|
||||
};
|
||||
findDeepestChild(viewGroup).addOnAttachStateChangeListener(childOnAttachStateChangeListener);
|
||||
}
|
||||
|
||||
private View findDeepestChild(ViewGroup viewGroup) {
|
||||
if (viewGroup.getChildCount() == 0) {
|
||||
return viewGroup;
|
||||
}
|
||||
|
||||
View lastChild = viewGroup.getChildAt(viewGroup.getChildCount() - 1);
|
||||
if (lastChild instanceof ViewGroup) {
|
||||
return findDeepestChild((ViewGroup)lastChild);
|
||||
} else {
|
||||
return lastChild;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,66 +1,69 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import org.junit.Assert;
|
||||
import com.bluelinelabs.conductor.util.TestController;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class BackstackTests {
|
||||
|
||||
private Backstack mBackstack;
|
||||
private Backstack backstack;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
mBackstack = new Backstack();
|
||||
backstack = new Backstack();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPush() {
|
||||
Assert.assertEquals(0, mBackstack.size());
|
||||
mBackstack.push(RouterTransaction.builder(new TestController()).build());
|
||||
Assert.assertEquals(1, mBackstack.size());
|
||||
assertEquals(0, backstack.size());
|
||||
backstack.push(RouterTransaction.with(new TestController()));
|
||||
assertEquals(1, backstack.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPop() {
|
||||
mBackstack.push(RouterTransaction.builder(new TestController()).build());
|
||||
mBackstack.push(RouterTransaction.builder(new TestController()).build());
|
||||
Assert.assertEquals(2, mBackstack.size());
|
||||
mBackstack.pop();
|
||||
Assert.assertEquals(1, mBackstack.size());
|
||||
mBackstack.pop();
|
||||
Assert.assertEquals(0, mBackstack.size());
|
||||
backstack.push(RouterTransaction.with(new TestController()));
|
||||
backstack.push(RouterTransaction.with(new TestController()));
|
||||
assertEquals(2, backstack.size());
|
||||
backstack.pop();
|
||||
assertEquals(1, backstack.size());
|
||||
backstack.pop();
|
||||
assertEquals(0, backstack.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPeek() {
|
||||
RouterTransaction transaction1 = RouterTransaction.builder(new TestController()).build();
|
||||
RouterTransaction transaction2 = RouterTransaction.builder(new TestController()).build();
|
||||
RouterTransaction transaction1 = RouterTransaction.with(new TestController());
|
||||
RouterTransaction transaction2 = RouterTransaction.with(new TestController());
|
||||
|
||||
mBackstack.push(transaction1);
|
||||
Assert.assertEquals(transaction1, mBackstack.peek());
|
||||
backstack.push(transaction1);
|
||||
assertEquals(transaction1, backstack.peek());
|
||||
|
||||
mBackstack.push(transaction2);
|
||||
Assert.assertEquals(transaction2, mBackstack.peek());
|
||||
backstack.push(transaction2);
|
||||
assertEquals(transaction2, backstack.peek());
|
||||
|
||||
mBackstack.pop();
|
||||
Assert.assertEquals(transaction1, mBackstack.peek());
|
||||
backstack.pop();
|
||||
assertEquals(transaction1, backstack.peek());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPopTo() {
|
||||
RouterTransaction transaction1 = RouterTransaction.builder(new TestController()).build();
|
||||
RouterTransaction transaction2 = RouterTransaction.builder(new TestController()).build();
|
||||
RouterTransaction transaction3 = RouterTransaction.builder(new TestController()).build();
|
||||
RouterTransaction transaction1 = RouterTransaction.with(new TestController());
|
||||
RouterTransaction transaction2 = RouterTransaction.with(new TestController());
|
||||
RouterTransaction transaction3 = RouterTransaction.with(new TestController());
|
||||
|
||||
mBackstack.push(transaction1);
|
||||
mBackstack.push(transaction2);
|
||||
mBackstack.push(transaction3);
|
||||
backstack.push(transaction1);
|
||||
backstack.push(transaction2);
|
||||
backstack.push(transaction3);
|
||||
|
||||
Assert.assertEquals(3, mBackstack.size());
|
||||
assertEquals(3, backstack.size());
|
||||
|
||||
mBackstack.popTo(transaction1);
|
||||
backstack.popTo(transaction1);
|
||||
|
||||
Assert.assertEquals(1, mBackstack.size());
|
||||
Assert.assertEquals(transaction1, mBackstack.peek());
|
||||
assertEquals(1, backstack.size());
|
||||
assertEquals(transaction1, backstack.peek());
|
||||
}
|
||||
}
|
||||
|
||||
+14
-13
@@ -2,10 +2,12 @@ package com.bluelinelabs.conductor;
|
||||
|
||||
import com.bluelinelabs.conductor.changehandler.FadeChangeHandler;
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
|
||||
import com.bluelinelabs.conductor.util.TestController;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class ControllerChangeHandlerTests {
|
||||
|
||||
@Test
|
||||
@@ -13,26 +15,25 @@ public class ControllerChangeHandlerTests {
|
||||
HorizontalChangeHandler horizontalChangeHandler = new HorizontalChangeHandler();
|
||||
FadeChangeHandler fadeChangeHandler = new FadeChangeHandler(120, false);
|
||||
|
||||
RouterTransaction transaction = RouterTransaction.builder(new TestController())
|
||||
RouterTransaction transaction = RouterTransaction.with(new TestController())
|
||||
.pushChangeHandler(horizontalChangeHandler)
|
||||
.popChangeHandler(fadeChangeHandler)
|
||||
.build();
|
||||
RouterTransaction restoredTransaction = new RouterTransaction(transaction.detachAndSaveInstanceState());
|
||||
.popChangeHandler(fadeChangeHandler);
|
||||
RouterTransaction restoredTransaction = new RouterTransaction(transaction.saveInstanceState());
|
||||
|
||||
ControllerChangeHandler restoredHorizontal = restoredTransaction.getPushControllerChangeHandler();
|
||||
ControllerChangeHandler restoredFade = restoredTransaction.getPopControllerChangeHandler();
|
||||
ControllerChangeHandler restoredHorizontal = restoredTransaction.pushChangeHandler();
|
||||
ControllerChangeHandler restoredFade = restoredTransaction.popChangeHandler();
|
||||
|
||||
Assert.assertEquals(horizontalChangeHandler.getClass(), restoredHorizontal.getClass());
|
||||
Assert.assertEquals(fadeChangeHandler.getClass(), restoredFade.getClass());
|
||||
assertEquals(horizontalChangeHandler.getClass(), restoredHorizontal.getClass());
|
||||
assertEquals(fadeChangeHandler.getClass(), restoredFade.getClass());
|
||||
|
||||
HorizontalChangeHandler restoredHorizontalCast = (HorizontalChangeHandler)restoredHorizontal;
|
||||
FadeChangeHandler restoredFadeCast = (FadeChangeHandler)restoredFade;
|
||||
|
||||
Assert.assertEquals(horizontalChangeHandler.getAnimationDuration(), restoredHorizontalCast.getAnimationDuration());
|
||||
Assert.assertEquals(horizontalChangeHandler.removesFromViewOnPush(), restoredHorizontalCast.removesFromViewOnPush());
|
||||
assertEquals(horizontalChangeHandler.getAnimationDuration(), restoredHorizontalCast.getAnimationDuration());
|
||||
assertEquals(horizontalChangeHandler.removesFromViewOnPush(), restoredHorizontalCast.removesFromViewOnPush());
|
||||
|
||||
Assert.assertEquals(fadeChangeHandler.getAnimationDuration(), restoredFadeCast.getAnimationDuration());
|
||||
Assert.assertEquals(fadeChangeHandler.removesFromViewOnPush(), restoredFadeCast.removesFromViewOnPush());
|
||||
assertEquals(fadeChangeHandler.getAnimationDuration(), restoredFadeCast.getAnimationDuration());
|
||||
assertEquals(fadeChangeHandler.removesFromViewOnPush(), restoredFadeCast.removesFromViewOnPush());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+269
@@ -0,0 +1,269 @@
|
||||
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.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.Arrays;
|
||||
import java.util.Collections;
|
||||
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(Collections.singletonList(true), listener.changeEndReferences);
|
||||
assertEquals(Collections.singletonList(true), listener.postCreateViewReferences);
|
||||
assertEquals(Collections.singletonList(true), listener.postAttachReferences);
|
||||
assertEquals(Collections.emptyList(), listener.postDetachReferences);
|
||||
assertEquals(Collections.emptyList(), listener.postDestroyViewReferences);
|
||||
assertEquals(Collections.emptyList(), 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(Collections.singletonList(true), listener.changeEndReferences);
|
||||
assertEquals(Collections.singletonList(true), listener.postCreateViewReferences);
|
||||
assertEquals(Collections.singletonList(true), listener.postAttachReferences);
|
||||
assertEquals(Collections.emptyList(), listener.postDetachReferences);
|
||||
assertEquals(Collections.emptyList(), listener.postDestroyViewReferences);
|
||||
assertEquals(Collections.emptyList(), 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(Arrays.asList(true, true), listener.changeEndReferences);
|
||||
assertEquals(Collections.singletonList(true), listener.postCreateViewReferences);
|
||||
assertEquals(Collections.singletonList(true), listener.postAttachReferences);
|
||||
assertEquals(Collections.singletonList(true), listener.postDetachReferences);
|
||||
assertEquals(Collections.singletonList(true), listener.postDestroyViewReferences);
|
||||
assertEquals(Collections.singletonList(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(Arrays.asList(true, true), listener.changeEndReferences);
|
||||
assertEquals(Collections.singletonList(true), listener.postCreateViewReferences);
|
||||
assertEquals(Collections.singletonList(true), listener.postAttachReferences);
|
||||
assertEquals(Collections.singletonList(true), listener.postDetachReferences);
|
||||
assertEquals(Collections.singletonList(true), listener.postDestroyViewReferences);
|
||||
assertEquals(Collections.singletonList(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(Collections.singletonList(true), listener.changeEndReferences);
|
||||
assertEquals(Collections.singletonList(true), listener.postCreateViewReferences);
|
||||
assertEquals(Collections.singletonList(true), listener.postAttachReferences);
|
||||
assertEquals(Collections.singletonList(true), listener.postDetachReferences);
|
||||
assertEquals(Collections.singletonList(true), listener.postDestroyViewReferences);
|
||||
assertEquals(Collections.singletonList(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(Collections.singletonList(true), listener.changeEndReferences);
|
||||
assertEquals(Collections.singletonList(true), listener.postCreateViewReferences);
|
||||
assertEquals(Collections.singletonList(true), listener.postAttachReferences);
|
||||
assertEquals(Collections.singletonList(true), listener.postDetachReferences);
|
||||
assertEquals(Collections.singletonList(true), listener.postDestroyViewReferences);
|
||||
assertEquals(Collections.singletonList(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(Collections.singletonList(true), listener.changeEndReferences);
|
||||
assertEquals(Collections.singletonList(true), listener.postCreateViewReferences);
|
||||
assertEquals(Collections.singletonList(true), listener.postAttachReferences);
|
||||
assertEquals(Collections.singletonList(true), listener.postDetachReferences);
|
||||
assertEquals(Collections.singletonList(true), listener.postDestroyViewReferences);
|
||||
assertEquals(Collections.singletonList(true), listener.postDestroyReferences);
|
||||
}
|
||||
|
||||
static class ActivityReferencingLifecycleListener extends Controller.LifecycleListener {
|
||||
final List<Boolean> changeEndReferences = new ArrayList<>();
|
||||
final List<Boolean> postCreateViewReferences = new ArrayList<>();
|
||||
final List<Boolean> postAttachReferences = new ArrayList<>();
|
||||
final List<Boolean> postDetachReferences = new ArrayList<>();
|
||||
final List<Boolean> postDestroyViewReferences = new ArrayList<>();
|
||||
final 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+625
@@ -0,0 +1,625 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller.LifecycleListener;
|
||||
import com.bluelinelabs.conductor.util.ActivityProxy;
|
||||
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;
|
||||
import org.junit.runner.RunWith;
|
||||
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 ControllerLifecycleCallbacksTests {
|
||||
|
||||
private Router router;
|
||||
|
||||
private ActivityProxy activityProxy;
|
||||
private CallState currentCallState;
|
||||
|
||||
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);
|
||||
if (!router.hasRootController()) {
|
||||
router.setRoot(RouterTransaction.with(new TestController()));
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
createActivityController(null, true);
|
||||
|
||||
currentCallState = new CallState(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNormalLifecycle() {
|
||||
TestController controller = new TestController();
|
||||
attachLifecycleListener(controller);
|
||||
|
||||
CallState expectedCallState = new CallState(false);
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
router.pushController(RouterTransaction.with(controller)
|
||||
.pushChangeHandler(getPushHandler(expectedCallState, controller))
|
||||
.popChangeHandler(getPopHandler(expectedCallState, controller)));
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
router.popCurrentController();
|
||||
|
||||
assertNull(controller.getView());
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLifecycleWithActivityStop() {
|
||||
TestController controller = new TestController();
|
||||
attachLifecycleListener(controller);
|
||||
|
||||
CallState expectedCallState = new CallState(false);
|
||||
|
||||
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();
|
||||
attachLifecycleListener(controller);
|
||||
|
||||
CallState expectedCallState = new CallState(false);
|
||||
|
||||
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(true);
|
||||
|
||||
expectedCallState.saveViewStateCalls++;
|
||||
expectedCallState.detachCalls++;
|
||||
expectedCallState.destroyViewCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityProxy.destroy();
|
||||
|
||||
expectedCallState.contextUnavailableCalls++;
|
||||
expectedCallState.destroyCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLifecycleWithActivityConfigurationChange() {
|
||||
TestController controller = new TestController();
|
||||
attachLifecycleListener(controller);
|
||||
|
||||
CallState expectedCallState = new CallState(false);
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
router.pushController(RouterTransaction.with(controller)
|
||||
.pushChangeHandler(getPushHandler(expectedCallState, controller))
|
||||
.tag("root"));
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityProxy.getActivity().isChangingConfigurations = true;
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
activityProxy.saveInstanceState(bundle);
|
||||
|
||||
expectedCallState.saveViewStateCalls++;
|
||||
expectedCallState.saveInstanceStateCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityProxy.pause();
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityProxy.stop(true);
|
||||
expectedCallState.detachCalls++;
|
||||
expectedCallState.destroyViewCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityProxy.destroy();
|
||||
expectedCallState.contextUnavailableCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
createActivityController(bundle, false);
|
||||
controller = (TestController)router.getControllerWithTag("root");
|
||||
|
||||
expectedCallState.contextAvailableCalls++;
|
||||
expectedCallState.restoreInstanceStateCalls++;
|
||||
expectedCallState.restoreViewStateCalls++;
|
||||
expectedCallState.changeStartCalls++;
|
||||
expectedCallState.createViewCalls++;
|
||||
|
||||
// Lifecycle listener isn't attached during restore, grab the current views from the controller for this stuff...
|
||||
currentCallState.restoreInstanceStateCalls = controller.currentCallState.restoreInstanceStateCalls;
|
||||
currentCallState.restoreViewStateCalls = controller.currentCallState.restoreViewStateCalls;
|
||||
currentCallState.changeStartCalls = controller.currentCallState.changeStartCalls;
|
||||
currentCallState.changeEndCalls = controller.currentCallState.changeEndCalls;
|
||||
currentCallState.createViewCalls = controller.currentCallState.createViewCalls;
|
||||
currentCallState.attachCalls = controller.currentCallState.attachCalls;
|
||||
currentCallState.contextAvailableCalls = controller.currentCallState.contextAvailableCalls;
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLifecycleWithActivityBackground() {
|
||||
TestController controller = new TestController();
|
||||
attachLifecycleListener(controller);
|
||||
|
||||
CallState expectedCallState = new CallState(false);
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
router.pushController(RouterTransaction.with(controller)
|
||||
.pushChangeHandler(getPushHandler(expectedCallState, controller)));
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityProxy.pause();
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
activityProxy.saveInstanceState(bundle);
|
||||
|
||||
expectedCallState.saveInstanceStateCalls++;
|
||||
expectedCallState.saveViewStateCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityProxy.resume();
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLifecycleCallOrder() {
|
||||
final TestController testController = new TestController();
|
||||
final CallState callState = new CallState(false);
|
||||
|
||||
testController.addLifecycleListener(new LifecycleListener() {
|
||||
@Override
|
||||
public void preCreateView(@NonNull Controller controller) {
|
||||
callState.createViewCalls++;
|
||||
assertEquals(1, callState.createViewCalls);
|
||||
assertEquals(0, testController.currentCallState.createViewCalls);
|
||||
|
||||
assertEquals(0, callState.attachCalls);
|
||||
assertEquals(0, testController.currentCallState.attachCalls);
|
||||
|
||||
assertEquals(0, callState.detachCalls);
|
||||
assertEquals(0, testController.currentCallState.detachCalls);
|
||||
|
||||
assertEquals(0, callState.destroyViewCalls);
|
||||
assertEquals(0, testController.currentCallState.destroyViewCalls);
|
||||
|
||||
assertEquals(0, callState.destroyCalls);
|
||||
assertEquals(0, testController.currentCallState.destroyCalls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postCreateView(@NonNull Controller controller, @NonNull View view) {
|
||||
callState.createViewCalls++;
|
||||
assertEquals(2, callState.createViewCalls);
|
||||
assertEquals(1, testController.currentCallState.createViewCalls);
|
||||
|
||||
assertEquals(0, callState.attachCalls);
|
||||
assertEquals(0, testController.currentCallState.attachCalls);
|
||||
|
||||
assertEquals(0, callState.detachCalls);
|
||||
assertEquals(0, testController.currentCallState.detachCalls);
|
||||
|
||||
assertEquals(0, callState.destroyViewCalls);
|
||||
assertEquals(0, testController.currentCallState.destroyViewCalls);
|
||||
|
||||
assertEquals(0, callState.destroyCalls);
|
||||
assertEquals(0, testController.currentCallState.destroyCalls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preAttach(@NonNull Controller controller, @NonNull View view) {
|
||||
callState.attachCalls++;
|
||||
assertEquals(2, callState.createViewCalls);
|
||||
assertEquals(1, testController.currentCallState.createViewCalls);
|
||||
|
||||
assertEquals(1, callState.attachCalls);
|
||||
assertEquals(0, testController.currentCallState.attachCalls);
|
||||
|
||||
assertEquals(0, callState.detachCalls);
|
||||
assertEquals(0, testController.currentCallState.detachCalls);
|
||||
|
||||
assertEquals(0, callState.destroyViewCalls);
|
||||
assertEquals(0, testController.currentCallState.destroyViewCalls);
|
||||
|
||||
assertEquals(0, callState.destroyCalls);
|
||||
assertEquals(0, testController.currentCallState.destroyCalls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postAttach(@NonNull Controller controller, @NonNull View view) {
|
||||
callState.attachCalls++;
|
||||
assertEquals(2, callState.createViewCalls);
|
||||
assertEquals(1, testController.currentCallState.createViewCalls);
|
||||
|
||||
assertEquals(2, callState.attachCalls);
|
||||
assertEquals(1, testController.currentCallState.attachCalls);
|
||||
|
||||
assertEquals(0, callState.detachCalls);
|
||||
assertEquals(0, testController.currentCallState.detachCalls);
|
||||
|
||||
assertEquals(0, callState.destroyViewCalls);
|
||||
assertEquals(0, testController.currentCallState.destroyViewCalls);
|
||||
|
||||
assertEquals(0, callState.destroyCalls);
|
||||
assertEquals(0, testController.currentCallState.destroyCalls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDetach(@NonNull Controller controller, @NonNull View view) {
|
||||
callState.detachCalls++;
|
||||
assertEquals(2, callState.createViewCalls);
|
||||
assertEquals(1, testController.currentCallState.createViewCalls);
|
||||
|
||||
assertEquals(2, callState.attachCalls);
|
||||
assertEquals(1, testController.currentCallState.attachCalls);
|
||||
|
||||
assertEquals(1, callState.detachCalls);
|
||||
assertEquals(0, testController.currentCallState.detachCalls);
|
||||
|
||||
assertEquals(0, callState.destroyViewCalls);
|
||||
assertEquals(0, testController.currentCallState.destroyViewCalls);
|
||||
|
||||
assertEquals(0, callState.destroyCalls);
|
||||
assertEquals(0, testController.currentCallState.destroyCalls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postDetach(@NonNull Controller controller, @NonNull View view) {
|
||||
callState.detachCalls++;
|
||||
assertEquals(2, callState.createViewCalls);
|
||||
assertEquals(1, testController.currentCallState.createViewCalls);
|
||||
|
||||
assertEquals(2, callState.attachCalls);
|
||||
assertEquals(1, testController.currentCallState.attachCalls);
|
||||
|
||||
assertEquals(2, callState.detachCalls);
|
||||
assertEquals(1, testController.currentCallState.detachCalls);
|
||||
|
||||
assertEquals(0, callState.destroyViewCalls);
|
||||
assertEquals(0, testController.currentCallState.destroyViewCalls);
|
||||
|
||||
assertEquals(0, callState.destroyCalls);
|
||||
assertEquals(0, testController.currentCallState.destroyCalls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestroyView(@NonNull Controller controller, @NonNull View view) {
|
||||
callState.destroyViewCalls++;
|
||||
assertEquals(2, callState.createViewCalls);
|
||||
assertEquals(1, testController.currentCallState.createViewCalls);
|
||||
|
||||
assertEquals(2, callState.attachCalls);
|
||||
assertEquals(1, testController.currentCallState.attachCalls);
|
||||
|
||||
assertEquals(2, callState.detachCalls);
|
||||
assertEquals(1, testController.currentCallState.detachCalls);
|
||||
|
||||
assertEquals(1, callState.destroyViewCalls);
|
||||
assertEquals(0, testController.currentCallState.destroyViewCalls);
|
||||
|
||||
assertEquals(0, callState.destroyCalls);
|
||||
assertEquals(0, testController.currentCallState.destroyCalls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postDestroyView(@NonNull Controller controller) {
|
||||
callState.destroyViewCalls++;
|
||||
assertEquals(2, callState.createViewCalls);
|
||||
assertEquals(1, testController.currentCallState.createViewCalls);
|
||||
|
||||
assertEquals(2, callState.attachCalls);
|
||||
assertEquals(1, testController.currentCallState.attachCalls);
|
||||
|
||||
assertEquals(2, callState.detachCalls);
|
||||
assertEquals(1, testController.currentCallState.detachCalls);
|
||||
|
||||
assertEquals(2, callState.destroyViewCalls);
|
||||
assertEquals(1, testController.currentCallState.destroyViewCalls);
|
||||
|
||||
assertEquals(0, callState.destroyCalls);
|
||||
assertEquals(0, testController.currentCallState.destroyCalls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestroy(@NonNull Controller controller) {
|
||||
callState.destroyCalls++;
|
||||
assertEquals(2, callState.createViewCalls);
|
||||
assertEquals(1, testController.currentCallState.createViewCalls);
|
||||
|
||||
assertEquals(2, callState.attachCalls);
|
||||
assertEquals(1, testController.currentCallState.attachCalls);
|
||||
|
||||
assertEquals(2, callState.detachCalls);
|
||||
assertEquals(1, testController.currentCallState.detachCalls);
|
||||
|
||||
assertEquals(2, callState.destroyViewCalls);
|
||||
assertEquals(1, testController.currentCallState.destroyViewCalls);
|
||||
|
||||
assertEquals(1, callState.destroyCalls);
|
||||
assertEquals(0, testController.currentCallState.destroyCalls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postDestroy(@NonNull Controller controller) {
|
||||
callState.destroyCalls++;
|
||||
assertEquals(2, callState.createViewCalls);
|
||||
assertEquals(1, testController.currentCallState.createViewCalls);
|
||||
|
||||
assertEquals(2, callState.attachCalls);
|
||||
assertEquals(1, testController.currentCallState.attachCalls);
|
||||
|
||||
assertEquals(2, callState.detachCalls);
|
||||
assertEquals(1, testController.currentCallState.detachCalls);
|
||||
|
||||
assertEquals(2, callState.destroyViewCalls);
|
||||
assertEquals(1, testController.currentCallState.destroyViewCalls);
|
||||
|
||||
assertEquals(2, callState.destroyCalls);
|
||||
assertEquals(1, testController.currentCallState.destroyCalls);
|
||||
}
|
||||
});
|
||||
|
||||
router.pushController(RouterTransaction.with(testController)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
router.popController(testController);
|
||||
|
||||
assertEquals(2, callState.createViewCalls);
|
||||
assertEquals(2, callState.attachCalls);
|
||||
assertEquals(2, callState.detachCalls);
|
||||
assertEquals(2, callState.destroyViewCalls);
|
||||
assertEquals(2, callState.destroyCalls);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildLifecycle() {
|
||||
Controller parent = new TestController();
|
||||
router.pushController(RouterTransaction.with(parent)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
TestController child = new TestController();
|
||||
attachLifecycleListener(child);
|
||||
|
||||
CallState expectedCallState = new CallState(false);
|
||||
|
||||
assertCalls(expectedCallState, child);
|
||||
|
||||
Router childRouter = parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouter
|
||||
.setRoot(RouterTransaction.with(child)
|
||||
.pushChangeHandler(getPushHandler(expectedCallState, child))
|
||||
.popChangeHandler(getPopHandler(expectedCallState, child)));
|
||||
|
||||
assertCalls(expectedCallState, child);
|
||||
|
||||
parent.removeChildRouter(childRouter);
|
||||
|
||||
assertCalls(expectedCallState, child);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildLifecycle2() {
|
||||
Controller parent = new TestController();
|
||||
router.pushController(RouterTransaction.with(parent)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
TestController child = new TestController();
|
||||
attachLifecycleListener(child);
|
||||
|
||||
CallState expectedCallState = new CallState(false);
|
||||
|
||||
assertCalls(expectedCallState, child);
|
||||
|
||||
Router childRouter = parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouter
|
||||
.setRoot(RouterTransaction.with(child)
|
||||
.pushChangeHandler(getPushHandler(expectedCallState, child))
|
||||
.popChangeHandler(getPopHandler(expectedCallState, child)));
|
||||
|
||||
assertCalls(expectedCallState, child);
|
||||
|
||||
router.popCurrentController();
|
||||
|
||||
expectedCallState.detachCalls++;
|
||||
expectedCallState.destroyViewCalls++;
|
||||
expectedCallState.contextUnavailableCalls++;
|
||||
expectedCallState.destroyCalls++;
|
||||
|
||||
assertCalls(expectedCallState, child);
|
||||
}
|
||||
|
||||
private MockChangeHandler getPushHandler(final CallState expectedCallState, final TestController controller) {
|
||||
return MockChangeHandler.listeningChangeHandler(new ChangeHandlerListener() {
|
||||
@Override
|
||||
public void willStartChange() {
|
||||
expectedCallState.contextAvailableCalls++;
|
||||
expectedCallState.changeStartCalls++;
|
||||
expectedCallState.createViewCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didAttachOrDetach() {
|
||||
expectedCallState.attachCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didEndChange() {
|
||||
expectedCallState.changeEndCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private MockChangeHandler getPopHandler(final CallState expectedCallState, final TestController controller) {
|
||||
return MockChangeHandler.listeningChangeHandler(new ChangeHandlerListener() {
|
||||
@Override
|
||||
public void willStartChange() {
|
||||
expectedCallState.changeStartCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didAttachOrDetach() {
|
||||
expectedCallState.destroyViewCalls++;
|
||||
expectedCallState.detachCalls++;
|
||||
expectedCallState.contextUnavailableCalls++;
|
||||
expectedCallState.destroyCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didEndChange() {
|
||||
expectedCallState.changeEndCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void assertCalls(CallState callState, TestController controller) {
|
||||
assertEquals("Expected call counts and controller call counts do not match.", callState, controller.currentCallState);
|
||||
assertEquals("Expected call counts and lifecycle call counts do not match.", callState, currentCallState);
|
||||
}
|
||||
|
||||
private void attachLifecycleListener(Controller controller) {
|
||||
controller.addLifecycleListener(new LifecycleListener() {
|
||||
@Override
|
||||
public void onChangeStart(@NonNull Controller controller, @NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
currentCallState.changeStartCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChangeEnd(@NonNull Controller controller, @NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
currentCallState.changeEndCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postContextAvailable(@NonNull Controller controller, @NonNull Context context) {
|
||||
currentCallState.contextAvailableCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postContextUnavailable(@NonNull Controller controller) {
|
||||
currentCallState.contextUnavailableCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postCreateView(@NonNull Controller controller, @NonNull View view) {
|
||||
currentCallState.createViewCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postAttach(@NonNull Controller controller, @NonNull View view) {
|
||||
currentCallState.attachCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postDestroyView(@NonNull Controller controller) {
|
||||
currentCallState.destroyViewCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postDetach(@NonNull Controller controller, @NonNull View view) {
|
||||
currentCallState.detachCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postDestroy(@NonNull Controller controller) {
|
||||
currentCallState.destroyCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(@NonNull Controller controller, @NonNull Bundle outState) {
|
||||
currentCallState.saveInstanceStateCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(@NonNull Controller controller, @NonNull Bundle savedInstanceState) {
|
||||
currentCallState.restoreInstanceStateCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveViewState(@NonNull Controller controller, @NonNull Bundle outState) {
|
||||
currentCallState.saveViewStateCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreViewState(@NonNull Controller controller, @NonNull Bundle savedViewState) {
|
||||
currentCallState.restoreViewStateCalls++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,627 +0,0 @@
|
||||
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.ControllerChangeHandler.ControllerChangeCompletedListener;
|
||||
import com.bluelinelabs.conductor.ControllerTransaction.ControllerChangeType;
|
||||
|
||||
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> mActivityController;
|
||||
private Router mRouter;
|
||||
|
||||
private CallState mCurrentCallState;
|
||||
|
||||
public void createActivityController(Bundle savedInstanceState) {
|
||||
mActivityController = Robolectric.buildActivity(TestActivity.class).create(savedInstanceState).start();
|
||||
|
||||
@IdRes int containerId = 4;
|
||||
FrameLayout routerContainer = new FrameLayout(mActivityController.get());
|
||||
routerContainer.setId(containerId);
|
||||
|
||||
mRouter = Conductor.attachRouter(mActivityController.get(), routerContainer, savedInstanceState);
|
||||
if (!mRouter.hasRootController()) {
|
||||
mRouter.setRoot(new TestController());
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
createActivityController(null);
|
||||
|
||||
mCurrentCallState = new CallState();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNormalLifecycle() {
|
||||
TestController controller = new TestController();
|
||||
attachLifecycleListener(controller);
|
||||
|
||||
CallState expectedCallState = new CallState();
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
mRouter.pushController(RouterTransaction.builder(controller)
|
||||
.pushChangeHandler(getPushHandler(expectedCallState, controller))
|
||||
.popChangeHandler(getPopHandler(expectedCallState, controller))
|
||||
.build()
|
||||
);
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
mRouter.popCurrentController();
|
||||
|
||||
Assert.assertNull(controller.getView());
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLifecycleWithActivityDestroy() {
|
||||
TestController controller = new TestController();
|
||||
attachLifecycleListener(controller);
|
||||
|
||||
CallState expectedCallState = new CallState();
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
mRouter.pushController(RouterTransaction.builder(controller)
|
||||
.pushChangeHandler(getPushHandler(expectedCallState, controller))
|
||||
.build()
|
||||
);
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
mActivityController.pause();
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
mActivityController.stop();
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
mActivityController.destroy();
|
||||
|
||||
expectedCallState.detachCalls++;
|
||||
expectedCallState.destroyViewCalls++;
|
||||
expectedCallState.destroyCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLifecycleWithActivityConfigurationChange() {
|
||||
TestController controller = new TestController();
|
||||
attachLifecycleListener(controller);
|
||||
|
||||
CallState expectedCallState = new CallState();
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
mRouter.pushController(RouterTransaction.builder(controller)
|
||||
.pushChangeHandler(getPushHandler(expectedCallState, controller))
|
||||
.tag("root")
|
||||
.build()
|
||||
);
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
mActivityController.get().isChangingConfigurations = true;
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
mActivityController.saveInstanceState(bundle);
|
||||
|
||||
expectedCallState.detachCalls++;
|
||||
expectedCallState.saveViewStateCalls++;
|
||||
expectedCallState.saveInstanceStateCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
mActivityController.pause();
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
mActivityController.stop();
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
mActivityController.destroy();
|
||||
expectedCallState.destroyViewCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
createActivityController(bundle);
|
||||
controller = (TestController)mRouter.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...
|
||||
mCurrentCallState.restoreInstanceStateCalls = controller.currentCallState.restoreInstanceStateCalls;
|
||||
mCurrentCallState.restoreViewStateCalls = controller.currentCallState.restoreViewStateCalls;
|
||||
mCurrentCallState.changeStartCalls = controller.currentCallState.changeStartCalls;
|
||||
mCurrentCallState.changeEndCalls = controller.currentCallState.changeEndCalls;
|
||||
mCurrentCallState.createViewCalls = controller.currentCallState.createViewCalls;
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
mActivityController.resume();
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLifecycleWithActivityBackground() {
|
||||
TestController controller = new TestController();
|
||||
attachLifecycleListener(controller);
|
||||
|
||||
CallState expectedCallState = new CallState();
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
mRouter.pushController(RouterTransaction.builder(controller)
|
||||
.pushChangeHandler(getPushHandler(expectedCallState, controller))
|
||||
.build()
|
||||
);
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
mActivityController.pause();
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
mActivityController.saveInstanceState(bundle);
|
||||
|
||||
expectedCallState.detachCalls++;
|
||||
expectedCallState.saveInstanceStateCalls++;
|
||||
expectedCallState.saveViewStateCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
mActivityController.resume();
|
||||
expectedCallState.createViewCalls++;
|
||||
expectedCallState.restoreViewStateCalls++;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLifecycleCallOrder() {
|
||||
final TestController testController = new TestController();
|
||||
final CallState callState = new CallState();
|
||||
|
||||
testController.addLifecycleListener(new LifecycleListener() {
|
||||
@Override
|
||||
public void preCreateView(@NonNull Controller controller) {
|
||||
callState.createViewCalls++;
|
||||
Assert.assertEquals(1, callState.createViewCalls);
|
||||
Assert.assertEquals(0, testController.currentCallState.createViewCalls);
|
||||
|
||||
Assert.assertEquals(0, callState.attachCalls);
|
||||
Assert.assertEquals(0, testController.currentCallState.attachCalls);
|
||||
|
||||
Assert.assertEquals(0, callState.detachCalls);
|
||||
Assert.assertEquals(0, testController.currentCallState.detachCalls);
|
||||
|
||||
Assert.assertEquals(0, callState.destroyViewCalls);
|
||||
Assert.assertEquals(0, testController.currentCallState.destroyViewCalls);
|
||||
|
||||
Assert.assertEquals(0, callState.destroyCalls);
|
||||
Assert.assertEquals(0, testController.currentCallState.destroyCalls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postCreateView(@NonNull Controller controller, @NonNull View view) {
|
||||
callState.createViewCalls++;
|
||||
Assert.assertEquals(2, callState.createViewCalls);
|
||||
Assert.assertEquals(1, testController.currentCallState.createViewCalls);
|
||||
|
||||
Assert.assertEquals(0, callState.attachCalls);
|
||||
Assert.assertEquals(0, testController.currentCallState.attachCalls);
|
||||
|
||||
Assert.assertEquals(0, callState.detachCalls);
|
||||
Assert.assertEquals(0, testController.currentCallState.detachCalls);
|
||||
|
||||
Assert.assertEquals(0, callState.destroyViewCalls);
|
||||
Assert.assertEquals(0, testController.currentCallState.destroyViewCalls);
|
||||
|
||||
Assert.assertEquals(0, callState.destroyCalls);
|
||||
Assert.assertEquals(0, testController.currentCallState.destroyCalls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preAttach(@NonNull Controller controller, @NonNull View view) {
|
||||
callState.attachCalls++;
|
||||
Assert.assertEquals(2, callState.createViewCalls);
|
||||
Assert.assertEquals(1, testController.currentCallState.createViewCalls);
|
||||
|
||||
Assert.assertEquals(1, callState.attachCalls);
|
||||
Assert.assertEquals(0, testController.currentCallState.attachCalls);
|
||||
|
||||
Assert.assertEquals(0, callState.detachCalls);
|
||||
Assert.assertEquals(0, testController.currentCallState.detachCalls);
|
||||
|
||||
Assert.assertEquals(0, callState.destroyViewCalls);
|
||||
Assert.assertEquals(0, testController.currentCallState.destroyViewCalls);
|
||||
|
||||
Assert.assertEquals(0, callState.destroyCalls);
|
||||
Assert.assertEquals(0, testController.currentCallState.destroyCalls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postAttach(@NonNull Controller controller, @NonNull View view) {
|
||||
callState.attachCalls++;
|
||||
Assert.assertEquals(2, callState.createViewCalls);
|
||||
Assert.assertEquals(1, testController.currentCallState.createViewCalls);
|
||||
|
||||
Assert.assertEquals(2, callState.attachCalls);
|
||||
Assert.assertEquals(1, testController.currentCallState.attachCalls);
|
||||
|
||||
Assert.assertEquals(0, callState.detachCalls);
|
||||
Assert.assertEquals(0, testController.currentCallState.detachCalls);
|
||||
|
||||
Assert.assertEquals(0, callState.destroyViewCalls);
|
||||
Assert.assertEquals(0, testController.currentCallState.destroyViewCalls);
|
||||
|
||||
Assert.assertEquals(0, callState.destroyCalls);
|
||||
Assert.assertEquals(0, testController.currentCallState.destroyCalls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDetach(@NonNull Controller controller, @NonNull View view) {
|
||||
callState.detachCalls++;
|
||||
Assert.assertEquals(2, callState.createViewCalls);
|
||||
Assert.assertEquals(1, testController.currentCallState.createViewCalls);
|
||||
|
||||
Assert.assertEquals(2, callState.attachCalls);
|
||||
Assert.assertEquals(1, testController.currentCallState.attachCalls);
|
||||
|
||||
Assert.assertEquals(1, callState.detachCalls);
|
||||
Assert.assertEquals(0, testController.currentCallState.detachCalls);
|
||||
|
||||
Assert.assertEquals(0, callState.destroyViewCalls);
|
||||
Assert.assertEquals(0, testController.currentCallState.destroyViewCalls);
|
||||
|
||||
Assert.assertEquals(0, callState.destroyCalls);
|
||||
Assert.assertEquals(0, testController.currentCallState.destroyCalls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postDetach(@NonNull Controller controller, @NonNull View view) {
|
||||
callState.detachCalls++;
|
||||
Assert.assertEquals(2, callState.createViewCalls);
|
||||
Assert.assertEquals(1, testController.currentCallState.createViewCalls);
|
||||
|
||||
Assert.assertEquals(2, callState.attachCalls);
|
||||
Assert.assertEquals(1, testController.currentCallState.attachCalls);
|
||||
|
||||
Assert.assertEquals(2, callState.detachCalls);
|
||||
Assert.assertEquals(1, testController.currentCallState.detachCalls);
|
||||
|
||||
Assert.assertEquals(0, callState.destroyViewCalls);
|
||||
Assert.assertEquals(0, testController.currentCallState.destroyViewCalls);
|
||||
|
||||
Assert.assertEquals(0, callState.destroyCalls);
|
||||
Assert.assertEquals(0, testController.currentCallState.destroyCalls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestroyView(@NonNull Controller controller, @NonNull View view) {
|
||||
callState.destroyViewCalls++;
|
||||
Assert.assertEquals(2, callState.createViewCalls);
|
||||
Assert.assertEquals(1, testController.currentCallState.createViewCalls);
|
||||
|
||||
Assert.assertEquals(2, callState.attachCalls);
|
||||
Assert.assertEquals(1, testController.currentCallState.attachCalls);
|
||||
|
||||
Assert.assertEquals(2, callState.detachCalls);
|
||||
Assert.assertEquals(1, testController.currentCallState.detachCalls);
|
||||
|
||||
Assert.assertEquals(1, callState.destroyViewCalls);
|
||||
Assert.assertEquals(0, testController.currentCallState.destroyViewCalls);
|
||||
|
||||
Assert.assertEquals(0, callState.destroyCalls);
|
||||
Assert.assertEquals(0, testController.currentCallState.destroyCalls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postDestroyView(@NonNull Controller controller) {
|
||||
callState.destroyViewCalls++;
|
||||
Assert.assertEquals(2, callState.createViewCalls);
|
||||
Assert.assertEquals(1, testController.currentCallState.createViewCalls);
|
||||
|
||||
Assert.assertEquals(2, callState.attachCalls);
|
||||
Assert.assertEquals(1, testController.currentCallState.attachCalls);
|
||||
|
||||
Assert.assertEquals(2, callState.detachCalls);
|
||||
Assert.assertEquals(1, testController.currentCallState.detachCalls);
|
||||
|
||||
Assert.assertEquals(2, callState.destroyViewCalls);
|
||||
Assert.assertEquals(1, testController.currentCallState.destroyViewCalls);
|
||||
|
||||
Assert.assertEquals(0, callState.destroyCalls);
|
||||
Assert.assertEquals(0, testController.currentCallState.destroyCalls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestroy(@NonNull Controller controller) {
|
||||
callState.destroyCalls++;
|
||||
Assert.assertEquals(2, callState.createViewCalls);
|
||||
Assert.assertEquals(1, testController.currentCallState.createViewCalls);
|
||||
|
||||
Assert.assertEquals(2, callState.attachCalls);
|
||||
Assert.assertEquals(1, testController.currentCallState.attachCalls);
|
||||
|
||||
Assert.assertEquals(2, callState.detachCalls);
|
||||
Assert.assertEquals(1, testController.currentCallState.detachCalls);
|
||||
|
||||
Assert.assertEquals(2, callState.destroyViewCalls);
|
||||
Assert.assertEquals(1, testController.currentCallState.destroyViewCalls);
|
||||
|
||||
Assert.assertEquals(1, callState.destroyCalls);
|
||||
Assert.assertEquals(0, testController.currentCallState.destroyCalls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postDestroy(@NonNull Controller controller) {
|
||||
callState.destroyCalls++;
|
||||
Assert.assertEquals(2, callState.createViewCalls);
|
||||
Assert.assertEquals(1, testController.currentCallState.createViewCalls);
|
||||
|
||||
Assert.assertEquals(2, callState.attachCalls);
|
||||
Assert.assertEquals(1, testController.currentCallState.attachCalls);
|
||||
|
||||
Assert.assertEquals(2, callState.detachCalls);
|
||||
Assert.assertEquals(1, testController.currentCallState.detachCalls);
|
||||
|
||||
Assert.assertEquals(2, callState.destroyViewCalls);
|
||||
Assert.assertEquals(1, testController.currentCallState.destroyViewCalls);
|
||||
|
||||
Assert.assertEquals(2, callState.destroyCalls);
|
||||
Assert.assertEquals(1, testController.currentCallState.destroyCalls);
|
||||
}
|
||||
});
|
||||
|
||||
mRouter.pushController(RouterTransaction.builder(testController)
|
||||
.pushChangeHandler(new ChangeHandler(new ChangeHandlerListener() {
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
container.addView(to);
|
||||
ViewUtils.setAttached(to, true);
|
||||
changeListener.onChangeCompleted();
|
||||
}
|
||||
}))
|
||||
.popChangeHandler(new ChangeHandler(new ChangeHandlerListener() {
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
container.removeView(from);
|
||||
ViewUtils.setAttached(from, false);
|
||||
changeListener.onChangeCompleted();
|
||||
}
|
||||
}))
|
||||
.build());
|
||||
|
||||
mRouter.popController(testController);
|
||||
|
||||
Assert.assertEquals(2, callState.createViewCalls);
|
||||
Assert.assertEquals(2, callState.attachCalls);
|
||||
Assert.assertEquals(2, callState.detachCalls);
|
||||
Assert.assertEquals(2, callState.destroyViewCalls);
|
||||
Assert.assertEquals(2, callState.destroyCalls);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildLifecycle() {
|
||||
Controller parent = new TestController();
|
||||
mRouter.pushController(RouterTransaction.builder(parent)
|
||||
.pushChangeHandler(new ChangeHandler(new ChangeHandlerListener() {
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
container.addView(to);
|
||||
ViewUtils.setAttached(to, true);
|
||||
changeListener.onChangeCompleted();
|
||||
}
|
||||
}))
|
||||
.build());
|
||||
|
||||
TestController child = new TestController();
|
||||
attachLifecycleListener(child);
|
||||
|
||||
CallState expectedCallState = new CallState();
|
||||
|
||||
assertCalls(expectedCallState, child);
|
||||
|
||||
parent.addChildController(ChildControllerTransaction.builder(child, TestController.VIEW_ID)
|
||||
.pushChangeHandler(getPushHandler(expectedCallState, child))
|
||||
.popChangeHandler(getPopHandler(expectedCallState, child))
|
||||
.build()
|
||||
);
|
||||
|
||||
assertCalls(expectedCallState, child);
|
||||
|
||||
parent.removeChildController(child);
|
||||
|
||||
assertCalls(expectedCallState, child);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildLifecycle2() {
|
||||
Controller parent = new TestController();
|
||||
mRouter.pushController(RouterTransaction.builder(parent)
|
||||
.pushChangeHandler(new ChangeHandler(new ChangeHandlerListener() {
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
container.addView(to);
|
||||
ViewUtils.setAttached(to, true);
|
||||
changeListener.onChangeCompleted();
|
||||
}
|
||||
}))
|
||||
.popChangeHandler(new ChangeHandler(new ChangeHandlerListener() {
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
container.removeView(from);
|
||||
ViewUtils.setAttached(from, false);
|
||||
changeListener.onChangeCompleted();
|
||||
}
|
||||
}))
|
||||
.build());
|
||||
|
||||
TestController child = new TestController();
|
||||
attachLifecycleListener(child);
|
||||
|
||||
CallState expectedCallState = new CallState();
|
||||
|
||||
assertCalls(expectedCallState, child);
|
||||
|
||||
parent.addChildController(ChildControllerTransaction.builder(child, TestController.VIEW_ID)
|
||||
.pushChangeHandler(getPushHandler(expectedCallState, child))
|
||||
.popChangeHandler(getPopHandler(expectedCallState, child))
|
||||
.build()
|
||||
);
|
||||
|
||||
assertCalls(expectedCallState, child);
|
||||
|
||||
mRouter.popCurrentController();
|
||||
ViewUtils.setAttached(child.getView(), false);
|
||||
|
||||
expectedCallState.detachCalls++;
|
||||
expectedCallState.destroyViewCalls++;
|
||||
expectedCallState.destroyCalls++;
|
||||
assertCalls(expectedCallState, child);
|
||||
}
|
||||
|
||||
private ChangeHandler getPushHandler(final CallState expectedCallState, final TestController controller) {
|
||||
return new ChangeHandler(new ChangeHandlerListener() {
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
expectedCallState.changeStartCalls++;
|
||||
expectedCallState.createViewCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
container.addView(to);
|
||||
ViewUtils.setAttached(to, true);
|
||||
|
||||
expectedCallState.attachCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
changeListener.onChangeCompleted();
|
||||
|
||||
expectedCallState.changeEndCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private ChangeHandler getPopHandler(final CallState expectedCallState, final TestController controller) {
|
||||
return new ChangeHandler(new ChangeHandlerListener() {
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
expectedCallState.changeStartCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
container.removeView(from);
|
||||
ViewUtils.setAttached(from, false);
|
||||
|
||||
expectedCallState.destroyViewCalls++;
|
||||
expectedCallState.detachCalls++;
|
||||
expectedCallState.destroyCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
changeListener.onChangeCompleted();
|
||||
|
||||
expectedCallState.changeEndCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void assertCalls(CallState callState, TestController controller) {
|
||||
Assert.assertEquals("Expected call counts and controller call counts do not match.", callState, controller.currentCallState);
|
||||
Assert.assertEquals("Expected call counts and lifecycle call counts do not match.", callState, mCurrentCallState);
|
||||
}
|
||||
|
||||
private void attachLifecycleListener(Controller controller) {
|
||||
controller.addLifecycleListener(new LifecycleListener() {
|
||||
@Override
|
||||
public void onChangeStart(@NonNull Controller controller, @NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
mCurrentCallState.changeStartCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChangeEnd(@NonNull Controller controller, @NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
mCurrentCallState.changeEndCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postCreateView(@NonNull Controller controller, @NonNull View view) {
|
||||
mCurrentCallState.createViewCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postAttach(@NonNull Controller controller, @NonNull View view) {
|
||||
mCurrentCallState.attachCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postDestroyView(@NonNull Controller controller) {
|
||||
mCurrentCallState.destroyViewCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postDetach(@NonNull Controller controller, @NonNull View view) {
|
||||
mCurrentCallState.detachCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postDestroy(@NonNull Controller controller) {
|
||||
mCurrentCallState.destroyCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(@NonNull Controller controller, @NonNull Bundle outState) {
|
||||
mCurrentCallState.saveInstanceStateCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(@NonNull Controller controller, @NonNull Bundle savedInstanceState) {
|
||||
mCurrentCallState.restoreInstanceStateCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveViewState(@NonNull Controller controller, @NonNull Bundle outState) {
|
||||
mCurrentCallState.saveViewStateCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreViewState(@NonNull Controller controller, @NonNull Bundle savedViewState) {
|
||||
mCurrentCallState.restoreViewStateCalls++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
interface ChangeHandlerListener {
|
||||
void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener);
|
||||
}
|
||||
|
||||
public static class ChangeHandler extends ControllerChangeHandler {
|
||||
|
||||
private ChangeHandlerListener mListener;
|
||||
|
||||
public ChangeHandler() { }
|
||||
|
||||
public ChangeHandler(ChangeHandlerListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
mListener.performChange(container, from, to, isPush, changeListener);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,38 +3,38 @@ 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.widget.FrameLayout;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller.RetainViewMode;
|
||||
import com.bluelinelabs.conductor.util.ActivityProxy;
|
||||
import com.bluelinelabs.conductor.util.CallState;
|
||||
import com.bluelinelabs.conductor.util.TestController;
|
||||
import com.bluelinelabs.conductor.util.ViewUtils;
|
||||
|
||||
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;
|
||||
|
||||
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)
|
||||
public class ControllerTests {
|
||||
|
||||
private ActivityController<TestActivity> mActivityController;
|
||||
private Router mRouter;
|
||||
private ActivityProxy activityProxy;
|
||||
private Router router;
|
||||
|
||||
public void createActivityController(Bundle savedInstanceState) {
|
||||
mActivityController = Robolectric.buildActivity(TestActivity.class).create(savedInstanceState).start();
|
||||
|
||||
@IdRes int containerId = 4;
|
||||
FrameLayout routerContainer = new FrameLayout(mActivityController.get());
|
||||
routerContainer.setId(containerId);
|
||||
|
||||
mRouter = Conductor.attachRouter(mActivityController.get(), routerContainer, savedInstanceState);
|
||||
if (!mRouter.hasRootController()) {
|
||||
mRouter.setRoot(new TestController());
|
||||
activityProxy = new ActivityProxy().create(savedInstanceState).start().resume();
|
||||
router = Conductor.attachRouter(activityProxy.getActivity(), activityProxy.getView(), savedInstanceState);
|
||||
if (!router.hasRootController()) {
|
||||
router.setRoot(RouterTransaction.with(new TestController()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,29 +46,30 @@ public class ControllerTests {
|
||||
@Test
|
||||
public void testViewRetention() {
|
||||
Controller controller = new TestController();
|
||||
controller.setRouter(router);
|
||||
|
||||
// Test View getting released w/ RELEASE_DETACH
|
||||
controller.setRetainViewMode(RetainViewMode.RELEASE_DETACH);
|
||||
Assert.assertNull(controller.getView());
|
||||
View view = controller.inflate(new FrameLayout(mRouter.getActivity()));
|
||||
Assert.assertNotNull(controller.getView());
|
||||
ViewUtils.setAttached(view, true);
|
||||
Assert.assertNotNull(controller.getView());
|
||||
ViewUtils.setAttached(view, false);
|
||||
Assert.assertNull(controller.getView());
|
||||
assertNull(controller.getView());
|
||||
View view = controller.inflate(router.container);
|
||||
assertNotNull(controller.getView());
|
||||
ViewUtils.reportAttached(view, true);
|
||||
assertNotNull(controller.getView());
|
||||
ViewUtils.reportAttached(view, false);
|
||||
assertNull(controller.getView());
|
||||
|
||||
// Test View getting retained w/ RETAIN_DETACH
|
||||
controller.setRetainViewMode(RetainViewMode.RETAIN_DETACH);
|
||||
view = controller.inflate(new FrameLayout(mRouter.getActivity()));
|
||||
Assert.assertNotNull(controller.getView());
|
||||
ViewUtils.setAttached(view, true);
|
||||
Assert.assertNotNull(controller.getView());
|
||||
ViewUtils.setAttached(view, false);
|
||||
Assert.assertNotNull(controller.getView());
|
||||
view = controller.inflate(router.container);
|
||||
assertNotNull(controller.getView());
|
||||
ViewUtils.reportAttached(view, true);
|
||||
assertNotNull(controller.getView());
|
||||
ViewUtils.reportAttached(view, false);
|
||||
assertNotNull(controller.getView());
|
||||
|
||||
// Ensure re-setting RELEASE_DETACH releases
|
||||
controller.setRetainViewMode(RetainViewMode.RELEASE_DETACH);
|
||||
Assert.assertNull(controller.getView());
|
||||
assertNull(controller.getView());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -76,22 +77,21 @@ public class ControllerTests {
|
||||
TestController controller = new TestController();
|
||||
CallState expectedCallState = new CallState(true);
|
||||
|
||||
mRouter.pushController(RouterTransaction.builder(controller).build());
|
||||
ViewUtils.setAttached(controller.getView(), true);
|
||||
router.pushController(RouterTransaction.with(controller));
|
||||
|
||||
// Ensure that calling onActivityResult w/o requesting a result doesn't do anything
|
||||
mRouter.onActivityResult(1, Activity.RESULT_OK, null);
|
||||
router.onActivityResult(1, Activity.RESULT_OK, null);
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
// Ensure starting an activity for result gets us the result back
|
||||
controller.startActivityForResult(new Intent("action"), 1);
|
||||
mRouter.onActivityResult(1, Activity.RESULT_OK, null);
|
||||
router.onActivityResult(1, Activity.RESULT_OK, null);
|
||||
expectedCallState.onActivityResultCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
// Ensure requesting a result w/o calling startActivityForResult works
|
||||
controller.registerForActivityResult(2);
|
||||
mRouter.onActivityResult(2, Activity.RESULT_OK, null);
|
||||
router.onActivityResult(2, Activity.RESULT_OK, null);
|
||||
expectedCallState.onActivityResultCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
@@ -101,29 +101,28 @@ public class ControllerTests {
|
||||
TestController parent = new TestController();
|
||||
TestController child = new TestController();
|
||||
|
||||
mRouter.pushController(RouterTransaction.builder(parent).build());
|
||||
ViewUtils.setAttached(parent.getView(), true);
|
||||
parent.addChildController(ChildControllerTransaction.builder(child, TestController.VIEW_ID).build());
|
||||
ViewUtils.setAttached(child.getView(), true);
|
||||
router.pushController(RouterTransaction.with(parent));
|
||||
parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.VIEW_ID))
|
||||
.setRoot(RouterTransaction.with(child));
|
||||
|
||||
CallState childExpectedCallState = new CallState(true);
|
||||
CallState parentExpectedCallState = new CallState(true);
|
||||
|
||||
// Ensure that calling onActivityResult w/o requesting a result doesn't do anything
|
||||
mRouter.onActivityResult(1, Activity.RESULT_OK, null);
|
||||
router.onActivityResult(1, Activity.RESULT_OK, null);
|
||||
assertCalls(childExpectedCallState, child);
|
||||
assertCalls(parentExpectedCallState, parent);
|
||||
|
||||
// Ensure starting an activity for result gets us the result back
|
||||
child.startActivityForResult(new Intent("action"), 1);
|
||||
mRouter.onActivityResult(1, Activity.RESULT_OK, null);
|
||||
router.onActivityResult(1, Activity.RESULT_OK, null);
|
||||
childExpectedCallState.onActivityResultCalls++;
|
||||
assertCalls(childExpectedCallState, child);
|
||||
assertCalls(parentExpectedCallState, parent);
|
||||
|
||||
// Ensure requesting a result w/o calling startActivityForResult works
|
||||
child.registerForActivityResult(2);
|
||||
mRouter.onActivityResult(2, Activity.RESULT_OK, null);
|
||||
router.onActivityResult(2, Activity.RESULT_OK, null);
|
||||
childExpectedCallState.onActivityResultCalls++;
|
||||
assertCalls(childExpectedCallState, child);
|
||||
assertCalls(parentExpectedCallState, parent);
|
||||
@@ -136,11 +135,10 @@ public class ControllerTests {
|
||||
TestController controller = new TestController();
|
||||
CallState expectedCallState = new CallState(true);
|
||||
|
||||
mRouter.pushController(RouterTransaction.builder(controller).build());
|
||||
ViewUtils.setAttached(controller.getView(), true);
|
||||
router.pushController(RouterTransaction.with(controller));
|
||||
|
||||
// Ensure that calling handleRequestedPermission w/o requesting a result doesn't do anything
|
||||
mRouter.onRequestPermissionsResult("anotherId", 1, requestedPermissions, new int[] {1});
|
||||
router.onRequestPermissionsResult("anotherId", 1, requestedPermissions, new int[] {1});
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
// Ensure requesting the permission gets us the result back
|
||||
@@ -148,7 +146,7 @@ public class ControllerTests {
|
||||
controller.requestPermissions(requestedPermissions, 1);
|
||||
} catch (NoSuchMethodError ignored) { }
|
||||
|
||||
mRouter.onRequestPermissionsResult(controller.getInstanceId(), 1, requestedPermissions, new int[] {1});
|
||||
router.onRequestPermissionsResult(controller.getInstanceId(), 1, requestedPermissions, new int[] {1});
|
||||
expectedCallState.onRequestPermissionsResultCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
@@ -160,16 +158,15 @@ public class ControllerTests {
|
||||
TestController parent = new TestController();
|
||||
TestController child = new TestController();
|
||||
|
||||
mRouter.pushController(RouterTransaction.builder(parent).build());
|
||||
ViewUtils.setAttached(parent.getView(), true);
|
||||
parent.addChildController(ChildControllerTransaction.builder(child, TestController.VIEW_ID).build());
|
||||
ViewUtils.setAttached(child.getView(), true);
|
||||
router.pushController(RouterTransaction.with(parent));
|
||||
parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.VIEW_ID))
|
||||
.setRoot(RouterTransaction.with(child));
|
||||
|
||||
CallState childExpectedCallState = new CallState(true);
|
||||
CallState parentExpectedCallState = new CallState(true);
|
||||
|
||||
// Ensure that calling handleRequestedPermission w/o requesting a result doesn't do anything
|
||||
mRouter.onRequestPermissionsResult("anotherId", 1, requestedPermissions, new int[] {1});
|
||||
router.onRequestPermissionsResult("anotherId", 1, requestedPermissions, new int[] {1});
|
||||
assertCalls(childExpectedCallState, child);
|
||||
assertCalls(parentExpectedCallState, parent);
|
||||
|
||||
@@ -178,7 +175,7 @@ public class ControllerTests {
|
||||
child.requestPermissions(requestedPermissions, 1);
|
||||
} catch (NoSuchMethodError ignored) { }
|
||||
|
||||
mRouter.onRequestPermissionsResult(child.getInstanceId(), 1, requestedPermissions, new int[] {1});
|
||||
router.onRequestPermissionsResult(child.getInstanceId(), 1, requestedPermissions, new int[] {1});
|
||||
childExpectedCallState.onRequestPermissionsResultCalls++;
|
||||
assertCalls(childExpectedCallState, child);
|
||||
assertCalls(parentExpectedCallState, parent);
|
||||
@@ -189,35 +186,34 @@ public class ControllerTests {
|
||||
TestController controller = new TestController();
|
||||
CallState expectedCallState = new CallState(true);
|
||||
|
||||
mRouter.pushController(RouterTransaction.builder(controller).build());
|
||||
ViewUtils.setAttached(controller.getView(), true);
|
||||
router.pushController(RouterTransaction.with(controller));
|
||||
|
||||
// Ensure that calling onCreateOptionsMenu w/o declaring that we have one doesn't do anything
|
||||
mRouter.onCreateOptionsMenu(null, null);
|
||||
router.onCreateOptionsMenu(null, null);
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
// Ensure calling onCreateOptionsMenu with a menu works
|
||||
controller.setHasOptionsMenu(true);
|
||||
|
||||
// Ensure it'll still get called back next time onCreateOptionsMenu is called
|
||||
mRouter.onCreateOptionsMenu(null, null);
|
||||
router.onCreateOptionsMenu(null, null);
|
||||
expectedCallState.createOptionsMenuCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
// Ensure we stop getting them when we hide it
|
||||
controller.setOptionsMenuHidden(true);
|
||||
mRouter.onCreateOptionsMenu(null, null);
|
||||
router.onCreateOptionsMenu(null, null);
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
// Ensure we get the callback them when we un-hide it
|
||||
controller.setOptionsMenuHidden(false);
|
||||
mRouter.onCreateOptionsMenu(null, null);
|
||||
router.onCreateOptionsMenu(null, null);
|
||||
expectedCallState.createOptionsMenuCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
// Ensure we don't get the callback when we no longer have a menu
|
||||
controller.setHasOptionsMenu(false);
|
||||
mRouter.onCreateOptionsMenu(null, null);
|
||||
router.onCreateOptionsMenu(null, null);
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
|
||||
@@ -226,16 +222,15 @@ public class ControllerTests {
|
||||
TestController parent = new TestController();
|
||||
TestController child = new TestController();
|
||||
|
||||
mRouter.pushController(RouterTransaction.builder(parent).build());
|
||||
ViewUtils.setAttached(parent.getView(), true);
|
||||
parent.addChildController(ChildControllerTransaction.builder(child, TestController.VIEW_ID).build());
|
||||
ViewUtils.setAttached(child.getView(), true);
|
||||
router.pushController(RouterTransaction.with(parent));
|
||||
parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.VIEW_ID))
|
||||
.setRoot(RouterTransaction.with(child));
|
||||
|
||||
CallState childExpectedCallState = new CallState(true);
|
||||
CallState parentExpectedCallState = new CallState(true);
|
||||
|
||||
// Ensure that calling onCreateOptionsMenu w/o declaring that we have one doesn't do anything
|
||||
mRouter.onCreateOptionsMenu(null, null);
|
||||
router.onCreateOptionsMenu(null, null);
|
||||
assertCalls(childExpectedCallState, child);
|
||||
assertCalls(parentExpectedCallState, parent);
|
||||
|
||||
@@ -243,33 +238,175 @@ public class ControllerTests {
|
||||
child.setHasOptionsMenu(true);
|
||||
|
||||
// Ensure it'll still get called back next time onCreateOptionsMenu is called
|
||||
mRouter.onCreateOptionsMenu(null, null);
|
||||
router.onCreateOptionsMenu(null, null);
|
||||
childExpectedCallState.createOptionsMenuCalls++;
|
||||
assertCalls(childExpectedCallState, child);
|
||||
assertCalls(parentExpectedCallState, parent);
|
||||
|
||||
// Ensure we stop getting them when we hide it
|
||||
child.setOptionsMenuHidden(true);
|
||||
mRouter.onCreateOptionsMenu(null, null);
|
||||
router.onCreateOptionsMenu(null, null);
|
||||
assertCalls(childExpectedCallState, child);
|
||||
assertCalls(parentExpectedCallState, parent);
|
||||
|
||||
// Ensure we get the callback them when we un-hide it
|
||||
child.setOptionsMenuHidden(false);
|
||||
mRouter.onCreateOptionsMenu(null, null);
|
||||
router.onCreateOptionsMenu(null, null);
|
||||
childExpectedCallState.createOptionsMenuCalls++;
|
||||
assertCalls(childExpectedCallState, child);
|
||||
assertCalls(parentExpectedCallState, parent);
|
||||
|
||||
// Ensure we don't get the callback when we no longer have a menu
|
||||
child.setHasOptionsMenu(false);
|
||||
mRouter.onCreateOptionsMenu(null, null);
|
||||
router.onCreateOptionsMenu(null, null);
|
||||
assertCalls(childExpectedCallState, child);
|
||||
assertCalls(parentExpectedCallState, parent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddRemoveChildControllers() {
|
||||
TestController parent = new TestController();
|
||||
TestController child1 = new TestController();
|
||||
TestController child2 = new TestController();
|
||||
|
||||
router.pushController(RouterTransaction.with(parent));
|
||||
|
||||
assertEquals(0, parent.getChildRouters().size());
|
||||
assertNull(child1.getParentController());
|
||||
assertNull(child2.getParentController());
|
||||
|
||||
Router childRouter = parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouter.setPopsLastView(true);
|
||||
childRouter.setRoot(RouterTransaction.with(child1));
|
||||
|
||||
assertEquals(1, parent.getChildRouters().size());
|
||||
assertEquals(childRouter, parent.getChildRouters().get(0));
|
||||
assertEquals(1, childRouter.getBackstackSize());
|
||||
assertEquals(child1, childRouter.getControllers().get(0));
|
||||
assertEquals(parent, child1.getParentController());
|
||||
assertNull(child2.getParentController());
|
||||
|
||||
childRouter = parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouter.pushController(RouterTransaction.with(child2));
|
||||
|
||||
assertEquals(1, parent.getChildRouters().size());
|
||||
assertEquals(childRouter, parent.getChildRouters().get(0));
|
||||
assertEquals(2, childRouter.getBackstackSize());
|
||||
assertEquals(child1, childRouter.getControllers().get(0));
|
||||
assertEquals(child2, childRouter.getControllers().get(1));
|
||||
assertEquals(parent, child1.getParentController());
|
||||
assertEquals(parent, child2.getParentController());
|
||||
|
||||
childRouter.popController(child2);
|
||||
|
||||
assertEquals(1, parent.getChildRouters().size());
|
||||
assertEquals(childRouter, parent.getChildRouters().get(0));
|
||||
assertEquals(1, childRouter.getBackstackSize());
|
||||
assertEquals(child1, childRouter.getControllers().get(0));
|
||||
assertEquals(parent, child1.getParentController());
|
||||
assertNull(child2.getParentController());
|
||||
|
||||
childRouter.popController(child1);
|
||||
|
||||
assertEquals(1, parent.getChildRouters().size());
|
||||
assertEquals(childRouter, parent.getChildRouters().get(0));
|
||||
assertEquals(0, childRouter.getBackstackSize());
|
||||
assertNull(child1.getParentController());
|
||||
assertNull(child2.getParentController());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddRemoveChildRouters() {
|
||||
TestController parent = new TestController();
|
||||
|
||||
TestController child1 = new TestController();
|
||||
TestController child2 = new TestController();
|
||||
|
||||
router.pushController(RouterTransaction.with(parent));
|
||||
|
||||
assertEquals(0, parent.getChildRouters().size());
|
||||
assertNull(child1.getParentController());
|
||||
assertNull(child2.getParentController());
|
||||
|
||||
Router childRouter1 = parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.CHILD_VIEW_ID_1));
|
||||
Router childRouter2 = parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.CHILD_VIEW_ID_2));
|
||||
|
||||
childRouter1.setRoot(RouterTransaction.with(child1));
|
||||
childRouter2.setRoot(RouterTransaction.with(child2));
|
||||
|
||||
assertEquals(2, parent.getChildRouters().size());
|
||||
assertEquals(childRouter1, parent.getChildRouters().get(0));
|
||||
assertEquals(childRouter2, parent.getChildRouters().get(1));
|
||||
assertEquals(1, childRouter1.getBackstackSize());
|
||||
assertEquals(1, childRouter2.getBackstackSize());
|
||||
assertEquals(child1, childRouter1.getControllers().get(0));
|
||||
assertEquals(child2, childRouter2.getControllers().get(0));
|
||||
assertEquals(parent, child1.getParentController());
|
||||
assertEquals(parent, child2.getParentController());
|
||||
|
||||
parent.removeChildRouter(childRouter2);
|
||||
|
||||
assertEquals(1, parent.getChildRouters().size());
|
||||
assertEquals(childRouter1, parent.getChildRouters().get(0));
|
||||
assertEquals(1, childRouter1.getBackstackSize());
|
||||
assertEquals(0, childRouter2.getBackstackSize());
|
||||
assertEquals(child1, childRouter1.getControllers().get(0));
|
||||
assertEquals(parent, child1.getParentController());
|
||||
assertNull(child2.getParentController());
|
||||
|
||||
parent.removeChildRouter(childRouter1);
|
||||
|
||||
assertEquals(0, parent.getChildRouters().size());
|
||||
assertEquals(0, childRouter1.getBackstackSize());
|
||||
assertEquals(0, childRouter2.getBackstackSize());
|
||||
assertNull(child1.getParentController());
|
||||
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) {
|
||||
Assert.assertEquals("Expected call counts and controller call counts do not match.", callState, controller.currentCallState);
|
||||
assertEquals("Expected call counts and controller call counts do not match.", callState, controller.currentCallState);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+10
-30
@@ -1,52 +1,32 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.IdRes;
|
||||
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
|
||||
import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler;
|
||||
|
||||
import junit.framework.Assert;
|
||||
import com.bluelinelabs.conductor.util.TestController;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class ControllerTransactionTests {
|
||||
|
||||
@Test
|
||||
public void testRouterSaveRestore() {
|
||||
RouterTransaction transaction = RouterTransaction.builder(new TestController())
|
||||
RouterTransaction transaction = RouterTransaction.with(new TestController())
|
||||
.pushChangeHandler(new HorizontalChangeHandler())
|
||||
.popChangeHandler(new VerticalChangeHandler())
|
||||
.tag("Test Tag")
|
||||
.build();
|
||||
.tag("Test Tag");
|
||||
|
||||
Bundle bundle = transaction.detachAndSaveInstanceState();
|
||||
Bundle bundle = transaction.saveInstanceState();
|
||||
|
||||
RouterTransaction restoredTransaction = new RouterTransaction(bundle);
|
||||
|
||||
Assert.assertEquals(transaction.getController().getClass(), restoredTransaction.getController().getClass());
|
||||
Assert.assertEquals(transaction.getPushControllerChangeHandler().getClass(), restoredTransaction.getPushControllerChangeHandler().getClass());
|
||||
Assert.assertEquals(transaction.getPopControllerChangeHandler().getClass(), restoredTransaction.getPopControllerChangeHandler().getClass());
|
||||
Assert.assertEquals(transaction.getTag(), restoredTransaction.getTag());
|
||||
assertEquals(transaction.controller.getClass(), restoredTransaction.controller.getClass());
|
||||
assertEquals(transaction.pushChangeHandler().getClass(), restoredTransaction.pushChangeHandler().getClass());
|
||||
assertEquals(transaction.popChangeHandler().getClass(), restoredTransaction.popChangeHandler().getClass());
|
||||
assertEquals(transaction.tag(), restoredTransaction.tag());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildSaveRestore() {
|
||||
@IdRes int layoutId = 234;
|
||||
ChildControllerTransaction transaction = ChildControllerTransaction.builder(new TestController(), layoutId)
|
||||
.pushChangeHandler(new HorizontalChangeHandler())
|
||||
.popChangeHandler(new VerticalChangeHandler())
|
||||
.tag("Test Tag")
|
||||
.build();
|
||||
|
||||
Bundle bundle = transaction.detachAndSaveInstanceState();
|
||||
|
||||
ChildControllerTransaction restoredTransaction = new ChildControllerTransaction(bundle);
|
||||
|
||||
Assert.assertEquals(transaction.containerId, restoredTransaction.containerId);
|
||||
Assert.assertEquals(transaction.getController().getClass(), restoredTransaction.getController().getClass());
|
||||
Assert.assertEquals(transaction.getPushControllerChangeHandler().getClass(), restoredTransaction.getPushControllerChangeHandler().getClass());
|
||||
Assert.assertEquals(transaction.getPopControllerChangeHandler().getClass(), restoredTransaction.getPopControllerChangeHandler().getClass());
|
||||
Assert.assertEquals(transaction.getTag(), restoredTransaction.getTag());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.util.ActivityProxy;
|
||||
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 static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(manifest = Config.NONE)
|
||||
public class ReattachCaseTests {
|
||||
|
||||
private ActivityProxy activityProxy;
|
||||
private Router router;
|
||||
|
||||
public void createActivityController(Bundle 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()));
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
createActivityController(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNeedsAttachingOnPauseAndOrientation() {
|
||||
final TestController controllerA = new TestController();
|
||||
final TestController controllerB = new TestController();
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerA)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
assertTrue(controllerA.isAttached());
|
||||
assertFalse(controllerB.isAttached());
|
||||
|
||||
sleepWakeDevice();
|
||||
|
||||
assertTrue(controllerA.isAttached());
|
||||
assertFalse(controllerB.isAttached());
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerB)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
assertFalse(controllerA.isAttached());
|
||||
assertTrue(controllerB.isAttached());
|
||||
|
||||
activityProxy.rotate();
|
||||
router.rebindIfNeeded();
|
||||
|
||||
assertFalse(controllerA.isAttached());
|
||||
assertTrue(controllerB.isAttached());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildNeedsAttachOnPauseAndOrientation() {
|
||||
final Controller controllerA = new TestController();
|
||||
final Controller childController = new TestController();
|
||||
final Controller controllerB = new TestController();
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerA)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
Router childRouter = controllerA.getChildRouter((ViewGroup)controllerA.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouter.pushController(RouterTransaction.with(childController)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
assertTrue(controllerA.isAttached());
|
||||
assertTrue(childController.isAttached());
|
||||
assertFalse(controllerB.isAttached());
|
||||
|
||||
sleepWakeDevice();
|
||||
|
||||
assertTrue(controllerA.isAttached());
|
||||
assertTrue(childController.isAttached());
|
||||
assertFalse(controllerB.isAttached());
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerB)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
assertFalse(controllerA.isAttached());
|
||||
assertFalse(childController.isAttached());
|
||||
assertTrue(controllerB.isAttached());
|
||||
|
||||
activityProxy.rotate();
|
||||
router.rebindIfNeeded();
|
||||
|
||||
assertFalse(controllerA.isAttached());
|
||||
assertFalse(childController.isAttached());
|
||||
assertTrue(childController.getNeedsAttach());
|
||||
assertTrue(controllerB.isAttached());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildHandleBackOnOrientation() {
|
||||
final TestController controllerA = new TestController();
|
||||
final TestController controllerB = new TestController();
|
||||
final TestController childController = new TestController();
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerA)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
assertTrue(controllerA.isAttached());
|
||||
assertFalse(controllerB.isAttached());
|
||||
assertFalse(childController.isAttached());
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerB)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
Router childRouter = controllerB.getChildRouter((ViewGroup)controllerB.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouter.setPopsLastView(true);
|
||||
childRouter.pushController(RouterTransaction.with(childController)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
assertFalse(controllerA.isAttached());
|
||||
assertTrue(controllerB.isAttached());
|
||||
assertTrue(childController.isAttached());
|
||||
|
||||
activityProxy.rotate();
|
||||
router.rebindIfNeeded();
|
||||
|
||||
assertFalse(controllerA.isAttached());
|
||||
assertTrue(controllerB.isAttached());
|
||||
assertTrue(childController.isAttached());
|
||||
|
||||
router.handleBack();
|
||||
|
||||
assertFalse(controllerA.isAttached());
|
||||
assertTrue(controllerB.isAttached());
|
||||
assertFalse(childController.isAttached());
|
||||
|
||||
router.handleBack();
|
||||
|
||||
assertTrue(controllerA.isAttached());
|
||||
assertFalse(controllerB.isAttached());
|
||||
assertFalse(childController.isAttached());
|
||||
}
|
||||
|
||||
// Attempt to test https://github.com/bluelinelabs/Conductor/issues/86#issuecomment-231381271
|
||||
@Test
|
||||
public void testReusedChildRouterHandleBackOnOrientation() {
|
||||
TestController controllerA = new TestController();
|
||||
TestController controllerB = new TestController();
|
||||
TestController childController = new TestController();
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerA)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
assertTrue(controllerA.isAttached());
|
||||
assertFalse(controllerB.isAttached());
|
||||
assertFalse(childController.isAttached());
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerB)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
Router childRouter = controllerB.getChildRouter((ViewGroup)controllerB.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouter.setPopsLastView(true);
|
||||
childRouter.pushController(RouterTransaction.with(childController)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
assertFalse(controllerA.isAttached());
|
||||
assertTrue(controllerB.isAttached());
|
||||
assertTrue(childController.isAttached());
|
||||
|
||||
router.handleBack();
|
||||
|
||||
assertFalse(controllerA.isAttached());
|
||||
assertTrue(controllerB.isAttached());
|
||||
assertFalse(childController.isAttached());
|
||||
|
||||
childController = new TestController();
|
||||
childRouter.pushController(RouterTransaction.with(childController)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
assertFalse(controllerA.isAttached());
|
||||
assertTrue(controllerB.isAttached());
|
||||
assertTrue(childController.isAttached());
|
||||
|
||||
activityProxy.rotate();
|
||||
router.rebindIfNeeded();
|
||||
|
||||
assertFalse(controllerA.isAttached());
|
||||
assertTrue(controllerB.isAttached());
|
||||
assertTrue(childController.isAttached());
|
||||
|
||||
router.handleBack();
|
||||
|
||||
childController = new TestController();
|
||||
childRouter.pushController(RouterTransaction.with(childController)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
assertFalse(controllerA.isAttached());
|
||||
assertTrue(controllerB.isAttached());
|
||||
assertTrue(childController.isAttached());
|
||||
|
||||
router.handleBack();
|
||||
|
||||
assertFalse(controllerA.isAttached());
|
||||
assertTrue(controllerB.isAttached());
|
||||
assertFalse(childController.isAttached());
|
||||
|
||||
router.handleBack();
|
||||
|
||||
assertTrue(controllerA.isAttached());
|
||||
assertFalse(controllerB.isAttached());
|
||||
assertFalse(childController.isAttached());
|
||||
}
|
||||
|
||||
private void sleepWakeDevice() {
|
||||
activityProxy.saveInstanceState(new Bundle()).pause();
|
||||
activityProxy.resume();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import com.bluelinelabs.conductor.util.ActivityProxy;
|
||||
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.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
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)
|
||||
public class RouterChangeHandlerTests {
|
||||
|
||||
private Router router;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
ActivityProxy activityProxy = new ActivityProxy().create(null).start().resume();
|
||||
router = Conductor.attachRouter(activityProxy.getActivity(), activityProxy.getView(), null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetRootHandler() {
|
||||
MockChangeHandler handler = MockChangeHandler.taggedHandler("root", true);
|
||||
TestController rootController = new TestController();
|
||||
router.setRoot(RouterTransaction.with(rootController).pushChangeHandler(handler));
|
||||
|
||||
assertTrue(rootController.changeHandlerHistory.isValidHistory);
|
||||
assertNull(rootController.changeHandlerHistory.latestFromView());
|
||||
assertNotNull(rootController.changeHandlerHistory.latestToView());
|
||||
assertEquals(rootController.getView(), rootController.changeHandlerHistory.latestToView());
|
||||
assertTrue(rootController.changeHandlerHistory.latestIsPush());
|
||||
assertEquals(handler.tag, rootController.changeHandlerHistory.latestChangeHandler().tag);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPushPopHandlers() {
|
||||
TestController rootController = new TestController();
|
||||
router.setRoot(RouterTransaction.with(rootController).pushChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
View rootView = rootController.getView();
|
||||
|
||||
MockChangeHandler pushHandler = MockChangeHandler.taggedHandler("push", true);
|
||||
MockChangeHandler popHandler = MockChangeHandler.taggedHandler("pop", true);
|
||||
TestController pushController = new TestController();
|
||||
router.pushController(RouterTransaction.with(pushController).pushChangeHandler(pushHandler).popChangeHandler(popHandler));
|
||||
|
||||
assertTrue(rootController.changeHandlerHistory.isValidHistory);
|
||||
assertTrue(pushController.changeHandlerHistory.isValidHistory);
|
||||
|
||||
assertNotNull(pushController.changeHandlerHistory.latestFromView());
|
||||
assertNotNull(pushController.changeHandlerHistory.latestToView());
|
||||
assertEquals(rootView, pushController.changeHandlerHistory.latestFromView());
|
||||
assertEquals(pushController.getView(), pushController.changeHandlerHistory.latestToView());
|
||||
assertTrue(pushController.changeHandlerHistory.latestIsPush());
|
||||
assertEquals(pushHandler.tag, pushController.changeHandlerHistory.latestChangeHandler().tag);
|
||||
|
||||
View pushView = pushController.getView();
|
||||
router.popController(pushController);
|
||||
|
||||
assertNotNull(pushController.changeHandlerHistory.latestFromView());
|
||||
assertNotNull(pushController.changeHandlerHistory.latestToView());
|
||||
assertEquals(pushView, pushController.changeHandlerHistory.fromViewAt(1));
|
||||
assertEquals(rootController.getView(), pushController.changeHandlerHistory.latestToView());
|
||||
assertFalse(pushController.changeHandlerHistory.latestIsPush());
|
||||
assertEquals(popHandler.tag, pushController.changeHandlerHistory.latestChangeHandler().tag);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResetRootHandlers() {
|
||||
TestController initialController1 = new TestController();
|
||||
MockChangeHandler initialPushHandler1 = MockChangeHandler.taggedHandler("initialPush1", true);
|
||||
MockChangeHandler initialPopHandler1 = MockChangeHandler.taggedHandler("initialPop1", true);
|
||||
router.setRoot(RouterTransaction.with(initialController1).pushChangeHandler(initialPushHandler1).popChangeHandler(initialPopHandler1));
|
||||
TestController initialController2 = new TestController();
|
||||
MockChangeHandler initialPushHandler2 = MockChangeHandler.taggedHandler("initialPush2", false);
|
||||
MockChangeHandler initialPopHandler2 = MockChangeHandler.taggedHandler("initialPop2", false);
|
||||
router.pushController(RouterTransaction.with(initialController2).pushChangeHandler(initialPushHandler2).popChangeHandler(initialPopHandler2));
|
||||
|
||||
View initialView1 = initialController1.getView();
|
||||
View initialView2 = initialController2.getView();
|
||||
|
||||
TestController newRootController = new TestController();
|
||||
MockChangeHandler newRootHandler = MockChangeHandler.taggedHandler("newRootHandler", true);
|
||||
|
||||
router.setRoot(RouterTransaction.with(newRootController).pushChangeHandler(newRootHandler));
|
||||
|
||||
assertTrue(initialController1.changeHandlerHistory.isValidHistory);
|
||||
assertTrue(initialController2.changeHandlerHistory.isValidHistory);
|
||||
assertTrue(newRootController.changeHandlerHistory.isValidHistory);
|
||||
|
||||
assertEquals(3, initialController1.changeHandlerHistory.size());
|
||||
assertEquals(2, initialController2.changeHandlerHistory.size());
|
||||
assertEquals(1, newRootController.changeHandlerHistory.size());
|
||||
|
||||
assertNotNull(initialController1.changeHandlerHistory.latestToView());
|
||||
assertEquals(newRootController.getView(), initialController1.changeHandlerHistory.latestToView());
|
||||
assertEquals(initialView1, initialController1.changeHandlerHistory.latestFromView());
|
||||
assertEquals(newRootHandler.tag, initialController1.changeHandlerHistory.latestChangeHandler().tag);
|
||||
assertTrue(initialController1.changeHandlerHistory.latestIsPush());
|
||||
|
||||
assertNull(initialController2.changeHandlerHistory.latestToView());
|
||||
assertEquals(initialView2, initialController2.changeHandlerHistory.latestFromView());
|
||||
assertEquals(newRootHandler.tag, initialController2.changeHandlerHistory.latestChangeHandler().tag);
|
||||
assertTrue(initialController2.changeHandlerHistory.latestIsPush());
|
||||
|
||||
assertNotNull(newRootController.changeHandlerHistory.latestToView());
|
||||
assertEquals(newRootController.getView(), newRootController.changeHandlerHistory.latestToView());
|
||||
assertEquals(initialView1, newRootController.changeHandlerHistory.latestFromView());
|
||||
assertEquals(newRootHandler.tag, newRootController.changeHandlerHistory.latestChangeHandler().tag);
|
||||
assertTrue(newRootController.changeHandlerHistory.latestIsPush());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetBackstackHandlers() {
|
||||
TestController initialController1 = new TestController();
|
||||
MockChangeHandler initialPushHandler1 = MockChangeHandler.taggedHandler("initialPush1", true);
|
||||
MockChangeHandler initialPopHandler1 = MockChangeHandler.taggedHandler("initialPop1", true);
|
||||
router.setRoot(RouterTransaction.with(initialController1).pushChangeHandler(initialPushHandler1).popChangeHandler(initialPopHandler1));
|
||||
TestController initialController2 = new TestController();
|
||||
MockChangeHandler initialPushHandler2 = MockChangeHandler.taggedHandler("initialPush2", false);
|
||||
MockChangeHandler initialPopHandler2 = MockChangeHandler.taggedHandler("initialPop2", false);
|
||||
router.pushController(RouterTransaction.with(initialController2).pushChangeHandler(initialPushHandler2).popChangeHandler(initialPopHandler2));
|
||||
|
||||
View initialView1 = initialController1.getView();
|
||||
View initialView2 = initialController2.getView();
|
||||
|
||||
TestController newController1 = new TestController();
|
||||
TestController newController2 = new TestController();
|
||||
MockChangeHandler setBackstackHandler = MockChangeHandler.taggedHandler("setBackstackHandler", true);
|
||||
|
||||
List<RouterTransaction> newBackstack = Arrays.asList(
|
||||
RouterTransaction.with(newController1),
|
||||
RouterTransaction.with(newController2)
|
||||
);
|
||||
|
||||
router.setBackstack(newBackstack, setBackstackHandler);
|
||||
|
||||
assertTrue(initialController1.changeHandlerHistory.isValidHistory);
|
||||
assertTrue(initialController2.changeHandlerHistory.isValidHistory);
|
||||
assertTrue(newController1.changeHandlerHistory.isValidHistory);
|
||||
|
||||
assertEquals(3, initialController1.changeHandlerHistory.size());
|
||||
assertEquals(2, initialController2.changeHandlerHistory.size());
|
||||
assertEquals(0, newController1.changeHandlerHistory.size());
|
||||
assertEquals(1, newController2.changeHandlerHistory.size());
|
||||
|
||||
assertNotNull(initialController1.changeHandlerHistory.latestToView());
|
||||
assertEquals(newController2.getView(), initialController1.changeHandlerHistory.latestToView());
|
||||
assertEquals(initialView1, initialController1.changeHandlerHistory.latestFromView());
|
||||
assertEquals(setBackstackHandler.tag, initialController1.changeHandlerHistory.latestChangeHandler().tag);
|
||||
assertTrue(initialController1.changeHandlerHistory.latestIsPush());
|
||||
|
||||
assertNull(initialController2.changeHandlerHistory.latestToView());
|
||||
assertEquals(initialView2, initialController2.changeHandlerHistory.latestFromView());
|
||||
assertEquals(setBackstackHandler.tag, initialController2.changeHandlerHistory.latestChangeHandler().tag);
|
||||
assertTrue(initialController2.changeHandlerHistory.latestIsPush());
|
||||
|
||||
assertNotNull(newController2.changeHandlerHistory.latestToView());
|
||||
assertEquals(newController2.getView(), newController2.changeHandlerHistory.latestToView());
|
||||
assertEquals(initialView1, newController2.changeHandlerHistory.latestFromView());
|
||||
assertEquals(setBackstackHandler.tag, newController2.changeHandlerHistory.latestChangeHandler().tag);
|
||||
assertTrue(newController2.changeHandlerHistory.latestIsPush());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetBackstackWithTwoVisibleHandlers() {
|
||||
TestController initialController1 = new TestController();
|
||||
MockChangeHandler initialPushHandler1 = MockChangeHandler.taggedHandler("initialPush1", true);
|
||||
MockChangeHandler initialPopHandler1 = MockChangeHandler.taggedHandler("initialPop1", true);
|
||||
router.setRoot(RouterTransaction.with(initialController1).pushChangeHandler(initialPushHandler1).popChangeHandler(initialPopHandler1));
|
||||
TestController initialController2 = new TestController();
|
||||
MockChangeHandler initialPushHandler2 = MockChangeHandler.taggedHandler("initialPush2", false);
|
||||
MockChangeHandler initialPopHandler2 = MockChangeHandler.taggedHandler("initialPop2", false);
|
||||
router.pushController(RouterTransaction.with(initialController2).pushChangeHandler(initialPushHandler2).popChangeHandler(initialPopHandler2));
|
||||
|
||||
View initialView1 = initialController1.getView();
|
||||
View initialView2 = initialController2.getView();
|
||||
|
||||
TestController newController1 = new TestController();
|
||||
TestController newController2 = new TestController();
|
||||
MockChangeHandler setBackstackHandler = MockChangeHandler.taggedHandler("setBackstackHandler", true);
|
||||
|
||||
List<RouterTransaction> newBackstack = Arrays.asList(
|
||||
RouterTransaction.with(newController1),
|
||||
RouterTransaction.with(newController2).pushChangeHandler(MockChangeHandler.noRemoveViewOnPushHandler())
|
||||
);
|
||||
|
||||
router.setBackstack(newBackstack, setBackstackHandler);
|
||||
|
||||
assertTrue(initialController1.changeHandlerHistory.isValidHistory);
|
||||
assertTrue(initialController2.changeHandlerHistory.isValidHistory);
|
||||
assertTrue(newController1.changeHandlerHistory.isValidHistory);
|
||||
|
||||
assertEquals(3, initialController1.changeHandlerHistory.size());
|
||||
assertEquals(2, initialController2.changeHandlerHistory.size());
|
||||
assertEquals(2, newController1.changeHandlerHistory.size());
|
||||
assertEquals(1, newController2.changeHandlerHistory.size());
|
||||
|
||||
assertNotNull(initialController1.changeHandlerHistory.latestToView());
|
||||
assertEquals(newController1.getView(), initialController1.changeHandlerHistory.latestToView());
|
||||
assertEquals(initialView1, initialController1.changeHandlerHistory.latestFromView());
|
||||
assertEquals(setBackstackHandler.tag, initialController1.changeHandlerHistory.latestChangeHandler().tag);
|
||||
assertTrue(initialController1.changeHandlerHistory.latestIsPush());
|
||||
|
||||
assertNull(initialController2.changeHandlerHistory.latestToView());
|
||||
assertEquals(initialView2, initialController2.changeHandlerHistory.latestFromView());
|
||||
assertEquals(setBackstackHandler.tag, initialController2.changeHandlerHistory.latestChangeHandler().tag);
|
||||
assertTrue(initialController2.changeHandlerHistory.latestIsPush());
|
||||
|
||||
assertNotNull(newController1.changeHandlerHistory.latestToView());
|
||||
assertEquals(newController1.getView(), newController1.changeHandlerHistory.toViewAt(0));
|
||||
assertEquals(newController2.getView(), newController1.changeHandlerHistory.latestToView());
|
||||
assertEquals(initialView1, newController1.changeHandlerHistory.fromViewAt(0));
|
||||
assertEquals(newController1.getView(), newController1.changeHandlerHistory.latestFromView());
|
||||
assertEquals(setBackstackHandler.tag, newController1.changeHandlerHistory.latestChangeHandler().tag);
|
||||
assertTrue(newController1.changeHandlerHistory.latestIsPush());
|
||||
|
||||
assertNotNull(newController2.changeHandlerHistory.latestToView());
|
||||
assertEquals(newController2.getView(), newController2.changeHandlerHistory.latestToView());
|
||||
assertEquals(newController1.getView(), newController2.changeHandlerHistory.latestFromView());
|
||||
assertEquals(setBackstackHandler.tag, newController2.changeHandlerHistory.latestChangeHandler().tag);
|
||||
assertTrue(newController2.changeHandlerHistory.latestIsPush());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,26 +1,37 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.widget.FrameLayout;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.changehandler.FadeChangeHandler;
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
|
||||
import com.bluelinelabs.conductor.util.ActivityProxy;
|
||||
import com.bluelinelabs.conductor.util.MockChangeHandler;
|
||||
import com.bluelinelabs.conductor.util.TestController;
|
||||
|
||||
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.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(manifest = Config.NONE)
|
||||
public class RouterTests {
|
||||
|
||||
private Router mRouter;
|
||||
private Router router;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
Activity activity = Robolectric.buildActivity(TestActivity.class).create().get();
|
||||
mRouter = Conductor.attachRouter(activity, new FrameLayout(activity), null);
|
||||
ActivityProxy activityProxy = new ActivityProxy().create(null).start().resume();
|
||||
router = Conductor.attachRouter(activityProxy.getActivity(), activityProxy.getView(), null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -29,13 +40,13 @@ public class RouterTests {
|
||||
|
||||
Controller rootController = new TestController();
|
||||
|
||||
Assert.assertFalse(mRouter.hasRootController());
|
||||
assertFalse(router.hasRootController());
|
||||
|
||||
mRouter.setRoot(rootController, rootTag);
|
||||
router.setRoot(RouterTransaction.with(rootController).tag(rootTag));
|
||||
|
||||
Assert.assertTrue(mRouter.hasRootController());
|
||||
assertTrue(router.hasRootController());
|
||||
|
||||
Assert.assertEquals(rootController, mRouter.getControllerWithTag(rootTag));
|
||||
assertEquals(rootController, router.getControllerWithTag(rootTag));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -46,21 +57,21 @@ public class RouterTests {
|
||||
Controller oldRootController = new TestController();
|
||||
Controller newRootController = new TestController();
|
||||
|
||||
mRouter.setRoot(oldRootController, oldRootTag);
|
||||
mRouter.setRoot(newRootController, newRootTag);
|
||||
router.setRoot(RouterTransaction.with(oldRootController).tag(oldRootTag));
|
||||
router.setRoot(RouterTransaction.with(newRootController).tag(newRootTag));
|
||||
|
||||
Assert.assertNull(mRouter.getControllerWithTag(oldRootTag));
|
||||
Assert.assertEquals(newRootController, mRouter.getControllerWithTag(newRootTag));
|
||||
assertNull(router.getControllerWithTag(oldRootTag));
|
||||
assertEquals(newRootController, router.getControllerWithTag(newRootTag));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetByInstanceId() {
|
||||
Controller controller = new TestController();
|
||||
|
||||
mRouter.pushController(RouterTransaction.builder(controller).build());
|
||||
router.pushController(RouterTransaction.with(controller));
|
||||
|
||||
Assert.assertEquals(controller, mRouter.getControllerWithInstanceId(controller.getInstanceId()));
|
||||
Assert.assertNull(mRouter.getControllerWithInstanceId("fake id"));
|
||||
assertEquals(controller, router.getControllerWithInstanceId(controller.getInstanceId()));
|
||||
assertNull(router.getControllerWithInstanceId("fake id"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -71,16 +82,14 @@ public class RouterTests {
|
||||
Controller controller1 = new TestController();
|
||||
Controller controller2 = new TestController();
|
||||
|
||||
mRouter.pushController(RouterTransaction.builder(controller1)
|
||||
.tag(controller1Tag)
|
||||
.build());
|
||||
router.pushController(RouterTransaction.with(controller1)
|
||||
.tag(controller1Tag));
|
||||
|
||||
mRouter.pushController(RouterTransaction.builder(controller2)
|
||||
.tag(controller2Tag)
|
||||
.build());
|
||||
router.pushController(RouterTransaction.with(controller2)
|
||||
.tag(controller2Tag));
|
||||
|
||||
Assert.assertEquals(controller1, mRouter.getControllerWithTag(controller1Tag));
|
||||
Assert.assertEquals(controller2, mRouter.getControllerWithTag(controller2Tag));
|
||||
assertEquals(controller1, router.getControllerWithTag(controller1Tag));
|
||||
assertEquals(controller2, router.getControllerWithTag(controller2Tag));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -91,31 +100,29 @@ public class RouterTests {
|
||||
Controller controller1 = new TestController();
|
||||
Controller controller2 = new TestController();
|
||||
|
||||
mRouter.pushController(RouterTransaction.builder(controller1)
|
||||
.tag(controller1Tag)
|
||||
.build());
|
||||
router.pushController(RouterTransaction.with(controller1)
|
||||
.tag(controller1Tag));
|
||||
|
||||
Assert.assertEquals(1, mRouter.getBackstackSize());
|
||||
assertEquals(1, router.getBackstackSize());
|
||||
|
||||
mRouter.pushController(RouterTransaction.builder(controller2)
|
||||
.tag(controller2Tag)
|
||||
.build());
|
||||
router.pushController(RouterTransaction.with(controller2)
|
||||
.tag(controller2Tag));
|
||||
|
||||
Assert.assertEquals(2, mRouter.getBackstackSize());
|
||||
assertEquals(2, router.getBackstackSize());
|
||||
|
||||
mRouter.popCurrentController();
|
||||
router.popCurrentController();
|
||||
|
||||
Assert.assertEquals(1, mRouter.getBackstackSize());
|
||||
assertEquals(1, router.getBackstackSize());
|
||||
|
||||
Assert.assertEquals(controller1, mRouter.getControllerWithTag(controller1Tag));
|
||||
Assert.assertNull(mRouter.getControllerWithTag(controller2Tag));
|
||||
assertEquals(controller1, router.getControllerWithTag(controller1Tag));
|
||||
assertNull(router.getControllerWithTag(controller2Tag));
|
||||
|
||||
mRouter.popCurrentController();
|
||||
router.popCurrentController();
|
||||
|
||||
Assert.assertEquals(0, mRouter.getBackstackSize());
|
||||
assertEquals(0, router.getBackstackSize());
|
||||
|
||||
Assert.assertNull(mRouter.getControllerWithTag(controller1Tag));
|
||||
Assert.assertNull(mRouter.getControllerWithTag(controller2Tag));
|
||||
assertNull(router.getControllerWithTag(controller1Tag));
|
||||
assertNull(router.getControllerWithTag(controller2Tag));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -130,29 +137,25 @@ public class RouterTests {
|
||||
Controller controller3 = new TestController();
|
||||
Controller controller4 = new TestController();
|
||||
|
||||
mRouter.pushController(RouterTransaction.builder(controller1)
|
||||
.tag(controller1Tag)
|
||||
.build());
|
||||
router.pushController(RouterTransaction.with(controller1)
|
||||
.tag(controller1Tag));
|
||||
|
||||
mRouter.pushController(RouterTransaction.builder(controller2)
|
||||
.tag(controller2Tag)
|
||||
.build());
|
||||
router.pushController(RouterTransaction.with(controller2)
|
||||
.tag(controller2Tag));
|
||||
|
||||
mRouter.pushController(RouterTransaction.builder(controller3)
|
||||
.tag(controller3Tag)
|
||||
.build());
|
||||
router.pushController(RouterTransaction.with(controller3)
|
||||
.tag(controller3Tag));
|
||||
|
||||
mRouter.pushController(RouterTransaction.builder(controller4)
|
||||
.tag(controller4Tag)
|
||||
.build());
|
||||
router.pushController(RouterTransaction.with(controller4)
|
||||
.tag(controller4Tag));
|
||||
|
||||
mRouter.popToTag(controller2Tag);
|
||||
router.popToTag(controller2Tag);
|
||||
|
||||
Assert.assertEquals(2, mRouter.getBackstackSize());
|
||||
Assert.assertEquals(controller1, mRouter.getControllerWithTag(controller1Tag));
|
||||
Assert.assertEquals(controller2, mRouter.getControllerWithTag(controller2Tag));
|
||||
Assert.assertNull(mRouter.getControllerWithTag(controller3Tag));
|
||||
Assert.assertNull(mRouter.getControllerWithTag(controller4Tag));
|
||||
assertEquals(2, router.getBackstackSize());
|
||||
assertEquals(controller1, router.getControllerWithTag(controller1Tag));
|
||||
assertEquals(controller2, router.getControllerWithTag(controller2Tag));
|
||||
assertNull(router.getControllerWithTag(controller3Tag));
|
||||
assertNull(router.getControllerWithTag(controller4Tag));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -165,24 +168,267 @@ public class RouterTests {
|
||||
Controller controller2 = new TestController();
|
||||
Controller controller3 = new TestController();
|
||||
|
||||
mRouter.pushController(RouterTransaction.builder(controller1)
|
||||
.tag(controller1Tag)
|
||||
.build());
|
||||
router.pushController(RouterTransaction.with(controller1)
|
||||
.tag(controller1Tag));
|
||||
|
||||
mRouter.pushController(RouterTransaction.builder(controller2)
|
||||
.tag(controller2Tag)
|
||||
.build());
|
||||
router.pushController(RouterTransaction.with(controller2)
|
||||
.tag(controller2Tag));
|
||||
|
||||
mRouter.pushController(RouterTransaction.builder(controller3)
|
||||
.tag(controller3Tag)
|
||||
.build());
|
||||
router.pushController(RouterTransaction.with(controller3)
|
||||
.tag(controller3Tag));
|
||||
|
||||
mRouter.popController(controller2);
|
||||
router.popController(controller2);
|
||||
|
||||
Assert.assertEquals(2, mRouter.getBackstackSize());
|
||||
Assert.assertEquals(controller1, mRouter.getControllerWithTag(controller1Tag));
|
||||
Assert.assertNull(mRouter.getControllerWithTag(controller2Tag));
|
||||
Assert.assertEquals(controller3, mRouter.getControllerWithTag(controller3Tag));
|
||||
assertEquals(2, router.getBackstackSize());
|
||||
assertEquals(controller1, router.getControllerWithTag(controller1Tag));
|
||||
assertNull(router.getControllerWithTag(controller2Tag));
|
||||
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 = Arrays.asList(rootTransaction, middleTransaction, topTransaction);
|
||||
router.setBackstack(backstack, null);
|
||||
|
||||
assertEquals(3, router.getBackstackSize());
|
||||
|
||||
List<RouterTransaction> fetchedBackstack = router.getBackstack();
|
||||
assertEquals(rootTransaction, fetchedBackstack.get(0));
|
||||
assertEquals(middleTransaction, fetchedBackstack.get(1));
|
||||
assertEquals(topTransaction, fetchedBackstack.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNewSetBackstack() {
|
||||
router.setRoot(RouterTransaction.with(new TestController()));
|
||||
|
||||
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 = Arrays.asList(rootTransaction, middleTransaction, topTransaction);
|
||||
router.setBackstack(backstack, null);
|
||||
|
||||
assertEquals(3, router.getBackstackSize());
|
||||
|
||||
List<RouterTransaction> fetchedBackstack = router.getBackstack();
|
||||
assertEquals(rootTransaction, fetchedBackstack.get(0));
|
||||
assertEquals(middleTransaction, fetchedBackstack.get(1));
|
||||
assertEquals(topTransaction, fetchedBackstack.get(2));
|
||||
|
||||
assertEquals(router, rootTransaction.controller.getRouter());
|
||||
assertEquals(router, middleTransaction.controller.getRouter());
|
||||
assertEquals(router, topTransaction.controller.getRouter());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNewSetBackstackWithNoRemoveViewOnPush() {
|
||||
RouterTransaction oldRootTransaction = RouterTransaction.with(new TestController());
|
||||
RouterTransaction oldTopTransaction = RouterTransaction.with(new TestController()).pushChangeHandler(MockChangeHandler.noRemoveViewOnPushHandler());
|
||||
|
||||
router.setRoot(oldRootTransaction);
|
||||
router.pushController(oldTopTransaction);
|
||||
assertEquals(2, router.getBackstackSize());
|
||||
|
||||
assertTrue(oldRootTransaction.controller.isAttached());
|
||||
assertTrue(oldTopTransaction.controller.isAttached());
|
||||
|
||||
RouterTransaction rootTransaction = RouterTransaction.with(new TestController());
|
||||
RouterTransaction middleTransaction = RouterTransaction.with(new TestController()).pushChangeHandler(MockChangeHandler.noRemoveViewOnPushHandler());
|
||||
RouterTransaction topTransaction = RouterTransaction.with(new TestController()).pushChangeHandler(MockChangeHandler.noRemoveViewOnPushHandler());
|
||||
|
||||
List<RouterTransaction> backstack = Arrays.asList(rootTransaction, middleTransaction, topTransaction);
|
||||
router.setBackstack(backstack, null);
|
||||
|
||||
assertEquals(3, router.getBackstackSize());
|
||||
|
||||
List<RouterTransaction> fetchedBackstack = router.getBackstack();
|
||||
assertEquals(rootTransaction, fetchedBackstack.get(0));
|
||||
assertEquals(middleTransaction, fetchedBackstack.get(1));
|
||||
assertEquals(topTransaction, fetchedBackstack.get(2));
|
||||
|
||||
assertFalse(oldRootTransaction.controller.isAttached());
|
||||
assertFalse(oldTopTransaction.controller.isAttached());
|
||||
assertTrue(rootTransaction.controller.isAttached());
|
||||
assertTrue(middleTransaction.controller.isAttached());
|
||||
assertTrue(topTransaction.controller.isAttached());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPopToRoot() {
|
||||
RouterTransaction rootTransaction = RouterTransaction.with(new TestController());
|
||||
RouterTransaction transaction1 = RouterTransaction.with(new TestController());
|
||||
RouterTransaction transaction2 = RouterTransaction.with(new TestController());
|
||||
|
||||
List<RouterTransaction> backstack = Arrays.asList(rootTransaction, transaction1, transaction2);
|
||||
router.setBackstack(backstack, null);
|
||||
|
||||
assertEquals(3, router.getBackstackSize());
|
||||
|
||||
router.popToRoot();
|
||||
|
||||
assertEquals(1, router.getBackstackSize());
|
||||
assertEquals(rootTransaction, router.getBackstack().get(0));
|
||||
|
||||
assertTrue(rootTransaction.controller.isAttached());
|
||||
assertFalse(transaction1.controller.isAttached());
|
||||
assertFalse(transaction2.controller.isAttached());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPopToRootWithNoRemoveViewOnPush() {
|
||||
RouterTransaction rootTransaction = RouterTransaction.with(new TestController()).pushChangeHandler(new HorizontalChangeHandler(false));
|
||||
RouterTransaction transaction1 = RouterTransaction.with(new TestController()).pushChangeHandler(new HorizontalChangeHandler(false));
|
||||
RouterTransaction transaction2 = RouterTransaction.with(new TestController()).pushChangeHandler(new HorizontalChangeHandler(false));
|
||||
|
||||
List<RouterTransaction> backstack = Arrays.asList(rootTransaction, transaction1, transaction2);
|
||||
router.setBackstack(backstack, null);
|
||||
|
||||
assertEquals(3, router.getBackstackSize());
|
||||
|
||||
router.popToRoot();
|
||||
|
||||
assertEquals(1, router.getBackstackSize());
|
||||
assertEquals(rootTransaction, router.getBackstack().get(0));
|
||||
|
||||
assertTrue(rootTransaction.controller.isAttached());
|
||||
assertFalse(transaction1.controller.isAttached());
|
||||
assertFalse(transaction2.controller.isAttached());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplaceTopController() {
|
||||
RouterTransaction rootTransaction = RouterTransaction.with(new TestController());
|
||||
RouterTransaction topTransaction = RouterTransaction.with(new TestController());
|
||||
|
||||
List<RouterTransaction> backstack = Arrays.asList(rootTransaction, topTransaction);
|
||||
router.setBackstack(backstack, null);
|
||||
|
||||
assertEquals(2, router.getBackstackSize());
|
||||
|
||||
List<RouterTransaction> fetchedBackstack = router.getBackstack();
|
||||
assertEquals(rootTransaction, fetchedBackstack.get(0));
|
||||
assertEquals(topTransaction, fetchedBackstack.get(1));
|
||||
|
||||
RouterTransaction newTopTransaction = RouterTransaction.with(new TestController());
|
||||
router.replaceTopController(newTopTransaction);
|
||||
|
||||
assertEquals(2, router.getBackstackSize());
|
||||
|
||||
fetchedBackstack = router.getBackstack();
|
||||
assertEquals(rootTransaction, fetchedBackstack.get(0));
|
||||
assertEquals(newTopTransaction, fetchedBackstack.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplaceTopControllerWithNoRemoveViewOnPush() {
|
||||
RouterTransaction rootTransaction = RouterTransaction.with(new TestController());
|
||||
RouterTransaction topTransaction = RouterTransaction.with(new TestController()).pushChangeHandler(MockChangeHandler.noRemoveViewOnPushHandler());
|
||||
|
||||
List<RouterTransaction> backstack = Arrays.asList(rootTransaction, topTransaction);
|
||||
router.setBackstack(backstack, null);
|
||||
|
||||
assertEquals(2, router.getBackstackSize());
|
||||
|
||||
assertTrue(rootTransaction.controller.isAttached());
|
||||
assertTrue(topTransaction.controller.isAttached());
|
||||
|
||||
List<RouterTransaction> fetchedBackstack = router.getBackstack();
|
||||
assertEquals(rootTransaction, fetchedBackstack.get(0));
|
||||
assertEquals(topTransaction, fetchedBackstack.get(1));
|
||||
|
||||
RouterTransaction newTopTransaction = RouterTransaction.with(new TestController()).pushChangeHandler(MockChangeHandler.noRemoveViewOnPushHandler());
|
||||
router.replaceTopController(newTopTransaction);
|
||||
newTopTransaction.pushChangeHandler().completeImmediately();
|
||||
|
||||
assertEquals(2, router.getBackstackSize());
|
||||
|
||||
fetchedBackstack = router.getBackstack();
|
||||
assertEquals(rootTransaction, fetchedBackstack.get(0));
|
||||
assertEquals(newTopTransaction, fetchedBackstack.get(1));
|
||||
|
||||
assertTrue(rootTransaction.controller.isAttached());
|
||||
assertFalse(topTransaction.controller.isAttached());
|
||||
assertTrue(newTopTransaction.controller.isAttached());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRearrangeTransactionBackstack() {
|
||||
RouterTransaction transaction1 = RouterTransaction.with(new TestController());
|
||||
RouterTransaction transaction2 = RouterTransaction.with(new TestController());
|
||||
|
||||
List<RouterTransaction> backstack = Arrays.asList(transaction1, transaction2);
|
||||
router.setBackstack(backstack, null);
|
||||
|
||||
assertEquals(1, transaction1.transactionIndex);
|
||||
assertEquals(2, transaction2.transactionIndex);
|
||||
|
||||
backstack = Arrays.asList(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 = Arrays.asList(transaction1, transaction2);
|
||||
childRouter.setBackstack(backstack, null);
|
||||
|
||||
assertEquals(2, transaction1.transactionIndex);
|
||||
assertEquals(3, transaction2.transactionIndex);
|
||||
|
||||
backstack = Arrays.asList(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());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemovesAllViewsOnDestroy() {
|
||||
Controller controller1 = new TestController();
|
||||
Controller controller2 = new TestController();
|
||||
|
||||
router.setRoot(RouterTransaction.with(controller1));
|
||||
router.pushController(RouterTransaction.with(controller2)
|
||||
.pushChangeHandler(new FadeChangeHandler(false)));
|
||||
|
||||
assertEquals(2, router.container.getChildCount());
|
||||
|
||||
router.destroy(true);
|
||||
|
||||
assertEquals(0, router.container.getChildCount());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.util.ActivityProxy;
|
||||
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 static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(manifest = Config.NONE)
|
||||
public class TargetControllerTests {
|
||||
|
||||
private Router router;
|
||||
|
||||
public void createActivityController(Bundle 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()));
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
createActivityController(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSiblingTarget() {
|
||||
final TestController controllerA = new TestController();
|
||||
final TestController controllerB = new TestController();
|
||||
|
||||
assertNull(controllerA.getTargetController());
|
||||
assertNull(controllerB.getTargetController());
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerA)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
controllerB.setTargetController(controllerA);
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerB)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
assertNull(controllerA.getTargetController());
|
||||
assertEquals(controllerA, controllerB.getTargetController());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParentChildTarget() {
|
||||
final TestController controllerA = new TestController();
|
||||
final TestController controllerB = new TestController();
|
||||
|
||||
assertNull(controllerA.getTargetController());
|
||||
assertNull(controllerB.getTargetController());
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerA)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
controllerB.setTargetController(controllerA);
|
||||
|
||||
Router childRouter = controllerA.getChildRouter((ViewGroup)controllerA.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouter.pushController(RouterTransaction.with(controllerB)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
assertNull(controllerA.getTargetController());
|
||||
assertEquals(controllerA, controllerB.getTargetController());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildParentTarget() {
|
||||
final TestController controllerA = new TestController();
|
||||
final TestController controllerB = new TestController();
|
||||
|
||||
assertNull(controllerA.getTargetController());
|
||||
assertNull(controllerB.getTargetController());
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerA)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
controllerA.setTargetController(controllerB);
|
||||
|
||||
Router childRouter = controllerA.getChildRouter((ViewGroup)controllerA.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouter.pushController(RouterTransaction.with(controllerB)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
assertNull(controllerB.getTargetController());
|
||||
assertEquals(controllerB, controllerA.getTargetController());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.bluelinelabs.conductor.internal.ViewAttachHandler;
|
||||
import com.bluelinelabs.conductor.internal.ViewAttachHandler.ViewAttachListener;
|
||||
import com.bluelinelabs.conductor.util.ActivityProxy;
|
||||
import com.bluelinelabs.conductor.util.ViewUtils;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(manifest = Config.NONE)
|
||||
public class ViewAttachHandlerTests {
|
||||
|
||||
private Activity activity;
|
||||
private ViewAttachHandler viewAttachHandler;
|
||||
private CountingViewAttachListener viewAttachListener;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
activity = new ActivityProxy().create(null).getActivity();
|
||||
viewAttachListener = new CountingViewAttachListener();
|
||||
viewAttachHandler = new ViewAttachHandler(viewAttachListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleViewAttachDetach() {
|
||||
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
|
||||
public void testSimpleViewGroupAttachDetach() {
|
||||
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
|
||||
public void testNestedViewGroupAttachDetach() {
|
||||
ViewGroup view = new LinearLayout(activity);
|
||||
View child = new LinearLayout(activity);
|
||||
view.addView(child);
|
||||
viewAttachHandler.listenForAttach(view);
|
||||
|
||||
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() {
|
||||
attaches++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetached(boolean fromActivityStop) {
|
||||
detaches++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewDetachAfterStop() {
|
||||
detachAfterStops++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.util.ActivityProxy;
|
||||
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 static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(manifest = Config.NONE)
|
||||
public class ViewLeakTests {
|
||||
|
||||
private ActivityProxy activityProxy;
|
||||
private Router router;
|
||||
|
||||
public void createActivityController(Bundle 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()));
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
createActivityController(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPop() {
|
||||
Controller controller = new TestController();
|
||||
router.pushController(RouterTransaction.with(controller));
|
||||
|
||||
assertNotNull(controller.getView());
|
||||
|
||||
router.popCurrentController();
|
||||
|
||||
assertNull(controller.getView());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPopWhenPushNeverAdded() {
|
||||
Controller controller = new TestController();
|
||||
router.pushController(RouterTransaction.with(controller).pushChangeHandler(new NeverAddChangeHandler()));
|
||||
|
||||
assertNotNull(controller.getView());
|
||||
|
||||
router.popCurrentController();
|
||||
|
||||
assertNull(controller.getView());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPopWhenPushNeverCompleted() {
|
||||
Controller controller = new TestController();
|
||||
router.pushController(RouterTransaction.with(controller).pushChangeHandler(new NeverCompleteChangeHandler()));
|
||||
|
||||
assertNotNull(controller.getView());
|
||||
|
||||
router.popCurrentController();
|
||||
|
||||
assertNull(controller.getView());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActivityStop() {
|
||||
Controller controller = new TestController();
|
||||
router.pushController(RouterTransaction.with(controller));
|
||||
|
||||
assertNotNull(controller.getView());
|
||||
|
||||
activityProxy.stop(true);
|
||||
|
||||
assertNull(controller.getView());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActivityStopWhenPushNeverCompleted() {
|
||||
Controller controller = new TestController();
|
||||
router.pushController(RouterTransaction.with(controller).pushChangeHandler(new NeverCompleteChangeHandler()));
|
||||
|
||||
assertNotNull(controller.getView());
|
||||
|
||||
activityProxy.stop(true);
|
||||
|
||||
assertNull(controller.getView());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActivityDestroyWhenPushNeverAdded() {
|
||||
Controller controller = new TestController();
|
||||
router.pushController(RouterTransaction.with(controller).pushChangeHandler(new NeverAddChangeHandler()));
|
||||
|
||||
assertNotNull(controller.getView());
|
||||
|
||||
activityProxy.stop(true).destroy();
|
||||
|
||||
assertNull(controller.getView());
|
||||
}
|
||||
|
||||
public static class NeverAddChangeHandler extends ControllerChangeHandler {
|
||||
@Override
|
||||
public void performChange(@NonNull final ViewGroup container, @Nullable View from, @Nullable final View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
if (from != null) {
|
||||
container.removeView(from);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class NeverCompleteChangeHandler extends ControllerChangeHandler {
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, @Nullable View from, @Nullable View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
if (from != null) {
|
||||
container.removeView(from);
|
||||
}
|
||||
container.addView(to);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.View.OnAttachStateChangeListener;
|
||||
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ViewUtils {
|
||||
|
||||
static void setAttached(View view, boolean attached) {
|
||||
Object listenerInfo = ReflectionHelpers.callInstanceMethod(view, "getListenerInfo");
|
||||
List<OnAttachStateChangeListener> listeners = ReflectionHelpers.getField(listenerInfo, "mOnAttachStateChangeListeners");
|
||||
|
||||
for (OnAttachStateChangeListener listener : listeners) {
|
||||
if (attached) {
|
||||
listener.onViewAttachedToWindow(view);
|
||||
} else {
|
||||
listener.onViewDetachedFromWindow(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.bluelinelabs.conductor.util;
|
||||
|
||||
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(boolean detachView) {
|
||||
activityController.stop();
|
||||
|
||||
if (detachView) {
|
||||
view.setAttached(false);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public ActivityProxy destroy() {
|
||||
activityController.destroy();
|
||||
view.setAttached(false);
|
||||
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.util;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
+25
-12
@@ -1,4 +1,4 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
package com.bluelinelabs.conductor.util;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
@@ -19,10 +19,8 @@ public class CallState implements Parcelable {
|
||||
public int onActivityResultCalls;
|
||||
public int onRequestPermissionsResultCalls;
|
||||
public int createOptionsMenuCalls;
|
||||
|
||||
public CallState() {
|
||||
this(false);
|
||||
}
|
||||
public int contextAvailableCalls;
|
||||
public int contextUnavailableCalls;
|
||||
|
||||
public CallState(boolean setupForAddedController) {
|
||||
if (setupForAddedController) {
|
||||
@@ -30,6 +28,7 @@ public class CallState implements Parcelable {
|
||||
changeEndCalls++;
|
||||
createViewCalls++;
|
||||
attachCalls++;
|
||||
contextAvailableCalls++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,8 +41,14 @@ public class CallState implements Parcelable {
|
||||
return false;
|
||||
}
|
||||
|
||||
CallState callState = (CallState)o;
|
||||
CallState callState = (CallState) o;
|
||||
|
||||
if (contextAvailableCalls != callState.contextAvailableCalls) {
|
||||
return false;
|
||||
}
|
||||
if (contextUnavailableCalls != callState.contextUnavailableCalls) {
|
||||
return false;
|
||||
}
|
||||
if (changeStartCalls != callState.changeStartCalls) {
|
||||
return false;
|
||||
}
|
||||
@@ -102,6 +107,8 @@ public class CallState implements Parcelable {
|
||||
result = 31 * result + onActivityResultCalls;
|
||||
result = 31 * result + onRequestPermissionsResultCalls;
|
||||
result = 31 * result + createOptionsMenuCalls;
|
||||
result = 31 * result + contextAvailableCalls;
|
||||
result = 31 * result + contextUnavailableCalls;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -119,9 +126,11 @@ 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 contextAvailableCalls=" + contextAvailableCalls +
|
||||
"\n contextUnavailableCalls=" + contextUnavailableCalls +
|
||||
"}\n";
|
||||
}
|
||||
|
||||
@@ -144,11 +153,13 @@ public class CallState implements Parcelable {
|
||||
out.writeInt(onActivityResultCalls);
|
||||
out.writeInt(onRequestPermissionsResultCalls);
|
||||
out.writeInt(createOptionsMenuCalls);
|
||||
out.writeInt(contextAvailableCalls);
|
||||
out.writeInt(contextUnavailableCalls);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<CallState> CREATOR = new Parcelable.Creator<CallState>() {
|
||||
public static final Parcelable.Creator<CallState> CREATOR = new Parcelable.Creator<CallState>() {
|
||||
public CallState createFromParcel(Parcel in) {
|
||||
CallState state = new CallState();
|
||||
CallState state = new CallState(false);
|
||||
|
||||
state.changeStartCalls = in.readInt();
|
||||
state.changeEndCalls = in.readInt();
|
||||
@@ -164,6 +175,8 @@ public class CallState implements Parcelable {
|
||||
state.onActivityResultCalls = in.readInt();
|
||||
state.onRequestPermissionsResultCalls = in.readInt();
|
||||
state.createOptionsMenuCalls = in.readInt();
|
||||
state.contextAvailableCalls = in.readInt();
|
||||
state.contextUnavailableCalls = in.readInt();
|
||||
|
||||
return state;
|
||||
}
|
||||
@@ -172,4 +185,4 @@ public class CallState implements Parcelable {
|
||||
return new CallState[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.bluelinelabs.conductor.util;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ChangeHandlerHistory {
|
||||
|
||||
private List<Entry> entries = new ArrayList<>();
|
||||
public boolean isValidHistory = true;
|
||||
|
||||
public void addEntry(View from, View to, boolean isPush, MockChangeHandler handler) {
|
||||
entries.add(new Entry(from, to, isPush, handler));
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return entries.size();
|
||||
}
|
||||
|
||||
public View fromViewAt(int index) {
|
||||
return entries.get(index).from;
|
||||
}
|
||||
|
||||
public View toViewAt(int index) {
|
||||
return entries.get(index).to;
|
||||
}
|
||||
|
||||
public boolean isPushAt(int index) {
|
||||
return entries.get(index).isPush;
|
||||
}
|
||||
|
||||
public MockChangeHandler changeHandlerAt(int index) {
|
||||
return entries.get(index).changeHandler;
|
||||
}
|
||||
|
||||
public View latestFromView() {
|
||||
return fromViewAt(size() - 1);
|
||||
}
|
||||
|
||||
public View latestToView() {
|
||||
return toViewAt(size() - 1);
|
||||
}
|
||||
|
||||
public boolean latestIsPush() {
|
||||
return isPushAt(size() - 1);
|
||||
}
|
||||
|
||||
public MockChangeHandler latestChangeHandler() {
|
||||
return changeHandlerAt(size() - 1);
|
||||
}
|
||||
|
||||
private static class Entry {
|
||||
final View from;
|
||||
final View to;
|
||||
final boolean isPush;
|
||||
final MockChangeHandler changeHandler;
|
||||
|
||||
Entry(View from, View to, boolean isPush, MockChangeHandler changeHandler) {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.isPush = isPush;
|
||||
this.changeHandler = changeHandler;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.bluelinelabs.conductor.util;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
|
||||
public class MockChangeHandler extends ControllerChangeHandler {
|
||||
|
||||
private static final String KEY_REMOVES_FROM_VIEW_ON_PUSH = "MockChangeHandler.removesFromViewOnPush";
|
||||
private static final String KEY_TAG = "MockChangeHandler.tag";
|
||||
|
||||
public static class ChangeHandlerListener {
|
||||
public void willStartChange() { }
|
||||
public void didAttachOrDetach() { }
|
||||
public void didEndChange() { }
|
||||
}
|
||||
|
||||
private final ChangeHandlerListener listener;
|
||||
private boolean removesFromViewOnPush;
|
||||
|
||||
public View from;
|
||||
public View to;
|
||||
public String tag;
|
||||
|
||||
public static MockChangeHandler defaultHandler() {
|
||||
return new MockChangeHandler(true, null, null);
|
||||
}
|
||||
|
||||
public static MockChangeHandler noRemoveViewOnPushHandler() {
|
||||
return new MockChangeHandler(false, null, null);
|
||||
}
|
||||
|
||||
public static MockChangeHandler listeningChangeHandler(@NonNull ChangeHandlerListener listener) {
|
||||
return new MockChangeHandler(true, null, listener);
|
||||
}
|
||||
|
||||
public static MockChangeHandler taggedHandler(String tag, boolean removeViewOnPush) {
|
||||
return new MockChangeHandler(removeViewOnPush, tag, null);
|
||||
}
|
||||
|
||||
public MockChangeHandler() {
|
||||
listener = null;
|
||||
}
|
||||
|
||||
private MockChangeHandler(boolean removesFromViewOnPush, String tag, ChangeHandlerListener listener) {
|
||||
this.removesFromViewOnPush = removesFromViewOnPush;
|
||||
|
||||
if (listener == null) {
|
||||
this.listener = new ChangeHandlerListener() { };
|
||||
} else {
|
||||
this.listener = listener;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, @Nullable View from, @Nullable View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
|
||||
listener.willStartChange();
|
||||
|
||||
if (isPush) {
|
||||
if (to != null) {
|
||||
container.addView(to);
|
||||
listener.didAttachOrDetach();
|
||||
}
|
||||
|
||||
if (removesFromViewOnPush && from != null) {
|
||||
container.removeView(from);
|
||||
}
|
||||
} else {
|
||||
container.removeView(from);
|
||||
listener.didAttachOrDetach();
|
||||
|
||||
if (to != null) {
|
||||
container.addView(to);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
changeListener.onChangeCompleted();
|
||||
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);
|
||||
bundle.putString(KEY_TAG, tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreFromBundle(@NonNull Bundle bundle) {
|
||||
super.restoreFromBundle(bundle);
|
||||
removesFromViewOnPush = bundle.getBoolean(KEY_REMOVES_FROM_VIEW_ON_PUSH);
|
||||
tag = bundle.getString(KEY_TAG);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ControllerChangeHandler copy() {
|
||||
return new MockChangeHandler(removesFromViewOnPush, tag, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReusable() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user