Compare commits
130 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 |
Executable → Regular
+4
-2
@@ -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
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
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,25 +20,28 @@ Conductor is architecture-agnostic and does not try to force any design decision
|
||||
## Installation
|
||||
|
||||
```gradle
|
||||
compile 'com.bluelinelabs:conductor:2.0.2'
|
||||
compile 'com.bluelinelabs:conductor:2.1.4'
|
||||
|
||||
// If you want the components that go along with
|
||||
// Android's support libraries (currently just a PagerAdapter):
|
||||
compile 'com.bluelinelabs:conductor-support:2.0.2'
|
||||
compile 'com.bluelinelabs:conductor-support:2.1.4'
|
||||
|
||||
// If you want RxJava/RxAndroid lifecycle support:
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:2.0.2'
|
||||
// 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:
|
||||
|
||||
```gradle
|
||||
compile 'com.bluelinelabs:conductor:2.0.3-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor-support:2.0.3-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:2.0.3-SNAPSHOT'
|
||||
```
|
||||
|
||||
You also have to add the url to the snapshot repository:
|
||||
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
|
||||
allprojects {
|
||||
@@ -51,7 +54,7 @@ allprojects {
|
||||
|
||||
## 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.
|
||||
@@ -114,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
|
||||
|
||||
|
||||
Executable → Regular
+3
-6
@@ -3,19 +3,16 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.2.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 @@
|
||||
<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);
|
||||
+6
-4
@@ -5,6 +5,8 @@ import android.support.annotation.CheckResult;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
import com.trello.rxlifecycle.LifecycleProvider;
|
||||
import com.trello.rxlifecycle.LifecycleTransformer;
|
||||
import com.trello.rxlifecycle.RxLifecycle;
|
||||
|
||||
import rx.Observable;
|
||||
@@ -13,7 +15,7 @@ import rx.subjects.BehaviorSubject;
|
||||
/**
|
||||
* A base {@link Controller} that can be used to expose lifecycle events using RxJava
|
||||
*/
|
||||
public abstract class RxController extends Controller implements ControllerLifecycleProvider {
|
||||
public abstract class RxController extends Controller implements LifecycleProvider<ControllerEvent> {
|
||||
|
||||
private final BehaviorSubject<ControllerEvent> lifecycleSubject;
|
||||
|
||||
@@ -36,15 +38,15 @@ public abstract class RxController extends Controller implements ControllerLifec
|
||||
@Override
|
||||
@NonNull
|
||||
@CheckResult
|
||||
public final <T> Observable.Transformer<T, T> bindUntilEvent(@NonNull ControllerEvent event) {
|
||||
public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull ControllerEvent event) {
|
||||
return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
@CheckResult
|
||||
public final <T> Observable.Transformer<T, T> bindToLifecycle() {
|
||||
public final <T> LifecycleTransformer<T> bindToLifecycle() {
|
||||
return RxControllerLifecycle.bindController(lifecycleSubject);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+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
-1
@@ -1,3 +1,3 @@
|
||||
<manifest package="com.bluelinelabs.conductor.support">
|
||||
<application />
|
||||
</manifest>
|
||||
</manifest>
|
||||
+53
-4
@@ -2,6 +2,8 @@ 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;
|
||||
@@ -12,21 +14,28 @@ 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(Controller host, boolean saveControllerState) {
|
||||
public ControllerPagerAdapter(@NonNull Controller host, boolean saveControllerState) {
|
||||
this.host = host;
|
||||
savesState = saveControllerState;
|
||||
}
|
||||
@@ -34,6 +43,7 @@ public abstract class ControllerPagerAdapter extends PagerAdapter {
|
||||
/**
|
||||
* Return the Controller associated with a specified position.
|
||||
*/
|
||||
@NonNull
|
||||
public abstract Controller getItem(int position);
|
||||
|
||||
@Override
|
||||
@@ -49,11 +59,17 @@ public abstract class ControllerPagerAdapter extends PagerAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
final Controller controller;
|
||||
if (!router.hasRootController()) {
|
||||
router.setRoot(RouterTransaction.with(getItem(position))
|
||||
.tag(name));
|
||||
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);
|
||||
@@ -69,6 +85,8 @@ public abstract class ControllerPagerAdapter extends PagerAdapter {
|
||||
savedPages.put(position, savedState);
|
||||
}
|
||||
|
||||
visiblePageIds.remove(position);
|
||||
|
||||
host.removeChildRouter(router);
|
||||
}
|
||||
|
||||
@@ -82,6 +100,16 @@ public abstract class ControllerPagerAdapter extends PagerAdapter {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -91,6 +119,27 @@ public abstract class ControllerPagerAdapter extends PagerAdapter {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,4 +151,4 @@ public abstract class ControllerPagerAdapter extends PagerAdapter {
|
||||
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();
|
||||
|
||||
}
|
||||
Executable → Regular
+3
-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
|
||||
@@ -43,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"
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
-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>
|
||||
|
||||
@@ -2,18 +2,23 @@ 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) {
|
||||
@@ -27,16 +32,32 @@ public class ActivityHostedRouter extends Router {
|
||||
|
||||
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(Activity activity) {
|
||||
public void onActivityDestroyed(@NonNull Activity activity) {
|
||||
super.onActivityDestroyed(activity);
|
||||
lifecycleHandler = null;
|
||||
}
|
||||
@@ -49,37 +70,43 @@ public class ActivityHostedRouter extends Router {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
||||
lifecycleHandler.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
void startActivity(Intent intent) {
|
||||
void startActivity(@NonNull Intent intent) {
|
||||
lifecycleHandler.startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
void startActivityForResult(String instanceId, Intent intent, int requestCode) {
|
||||
void startActivityForResult(@NonNull String instanceId, @NonNull Intent intent, int requestCode) {
|
||||
lifecycleHandler.startActivityForResult(instanceId, intent, requestCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
void startActivityForResult(String instanceId, Intent intent, int requestCode, Bundle options) {
|
||||
void startActivityForResult(@NonNull String instanceId, @NonNull Intent intent, int requestCode, @Nullable Bundle options) {
|
||||
lifecycleHandler.startActivityForResult(instanceId, intent, requestCode, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
void registerForActivityResult(String instanceId, int requestCode) {
|
||||
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(String instanceId) {
|
||||
void unregisterForActivityResults(@NonNull String instanceId) {
|
||||
lifecycleHandler.unregisterForActivityResults(instanceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
void requestPermissions(String instanceId, @NonNull String[] permissions, int requestCode) {
|
||||
void requestPermissions(@NonNull String instanceId, @NonNull String[] permissions, int requestCode) {
|
||||
lifecycleHandler.requestPermissions(instanceId, permissions, requestCode);
|
||||
}
|
||||
|
||||
@@ -88,13 +115,24 @@ public class ActivityHostedRouter extends Router {
|
||||
return lifecycleHandler != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override @NonNull
|
||||
List<Router> getSiblingRouters() {
|
||||
return lifecycleHandler.getRouters();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override @NonNull
|
||||
Router getRootRouter() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override @Nullable
|
||||
TransactionIndexer getTransactionIndexer() {
|
||||
return transactionIndexer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContextAvailable() {
|
||||
super.onContextAvailable();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Executable → Regular
+44
-32
@@ -1,6 +1,8 @@
|
||||
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;
|
||||
@@ -13,34 +15,37 @@ class Backstack implements Iterable<RouterTransaction> {
|
||||
|
||||
private static final String KEY_ENTRIES = "Backstack.entries";
|
||||
|
||||
private final Deque<RouterTransaction> backStack = new ArrayDeque<>();
|
||||
private final Deque<RouterTransaction> backstack = new ArrayDeque<>();
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
public boolean isEmpty() {
|
||||
return backStack.isEmpty();
|
||||
boolean isEmpty() {
|
||||
return backstack.isEmpty();
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return backStack.size();
|
||||
int size() {
|
||||
return backstack.size();
|
||||
}
|
||||
|
||||
public RouterTransaction root() {
|
||||
return backStack.size() > 0 ? backStack.getLast() : null;
|
||||
@Nullable
|
||||
RouterTransaction root() {
|
||||
return backstack.size() > 0 ? backstack.getLast() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override @NonNull
|
||||
public Iterator<RouterTransaction> iterator() {
|
||||
return backStack.iterator();
|
||||
return backstack.iterator();
|
||||
}
|
||||
|
||||
public Iterator<RouterTransaction> reverseIterator() {
|
||||
return backStack.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 (backStack.contains(transaction)) {
|
||||
while (backStack.peek() != transaction) {
|
||||
if (backstack.contains(transaction)) {
|
||||
while (backstack.peek() != transaction) {
|
||||
RouterTransaction poppedTransaction = pop();
|
||||
popped.add(poppedTransaction);
|
||||
}
|
||||
@@ -50,25 +55,28 @@ class Backstack implements Iterable<RouterTransaction> {
|
||||
return popped;
|
||||
}
|
||||
|
||||
public RouterTransaction pop() {
|
||||
RouterTransaction popped = backStack.pop();
|
||||
@NonNull
|
||||
RouterTransaction pop() {
|
||||
RouterTransaction popped = backstack.pop();
|
||||
popped.controller.destroy();
|
||||
return popped;
|
||||
}
|
||||
|
||||
public RouterTransaction peek() {
|
||||
return backStack.peek();
|
||||
@Nullable
|
||||
RouterTransaction peek() {
|
||||
return backstack.peek();
|
||||
}
|
||||
|
||||
public void remove(RouterTransaction transaction) {
|
||||
backStack.removeFirstOccurrence(transaction);
|
||||
void remove(@NonNull RouterTransaction transaction) {
|
||||
backstack.removeFirstOccurrence(transaction);
|
||||
}
|
||||
|
||||
public void push(RouterTransaction transaction) {
|
||||
backStack.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());
|
||||
@@ -76,8 +84,8 @@ class Backstack implements Iterable<RouterTransaction> {
|
||||
return list;
|
||||
}
|
||||
|
||||
public void setBackstack(List<RouterTransaction> backstack) {
|
||||
for (RouterTransaction existingTransaction : backStack) {
|
||||
void setBackstack(@NonNull List<RouterTransaction> backstack) {
|
||||
for (RouterTransaction existingTransaction : this.backstack) {
|
||||
boolean contains = false;
|
||||
for (RouterTransaction newTransaction : backstack) {
|
||||
if (existingTransaction.controller == newTransaction.controller) {
|
||||
@@ -91,27 +99,31 @@ class Backstack implements Iterable<RouterTransaction> {
|
||||
}
|
||||
}
|
||||
|
||||
backStack.clear();
|
||||
this.backstack.clear();
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
backStack.push(transaction);
|
||||
this.backstack.push(transaction);
|
||||
}
|
||||
}
|
||||
|
||||
public void saveInstanceState(Bundle outState) {
|
||||
ArrayList<Bundle> entryBundles = new ArrayList<>(backStack.size());
|
||||
for (RouterTransaction entry : backStack) {
|
||||
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) {
|
||||
backStack.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;
|
||||
@@ -42,12 +44,12 @@ public class ChangeHandlerFrameLayout extends FrameLayout implements ControllerC
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChangeStarted(Controller to, Controller from, boolean isPush, ViewGroup container, ControllerChangeHandler handler) {
|
||||
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) {
|
||||
public void onChangeCompleted(@Nullable Controller to, @Nullable Controller from, boolean isPush, @NonNull ViewGroup container, @NonNull ControllerChangeHandler handler) {
|
||||
inProgressTransactionCount--;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
+360
-152
File diff suppressed because it is too large
Load Diff
@@ -2,13 +2,14 @@ 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.changehandler.SimpleSwapChangeHandler;
|
||||
import com.bluelinelabs.conductor.internal.ClassUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -23,18 +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, ControllerChangeHandler> inProgressPushHandlers = new HashMap<>();
|
||||
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 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();
|
||||
@@ -58,10 +62,11 @@ public abstract class ControllerChangeHandler {
|
||||
* Will be called on change handlers that push a controller if the controller being pushed is
|
||||
* popped before it has completed.
|
||||
*
|
||||
* @param newHandler the change handler that has caused this push to be aborted
|
||||
* @param newTop the controller that will now be at the top of the backstack
|
||||
* @param newHandler The change handler that has caused this push to be aborted
|
||||
* @param newTop The Controller that will now be at the top of the backstack or {@code null}
|
||||
* if there will be no new Controller at the top
|
||||
*/
|
||||
public void onAbortPush(@NonNull ControllerChangeHandler newHandler, Controller newTop) { }
|
||||
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
|
||||
@@ -69,6 +74,27 @@ public abstract class ControllerChangeHandler {
|
||||
*/
|
||||
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());
|
||||
@@ -88,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);
|
||||
@@ -100,37 +127,59 @@ public abstract class ControllerChangeHandler {
|
||||
}
|
||||
}
|
||||
|
||||
public static void completePushImmediately(String controllerInstanceId) {
|
||||
ControllerChangeHandler changeHandler = inProgressPushHandlers.get(controllerInstanceId);
|
||||
if (changeHandler != null) {
|
||||
changeHandler.completeImmediately();
|
||||
static boolean completeHandlerImmediately(@NonNull String controllerInstanceId) {
|
||||
ChangeHandlerData changeHandlerData = inProgressChangeHandlers.get(controllerInstanceId);
|
||||
if (changeHandlerData != null) {
|
||||
changeHandlerData.changeHandler.completeImmediately();
|
||||
inProgressChangeHandlers.remove(controllerInstanceId);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
public static void abortPush(Controller toAbort, Controller newController, ControllerChangeHandler newChangeHandler) {
|
||||
ControllerChangeHandler handlerForPush = inProgressPushHandlers.get(toAbort.getInstanceId());
|
||||
if (handlerForPush != null) {
|
||||
handlerForPush.onAbortPush(newChangeHandler, newController);
|
||||
inProgressPushHandlers.remove(toAbort.getInstanceId());
|
||||
}
|
||||
static void executeChange(@NonNull final ChangeTransaction transaction) {
|
||||
executeChange(transaction.to, transaction.from, transaction.isPush, transaction.container, transaction.changeHandler, transaction.listeners);
|
||||
}
|
||||
|
||||
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>());
|
||||
}
|
||||
|
||||
public static void executeChange(final Controller to, final Controller from, final boolean isPush, final ViewGroup container, final ControllerChangeHandler inHandler, @NonNull final List<ControllerChangeListener> 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 = inHandler != null ? inHandler : new SimpleSwapChangeHandler();
|
||||
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 (isPush && to != null) {
|
||||
inProgressPushHandlers.put(to.getInstanceId(), handler);
|
||||
} else if (!isPush && from != null) {
|
||||
abortPush(from, to, handler);
|
||||
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;
|
||||
@@ -160,12 +209,23 @@ public abstract class ControllerChangeHandler {
|
||||
}
|
||||
|
||||
if (to != null) {
|
||||
inProgressPushHandlers.remove(to.getInstanceId());
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -176,6 +236,28 @@ public abstract class ControllerChangeHandler {
|
||||
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.
|
||||
*/
|
||||
@@ -183,24 +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 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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -214,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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,12 +2,16 @@ 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;
|
||||
@@ -21,10 +25,11 @@ class ControllerHostedRouter extends Router {
|
||||
|
||||
@IdRes private int hostId;
|
||||
private String tag;
|
||||
private boolean isDetachFrozen;
|
||||
|
||||
ControllerHostedRouter() { }
|
||||
|
||||
ControllerHostedRouter(int hostId, String tag) {
|
||||
ControllerHostedRouter(int hostId, @Nullable String tag) {
|
||||
this.hostId = hostId;
|
||||
this.tag = tag;
|
||||
}
|
||||
@@ -39,6 +44,12 @@ class ControllerHostedRouter extends Router {
|
||||
|
||||
hostController = controller;
|
||||
this.container = container;
|
||||
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
transaction.controller.setParentController(controller);
|
||||
}
|
||||
|
||||
watchContainerAttach();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,14 +58,15 @@ class ControllerHostedRouter extends Router {
|
||||
removeChangeListener((ControllerChangeListener)container);
|
||||
}
|
||||
|
||||
for (Controller controller : destroyingControllers) {
|
||||
final List<Controller> controllersToDestroy = new ArrayList<>(destroyingControllers);
|
||||
for (Controller controller : controllersToDestroy) {
|
||||
if (controller.getView() != null) {
|
||||
controller.detach(controller.getView(), true);
|
||||
controller.detach(controller.getView(), true, false);
|
||||
}
|
||||
}
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
if (transaction.controller.getView() != null) {
|
||||
transaction.controller.detach(transaction.controller.getView(), true);
|
||||
transaction.controller.detach(transaction.controller.getView(), true, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,31 +76,50 @@ class ControllerHostedRouter extends Router {
|
||||
}
|
||||
|
||||
final void setDetachFrozen(boolean frozen) {
|
||||
isDetachFrozen = frozen;
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
transaction.controller.setDetachFrozen(frozen);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void destroy() {
|
||||
void destroy(boolean popViews) {
|
||||
setDetachFrozen(false);
|
||||
super.destroy();
|
||||
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(Activity activity) {
|
||||
public void onActivityDestroyed(@NonNull Activity activity) {
|
||||
super.onActivityDestroyed(activity);
|
||||
|
||||
removeHost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
||||
if (hostController != null && hostController.getRouter() != null) {
|
||||
hostController.getRouter().onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
@@ -102,42 +133,49 @@ class ControllerHostedRouter extends Router {
|
||||
}
|
||||
|
||||
@Override
|
||||
void startActivity(Intent intent) {
|
||||
void startActivity(@NonNull Intent intent) {
|
||||
if (hostController != null && hostController.getRouter() != null) {
|
||||
hostController.getRouter().startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void startActivityForResult(String instanceId, Intent intent, int requestCode) {
|
||||
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(String instanceId, Intent intent, int requestCode, Bundle options) {
|
||||
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 registerForActivityResult(String instanceId, int requestCode) {
|
||||
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(String instanceId) {
|
||||
void unregisterForActivityResults(@NonNull String instanceId) {
|
||||
if (hostController != null && hostController.getRouter() != null) {
|
||||
hostController.getRouter().unregisterForActivityResults(instanceId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void requestPermissions(String instanceId, String[] permissions, int requestCode) {
|
||||
void requestPermissions(@NonNull String instanceId, @NonNull String[] permissions, int requestCode) {
|
||||
if (hostController != null && hostController.getRouter() != null) {
|
||||
hostController.getRouter().requestPermissions(instanceId, permissions, requestCode);
|
||||
}
|
||||
@@ -149,7 +187,7 @@ class ControllerHostedRouter extends Router {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveInstanceState(Bundle outState) {
|
||||
public void saveInstanceState(@NonNull Bundle outState) {
|
||||
super.saveInstanceState(outState);
|
||||
|
||||
outState.putInt(KEY_HOST_ID, hostId);
|
||||
@@ -157,7 +195,7 @@ class ControllerHostedRouter extends Router {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreInstanceState(Bundle savedInstanceState) {
|
||||
public void restoreInstanceState(@NonNull Bundle savedInstanceState) {
|
||||
super.restoreInstanceState(savedInstanceState);
|
||||
|
||||
hostId = savedInstanceState.getInt(KEY_HOST_ID);
|
||||
@@ -165,7 +203,7 @@ class ControllerHostedRouter extends Router {
|
||||
}
|
||||
|
||||
@Override
|
||||
void setControllerRouter(Controller controller) {
|
||||
void setControllerRouter(@NonNull Controller controller) {
|
||||
super.setControllerRouter(controller);
|
||||
controller.setParentController(hostController);
|
||||
}
|
||||
@@ -174,11 +212,12 @@ class ControllerHostedRouter extends Router {
|
||||
return hostId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
String getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override @NonNull
|
||||
List<Router> getSiblingRouters() {
|
||||
List<Router> list = new ArrayList<>();
|
||||
list.addAll(hostController.getChildRouters());
|
||||
@@ -186,7 +225,7 @@ class ControllerHostedRouter extends Router {
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override @NonNull
|
||||
Router getRootRouter() {
|
||||
if (hostController != null && hostController.getRouter() != null) {
|
||||
return hostController.getRouter().getRootRouter();
|
||||
@@ -194,4 +233,9 @@ class ControllerHostedRouter extends Router {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@Override @Nullable
|
||||
TransactionIndexer getTransactionIndexer() {
|
||||
return getRootRouter().getTransactionIndexer();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
+338
-133
@@ -2,8 +2,11 @@ package com.bluelinelabs.conductor;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
@@ -11,9 +14,12 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller.LifecycleListener;
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler.ChangeTransaction;
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler.ControllerChangeListener;
|
||||
import com.bluelinelabs.conductor.changehandler.SimpleSwapChangeHandler;
|
||||
import com.bluelinelabs.conductor.internal.NoOpControllerChangeHandler;
|
||||
import com.bluelinelabs.conductor.internal.ThreadUtils;
|
||||
import com.bluelinelabs.conductor.internal.TransactionIndexer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -30,17 +36,21 @@ public abstract class Router {
|
||||
private static final String KEY_BACKSTACK = "Router.backstack";
|
||||
private static final String KEY_POPS_LAST_VIEW = "Router.popsLastView";
|
||||
|
||||
protected final Backstack backstack = new Backstack();
|
||||
final Backstack backstack = new Backstack();
|
||||
private final List<ControllerChangeListener> changeListeners = new ArrayList<>();
|
||||
private final List<ChangeTransaction> pendingControllerChanges = new ArrayList<>();
|
||||
final List<Controller> destroyingControllers = new ArrayList<>();
|
||||
|
||||
private boolean popsLastView = false;
|
||||
boolean containerFullyAttached = false;
|
||||
|
||||
ViewGroup container;
|
||||
|
||||
/**
|
||||
* Returns this Router's host Activity
|
||||
* Returns this Router's host Activity or {@code null} if it has either not yet been attached to
|
||||
* an Activity or if the Activity has been destroyed.
|
||||
*/
|
||||
@Nullable
|
||||
public abstract Activity getActivity();
|
||||
|
||||
/**
|
||||
@@ -48,21 +58,21 @@ public abstract class Router {
|
||||
* of the controller that called startActivityForResult is not known.
|
||||
*
|
||||
* @param requestCode The Activity's onActivityResult requestCode
|
||||
* @param resultCode The Activity's onActivityResult resultCode
|
||||
* @param data The Activity's onActivityResult data
|
||||
* @param resultCode The Activity's onActivityResult resultCode
|
||||
* @param data The Activity's onActivityResult data
|
||||
*/
|
||||
public abstract void onActivityResult(int requestCode, int resultCode, Intent data);
|
||||
public abstract void onActivityResult(int requestCode, int resultCode, @Nullable Intent data);
|
||||
|
||||
/**
|
||||
* This should be called by the host Activity when its onRequestPermissionsResult method is called. The call will be forwarded
|
||||
* to the {@link Controller} with the instanceId passed in.
|
||||
*
|
||||
* @param instanceId The instanceId of the Controller to which this result should be forwarded
|
||||
* @param requestCode The Activity's onRequestPermissionsResult requestCode
|
||||
* @param permissions The Activity's onRequestPermissionsResult permissions
|
||||
* @param instanceId The instanceId of the Controller to which this result should be forwarded
|
||||
* @param requestCode The Activity's onRequestPermissionsResult requestCode
|
||||
* @param permissions The Activity's onRequestPermissionsResult permissions
|
||||
* @param grantResults The Activity's onRequestPermissionsResult grantResults
|
||||
*/
|
||||
public void onRequestPermissionsResult(String instanceId, int requestCode, String[] permissions, int[] grantResults) {
|
||||
public void onRequestPermissionsResult(@NonNull String instanceId, int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
Controller controller = getControllerWithInstanceId(instanceId);
|
||||
if (controller != null) {
|
||||
controller.requestPermissionsResult(requestCode, permissions, grantResults);
|
||||
@@ -75,8 +85,12 @@ public abstract class Router {
|
||||
*
|
||||
* @return Whether or not a back action was handled by the Router
|
||||
*/
|
||||
@UiThread
|
||||
public boolean handleBack() {
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
if (!backstack.isEmpty()) {
|
||||
//noinspection ConstantConditions
|
||||
if (backstack.peek().controller.handleBack()) {
|
||||
return true;
|
||||
} else if (popCurrentController()) {
|
||||
@@ -92,8 +106,16 @@ public abstract class Router {
|
||||
*
|
||||
* @return Whether or not this Router still has controllers remaining on it after popping.
|
||||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
@UiThread
|
||||
public boolean popCurrentController() {
|
||||
return popController(backstack.peek().controller);
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
RouterTransaction transaction = backstack.peek();
|
||||
if (transaction == null) {
|
||||
throw new IllegalStateException("Trying to pop the current controller when there are none on the backstack.");
|
||||
}
|
||||
return popController(transaction.controller);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,27 +124,41 @@ public abstract class Router {
|
||||
* @param controller The controller that should be popped from this Router
|
||||
* @return Whether or not this Router still has controllers remaining on it after popping.
|
||||
*/
|
||||
public boolean popController(Controller controller) {
|
||||
RouterTransaction topController = backstack.peek();
|
||||
boolean poppingTopController = topController != null && topController.controller == controller;
|
||||
@UiThread
|
||||
public boolean popController(@NonNull Controller controller) {
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
RouterTransaction topTransaction = backstack.peek();
|
||||
boolean poppingTopController = topTransaction != null && topTransaction.controller == controller;
|
||||
|
||||
if (poppingTopController) {
|
||||
trackDestroyingController(backstack.pop());
|
||||
performControllerChange(backstack.peek(), topTransaction, false);
|
||||
} else {
|
||||
RouterTransaction removedTransaction = null;
|
||||
RouterTransaction nextTransaction = null;
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
if (transaction.controller == controller) {
|
||||
if (controller.isAttached()) {
|
||||
trackDestroyingController(transaction);
|
||||
}
|
||||
backstack.remove(transaction);
|
||||
removedTransaction = transaction;
|
||||
} else if (removedTransaction != null) {
|
||||
if (!transaction.controller.isAttached()) {
|
||||
nextTransaction = transaction;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (poppingTopController) {
|
||||
performControllerChange(backstack.peek(), topController, false);
|
||||
if (removedTransaction != null) {
|
||||
performControllerChange(nextTransaction, removedTransaction, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (popsLastView) {
|
||||
return topController != null;
|
||||
return topTransaction != null;
|
||||
} else {
|
||||
return !backstack.isEmpty();
|
||||
}
|
||||
@@ -134,7 +170,10 @@ public abstract class Router {
|
||||
* @param transaction The transaction detailing what should be pushed, including the {@link Controller},
|
||||
* and its push and pop {@link ControllerChangeHandler}, and its tag.
|
||||
*/
|
||||
@UiThread
|
||||
public void pushController(@NonNull RouterTransaction transaction) {
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
RouterTransaction from = backstack.peek();
|
||||
pushToBackstack(transaction);
|
||||
performControllerChange(transaction, from, true);
|
||||
@@ -146,24 +185,56 @@ public abstract class Router {
|
||||
* @param transaction The transaction detailing what should be pushed, including the {@link Controller},
|
||||
* and its push and pop {@link ControllerChangeHandler}, and its tag.
|
||||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
@UiThread
|
||||
public void replaceTopController(@NonNull RouterTransaction transaction) {
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
RouterTransaction topTransaction = backstack.peek();
|
||||
if (!backstack.isEmpty()) {
|
||||
trackDestroyingController(backstack.pop());
|
||||
}
|
||||
|
||||
final ControllerChangeHandler handler = transaction.pushChangeHandler();
|
||||
if (topTransaction != null) {
|
||||
//noinspection ConstantConditions
|
||||
final boolean oldHandlerRemovedViews = topTransaction.pushChangeHandler() == null || topTransaction.pushChangeHandler().removesFromViewOnPush();
|
||||
final boolean newHandlerRemovesViews = handler == null || handler.removesFromViewOnPush();
|
||||
if (!oldHandlerRemovedViews && newHandlerRemovesViews) {
|
||||
for (RouterTransaction visibleTransaction : getVisibleTransactions(backstack.iterator())) {
|
||||
performControllerChange(null, visibleTransaction, true, handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pushToBackstack(transaction);
|
||||
performControllerChange(transaction, topTransaction, true);
|
||||
|
||||
if (handler != null) {
|
||||
handler.setForceRemoveViewOnPush(true);
|
||||
}
|
||||
performControllerChange(transaction.pushChangeHandler(handler), topTransaction, true);
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
void destroy(boolean popViews) {
|
||||
popsLastView = true;
|
||||
List<RouterTransaction> poppedControllers = backstack.popAll();
|
||||
final List<RouterTransaction> poppedControllers = backstack.popAll();
|
||||
trackDestroyingControllers(poppedControllers);
|
||||
|
||||
if (poppedControllers.size() > 0) {
|
||||
trackDestroyingControllers(poppedControllers);
|
||||
if (popViews && poppedControllers.size() > 0) {
|
||||
RouterTransaction topTransaction = poppedControllers.get(0);
|
||||
topTransaction.controller().addLifecycleListener(new LifecycleListener() {
|
||||
@Override
|
||||
public void onChangeEnd(@NonNull Controller controller, @NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
if (changeType == ControllerChangeType.POP_EXIT) {
|
||||
for (int i = poppedControllers.size() - 1; i > 0; i--) {
|
||||
RouterTransaction transaction = poppedControllers.get(i);
|
||||
performControllerChange(null, transaction, true, new SimpleSwapChangeHandler());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
performControllerChange(null, poppedControllers.get(0).controller, false, poppedControllers.get(0).popChangeHandler());
|
||||
performControllerChange(null, topTransaction, false, topTransaction.popChangeHandler());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,6 +247,7 @@ public abstract class Router {
|
||||
* in the stack. This defaults to false so that the developer can either finish its containing Activity or otherwise
|
||||
* hide its parent view without any strange artifacting.
|
||||
*/
|
||||
@NonNull
|
||||
public Router setPopsLastView(boolean popsLastView) {
|
||||
this.popsLastView = popsLastView;
|
||||
return this;
|
||||
@@ -186,7 +258,10 @@ public abstract class Router {
|
||||
*
|
||||
* @return Whether or not any {@link Controller}s were popped in order to get to the root transaction
|
||||
*/
|
||||
@UiThread
|
||||
public boolean popToRoot() {
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
return popToRoot(null);
|
||||
}
|
||||
|
||||
@@ -196,8 +271,13 @@ public abstract class Router {
|
||||
* @param changeHandler The {@link ControllerChangeHandler} to handle this transaction
|
||||
* @return Whether or not any {@link Controller}s were popped in order to get to the root transaction
|
||||
*/
|
||||
public boolean popToRoot(ControllerChangeHandler changeHandler) {
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
@UiThread
|
||||
public boolean popToRoot(@Nullable ControllerChangeHandler changeHandler) {
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
if (backstack.size() > 1) {
|
||||
//noinspection ConstantConditions
|
||||
popToTransaction(backstack.root(), changeHandler);
|
||||
return true;
|
||||
} else {
|
||||
@@ -211,18 +291,25 @@ public abstract class Router {
|
||||
* @param tag The tag being popped to
|
||||
* @return Whether or not any {@link Controller}s were popped in order to get to the transaction with the passed tag
|
||||
*/
|
||||
@UiThread
|
||||
public boolean popToTag(@NonNull String tag) {
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
return popToTag(tag, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pops all {@link Controller}s until the {@link Controller} with the passed tag is at the top
|
||||
*
|
||||
* @param tag The tag being popped to
|
||||
* @param tag The tag being popped to
|
||||
* @param changeHandler The {@link ControllerChangeHandler} to handle this transaction
|
||||
* @return Whether or not the {@link Controller} with the passed tag is now at the top
|
||||
*/
|
||||
public boolean popToTag(@NonNull String tag, ControllerChangeHandler changeHandler) {
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
@UiThread
|
||||
public boolean popToTag(@NonNull String tag, @Nullable ControllerChangeHandler changeHandler) {
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
if (tag.equals(transaction.tag())) {
|
||||
popToTransaction(transaction, changeHandler);
|
||||
@@ -238,39 +325,22 @@ public abstract class Router {
|
||||
* @param transaction The transaction detailing what should be pushed, including the {@link Controller},
|
||||
* and its push and pop {@link ControllerChangeHandler}, and its tag.
|
||||
*/
|
||||
@UiThread
|
||||
public void setRoot(@NonNull RouterTransaction transaction) {
|
||||
ControllerChangeHandler newHandler = transaction.pushChangeHandler() != null ? transaction.pushChangeHandler() : new SimpleSwapChangeHandler();
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
List<RouterTransaction> visibleTransactions = getVisibleTransactions(backstack.iterator());
|
||||
RouterTransaction rootTransaction = visibleTransactions.size() > 0 ? visibleTransactions.get(0) : null;
|
||||
|
||||
removeAllExceptVisibleAndUnowned();
|
||||
|
||||
trackDestroyingControllers(backstack.popAll());
|
||||
|
||||
pushToBackstack(transaction);
|
||||
|
||||
for (int i = visibleTransactions.size() - 1; i > 0; i--) {
|
||||
if (visibleTransactions.get(i).controller.getView() == null) {
|
||||
ControllerChangeHandler.abortPush(visibleTransactions.get(i).controller, transaction.controller, newHandler);
|
||||
} else {
|
||||
performControllerChange(null, visibleTransactions.get(i).controller, true, newHandler);
|
||||
}
|
||||
}
|
||||
|
||||
if (rootTransaction != null && rootTransaction.controller.getView() == null) {
|
||||
ControllerChangeHandler.abortPush(rootTransaction.controller, transaction.controller, newHandler);
|
||||
}
|
||||
performControllerChange(transaction, rootTransaction, true);
|
||||
List<RouterTransaction> transactions = Collections.singletonList(transaction);
|
||||
setBackstack(transactions, transaction.pushChangeHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hosted Controller with the given instance id, if available.
|
||||
* Returns the hosted Controller with the given instance id or {@code null} if no such
|
||||
* Controller exists in this Router.
|
||||
*
|
||||
* @param instanceId The instance ID being searched for
|
||||
* @return The matching Controller, if one exists
|
||||
*/
|
||||
public Controller getControllerWithInstanceId(String instanceId) {
|
||||
@Nullable
|
||||
public Controller getControllerWithInstanceId(@NonNull String instanceId) {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
Controller controllerWithId = transaction.controller.findController(instanceId);
|
||||
if (controllerWithId != null) {
|
||||
@@ -281,12 +351,13 @@ public abstract class Router {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hosted Controller that was pushed with the given tag, if available.
|
||||
* Returns the hosted Controller that was pushed with the given tag or {@code null} if no
|
||||
* such Controller exists in this Router.
|
||||
*
|
||||
* @param tag The tag being searched for
|
||||
* @return The matching Controller, if one exists
|
||||
*/
|
||||
public Controller getControllerWithTag(String tag) {
|
||||
@Nullable
|
||||
public Controller getControllerWithTag(@NonNull String tag) {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
if (tag.equals(transaction.tag())) {
|
||||
return transaction.controller;
|
||||
@@ -298,6 +369,7 @@ public abstract class Router {
|
||||
/**
|
||||
* Returns the number of {@link Controller}s currently in the backstack
|
||||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public int getBackstackSize() {
|
||||
return backstack.size();
|
||||
}
|
||||
@@ -305,6 +377,7 @@ public abstract class Router {
|
||||
/**
|
||||
* Returns the current backstack, ordered from root to most recently pushed.
|
||||
*/
|
||||
@NonNull
|
||||
public List<RouterTransaction> getBackstack() {
|
||||
List<RouterTransaction> list = new ArrayList<>();
|
||||
Iterator<RouterTransaction> backstackIterator = backstack.reverseIterator();
|
||||
@@ -318,53 +391,70 @@ public abstract class Router {
|
||||
* Sets the backstack, transitioning from the current top controller to the top of the new stack (if different)
|
||||
* using the passed {@link ControllerChangeHandler}
|
||||
*
|
||||
* @param newBackstack The new backstack
|
||||
* @param changeHandler An optional change handler to be used to handle the transition
|
||||
* @param newBackstack The new backstack
|
||||
* @param changeHandler An optional change handler to be used to handle the root view of transition
|
||||
*/
|
||||
public void setBackstack(@NonNull List<RouterTransaction> newBackstack, ControllerChangeHandler changeHandler) {
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
@UiThread
|
||||
public void setBackstack(@NonNull List<RouterTransaction> newBackstack, @Nullable ControllerChangeHandler changeHandler) {
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
List<RouterTransaction> oldVisibleTransactions = getVisibleTransactions(backstack.iterator());
|
||||
|
||||
boolean newRootRequiresPush = !(newBackstack.size() > 0 && backstack.contains(newBackstack.get(0)));
|
||||
|
||||
removeAllExceptVisibleAndUnowned();
|
||||
ensureOrderedTransactionIndices(newBackstack);
|
||||
|
||||
backstack.setBackstack(newBackstack);
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
transaction.onAttachedToRouter();
|
||||
}
|
||||
|
||||
if (newBackstack.size() > 0) {
|
||||
List<RouterTransaction> reverseNewBackstack = new ArrayList<>(newBackstack);
|
||||
Collections.reverse(reverseNewBackstack);
|
||||
List<RouterTransaction> newVisibleTransactions = getVisibleTransactions(reverseNewBackstack.iterator());
|
||||
|
||||
boolean visibleTransactionsChanged = newVisibleTransactions.size() != oldVisibleTransactions.size();
|
||||
if (!visibleTransactionsChanged) {
|
||||
for (int i = 0; i < oldVisibleTransactions.size(); i++) {
|
||||
if (oldVisibleTransactions.get(i).controller != newVisibleTransactions.get(i).controller) {
|
||||
visibleTransactionsChanged = true;
|
||||
break;
|
||||
boolean visibleTransactionsChanged = !backstacksAreEqual(newVisibleTransactions, oldVisibleTransactions);
|
||||
if (visibleTransactionsChanged) {
|
||||
RouterTransaction oldRootTransaction = oldVisibleTransactions.size() > 0 ? oldVisibleTransactions.get(0) : null;
|
||||
RouterTransaction newRootTransaction = newVisibleTransactions.get(0);
|
||||
|
||||
// Replace the old root with the new one
|
||||
if (oldRootTransaction == null || oldRootTransaction.controller != newRootTransaction.controller) {
|
||||
// Ensure the existing root controller is fully pushed to the view hierarchy
|
||||
if (oldRootTransaction != null) {
|
||||
ControllerChangeHandler.completeHandlerImmediately(oldRootTransaction.controller.getInstanceId());
|
||||
}
|
||||
performControllerChange(newRootTransaction, oldRootTransaction, newRootRequiresPush, changeHandler);
|
||||
}
|
||||
|
||||
// Remove all visible controllers that were previously on the backstack
|
||||
for (int i = oldVisibleTransactions.size() - 1; i > 0; i--) {
|
||||
RouterTransaction transaction = oldVisibleTransactions.get(i);
|
||||
if (!newVisibleTransactions.contains(transaction)) {
|
||||
ControllerChangeHandler localHandler = changeHandler != null ? changeHandler.copy() : new SimpleSwapChangeHandler();
|
||||
localHandler.setForceRemoveViewOnPush(true);
|
||||
ControllerChangeHandler.completeHandlerImmediately(transaction.controller.getInstanceId());
|
||||
performControllerChange(null, transaction, newRootRequiresPush, localHandler);
|
||||
}
|
||||
}
|
||||
|
||||
// Add any new controllers to the backstack
|
||||
for (int i = 1; i < newVisibleTransactions.size(); i++) {
|
||||
RouterTransaction transaction = newVisibleTransactions.get(i);
|
||||
if (!oldVisibleTransactions.contains(transaction)) {
|
||||
performControllerChange(transaction, newVisibleTransactions.get(i - 1), true, transaction.pushChangeHandler());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (visibleTransactionsChanged) {
|
||||
ControllerChangeHandler handler = changeHandler != null ? changeHandler : new SimpleSwapChangeHandler();
|
||||
|
||||
Controller rootController = oldVisibleTransactions.size() > 0 ? oldVisibleTransactions.get(0).controller : null;
|
||||
performControllerChange(newVisibleTransactions.get(0).controller, rootController, true, handler);
|
||||
|
||||
for (int i = oldVisibleTransactions.size() - 1; i > 0; i--) {
|
||||
RouterTransaction transaction = oldVisibleTransactions.get(i);
|
||||
performControllerChange(null, transaction.controller, true, handler);
|
||||
}
|
||||
|
||||
for (int i = 1; i < newVisibleTransactions.size(); i++) {
|
||||
RouterTransaction transaction = newVisibleTransactions.get(i);
|
||||
handler = transaction.pushChangeHandler() != null ? transaction.pushChangeHandler(): new SimpleSwapChangeHandler();
|
||||
performControllerChange(transaction.controller, newVisibleTransactions.get(i - 1).controller, true, handler);
|
||||
}
|
||||
// Ensure all new controllers have a valid router set
|
||||
for (RouterTransaction transaction : newBackstack) {
|
||||
transaction.controller.setRouter(this);
|
||||
}
|
||||
}
|
||||
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
transaction.onAttachedToRouter();
|
||||
}
|
||||
|
||||
backstack.setBackstack(newBackstack);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -379,7 +469,8 @@ public abstract class Router {
|
||||
*
|
||||
* @param changeListener The listener
|
||||
*/
|
||||
public void addChangeListener(ControllerChangeListener changeListener) {
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public void addChangeListener(@NonNull ControllerChangeListener changeListener) {
|
||||
if (!changeListeners.contains(changeListener)) {
|
||||
changeListeners.add(changeListener);
|
||||
}
|
||||
@@ -390,32 +481,36 @@ public abstract class Router {
|
||||
*
|
||||
* @param changeListener The listener to be removed
|
||||
*/
|
||||
public void removeChangeListener(ControllerChangeListener changeListener) {
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public void removeChangeListener(@NonNull ControllerChangeListener changeListener) {
|
||||
changeListeners.remove(changeListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches this Router's existing backstack to its container if one exists.
|
||||
*/
|
||||
@UiThread
|
||||
public void rebindIfNeeded() {
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
Iterator<RouterTransaction> backstackIterator = backstack.reverseIterator();
|
||||
while (backstackIterator.hasNext()) {
|
||||
RouterTransaction transaction = backstackIterator.next();
|
||||
|
||||
if (transaction.controller.getNeedsAttach()) {
|
||||
performControllerChange(transaction.controller, null, true, new SimpleSwapChangeHandler(false));
|
||||
performControllerChange(transaction, null, true, new SimpleSwapChangeHandler(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void onActivityResult(String instanceId, int requestCode, int resultCode, Intent data) {
|
||||
public final void onActivityResult(@NonNull String instanceId, int requestCode, int resultCode, @Nullable Intent data) {
|
||||
Controller controller = getControllerWithInstanceId(instanceId);
|
||||
if (controller != null) {
|
||||
controller.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
public final void onActivityStarted(Activity activity) {
|
||||
public final void onActivityStarted(@NonNull Activity activity) {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
transaction.controller.activityStarted(activity);
|
||||
|
||||
@@ -425,7 +520,7 @@ public abstract class Router {
|
||||
}
|
||||
}
|
||||
|
||||
public final void onActivityResumed(Activity activity) {
|
||||
public final void onActivityResumed(@NonNull Activity activity) {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
transaction.controller.activityResumed(activity);
|
||||
|
||||
@@ -435,7 +530,7 @@ public abstract class Router {
|
||||
}
|
||||
}
|
||||
|
||||
public final void onActivityPaused(Activity activity) {
|
||||
public final void onActivityPaused(@NonNull Activity activity) {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
transaction.controller.activityPaused(activity);
|
||||
|
||||
@@ -445,7 +540,7 @@ public abstract class Router {
|
||||
}
|
||||
}
|
||||
|
||||
public final void onActivityStopped(Activity activity) {
|
||||
public final void onActivityStopped(@NonNull Activity activity) {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
transaction.controller.activityStopped(activity);
|
||||
|
||||
@@ -455,12 +550,12 @@ public abstract class Router {
|
||||
}
|
||||
}
|
||||
|
||||
public void onActivityDestroyed(Activity activity) {
|
||||
public void onActivityDestroyed(@NonNull Activity activity) {
|
||||
prepareForContainerRemoval();
|
||||
changeListeners.clear();
|
||||
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
transaction.controller.activityDestroyed(activity.isChangingConfigurations());
|
||||
transaction.controller.activityDestroyed(activity);
|
||||
|
||||
for (Router childRouter : transaction.controller.getChildRouters()) {
|
||||
childRouter.onActivityDestroyed(activity);
|
||||
@@ -469,7 +564,7 @@ public abstract class Router {
|
||||
|
||||
for (int index = destroyingControllers.size() - 1; index >= 0; index--) {
|
||||
Controller controller = destroyingControllers.get(index);
|
||||
controller.activityDestroyed(activity.isChangingConfigurations());
|
||||
controller.activityDestroyed(activity);
|
||||
|
||||
for (Router childRouter : controller.getChildRouters()) {
|
||||
childRouter.onActivityDestroyed(activity);
|
||||
@@ -479,14 +574,16 @@ public abstract class Router {
|
||||
container = null;
|
||||
}
|
||||
|
||||
public void prepareForHostDetach() {
|
||||
void prepareForHostDetach() {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
ControllerChangeHandler.completePushImmediately(transaction.controller.getInstanceId());
|
||||
if (ControllerChangeHandler.completeHandlerImmediately(transaction.controller.getInstanceId())) {
|
||||
transaction.controller.setNeedsAttach(true);
|
||||
}
|
||||
transaction.controller.prepareForHostDetach();
|
||||
}
|
||||
}
|
||||
|
||||
public void saveInstanceState(Bundle outState) {
|
||||
public void saveInstanceState(@NonNull Bundle outState) {
|
||||
prepareForHostDetach();
|
||||
|
||||
Bundle backstackState = new Bundle();
|
||||
@@ -496,8 +593,9 @@ public abstract class Router {
|
||||
outState.putBoolean(KEY_POPS_LAST_VIEW, popsLastView);
|
||||
}
|
||||
|
||||
public void restoreInstanceState(Bundle savedInstanceState) {
|
||||
public void restoreInstanceState(@NonNull Bundle savedInstanceState) {
|
||||
Bundle backstackBundle = savedInstanceState.getParcelable(KEY_BACKSTACK);
|
||||
//noinspection ConstantConditions
|
||||
backstack.restoreInstanceState(backstackBundle);
|
||||
popsLastView = savedInstanceState.getBoolean(KEY_POPS_LAST_VIEW);
|
||||
|
||||
@@ -507,7 +605,7 @@ public abstract class Router {
|
||||
}
|
||||
}
|
||||
|
||||
public final void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
public final void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
transaction.controller.createOptionsMenu(menu, inflater);
|
||||
|
||||
@@ -517,7 +615,7 @@ public abstract class Router {
|
||||
}
|
||||
}
|
||||
|
||||
public final void onPrepareOptionsMenu(Menu menu) {
|
||||
public final void onPrepareOptionsMenu(@NonNull Menu menu) {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
transaction.controller.prepareOptionsMenu(menu);
|
||||
|
||||
@@ -527,7 +625,7 @@ public abstract class Router {
|
||||
}
|
||||
}
|
||||
|
||||
public final boolean onOptionsItemSelected(MenuItem item) {
|
||||
public final boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
if (transaction.controller.optionsItemSelected(item)) {
|
||||
return true;
|
||||
@@ -542,26 +640,53 @@ public abstract class Router {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void popToTransaction(@NonNull RouterTransaction transaction, ControllerChangeHandler changeHandler) {
|
||||
RouterTransaction topTransaction = backstack.peek();
|
||||
List<RouterTransaction> poppedTransactions = backstack.popTo(transaction);
|
||||
trackDestroyingControllers(poppedTransactions);
|
||||
private void popToTransaction(@NonNull RouterTransaction transaction, @Nullable ControllerChangeHandler changeHandler) {
|
||||
if (backstack.size() > 0) {
|
||||
RouterTransaction topTransaction = backstack.peek();
|
||||
|
||||
List<RouterTransaction> updatedBackstack = new ArrayList<>();
|
||||
Iterator<RouterTransaction> backstackIterator = backstack.reverseIterator();
|
||||
while (backstackIterator.hasNext()) {
|
||||
RouterTransaction existingTransaction = backstackIterator.next();
|
||||
updatedBackstack.add(existingTransaction);
|
||||
if (existingTransaction == transaction) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (poppedTransactions.size() > 0) {
|
||||
if (changeHandler == null) {
|
||||
//noinspection ConstantConditions
|
||||
changeHandler = topTransaction.popChangeHandler();
|
||||
}
|
||||
|
||||
performControllerChange(backstack.peek().controller, topTransaction.controller, false, changeHandler);
|
||||
setBackstack(updatedBackstack, changeHandler);
|
||||
}
|
||||
}
|
||||
|
||||
void watchContainerAttach() {
|
||||
container.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
containerFullyAttached = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void prepareForContainerRemoval() {
|
||||
containerFullyAttached = false;
|
||||
|
||||
if (container != null) {
|
||||
container.setOnHierarchyChangeListener(null);
|
||||
}
|
||||
}
|
||||
|
||||
void onContextAvailable() {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
transaction.controller.onContextAvailable();
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
final List<Controller> getControllers() {
|
||||
List<Controller> controllers = new ArrayList<>();
|
||||
|
||||
@@ -573,6 +698,7 @@ public abstract class Router {
|
||||
return controllers;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public final Boolean handleRequestedPermission(@NonNull String permission) {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
if (transaction.controller.didRequestPermission(permission)) {
|
||||
@@ -582,7 +708,7 @@ public abstract class Router {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void performControllerChange(RouterTransaction to, RouterTransaction from, boolean isPush) {
|
||||
private void performControllerChange(@Nullable RouterTransaction to, @Nullable RouterTransaction from, boolean isPush) {
|
||||
if (isPush && to != null) {
|
||||
to.onAttachedToRouter();
|
||||
}
|
||||
@@ -594,32 +720,75 @@ public abstract class Router {
|
||||
} else if (from != null) {
|
||||
changeHandler = from.popChangeHandler();
|
||||
} else {
|
||||
changeHandler = new SimpleSwapChangeHandler();
|
||||
changeHandler = null;
|
||||
}
|
||||
|
||||
performControllerChange(to, from, isPush, changeHandler);
|
||||
}
|
||||
|
||||
private void performControllerChange(@Nullable RouterTransaction to, @Nullable RouterTransaction from, boolean isPush, @Nullable ControllerChangeHandler changeHandler) {
|
||||
Controller toController = to != null ? to.controller : null;
|
||||
Controller fromController = from != null ? from.controller : null;
|
||||
boolean forceDetachDestroy = false;
|
||||
|
||||
performControllerChange(toController, fromController, isPush, changeHandler);
|
||||
}
|
||||
|
||||
private void performControllerChange(final Controller to, final Controller from, boolean isPush, @NonNull ControllerChangeHandler changeHandler) {
|
||||
if (to != null) {
|
||||
setControllerRouter(to);
|
||||
to.ensureValidIndex(getTransactionIndexer());
|
||||
setControllerRouter(toController);
|
||||
} else if (backstack.size() == 0 && !popsLastView) {
|
||||
// We're emptying out the backstack. Views get weird if you transition them out, so just no-op it. The hosting
|
||||
// Activity should be handling this by finishing or at least hiding this view.
|
||||
// We're emptying out the backstack. Views get weird if you transition them out, so just no-op it. The host
|
||||
// Activity or controller should be handling this by finishing or at least hiding this view.
|
||||
changeHandler = new NoOpControllerChangeHandler();
|
||||
forceDetachDestroy = true;
|
||||
}
|
||||
|
||||
ControllerChangeHandler.executeChange(to, from, isPush, container, changeHandler, changeListeners);
|
||||
performControllerChange(toController, fromController, isPush, changeHandler);
|
||||
|
||||
if (forceDetachDestroy && fromController != null && fromController.getView() != null) {
|
||||
fromController.detach(fromController.getView(), true, false);
|
||||
}
|
||||
}
|
||||
|
||||
void pushToBackstack(@NonNull RouterTransaction entry) {
|
||||
private void performControllerChange(@Nullable final Controller to, @Nullable final Controller from, final boolean isPush, @Nullable final ControllerChangeHandler changeHandler) {
|
||||
if (isPush && to != null && to.isDestroyed()) {
|
||||
throw new IllegalStateException("Trying to push a controller that has already been destroyed. (" + to.getClass().getSimpleName() + ")");
|
||||
}
|
||||
|
||||
final ChangeTransaction transaction = new ChangeTransaction(to, from, isPush, container, changeHandler, changeListeners);
|
||||
|
||||
if (pendingControllerChanges.size() > 0) {
|
||||
// If we already have changes queued up (awaiting full container attach), queue this one up as well so they don't happen
|
||||
// out of order.
|
||||
pendingControllerChanges.add(transaction);
|
||||
} else if (from != null && (changeHandler == null || changeHandler.removesFromViewOnPush()) && !containerFullyAttached) {
|
||||
// If the change handler will remove the from view, we have to make sure the container is fully attached first so we avoid NPEs
|
||||
// within ViewGroup (details on issue #287). Post this to the container to ensure the attach is complete before we try to remove
|
||||
// anything.
|
||||
pendingControllerChanges.add(transaction);
|
||||
container.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
performPendingControllerChanges();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ControllerChangeHandler.executeChange(transaction);
|
||||
}
|
||||
}
|
||||
|
||||
void performPendingControllerChanges() {
|
||||
// We're intentionally using dynamic size checking (list.size()) here so we can account for changes
|
||||
// that occur during this loop (ex: if a controller is popped from within onAttach)
|
||||
for (int i = 0; i < pendingControllerChanges.size(); i++) {
|
||||
ControllerChangeHandler.executeChange(pendingControllerChanges.get(i));
|
||||
}
|
||||
pendingControllerChanges.clear();
|
||||
}
|
||||
|
||||
protected void pushToBackstack(@NonNull RouterTransaction entry) {
|
||||
backstack.push(entry);
|
||||
}
|
||||
|
||||
private void trackDestroyingController(RouterTransaction transaction) {
|
||||
private void trackDestroyingController(@NonNull RouterTransaction transaction) {
|
||||
if (!transaction.controller.isDestroyed()) {
|
||||
destroyingControllers.add(transaction.controller);
|
||||
|
||||
@@ -632,7 +801,7 @@ public abstract class Router {
|
||||
}
|
||||
}
|
||||
|
||||
private void trackDestroyingControllers(List<RouterTransaction> transactions) {
|
||||
private void trackDestroyingControllers(@NonNull List<RouterTransaction> transactions) {
|
||||
for (RouterTransaction transaction : transactions) {
|
||||
trackDestroyingController(transaction);
|
||||
}
|
||||
@@ -662,7 +831,23 @@ public abstract class Router {
|
||||
}
|
||||
}
|
||||
|
||||
private void addRouterViewsToList(Router router, List<View> list) {
|
||||
// Swap around transaction indicies to ensure they don't get thrown out of order by the
|
||||
// developer rearranging the backstack at runtime.
|
||||
private void ensureOrderedTransactionIndices(List<RouterTransaction> backstack) {
|
||||
List<Integer> indices = new ArrayList<>();
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
transaction.ensureValidIndex(getTransactionIndexer());
|
||||
indices.add(transaction.transactionIndex);
|
||||
}
|
||||
|
||||
Collections.sort(indices);
|
||||
|
||||
for (int i = 0; i < backstack.size(); i++) {
|
||||
backstack.get(i).transactionIndex = indices.get(i);
|
||||
}
|
||||
}
|
||||
|
||||
private void addRouterViewsToList(@NonNull Router router, @NonNull List<View> list) {
|
||||
for (Controller controller : router.getControllers()) {
|
||||
if (controller.getView() != null) {
|
||||
list.add(controller.getView());
|
||||
@@ -674,12 +859,13 @@ public abstract class Router {
|
||||
}
|
||||
}
|
||||
|
||||
private List<RouterTransaction> getVisibleTransactions(Iterator<RouterTransaction> backstackIterator) {
|
||||
private List<RouterTransaction> getVisibleTransactions(@NonNull Iterator<RouterTransaction> backstackIterator) {
|
||||
List<RouterTransaction> transactions = new ArrayList<>();
|
||||
while (backstackIterator.hasNext()) {
|
||||
RouterTransaction transaction = backstackIterator.next();
|
||||
transactions.add(transaction);
|
||||
|
||||
//noinspection ConstantConditions
|
||||
if (transaction.pushChangeHandler() == null || transaction.pushChangeHandler().removesFromViewOnPush()) {
|
||||
break;
|
||||
}
|
||||
@@ -689,18 +875,37 @@ public abstract class Router {
|
||||
return transactions;
|
||||
}
|
||||
|
||||
void setControllerRouter(Controller controller) {
|
||||
private boolean backstacksAreEqual(List<RouterTransaction> lhs, List<RouterTransaction> rhs) {
|
||||
if (lhs.size() != rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rhs.size(); i++) {
|
||||
if (rhs.get(i).controller() != lhs.get(i).controller()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void setControllerRouter(@NonNull Controller controller) {
|
||||
controller.setRouter(this);
|
||||
controller.onContextAvailable();
|
||||
}
|
||||
|
||||
abstract void invalidateOptionsMenu();
|
||||
abstract void startActivity(Intent intent);
|
||||
abstract void startActivityForResult(String instanceId, Intent intent, int requestCode);
|
||||
abstract void startActivityForResult(String instanceId, Intent intent, int requestCode, Bundle options);
|
||||
abstract void registerForActivityResult(String instanceId, int requestCode);
|
||||
abstract void unregisterForActivityResults(String instanceId);
|
||||
abstract void requestPermissions(String instanceId, String[] permissions, int requestCode);
|
||||
abstract void startActivity(@NonNull Intent intent);
|
||||
abstract void startActivityForResult(@NonNull String instanceId, @NonNull Intent intent, int requestCode);
|
||||
abstract void startActivityForResult(@NonNull String instanceId, @NonNull Intent intent, int requestCode, @Nullable Bundle options);
|
||||
abstract 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;
|
||||
abstract void registerForActivityResult(@NonNull String instanceId, int requestCode);
|
||||
abstract void unregisterForActivityResults(@NonNull String instanceId);
|
||||
abstract void requestPermissions(@NonNull String instanceId, @NonNull String[] permissions, int requestCode);
|
||||
abstract boolean hasHost();
|
||||
abstract List<Router> getSiblingRouters();
|
||||
abstract Router getRootRouter();
|
||||
@NonNull abstract List<Router> getSiblingRouters();
|
||||
@NonNull abstract Router getRootRouter();
|
||||
@Nullable abstract TransactionIndexer getTransactionIndexer();
|
||||
|
||||
}
|
||||
|
||||
@@ -2,16 +2,22 @@ package com.bluelinelabs.conductor;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.bluelinelabs.conductor.internal.TransactionIndexer;
|
||||
|
||||
/**
|
||||
* Metadata used for adding {@link Controller}s to a {@link Router}.
|
||||
*/
|
||||
public class RouterTransaction {
|
||||
|
||||
private static int INVALID_INDEX = -1;
|
||||
|
||||
private static final String KEY_VIEW_CONTROLLER_BUNDLE = "RouterTransaction.controller.bundle";
|
||||
private static final String KEY_PUSH_TRANSITION = "RouterTransaction.pushControllerChangeHandler";
|
||||
private static final String KEY_POP_TRANSITION = "RouterTransaction.popControllerChangeHandler";
|
||||
private static final String KEY_TAG = "RouterTransaction.tag";
|
||||
private static final String KEY_INDEX = "RouterTransaction.transactionIndex";
|
||||
private static final String KEY_ATTACHED_TO_ROUTER = "RouterTransaction.attachedToRouter";
|
||||
|
||||
@NonNull final Controller controller;
|
||||
@@ -20,7 +26,9 @@ public class RouterTransaction {
|
||||
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);
|
||||
}
|
||||
@@ -34,6 +42,7 @@ public class RouterTransaction {
|
||||
pushControllerChangeHandler = ControllerChangeHandler.fromBundle(bundle.getBundle(KEY_PUSH_TRANSITION));
|
||||
popControllerChangeHandler = ControllerChangeHandler.fromBundle(bundle.getBundle(KEY_POP_TRANSITION));
|
||||
tag = bundle.getString(KEY_TAG);
|
||||
transactionIndex = bundle.getInt(KEY_INDEX);
|
||||
attachedToRouter = bundle.getBoolean(KEY_ATTACHED_TO_ROUTER);
|
||||
}
|
||||
|
||||
@@ -41,15 +50,18 @@ public class RouterTransaction {
|
||||
attachedToRouter = true;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Controller controller() {
|
||||
return controller;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String tag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
public RouterTransaction tag(String tag) {
|
||||
@NonNull
|
||||
public RouterTransaction tag(@Nullable String tag) {
|
||||
if (!attachedToRouter) {
|
||||
this.tag = tag;
|
||||
return this;
|
||||
@@ -58,6 +70,7 @@ public class RouterTransaction {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ControllerChangeHandler pushChangeHandler() {
|
||||
ControllerChangeHandler handler = controller.getOverriddenPushHandler();
|
||||
if (handler == null) {
|
||||
@@ -66,7 +79,8 @@ public class RouterTransaction {
|
||||
return handler;
|
||||
}
|
||||
|
||||
public RouterTransaction pushChangeHandler(ControllerChangeHandler handler) {
|
||||
@NonNull
|
||||
public RouterTransaction pushChangeHandler(@Nullable ControllerChangeHandler handler) {
|
||||
if (!attachedToRouter) {
|
||||
pushControllerChangeHandler = handler;
|
||||
return this;
|
||||
@@ -75,6 +89,7 @@ public class RouterTransaction {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ControllerChangeHandler popChangeHandler() {
|
||||
ControllerChangeHandler handler = controller.getOverriddenPopHandler();
|
||||
if (handler == null) {
|
||||
@@ -83,7 +98,8 @@ public class RouterTransaction {
|
||||
return handler;
|
||||
}
|
||||
|
||||
public RouterTransaction popChangeHandler(ControllerChangeHandler handler) {
|
||||
@NonNull
|
||||
public RouterTransaction popChangeHandler(@Nullable ControllerChangeHandler handler) {
|
||||
if (!attachedToRouter) {
|
||||
popControllerChangeHandler = handler;
|
||||
return this;
|
||||
@@ -92,9 +108,19 @@ public class RouterTransaction {
|
||||
}
|
||||
}
|
||||
|
||||
void ensureValidIndex(@Nullable TransactionIndexer indexer) {
|
||||
if (indexer == null) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
if (transactionIndex == INVALID_INDEX && indexer != null) {
|
||||
transactionIndex = indexer.nextIndex();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to serialize this transaction into a Bundle
|
||||
*/
|
||||
@NonNull
|
||||
public Bundle saveInstanceState() {
|
||||
Bundle bundle = new Bundle();
|
||||
|
||||
@@ -108,9 +134,10 @@ public class RouterTransaction {
|
||||
}
|
||||
|
||||
bundle.putString(KEY_TAG, tag);
|
||||
bundle.putInt(KEY_INDEX, transactionIndex);
|
||||
bundle.putBoolean(KEY_ATTACHED_TO_ROUTER, attachedToRouter);
|
||||
|
||||
return bundle;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Executable → Regular
+103
-33
@@ -1,9 +1,11 @@
|
||||
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;
|
||||
@@ -19,22 +21,28 @@ 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 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);
|
||||
}
|
||||
@@ -59,12 +67,14 @@ public abstract class AnimatorChangeHandler extends ControllerChangeHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAbortPush(@NonNull ControllerChangeHandler newHandler, Controller newTop) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +84,9 @@ public abstract class AnimatorChangeHandler extends ControllerChangeHandler {
|
||||
|
||||
needsImmediateCompletion = true;
|
||||
if (animator != null) {
|
||||
animator.cancel();
|
||||
animator.end();
|
||||
} else if (onAnimationReadyOrAbortedListener != null) {
|
||||
onAnimationReadyOrAbortedListener.onReadyOrAborted();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,12 +103,13 @@ public abstract class AnimatorChangeHandler extends ControllerChangeHandler {
|
||||
* Should be overridden to return the Animator to use while replacing Views.
|
||||
*
|
||||
* @param container The container these Views are hosted in.
|
||||
* @param from The previous View in the container, if any.
|
||||
* @param to The next View that should be put in the container, if any.
|
||||
* @param from The previous View in the container or {@code null} if there was no Controller before this transition
|
||||
* @param to The next View that should be put in the container or {@code null} if no Controller is being transitioned to
|
||||
* @param isPush True if this is a push transaction, false if it's a pop.
|
||||
* @param toAddedToContainer True if the "to" view was added to the container as a part of this ChangeHandler. False if it was already in the hierarchy.
|
||||
*/
|
||||
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.
|
||||
@@ -104,7 +117,7 @@ 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;
|
||||
|
||||
@@ -117,17 +130,8 @@ public abstract class AnimatorChangeHandler extends ControllerChangeHandler {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,9 +140,36 @@ public abstract class AnimatorChangeHandler extends ControllerChangeHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private void performAnimation(@NonNull final ViewGroup container, final View from, final View to, final boolean isPush, final boolean toAddedToContainer, @NonNull final ControllerChangeCompletedListener changeListener) {
|
||||
if (canceled) {
|
||||
private void complete(@NonNull ControllerChangeCompletedListener changeListener, @Nullable AnimatorListener animatorListener) {
|
||||
if (!completed) {
|
||||
completed = true;
|
||||
changeListener.onChangeCompleted();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -155,29 +186,68 @@ public abstract class AnimatorChangeHandler extends ControllerChangeHandler {
|
||||
container.removeView(from);
|
||||
}
|
||||
|
||||
changeListener.onChangeCompleted();
|
||||
animator.removeListener(this);
|
||||
animator = null;
|
||||
complete(changeListener, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (from != null && (!isPush || removesFromViewOnPush) && !canceled) {
|
||||
container.removeView(from);
|
||||
if (!canceled && animator != null) {
|
||||
if (from != null && (!isPush || removesFromViewOnPush)) {
|
||||
container.removeView(from);
|
||||
}
|
||||
|
||||
complete(changeListener, this);
|
||||
|
||||
if (isPush && from != null) {
|
||||
resetFromView(from);
|
||||
}
|
||||
}
|
||||
|
||||
changeListener.onChangeCompleted();
|
||||
animator.removeListener(this);
|
||||
|
||||
if (isPush && from != null) {
|
||||
resetFromView(from);
|
||||
}
|
||||
|
||||
animator = null;
|
||||
}
|
||||
});
|
||||
|
||||
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
+14
-3
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,4 +58,10 @@ public class HorizontalChangeHandler extends AnimatorChangeHandler {
|
||||
protected void resetFromView(@NonNull View from) {
|
||||
from.setTranslationX(0);
|
||||
}
|
||||
|
||||
@Override @NonNull
|
||||
public ControllerChangeHandler copy() {
|
||||
return new HorizontalChangeHandler(getAnimationDuration(), removesFromViewOnPush());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Executable → Regular
+51
-4
@@ -2,7 +2,9 @@ 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;
|
||||
@@ -11,7 +13,7 @@ import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
/**
|
||||
* A {@link ControllerChangeHandler} that will instantly swap Views with no animations or transitions.
|
||||
*/
|
||||
public class SimpleSwapChangeHandler extends ControllerChangeHandler {
|
||||
public class SimpleSwapChangeHandler extends ControllerChangeHandler implements OnAttachStateChangeListener {
|
||||
|
||||
private static final String KEY_REMOVES_FROM_ON_PUSH = "SimpleSwapChangeHandler.removesFromViewOnPush";
|
||||
|
||||
@@ -19,6 +21,9 @@ public class SimpleSwapChangeHandler extends ControllerChangeHandler {
|
||||
|
||||
private boolean canceled;
|
||||
|
||||
private ViewGroup container;
|
||||
private ControllerChangeCompletedListener changeListener;
|
||||
|
||||
public SimpleSwapChangeHandler() {
|
||||
this(true);
|
||||
}
|
||||
@@ -40,14 +45,25 @@ public class SimpleSwapChangeHandler extends ControllerChangeHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAbortPush(@NonNull ControllerChangeHandler newHandler, Controller newTop) {
|
||||
public void onAbortPush(@NonNull ControllerChangeHandler newHandler, @Nullable Controller newTop) {
|
||||
super.onAbortPush(newHandler, newTop);
|
||||
|
||||
canceled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull final ControllerChangeCompletedListener changeListener) {
|
||||
public void completeImmediately() {
|
||||
if (changeListener != null) {
|
||||
changeListener.onChangeCompleted();
|
||||
changeListener = null;
|
||||
|
||||
container.removeOnAttachStateChangeListener(this);
|
||||
container = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, @Nullable View from, @Nullable View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
if (!canceled) {
|
||||
if (from != null && (!isPush || removesFromViewOnPush)) {
|
||||
container.removeView(from);
|
||||
@@ -58,11 +74,42 @@ public class SimpleSwapChangeHandler extends ControllerChangeHandler {
|
||||
}
|
||||
}
|
||||
|
||||
changeListener.onChangeCompleted();
|
||||
if (container.getWindowToken() != null) {
|
||||
changeListener.onChangeCompleted();
|
||||
} else {
|
||||
this.changeListener = changeListener;
|
||||
this.container = container;
|
||||
container.addOnAttachStateChangeListener(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removesFromViewOnPush() {
|
||||
return removesFromViewOnPush;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewAttachedToWindow(@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;
|
||||
}
|
||||
}
|
||||
|
||||
+69
-15
@@ -3,6 +3,7 @@ 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;
|
||||
@@ -18,34 +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 onAbortPush(@NonNull ControllerChangeHandler newHandler, Controller newTop) {
|
||||
public void onAbortPush(@NonNull ControllerChangeHandler newHandler, @Nullable Controller newTop) {
|
||||
super.onAbortPush(newHandler, newTop);
|
||||
|
||||
canceled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performChange(@NonNull final ViewGroup container, View from, View to, boolean isPush, @NonNull final ControllerChangeCompletedListener changeListener) {
|
||||
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;
|
||||
}
|
||||
|
||||
Transition transition = getTransition(container, from, to, isPush);
|
||||
final Transition transition = getTransition(container, from, to, isPush);
|
||||
transition.addListener(new TransitionListener() {
|
||||
@Override
|
||||
public void onTransitionStart(Transition transition) { }
|
||||
@@ -67,17 +85,53 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean removesFromViewOnPush() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+43
-34
@@ -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 transitionChangeHandler;
|
||||
private ControllerChangeHandler fallbackChangeHandler;
|
||||
private ControllerChangeHandler changeHandler;
|
||||
|
||||
public TransitionChangeHandlerCompat() { }
|
||||
|
||||
@@ -32,57 +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) {
|
||||
this.transitionChangeHandler = transitionChangeHandler;
|
||||
this.fallbackChangeHandler = 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) {
|
||||
transitionChangeHandler.performChange(container, from, to, isPush, changeListener);
|
||||
} else {
|
||||
fallbackChangeHandler.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, transitionChangeHandler.getClass().getName());
|
||||
bundle.putString(KEY_FALLBACK_HANDLER_CLASS, fallbackChangeHandler.getClass().getName());
|
||||
bundle.putString(KEY_CHANGE_HANDLER_CLASS, changeHandler.getClass().getName());
|
||||
|
||||
Bundle transitionBundle = new Bundle();
|
||||
transitionChangeHandler.saveToBundle(transitionBundle);
|
||||
bundle.putBundle(KEY_TRANSITION_HANDLER_STATE, transitionBundle);
|
||||
|
||||
Bundle fallbackBundle = new Bundle();
|
||||
fallbackChangeHandler.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);
|
||||
transitionChangeHandler = ClassUtils.newInstance(transitionClassName);
|
||||
String className = bundle.getString(KEY_CHANGE_HANDLER_CLASS);
|
||||
changeHandler = ClassUtils.newInstance(className);
|
||||
//noinspection ConstantConditions
|
||||
transitionChangeHandler.restoreFromBundle(bundle.getBundle(KEY_TRANSITION_HANDLER_STATE));
|
||||
|
||||
String fallbackClassName = bundle.getString(KEY_FALLBACK_HANDLER_CLASS);
|
||||
fallbackChangeHandler = ClassUtils.newInstance(fallbackClassName);
|
||||
//noinspection ConstantConditions
|
||||
fallbackChangeHandler.restoreFromBundle(bundle.getBundle(KEY_FALLBACK_HANDLER_STATE));
|
||||
changeHandler.restoreFromBundle(bundle.getBundle(KEY_HANDLER_STATE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removesFromViewOnPush() {
|
||||
return changeHandler.removesFromViewOnPush();
|
||||
}
|
||||
|
||||
@Override @NonNull
|
||||
public ControllerChangeHandler copy() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
return transitionChangeHandler.removesFromViewOnPush();
|
||||
return new TransitionChangeHandlerCompat((TransitionChangeHandler)changeHandler.copy(), null);
|
||||
} else {
|
||||
return fallbackChangeHandler.removesFromViewOnPush();
|
||||
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());
|
||||
|
||||
+121
-16
@@ -6,9 +6,13 @@ 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;
|
||||
@@ -27,6 +31,7 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
|
||||
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";
|
||||
@@ -34,9 +39,12 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
private Activity activity;
|
||||
private boolean hasRegisteredCallbacks;
|
||||
private boolean destroyed;
|
||||
private boolean attached;
|
||||
|
||||
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, ActivityHostedRouter> routerMap = new HashMap<>();
|
||||
|
||||
@@ -45,15 +53,20 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
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();
|
||||
@@ -63,7 +76,8 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
return lifecycleHandler;
|
||||
}
|
||||
|
||||
public Router getRouter(ViewGroup container, Bundle savedInstanceState) {
|
||||
@NonNull
|
||||
public Router getRouter(@NonNull ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
ActivityHostedRouter router = routerMap.get(getRouterHashKey(container));
|
||||
if (router == null) {
|
||||
router = new ActivityHostedRouter();
|
||||
@@ -83,24 +97,31 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
return router;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<Router> getRouters() {
|
||||
return new ArrayList<Router>(routerMap.values());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Activity getLifecycleActivity() {
|
||||
return activity;
|
||||
}
|
||||
|
||||
private static int getRouterHashKey(ViewGroup viewGroup) {
|
||||
private static int getRouterHashKey(@NonNull ViewGroup viewGroup) {
|
||||
return viewGroup.getId();
|
||||
}
|
||||
|
||||
private void registerActivityListener(Activity activity) {
|
||||
private void registerActivityListener(@NonNull Activity activity) {
|
||||
this.activity = activity;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,10 +131,13 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
StringSparseArrayParceler permissionParcel = savedInstanceState.getParcelable(KEY_PERMISSION_REQUEST_CODES);
|
||||
permissionRequestMap = permissionParcel != null ? permissionParcel.getStringSparseArray() : null;
|
||||
permissionRequestMap = permissionParcel != null ? permissionParcel.getStringSparseArray() : new SparseArray<String>();
|
||||
|
||||
StringSparseArrayParceler activityParcel = savedInstanceState.getParcelable(KEY_ACTIVITY_REQUEST_CODES);
|
||||
activityRequestMap = 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>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +147,7 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
|
||||
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
|
||||
@@ -131,6 +156,7 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
|
||||
if (activity != null) {
|
||||
activity.getApplication().unregisterActivityLifecycleCallbacks(this);
|
||||
activeLifecycleHandlers.remove(activity);
|
||||
destroyRouters();
|
||||
activity = null;
|
||||
}
|
||||
@@ -141,21 +167,39 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
for (ActivityHostedRouter router : routerMap.values()) {
|
||||
router.onContextAvailable();
|
||||
}
|
||||
}
|
||||
|
||||
private void destroyRouters() {
|
||||
if (!destroyed) {
|
||||
destroyed = true;
|
||||
@@ -232,11 +276,11 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
public void registerForActivityResult(String instanceId, int requestCode) {
|
||||
public void registerForActivityResult(@NonNull String instanceId, int requestCode) {
|
||||
activityRequestMap.put(requestCode, instanceId);
|
||||
}
|
||||
|
||||
public void unregisterForActivityResults(String instanceId) {
|
||||
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);
|
||||
@@ -244,26 +288,42 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
}
|
||||
}
|
||||
|
||||
public void startActivityForResult(String instanceId, Intent intent, int 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) {
|
||||
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) {
|
||||
permissionRequestMap.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 (this.activity == null && findInActivity(activity) == LifecycleHandler.this) {
|
||||
this.activity = activity;
|
||||
|
||||
for (ActivityHostedRouter router : routerMap.values()) {
|
||||
router.onContextAvailable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,5 +375,50 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
+8
-2
@@ -2,17 +2,18 @@ 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> stringSparseArray;
|
||||
|
||||
public StringSparseArrayParceler(SparseArray<String> stringSparseArray) {
|
||||
public StringSparseArrayParceler(@NonNull SparseArray<String> stringSparseArray) {
|
||||
this.stringSparseArray = stringSparseArray;
|
||||
}
|
||||
|
||||
private StringSparseArrayParceler(Parcel in) {
|
||||
private StringSparseArrayParceler(@NonNull Parcel in) {
|
||||
stringSparseArray = new SparseArray<>();
|
||||
|
||||
final int size = in.readInt();
|
||||
@@ -22,14 +23,17 @@ public class StringSparseArrayParceler implements Parcelable {
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public SparseArray<String> getStringSparseArray() {
|
||||
return stringSparseArray;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
final int size = stringSparseArray.size();
|
||||
|
||||
@@ -44,10 +48,12 @@ public class StringSparseArrayParceler implements Parcelable {
|
||||
}
|
||||
|
||||
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,9 +1,12 @@
|
||||
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 backstack;
|
||||
@@ -15,20 +18,20 @@ public class BackstackTests {
|
||||
|
||||
@Test
|
||||
public void testPush() {
|
||||
Assert.assertEquals(0, backstack.size());
|
||||
assertEquals(0, backstack.size());
|
||||
backstack.push(RouterTransaction.with(new TestController()));
|
||||
Assert.assertEquals(1, backstack.size());
|
||||
assertEquals(1, backstack.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPop() {
|
||||
backstack.push(RouterTransaction.with(new TestController()));
|
||||
backstack.push(RouterTransaction.with(new TestController()));
|
||||
Assert.assertEquals(2, backstack.size());
|
||||
assertEquals(2, backstack.size());
|
||||
backstack.pop();
|
||||
Assert.assertEquals(1, backstack.size());
|
||||
assertEquals(1, backstack.size());
|
||||
backstack.pop();
|
||||
Assert.assertEquals(0, backstack.size());
|
||||
assertEquals(0, backstack.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -37,13 +40,13 @@ public class BackstackTests {
|
||||
RouterTransaction transaction2 = RouterTransaction.with(new TestController());
|
||||
|
||||
backstack.push(transaction1);
|
||||
Assert.assertEquals(transaction1, backstack.peek());
|
||||
assertEquals(transaction1, backstack.peek());
|
||||
|
||||
backstack.push(transaction2);
|
||||
Assert.assertEquals(transaction2, backstack.peek());
|
||||
assertEquals(transaction2, backstack.peek());
|
||||
|
||||
backstack.pop();
|
||||
Assert.assertEquals(transaction1, backstack.peek());
|
||||
assertEquals(transaction1, backstack.peek());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -56,11 +59,11 @@ public class BackstackTests {
|
||||
backstack.push(transaction2);
|
||||
backstack.push(transaction3);
|
||||
|
||||
Assert.assertEquals(3, backstack.size());
|
||||
assertEquals(3, backstack.size());
|
||||
|
||||
backstack.popTo(transaction1);
|
||||
|
||||
Assert.assertEquals(1, backstack.size());
|
||||
Assert.assertEquals(transaction1, backstack.peek());
|
||||
assertEquals(1, backstack.size());
|
||||
assertEquals(transaction1, backstack.peek());
|
||||
}
|
||||
}
|
||||
|
||||
+9
-7
@@ -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
|
||||
@@ -21,17 +23,17 @@ public class ControllerChangeHandlerTests {
|
||||
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,556 +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.MockChangeHandler.ChangeHandlerListener;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.util.ActivityController;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(manifest = Config.NONE)
|
||||
public class ControllerLifecycleTests {
|
||||
|
||||
private ActivityController<TestActivity> activityController;
|
||||
private Router router;
|
||||
|
||||
private CallState currentCallState;
|
||||
|
||||
public void createActivityController(Bundle savedInstanceState) {
|
||||
activityController = Robolectric.buildActivity(TestActivity.class).create(savedInstanceState).start();
|
||||
|
||||
@IdRes int containerId = 4;
|
||||
FrameLayout routerContainer = new FrameLayout(activityController.get());
|
||||
routerContainer.setId(containerId);
|
||||
|
||||
router = Conductor.attachRouter(activityController.get(), routerContainer, savedInstanceState);
|
||||
if (!router.hasRootController()) {
|
||||
router.setRoot(RouterTransaction.with(new TestController()));
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
createActivityController(null);
|
||||
|
||||
currentCallState = new CallState();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNormalLifecycle() {
|
||||
TestController controller = new TestController();
|
||||
attachLifecycleListener(controller);
|
||||
|
||||
CallState expectedCallState = new CallState();
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
router.pushController(RouterTransaction.with(controller)
|
||||
.pushChangeHandler(getPushHandler(expectedCallState, controller))
|
||||
.popChangeHandler(getPopHandler(expectedCallState, controller)));
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
router.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);
|
||||
router.pushController(RouterTransaction.with(controller)
|
||||
.pushChangeHandler(getPushHandler(expectedCallState, controller)));
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityController.pause();
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityController.stop();
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityController.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);
|
||||
router.pushController(RouterTransaction.with(controller)
|
||||
.pushChangeHandler(getPushHandler(expectedCallState, controller))
|
||||
.tag("root"));
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityController.get().isChangingConfigurations = true;
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
activityController.saveInstanceState(bundle);
|
||||
|
||||
expectedCallState.saveViewStateCalls++;
|
||||
expectedCallState.saveInstanceStateCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityController.pause();
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityController.stop();
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityController.destroy();
|
||||
expectedCallState.detachCalls++;
|
||||
expectedCallState.destroyViewCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
createActivityController(bundle);
|
||||
controller = (TestController)router.getControllerWithTag("root");
|
||||
|
||||
expectedCallState.restoreInstanceStateCalls++;
|
||||
expectedCallState.restoreViewStateCalls++;
|
||||
expectedCallState.changeStartCalls++;
|
||||
expectedCallState.changeEndCalls++;
|
||||
expectedCallState.createViewCalls++;
|
||||
|
||||
// Lifecycle listener isn't attached during restore, grab the current views from the controller for this stuff...
|
||||
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;
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityController.resume();
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLifecycleWithActivityBackground() {
|
||||
TestController controller = new TestController();
|
||||
attachLifecycleListener(controller);
|
||||
|
||||
CallState expectedCallState = new CallState();
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
router.pushController(RouterTransaction.with(controller)
|
||||
.pushChangeHandler(getPushHandler(expectedCallState, controller)));
|
||||
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityController.pause();
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
activityController.saveInstanceState(bundle);
|
||||
|
||||
expectedCallState.saveInstanceStateCalls++;
|
||||
expectedCallState.saveViewStateCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
|
||||
activityController.resume();
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
});
|
||||
|
||||
router.pushController(RouterTransaction.with(testController)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
|
||||
router.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();
|
||||
router.pushController(RouterTransaction.with(parent)
|
||||
.pushChangeHandler(new MockChangeHandler()));
|
||||
|
||||
TestController child = new TestController();
|
||||
attachLifecycleListener(child);
|
||||
|
||||
CallState expectedCallState = new CallState();
|
||||
|
||||
assertCalls(expectedCallState, child);
|
||||
|
||||
Router childRouter = parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.VIEW_ID), null);
|
||||
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(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
|
||||
TestController child = new TestController();
|
||||
attachLifecycleListener(child);
|
||||
|
||||
CallState expectedCallState = new CallState();
|
||||
|
||||
assertCalls(expectedCallState, child);
|
||||
|
||||
Router childRouter = parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.VIEW_ID), null);
|
||||
childRouter
|
||||
.setRoot(RouterTransaction.with(child)
|
||||
.pushChangeHandler(getPushHandler(expectedCallState, child))
|
||||
.popChangeHandler(getPopHandler(expectedCallState, child)));
|
||||
|
||||
assertCalls(expectedCallState, child);
|
||||
|
||||
router.popCurrentController();
|
||||
|
||||
assertCalls(expectedCallState, child);
|
||||
}
|
||||
|
||||
private MockChangeHandler getPushHandler(final CallState expectedCallState, final TestController controller) {
|
||||
return new MockChangeHandler(new ChangeHandlerListener() {
|
||||
@Override
|
||||
void willStartChange() {
|
||||
expectedCallState.changeStartCalls++;
|
||||
expectedCallState.createViewCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
|
||||
@Override
|
||||
void didAttachOrDetach() {
|
||||
expectedCallState.attachCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
|
||||
@Override
|
||||
void didEndChange() {
|
||||
expectedCallState.changeEndCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private MockChangeHandler getPopHandler(final CallState expectedCallState, final TestController controller) {
|
||||
return new MockChangeHandler(new ChangeHandlerListener() {
|
||||
@Override
|
||||
void willStartChange() {
|
||||
expectedCallState.changeStartCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
|
||||
@Override
|
||||
void didAttachOrDetach() {
|
||||
expectedCallState.destroyViewCalls++;
|
||||
expectedCallState.detachCalls++;
|
||||
expectedCallState.destroyCalls++;
|
||||
assertCalls(expectedCallState, controller);
|
||||
}
|
||||
|
||||
@Override
|
||||
void didEndChange() {
|
||||
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, 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 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++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,37 +3,36 @@ package com.bluelinelabs.conductor;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.IdRes;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller.RetainViewMode;
|
||||
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> activityController;
|
||||
private ActivityProxy activityProxy;
|
||||
private Router router;
|
||||
|
||||
public void createActivityController(Bundle savedInstanceState) {
|
||||
activityController = Robolectric.buildActivity(TestActivity.class).create(savedInstanceState).start();
|
||||
|
||||
@IdRes int containerId = 4;
|
||||
FrameLayout routerContainer = new FrameLayout(activityController.get());
|
||||
routerContainer.setId(containerId);
|
||||
|
||||
router = Conductor.attachRouter(activityController.get(), routerContainer, savedInstanceState);
|
||||
activityProxy = new ActivityProxy().create(savedInstanceState).start().resume();
|
||||
router = Conductor.attachRouter(activityProxy.getActivity(), activityProxy.getView(), savedInstanceState);
|
||||
if (!router.hasRootController()) {
|
||||
router.setRoot(RouterTransaction.with(new TestController()));
|
||||
}
|
||||
@@ -47,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(router.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(router.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
|
||||
@@ -78,7 +78,6 @@ public class ControllerTests {
|
||||
CallState expectedCallState = new CallState(true);
|
||||
|
||||
router.pushController(RouterTransaction.with(controller));
|
||||
ViewUtils.setAttached(controller.getView(), true);
|
||||
|
||||
// Ensure that calling onActivityResult w/o requesting a result doesn't do anything
|
||||
router.onActivityResult(1, Activity.RESULT_OK, null);
|
||||
@@ -103,10 +102,8 @@ public class ControllerTests {
|
||||
TestController child = new TestController();
|
||||
|
||||
router.pushController(RouterTransaction.with(parent));
|
||||
ViewUtils.setAttached(parent.getView(), true);
|
||||
parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.VIEW_ID), null)
|
||||
parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.VIEW_ID))
|
||||
.setRoot(RouterTransaction.with(child));
|
||||
ViewUtils.setAttached(child.getView(), true);
|
||||
|
||||
CallState childExpectedCallState = new CallState(true);
|
||||
CallState parentExpectedCallState = new CallState(true);
|
||||
@@ -139,7 +136,6 @@ public class ControllerTests {
|
||||
CallState expectedCallState = new CallState(true);
|
||||
|
||||
router.pushController(RouterTransaction.with(controller));
|
||||
ViewUtils.setAttached(controller.getView(), true);
|
||||
|
||||
// Ensure that calling handleRequestedPermission w/o requesting a result doesn't do anything
|
||||
router.onRequestPermissionsResult("anotherId", 1, requestedPermissions, new int[] {1});
|
||||
@@ -163,10 +159,8 @@ public class ControllerTests {
|
||||
TestController child = new TestController();
|
||||
|
||||
router.pushController(RouterTransaction.with(parent));
|
||||
ViewUtils.setAttached(parent.getView(), true);
|
||||
parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.VIEW_ID), null)
|
||||
parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.VIEW_ID))
|
||||
.setRoot(RouterTransaction.with(child));
|
||||
ViewUtils.setAttached(child.getView(), true);
|
||||
|
||||
CallState childExpectedCallState = new CallState(true);
|
||||
CallState parentExpectedCallState = new CallState(true);
|
||||
@@ -193,7 +187,6 @@ public class ControllerTests {
|
||||
CallState expectedCallState = new CallState(true);
|
||||
|
||||
router.pushController(RouterTransaction.with(controller));
|
||||
ViewUtils.setAttached(controller.getView(), true);
|
||||
|
||||
// Ensure that calling onCreateOptionsMenu w/o declaring that we have one doesn't do anything
|
||||
router.onCreateOptionsMenu(null, null);
|
||||
@@ -230,10 +223,8 @@ public class ControllerTests {
|
||||
TestController child = new TestController();
|
||||
|
||||
router.pushController(RouterTransaction.with(parent));
|
||||
ViewUtils.setAttached(parent.getView(), true);
|
||||
parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.VIEW_ID), null)
|
||||
parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.VIEW_ID))
|
||||
.setRoot(RouterTransaction.with(child));
|
||||
ViewUtils.setAttached(child.getView(), true);
|
||||
|
||||
CallState childExpectedCallState = new CallState(true);
|
||||
CallState parentExpectedCallState = new CallState(true);
|
||||
@@ -280,47 +271,48 @@ public class ControllerTests {
|
||||
|
||||
router.pushController(RouterTransaction.with(parent));
|
||||
|
||||
Assert.assertEquals(0, parent.getChildRouters().size());
|
||||
Assert.assertNull(child1.getParentController());
|
||||
Assert.assertNull(child2.getParentController());
|
||||
assertEquals(0, parent.getChildRouters().size());
|
||||
assertNull(child1.getParentController());
|
||||
assertNull(child2.getParentController());
|
||||
|
||||
Router childRouter = parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.VIEW_ID), null);
|
||||
Router childRouter = parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouter.setPopsLastView(true);
|
||||
childRouter.setRoot(RouterTransaction.with(child1));
|
||||
|
||||
Assert.assertEquals(1, parent.getChildRouters().size());
|
||||
Assert.assertEquals(childRouter, parent.getChildRouters().get(0));
|
||||
Assert.assertEquals(1, childRouter.getBackstackSize());
|
||||
Assert.assertEquals(child1, childRouter.getControllers().get(0));
|
||||
Assert.assertEquals(parent, child1.getParentController());
|
||||
Assert.assertNull(child2.getParentController());
|
||||
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), null);
|
||||
childRouter = parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouter.pushController(RouterTransaction.with(child2));
|
||||
|
||||
Assert.assertEquals(1, parent.getChildRouters().size());
|
||||
Assert.assertEquals(childRouter, parent.getChildRouters().get(0));
|
||||
Assert.assertEquals(2, childRouter.getBackstackSize());
|
||||
Assert.assertEquals(child1, childRouter.getControllers().get(0));
|
||||
Assert.assertEquals(child2, childRouter.getControllers().get(1));
|
||||
Assert.assertEquals(parent, child1.getParentController());
|
||||
Assert.assertEquals(parent, child2.getParentController());
|
||||
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);
|
||||
|
||||
Assert.assertEquals(1, parent.getChildRouters().size());
|
||||
Assert.assertEquals(childRouter, parent.getChildRouters().get(0));
|
||||
Assert.assertEquals(1, childRouter.getBackstackSize());
|
||||
Assert.assertEquals(child1, childRouter.getControllers().get(0));
|
||||
Assert.assertEquals(parent, child1.getParentController());
|
||||
Assert.assertNull(child2.getParentController());
|
||||
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);
|
||||
|
||||
Assert.assertEquals(1, parent.getChildRouters().size());
|
||||
Assert.assertEquals(childRouter, parent.getChildRouters().get(0));
|
||||
Assert.assertEquals(0, childRouter.getBackstackSize());
|
||||
Assert.assertNull(child1.getParentController());
|
||||
Assert.assertNull(child2.getParentController());
|
||||
assertEquals(1, parent.getChildRouters().size());
|
||||
assertEquals(childRouter, parent.getChildRouters().get(0));
|
||||
assertEquals(0, childRouter.getBackstackSize());
|
||||
assertNull(child1.getParentController());
|
||||
assertNull(child2.getParentController());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -332,47 +324,89 @@ public class ControllerTests {
|
||||
|
||||
router.pushController(RouterTransaction.with(parent));
|
||||
|
||||
Assert.assertEquals(0, parent.getChildRouters().size());
|
||||
Assert.assertNull(child1.getParentController());
|
||||
Assert.assertNull(child2.getParentController());
|
||||
assertEquals(0, parent.getChildRouters().size());
|
||||
assertNull(child1.getParentController());
|
||||
assertNull(child2.getParentController());
|
||||
|
||||
Router childRouter1 = parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.CHILD_VIEW_ID_1), null);
|
||||
Router childRouter2 = parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.CHILD_VIEW_ID_2), null);
|
||||
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));
|
||||
|
||||
Assert.assertEquals(2, parent.getChildRouters().size());
|
||||
Assert.assertEquals(childRouter1, parent.getChildRouters().get(0));
|
||||
Assert.assertEquals(childRouter2, parent.getChildRouters().get(1));
|
||||
Assert.assertEquals(1, childRouter1.getBackstackSize());
|
||||
Assert.assertEquals(1, childRouter2.getBackstackSize());
|
||||
Assert.assertEquals(child1, childRouter1.getControllers().get(0));
|
||||
Assert.assertEquals(child2, childRouter2.getControllers().get(0));
|
||||
Assert.assertEquals(parent, child1.getParentController());
|
||||
Assert.assertEquals(parent, child2.getParentController());
|
||||
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);
|
||||
|
||||
Assert.assertEquals(1, parent.getChildRouters().size());
|
||||
Assert.assertEquals(childRouter1, parent.getChildRouters().get(0));
|
||||
Assert.assertEquals(1, childRouter1.getBackstackSize());
|
||||
Assert.assertEquals(0, childRouter2.getBackstackSize());
|
||||
Assert.assertEquals(child1, childRouter1.getControllers().get(0));
|
||||
Assert.assertEquals(parent, child1.getParentController());
|
||||
Assert.assertNull(child2.getParentController());
|
||||
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);
|
||||
|
||||
Assert.assertEquals(0, parent.getChildRouters().size());
|
||||
Assert.assertEquals(0, childRouter1.getBackstackSize());
|
||||
Assert.assertEquals(0, childRouter2.getBackstackSize());
|
||||
Assert.assertNull(child1.getParentController());
|
||||
Assert.assertNull(child2.getParentController());
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,11 +4,12 @@ import android.os.Bundle;
|
||||
|
||||
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
|
||||
@@ -22,10 +23,10 @@ public class ControllerTransactionTests {
|
||||
|
||||
RouterTransaction restoredTransaction = new RouterTransaction(bundle);
|
||||
|
||||
Assert.assertEquals(transaction.controller.getClass(), restoredTransaction.controller.getClass());
|
||||
Assert.assertEquals(transaction.pushChangeHandler().getClass(), restoredTransaction.pushChangeHandler().getClass());
|
||||
Assert.assertEquals(transaction.popChangeHandler().getClass(), restoredTransaction.popChangeHandler().getClass());
|
||||
Assert.assertEquals(transaction.tag(), restoredTransaction.tag());
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
public class MockChangeHandler extends ControllerChangeHandler {
|
||||
|
||||
static class ChangeHandlerListener {
|
||||
void willStartChange() { }
|
||||
void didAttachOrDetach() { }
|
||||
void didEndChange() { }
|
||||
}
|
||||
|
||||
final ChangeHandlerListener listener;
|
||||
|
||||
public MockChangeHandler() {
|
||||
this(new ChangeHandlerListener() { });
|
||||
}
|
||||
|
||||
public MockChangeHandler(@NonNull ChangeHandlerListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
|
||||
listener.willStartChange();
|
||||
|
||||
if (isPush) {
|
||||
container.addView(to);
|
||||
ViewUtils.setAttached(to, true);
|
||||
|
||||
listener.didAttachOrDetach();
|
||||
|
||||
if (from != null) {
|
||||
container.removeView(from);
|
||||
ViewUtils.setAttached(from, false);
|
||||
}
|
||||
} else {
|
||||
container.removeView(from);
|
||||
ViewUtils.setAttached(from, false);
|
||||
|
||||
listener.didAttachOrDetach();
|
||||
|
||||
if (to != null) {
|
||||
container.addView(to);
|
||||
ViewUtils.setAttached(to, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
changeListener.onChangeCompleted();
|
||||
listener.didEndChange();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,34 +1,31 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.IdRes;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import org.junit.Assert;
|
||||
import 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.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.util.ActivityController;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(manifest = Config.NONE)
|
||||
public class ReattachCaseTests {
|
||||
|
||||
private ActivityController<TestActivity> activityController;
|
||||
private ActivityProxy activityProxy;
|
||||
private Router router;
|
||||
|
||||
public void createActivityController(Bundle savedInstanceState) {
|
||||
activityController = Robolectric.buildActivity(TestActivity.class).create(savedInstanceState).start();
|
||||
|
||||
@IdRes int containerId = 4;
|
||||
FrameLayout routerContainer = new FrameLayout(activityController.get());
|
||||
routerContainer.setId(containerId);
|
||||
|
||||
router = Conductor.attachRouter(activityController.get(), routerContainer, savedInstanceState);
|
||||
activityProxy = new ActivityProxy().create(savedInstanceState).start().resume();
|
||||
router = Conductor.attachRouter(activityProxy.getActivity(), activityProxy.getView(), savedInstanceState);
|
||||
if (!router.hasRootController()) {
|
||||
router.setRoot(RouterTransaction.with(new TestController()));
|
||||
}
|
||||
@@ -45,69 +42,71 @@ public class ReattachCaseTests {
|
||||
final TestController controllerB = new TestController();
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerA)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
Assert.assertTrue(controllerA.isAttached());
|
||||
Assert.assertFalse(controllerB.isAttached());
|
||||
assertTrue(controllerA.isAttached());
|
||||
assertFalse(controllerB.isAttached());
|
||||
|
||||
sleepWakeDevice();
|
||||
|
||||
Assert.assertTrue(controllerA.isAttached());
|
||||
Assert.assertFalse(controllerB.isAttached());
|
||||
assertTrue(controllerA.isAttached());
|
||||
assertFalse(controllerB.isAttached());
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerB)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
assertFalse(controllerA.isAttached());
|
||||
assertTrue(controllerB.isAttached());
|
||||
|
||||
rotateDevice();
|
||||
activityProxy.rotate();
|
||||
router.rebindIfNeeded();
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
assertFalse(controllerA.isAttached());
|
||||
assertTrue(controllerB.isAttached());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildNeedsAttachOnPauseAndOrientation() {
|
||||
final TestController controllerA = new TestController();
|
||||
final TestController childController = new TestController();
|
||||
final TestController controllerB = new TestController();
|
||||
final Controller controllerA = new TestController();
|
||||
final Controller childController = new TestController();
|
||||
final Controller controllerB = new TestController();
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerA)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
Router childRouter = controllerA.getChildRouter((ViewGroup)controllerA.getView().findViewById(TestController.VIEW_ID), null);
|
||||
Router childRouter = controllerA.getChildRouter((ViewGroup)controllerA.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouter.pushController(RouterTransaction.with(childController)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
Assert.assertTrue(controllerA.isAttached());
|
||||
Assert.assertTrue(childController.isAttached());
|
||||
Assert.assertFalse(controllerB.isAttached());
|
||||
assertTrue(controllerA.isAttached());
|
||||
assertTrue(childController.isAttached());
|
||||
assertFalse(controllerB.isAttached());
|
||||
|
||||
sleepWakeDevice();
|
||||
|
||||
Assert.assertTrue(controllerA.isAttached());
|
||||
Assert.assertTrue(childController.isAttached());
|
||||
Assert.assertFalse(controllerB.isAttached());
|
||||
assertTrue(controllerA.isAttached());
|
||||
assertTrue(childController.isAttached());
|
||||
assertFalse(controllerB.isAttached());
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerB)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertFalse(childController.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
assertFalse(controllerA.isAttached());
|
||||
assertFalse(childController.isAttached());
|
||||
assertTrue(controllerB.isAttached());
|
||||
|
||||
rotateDevice();
|
||||
activityProxy.rotate();
|
||||
router.rebindIfNeeded();
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertFalse(childController.isAttached());
|
||||
Assert.assertTrue(childController.getNeedsAttach());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
assertFalse(controllerA.isAttached());
|
||||
assertFalse(childController.isAttached());
|
||||
assertTrue(childController.getNeedsAttach());
|
||||
assertTrue(controllerB.isAttached());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -117,44 +116,45 @@ public class ReattachCaseTests {
|
||||
final TestController childController = new TestController();
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerA)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
Assert.assertTrue(controllerA.isAttached());
|
||||
Assert.assertFalse(controllerB.isAttached());
|
||||
Assert.assertFalse(childController.isAttached());
|
||||
assertTrue(controllerA.isAttached());
|
||||
assertFalse(controllerB.isAttached());
|
||||
assertFalse(childController.isAttached());
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerB)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
Router childRouter = controllerB.getChildRouter((ViewGroup)controllerB.getView().findViewById(TestController.VIEW_ID), null);
|
||||
Router childRouter = controllerB.getChildRouter((ViewGroup)controllerB.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouter.setPopsLastView(true);
|
||||
childRouter.pushController(RouterTransaction.with(childController)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
Assert.assertTrue(childController.isAttached());
|
||||
assertFalse(controllerA.isAttached());
|
||||
assertTrue(controllerB.isAttached());
|
||||
assertTrue(childController.isAttached());
|
||||
|
||||
rotateDevice();
|
||||
activityProxy.rotate();
|
||||
router.rebindIfNeeded();
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
Assert.assertTrue(childController.isAttached());
|
||||
assertFalse(controllerA.isAttached());
|
||||
assertTrue(controllerB.isAttached());
|
||||
assertTrue(childController.isAttached());
|
||||
|
||||
router.handleBack();
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
Assert.assertFalse(childController.isAttached());
|
||||
assertFalse(controllerA.isAttached());
|
||||
assertTrue(controllerB.isAttached());
|
||||
assertFalse(childController.isAttached());
|
||||
|
||||
router.handleBack();
|
||||
|
||||
Assert.assertTrue(controllerA.isAttached());
|
||||
Assert.assertFalse(controllerB.isAttached());
|
||||
Assert.assertFalse(childController.isAttached());
|
||||
assertTrue(controllerA.isAttached());
|
||||
assertFalse(controllerB.isAttached());
|
||||
assertFalse(childController.isAttached());
|
||||
}
|
||||
|
||||
// Attempt to test https://github.com/bluelinelabs/Conductor/issues/86#issuecomment-231381271
|
||||
@@ -165,85 +165,76 @@ public class ReattachCaseTests {
|
||||
TestController childController = new TestController();
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerA)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
Assert.assertTrue(controllerA.isAttached());
|
||||
Assert.assertFalse(controllerB.isAttached());
|
||||
Assert.assertFalse(childController.isAttached());
|
||||
assertTrue(controllerA.isAttached());
|
||||
assertFalse(controllerB.isAttached());
|
||||
assertFalse(childController.isAttached());
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerB)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
Router childRouter = controllerB.getChildRouter((ViewGroup)controllerB.getView().findViewById(TestController.VIEW_ID), null);
|
||||
Router childRouter = controllerB.getChildRouter((ViewGroup)controllerB.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouter.setPopsLastView(true);
|
||||
childRouter.pushController(RouterTransaction.with(childController)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
Assert.assertTrue(childController.isAttached());
|
||||
assertFalse(controllerA.isAttached());
|
||||
assertTrue(controllerB.isAttached());
|
||||
assertTrue(childController.isAttached());
|
||||
|
||||
router.handleBack();
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
Assert.assertFalse(childController.isAttached());
|
||||
assertFalse(controllerA.isAttached());
|
||||
assertTrue(controllerB.isAttached());
|
||||
assertFalse(childController.isAttached());
|
||||
|
||||
childController = new TestController();
|
||||
childRouter.pushController(RouterTransaction.with(childController)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
Assert.assertTrue(childController.isAttached());
|
||||
assertFalse(controllerA.isAttached());
|
||||
assertTrue(controllerB.isAttached());
|
||||
assertTrue(childController.isAttached());
|
||||
|
||||
rotateDevice();
|
||||
activityProxy.rotate();
|
||||
router.rebindIfNeeded();
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
Assert.assertTrue(childController.isAttached());
|
||||
assertFalse(controllerA.isAttached());
|
||||
assertTrue(controllerB.isAttached());
|
||||
assertTrue(childController.isAttached());
|
||||
|
||||
router.handleBack();
|
||||
|
||||
childController = new TestController();
|
||||
childRouter.pushController(RouterTransaction.with(childController)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
Assert.assertTrue(childController.isAttached());
|
||||
assertFalse(controllerA.isAttached());
|
||||
assertTrue(controllerB.isAttached());
|
||||
assertTrue(childController.isAttached());
|
||||
|
||||
router.handleBack();
|
||||
|
||||
Assert.assertFalse(controllerA.isAttached());
|
||||
Assert.assertTrue(controllerB.isAttached());
|
||||
Assert.assertFalse(childController.isAttached());
|
||||
assertFalse(controllerA.isAttached());
|
||||
assertTrue(controllerB.isAttached());
|
||||
assertFalse(childController.isAttached());
|
||||
|
||||
router.handleBack();
|
||||
|
||||
Assert.assertTrue(controllerA.isAttached());
|
||||
Assert.assertFalse(controllerB.isAttached());
|
||||
Assert.assertFalse(childController.isAttached());
|
||||
assertTrue(controllerA.isAttached());
|
||||
assertFalse(controllerB.isAttached());
|
||||
assertFalse(childController.isAttached());
|
||||
}
|
||||
|
||||
private void sleepWakeDevice() {
|
||||
activityController.saveInstanceState(new Bundle()).pause();
|
||||
activityController.resume();
|
||||
}
|
||||
|
||||
private void rotateDevice() {
|
||||
@IdRes int containerId = 4;
|
||||
FrameLayout routerContainer = new FrameLayout(activityController.get());
|
||||
routerContainer.setId(containerId);
|
||||
|
||||
activityController.get().isChangingConfigurations = true;
|
||||
activityController.get().recreate();
|
||||
router.rebindIfNeeded();
|
||||
activityProxy.saveInstanceState(new Bundle()).pause();
|
||||
activityProxy.resume();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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,16 +1,27 @@
|
||||
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 {
|
||||
@@ -19,8 +30,8 @@ public class RouterTests {
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
Activity activity = Robolectric.buildActivity(TestActivity.class).create().get();
|
||||
router = Conductor.attachRouter(activity, new FrameLayout(activity), null);
|
||||
ActivityProxy activityProxy = new ActivityProxy().create(null).start().resume();
|
||||
router = Conductor.attachRouter(activityProxy.getActivity(), activityProxy.getView(), null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -29,13 +40,13 @@ public class RouterTests {
|
||||
|
||||
Controller rootController = new TestController();
|
||||
|
||||
Assert.assertFalse(router.hasRootController());
|
||||
assertFalse(router.hasRootController());
|
||||
|
||||
router.setRoot(RouterTransaction.with(rootController).tag(rootTag));
|
||||
|
||||
Assert.assertTrue(router.hasRootController());
|
||||
assertTrue(router.hasRootController());
|
||||
|
||||
Assert.assertEquals(rootController, router.getControllerWithTag(rootTag));
|
||||
assertEquals(rootController, router.getControllerWithTag(rootTag));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -49,8 +60,8 @@ public class RouterTests {
|
||||
router.setRoot(RouterTransaction.with(oldRootController).tag(oldRootTag));
|
||||
router.setRoot(RouterTransaction.with(newRootController).tag(newRootTag));
|
||||
|
||||
Assert.assertNull(router.getControllerWithTag(oldRootTag));
|
||||
Assert.assertEquals(newRootController, router.getControllerWithTag(newRootTag));
|
||||
assertNull(router.getControllerWithTag(oldRootTag));
|
||||
assertEquals(newRootController, router.getControllerWithTag(newRootTag));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -59,8 +70,8 @@ public class RouterTests {
|
||||
|
||||
router.pushController(RouterTransaction.with(controller));
|
||||
|
||||
Assert.assertEquals(controller, router.getControllerWithInstanceId(controller.getInstanceId()));
|
||||
Assert.assertNull(router.getControllerWithInstanceId("fake id"));
|
||||
assertEquals(controller, router.getControllerWithInstanceId(controller.getInstanceId()));
|
||||
assertNull(router.getControllerWithInstanceId("fake id"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -77,8 +88,8 @@ public class RouterTests {
|
||||
router.pushController(RouterTransaction.with(controller2)
|
||||
.tag(controller2Tag));
|
||||
|
||||
Assert.assertEquals(controller1, router.getControllerWithTag(controller1Tag));
|
||||
Assert.assertEquals(controller2, router.getControllerWithTag(controller2Tag));
|
||||
assertEquals(controller1, router.getControllerWithTag(controller1Tag));
|
||||
assertEquals(controller2, router.getControllerWithTag(controller2Tag));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -92,26 +103,26 @@ public class RouterTests {
|
||||
router.pushController(RouterTransaction.with(controller1)
|
||||
.tag(controller1Tag));
|
||||
|
||||
Assert.assertEquals(1, router.getBackstackSize());
|
||||
assertEquals(1, router.getBackstackSize());
|
||||
|
||||
router.pushController(RouterTransaction.with(controller2)
|
||||
.tag(controller2Tag));
|
||||
|
||||
Assert.assertEquals(2, router.getBackstackSize());
|
||||
assertEquals(2, router.getBackstackSize());
|
||||
|
||||
router.popCurrentController();
|
||||
|
||||
Assert.assertEquals(1, router.getBackstackSize());
|
||||
assertEquals(1, router.getBackstackSize());
|
||||
|
||||
Assert.assertEquals(controller1, router.getControllerWithTag(controller1Tag));
|
||||
Assert.assertNull(router.getControllerWithTag(controller2Tag));
|
||||
assertEquals(controller1, router.getControllerWithTag(controller1Tag));
|
||||
assertNull(router.getControllerWithTag(controller2Tag));
|
||||
|
||||
router.popCurrentController();
|
||||
|
||||
Assert.assertEquals(0, router.getBackstackSize());
|
||||
assertEquals(0, router.getBackstackSize());
|
||||
|
||||
Assert.assertNull(router.getControllerWithTag(controller1Tag));
|
||||
Assert.assertNull(router.getControllerWithTag(controller2Tag));
|
||||
assertNull(router.getControllerWithTag(controller1Tag));
|
||||
assertNull(router.getControllerWithTag(controller2Tag));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -140,11 +151,11 @@ public class RouterTests {
|
||||
|
||||
router.popToTag(controller2Tag);
|
||||
|
||||
Assert.assertEquals(2, router.getBackstackSize());
|
||||
Assert.assertEquals(controller1, router.getControllerWithTag(controller1Tag));
|
||||
Assert.assertEquals(controller2, router.getControllerWithTag(controller2Tag));
|
||||
Assert.assertNull(router.getControllerWithTag(controller3Tag));
|
||||
Assert.assertNull(router.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
|
||||
@@ -168,10 +179,256 @@ public class RouterTests {
|
||||
|
||||
router.popController(controller2);
|
||||
|
||||
Assert.assertEquals(2, router.getBackstackSize());
|
||||
Assert.assertEquals(controller1, router.getControllerWithTag(controller1Tag));
|
||||
Assert.assertNull(router.getControllerWithTag(controller2Tag));
|
||||
Assert.assertEquals(controller3, router.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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,34 +1,30 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.IdRes;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import org.junit.Assert;
|
||||
import 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.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.assertNull;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(manifest = Config.NONE)
|
||||
public class TargetControllerTests {
|
||||
|
||||
private ActivityController<TestActivity> activityController;
|
||||
private Router router;
|
||||
|
||||
public void createActivityController(Bundle savedInstanceState) {
|
||||
activityController = Robolectric.buildActivity(TestActivity.class).create(savedInstanceState).start();
|
||||
|
||||
@IdRes int containerId = 4;
|
||||
FrameLayout routerContainer = new FrameLayout(activityController.get());
|
||||
routerContainer.setId(containerId);
|
||||
|
||||
router = Conductor.attachRouter(activityController.get(), routerContainer, savedInstanceState);
|
||||
ActivityProxy activityProxy = new ActivityProxy().create(savedInstanceState).start().resume();
|
||||
router = Conductor.attachRouter(activityProxy.getActivity(), activityProxy.getView(), savedInstanceState);
|
||||
if (!router.hasRootController()) {
|
||||
router.setRoot(RouterTransaction.with(new TestController()));
|
||||
}
|
||||
@@ -44,21 +40,21 @@ public class TargetControllerTests {
|
||||
final TestController controllerA = new TestController();
|
||||
final TestController controllerB = new TestController();
|
||||
|
||||
Assert.assertNull(controllerA.getTargetController());
|
||||
Assert.assertNull(controllerB.getTargetController());
|
||||
assertNull(controllerA.getTargetController());
|
||||
assertNull(controllerB.getTargetController());
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerA)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
controllerB.setTargetController(controllerA);
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerB)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
Assert.assertNull(controllerA.getTargetController());
|
||||
Assert.assertEquals(controllerA, controllerB.getTargetController());
|
||||
assertNull(controllerA.getTargetController());
|
||||
assertEquals(controllerA, controllerB.getTargetController());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -66,22 +62,22 @@ public class TargetControllerTests {
|
||||
final TestController controllerA = new TestController();
|
||||
final TestController controllerB = new TestController();
|
||||
|
||||
Assert.assertNull(controllerA.getTargetController());
|
||||
Assert.assertNull(controllerB.getTargetController());
|
||||
assertNull(controllerA.getTargetController());
|
||||
assertNull(controllerB.getTargetController());
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerA)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
controllerB.setTargetController(controllerA);
|
||||
|
||||
Router childRouter = controllerA.getChildRouter((ViewGroup)controllerA.getView().findViewById(TestController.VIEW_ID), null);
|
||||
Router childRouter = controllerA.getChildRouter((ViewGroup)controllerA.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouter.pushController(RouterTransaction.with(controllerB)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
Assert.assertNull(controllerA.getTargetController());
|
||||
Assert.assertEquals(controllerA, controllerB.getTargetController());
|
||||
assertNull(controllerA.getTargetController());
|
||||
assertEquals(controllerA, controllerB.getTargetController());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -89,22 +85,22 @@ public class TargetControllerTests {
|
||||
final TestController controllerA = new TestController();
|
||||
final TestController controllerB = new TestController();
|
||||
|
||||
Assert.assertNull(controllerA.getTargetController());
|
||||
Assert.assertNull(controllerB.getTargetController());
|
||||
assertNull(controllerA.getTargetController());
|
||||
assertNull(controllerB.getTargetController());
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerA)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
controllerA.setTargetController(controllerB);
|
||||
|
||||
Router childRouter = controllerA.getChildRouter((ViewGroup)controllerA.getView().findViewById(TestController.VIEW_ID), null);
|
||||
Router childRouter = controllerA.getChildRouter((ViewGroup)controllerA.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouter.pushController(RouterTransaction.with(controllerB)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
Assert.assertNull(controllerB.getTargetController());
|
||||
Assert.assertEquals(controllerB, controllerA.getTargetController());
|
||||
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 {
|
||||
|
||||
public 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;
|
||||
}
|
||||
}
|
||||
+6
-1
@@ -1,14 +1,19 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
package com.bluelinelabs.conductor.util;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
public class TestActivity extends Activity {
|
||||
|
||||
public boolean isChangingConfigurations = false;
|
||||
public boolean isDestroying = false;
|
||||
|
||||
@Override
|
||||
public boolean isChangingConfigurations() {
|
||||
return isChangingConfigurations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDestroyed() {
|
||||
return isDestroying || super.isDestroyed();
|
||||
}
|
||||
}
|
||||
+31
-10
@@ -1,5 +1,6 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
package com.bluelinelabs.conductor.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.IdRes;
|
||||
@@ -11,6 +12,10 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
import com.bluelinelabs.conductor.ControllerChangeType;
|
||||
|
||||
public class TestController extends Controller {
|
||||
|
||||
@IdRes public static final int VIEW_ID = 2342;
|
||||
@@ -19,24 +24,21 @@ public class TestController extends Controller {
|
||||
|
||||
private static final String KEY_CALL_STATE = "TestController.currentCallState";
|
||||
|
||||
public CallState currentCallState;
|
||||
|
||||
public TestController() {
|
||||
currentCallState = new CallState();
|
||||
}
|
||||
public CallState currentCallState = new CallState(false);
|
||||
public ChangeHandlerHistory changeHandlerHistory = new ChangeHandlerHistory();
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
currentCallState.createViewCalls++;
|
||||
FrameLayout view = new FrameLayout(inflater.getContext());
|
||||
FrameLayout view = new AttachFakingFrameLayout(inflater.getContext());
|
||||
view.setId(VIEW_ID);
|
||||
|
||||
FrameLayout childContainer1 = new FrameLayout(inflater.getContext());
|
||||
FrameLayout childContainer1 = new AttachFakingFrameLayout(inflater.getContext());
|
||||
childContainer1.setId(CHILD_VIEW_ID_1);
|
||||
view.addView(childContainer1);
|
||||
|
||||
FrameLayout childContainer2 = new FrameLayout(inflater.getContext());
|
||||
FrameLayout childContainer2 = new AttachFakingFrameLayout(inflater.getContext());
|
||||
childContainer2.setId(CHILD_VIEW_ID_2);
|
||||
view.addView(childContainer2);
|
||||
|
||||
@@ -53,6 +55,25 @@ public class TestController extends Controller {
|
||||
protected void onChangeEnded(@NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
super.onChangeEnded(changeHandler, changeType);
|
||||
currentCallState.changeEndCalls++;
|
||||
|
||||
if (changeHandler instanceof MockChangeHandler) {
|
||||
MockChangeHandler mockHandler = (MockChangeHandler)changeHandler;
|
||||
changeHandlerHistory.addEntry(mockHandler.from, mockHandler.to, changeType.isPush, mockHandler);
|
||||
} else {
|
||||
changeHandlerHistory.isValidHistory = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onContextAvailable(@NonNull Context context) {
|
||||
super.onContextAvailable(context);
|
||||
currentCallState.contextAvailableCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onContextUnavailable() {
|
||||
super.onContextUnavailable();
|
||||
currentCallState.contextUnavailableCalls++;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -68,7 +89,7 @@ public class TestController extends Controller {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroyView(View view) {
|
||||
protected void onDestroyView(@NonNull View view) {
|
||||
super.onDestroyView(view);
|
||||
currentCallState.destroyViewCalls++;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.bluelinelabs.conductor.util;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.View.OnAttachStateChangeListener;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ViewUtils {
|
||||
|
||||
public static void reportAttached(View view, boolean attached) {
|
||||
reportAttached(view, attached, true);
|
||||
}
|
||||
|
||||
public static void reportAttached(View view, boolean attached, boolean propogateToChildren) {
|
||||
if (view instanceof AttachFakingFrameLayout) {
|
||||
((AttachFakingFrameLayout)view).setAttached(attached, false);
|
||||
}
|
||||
|
||||
List<OnAttachStateChangeListener> listeners = getAttachStateListeners(view);
|
||||
|
||||
// Add, then remove an OnAttachStateChangeListener to initialize the attachStateListeners variable inside a view
|
||||
if (listeners == null) {
|
||||
OnAttachStateChangeListener tmpListener = new OnAttachStateChangeListener() {
|
||||
@Override
|
||||
public void onViewAttachedToWindow(View v) { }
|
||||
|
||||
@Override
|
||||
public void onViewDetachedFromWindow(View v) { }
|
||||
};
|
||||
view.addOnAttachStateChangeListener(tmpListener);
|
||||
view.removeOnAttachStateChangeListener(tmpListener);
|
||||
listeners = getAttachStateListeners(view);
|
||||
}
|
||||
|
||||
for (OnAttachStateChangeListener listener : listeners) {
|
||||
if (attached) {
|
||||
listener.onViewAttachedToWindow(view);
|
||||
} else {
|
||||
listener.onViewDetachedFromWindow(view);
|
||||
}
|
||||
}
|
||||
|
||||
if (propogateToChildren && view instanceof ViewGroup) {
|
||||
ViewGroup viewGroup = (ViewGroup)view;
|
||||
int childCount = viewGroup.getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
reportAttached(viewGroup.getChildAt(i), attached, true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static List<OnAttachStateChangeListener> getAttachStateListeners(View view) {
|
||||
Object listenerInfo = ReflectionHelpers.callInstanceMethod(view, "getListenerInfo");
|
||||
return ReflectionHelpers.getField(listenerInfo, "mOnAttachStateChangeListeners");
|
||||
}
|
||||
|
||||
}
|
||||
Executable → Regular
+15
-21
@@ -1,22 +1,8 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'com.neenbedankt.android-apt'
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.2"
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
@@ -25,10 +11,11 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.bluelinelabs.conductor.demo"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 23
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 1
|
||||
versionName "1.0.0"
|
||||
vectorDrawables.useSupportLibrary true
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
@@ -37,18 +24,25 @@ android {
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
exclude 'META-INF/rxjava.properties'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile rootProject.ext.supportV4
|
||||
compile rootProject.ext.supportDesign
|
||||
|
||||
apt rootProject.ext.butterknifeCompiler
|
||||
annotationProcessor rootProject.ext.butterknifeCompiler
|
||||
compile rootProject.ext.butterknife
|
||||
compile rootProject.ext.picasso
|
||||
|
||||
compile project(':conductor-support')
|
||||
compile project(':conductor-rxlifecycle')
|
||||
compile project(':conductor-modules:support')
|
||||
compile project(':conductor-modules:rxlifecycle')
|
||||
compile project(':conductor-modules:rxlifecycle2')
|
||||
compile project(':conductor-modules:autodispose')
|
||||
compile project(':conductor-modules:arch-components-lifecycle')
|
||||
|
||||
debugCompile rootProject.ext.leakCanary
|
||||
releaseCompile rootProject.ext.leakCanaryNoOp
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user