Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2ce8c0a45d | |||
| 77ad6b4512 | |||
| efbdf913bd | |||
| dfb01389f7 | |||
| e390261b53 | |||
| 27f5275172 | |||
| d15f2b68ab | |||
| 44ed19858b | |||
| e7c195d910 | |||
| 23a4dbbb60 | |||
| 0ac81767dd | |||
| 54cdc51557 | |||
| 09ce640d9c | |||
| 01df673a34 | |||
| 553bae0be5 |
+4
-2
@@ -3,9 +3,11 @@ language: android
|
||||
android:
|
||||
components:
|
||||
- tools
|
||||
- build-tools-23.0.2
|
||||
- android-23
|
||||
- build-tools-24.0.3
|
||||
- android-25
|
||||
- extra-android-m2repository
|
||||
licenses:
|
||||
- '.+'
|
||||
|
||||
script:
|
||||
- ./gradlew test
|
||||
|
||||
@@ -20,22 +20,26 @@ Conductor is architecture-agnostic and does not try to force any design decision
|
||||
## Installation
|
||||
|
||||
```gradle
|
||||
compile 'com.bluelinelabs:conductor:2.0.5'
|
||||
compile 'com.bluelinelabs:conductor:2.0.6'
|
||||
|
||||
// If you want the components that go along with
|
||||
// Android's support libraries (currently just a PagerAdapter):
|
||||
compile 'com.bluelinelabs:conductor-support:2.0.5'
|
||||
compile 'com.bluelinelabs:conductor-support:2.0.6'
|
||||
|
||||
// If you want RxJava/RxAndroid lifecycle support:
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:2.0.5'
|
||||
// If you want RxJava lifecycle support:
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:2.0.6'
|
||||
|
||||
// If you want RxJava2 lifecycle support:
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle2:2.0.6'
|
||||
```
|
||||
|
||||
SNAPSHOT:
|
||||
|
||||
```gradle
|
||||
compile 'com.bluelinelabs:conductor:2.0.6-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor-support:2.0.6-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:2.0.6-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor:2.0.7-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor-support:2.0.7-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle:2.0.7-SNAPSHOT'
|
||||
compile 'com.bluelinelabs:conductor-rxlifecycle2:2.0.7-SNAPSHOT'
|
||||
```
|
||||
|
||||
You also have to add the url to the snapshot repository:
|
||||
|
||||
+1
-5
@@ -10,12 +10,8 @@ buildscript {
|
||||
allprojects {
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
+22
-53
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
-55
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+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;
|
||||
}
|
||||
}
|
||||
@@ -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,7 +22,6 @@ android {
|
||||
|
||||
dependencies {
|
||||
compile rootProject.ext.rxJava
|
||||
compile rootProject.ext.rxAndroid
|
||||
compile rootProject.ext.rxLifecycle
|
||||
compile rootProject.ext.rxLifecycleAndroid
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -18,12 +18,13 @@ import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnAttachStateChangeListener;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.Router.OnControllerPushedListener;
|
||||
import com.bluelinelabs.conductor.internal.ClassUtils;
|
||||
import com.bluelinelabs.conductor.internal.RouterRequiringFunc;
|
||||
import com.bluelinelabs.conductor.internal.ViewAttachHandler;
|
||||
import com.bluelinelabs.conductor.internal.ViewAttachHandler.ViewAttachListener;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Constructor;
|
||||
@@ -80,7 +81,7 @@ public abstract class Controller {
|
||||
private ControllerChangeHandler overriddenPushHandler;
|
||||
private ControllerChangeHandler overriddenPopHandler;
|
||||
private RetainViewMode retainViewMode = RetainViewMode.RELEASE_DETACH;
|
||||
private OnAttachStateChangeListener onAttachStateChangeListener;
|
||||
private ViewAttachHandler viewAttachHandler;
|
||||
private final List<ControllerHostedRouter> childRouters = new ArrayList<>();
|
||||
private final List<LifecycleListener> lifecycleListeners = new ArrayList<>();
|
||||
private final ArrayList<String> requestedPermissions = new ArrayList<>();
|
||||
@@ -229,7 +230,7 @@ public abstract class Controller {
|
||||
|
||||
public final void removeChildRouter(@NonNull Router childRouter) {
|
||||
if ((childRouter instanceof ControllerHostedRouter) && childRouters.remove(childRouter)) {
|
||||
childRouter.destroy();
|
||||
childRouter.destroy(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -751,6 +752,7 @@ public abstract class Controller {
|
||||
attach(view);
|
||||
} else if (attached) {
|
||||
needsAttach = false;
|
||||
hasSavedViewState = false;
|
||||
}
|
||||
|
||||
onActivityResumed(activity);
|
||||
@@ -846,7 +848,7 @@ public abstract class Controller {
|
||||
|
||||
onDestroyView(view);
|
||||
|
||||
view.removeOnAttachStateChangeListener(onAttachStateChangeListener);
|
||||
viewAttachHandler.unregisterAttachListener(view);
|
||||
viewIsAttached = false;
|
||||
|
||||
if (isBeingDestroyed) {
|
||||
@@ -893,9 +895,9 @@ public abstract class Controller {
|
||||
|
||||
restoreViewState(view);
|
||||
|
||||
onAttachStateChangeListener = new OnAttachStateChangeListener() {
|
||||
viewAttachHandler = new ViewAttachHandler(new ViewAttachListener() {
|
||||
@Override
|
||||
public void onViewAttachedToWindow(View v) {
|
||||
public void onAttached(View v) {
|
||||
if (v == view) {
|
||||
viewIsAttached = true;
|
||||
viewWasDetached = false;
|
||||
@@ -904,7 +906,7 @@ public abstract class Controller {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewDetachedFromWindow(View v) {
|
||||
public void onDetached(View v) {
|
||||
viewIsAttached = false;
|
||||
viewWasDetached = true;
|
||||
|
||||
@@ -912,9 +914,8 @@ public abstract class Controller {
|
||||
detach(v, false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
view.addOnAttachStateChangeListener(onAttachStateChangeListener);
|
||||
});
|
||||
viewAttachHandler.listenForAttach(view);
|
||||
} else if (retainViewMode == RetainViewMode.RETAIN_DETACH) {
|
||||
restoreChildControllerHosts();
|
||||
}
|
||||
@@ -968,7 +969,7 @@ public abstract class Controller {
|
||||
}
|
||||
|
||||
for (ControllerHostedRouter childRouter : childRouters) {
|
||||
childRouter.destroy();
|
||||
childRouter.destroy(false);
|
||||
}
|
||||
|
||||
if (!attached) {
|
||||
@@ -981,7 +982,7 @@ public abstract class Controller {
|
||||
private void saveViewState(@NonNull View view) {
|
||||
hasSavedViewState = true;
|
||||
|
||||
viewState = new Bundle();
|
||||
viewState = new Bundle(getClass().getClassLoader());
|
||||
|
||||
SparseArray<Parcelable> hierarchyState = new SparseArray<>();
|
||||
view.saveHierarchyState(hierarchyState);
|
||||
@@ -1062,6 +1063,10 @@ public abstract class Controller {
|
||||
|
||||
private void restoreInstanceState(@NonNull Bundle savedInstanceState) {
|
||||
viewState = savedInstanceState.getBundle(KEY_VIEW_STATE);
|
||||
if (viewState != null) {
|
||||
viewState.setClassLoader(getClass().getClassLoader());
|
||||
}
|
||||
|
||||
instanceId = savedInstanceState.getString(KEY_INSTANCE_ID);
|
||||
targetInstanceId = savedInstanceState.getString(KEY_TARGET_INSTANCE_ID);
|
||||
requestedPermissions.addAll(savedInstanceState.getStringArrayList(KEY_REQUESTED_PERMISSIONS));
|
||||
|
||||
@@ -28,6 +28,7 @@ public abstract class ControllerChangeHandler {
|
||||
private static final Map<String, ControllerChangeHandler> inProgressPushHandlers = new HashMap<>();
|
||||
|
||||
private boolean forceRemoveViewOnPush;
|
||||
private boolean hasBeenUsed;
|
||||
|
||||
/**
|
||||
* Responsible for swapping Views from one Controller to another.
|
||||
@@ -73,6 +74,26 @@ 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();
|
||||
@@ -85,11 +106,6 @@ public abstract class ControllerChangeHandler {
|
||||
return bundle;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
final ControllerChangeHandler copy() {
|
||||
return fromBundle(toBundle());
|
||||
}
|
||||
|
||||
private void ensureDefaultConstructor() {
|
||||
try {
|
||||
getClass().getConstructor();
|
||||
@@ -134,8 +150,20 @@ public abstract class ControllerChangeHandler {
|
||||
}
|
||||
|
||||
public static void executeChange(@Nullable final Controller to, @Nullable final Controller from, final boolean isPush, @Nullable final ViewGroup container, @Nullable final ControllerChangeHandler inHandler, @NonNull final List<ControllerChangeListener> listeners) {
|
||||
if (isPush && to != null && to.isDestroyed()) {
|
||||
throw new IllegalStateException("Trying to push a controller that has already been destroyed. (" + to.getClass().getSimpleName() + ")");
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -72,9 +72,9 @@ class ControllerHostedRouter extends Router {
|
||||
}
|
||||
|
||||
@Override
|
||||
void destroy() {
|
||||
void destroy(boolean popViews) {
|
||||
setDetachFrozen(false);
|
||||
super.destroy();
|
||||
super.destroy(popViews);
|
||||
}
|
||||
|
||||
@Override @Nullable
|
||||
|
||||
@@ -172,7 +172,7 @@ public abstract class Router {
|
||||
final boolean newHandlerRemovesViews = handler == null || handler.removesFromViewOnPush();
|
||||
if (!oldHandlerRemovedViews && newHandlerRemovesViews) {
|
||||
for (RouterTransaction visibleTransaction : getVisibleTransactions(backstack.iterator())) {
|
||||
performControllerChange(null, visibleTransaction.controller, true, handler != null ? handler.copy() : new SimpleSwapChangeHandler());
|
||||
performControllerChange(null, visibleTransaction.controller, true, handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,11 +185,11 @@ public abstract class Router {
|
||||
performControllerChange(transaction.pushChangeHandler(handler), topTransaction, true);
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
void destroy(boolean popViews) {
|
||||
popsLastView = true;
|
||||
List<RouterTransaction> poppedControllers = backstack.popAll();
|
||||
|
||||
if (poppedControllers.size() > 0) {
|
||||
if (popViews && poppedControllers.size() > 0) {
|
||||
trackDestroyingControllers(poppedControllers);
|
||||
|
||||
performControllerChange(null, poppedControllers.get(0).controller, false, poppedControllers.get(0).popChangeHandler());
|
||||
@@ -368,22 +368,19 @@ public abstract class Router {
|
||||
}
|
||||
|
||||
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);
|
||||
performControllerChange(newVisibleTransactions.get(0).controller, rootController, true, changeHandler);
|
||||
|
||||
for (int i = oldVisibleTransactions.size() - 1; i > 0; i--) {
|
||||
RouterTransaction transaction = oldVisibleTransactions.get(i);
|
||||
ControllerChangeHandler localHandler = handler.copy();
|
||||
ControllerChangeHandler localHandler = changeHandler != null ? changeHandler.copy() : new SimpleSwapChangeHandler();
|
||||
localHandler.setForceRemoveViewOnPush(true);
|
||||
performControllerChange(null, transaction.controller, true, localHandler);
|
||||
}
|
||||
|
||||
for (int i = 1; i < newVisibleTransactions.size(); i++) {
|
||||
RouterTransaction transaction = newVisibleTransactions.get(i);
|
||||
handler = transaction.pushChangeHandler() != null ? transaction.pushChangeHandler().copy() : new SimpleSwapChangeHandler();
|
||||
performControllerChange(transaction.controller, newVisibleTransactions.get(i - 1).controller, true, handler);
|
||||
performControllerChange(transaction.controller, newVisibleTransactions.get(i - 1).controller, true, transaction.pushChangeHandler());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -631,7 +628,7 @@ public abstract class Router {
|
||||
} else if (from != null) {
|
||||
changeHandler = from.popChangeHandler();
|
||||
} else {
|
||||
changeHandler = new SimpleSwapChangeHandler();
|
||||
changeHandler = null;
|
||||
}
|
||||
|
||||
Controller toController = to != null ? to.controller : null;
|
||||
|
||||
+7
@@ -9,6 +9,8 @@ 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.
|
||||
*/
|
||||
@@ -20,4 +22,9 @@ public class AutoTransitionChangeHandler extends TransitionChangeHandler {
|
||||
return new AutoTransition();
|
||||
}
|
||||
|
||||
@Override @NonNull
|
||||
public ControllerChangeHandler copy() {
|
||||
return new AutoTransitionChangeHandler();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+8
@@ -8,6 +8,8 @@ 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
|
||||
*/
|
||||
@@ -45,4 +47,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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+8
@@ -8,6 +8,8 @@ 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.
|
||||
*/
|
||||
@@ -54,4 +56,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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+10
@@ -102,4 +102,14 @@ public class SimpleSwapChangeHandler extends ControllerChangeHandler implements
|
||||
|
||||
@Override
|
||||
public void onViewDetachedFromWindow(@NonNull View v) { }
|
||||
|
||||
@Override @NonNull
|
||||
public ControllerChangeHandler copy() {
|
||||
return new SimpleSwapChangeHandler(removesFromViewOnPush());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReusable() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -81,4 +81,5 @@ public abstract class TransitionChangeHandler extends ControllerChangeHandler {
|
||||
public final boolean removesFromViewOnPush() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+9
@@ -69,4 +69,13 @@ public class TransitionChangeHandlerCompat extends ControllerChangeHandler {
|
||||
return changeHandler.removesFromViewOnPush();
|
||||
}
|
||||
|
||||
@Override @NonNull
|
||||
public ControllerChangeHandler copy() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
return new TransitionChangeHandlerCompat((TransitionChangeHandler)changeHandler.copy(), null);
|
||||
} else {
|
||||
return new TransitionChangeHandlerCompat(null, changeHandler.copy());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+7
@@ -8,6 +8,8 @@ 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;
|
||||
|
||||
@@ -49,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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+10
@@ -14,4 +14,14 @@ public class NoOpControllerChangeHandler extends ControllerChangeHandler {
|
||||
changeListener.onChangeCompleted();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ControllerChangeHandler copy() {
|
||||
return new NoOpControllerChangeHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReusable() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package com.bluelinelabs.conductor.internal;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.View.OnAttachStateChangeListener;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
public class ViewAttachHandler {
|
||||
|
||||
public interface ViewAttachListener {
|
||||
void onAttached(View view);
|
||||
void onDetached(View view);
|
||||
}
|
||||
|
||||
private interface ChildAttachListener {
|
||||
void onAttached();
|
||||
}
|
||||
|
||||
private ViewAttachListener attachListener;
|
||||
private OnAttachStateChangeListener rootOnAttachStateChangeListener = new OnAttachStateChangeListener() {
|
||||
boolean rootAttached = false;
|
||||
boolean childrenAttached = false;
|
||||
|
||||
@Override
|
||||
public void onViewAttachedToWindow(final View v) {
|
||||
if (rootAttached) {
|
||||
return;
|
||||
}
|
||||
|
||||
rootAttached = true;
|
||||
listenForDeepestChildAttach(v, new ChildAttachListener() {
|
||||
@Override
|
||||
public void onAttached() {
|
||||
childrenAttached = true;
|
||||
attachListener.onAttached(v);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewDetachedFromWindow(View v) {
|
||||
rootAttached = false;
|
||||
if (childrenAttached) {
|
||||
childrenAttached = false;
|
||||
attachListener.onDetached(v);
|
||||
}
|
||||
}
|
||||
};
|
||||
private OnAttachStateChangeListener childOnAttachStateChangeListener;
|
||||
|
||||
public ViewAttachHandler(ViewAttachListener attachListener) {
|
||||
this.attachListener = attachListener;
|
||||
}
|
||||
|
||||
public void listenForAttach(final View view) {
|
||||
view.addOnAttachStateChangeListener(rootOnAttachStateChangeListener);
|
||||
}
|
||||
|
||||
public void unregisterAttachListener(View view) {
|
||||
view.removeOnAttachStateChangeListener(rootOnAttachStateChangeListener);
|
||||
|
||||
if (view instanceof ViewGroup) {
|
||||
findDeepestChild((ViewGroup)view).removeOnAttachStateChangeListener(childOnAttachStateChangeListener);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -383,8 +383,8 @@ public class ControllerLifecycleTests {
|
||||
});
|
||||
|
||||
router.pushController(RouterTransaction.with(testController)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
router.popController(testController);
|
||||
|
||||
@@ -399,7 +399,7 @@ public class ControllerLifecycleTests {
|
||||
public void testChildLifecycle() {
|
||||
Controller parent = new TestController();
|
||||
router.pushController(RouterTransaction.with(parent)
|
||||
.pushChangeHandler(new MockChangeHandler()));
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
TestController child = new TestController();
|
||||
attachLifecycleListener(child);
|
||||
@@ -425,8 +425,8 @@ public class ControllerLifecycleTests {
|
||||
public void testChildLifecycle2() {
|
||||
Controller parent = new TestController();
|
||||
router.pushController(RouterTransaction.with(parent)
|
||||
.pushChangeHandler(new MockChangeHandler())
|
||||
.popChangeHandler(new MockChangeHandler()));
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
TestController child = new TestController();
|
||||
attachLifecycleListener(child);
|
||||
@@ -445,11 +445,15 @@ public class ControllerLifecycleTests {
|
||||
|
||||
router.popCurrentController();
|
||||
|
||||
expectedCallState.detachCalls++;
|
||||
expectedCallState.destroyViewCalls++;
|
||||
expectedCallState.destroyCalls++;
|
||||
|
||||
assertCalls(expectedCallState, child);
|
||||
}
|
||||
|
||||
private MockChangeHandler getPushHandler(final CallState expectedCallState, final TestController controller) {
|
||||
return new MockChangeHandler(new ChangeHandlerListener() {
|
||||
return MockChangeHandler.listeningChangeHandler(new ChangeHandlerListener() {
|
||||
@Override
|
||||
void willStartChange() {
|
||||
expectedCallState.changeStartCalls++;
|
||||
@@ -472,7 +476,7 @@ public class ControllerLifecycleTests {
|
||||
}
|
||||
|
||||
private MockChangeHandler getPopHandler(final CallState expectedCallState, final TestController controller) {
|
||||
return new MockChangeHandler(new ChangeHandlerListener() {
|
||||
return MockChangeHandler.listeningChangeHandler(new ChangeHandlerListener() {
|
||||
@Override
|
||||
void willStartChange() {
|
||||
expectedCallState.changeStartCalls++;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.View;
|
||||
@@ -19,19 +18,23 @@ public class MockChangeHandler extends ControllerChangeHandler {
|
||||
final ChangeHandlerListener listener;
|
||||
boolean removesFromViewOnPush;
|
||||
|
||||
public static MockChangeHandler defaultHandler() {
|
||||
return new MockChangeHandler(true, null);
|
||||
}
|
||||
|
||||
public static MockChangeHandler noRemoveViewOnPushHandler() {
|
||||
return new MockChangeHandler(false, null);
|
||||
}
|
||||
|
||||
public static MockChangeHandler listeningChangeHandler(@NonNull ChangeHandlerListener listener) {
|
||||
return new MockChangeHandler(true , listener);
|
||||
}
|
||||
|
||||
public MockChangeHandler() {
|
||||
this(true, null);
|
||||
listener = null;
|
||||
}
|
||||
|
||||
public MockChangeHandler(boolean removesViewOnPush) {
|
||||
this(removesViewOnPush, null);
|
||||
}
|
||||
|
||||
public MockChangeHandler(@NonNull ChangeHandlerListener listener) {
|
||||
this(true, listener);
|
||||
}
|
||||
|
||||
public MockChangeHandler(boolean removesFromViewOnPush, ChangeHandlerListener listener) {
|
||||
private MockChangeHandler(boolean removesFromViewOnPush, ChangeHandlerListener listener) {
|
||||
this.removesFromViewOnPush = removesFromViewOnPush;
|
||||
|
||||
if (listener == null) {
|
||||
@@ -82,4 +85,15 @@ public class MockChangeHandler extends ControllerChangeHandler {
|
||||
super.restoreFromBundle(bundle);
|
||||
removesFromViewOnPush = bundle.getBoolean(KEY_REMOVES_FROM_VIEW_ON_PUSH);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ControllerChangeHandler copy() {
|
||||
return new MockChangeHandler(removesFromViewOnPush, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReusable() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,8 +36,8 @@ 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());
|
||||
@@ -48,8 +48,8 @@ public class ReattachCaseTests {
|
||||
Assert.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());
|
||||
@@ -68,13 +68,13 @@ 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()));
|
||||
|
||||
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());
|
||||
@@ -87,8 +87,8 @@ public class ReattachCaseTests {
|
||||
Assert.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());
|
||||
@@ -110,22 +110,22 @@ 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());
|
||||
|
||||
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));
|
||||
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());
|
||||
@@ -159,22 +159,22 @@ 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());
|
||||
|
||||
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));
|
||||
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());
|
||||
@@ -188,8 +188,8 @@ public class ReattachCaseTests {
|
||||
|
||||
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());
|
||||
@@ -206,8 +206,8 @@ public class ReattachCaseTests {
|
||||
|
||||
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());
|
||||
|
||||
@@ -222,7 +222,7 @@ public class RouterTests {
|
||||
@Test
|
||||
public void testNewSetBackstackWithNoRemoveViewOnPush() {
|
||||
RouterTransaction oldRootTransaction = RouterTransaction.with(new TestController());
|
||||
RouterTransaction oldTopTransaction = RouterTransaction.with(new TestController()).pushChangeHandler(new MockChangeHandler(false));
|
||||
RouterTransaction oldTopTransaction = RouterTransaction.with(new TestController()).pushChangeHandler(MockChangeHandler.noRemoveViewOnPushHandler());
|
||||
|
||||
router.setRoot(oldRootTransaction);
|
||||
router.pushController(oldTopTransaction);
|
||||
@@ -232,8 +232,8 @@ public class RouterTests {
|
||||
Assert.assertTrue(oldTopTransaction.controller.isAttached());
|
||||
|
||||
RouterTransaction rootTransaction = RouterTransaction.with(new TestController());
|
||||
RouterTransaction middleTransaction = RouterTransaction.with(new TestController()).pushChangeHandler(new MockChangeHandler(false));
|
||||
RouterTransaction topTransaction = RouterTransaction.with(new TestController()).pushChangeHandler(new MockChangeHandler(false));
|
||||
RouterTransaction middleTransaction = RouterTransaction.with(new TestController()).pushChangeHandler(MockChangeHandler.noRemoveViewOnPushHandler());
|
||||
RouterTransaction topTransaction = RouterTransaction.with(new TestController()).pushChangeHandler(MockChangeHandler.noRemoveViewOnPushHandler());
|
||||
|
||||
List<RouterTransaction> backstack = new ArrayList<>();
|
||||
backstack.add(rootTransaction);
|
||||
@@ -286,7 +286,7 @@ public class RouterTests {
|
||||
@Test
|
||||
public void testReplaceTopControllerWithNoRemoveViewOnPush() {
|
||||
RouterTransaction rootTransaction = RouterTransaction.with(new TestController());
|
||||
RouterTransaction topTransaction = RouterTransaction.with(new TestController()).pushChangeHandler(new MockChangeHandler(false));
|
||||
RouterTransaction topTransaction = RouterTransaction.with(new TestController()).pushChangeHandler(MockChangeHandler.noRemoveViewOnPushHandler());
|
||||
|
||||
List<RouterTransaction> backstack = new ArrayList<>();
|
||||
backstack.add(rootTransaction);
|
||||
@@ -303,7 +303,7 @@ public class RouterTests {
|
||||
Assert.assertEquals(rootTransaction, fetchedBackstack.get(0));
|
||||
Assert.assertEquals(topTransaction, fetchedBackstack.get(1));
|
||||
|
||||
RouterTransaction newTopTransaction = RouterTransaction.with(new TestController()).pushChangeHandler(new MockChangeHandler(false));
|
||||
RouterTransaction newTopTransaction = RouterTransaction.with(new TestController()).pushChangeHandler(MockChangeHandler.noRemoveViewOnPushHandler());
|
||||
router.replaceTopController(newTopTransaction);
|
||||
newTopTransaction.pushChangeHandler().completeImmediately();
|
||||
|
||||
|
||||
@@ -38,14 +38,14 @@ public class TargetControllerTests {
|
||||
Assert.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());
|
||||
@@ -60,15 +60,15 @@ public class TargetControllerTests {
|
||||
Assert.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));
|
||||
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());
|
||||
@@ -83,15 +83,15 @@ public class TargetControllerTests {
|
||||
Assert.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));
|
||||
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());
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
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 org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
@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);
|
||||
|
||||
Assert.assertEquals(0, viewAttachListener.attaches);
|
||||
Assert.assertEquals(0, viewAttachListener.detaches);
|
||||
|
||||
ViewUtils.reportAttached(view, true);
|
||||
Assert.assertEquals(1, viewAttachListener.attaches);
|
||||
Assert.assertEquals(0, viewAttachListener.detaches);
|
||||
|
||||
ViewUtils.reportAttached(view, true);
|
||||
Assert.assertEquals(1, viewAttachListener.attaches);
|
||||
Assert.assertEquals(0, viewAttachListener.detaches);
|
||||
|
||||
ViewUtils.reportAttached(view, false);
|
||||
Assert.assertEquals(1, viewAttachListener.attaches);
|
||||
Assert.assertEquals(1, viewAttachListener.detaches);
|
||||
|
||||
ViewUtils.reportAttached(view, false);
|
||||
Assert.assertEquals(1, viewAttachListener.attaches);
|
||||
Assert.assertEquals(1, viewAttachListener.detaches);
|
||||
|
||||
ViewUtils.reportAttached(view, true);
|
||||
Assert.assertEquals(2, viewAttachListener.attaches);
|
||||
Assert.assertEquals(1, viewAttachListener.detaches);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleViewGroupAttachDetach() {
|
||||
View view = new LinearLayout(activity);
|
||||
viewAttachHandler.listenForAttach(view);
|
||||
|
||||
Assert.assertEquals(0, viewAttachListener.attaches);
|
||||
Assert.assertEquals(0, viewAttachListener.detaches);
|
||||
|
||||
ViewUtils.reportAttached(view, true);
|
||||
Assert.assertEquals(1, viewAttachListener.attaches);
|
||||
Assert.assertEquals(0, viewAttachListener.detaches);
|
||||
|
||||
ViewUtils.reportAttached(view, true);
|
||||
Assert.assertEquals(1, viewAttachListener.attaches);
|
||||
Assert.assertEquals(0, viewAttachListener.detaches);
|
||||
|
||||
ViewUtils.reportAttached(view, false);
|
||||
Assert.assertEquals(1, viewAttachListener.attaches);
|
||||
Assert.assertEquals(1, viewAttachListener.detaches);
|
||||
|
||||
ViewUtils.reportAttached(view, false);
|
||||
Assert.assertEquals(1, viewAttachListener.attaches);
|
||||
Assert.assertEquals(1, viewAttachListener.detaches);
|
||||
|
||||
ViewUtils.reportAttached(view, true);
|
||||
Assert.assertEquals(2, viewAttachListener.attaches);
|
||||
Assert.assertEquals(1, viewAttachListener.detaches);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNestedViewGroupAttachDetach() {
|
||||
ViewGroup view = new LinearLayout(activity);
|
||||
View child = new LinearLayout(activity);
|
||||
view.addView(child);
|
||||
viewAttachHandler.listenForAttach(view);
|
||||
|
||||
Assert.assertEquals(0, viewAttachListener.attaches);
|
||||
Assert.assertEquals(0, viewAttachListener.detaches);
|
||||
|
||||
ViewUtils.reportAttached(view, true, false);
|
||||
Assert.assertEquals(0, viewAttachListener.attaches);
|
||||
Assert.assertEquals(0, viewAttachListener.detaches);
|
||||
|
||||
ViewUtils.reportAttached(child, true, false);
|
||||
Assert.assertEquals(1, viewAttachListener.attaches);
|
||||
Assert.assertEquals(0, viewAttachListener.detaches);
|
||||
|
||||
ViewUtils.reportAttached(view, true, false);
|
||||
ViewUtils.reportAttached(child, true, false);
|
||||
Assert.assertEquals(1, viewAttachListener.attaches);
|
||||
Assert.assertEquals(0, viewAttachListener.detaches);
|
||||
|
||||
ViewUtils.reportAttached(view, false, false);
|
||||
Assert.assertEquals(1, viewAttachListener.attaches);
|
||||
Assert.assertEquals(1, viewAttachListener.detaches);
|
||||
|
||||
ViewUtils.reportAttached(view, false, false);
|
||||
Assert.assertEquals(1, viewAttachListener.attaches);
|
||||
Assert.assertEquals(1, viewAttachListener.detaches);
|
||||
|
||||
ViewUtils.reportAttached(view, true, false);
|
||||
Assert.assertEquals(1, viewAttachListener.attaches);
|
||||
Assert.assertEquals(1, viewAttachListener.detaches);
|
||||
|
||||
ViewUtils.reportAttached(child, true, false);
|
||||
Assert.assertEquals(2, viewAttachListener.attaches);
|
||||
Assert.assertEquals(1, viewAttachListener.detaches);
|
||||
}
|
||||
|
||||
private static class CountingViewAttachListener implements ViewAttachListener {
|
||||
int attaches;
|
||||
int detaches;
|
||||
|
||||
@Override
|
||||
public void onAttached(View view) {
|
||||
attaches++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetached(View view) {
|
||||
detaches++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.bluelinelabs.conductor;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.View.OnAttachStateChangeListener;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
@@ -10,6 +11,10 @@ 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);
|
||||
}
|
||||
@@ -38,6 +43,14 @@ public class ViewUtils {
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
+5
-4
@@ -11,11 +11,12 @@ apply plugin: 'com.android.application'
|
||||
apply plugin: 'com.neenbedankt.android-apt'
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.2"
|
||||
compileSdkVersion 25
|
||||
buildToolsVersion '24.0.3'
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
abortOnError true
|
||||
ignore 'UnusedResources'
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
@@ -26,7 +27,7 @@ android {
|
||||
defaultConfig {
|
||||
applicationId "com.bluelinelabs.conductor.demo"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 23
|
||||
targetSdkVersion 25
|
||||
versionCode 1
|
||||
versionName "1.0.0"
|
||||
}
|
||||
|
||||
+13
-11
@@ -50,17 +50,19 @@ public class ParentController extends BaseController {
|
||||
childController.addLifecycleListener(new LifecycleListener() {
|
||||
@Override
|
||||
public void onChangeEnd(@NonNull Controller controller, @NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
if (changeType == ControllerChangeType.PUSH_ENTER && !hasShownAll) {
|
||||
if (index < NUMBER_OF_CHILDREN - 1) {
|
||||
addChild(index + 1);
|
||||
} else {
|
||||
hasShownAll = true;
|
||||
}
|
||||
} else if (changeType == ControllerChangeType.POP_EXIT) {
|
||||
if (index > 0) {
|
||||
removeChild(index - 1);
|
||||
} else {
|
||||
getRouter().popController(ParentController.this);
|
||||
if (!isBeingDestroyed()) {
|
||||
if (changeType == ControllerChangeType.PUSH_ENTER && !hasShownAll) {
|
||||
if (index < NUMBER_OF_CHILDREN - 1) {
|
||||
addChild(index + 1);
|
||||
} else {
|
||||
hasShownAll = true;
|
||||
}
|
||||
} else if (changeType == ControllerChangeType.POP_EXIT) {
|
||||
if (index > 0) {
|
||||
removeChild(index - 1);
|
||||
} else {
|
||||
getRouter().popController(ParentController.this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.bluelinelabs.conductor.demo.widget;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.support.v4.view.NestedScrollingParent;
|
||||
import android.support.v4.view.animation.FastOutSlowInInterpolator;
|
||||
@@ -32,6 +33,7 @@ import java.util.List;
|
||||
* Applies an elasticity factor to reduce movement as you approach the given dismiss distance.
|
||||
* Optionally also scales down content during drag.
|
||||
*/
|
||||
@TargetApi(21)
|
||||
public class ElasticDragDismissFrameLayout extends FrameLayout implements NestedScrollingParent {
|
||||
|
||||
public static abstract class ElasticDragDismissCallback {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="horizontal"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal"
|
||||
tools:ignore="InconsistentLayout">
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
android:textSize="22sp"
|
||||
android:padding="16dp"
|
||||
android:text="@string/drag_to_dismiss"
|
||||
android:clickable="true"
|
||||
/>
|
||||
|
||||
<android.support.v4.widget.NestedScrollView
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
@@ -13,6 +14,7 @@
|
||||
android:elevation="6dp"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
|
||||
tools:targetApi="lollipop"
|
||||
/>
|
||||
|
||||
<android.support.v4.view.ViewPager
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/transition_root"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
@@ -19,6 +20,7 @@
|
||||
android:layout_marginTop="96dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:transitionName="@string/transition_tag_title"
|
||||
tools:targetApi="lollipop"
|
||||
/>
|
||||
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
@@ -31,6 +33,7 @@
|
||||
app:elevation="0dp"
|
||||
app:pressedTranslationZ="0dp"
|
||||
android:transitionName="@string/transition_tag_dot"
|
||||
tools:targetApi="lollipop"
|
||||
/>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/transition_root"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
@@ -13,6 +14,7 @@
|
||||
android:layout_marginTop="24dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:transitionName="@string/transition_tag_title"
|
||||
tools:targetApi="lollipop"
|
||||
/>
|
||||
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
@@ -24,6 +26,7 @@
|
||||
app:elevation="0dp"
|
||||
app:pressedTranslationZ="0dp"
|
||||
android:transitionName="@string/transition_tag_dot"
|
||||
tools:targetApi="lollipop"
|
||||
/>
|
||||
|
||||
</FrameLayout>
|
||||
+36
-23
@@ -1,36 +1,49 @@
|
||||
ext {
|
||||
minSdkVersion = 16
|
||||
compileSdkVersion = 23
|
||||
targetSdkVersion = 23
|
||||
buildToolsVersion = '23.0.2'
|
||||
compileSdkVersion = 25
|
||||
targetSdkVersion = 25
|
||||
buildToolsVersion = '24.0.3'
|
||||
|
||||
supportV4 = 'com.android.support:support-v4:23.1.1'
|
||||
supportDesign = 'com.android.support:design:23.1.1'
|
||||
supportAnnotations = 'com.android.support:support-annotations:23.1.1'
|
||||
supportAppCompat = 'com.android.support:appcompat-v7:23.1.1'
|
||||
supportVersion = '23.1.1'
|
||||
butterknifeVersion = '8.0.1'
|
||||
picassoVersion = '2.5.2'
|
||||
leakCanaryVersion = '1.4'
|
||||
rxJavaVersion = '1.2.0'
|
||||
rxJava2Version = '2.0.1'
|
||||
rxLifecycleVersion = '0.8.0'
|
||||
rxLifecycle2Version = '2.0'
|
||||
junitVersion = '4.11'
|
||||
roboelectricVersion = '3.0'
|
||||
lintVersion = '25.2.0'
|
||||
|
||||
butterknife = 'com.jakewharton:butterknife:8.0.1'
|
||||
butterknifeCompiler = 'com.jakewharton:butterknife-compiler:8.0.1'
|
||||
supportV4 = "com.android.support:support-v4:$supportVersion"
|
||||
supportDesign = "com.android.support:design:$supportVersion"
|
||||
supportAnnotations = "com.android.support:support-annotations:$supportVersion"
|
||||
supportAppCompat = "com.android.support:appcompat-v7:$supportVersion"
|
||||
|
||||
picasso = 'com.squareup.picasso:picasso:2.5.2'
|
||||
butterknife = "com.jakewharton:butterknife:$butterknifeVersion"
|
||||
butterknifeCompiler = "com.jakewharton:butterknife-compiler:$butterknifeVersion"
|
||||
|
||||
leakCanary = 'com.squareup.leakcanary:leakcanary-android:1.4'
|
||||
leakCanaryNoOp = 'com.squareup.leakcanary:leakcanary-android-no-op:1.4'
|
||||
picasso = "com.squareup.picasso:picasso:$picassoVersion"
|
||||
|
||||
rxJava = 'io.reactivex:rxjava:1.2.0'
|
||||
rxJava2 = "io.reactivex.rxjava2:rxjava:2.0.1"
|
||||
leakCanary = "com.squareup.leakcanary:leakcanary-android:$leakCanaryVersion"
|
||||
leakCanaryNoOp = "com.squareup.leakcanary:leakcanary-android-no-op:$leakCanaryVersion"
|
||||
|
||||
rxAndroid = 'io.reactivex:rxandroid:1.2.1'
|
||||
rxJava = "io.reactivex:rxjava:$rxJavaVersion"
|
||||
rxJava2 = "io.reactivex.rxjava2:rxjava:$rxJava2Version"
|
||||
|
||||
rxLifecycle = 'com.trello:rxlifecycle:0.8.0'
|
||||
rxLifecycleAndroid = 'com.trello:rxlifecycle-android:0.8.0'
|
||||
rxLifecycle = "com.trello:rxlifecycle:$rxLifecycleVersion"
|
||||
rxLifecycleAndroid = "com.trello:rxlifecycle-android:$rxLifecycleVersion"
|
||||
|
||||
rxLifecycle2 = "com.trello.rxlifecycle2:rxlifecycle:2.0"
|
||||
rxLifecycleAndroid2 = "com.trello.rxlifecycle2:rxlifecycle-android:2.0"
|
||||
rxLifecycle2 = "com.trello.rxlifecycle2:rxlifecycle:$rxLifecycle2Version"
|
||||
rxLifecycleAndroid2 = "com.trello.rxlifecycle2:rxlifecycle-android:$rxLifecycle2Version"
|
||||
|
||||
junit = 'junit:junit:4.11'
|
||||
roboelectric = 'org.robolectric:robolectric:3.0'
|
||||
junit = "junit:junit:$junitVersion"
|
||||
roboelectric = "org.robolectric:robolectric:$roboelectricVersion"
|
||||
|
||||
lintapi = "com.android.tools.lint:lint-api:$lintVersion"
|
||||
lintchecks = "com.android.tools.lint:lint-checks:$lintVersion"
|
||||
lint = "com.android.tools.lint:lint:$lintVersion"
|
||||
lintTests = "com.android.tools.lint:lint-tests:$lintVersion"
|
||||
|
||||
lintapi = 'com.android.tools.lint:lint-api:24.5.0'
|
||||
lintchecks = 'com.android.tools.lint:lint-checks:24.5.0'
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
VERSION_NAME=2.0.6-SNAPSHOT
|
||||
VERSION_NAME=2.0.7-SNAPSHOT
|
||||
VERSION_CODE=2
|
||||
GROUP=com.bluelinelabs
|
||||
|
||||
|
||||
Reference in New Issue
Block a user