Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8a8622c261 | |||
| 6820aa7d6a | |||
| 9ce27e4dee | |||
| 3c8ad0a833 | |||
| a720ac57e8 | |||
| 7d6901389b | |||
| e54e88bf0d | |||
| 010117603c | |||
| cd11ac9d6b | |||
| e78347709b | |||
| 341debc5b9 | |||
| 2346e48154 | |||
| 4174e12958 | |||
| c0abed0813 | |||
| 6fdb1d6ed3 | |||
| 3334b8e21f | |||
| 240424dc63 | |||
| f768e9ab00 | |||
| 4c89124683 | |||
| e0bbd48935 | |||
| 76074d1e3d | |||
| 623ed03df8 | |||
| 28b8810e56 | |||
| 5c397404ce | |||
| 3b81a962b1 | |||
| 4f3662bbf6 | |||
| c53b8d9d49 | |||
| c8f6e552e4 | |||
| c1cc2e2bca | |||
| 9ace20b88b | |||
| d8606498b0 | |||
| f35768393d | |||
| bbb4e8c066 |
@@ -1,26 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Deploy a jar, source jar, and javadoc jar to Sonatype's snapshot repo.
|
||||
#
|
||||
# Adapted from https://coderwall.com/p/9b_lfq and
|
||||
# http://benlimmer.com/2013/12/26/automatically-publish-javadoc-to-gh-pages-with-travis-ci/
|
||||
|
||||
SLUG="bluelinelabs/Conductor"
|
||||
JDK="oraclejdk8"
|
||||
BRANCH="develop"
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$TRAVIS_REPO_SLUG" != "$SLUG" ]; then
|
||||
echo "Skipping snapshot deployment: wrong repository. Expected '$SLUG' but was '$TRAVIS_REPO_SLUG'."
|
||||
elif [ "$TRAVIS_JDK_VERSION" != "$JDK" ]; then
|
||||
echo "Skipping snapshot deployment: wrong JDK. Expected '$JDK' but was '$TRAVIS_JDK_VERSION'."
|
||||
elif [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
|
||||
echo "Skipping snapshot deployment: was pull request."
|
||||
elif [ "$TRAVIS_BRANCH" != "$BRANCH" ]; then
|
||||
echo "Skipping snapshot deployment: wrong branch. Expected '$BRANCH' but was '$TRAVIS_BRANCH'."
|
||||
else
|
||||
echo "Deploying snapshot..."
|
||||
./gradlew clean uploadArchives
|
||||
echo "Snapshot deployed!"
|
||||
fi
|
||||
@@ -0,0 +1,36 @@
|
||||
name: Test & Publish
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: set up JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
- name: Test with Gradle
|
||||
run: ./gradlew test
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/develop'
|
||||
needs: test
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: set up JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
- name: Test with Gradle
|
||||
run: ./gradlew clean uploadArchives
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_SONATYPE_NEXUS_USERNAME: ${{ secrets.SONATYPE_NEXUS_USERNAME }}
|
||||
ORG_GRADLE_PROJECT_SONATYPE_NEXUS_PASSWORD: ${{ secrets.SONATYPE_NEXUS_PASSWORD }}
|
||||
+1
-3
@@ -35,6 +35,4 @@ pom.xml.*
|
||||
local.properties
|
||||
|
||||
*.prefs
|
||||
|
||||
# The keystore file
|
||||
app/spothero-release.keystore
|
||||
.DS_Store
|
||||
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
language: android
|
||||
|
||||
android:
|
||||
components:
|
||||
- tools
|
||||
- build-tools-28.0.3
|
||||
- android-28
|
||||
- extra-android-m2repository
|
||||
licenses:
|
||||
- '.+'
|
||||
|
||||
script:
|
||||
- ./gradlew test
|
||||
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- master
|
||||
|
||||
after_success:
|
||||
- .buildscript/deploy_snapshot.sh
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.gradle
|
||||
|
||||
sudo: false
|
||||
|
||||
env:
|
||||
global:
|
||||
- secure: Px0uj7aMtKUVZVBnoEjHrEHh4cwO2qKJrHHPvwBiDqhwLBaWQIVXYOy0njaYc5o8p96Fv7bMu7NZx/72vMu1+nmTKxgzrMIxvMW6kczBvJUpv6xd1NuC34x+5Xq5gBNOFvb8JarWStcKgIvnFqvBsRUeI1Hsz7Olb8HF+fEo1kShuP18ezSsBkXruw8JuGiU9x0kq4YhZ7vRvFnc3sJX2FL6heuvQsnUWrolUOsKRadNkCibo+Euuls7ExvbbAXN4LEO3rs0G2eBUBbi2wXvTMG9symtItEHTMPO7K+aQfNQnHsY91TYveH/IJM1u5p6OldsUSOUigzpDmpVYW94aLuJaYqc6Ibq3eUws+tv2didOHXZW5zOCFjldDFBIQFPA3fih/wK/JP0taQ0uIu1+2eifvuERarMkGsYlOFe5tJd10ipi+kK5vNxoRwS9kGv5WwP5fVPX2m5XbD2y1LnugCCcAumfNX7NyNBIRqTy7BP34O3EMLZpMxjwSLnUBnYd4V/0LEvoVmbYmrLhWwpojBJmdwe2QknrPuvRErxNujRA1uEVupbU5A6RW1BmrtzSahJYoROI+ayG7UTOSbFN8+DorER1SUXsrOFlawak8yWsoi6OIynTKucrFM3YcBdJ3Su5AIhfBAOASZa6CUa6sn6Zo8mHmDVGKeckvXnLCU=
|
||||
- secure: 3Dj6roVTO2a9Z8lwlTGzJJ+QGeyIYSuQ/Z6YsYnW3wB9Mw36uWqw9rOmMNIGjjyAlER8bKgalHr90Pus87oaNaIlbEyvq+L+I0FVAwognViFjo/a2apCcq81THoKjT1l0sgGRzvLNDynQe1q2L0tOu21wtBhMLb7FKQYB9+oD3H+rcU63xD2tv3ToJz+j+dccZ9nrtgk0MQ1xAeMtEb4tdq3flKATKhIuDkp9chaDxx/ZGwyhdE0UP29UUyP5Np1QvpFAlAJIZloZPvde2e05fwxTh4rwUCetkfJknDK6WrGiq97WGRXJpfORNuwGn7jxDCtgxcAm9nGF8qmI/v78BhjJ857CfJBTLGv4QI0RszlhXyezJqqRYjCn9S4yx8UAOixVJfJfFNHLqD4MFn41b7j8J3HDJPxNt0t/qYhUMrgrZVosNOUqhwCyQTKDqtrpvmSUbhHpk2+fxZF1GEL5N540rA0OjLmFUUEDSvRQVaa/waeqXrRefOhsXIx20dHs93C6XDffjnxVMKlqtPab2MlHV/23QCuSa6eW+lKyOZ6ZkWLwwDsLbnuD0WJfJpiL1xaTdPJNIDb6kB/Bno4V1O4xrYncEOaA4Vr4amO8le9Q33/QeNrUZSXs/eye5t0f02wCzubuiEDVEeoR/qj5+5CNWv3AWr/f8AgXp80Pzw=
|
||||
@@ -13,29 +13,32 @@ A small, yet full-featured framework that allows building View-based Android app
|
||||
:twisted_rightwards_arrows: | Beautiful transitions between views
|
||||
:floppy_disk: | State persistence
|
||||
:phone: | Callbacks for onActivityResult, onRequestPermissionsResult, etc
|
||||
:european_post_office: | MVP / MVVM / VIPER / MVC ready
|
||||
:european_post_office: | MVP / MVVM / MVI / VIPER / MVC ready
|
||||
|
||||
Conductor is architecture-agnostic and does not try to force any design decisions on the developer. We here at BlueLine Labs tend to use either MVP or MVVM, but it would work equally well with standard MVC or whatever else you want to throw at it.
|
||||
|
||||
## Installation
|
||||
|
||||
```gradle
|
||||
implementation 'com.bluelinelabs:conductor:3.0.0-rc4'
|
||||
implementation 'com.bluelinelabs:conductor:3.0.0'
|
||||
|
||||
// AndroidX Transition change handlers:
|
||||
implementation 'com.bluelinelabs:conductor-androidx-transition:3.0.0-rc4'
|
||||
implementation 'com.bluelinelabs:conductor-androidx-transition:3.0.0'
|
||||
|
||||
// ViewPager PagerAdapter:
|
||||
implementation 'com.bluelinelabs:conductor-viewpager:3.0.0-rc4'
|
||||
implementation 'com.bluelinelabs:conductor-viewpager:3.0.0'
|
||||
|
||||
// ViewPager2 Adapter:
|
||||
implementation 'com.bluelinelabs:conductor-viewpager2:3.0.0'
|
||||
|
||||
// RxJava2 lifecycle support:
|
||||
implementation 'com.bluelinelabs:conductor-rxlifecycle2:3.0.0-rc4'
|
||||
implementation 'com.bluelinelabs:conductor-rxlifecycle2:3.0.0'
|
||||
|
||||
// RxJava2 Autodispose support:
|
||||
implementation 'com.bluelinelabs:conductor-autodispose:3.0.0-rc4'
|
||||
implementation 'com.bluelinelabs:conductor-autodispose:3.0.0'
|
||||
|
||||
// Lifecycle-aware Controllers (architecture components):
|
||||
implementation 'com.bluelinelabs:conductor-archlifecycle:3.0.0-rc4'
|
||||
implementation 'com.bluelinelabs:conductor-archlifecycle:3.0.0'
|
||||
```
|
||||
|
||||
**SNAPSHOT**
|
||||
|
||||
+6
-22
@@ -1,41 +1,25 @@
|
||||
buildscript {
|
||||
|
||||
apply from: rootProject.file('dependencies.gradle')
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.6.1'
|
||||
classpath "com.android.tools.build:gradle:$agpVersion"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
||||
classpath "com.vanniktech:gradle-maven-publish-plugin:$mvnPublishVersion"
|
||||
classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokkaVersion"
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven { url 'https://maven.google.com' }
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
plugins.withType(com.android.build.gradle.BasePlugin).configureEach { plugin ->
|
||||
plugin.extension.compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach { task ->
|
||||
task.sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
task.targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
tasks.withType(org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile).configureEach { task ->
|
||||
task.kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply from: rootProject.file('dependencies.gradle')
|
||||
|
||||
+1
-8
@@ -14,7 +14,6 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.uast.UClass;
|
||||
import org.jetbrains.uast.UElement;
|
||||
import org.jetbrains.uast.UMethod;
|
||||
import org.jetbrains.uast.UTypeReferenceExpression;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -48,13 +47,7 @@ public final class ControllerChangeHandlerIssueDetector extends Detector impleme
|
||||
return;
|
||||
}
|
||||
|
||||
boolean hasSuperType = false;
|
||||
for (UTypeReferenceExpression superType : node.getUastSuperTypes()) {
|
||||
if (CLASS_NAME.equals(superType.asRenderString())) {
|
||||
hasSuperType = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
final boolean hasSuperType = evaluator.extendsClass(node.getPsi(), CLASS_NAME, true);
|
||||
if (!hasSuperType) {
|
||||
return;
|
||||
}
|
||||
|
||||
+2
-9
@@ -16,7 +16,6 @@ import org.jetbrains.uast.UClass;
|
||||
import org.jetbrains.uast.UElement;
|
||||
import org.jetbrains.uast.UMethod;
|
||||
import org.jetbrains.uast.UParameter;
|
||||
import org.jetbrains.uast.UTypeReferenceExpression;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -35,7 +34,7 @@ public final class ControllerIssueDetector extends Detector implements Detector.
|
||||
|
||||
@Override
|
||||
public List<Class<? extends UElement>> getApplicableUastTypes() {
|
||||
return Collections.<Class<? extends UElement>>singletonList(UClass.class);
|
||||
return Collections.singletonList(UClass.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -49,13 +48,7 @@ public final class ControllerIssueDetector extends Detector implements Detector.
|
||||
return;
|
||||
}
|
||||
|
||||
boolean hasSuperType = false;
|
||||
for (UTypeReferenceExpression superType : node.getUastSuperTypes()) {
|
||||
if (CLASS_NAME.equals(superType.asRenderString())) {
|
||||
hasSuperType = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
final boolean hasSuperType = evaluator.extendsClass(node.getPsi(), CLASS_NAME, true);
|
||||
if (!hasSuperType) {
|
||||
return;
|
||||
}
|
||||
|
||||
+38
-13
@@ -1,24 +1,27 @@
|
||||
package com.bluelinelabs.conductor.lint;
|
||||
|
||||
import com.android.tools.lint.checks.infrastructure.LintDetectorTest;
|
||||
|
||||
import org.intellij.lang.annotations.Language;
|
||||
import org.junit.Test;
|
||||
|
||||
import static com.android.tools.lint.checks.infrastructure.TestFiles.java;
|
||||
import static com.android.tools.lint.checks.infrastructure.TestLintTask.lint;
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public class ControllerChangeHandlerDetectorTest {
|
||||
|
||||
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"
|
||||
+ "public class SampleHandler extends com.bluelinelabs.conductor.ControllerChangeHandler {\n"
|
||||
+ "^\n"
|
||||
+ "1 errors, 0 warnings\n";
|
||||
|
||||
private final LintDetectorTest.TestFile controllerChangeHandlerStub = java(
|
||||
"package com.bluelinelabs.conductor;\n"
|
||||
+ "abstract class ControllerChangeHandler {}"
|
||||
);
|
||||
|
||||
@Test
|
||||
public void testWithNoConstructor() {
|
||||
@Language("JAVA") String source = ""
|
||||
@@ -27,7 +30,7 @@ public class ControllerChangeHandlerDetectorTest {
|
||||
+ "}";
|
||||
|
||||
lint()
|
||||
.files(java(source))
|
||||
.files(controllerChangeHandlerStub, java(source))
|
||||
.issues(ControllerIssueDetector.ISSUE, ControllerChangeHandlerIssueDetector.ISSUE)
|
||||
.run()
|
||||
.expectClean();
|
||||
@@ -42,7 +45,7 @@ public class ControllerChangeHandlerDetectorTest {
|
||||
+ "}";
|
||||
|
||||
lint()
|
||||
.files(java(source))
|
||||
.files(controllerChangeHandlerStub, java(source))
|
||||
.issues(ControllerIssueDetector.ISSUE, ControllerChangeHandlerIssueDetector.ISSUE)
|
||||
.run()
|
||||
.expectClean();
|
||||
@@ -57,7 +60,7 @@ public class ControllerChangeHandlerDetectorTest {
|
||||
+ "}";
|
||||
|
||||
lint()
|
||||
.files(java(source))
|
||||
.files(controllerChangeHandlerStub, java(source))
|
||||
.issues(ControllerIssueDetector.ISSUE, ControllerChangeHandlerIssueDetector.ISSUE)
|
||||
.run()
|
||||
.expect(CONSTRUCTOR);
|
||||
@@ -73,7 +76,7 @@ public class ControllerChangeHandlerDetectorTest {
|
||||
+ "}";
|
||||
|
||||
lint()
|
||||
.files(java(source))
|
||||
.files(controllerChangeHandlerStub, java(source))
|
||||
.issues(ControllerIssueDetector.ISSUE, ControllerChangeHandlerIssueDetector.ISSUE)
|
||||
.run()
|
||||
.expectClean();
|
||||
@@ -88,7 +91,7 @@ public class ControllerChangeHandlerDetectorTest {
|
||||
+ "}";
|
||||
|
||||
lint()
|
||||
.files(java(source))
|
||||
.files(controllerChangeHandlerStub, java(source))
|
||||
.issues(ControllerIssueDetector.ISSUE, ControllerChangeHandlerIssueDetector.ISSUE)
|
||||
.run()
|
||||
.expect(CONSTRUCTOR);
|
||||
@@ -103,10 +106,32 @@ public class ControllerChangeHandlerDetectorTest {
|
||||
+ "}";
|
||||
|
||||
lint()
|
||||
.files(java(source))
|
||||
.files(controllerChangeHandlerStub, java(source))
|
||||
.issues(ControllerIssueDetector.ISSUE, ControllerChangeHandlerIssueDetector.ISSUE)
|
||||
.run()
|
||||
.expect(PRIVATE_CLASS_ERROR);
|
||||
.expect("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");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithPrivateClassOfBaseClass() {
|
||||
@Language("JAVA") String baseClass = ""
|
||||
+ "package test;\n"
|
||||
+ "abstract class BaseChangeHandler extends com.bluelinelabs.conductor.ControllerChangeHandler {}";
|
||||
|
||||
@Language("JAVA") String source = ""
|
||||
+ "package test;\n"
|
||||
+ "private class SampleHandler extends test.BaseChangeHandler {}";
|
||||
|
||||
lint()
|
||||
.files(controllerChangeHandlerStub, java(baseClass), java(source))
|
||||
.issues(ControllerIssueDetector.ISSUE, ControllerChangeHandlerIssueDetector.ISSUE)
|
||||
.run()
|
||||
.expect("src/test/SampleHandler.java:2: Error: This ControllerChangeHandler class should be public (test.SampleHandler) [ValidControllerChangeHandler]\n" +
|
||||
"private class SampleHandler extends test.BaseChangeHandler {}\n" +
|
||||
"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
|
||||
"1 errors, 0 warnings");
|
||||
}
|
||||
}
|
||||
|
||||
+44
-9
@@ -1,24 +1,33 @@
|
||||
package com.bluelinelabs.conductor.lint;
|
||||
|
||||
import com.android.tools.lint.checks.infrastructure.LintDetectorTest;
|
||||
|
||||
import org.intellij.lang.annotations.Language;
|
||||
import org.junit.Test;
|
||||
|
||||
import static com.android.tools.lint.checks.infrastructure.TestFiles.java;
|
||||
import static com.android.tools.lint.checks.infrastructure.TestLintTask.lint;
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public class ControllerDetectorTest {
|
||||
|
||||
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";
|
||||
+ "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";
|
||||
|
||||
private final LintDetectorTest.TestFile controllerStub = java(
|
||||
"package com.bluelinelabs.conductor;\n"
|
||||
+ "abstract class Controller {}"
|
||||
);
|
||||
|
||||
|
||||
@Test
|
||||
public void testWithNoConstructor() {
|
||||
@Language("JAVA") String source = ""
|
||||
@@ -27,7 +36,7 @@ public class ControllerDetectorTest {
|
||||
+ "}";
|
||||
|
||||
lint()
|
||||
.files(java(source))
|
||||
.files(controllerStub, java(source))
|
||||
.issues(ControllerIssueDetector.ISSUE, ControllerChangeHandlerIssueDetector.ISSUE)
|
||||
.run()
|
||||
.expectClean();
|
||||
@@ -42,7 +51,7 @@ public class ControllerDetectorTest {
|
||||
+ "}";
|
||||
|
||||
lint()
|
||||
.files(java(source))
|
||||
.files(controllerStub, java(source))
|
||||
.issues(ControllerIssueDetector.ISSUE, ControllerChangeHandlerIssueDetector.ISSUE)
|
||||
.run()
|
||||
.expectClean();
|
||||
@@ -57,7 +66,7 @@ public class ControllerDetectorTest {
|
||||
+ "}";
|
||||
|
||||
lint()
|
||||
.files(java(source))
|
||||
.files(controllerStub, java(source))
|
||||
.issues(ControllerIssueDetector.ISSUE, ControllerChangeHandlerIssueDetector.ISSUE)
|
||||
.run()
|
||||
.expect(CONSTRUCTOR_ERROR);
|
||||
@@ -73,12 +82,38 @@ public class ControllerDetectorTest {
|
||||
+ "}";
|
||||
|
||||
lint()
|
||||
.files(java(source))
|
||||
.files(controllerStub, java(source))
|
||||
.issues(ControllerIssueDetector.ISSUE, ControllerChangeHandlerIssueDetector.ISSUE)
|
||||
.run()
|
||||
.expectClean();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithBaseClassAndPrivateConstructor() {
|
||||
@Language("JAVA")
|
||||
String baseClass = ""
|
||||
+ "package test;\n"
|
||||
+ "public class BaseController extends com.bluelinelabs.conductor.Controller {}";
|
||||
|
||||
@Language("JAVA")
|
||||
String source = ""
|
||||
+ "package test;\n"
|
||||
+ "public class SampleController extends BaseController {\n"
|
||||
+ " private SampleController() { }\n"
|
||||
+ "}";
|
||||
|
||||
lint()
|
||||
.files(controllerStub, java(baseClass), java(source))
|
||||
.issues(ControllerIssueDetector.ISSUE, ControllerChangeHandlerIssueDetector.ISSUE)
|
||||
.run()
|
||||
.expect(
|
||||
"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 BaseController {\n" +
|
||||
"^\n" +
|
||||
"1 errors, 0 warnings"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithPrivateConstructor() {
|
||||
@Language("JAVA") String source = ""
|
||||
@@ -88,7 +123,7 @@ public class ControllerDetectorTest {
|
||||
+ "}";
|
||||
|
||||
lint()
|
||||
.files(java(source))
|
||||
.files(controllerStub, java(source))
|
||||
.issues(ControllerIssueDetector.ISSUE, ControllerChangeHandlerIssueDetector.ISSUE)
|
||||
.run()
|
||||
.expect(CONSTRUCTOR_ERROR);
|
||||
@@ -103,7 +138,7 @@ public class ControllerDetectorTest {
|
||||
+ "}";
|
||||
|
||||
lint()
|
||||
.files(java(source))
|
||||
.files(controllerStub, java(source))
|
||||
.issues(ControllerIssueDetector.ISSUE, ControllerChangeHandlerIssueDetector.ISSUE)
|
||||
.run()
|
||||
.expect(CLASS_ERROR);
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
@@ -27,4 +21,4 @@ dependencies {
|
||||
ext.artifactId = 'conductor-androidx-transition'
|
||||
|
||||
apply from: rootProject.file('dependencies.gradle')
|
||||
apply from: rootProject.file('gradle-mvn-push.gradle')
|
||||
apply plugin: "com.vanniktech.maven.publish"
|
||||
|
||||
+3
@@ -25,6 +25,9 @@ import java.util.List;
|
||||
/**
|
||||
* A TransitionChangeHandler that facilitates using different Transitions for the entering view, the exiting view,
|
||||
* and shared elements between the two.
|
||||
* <p/>
|
||||
* Note that this class uses the <b>androidx</b> {@link Transition}. If you're using Android's platform transitions,
|
||||
* consider using the {@code SharedElementTransitionChangeHandler} provided by the {@code android-transitions} Conductor module.
|
||||
*/
|
||||
// Much of this class is based on FragmentTransition.java and FragmentTransitionCompat21.java from the Android support library
|
||||
public abstract class SharedElementTransitionChangeHandler extends TransitionChangeHandler {
|
||||
|
||||
+3
@@ -13,6 +13,9 @@ import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
|
||||
/**
|
||||
* A base {@link ControllerChangeHandler} that facilitates using {@link Transition}s to replace Controller Views.
|
||||
* <p/>
|
||||
* Note that this class uses the <b>androidx</b> {@link Transition}. If you're using Android's platform transitions,
|
||||
* consider using the {@code TransitionChangeHandler} provided by the {@code android-transitions} Conductor module.
|
||||
*/
|
||||
public abstract class TransitionChangeHandler extends ControllerChangeHandler {
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
apply from: rootProject.file('dependencies.gradle')
|
||||
apply from: rootProject.file('gradle-mvn-push.gradle')
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
@@ -21,3 +18,6 @@ dependencies {
|
||||
}
|
||||
|
||||
ext.artifactId = 'conductor-arch-components-lifecycle'
|
||||
|
||||
apply from: rootProject.file('dependencies.gradle')
|
||||
apply plugin: "com.vanniktech.maven.publish"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
apply from: rootProject.file('dependencies.gradle')
|
||||
apply from: rootProject.file('gradle-mvn-push.gradle')
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: "com.vanniktech.maven.publish"
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
apply from: rootProject.file('dependencies.gradle')
|
||||
apply from: rootProject.file('gradle-mvn-push.gradle')
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: "com.vanniktech.maven.publish"
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
@@ -22,4 +23,4 @@ dependencies {
|
||||
ext.artifactId = 'conductor-viewpager'
|
||||
|
||||
apply from: rootProject.file('dependencies.gradle')
|
||||
apply from: rootProject.file('gradle-mvn-push.gradle')
|
||||
apply plugin: "com.vanniktech.maven.publish"
|
||||
|
||||
+11
-11
@@ -6,6 +6,10 @@ import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.viewpager.widget.PagerAdapter;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
import com.bluelinelabs.conductor.Router;
|
||||
import com.bluelinelabs.conductor.RouterTransaction;
|
||||
@@ -15,12 +19,8 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.viewpager.widget.PagerAdapter;
|
||||
|
||||
/**
|
||||
* An adapter for ViewPagers that uses Routers as pages
|
||||
* An ViewPager adapter that uses Routers as pages
|
||||
*/
|
||||
public abstract class RouterPagerAdapter extends PagerAdapter {
|
||||
|
||||
@@ -32,9 +32,9 @@ public abstract class RouterPagerAdapter extends PagerAdapter {
|
||||
|
||||
private final Controller host;
|
||||
private int maxPagesToStateSave = Integer.MAX_VALUE;
|
||||
private Map<Integer, String> tags = new HashMap<>();
|
||||
private final Map<Integer, String> tags = new HashMap<>();
|
||||
private SparseArray<Bundle> savedPages = new SparseArray<>();
|
||||
private SparseArray<Router> visibleRouters = new SparseArray<>();
|
||||
private final SparseArray<Router> visibleRouters = new SparseArray<>();
|
||||
private ArrayList<Integer> savedPageHistory = new ArrayList<>();
|
||||
private Router currentPrimaryRouter;
|
||||
|
||||
@@ -103,7 +103,7 @@ public abstract class RouterPagerAdapter extends PagerAdapter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyItem(ViewGroup container, int position, Object object) {
|
||||
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
|
||||
Router router = (Router)object;
|
||||
|
||||
Bundle savedState = new Bundle();
|
||||
@@ -121,8 +121,8 @@ public abstract class RouterPagerAdapter extends PagerAdapter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrimaryItem(ViewGroup container, int position, Object object) {
|
||||
Router router = (Router)object;
|
||||
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
|
||||
Router router = (Router) object;
|
||||
if (router != currentPrimaryRouter) {
|
||||
if (currentPrimaryRouter != null) {
|
||||
for (RouterTransaction transaction : currentPrimaryRouter.getBackstack()) {
|
||||
@@ -139,7 +139,7 @@ public abstract class RouterPagerAdapter extends PagerAdapter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isViewFromObject(View view, Object object) {
|
||||
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
|
||||
Router router = (Router)object;
|
||||
final List<RouterTransaction> backstack = router.getBackstack();
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
|
||||
-113
@@ -1,113 +0,0 @@
|
||||
package com.bluelinelabs.conductor.viewpager;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import androidx.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.viewpager.util.FakePager;
|
||||
import com.bluelinelabs.conductor.viewpager.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.android.controller.ActivityController;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
package com.bluelinelabs.conductor.viewpager
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Looper.getMainLooper
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import com.bluelinelabs.conductor.Conductor
|
||||
import com.bluelinelabs.conductor.Router
|
||||
import com.bluelinelabs.conductor.RouterTransaction.Companion.with
|
||||
import com.bluelinelabs.conductor.viewpager.util.TestController
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.Robolectric
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.Shadows.shadowOf
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(manifest = Config.NONE)
|
||||
class StateSaveTests {
|
||||
|
||||
private val pager: ViewPager
|
||||
private val pagerAdapter: RouterPagerAdapter
|
||||
private val destroyedItems = mutableListOf<Int>()
|
||||
|
||||
init {
|
||||
val activityController = Robolectric.buildActivity(Activity::class.java).setup()
|
||||
val layout = FrameLayout(activityController.get())
|
||||
activityController.get().setContentView(layout)
|
||||
val router = Conductor.attachRouter(activityController.get(), FrameLayout(activityController.get()), null)
|
||||
val controller = TestController()
|
||||
router.setRoot(with(controller))
|
||||
pager = ViewPager(activityController.get()).also {
|
||||
it.id = ViewCompat.generateViewId()
|
||||
}
|
||||
layout.addView(pager)
|
||||
pager.offscreenPageLimit = 1
|
||||
pagerAdapter = object : RouterPagerAdapter(controller) {
|
||||
override fun configureRouter(router: Router, position: Int) {
|
||||
if (!router.hasRootController()) {
|
||||
router.setRoot(with(TestController()))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getCount(): Int {
|
||||
return 20
|
||||
}
|
||||
|
||||
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
|
||||
super.destroyItem(container, position, `object`)
|
||||
destroyedItems.add(position)
|
||||
}
|
||||
}
|
||||
pager.adapter = pagerAdapter
|
||||
shadowOf(getMainLooper()).idle()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNoMaxSaves() {
|
||||
// Load all pages
|
||||
for (i in 0 until pagerAdapter.count) {
|
||||
pager.currentItem = i
|
||||
shadowOf(getMainLooper()).idle()
|
||||
}
|
||||
|
||||
// Ensure all non-visible pages are saved
|
||||
assertEquals(
|
||||
destroyedItems.size,
|
||||
pagerAdapter.savedPages.size()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMaxSavedSet() {
|
||||
val maxPages = 3
|
||||
pagerAdapter.setMaxPagesToStateSave(maxPages)
|
||||
|
||||
// Load all pages
|
||||
for (i in 0 until pagerAdapter.count) {
|
||||
pager.currentItem = i
|
||||
shadowOf(getMainLooper()).idle()
|
||||
}
|
||||
|
||||
val firstSelectedItem = pagerAdapter.count / 2
|
||||
for (i in pagerAdapter.count downTo firstSelectedItem) {
|
||||
pager.currentItem = i
|
||||
shadowOf(getMainLooper()).idle()
|
||||
}
|
||||
|
||||
var savedPages = pagerAdapter.savedPages
|
||||
|
||||
// Ensure correct number of pages are saved
|
||||
assertEquals(maxPages, savedPages.size())
|
||||
|
||||
// Ensure correct pages are saved
|
||||
assertEquals(destroyedItems[destroyedItems.lastIndex], savedPages.keyAt(0))
|
||||
assertEquals(destroyedItems[destroyedItems.lastIndex - 1], savedPages.keyAt(1))
|
||||
assertEquals(destroyedItems[destroyedItems.lastIndex - 2], savedPages.keyAt(2))
|
||||
|
||||
val secondSelectedItem = 1
|
||||
for (i in firstSelectedItem downTo secondSelectedItem) {
|
||||
pager.currentItem = i
|
||||
shadowOf(getMainLooper()).idle()
|
||||
}
|
||||
|
||||
savedPages = pagerAdapter.savedPages
|
||||
|
||||
// Ensure correct number of pages are saved
|
||||
assertEquals(maxPages, savedPages.size())
|
||||
|
||||
// Ensure correct pages are saved
|
||||
assertEquals(destroyedItems[destroyedItems.lastIndex], savedPages.keyAt(0))
|
||||
assertEquals(destroyedItems[destroyedItems.lastIndex - 1], savedPages.keyAt(1))
|
||||
assertEquals(destroyedItems[destroyedItems.lastIndex - 2], savedPages.keyAt(2))
|
||||
}
|
||||
}
|
||||
-61
@@ -1,61 +0,0 @@
|
||||
package com.bluelinelabs.conductor.viewpager.util;
|
||||
|
||||
import android.util.SparseArray;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.viewpager.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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode Integer.parseInt(project.VERSION_CODE)
|
||||
versionName project.VERSION_NAME
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests {
|
||||
includeAndroidResources = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation rootProject.ext.junit
|
||||
testImplementation rootProject.ext.roboelectric
|
||||
|
||||
implementation rootProject.ext.androidxAppCompat
|
||||
implementation rootProject.ext.androidxViewPager2
|
||||
implementation project(':conductor')
|
||||
}
|
||||
|
||||
ext.artifactId = 'conductor-viewpager2'
|
||||
|
||||
apply from: rootProject.file('dependencies.gradle')
|
||||
apply plugin: "com.vanniktech.maven.publish"
|
||||
@@ -0,0 +1,3 @@
|
||||
POM_NAME=Conductor ViewPager2 Adapter
|
||||
POM_ARTIFACT_ID=conductor-viewpager2
|
||||
POM_PACKAGING=aar
|
||||
@@ -0,0 +1,3 @@
|
||||
<manifest package="com.bluelinelabs.conductor.viewpager2">
|
||||
<application />
|
||||
</manifest>
|
||||
+253
@@ -0,0 +1,253 @@
|
||||
package com.bluelinelabs.conductor.viewpager2
|
||||
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.util.LongSparseArray
|
||||
import android.util.SparseArray
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager2.adapter.StatefulAdapter
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.bluelinelabs.conductor.Controller
|
||||
import com.bluelinelabs.conductor.Router
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
/**
|
||||
* An ViewPager2 adapter that uses Routers as pages
|
||||
*/
|
||||
abstract class RouterStateAdapter(private val host: Controller) :
|
||||
RecyclerView.Adapter<RouterViewHolder>(), StatefulAdapter {
|
||||
|
||||
private var savedPages = LongSparseArray<Bundle>()
|
||||
internal var savedPageHistory = mutableListOf<Long>()
|
||||
private var maxPagesToStateSave = Int.MAX_VALUE
|
||||
private val visibleRouters = SparseArray<Router>()
|
||||
private var currentPrimaryRouterPosition = 0
|
||||
private var primaryItemCallback: PrimaryItemCallback? = null
|
||||
|
||||
init {
|
||||
super.setHasStableIds(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
abstract fun configureRouter(router: Router, position: Int)
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
open fun setMaxPagesToStateSave(maxPagesToStateSave: Int) {
|
||||
require(maxPagesToStateSave >= 0) { "Only positive integers may be passed for maxPagesToStateSave." }
|
||||
this.maxPagesToStateSave = maxPagesToStateSave
|
||||
ensurePagesSaved()
|
||||
}
|
||||
|
||||
private fun inferViewPager(recyclerView: RecyclerView): ViewPager2 {
|
||||
return recyclerView.parent as? ViewPager2 ?: error("Expected ViewPager2 instance. Got: ${recyclerView.parent}")
|
||||
}
|
||||
|
||||
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
|
||||
val viewPager = inferViewPager(recyclerView)
|
||||
primaryItemCallback = PrimaryItemCallback().also {
|
||||
viewPager.registerOnPageChangeCallback(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
|
||||
val viewPager = inferViewPager(recyclerView)
|
||||
primaryItemCallback?.let {
|
||||
viewPager.unregisterOnPageChangeCallback(it)
|
||||
}
|
||||
primaryItemCallback = null
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RouterViewHolder {
|
||||
return RouterViewHolder(parent)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RouterViewHolder, position: Int) {
|
||||
holder.currentItemPosition = position
|
||||
|
||||
attachRouter(holder, position)
|
||||
}
|
||||
|
||||
override fun onViewAttachedToWindow(holder: RouterViewHolder) {
|
||||
super.onViewAttachedToWindow(holder)
|
||||
|
||||
if (!holder.attached) {
|
||||
attachRouter(holder, holder.currentItemPosition)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewDetachedFromWindow(holder: RouterViewHolder) {
|
||||
super.onViewDetachedFromWindow(holder)
|
||||
|
||||
detachRouter(holder)
|
||||
|
||||
// Controller has fully detached and destroyed its view reference by now. Remove the leftover
|
||||
// view from the container.
|
||||
holder.container.removeAllViews()
|
||||
}
|
||||
|
||||
override fun onViewRecycled(holder: RouterViewHolder) {
|
||||
super.onViewRecycled(holder)
|
||||
|
||||
detachRouter(holder)
|
||||
|
||||
holder.currentRouter?.let { router ->
|
||||
host.removeChildRouter(router)
|
||||
holder.currentRouter = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailedToRecycleView(holder: RouterViewHolder): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
return position.toLong()
|
||||
}
|
||||
|
||||
override fun setHasStableIds(hasStableIds: Boolean) {
|
||||
throw UnsupportedOperationException("Stable Ids are required for the adapter to function properly")
|
||||
}
|
||||
|
||||
override fun saveState(): Parcelable {
|
||||
// Ensure all visible pages are saved, starting at the outermost pages and working our way in
|
||||
val visiblePositions = (0 until visibleRouters.size()).map { visibleRouters.keyAt(it) }.toMutableList()
|
||||
while (visiblePositions.isNotEmpty()) {
|
||||
val lastPosition = visiblePositions.removeAt(visiblePositions.lastIndex)
|
||||
savePage(getItemId(lastPosition), visibleRouters[lastPosition])
|
||||
|
||||
if (visiblePositions.isNotEmpty()) {
|
||||
val firstPosition = visiblePositions.removeAt(0)
|
||||
savePage(getItemId(firstPosition), visibleRouters[firstPosition])
|
||||
}
|
||||
}
|
||||
|
||||
return SavedState(
|
||||
savedPagesKeys = (0 until savedPages.size()).map { savedPages.keyAt(it) },
|
||||
savedPagesValues = (0 until savedPages.size()).map { savedPages.valueAt(it) },
|
||||
savedPageHistory = savedPageHistory,
|
||||
maxPagesToStateSave = maxPagesToStateSave
|
||||
)
|
||||
}
|
||||
|
||||
override fun restoreState(state: Parcelable) {
|
||||
if (state !is SavedState) return
|
||||
|
||||
savedPages = LongSparseArray()
|
||||
state.savedPagesKeys.indices.forEach { index ->
|
||||
savedPages.put(state.savedPagesKeys[index], state.savedPagesValues[index])
|
||||
}
|
||||
|
||||
savedPageHistory = state.savedPageHistory.toMutableList()
|
||||
maxPagesToStateSave = state.maxPagesToStateSave
|
||||
}
|
||||
|
||||
private fun attachRouter(holder: RouterViewHolder, position: Int) {
|
||||
val itemId = getItemId(position)
|
||||
val router = host.getChildRouter(holder.container, "$itemId")
|
||||
|
||||
// This should have already been handled by onViewRecycled, but it seems like this wasn't
|
||||
// always reliably called
|
||||
if (router != holder.currentRouter) {
|
||||
holder.currentRouter?.let { host.removeChildRouter(it) }
|
||||
}
|
||||
|
||||
holder.currentRouter = router
|
||||
holder.currentItemId = itemId
|
||||
|
||||
if (!router.hasRootController()) {
|
||||
val routerSavedState = savedPages[itemId]
|
||||
if (routerSavedState != null) {
|
||||
router.restoreInstanceState(routerSavedState)
|
||||
savedPages.remove(itemId)
|
||||
savedPageHistory.remove(itemId)
|
||||
}
|
||||
}
|
||||
|
||||
router.rebindIfNeeded()
|
||||
configureRouter(router, position)
|
||||
|
||||
if (position != currentPrimaryRouterPosition) {
|
||||
for (transaction in router.backstack) {
|
||||
transaction.controller.setOptionsMenuHidden(true)
|
||||
}
|
||||
}
|
||||
|
||||
visibleRouters.put(position, router)
|
||||
|
||||
holder.attached = true
|
||||
}
|
||||
|
||||
private fun detachRouter(holder: RouterViewHolder) {
|
||||
if (!holder.attached) {
|
||||
return
|
||||
}
|
||||
|
||||
holder.currentRouter?.let { router ->
|
||||
router.prepareForHostDetach()
|
||||
|
||||
savePage(holder.currentItemId, router)
|
||||
|
||||
if (visibleRouters[holder.currentItemPosition] == router) {
|
||||
visibleRouters.remove(holder.currentItemPosition)
|
||||
}
|
||||
}
|
||||
|
||||
holder.attached = false
|
||||
}
|
||||
|
||||
private fun savePage(itemId: Long, router: Router) {
|
||||
val savedState = Bundle()
|
||||
router.saveInstanceState(savedState)
|
||||
savedPages.put(itemId, savedState)
|
||||
|
||||
savedPageHistory.remove(itemId)
|
||||
savedPageHistory.add(itemId)
|
||||
|
||||
ensurePagesSaved()
|
||||
}
|
||||
|
||||
private fun ensurePagesSaved() {
|
||||
while (savedPages.size() > maxPagesToStateSave) {
|
||||
val routerIdToRemove = savedPageHistory.removeAt(0)
|
||||
savedPages.remove(routerIdToRemove)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the already instantiated Router in the specified position or `null` if there
|
||||
* is no router associated with this position.
|
||||
*/
|
||||
fun getRouter(position: Int): Router? {
|
||||
return visibleRouters[position]
|
||||
}
|
||||
|
||||
inner class PrimaryItemCallback : ViewPager2.OnPageChangeCallback() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
val router = visibleRouters[position]
|
||||
if (position != currentPrimaryRouterPosition) {
|
||||
val previousRouter = visibleRouters[currentPrimaryRouterPosition]
|
||||
|
||||
previousRouter?.backstack?.forEach { it.controller.setOptionsMenuHidden(true) }
|
||||
router?.backstack?.forEach { it.controller.setOptionsMenuHidden(false) }
|
||||
currentPrimaryRouterPosition = position
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
private data class SavedState(
|
||||
val savedPagesKeys: List<Long>,
|
||||
val savedPagesValues: List<Bundle>,
|
||||
val savedPageHistory: List<Long>,
|
||||
val maxPagesToStateSave: Int
|
||||
) : Parcelable
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.bluelinelabs.conductor.viewpager2
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.bluelinelabs.conductor.ChangeHandlerFrameLayout
|
||||
import com.bluelinelabs.conductor.Router
|
||||
|
||||
class RouterViewHolder private constructor(val container: ChangeHandlerFrameLayout) : ViewHolder(container) {
|
||||
var currentRouter: Router? = null
|
||||
var currentItemPosition = 0
|
||||
var currentItemId = 0L
|
||||
var attached = false
|
||||
|
||||
companion object {
|
||||
operator fun invoke(parent: ViewGroup): RouterViewHolder {
|
||||
val container = ChangeHandlerFrameLayout(parent.context)
|
||||
container.id = ViewCompat.generateViewId()
|
||||
container.layoutParams = ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
container.isSaveEnabled = false
|
||||
return RouterViewHolder(container)
|
||||
}
|
||||
}
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
package com.bluelinelabs.conductor.viewpager2
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Looper.getMainLooper
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.bluelinelabs.conductor.Conductor
|
||||
import com.bluelinelabs.conductor.Router
|
||||
import com.bluelinelabs.conductor.RouterTransaction.Companion.with
|
||||
import com.bluelinelabs.conductor.viewpager2.util.TestController
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.Robolectric
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.Shadows.shadowOf
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(manifest = Config.NONE)
|
||||
class StateSaveTests {
|
||||
|
||||
private val pager: ViewPager2
|
||||
private val adapter: RouterStateAdapter
|
||||
private val destroyedItems = mutableListOf<Int>()
|
||||
|
||||
init {
|
||||
val activityController = Robolectric.buildActivity(Activity::class.java).setup()
|
||||
val layout = FrameLayout(activityController.get())
|
||||
activityController.get().setContentView(layout)
|
||||
val router = Conductor.attachRouter(activityController.get(), FrameLayout(activityController.get()), null)
|
||||
val controller = TestController()
|
||||
router.setRoot(with(controller))
|
||||
pager = ViewPager2(activityController.get()).also {
|
||||
it.id = ViewCompat.generateViewId()
|
||||
}
|
||||
layout.addView(pager)
|
||||
pager.offscreenPageLimit = 1
|
||||
adapter = object : RouterStateAdapter(controller) {
|
||||
override fun configureRouter(router: Router, position: Int) {
|
||||
if (!router.hasRootController()) {
|
||||
router.setRoot(with(TestController()))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return 20
|
||||
}
|
||||
|
||||
override fun onViewDetachedFromWindow(holder: RouterViewHolder) {
|
||||
super.onViewDetachedFromWindow(holder)
|
||||
|
||||
destroyedItems.add(holder.currentItemPosition)
|
||||
}
|
||||
}
|
||||
pager.adapter = adapter
|
||||
shadowOf(getMainLooper()).idle()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNoMaxSaves() {
|
||||
// Load all pages
|
||||
for (i in 0 until adapter.itemCount) {
|
||||
pager.setCurrentItem(i, false)
|
||||
shadowOf(getMainLooper()).idle()
|
||||
}
|
||||
|
||||
// Ensure all non-visible pages are saved
|
||||
assertEquals(
|
||||
destroyedItems.size,
|
||||
adapter.savedPageHistory.size
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMaxSavedSet() {
|
||||
val maxPages = 3
|
||||
adapter.setMaxPagesToStateSave(maxPages)
|
||||
|
||||
// Load all pages
|
||||
for (i in 0 until adapter.itemCount) {
|
||||
pager.setCurrentItem(i, false)
|
||||
shadowOf(getMainLooper()).idle()
|
||||
}
|
||||
|
||||
val firstSelectedItem = adapter.itemCount / 2
|
||||
for (i in adapter.itemCount downTo firstSelectedItem) {
|
||||
pager.setCurrentItem(i, false)
|
||||
shadowOf(getMainLooper()).idle()
|
||||
}
|
||||
|
||||
var savedPages = adapter.savedPageHistory
|
||||
|
||||
// Ensure correct number of pages are saved
|
||||
assertEquals(maxPages, savedPages.size)
|
||||
|
||||
// Ensure correct pages are saved
|
||||
assertEquals(destroyedItems[destroyedItems.lastIndex], savedPages[savedPages.lastIndex].toInt())
|
||||
assertEquals(destroyedItems[destroyedItems.lastIndex - 1], savedPages[savedPages.lastIndex - 1].toInt())
|
||||
assertEquals(destroyedItems[destroyedItems.lastIndex - 2], savedPages[savedPages.lastIndex - 2].toInt())
|
||||
|
||||
val secondSelectedItem = 1
|
||||
for (i in adapter.itemCount downTo secondSelectedItem) {
|
||||
pager.setCurrentItem(i, false)
|
||||
shadowOf(getMainLooper()).idle()
|
||||
}
|
||||
|
||||
savedPages = adapter.savedPageHistory
|
||||
|
||||
// Ensure correct number of pages are saved
|
||||
assertEquals(maxPages, savedPages.size)
|
||||
|
||||
// Ensure correct pages are saved
|
||||
assertEquals(destroyedItems[destroyedItems.lastIndex], savedPages[savedPages.lastIndex].toInt())
|
||||
assertEquals(destroyedItems[destroyedItems.lastIndex - 1], savedPages[savedPages.lastIndex - 1].toInt())
|
||||
assertEquals(destroyedItems[destroyedItems.lastIndex - 2], savedPages[savedPages.lastIndex - 2].toInt())
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.bluelinelabs.conductor.viewpager2.util;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
|
||||
public class TestController extends Controller {
|
||||
|
||||
@Override @NonNull
|
||||
protected View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container, @Nullable Bundle savedViewState) {
|
||||
return new FrameLayout(inflater.getContext());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,4 +26,4 @@ dependencies {
|
||||
ext.artifactId = 'conductor'
|
||||
|
||||
apply from: rootProject.file('dependencies.gradle')
|
||||
apply from: rootProject.file('gradle-mvn-push.gradle')
|
||||
apply plugin: "com.vanniktech.maven.publish"
|
||||
|
||||
@@ -5,9 +5,10 @@ import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.content.IntentSender.SendIntentException;
|
||||
import android.os.Bundle;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler.ControllerChangeListener;
|
||||
import com.bluelinelabs.conductor.internal.LifecycleHandler;
|
||||
@@ -57,9 +58,12 @@ public class ActivityHostedRouter extends Router {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityDestroyed(@NonNull Activity activity) {
|
||||
super.onActivityDestroyed(activity);
|
||||
lifecycleHandler = null;
|
||||
public void onActivityDestroyed(@NonNull Activity activity, boolean isConfigurationChange) {
|
||||
super.onActivityDestroyed(activity, isConfigurationChange);
|
||||
|
||||
if (!isConfigurationChange) {
|
||||
lifecycleHandler = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -14,7 +14,7 @@ import com.bluelinelabs.conductor.ControllerChangeHandler.ControllerChangeListen
|
||||
* [ControllerChangeHandler]s are performing changes. It is not required to use this
|
||||
* ViewGroup, but it can be helpful.
|
||||
*/
|
||||
class ChangeHandlerFrameLayout : FrameLayout, ControllerChangeListener {
|
||||
open class ChangeHandlerFrameLayout : FrameLayout, ControllerChangeListener {
|
||||
|
||||
private var inProgressTransactionCount = 0
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
@file:JvmName("Conductor")
|
||||
|
||||
package com.bluelinelabs.conductor
|
||||
|
||||
import android.app.Activity
|
||||
@@ -9,23 +7,26 @@ import androidx.annotation.UiThread
|
||||
import com.bluelinelabs.conductor.internal.LifecycleHandler
|
||||
import com.bluelinelabs.conductor.internal.ensureMainThread
|
||||
|
||||
|
||||
/**
|
||||
* Conductor will create a [Router] that has been initialized for your Activity and containing ViewGroup.
|
||||
* If an existing [Router] is already associated with this Activity/ViewGroup pair, either in memory
|
||||
* or in the savedInstanceState, that router will be used and rebound instead of creating a new one with
|
||||
* an empty backstack.
|
||||
*
|
||||
* @receiver The Activity that will host the [Router] being attached.
|
||||
* @param activity The Activity that will host the [Router] being attached.
|
||||
* @param container The ViewGroup in which the [Router]'s [Controller] views will be hosted
|
||||
* @param savedInstanceState The savedInstanceState passed into the hosting Activity's onCreate method. Used
|
||||
* for restoring the Router's state if possible.
|
||||
* @return A fully configured [Router] instance for use with this Activity/ViewGroup pair.
|
||||
*/
|
||||
@UiThread
|
||||
fun Activity.attachRouter(container: ViewGroup, savedInstanceState: Bundle?): Router {
|
||||
ensureMainThread()
|
||||
return LifecycleHandler.install(this)
|
||||
.getRouter(container, savedInstanceState)
|
||||
.also { it.rebindIfNeeded() }
|
||||
object Conductor {
|
||||
|
||||
@JvmStatic
|
||||
fun attachRouter(activity: Activity, container: ViewGroup, savedInstanceState: Bundle?): Router {
|
||||
ensureMainThread()
|
||||
return LifecycleHandler.install(activity)
|
||||
.getRouter(container, savedInstanceState)
|
||||
.also { it.rebindIfNeeded() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -148,12 +149,12 @@ public abstract class Controller {
|
||||
* for this method will be {@code return inflater.inflate(R.layout.my_layout, container, false);}, plus
|
||||
* any binding 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 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.
|
||||
* or {@code null} if no saved state exists.
|
||||
*/
|
||||
@NonNull
|
||||
protected abstract View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container, @Nullable Bundle savedViewState);
|
||||
@@ -213,6 +214,9 @@ public abstract class Controller {
|
||||
@Nullable
|
||||
public final Router getChildRouter(@NonNull ViewGroup container, @Nullable String tag, boolean createIfNeeded) {
|
||||
@IdRes final int containerId = container.getId();
|
||||
if (containerId == View.NO_ID) {
|
||||
throw new IllegalStateException("You must set an id on your container.");
|
||||
}
|
||||
|
||||
ControllerHostedRouter childRouter = null;
|
||||
for (ControllerHostedRouter router : childRouters) {
|
||||
@@ -225,7 +229,7 @@ public abstract class Controller {
|
||||
if (childRouter == null) {
|
||||
if (createIfNeeded) {
|
||||
childRouter = new ControllerHostedRouter(container.getId(), tag);
|
||||
childRouter.setHost(this, container);
|
||||
childRouter.setHostContainer(this, container);
|
||||
childRouters.add(childRouter);
|
||||
|
||||
if (isPerformingExitTransition) {
|
||||
@@ -233,7 +237,7 @@ public abstract class Controller {
|
||||
}
|
||||
}
|
||||
} else if (!childRouter.hasHost()) {
|
||||
childRouter.setHost(this, container);
|
||||
childRouter.setHostContainer(this, container);
|
||||
childRouter.rebindIfNeeded();
|
||||
}
|
||||
|
||||
@@ -519,21 +523,36 @@ public abstract class Controller {
|
||||
* Calls startActivity(Intent) from this Controller's host Activity.
|
||||
*/
|
||||
public final void startActivity(@NonNull final Intent intent) {
|
||||
executeWithRouter(() -> router.startActivity(intent));
|
||||
executeWithRouter(new RouterRequiringFunc() {
|
||||
@Override
|
||||
public void execute() {
|
||||
router.startActivity(intent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls startActivityForResult(Intent, int) from this Controller's host Activity.
|
||||
*/
|
||||
public final void startActivityForResult(@NonNull final Intent intent, final int requestCode) {
|
||||
executeWithRouter(() -> router.startActivityForResult(instanceId, intent, requestCode));
|
||||
executeWithRouter(new RouterRequiringFunc() {
|
||||
@Override
|
||||
public void execute() {
|
||||
router.startActivityForResult(instanceId, intent, requestCode);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls startActivityForResult(Intent, int, Bundle) from this Controller's host Activity.
|
||||
*/
|
||||
public final void startActivityForResult(@NonNull final Intent intent, final int requestCode, @Nullable final Bundle options) {
|
||||
executeWithRouter(() -> router.startActivityForResult(instanceId, intent, requestCode, options));
|
||||
executeWithRouter(new RouterRequiringFunc() {
|
||||
@Override
|
||||
public void execute() {
|
||||
router.startActivityForResult(instanceId, intent, requestCode, options);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -551,7 +570,12 @@ public abstract class Controller {
|
||||
* @param requestCode The request code being registered for.
|
||||
*/
|
||||
public final void registerForActivityResult(final int requestCode) {
|
||||
executeWithRouter(() -> router.registerForActivityResult(instanceId, requestCode));
|
||||
executeWithRouter(new RouterRequiringFunc() {
|
||||
@Override
|
||||
public void execute() {
|
||||
router.registerForActivityResult(instanceId, requestCode);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -574,7 +598,12 @@ public abstract class Controller {
|
||||
public final void requestPermissions(@NonNull final String[] permissions, final int requestCode) {
|
||||
requestedPermissions.addAll(Arrays.asList(permissions));
|
||||
|
||||
executeWithRouter(() -> router.requestPermissions(instanceId, permissions, requestCode));
|
||||
executeWithRouter(new RouterRequiringFunc() {
|
||||
@Override
|
||||
public void execute() {
|
||||
router.requestPermissions(instanceId, permissions, requestCode);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -609,7 +638,12 @@ public abstract class Controller {
|
||||
childTransactions.addAll(childRouter.getBackstack());
|
||||
}
|
||||
|
||||
Collections.sort(childTransactions, (o1, o2) -> o2.getTransactionIndex() - o1.getTransactionIndex());
|
||||
Collections.sort(childTransactions, new Comparator<RouterTransaction>() {
|
||||
@Override
|
||||
public int compare(RouterTransaction o1, RouterTransaction o2) {
|
||||
return o2.getTransactionIndex() - o1.getTransactionIndex();
|
||||
}
|
||||
});
|
||||
|
||||
for (RouterTransaction transaction : childTransactions) {
|
||||
Controller childController = transaction.controller();
|
||||
@@ -929,7 +963,9 @@ public abstract class Controller {
|
||||
}
|
||||
}
|
||||
|
||||
childRouter.rebindIfNeeded();
|
||||
if (childRouter.hasHost()) {
|
||||
childRouter.rebindIfNeeded();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -982,7 +1018,11 @@ public abstract class Controller {
|
||||
|
||||
onDestroyView(view);
|
||||
|
||||
viewAttachHandler.unregisterAttachListener(view);
|
||||
// viewAttachHandler may be null iff the controller was popped before we got here
|
||||
if (viewAttachHandler != null) {
|
||||
viewAttachHandler.unregisterAttachListener(view);
|
||||
}
|
||||
|
||||
viewAttachHandler = null;
|
||||
viewIsAttached = false;
|
||||
|
||||
@@ -1031,33 +1071,35 @@ public abstract class Controller {
|
||||
|
||||
restoreViewState(view);
|
||||
|
||||
viewAttachHandler = new ViewAttachHandler(new ViewAttachListener() {
|
||||
@Override
|
||||
public void onAttached() {
|
||||
viewIsAttached = true;
|
||||
viewWasDetached = false;
|
||||
attach(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetached(boolean fromActivityStop) {
|
||||
viewIsAttached = false;
|
||||
viewWasDetached = true;
|
||||
|
||||
if (!isDetachFrozen) {
|
||||
detach(view, false, fromActivityStop);
|
||||
if (!isBeingDestroyed) {
|
||||
viewAttachHandler = new ViewAttachHandler(new ViewAttachListener() {
|
||||
@Override
|
||||
public void onAttached() {
|
||||
viewIsAttached = true;
|
||||
viewWasDetached = false;
|
||||
attach(view);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewDetachAfterStop() {
|
||||
if (!isDetachFrozen) {
|
||||
detach(view, false, false);
|
||||
@Override
|
||||
public void onDetached(boolean fromActivityStop) {
|
||||
viewIsAttached = false;
|
||||
viewWasDetached = true;
|
||||
|
||||
if (!isDetachFrozen) {
|
||||
detach(view, false, fromActivityStop);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
viewAttachHandler.listenForAttach(view);
|
||||
} else if (retainViewMode == RetainViewMode.RETAIN_DETACH) {
|
||||
|
||||
@Override
|
||||
public void onViewDetachAfterStop() {
|
||||
if (!isDetachFrozen) {
|
||||
detach(view, false, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
viewAttachHandler.listenForAttach(view);
|
||||
}
|
||||
} else {
|
||||
restoreChildControllerHosts();
|
||||
}
|
||||
|
||||
@@ -1069,8 +1111,8 @@ public abstract class Controller {
|
||||
if (!childRouter.hasHost()) {
|
||||
View containerView = view.findViewById(childRouter.getHostId());
|
||||
|
||||
if (containerView != null && containerView instanceof ViewGroup) {
|
||||
childRouter.setHost(this, (ViewGroup) containerView);
|
||||
if (containerView instanceof ViewGroup) {
|
||||
childRouter.setHostContainer(this, (ViewGroup) containerView);
|
||||
childRouter.rebindIfNeeded();
|
||||
}
|
||||
}
|
||||
@@ -1229,6 +1271,7 @@ public abstract class Controller {
|
||||
List<Bundle> childBundles = savedInstanceState.getParcelableArrayList(KEY_CHILD_ROUTERS);
|
||||
for (Bundle childBundle : childBundles) {
|
||||
ControllerHostedRouter childRouter = new ControllerHostedRouter();
|
||||
childRouter.setHostController(this);
|
||||
childRouter.restoreInstanceState(childBundle);
|
||||
childRouters.add(childRouter);
|
||||
}
|
||||
@@ -1377,66 +1420,66 @@ public abstract class Controller {
|
||||
/**
|
||||
* Allows external classes to listen for lifecycle events in a Controller
|
||||
*/
|
||||
public interface LifecycleListener {
|
||||
public static abstract class LifecycleListener {
|
||||
|
||||
default void onChangeStart(@NonNull Controller controller, @NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
public void onChangeStart(@NonNull Controller controller, @NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
}
|
||||
|
||||
default void onChangeEnd(@NonNull Controller controller, @NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
public void onChangeEnd(@NonNull Controller controller, @NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
}
|
||||
|
||||
default void preCreateView(@NonNull Controller controller) {
|
||||
public void preCreateView(@NonNull Controller controller) {
|
||||
}
|
||||
|
||||
default void postCreateView(@NonNull Controller controller, @NonNull View view) {
|
||||
public void postCreateView(@NonNull Controller controller, @NonNull View view) {
|
||||
}
|
||||
|
||||
default void preAttach(@NonNull Controller controller, @NonNull View view) {
|
||||
public void preAttach(@NonNull Controller controller, @NonNull View view) {
|
||||
}
|
||||
|
||||
default void postAttach(@NonNull Controller controller, @NonNull View view) {
|
||||
public void postAttach(@NonNull Controller controller, @NonNull View view) {
|
||||
}
|
||||
|
||||
default void preDetach(@NonNull Controller controller, @NonNull View view) {
|
||||
public void preDetach(@NonNull Controller controller, @NonNull View view) {
|
||||
}
|
||||
|
||||
default void postDetach(@NonNull Controller controller, @NonNull View view) {
|
||||
public void postDetach(@NonNull Controller controller, @NonNull View view) {
|
||||
}
|
||||
|
||||
default void preDestroyView(@NonNull Controller controller, @NonNull View view) {
|
||||
public void preDestroyView(@NonNull Controller controller, @NonNull View view) {
|
||||
}
|
||||
|
||||
default void postDestroyView(@NonNull Controller controller) {
|
||||
public void postDestroyView(@NonNull Controller controller) {
|
||||
}
|
||||
|
||||
default void preDestroy(@NonNull Controller controller) {
|
||||
public void preDestroy(@NonNull Controller controller) {
|
||||
}
|
||||
|
||||
default void postDestroy(@NonNull Controller controller) {
|
||||
public void postDestroy(@NonNull Controller controller) {
|
||||
}
|
||||
|
||||
default void preContextAvailable(@NonNull Controller controller) {
|
||||
public void preContextAvailable(@NonNull Controller controller) {
|
||||
}
|
||||
|
||||
default void postContextAvailable(@NonNull Controller controller, @NonNull Context context) {
|
||||
public void postContextAvailable(@NonNull Controller controller, @NonNull Context context) {
|
||||
}
|
||||
|
||||
default void preContextUnavailable(@NonNull Controller controller, @NonNull Context context) {
|
||||
public void preContextUnavailable(@NonNull Controller controller, @NonNull Context context) {
|
||||
}
|
||||
|
||||
default void postContextUnavailable(@NonNull Controller controller) {
|
||||
public void postContextUnavailable(@NonNull Controller controller) {
|
||||
}
|
||||
|
||||
default void onSaveInstanceState(@NonNull Controller controller, @NonNull Bundle outState) {
|
||||
public void onSaveInstanceState(@NonNull Controller controller, @NonNull Bundle outState) {
|
||||
}
|
||||
|
||||
default void onRestoreInstanceState(@NonNull Controller controller, @NonNull Bundle savedInstanceState) {
|
||||
public void onRestoreInstanceState(@NonNull Controller controller, @NonNull Bundle savedInstanceState) {
|
||||
}
|
||||
|
||||
default void onSaveViewState(@NonNull Controller controller, @NonNull Bundle outState) {
|
||||
public void onSaveViewState(@NonNull Controller controller, @NonNull Bundle outState) {
|
||||
}
|
||||
|
||||
default void onRestoreViewState(@NonNull Controller controller, @NonNull Bundle savedViewState) {
|
||||
public void onRestoreViewState(@NonNull Controller controller, @NonNull Bundle savedViewState) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -206,29 +206,32 @@ public abstract class ControllerChangeHandler {
|
||||
fromView = null;
|
||||
}
|
||||
|
||||
handler.performChange(container, fromView, toView, isPush, () -> {
|
||||
if (from != null) {
|
||||
from.changeEnded(handler, fromChangeType);
|
||||
}
|
||||
|
||||
if (to != null) {
|
||||
inProgressChangeHandlers.remove(to.getInstanceId());
|
||||
to.changeEnded(handler, toChangeType);
|
||||
}
|
||||
|
||||
for (ControllerChangeListener listener : listeners) {
|
||||
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);
|
||||
handler.performChange(container, fromView, toView, isPush, new ControllerChangeCompletedListener() {
|
||||
@Override
|
||||
public void onChangeCompleted() {
|
||||
if (from != null) {
|
||||
from.changeEnded(handler, fromChangeType);
|
||||
}
|
||||
}
|
||||
|
||||
if (handler.removesFromViewOnPush() && from != null) {
|
||||
from.setNeedsAttach(false);
|
||||
if (to != null) {
|
||||
inProgressChangeHandlers.remove(to.getInstanceId());
|
||||
to.changeEnded(handler, toChangeType);
|
||||
}
|
||||
|
||||
for (ControllerChangeListener listener : listeners) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -258,8 +261,7 @@ public abstract class ControllerChangeHandler {
|
||||
* @param container The containing ViewGroup
|
||||
* @param handler The change handler being used.
|
||||
*/
|
||||
default void onChangeStarted(@Nullable Controller to, @Nullable Controller from, boolean isPush, @NonNull ViewGroup container, @NonNull 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
|
||||
@@ -270,8 +272,7 @@ public abstract class ControllerChangeHandler {
|
||||
* @param container The containing ViewGroup
|
||||
* @param handler The change handler that was used.
|
||||
*/
|
||||
default void onChangeCompleted(@Nullable Controller to, @Nullable Controller from, boolean isPush, @NonNull ViewGroup container, @NonNull ControllerChangeHandler handler) {
|
||||
}
|
||||
void onChangeCompleted(@Nullable Controller to, @Nullable Controller from, boolean isPush, @NonNull ViewGroup container, @NonNull ControllerChangeHandler handler);
|
||||
}
|
||||
|
||||
static class ChangeTransaction {
|
||||
|
||||
@@ -36,12 +36,18 @@ class ControllerHostedRouter extends Router {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
final void setHost(@NonNull Controller controller, @NonNull ViewGroup container) {
|
||||
final void setHostController(@NonNull Controller controller) {
|
||||
if (hostController == null) {
|
||||
hostController = controller;
|
||||
}
|
||||
}
|
||||
|
||||
final void setHostContainer(@NonNull Controller controller, @NonNull ViewGroup container) {
|
||||
if (hostController != controller || this.container != container) {
|
||||
removeHost();
|
||||
|
||||
if (container instanceof ControllerChangeListener) {
|
||||
addChangeListener((ControllerChangeListener)container);
|
||||
addChangeListener((ControllerChangeListener) container);
|
||||
}
|
||||
|
||||
hostController = controller;
|
||||
@@ -57,7 +63,7 @@ class ControllerHostedRouter extends Router {
|
||||
|
||||
final void removeHost() {
|
||||
if (container != null && container instanceof ControllerChangeListener) {
|
||||
removeChangeListener((ControllerChangeListener)container);
|
||||
removeChangeListener((ControllerChangeListener) container);
|
||||
}
|
||||
|
||||
final List<Controller> controllersToDestroy = new ArrayList<>(destroyingControllers);
|
||||
@@ -73,7 +79,6 @@ class ControllerHostedRouter extends Router {
|
||||
}
|
||||
|
||||
prepareForContainerRemoval();
|
||||
hostController = null;
|
||||
container = null;
|
||||
}
|
||||
|
||||
@@ -108,14 +113,29 @@ class ControllerHostedRouter extends Router {
|
||||
super.setBackstack(newBackstack, changeHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
void performControllerChange(@Nullable RouterTransaction to, @Nullable RouterTransaction from, boolean isPush) {
|
||||
super.performControllerChange(to, from, isPush);
|
||||
|
||||
// If we're pushing a transaction that will detach controllers to an unattached child
|
||||
// router, we need mark all other controllers as NOT needing to be reattached.
|
||||
if (to != null && !hostController.isAttached()) {
|
||||
if (to.pushChangeHandler() == null || to.pushChangeHandler().removesFromViewOnPush()) {
|
||||
for (RouterTransaction transaction : backstack) {
|
||||
transaction.controller().setNeedsAttach(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override @Nullable
|
||||
public Activity getActivity() {
|
||||
return hostController != null ? hostController.getActivity() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityDestroyed(@NonNull Activity activity) {
|
||||
super.onActivityDestroyed(activity);
|
||||
public void onActivityDestroyed(@NonNull Activity activity, boolean isConfigurationChange) {
|
||||
super.onActivityDestroyed(activity, isConfigurationChange);
|
||||
|
||||
removeHost();
|
||||
}
|
||||
@@ -185,7 +205,7 @@ class ControllerHostedRouter extends Router {
|
||||
|
||||
@Override
|
||||
boolean hasHost() {
|
||||
return hostController != null;
|
||||
return hostController != null && container != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -205,9 +225,9 @@ class ControllerHostedRouter extends Router {
|
||||
}
|
||||
|
||||
@Override
|
||||
void setControllerRouter(@NonNull Controller controller) {
|
||||
void setRouterOnController(@NonNull Controller controller) {
|
||||
controller.setParentController(hostController);
|
||||
super.setControllerRouter(controller);
|
||||
super.setRouterOnController(controller);
|
||||
}
|
||||
|
||||
int getHostId() {
|
||||
|
||||
@@ -205,7 +205,7 @@ public abstract class Router {
|
||||
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())) {
|
||||
for (RouterTransaction visibleTransaction : getVisibleTransactions(backstack.iterator(), true)) {
|
||||
performControllerChange(null, visibleTransaction, true, handler);
|
||||
}
|
||||
}
|
||||
@@ -404,7 +404,7 @@ public abstract class Router {
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
List<RouterTransaction> oldTransactions = getBackstack();
|
||||
List<RouterTransaction> oldVisibleTransactions = getVisibleTransactions(backstack.iterator());
|
||||
List<RouterTransaction> oldVisibleTransactions = getVisibleTransactions(backstack.iterator(), false);
|
||||
|
||||
removeAllExceptVisibleAndUnowned();
|
||||
ensureOrderedTransactionIndices(newBackstack);
|
||||
@@ -434,13 +434,13 @@ public abstract class Router {
|
||||
while (backstackIterator.hasNext()) {
|
||||
RouterTransaction transaction = backstackIterator.next();
|
||||
transaction.onAttachedToRouter();
|
||||
setControllerRouter(transaction.controller());
|
||||
setRouterOnController(transaction.controller());
|
||||
}
|
||||
|
||||
if (newBackstack.size() > 0) {
|
||||
List<RouterTransaction> reverseNewBackstack = new ArrayList<>(newBackstack);
|
||||
Collections.reverse(reverseNewBackstack);
|
||||
List<RouterTransaction> newVisibleTransactions = getVisibleTransactions(reverseNewBackstack.iterator());
|
||||
List<RouterTransaction> newVisibleTransactions = getVisibleTransactions(reverseNewBackstack.iterator(), false);
|
||||
boolean newRootRequiresPush = !(newVisibleTransactions.size() > 0 && oldTransactions.contains(newVisibleTransactions.get(0)));
|
||||
|
||||
boolean visibleTransactionsChanged = !backstacksAreEqual(newVisibleTransactions, oldVisibleTransactions);
|
||||
@@ -464,7 +464,10 @@ public abstract class Router {
|
||||
ControllerChangeHandler localHandler = changeHandler != null ? changeHandler.copy() : new SimpleSwapChangeHandler();
|
||||
localHandler.setForceRemoveViewOnPush(true);
|
||||
ControllerChangeHandler.completeHandlerImmediately(transaction.controller().getInstanceId());
|
||||
performControllerChange(null, transaction, newRootRequiresPush, localHandler);
|
||||
|
||||
if (transaction.controller().view != null) {
|
||||
performControllerChange(null, transaction, newRootRequiresPush, localHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -549,7 +552,7 @@ public abstract class Router {
|
||||
if (transaction.controller().getNeedsAttach()) {
|
||||
performControllerChange(transaction, null, true, new SimpleSwapChangeHandler(false));
|
||||
} else {
|
||||
setControllerRouter(transaction.controller());
|
||||
setRouterOnController(transaction.controller());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -605,7 +608,7 @@ public abstract class Router {
|
||||
isActivityStopped = true;
|
||||
}
|
||||
|
||||
public void onActivityDestroyed(@NonNull Activity activity) {
|
||||
public void onActivityDestroyed(@NonNull Activity activity, boolean isConfigurationChange) {
|
||||
prepareForContainerRemoval();
|
||||
changeListeners.clear();
|
||||
|
||||
@@ -613,7 +616,7 @@ public abstract class Router {
|
||||
transaction.controller().activityDestroyed(activity);
|
||||
|
||||
for (Router childRouter : transaction.controller().getChildRouters()) {
|
||||
childRouter.onActivityDestroyed(activity);
|
||||
childRouter.onActivityDestroyed(activity, isConfigurationChange);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -622,7 +625,7 @@ public abstract class Router {
|
||||
controller.activityDestroyed(activity);
|
||||
|
||||
for (Router childRouter : controller.getChildRouters()) {
|
||||
childRouter.onActivityDestroyed(activity);
|
||||
childRouter.onActivityDestroyed(activity, isConfigurationChange);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -657,7 +660,7 @@ public abstract class Router {
|
||||
|
||||
Iterator<RouterTransaction> backstackIterator = backstack.reverseIterator();
|
||||
while (backstackIterator.hasNext()) {
|
||||
setControllerRouter(backstackIterator.next().controller());
|
||||
setRouterOnController(backstackIterator.next().controller());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -720,7 +723,12 @@ public abstract class Router {
|
||||
}
|
||||
|
||||
void watchContainerAttach() {
|
||||
container.post(() -> containerFullyAttached = true);
|
||||
container.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
containerFullyAttached = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void prepareForContainerRemoval() {
|
||||
@@ -759,7 +767,7 @@ public abstract class Router {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void performControllerChange(@Nullable RouterTransaction to, @Nullable RouterTransaction from, boolean isPush) {
|
||||
void performControllerChange(@Nullable RouterTransaction to, @Nullable RouterTransaction from, boolean isPush) {
|
||||
if (isPush && to != null) {
|
||||
to.onAttachedToRouter();
|
||||
}
|
||||
@@ -784,7 +792,7 @@ public abstract class Router {
|
||||
|
||||
if (to != null) {
|
||||
to.ensureValidIndex(getTransactionIndexer());
|
||||
setControllerRouter(toController);
|
||||
setRouterOnController(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 host
|
||||
// Activity or controller should be handling this by finishing or at least hiding this view.
|
||||
@@ -802,7 +810,7 @@ public abstract class Router {
|
||||
if (fromController.getView() != null) {
|
||||
fromController.detach(fromController.getView(), true, false);
|
||||
} else {
|
||||
from.controller().destroy();
|
||||
fromController.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -829,7 +837,12 @@ public abstract class Router {
|
||||
to.setNeedsAttach(true);
|
||||
}
|
||||
pendingControllerChanges.add(transaction);
|
||||
container.post(this::performPendingControllerChanges);
|
||||
container.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
performPendingControllerChanges();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ControllerChangeHandler.executeChange(transaction);
|
||||
}
|
||||
@@ -873,7 +886,7 @@ public abstract class Router {
|
||||
private void removeAllExceptVisibleAndUnowned() {
|
||||
List<View> views = new ArrayList<>();
|
||||
|
||||
for (RouterTransaction transaction : getVisibleTransactions(backstack.iterator())) {
|
||||
for (RouterTransaction transaction : getVisibleTransactions(backstack.iterator(), false)) {
|
||||
if (transaction.controller().getView() != null) {
|
||||
views.add(transaction.controller().getView());
|
||||
}
|
||||
@@ -933,14 +946,20 @@ public abstract class Router {
|
||||
}
|
||||
}
|
||||
|
||||
private List<RouterTransaction> getVisibleTransactions(@NonNull Iterator<RouterTransaction> backstackIterator) {
|
||||
private List<RouterTransaction> getVisibleTransactions(@NonNull Iterator<RouterTransaction> backstackIterator, boolean onlyTop) {
|
||||
boolean visible = true;
|
||||
|
||||
List<RouterTransaction> transactions = new ArrayList<>();
|
||||
while (backstackIterator.hasNext()) {
|
||||
RouterTransaction transaction = backstackIterator.next();
|
||||
transactions.add(transaction);
|
||||
|
||||
//noinspection ConstantConditions
|
||||
if (transaction.pushChangeHandler() == null || transaction.pushChangeHandler().removesFromViewOnPush()) {
|
||||
if (visible) {
|
||||
transactions.add(transaction);
|
||||
}
|
||||
|
||||
visible = transaction.pushChangeHandler() != null && !transaction.pushChangeHandler().removesFromViewOnPush();
|
||||
|
||||
if (onlyTop && !visible) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -963,7 +982,7 @@ public abstract class Router {
|
||||
return true;
|
||||
}
|
||||
|
||||
void setControllerRouter(@NonNull Controller controller) {
|
||||
void setRouterOnController(@NonNull Controller controller) {
|
||||
controller.setRouter(this);
|
||||
controller.onContextAvailable();
|
||||
}
|
||||
|
||||
-185
@@ -1,185 +0,0 @@
|
||||
package com.bluelinelabs.conductor.internal;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.transition.Transition;
|
||||
import android.transition.TransitionSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public class LegacyTransitionUtils {
|
||||
|
||||
public static void findNamedViews(@NonNull Map<String, View> namedViews, View view) {
|
||||
if (view.getVisibility() == View.VISIBLE) {
|
||||
String transitionName = view.getTransitionName();
|
||||
if (transitionName != null) {
|
||||
namedViews.put(transitionName, view);
|
||||
}
|
||||
|
||||
if (view instanceof ViewGroup) {
|
||||
ViewGroup viewGroup = (ViewGroup) view;
|
||||
int childCount = viewGroup.getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
View child = viewGroup.getChildAt(i);
|
||||
findNamedViews(namedViews, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static View findNamedView(@NonNull View view, @NonNull String transitionName) {
|
||||
if (transitionName.equals(view.getTransitionName())) {
|
||||
return view;
|
||||
}
|
||||
|
||||
if (view instanceof ViewGroup) {
|
||||
ViewGroup viewGroup = (ViewGroup) view;
|
||||
int childCount = viewGroup.getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
View viewWithTransitionName = findNamedView(viewGroup.getChildAt(i), transitionName);
|
||||
if (viewWithTransitionName != null) {
|
||||
return viewWithTransitionName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void setEpicenter(@NonNull Transition transition, @Nullable View view) {
|
||||
if (view != null) {
|
||||
final Rect epicenter = new Rect();
|
||||
getBoundsOnScreen(view, epicenter);
|
||||
transition.setEpicenterCallback(new Transition.EpicenterCallback() {
|
||||
@Override
|
||||
public Rect onGetEpicenter(Transition transition) {
|
||||
return epicenter;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void getBoundsOnScreen(@NonNull View view, @NonNull Rect epicenter) {
|
||||
int[] loc = new int[2];
|
||||
view.getLocationOnScreen(loc);
|
||||
epicenter.set(loc[0], loc[1], loc[0] + view.getWidth(), loc[1] + view.getHeight());
|
||||
}
|
||||
|
||||
public static void setTargets(@NonNull Transition transition, @NonNull View nonExistentView, @NonNull List<View> sharedViews) {
|
||||
final List<View> views = transition.getTargets();
|
||||
views.clear();
|
||||
final int count = sharedViews.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final View view = sharedViews.get(i);
|
||||
bfsAddViewChildren(views, view);
|
||||
}
|
||||
views.add(nonExistentView);
|
||||
sharedViews.add(nonExistentView);
|
||||
addTargets(transition, sharedViews);
|
||||
}
|
||||
|
||||
public static void addTargets(@Nullable Transition transition, @NonNull List<View> views) {
|
||||
if (transition == null) {
|
||||
return;
|
||||
}
|
||||
if (transition instanceof TransitionSet) {
|
||||
TransitionSet set = (TransitionSet) transition;
|
||||
int numTransitions = set.getTransitionCount();
|
||||
for (int i = 0; i < numTransitions; i++) {
|
||||
Transition child = set.getTransitionAt(i);
|
||||
addTargets(child, views);
|
||||
}
|
||||
} else if (!hasSimpleTarget(transition)) {
|
||||
List<View> targets = transition.getTargets();
|
||||
if (isNullOrEmpty(targets)) {
|
||||
int numViews = views.size();
|
||||
for (int i = 0; i < numViews; i++) {
|
||||
transition.addTarget(views.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void replaceTargets(@NonNull Transition transition, @NonNull List<View> oldTargets, @Nullable List<View> newTargets) {
|
||||
if (transition instanceof TransitionSet) {
|
||||
TransitionSet set = (TransitionSet) transition;
|
||||
int numTransitions = set.getTransitionCount();
|
||||
for (int i = 0; i < numTransitions; i++) {
|
||||
Transition child = set.getTransitionAt(i);
|
||||
replaceTargets(child, oldTargets, newTargets);
|
||||
}
|
||||
} else if (!LegacyTransitionUtils.hasSimpleTarget(transition)) {
|
||||
List<View> targets = transition.getTargets();
|
||||
if (targets != null && targets.size() == oldTargets.size() && targets.containsAll(oldTargets)) {
|
||||
final int targetCount = newTargets == null ? 0 : newTargets.size();
|
||||
for (int i = 0; i < targetCount; i++) {
|
||||
transition.addTarget(newTargets.get(i));
|
||||
}
|
||||
for (int i = oldTargets.size() - 1; i >= 0; i--) {
|
||||
transition.removeTarget(oldTargets.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void bfsAddViewChildren(@NonNull final List<View> views, @NonNull final View startView) {
|
||||
final int startIndex = views.size();
|
||||
if (containedBeforeIndex(views, startView, startIndex)) {
|
||||
return; // This child is already in the list, so all its children are also.
|
||||
}
|
||||
views.add(startView);
|
||||
for (int index = startIndex; index < views.size(); index++) {
|
||||
final View view = views.get(index);
|
||||
if (view instanceof ViewGroup) {
|
||||
ViewGroup viewGroup = (ViewGroup) view;
|
||||
final int childCount = viewGroup.getChildCount();
|
||||
for (int childIndex = 0; childIndex < childCount; childIndex++) {
|
||||
final View child = viewGroup.getChildAt(childIndex);
|
||||
if (!containedBeforeIndex(views, child, startIndex)) {
|
||||
views.add(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean containedBeforeIndex(@NonNull List<View> views, View view, int maxIndex) {
|
||||
for (int i = 0; i < maxIndex; i++) {
|
||||
if (views.get(i) == view) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean hasSimpleTarget(@NonNull Transition transition) {
|
||||
return !isNullOrEmpty(transition.getTargetIds())
|
||||
|| !isNullOrEmpty(transition.getTargetNames())
|
||||
|| !isNullOrEmpty(transition.getTargetTypes());
|
||||
}
|
||||
|
||||
private static boolean isNullOrEmpty(@Nullable List list) {
|
||||
return list == null || list.isEmpty();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static TransitionSet mergeTransitions(int ordering, Transition... transitions) {
|
||||
TransitionSet transitionSet = new TransitionSet();
|
||||
for (Transition transition : transitions) {
|
||||
if (transition != null) {
|
||||
transitionSet.addTransition(transition);
|
||||
}
|
||||
}
|
||||
transitionSet.setOrdering(ordering);
|
||||
return transitionSet;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -101,7 +101,7 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
|
||||
@NonNull
|
||||
public List<Router> getRouters() {
|
||||
return new ArrayList<>(routerMap.values());
|
||||
return new ArrayList<Router>(routerMap.values());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -133,13 +133,13 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
StringSparseArrayParceler permissionParcel = savedInstanceState.getParcelable(KEY_PERMISSION_REQUEST_CODES);
|
||||
permissionRequestMap = permissionParcel != null ? permissionParcel.getStringSparseArray() : new SparseArray<>();
|
||||
permissionRequestMap = permissionParcel != null ? permissionParcel.getStringSparseArray() : new SparseArray<String>();
|
||||
|
||||
StringSparseArrayParceler activityParcel = savedInstanceState.getParcelable(KEY_ACTIVITY_REQUEST_CODES);
|
||||
activityRequestMap = activityParcel != null ? activityParcel.getStringSparseArray() : new SparseArray<>();
|
||||
activityRequestMap = activityParcel != null ? activityParcel.getStringSparseArray() : new SparseArray<String>();
|
||||
|
||||
ArrayList<PendingPermissionRequest> pendingRequests = savedInstanceState.getParcelableArrayList(KEY_PENDING_PERMISSION_REQUESTS);
|
||||
pendingPermissionRequests = pendingRequests != null ? pendingRequests : new ArrayList<>();
|
||||
pendingPermissionRequests = pendingRequests != null ? pendingRequests : new ArrayList<PendingPermissionRequest>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,13 +159,16 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
if (activity != null) {
|
||||
activity.getApplication().unregisterActivityLifecycleCallbacks(this);
|
||||
activeLifecycleHandlers.remove(activity);
|
||||
destroyRouters();
|
||||
destroyRouters(false);
|
||||
activity = null;
|
||||
}
|
||||
|
||||
routerMap.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
this.activity = activity;
|
||||
super.onAttach(activity);
|
||||
destroyed = false;
|
||||
setAttached();
|
||||
@@ -173,6 +176,10 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
if (context instanceof Activity) {
|
||||
this.activity = (Activity) context;
|
||||
}
|
||||
|
||||
super.onAttach(context);
|
||||
destroyed = false;
|
||||
setAttached();
|
||||
@@ -183,7 +190,10 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
super.onDetach();
|
||||
|
||||
attached = false;
|
||||
destroyRouters();
|
||||
|
||||
if (activity != null) {
|
||||
destroyRouters(activity.isChangingConfigurations());
|
||||
}
|
||||
}
|
||||
|
||||
private void setAttached() {
|
||||
@@ -194,20 +204,20 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
PendingPermissionRequest request = pendingPermissionRequests.remove(i);
|
||||
requestPermissions(request.instanceId, request.permissions, request.requestCode);
|
||||
}
|
||||
}
|
||||
|
||||
for (ActivityHostedRouter router : new ArrayList<>(routerMap.values())) {
|
||||
router.onContextAvailable();
|
||||
for (ActivityHostedRouter router : new ArrayList<>(routerMap.values())) {
|
||||
router.onContextAvailable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void destroyRouters() {
|
||||
private void destroyRouters(boolean configurationChange) {
|
||||
if (!destroyed) {
|
||||
destroyed = true;
|
||||
|
||||
if (activity != null) {
|
||||
for (Router router : getRouters()) {
|
||||
router.onActivityDestroyed(activity);
|
||||
router.onActivityDestroyed(activity, configurationChange);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -319,7 +329,7 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
|
||||
if (this.activity == null && findInActivity(activity) == LifecycleHandler.this) {
|
||||
if (findInActivity(activity) == LifecycleHandler.this) {
|
||||
this.activity = activity;
|
||||
|
||||
for (ActivityHostedRouter router : new ArrayList<>(routerMap.values())) {
|
||||
|
||||
@@ -40,10 +40,12 @@ public class ViewAttachHandler implements OnAttachStateChangeListener {
|
||||
}
|
||||
|
||||
rootAttached = true;
|
||||
listenForDeepestChildAttach(v, () -> {
|
||||
childrenAttached = true;
|
||||
reportAttached();
|
||||
|
||||
listenForDeepestChildAttach(v, new ChildAttachListener() {
|
||||
@Override
|
||||
public void onAttached() {
|
||||
childrenAttached = true;
|
||||
reportAttached();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -65,6 +67,7 @@ public class ViewAttachHandler implements OnAttachStateChangeListener {
|
||||
|
||||
if (childOnAttachStateChangeListener != null && view instanceof ViewGroup) {
|
||||
findDeepestChild((ViewGroup)view).removeOnAttachStateChangeListener(childOnAttachStateChangeListener);
|
||||
childOnAttachStateChangeListener = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +121,7 @@ public class ViewAttachHandler implements OnAttachStateChangeListener {
|
||||
|
||||
@Override
|
||||
public void onViewAttachedToWindow(View v) {
|
||||
if (!attached) {
|
||||
if (!attached && childOnAttachStateChangeListener != null) {
|
||||
attached = true;
|
||||
attachListener.onAttached();
|
||||
v.removeOnAttachStateChangeListener(this);
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
-keepclassmembers public class * extends com.bluelinelabs.conductor.Controller {
|
||||
public <init>();
|
||||
public <init>(android.os.Bundle);
|
||||
}
|
||||
|
||||
-keepclassmembers public class * extends com.bluelinelabs.conductor.ControllerChangeHandler {
|
||||
public <init>();
|
||||
}
|
||||
|
||||
+6
-5
@@ -2,6 +2,7 @@ package com.bluelinelabs.conductor;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
@@ -85,7 +86,7 @@ public class ControllerLifecycleActivityReferenceTests {
|
||||
ActivityReferencingLifecycleListener listener = new ActivityReferencingLifecycleListener();
|
||||
child.addLifecycleListener(listener);
|
||||
|
||||
Router childRouter = parent.getChildRouter(parent.getView().findViewById(TestController.VIEW_ID));
|
||||
Router childRouter = parent.getChildRouter((ViewGroup) parent.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouter.pushController(RouterTransaction.with(child)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
@@ -132,7 +133,7 @@ public class ControllerLifecycleActivityReferenceTests {
|
||||
ActivityReferencingLifecycleListener listener = new ActivityReferencingLifecycleListener();
|
||||
child.addLifecycleListener(listener);
|
||||
|
||||
Router childRouter = parent.getChildRouter(parent.getView().findViewById(TestController.VIEW_ID));
|
||||
Router childRouter = parent.getChildRouter((ViewGroup) parent.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouter.setPopsLastView(true);
|
||||
childRouter.pushController(RouterTransaction.with(child)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
@@ -161,7 +162,7 @@ public class ControllerLifecycleActivityReferenceTests {
|
||||
ActivityReferencingLifecycleListener listener = new ActivityReferencingLifecycleListener();
|
||||
child.addLifecycleListener(listener);
|
||||
|
||||
Router childRouter = parent.getChildRouter(parent.getView().findViewById(TestController.VIEW_ID));
|
||||
Router childRouter = parent.getChildRouter((ViewGroup) parent.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouter.setPopsLastView(true);
|
||||
childRouter.pushController(RouterTransaction.with(child)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
@@ -211,7 +212,7 @@ public class ControllerLifecycleActivityReferenceTests {
|
||||
ActivityReferencingLifecycleListener listener = new ActivityReferencingLifecycleListener();
|
||||
child.addLifecycleListener(listener);
|
||||
|
||||
Router childRouter = parent.getChildRouter(parent.getView().findViewById(TestController.VIEW_ID));
|
||||
Router childRouter = parent.getChildRouter((ViewGroup) parent.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouter.setPopsLastView(true);
|
||||
childRouter.pushController(RouterTransaction.with(child)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
@@ -227,7 +228,7 @@ public class ControllerLifecycleActivityReferenceTests {
|
||||
assertEquals(Collections.singletonList(true), listener.postDestroyReferences);
|
||||
}
|
||||
|
||||
static class ActivityReferencingLifecycleListener implements Controller.LifecycleListener {
|
||||
static class ActivityReferencingLifecycleListener extends Controller.LifecycleListener {
|
||||
final List<Boolean> changeEndReferences = new ArrayList<>();
|
||||
final List<Boolean> postCreateViewReferences = new ArrayList<>();
|
||||
final List<Boolean> postAttachReferences = new ArrayList<>();
|
||||
|
||||
+30
-1
@@ -2,10 +2,11 @@ package com.bluelinelabs.conductor;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller.LifecycleListener;
|
||||
import com.bluelinelabs.conductor.Controller.RetainViewMode;
|
||||
import com.bluelinelabs.conductor.changehandler.SimpleSwapChangeHandler;
|
||||
@@ -590,6 +591,34 @@ public class ControllerLifecycleCallbacksTests {
|
||||
assertTrue(child.isAttached());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildLifecycleAfterPushPopPush() {
|
||||
Controller parent = new TestController();
|
||||
parent.setRetainViewMode(RetainViewMode.RETAIN_DETACH);
|
||||
router.pushController(RouterTransaction.with(parent)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
TestController child = new TestController();
|
||||
Router childRouter = parent.getChildRouter((ViewGroup)parent.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouter
|
||||
.setRoot(RouterTransaction.with(child)
|
||||
.pushChangeHandler(new SimpleSwapChangeHandler())
|
||||
.popChangeHandler(new SimpleSwapChangeHandler()));
|
||||
|
||||
Controller nextController = new TestController();
|
||||
router.pushController(RouterTransaction.with(nextController));
|
||||
|
||||
TestController child2 = new TestController();
|
||||
childRouter.pushController(RouterTransaction.with(child2));
|
||||
|
||||
router.popCurrentController();
|
||||
|
||||
assertTrue(parent.isAttached());
|
||||
assertFalse(child.isAttached());
|
||||
assertTrue(child2.isAttached());
|
||||
}
|
||||
|
||||
private MockChangeHandler getPushHandler(final CallState expectedCallState, final TestController controller) {
|
||||
return MockChangeHandler.listeningChangeHandler(new ChangeHandlerListener() {
|
||||
@Override
|
||||
|
||||
@@ -8,7 +8,6 @@ import com.bluelinelabs.conductor.util.ActivityProxy;
|
||||
import com.bluelinelabs.conductor.util.AttachFakingFrameLayout;
|
||||
import com.bluelinelabs.conductor.util.MockChangeHandler;
|
||||
import com.bluelinelabs.conductor.util.TestController;
|
||||
import com.bluelinelabs.conductor.util.ViewUtils;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -17,6 +16,7 @@ import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@@ -80,7 +80,7 @@ public class ReattachCaseTests {
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
Router childRouter = controllerA.getChildRouter((ViewGroup)controllerA.getView().findViewById(TestController.VIEW_ID));
|
||||
Router childRouter = controllerA.getChildRouter((ViewGroup) controllerA.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouter.pushController(RouterTransaction.with(childController)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
@@ -350,6 +350,54 @@ public class ReattachCaseTests {
|
||||
assertTrue(controller2.isAttached());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHostAvailableDuringRotation() {
|
||||
final Controller controllerA = new TestController();
|
||||
final Controller childControllerA = new TestController();
|
||||
final Controller controllerB = new TestController();
|
||||
final Controller childControllerB = new TestController();
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerA)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
Router childRouterA = controllerA.getChildRouter((ViewGroup) controllerA.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouterA.pushController(RouterTransaction.with(childControllerA)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
assertNotNull(controllerA.getActivity());
|
||||
assertNotNull(childControllerA.getActivity());
|
||||
|
||||
router.pushController(RouterTransaction.with(controllerB)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
Router childRouterB = controllerB.getChildRouter((ViewGroup) controllerB.getView().findViewById(TestController.VIEW_ID));
|
||||
childRouterB.pushController(RouterTransaction.with(childControllerB)
|
||||
.pushChangeHandler(MockChangeHandler.defaultHandler())
|
||||
.popChangeHandler(MockChangeHandler.defaultHandler()));
|
||||
|
||||
assertNotNull(controllerA.getActivity());
|
||||
assertNotNull(childControllerA.getActivity());
|
||||
assertNotNull(controllerB.getActivity());
|
||||
assertNotNull(childControllerB.getActivity());
|
||||
|
||||
activityProxy.rotate();
|
||||
|
||||
assertNotNull(controllerA.getActivity());
|
||||
assertNotNull(childControllerA.getActivity());
|
||||
assertNotNull(controllerB.getActivity());
|
||||
assertNotNull(childControllerB.getActivity());
|
||||
|
||||
router.rebindIfNeeded();
|
||||
|
||||
assertNotNull(controllerA.getActivity());
|
||||
assertNotNull(childControllerA.getActivity());
|
||||
assertNotNull(controllerB.getActivity());
|
||||
assertNotNull(childControllerB.getActivity());
|
||||
}
|
||||
|
||||
private void sleepWakeDevice() {
|
||||
activityProxy.saveInstanceState(new Bundle()).pause();
|
||||
activityProxy.resume();
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package com.bluelinelabs.conductor;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.bluelinelabs.conductor.changehandler.SimpleSwapChangeHandler;
|
||||
import com.bluelinelabs.conductor.util.ActivityProxy;
|
||||
import com.bluelinelabs.conductor.util.TestController;
|
||||
|
||||
@@ -110,6 +112,25 @@ public class ViewLeakTests {
|
||||
assertNull(controller.getView());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testViewRemovedIfLayeredNotRemovesFromViewOnPush() {
|
||||
Controller controller = new TestController();
|
||||
router.pushController(RouterTransaction.with(controller));
|
||||
|
||||
router.pushController(RouterTransaction.with(new TestController()).pushChangeHandler(new SimpleSwapChangeHandler(false)));
|
||||
|
||||
View view = controller.view;
|
||||
assertNotNull(view.getParent());
|
||||
|
||||
router.pushController(RouterTransaction.with(new TestController()));
|
||||
|
||||
assertNotNull(view.getParent());
|
||||
|
||||
router.popToRoot();
|
||||
|
||||
assertNull(view.getParent());
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
package com.bluelinelabs.conductor.util;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.IdRes;
|
||||
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.android.controller.ActivityController;
|
||||
|
||||
public class ActivityProxy {
|
||||
private final @IdRes int containerId = 4;
|
||||
|
||||
private ActivityController<TestActivity> activityController;
|
||||
private AttachFakingFrameLayout view;
|
||||
|
||||
public ActivityProxy() {
|
||||
activityController = Robolectric.buildActivity(TestActivity.class);
|
||||
|
||||
view = new AttachFakingFrameLayout(activityController.get());
|
||||
view.setId(containerId);
|
||||
}
|
||||
|
||||
public void setView(AttachFakingFrameLayout view) {
|
||||
this.view = view;
|
||||
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,74 @@
|
||||
package com.bluelinelabs.conductor.util
|
||||
|
||||
import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
|
||||
import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||
import android.os.Bundle
|
||||
import androidx.annotation.IdRes
|
||||
import org.robolectric.Robolectric
|
||||
|
||||
class ActivityProxy {
|
||||
|
||||
@IdRes
|
||||
private val containerId = 4
|
||||
private val activityController = Robolectric.buildActivity(TestActivity::class.java)
|
||||
|
||||
val activity: TestActivity
|
||||
get() = activityController.get()
|
||||
|
||||
var view: AttachFakingFrameLayout = AttachFakingFrameLayout(activityController.get())
|
||||
.also { it.id = containerId }
|
||||
set(value) {
|
||||
value.id = containerId
|
||||
field = value
|
||||
}
|
||||
|
||||
fun create(savedInstanceState: Bundle?): ActivityProxy {
|
||||
activityController.create(savedInstanceState)
|
||||
return this
|
||||
}
|
||||
|
||||
fun start(): ActivityProxy {
|
||||
activityController.start()
|
||||
view.setAttached(true)
|
||||
return this
|
||||
}
|
||||
|
||||
fun resume(): ActivityProxy {
|
||||
activityController.resume()
|
||||
return this
|
||||
}
|
||||
|
||||
fun pause(): ActivityProxy {
|
||||
activityController.pause()
|
||||
return this
|
||||
}
|
||||
|
||||
fun saveInstanceState(outState: Bundle?): ActivityProxy {
|
||||
activityController.saveInstanceState(outState)
|
||||
return this
|
||||
}
|
||||
|
||||
fun stop(detachView: Boolean): ActivityProxy {
|
||||
activityController.stop()
|
||||
if (detachView) {
|
||||
view.setAttached(false)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun destroy(): ActivityProxy {
|
||||
activityController.destroy()
|
||||
view.setAttached(false)
|
||||
return this
|
||||
}
|
||||
|
||||
fun rotate(): ActivityProxy {
|
||||
val requestedPortrait = activity.requestedOrientation == SCREEN_ORIENTATION_PORTRAIT
|
||||
activity.requestedOrientation = if (requestedPortrait) {
|
||||
SCREEN_ORIENTATION_LANDSCAPE
|
||||
} else {
|
||||
SCREEN_ORIENTATION_PORTRAIT
|
||||
}
|
||||
return this
|
||||
}
|
||||
}
|
||||
+21
-9
@@ -1,13 +1,7 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility 1.8
|
||||
targetCompatibility 1.8
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.bluelinelabs.conductor.demo"
|
||||
minSdkVersion 21
|
||||
@@ -27,20 +21,38 @@ android {
|
||||
packagingOptions {
|
||||
exclude 'META-INF/rxjava.properties'
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
viewBinding = true
|
||||
}
|
||||
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation rootProject.ext.androidxAppCompat
|
||||
implementation rootProject.ext.androidxViewPager2
|
||||
implementation rootProject.ext.material
|
||||
implementation rootProject.ext.androidxCoreKtx
|
||||
|
||||
implementation rootProject.ext.archComponentsLiveDataCore // Fix duplicate classes
|
||||
|
||||
annotationProcessor rootProject.ext.butterknifeCompiler
|
||||
implementation rootProject.ext.butterknife
|
||||
implementation rootProject.ext.picasso
|
||||
|
||||
implementation rootProject.ext.autodisposeKtx
|
||||
|
||||
implementation project(':conductor')
|
||||
implementation project(':conductor-modules:viewpager')
|
||||
implementation project(':conductor-modules:viewpager2')
|
||||
implementation project(':conductor-modules:rxlifecycle2')
|
||||
implementation project(':conductor-modules:autodispose')
|
||||
implementation project(':conductor-modules:arch-components-lifecycle')
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
package com.bluelinelabs.conductor.demo;
|
||||
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
|
||||
public interface ActionBarProvider {
|
||||
ActionBar getSupportActionBar();
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package com.bluelinelabs.conductor.demo;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import com.squareup.leakcanary.LeakCanary;
|
||||
import com.squareup.leakcanary.RefWatcher;
|
||||
|
||||
public class DemoApplication extends Application {
|
||||
|
||||
public static RefWatcher refWatcher;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
refWatcher = LeakCanary.install(this);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.bluelinelabs.conductor.demo
|
||||
|
||||
import android.app.Application
|
||||
import com.squareup.leakcanary.LeakCanary
|
||||
import com.squareup.leakcanary.RefWatcher
|
||||
|
||||
class DemoApplication : Application() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
refWatcher = LeakCanary.install(this)
|
||||
}
|
||||
|
||||
companion object {
|
||||
lateinit var refWatcher: RefWatcher
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package com.bluelinelabs.conductor.demo;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.Conductor;
|
||||
import com.bluelinelabs.conductor.Router;
|
||||
import com.bluelinelabs.conductor.RouterTransaction;
|
||||
import com.bluelinelabs.conductor.demo.controllers.HomeController;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
public final class MainActivity extends AppCompatActivity implements ActionBarProvider {
|
||||
|
||||
@BindView(R.id.toolbar) Toolbar toolbar;
|
||||
@BindView(R.id.controller_container) ViewGroup container;
|
||||
|
||||
private Router router;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_main);
|
||||
ButterKnife.bind(this);
|
||||
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
router = Conductor.attachRouter(this, container, savedInstanceState);
|
||||
if (!router.hasRootController()) {
|
||||
router.setRoot(RouterTransaction.with(new HomeController()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (!router.handleBack()) {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.bluelinelabs.conductor.demo
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import com.bluelinelabs.conductor.Conductor.attachRouter
|
||||
import com.bluelinelabs.conductor.Router
|
||||
import com.bluelinelabs.conductor.RouterTransaction.Companion.with
|
||||
import com.bluelinelabs.conductor.demo.controllers.HomeController
|
||||
import com.bluelinelabs.conductor.demo.databinding.ActivityMainBinding
|
||||
|
||||
class MainActivity : AppCompatActivity(), ToolbarProvider {
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
private lateinit var router: Router
|
||||
|
||||
override val toolbar: Toolbar
|
||||
get() = binding.toolbar
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
router = attachRouter(this, binding.controllerContainer, savedInstanceState)
|
||||
if (!router.hasRootController()) {
|
||||
router.setRoot(with(HomeController()))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (!router.handleBack()) {
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.bluelinelabs.conductor.demo
|
||||
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
|
||||
interface ToolbarProvider {
|
||||
val toolbar: Toolbar
|
||||
}
|
||||
+1
-1
@@ -51,7 +51,7 @@ public class CircularRevealChangeHandler extends AnimatorChangeHandler {
|
||||
* @param removesFromViewOnPush If true, the view being replaced will be removed from the view hierarchy on pushes
|
||||
*/
|
||||
public CircularRevealChangeHandler(@NonNull View fromView, @NonNull View containerView, boolean removesFromViewOnPush) {
|
||||
this(fromView, containerView, DEFAULT_ANIMATION_DURATION, true);
|
||||
this(fromView, containerView, DEFAULT_ANIMATION_DURATION, removesFromViewOnPush);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
-140
@@ -1,140 +0,0 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.lifecycle.Lifecycle.Event;
|
||||
import androidx.lifecycle.LifecycleObserver;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.OnLifecycleEvent;
|
||||
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
import com.bluelinelabs.conductor.ControllerChangeType;
|
||||
import com.bluelinelabs.conductor.RouterTransaction;
|
||||
import com.bluelinelabs.conductor.archlifecycle.LifecycleController;
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
|
||||
import com.bluelinelabs.conductor.demo.ActionBarProvider;
|
||||
import com.bluelinelabs.conductor.demo.DemoApplication;
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
import butterknife.Unbinder;
|
||||
|
||||
public class ArchLifecycleController extends LifecycleController {
|
||||
|
||||
private static final String TAG = "ArchLifecycleController";
|
||||
|
||||
@BindView(R.id.tv_title) TextView tvTitle;
|
||||
|
||||
private Unbinder unbinder;
|
||||
private boolean hasExited;
|
||||
|
||||
public ArchLifecycleController() {
|
||||
Log.i(TAG, "Conductor: Constructor called");
|
||||
|
||||
getLifecycle().addObserver(new LifecycleObserver() {
|
||||
@OnLifecycleEvent(Event.ON_ANY)
|
||||
void onLifecycleEvent(@NonNull LifecycleOwner source, @NonNull Event event) {
|
||||
Log.d(TAG, "Lifecycle: " + source.getClass().getSimpleName() + " emitted event " + event + " and is now in state " + source.getLifecycle().getCurrentState());
|
||||
}
|
||||
});
|
||||
|
||||
Log.d(TAG, "Lifecycle: " + getClass().getSimpleName() + " is now in state " + getLifecycle().getCurrentState());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onContextAvailable(@NonNull Context context) {
|
||||
Log.i(TAG, "Conductor: onContextAvailable() called");
|
||||
super.onContextAvailable(context);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container, @Nullable Bundle savedViewState) {
|
||||
Log.i(TAG, "Conductor: onCreateView() called");
|
||||
|
||||
View view = inflater.inflate(R.layout.controller_lifecycle, container, false);
|
||||
view.setBackgroundColor(ContextCompat.getColor(container.getContext(), R.color.orange_300));
|
||||
unbinder = ButterKnife.bind(this, view);
|
||||
|
||||
tvTitle.setText(getResources().getString(R.string.rxlifecycle_title, TAG));
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttach(@NonNull View view) {
|
||||
Log.i(TAG, "Conductor: onAttach() called");
|
||||
super.onAttach(view);
|
||||
|
||||
(((ActionBarProvider) getActivity()).getSupportActionBar()).setTitle("Arch Components Lifecycle Demo");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetach(@NonNull View view) {
|
||||
Log.i(TAG, "Conductor: onDetach() called");
|
||||
super.onDetach(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroyView(@NonNull View view) {
|
||||
Log.i(TAG, "Conductor: onDestroyView() called");
|
||||
super.onDestroyView(view);
|
||||
|
||||
unbinder.unbind();
|
||||
unbinder = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onContextUnavailable() {
|
||||
Log.i(TAG, "Conductor: onContextUnavailable() called");
|
||||
super.onContextUnavailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
Log.i(TAG, "Conductor: onDestroy() called");
|
||||
super.onDestroy();
|
||||
|
||||
if (hasExited) {
|
||||
DemoApplication.refWatcher.watch(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onChangeEnded(@NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
super.onChangeEnded(changeHandler, changeType);
|
||||
|
||||
hasExited = !changeType.isEnter;
|
||||
if (isDestroyed()) {
|
||||
DemoApplication.refWatcher.watch(this);
|
||||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.btn_next_release_view) void onNextWithReleaseClicked() {
|
||||
setRetainViewMode(RetainViewMode.RELEASE_DETACH);
|
||||
|
||||
getRouter().pushController(RouterTransaction.with(new TextController("Logcat should now report that the Controller's onDetach() and LifecycleObserver's onPause() methods were called, followed by the Controller's onDestroyView() and LifecycleObserver's onStop()."))
|
||||
.pushChangeHandler(new HorizontalChangeHandler())
|
||||
.popChangeHandler(new HorizontalChangeHandler()));
|
||||
}
|
||||
|
||||
@OnClick(R.id.btn_next_retain_view) void onNextWithRetainClicked() {
|
||||
setRetainViewMode(RetainViewMode.RETAIN_DETACH);
|
||||
|
||||
getRouter().pushController(RouterTransaction.with(new TextController("Logcat should now report that the Controller's onDetach() and LifecycleObserver's onPause() methods were called."))
|
||||
.pushChangeHandler(new HorizontalChangeHandler())
|
||||
.popChangeHandler(new HorizontalChangeHandler()));
|
||||
}
|
||||
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.OnLifecycleEvent
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||
import com.bluelinelabs.conductor.ControllerChangeType
|
||||
import com.bluelinelabs.conductor.RouterTransaction
|
||||
import com.bluelinelabs.conductor.archlifecycle.LifecycleController
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
|
||||
import com.bluelinelabs.conductor.demo.DemoApplication
|
||||
import com.bluelinelabs.conductor.demo.R
|
||||
import com.bluelinelabs.conductor.demo.ToolbarProvider
|
||||
import com.bluelinelabs.conductor.demo.databinding.ControllerLifecycleBinding
|
||||
|
||||
class ArchLifecycleController : LifecycleController() {
|
||||
|
||||
private var hasExited = false
|
||||
|
||||
init {
|
||||
Log.i(TAG, "Conductor: Constructor called")
|
||||
|
||||
lifecycle.addObserver(object : LifecycleObserver {
|
||||
@OnLifecycleEvent(Lifecycle.Event.ON_ANY)
|
||||
fun onLifecycleEvent(source: LifecycleOwner, event: Lifecycle.Event) {
|
||||
Log.d(
|
||||
TAG,
|
||||
"Lifecycle: " + source.javaClass.simpleName + " emitted event " + event + " and is now in state " + source.lifecycle.currentState
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
Log.d(TAG, "Lifecycle: " + javaClass.simpleName + " is now in state " + lifecycle.currentState)
|
||||
}
|
||||
|
||||
override fun onContextAvailable(context: Context) {
|
||||
Log.i(TAG, "Conductor: onContextAvailable() called")
|
||||
super.onContextAvailable(context)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
savedViewState: Bundle?
|
||||
): View {
|
||||
Log.i(TAG, "Conductor: onCreateView() called")
|
||||
|
||||
val binding = ControllerLifecycleBinding.inflate(inflater, container, false)
|
||||
|
||||
binding.root.setBackgroundColor(ContextCompat.getColor(container.context, R.color.orange_300))
|
||||
binding.title.text = binding.root.resources.getString(R.string.rxlifecycle_title, TAG)
|
||||
|
||||
binding.nextReleaseView.setOnClickListener {
|
||||
retainViewMode = RetainViewMode.RELEASE_DETACH
|
||||
|
||||
router.pushController(
|
||||
RouterTransaction.with(TextController("Logcat should now report that the Controller's onDetach() and LifecycleObserver's onPause() methods were called, followed by the Controller's onDestroyView() and LifecycleObserver's onStop()."))
|
||||
.pushChangeHandler(HorizontalChangeHandler())
|
||||
.popChangeHandler(HorizontalChangeHandler())
|
||||
)
|
||||
}
|
||||
|
||||
binding.nextRetainView.setOnClickListener {
|
||||
retainViewMode = RetainViewMode.RETAIN_DETACH
|
||||
|
||||
router.pushController(
|
||||
RouterTransaction.with(TextController("Logcat should now report that the Controller's onDetach() and LifecycleObserver's onPause() methods were called."))
|
||||
.pushChangeHandler(HorizontalChangeHandler())
|
||||
.popChangeHandler(HorizontalChangeHandler())
|
||||
)
|
||||
}
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onAttach(view: View) {
|
||||
Log.i(TAG, "Conductor: onAttach() called")
|
||||
super.onAttach(view)
|
||||
(activity as ToolbarProvider).toolbar.title = "Arch Components Lifecycle Demo"
|
||||
}
|
||||
|
||||
override fun onDetach(view: View) {
|
||||
Log.i(TAG, "Conductor: onDetach() called")
|
||||
super.onDetach(view)
|
||||
}
|
||||
|
||||
override fun onDestroyView(view: View) {
|
||||
Log.i(TAG, "Conductor: onDestroyView() called")
|
||||
super.onDestroyView(view)
|
||||
}
|
||||
|
||||
override fun onContextUnavailable() {
|
||||
Log.i(TAG, "Conductor: onContextUnavailable() called")
|
||||
super.onContextUnavailable()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
Log.i(TAG, "Conductor: onDestroy() called")
|
||||
super.onDestroy()
|
||||
if (hasExited) {
|
||||
DemoApplication.refWatcher.watch(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onChangeEnded(
|
||||
changeHandler: ControllerChangeHandler,
|
||||
changeType: ControllerChangeType
|
||||
) {
|
||||
super.onChangeEnded(changeHandler, changeType)
|
||||
hasExited = !changeType.isEnter
|
||||
if (isDestroyed) {
|
||||
DemoApplication.refWatcher.watch(this)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "ArchLifecycleController"
|
||||
}
|
||||
}
|
||||
-143
@@ -1,143 +0,0 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
import com.bluelinelabs.conductor.ControllerChangeType;
|
||||
import com.bluelinelabs.conductor.RouterTransaction;
|
||||
import com.bluelinelabs.conductor.autodispose.ControllerScopeProvider;
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
|
||||
import com.bluelinelabs.conductor.demo.ActionBarProvider;
|
||||
import com.bluelinelabs.conductor.demo.DemoApplication;
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.uber.autodispose.AutoDispose;
|
||||
import com.uber.autodispose.lifecycle.LifecycleScopeProvider;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
import butterknife.Unbinder;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.functions.Action;
|
||||
import io.reactivex.functions.Consumer;
|
||||
|
||||
// Shamelessly borrowed from the official RxLifecycle demo by Trello and adapted for Conductor Controllers
|
||||
// instead of Activities or Fragments.
|
||||
public class AutodisposeController extends Controller {
|
||||
|
||||
private static final String TAG = "AutodisposeController";
|
||||
|
||||
@BindView(R.id.tv_title) TextView tvTitle;
|
||||
|
||||
private Unbinder unbinder;
|
||||
private boolean hasExited;
|
||||
private final LifecycleScopeProvider scopeProvider = ControllerScopeProvider.from(this);
|
||||
|
||||
public AutodisposeController() {
|
||||
Observable.interval(1, TimeUnit.SECONDS)
|
||||
.doOnDispose(() -> Log.i(TAG, "Disposing from constructor"))
|
||||
.as(AutoDispose.<Long>autoDisposable((scopeProvider)))
|
||||
.subscribe(num -> Log.i(TAG, "Started in constructor, running until onDestroy(): " + num));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container, @Nullable Bundle savedViewState) {
|
||||
Log.i(TAG, "onCreateView() called");
|
||||
|
||||
View view = inflater.inflate(R.layout.controller_lifecycle, container, false);
|
||||
view.setBackgroundColor(ContextCompat.getColor(container.getContext(), R.color.purple_300));
|
||||
unbinder = ButterKnife.bind(this, view);
|
||||
|
||||
tvTitle.setText(getResources().getString(R.string.rxlifecycle_title, TAG));
|
||||
|
||||
Observable.interval(1, TimeUnit.SECONDS)
|
||||
.doOnDispose(() -> Log.i(TAG, "Disposing from onCreateView()"))
|
||||
.as(AutoDispose.<Long>autoDisposable((scopeProvider)))
|
||||
.subscribe(num -> Log.i(TAG, "Started in onCreateView(), running until onDestroyView(): " + num));
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttach(@NonNull View view) {
|
||||
super.onAttach(view);
|
||||
|
||||
Log.i(TAG, "onAttach() called");
|
||||
|
||||
(((ActionBarProvider) getActivity()).getSupportActionBar()).setTitle("Autodispose Demo");
|
||||
|
||||
Observable.interval(1, TimeUnit.SECONDS)
|
||||
.doOnDispose(() -> Log.i(TAG, "Disposing from onAttach()"))
|
||||
.as(AutoDispose.<Long>autoDisposable((scopeProvider)))
|
||||
.subscribe(num -> Log.i(TAG, "Started in onAttach(), running until onDetach(): " + num));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroyView(@NonNull View view) {
|
||||
super.onDestroyView(view);
|
||||
|
||||
Log.i(TAG, "onDestroyView() called");
|
||||
|
||||
unbinder.unbind();
|
||||
unbinder = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetach(@NonNull View view) {
|
||||
super.onDetach(view);
|
||||
|
||||
Log.i(TAG, "onDetach() called");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
Log.i(TAG, "onDestroy() called");
|
||||
|
||||
if (hasExited) {
|
||||
DemoApplication.refWatcher.watch(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onChangeEnded(@NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
super.onChangeEnded(changeHandler, changeType);
|
||||
|
||||
hasExited = !changeType.isEnter;
|
||||
if (isDestroyed()) {
|
||||
DemoApplication.refWatcher.watch(this);
|
||||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.btn_next_release_view)
|
||||
void onNextWithReleaseClicked() {
|
||||
setRetainViewMode(RetainViewMode.RELEASE_DETACH);
|
||||
|
||||
getRouter().pushController(RouterTransaction.with(new TextController("Logcat should now report that the observables from onAttach() and onViewBound() have been disposed of, while the constructor observable is still running."))
|
||||
.pushChangeHandler(new HorizontalChangeHandler())
|
||||
.popChangeHandler(new HorizontalChangeHandler()));
|
||||
}
|
||||
|
||||
@OnClick(R.id.btn_next_retain_view)
|
||||
void onNextWithRetainClicked() {
|
||||
setRetainViewMode(RetainViewMode.RETAIN_DETACH);
|
||||
|
||||
getRouter().pushController(RouterTransaction.with(new TextController("Logcat should now report that the observables from onAttach() has been disposed of, while the constructor and onViewBound() observables are still running."))
|
||||
.pushChangeHandler(new HorizontalChangeHandler())
|
||||
.popChangeHandler(new HorizontalChangeHandler()));
|
||||
}
|
||||
}
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.bluelinelabs.conductor.Controller
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||
import com.bluelinelabs.conductor.ControllerChangeType
|
||||
import com.bluelinelabs.conductor.RouterTransaction.Companion.with
|
||||
import com.bluelinelabs.conductor.autodispose.ControllerScopeProvider
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
|
||||
import com.bluelinelabs.conductor.demo.DemoApplication
|
||||
import com.bluelinelabs.conductor.demo.R
|
||||
import com.bluelinelabs.conductor.demo.ToolbarProvider
|
||||
import com.bluelinelabs.conductor.demo.databinding.ControllerLifecycleBinding
|
||||
import com.uber.autodispose.autoDisposable
|
||||
import io.reactivex.Observable
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
// Shamelessly borrowed from the official RxLifecycle demo by Trello and adapted for Conductor Controllers
|
||||
// instead of Activities or Fragments.
|
||||
class AutodisposeController : Controller() {
|
||||
|
||||
private var hasExited = false
|
||||
private val scopeProvider = ControllerScopeProvider.from(this)
|
||||
|
||||
init {
|
||||
Observable.interval(1, TimeUnit.SECONDS)
|
||||
.doOnDispose { Log.i(TAG, "Disposing from constructor") }
|
||||
.autoDisposable(scopeProvider)
|
||||
.subscribe { num: Long ->
|
||||
Log.i(TAG, "Started in constructor, running until onDestroy(): $num")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
savedViewState: Bundle?
|
||||
): View {
|
||||
Log.i(TAG, "onCreateView() called")
|
||||
val binding = ControllerLifecycleBinding.inflate(inflater, container, false)
|
||||
binding.title.text = binding.root.resources.getString(R.string.rxlifecycle_title, TAG)
|
||||
|
||||
binding.nextReleaseView.setOnClickListener {
|
||||
retainViewMode = RetainViewMode.RELEASE_DETACH
|
||||
router.pushController(
|
||||
with(TextController("Logcat should now report that the observables from onAttach() and onViewBound() have been disposed of, while the constructor observable is still running."))
|
||||
.pushChangeHandler(HorizontalChangeHandler())
|
||||
.popChangeHandler(HorizontalChangeHandler())
|
||||
)
|
||||
}
|
||||
|
||||
binding.nextRetainView.setOnClickListener {
|
||||
retainViewMode = RetainViewMode.RETAIN_DETACH
|
||||
router.pushController(
|
||||
with(TextController("Logcat should now report that the observables from onAttach() has been disposed of, while the constructor and onViewBound() observables are still running."))
|
||||
.pushChangeHandler(HorizontalChangeHandler())
|
||||
.popChangeHandler(HorizontalChangeHandler())
|
||||
)
|
||||
}
|
||||
|
||||
Observable.interval(1, TimeUnit.SECONDS)
|
||||
.doOnDispose { Log.i(TAG, "Disposing from onCreateView()") }
|
||||
.autoDisposable(scopeProvider)
|
||||
.subscribe { num: Long ->
|
||||
Log.i(TAG, "Started in onCreateView(), running until onDestroyView(): $num")
|
||||
}
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onAttach(view: View) {
|
||||
super.onAttach(view)
|
||||
Log.i(TAG, "onAttach() called")
|
||||
|
||||
(activity as ToolbarProvider).toolbar.title = "Autodispose Demo"
|
||||
Observable.interval(1, TimeUnit.SECONDS)
|
||||
.doOnDispose { Log.i(TAG, "Disposing from onAttach()") }
|
||||
.autoDisposable(scopeProvider)
|
||||
.subscribe { num: Long ->
|
||||
Log.i(TAG, "Started in onAttach(), running until onDetach(): $num")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView(view: View) {
|
||||
super.onDestroyView(view)
|
||||
Log.i(TAG, "onDestroyView() called")
|
||||
}
|
||||
|
||||
override fun onDetach(view: View) {
|
||||
super.onDetach(view)
|
||||
Log.i(TAG, "onDetach() called")
|
||||
}
|
||||
|
||||
public override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
Log.i(TAG, "onDestroy() called")
|
||||
if (hasExited) {
|
||||
DemoApplication.refWatcher.watch(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onChangeEnded(
|
||||
changeHandler: ControllerChangeHandler,
|
||||
changeType: ControllerChangeType
|
||||
) {
|
||||
super.onChangeEnded(changeHandler, changeType)
|
||||
hasExited = !changeType.isEnter
|
||||
if (isDestroyed) {
|
||||
DemoApplication.refWatcher.watch(this)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "AutodisposeController"
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
import com.bluelinelabs.conductor.demo.util.BundleBuilder;
|
||||
|
||||
import butterknife.BindView;
|
||||
|
||||
public class ChildController extends BaseController {
|
||||
|
||||
private static final String KEY_TITLE = "ChildController.title";
|
||||
private static final String KEY_BG_COLOR = "ChildController.bgColor";
|
||||
private static final String KEY_COLOR_IS_RES = "ChildController.colorIsResId";
|
||||
|
||||
@BindView(R.id.tv_title) TextView tvTitle;
|
||||
|
||||
public ChildController(String title, int backgroundColor, boolean colorIsResId) {
|
||||
this(new BundleBuilder(new Bundle())
|
||||
.putString(KEY_TITLE, title)
|
||||
.putInt(KEY_BG_COLOR, backgroundColor)
|
||||
.putBoolean(KEY_COLOR_IS_RES, colorIsResId)
|
||||
.build());
|
||||
}
|
||||
|
||||
public ChildController(Bundle args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
return inflater.inflate(R.layout.controller_child, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onViewBound(@NonNull View view) {
|
||||
super.onViewBound(view);
|
||||
|
||||
tvTitle.setText(getArgs().getString(KEY_TITLE));
|
||||
|
||||
int bgColor = getArgs().getInt(KEY_BG_COLOR);
|
||||
if (getArgs().getBoolean(KEY_COLOR_IS_RES)) {
|
||||
bgColor = ContextCompat.getColor(getActivity(), bgColor);
|
||||
}
|
||||
view.setBackgroundColor(bgColor);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.bundleOf
|
||||
import com.bluelinelabs.conductor.demo.R
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController
|
||||
import com.bluelinelabs.conductor.demo.databinding.ControllerChildBinding
|
||||
import com.bluelinelabs.conductor.demo.util.viewBinding
|
||||
|
||||
class ChildController(args: Bundle) : BaseController(R.layout.controller_child, args) {
|
||||
private val binding: ControllerChildBinding by viewBinding(ControllerChildBinding::bind)
|
||||
|
||||
constructor(title: String, backgroundColor: Int, colorIsResId: Boolean) : this(
|
||||
bundleOf(
|
||||
KEY_TITLE to title,
|
||||
KEY_BG_COLOR to backgroundColor,
|
||||
KEY_COLOR_IS_RES to colorIsResId
|
||||
)
|
||||
)
|
||||
|
||||
override fun onViewCreated(view: View) {
|
||||
binding.title.text = args.getString(KEY_TITLE)
|
||||
|
||||
val bgColor = args.getInt(KEY_BG_COLOR)
|
||||
if (args.getBoolean(KEY_COLOR_IS_RES)) {
|
||||
view.setBackgroundColor(ContextCompat.getColor(view.context, bgColor))
|
||||
} else {
|
||||
view.setBackgroundColor(bgColor)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val KEY_TITLE = "ChildController.title"
|
||||
private const val KEY_BG_COLOR = "ChildController.bgColor"
|
||||
private const val KEY_COLOR_IS_RES = "ChildController.colorIsResId"
|
||||
}
|
||||
}
|
||||
-167
@@ -1,167 +0,0 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
import com.bluelinelabs.conductor.demo.util.BundleBuilder;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
public class CityDetailController extends BaseController {
|
||||
|
||||
private static final String KEY_TITLE = "CityDetailController.title";
|
||||
private static final String KEY_IMAGE = "CityDetailController.image";
|
||||
|
||||
private static final String[] LIST_ROWS = new String[] {
|
||||
"• This is a city.",
|
||||
"• There's some cool stuff about it.",
|
||||
"• But really this is just a demo, not a city guide app.",
|
||||
"• This demo is meant to show some nice transitions.",
|
||||
"• You should have seen some sweet shared element transitions using the ImageView and the TextView in the \"header\" above.",
|
||||
"• This transition utilized some callbacks to ensure all the necessary rows in the RecyclerView were laid about before the transition occurred.",
|
||||
"• Just adding some more lines so it scrolls now...\n\n\n\n\n\n\nThe end."
|
||||
};
|
||||
|
||||
@BindView(R.id.recycler_view) RecyclerView recyclerView;
|
||||
|
||||
@DrawableRes private int imageDrawableRes;
|
||||
private String title;
|
||||
|
||||
public CityDetailController(@DrawableRes int imageDrawableRes, String title) {
|
||||
this(new BundleBuilder(new Bundle())
|
||||
.putInt(KEY_IMAGE, imageDrawableRes)
|
||||
.putString(KEY_TITLE, title)
|
||||
.build());
|
||||
}
|
||||
|
||||
public CityDetailController(Bundle args) {
|
||||
super(args);
|
||||
imageDrawableRes = getArgs().getInt(KEY_IMAGE);
|
||||
title = getArgs().getString(KEY_TITLE);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
return inflater.inflate(R.layout.controller_city_detail, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onViewBound(@NonNull View view) {
|
||||
super.onViewBound(view);
|
||||
|
||||
recyclerView.setHasFixedSize(true);
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(view.getContext()));
|
||||
recyclerView.setAdapter(new CityDetailAdapter(LayoutInflater.from(view.getContext()), title, imageDrawableRes, LIST_ROWS, title));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
static class CityDetailAdapter extends RecyclerView.Adapter<CityDetailAdapter.ViewHolder> {
|
||||
|
||||
private static final int VIEW_TYPE_HEADER = 0;
|
||||
private static final int VIEW_TYPE_DETAIL = 1;
|
||||
|
||||
private final LayoutInflater inflater;
|
||||
private final String title;
|
||||
@DrawableRes private final int imageDrawableRes;
|
||||
private final String imageViewTransitionName;
|
||||
private final String textViewTransitionName;
|
||||
private final String[] details;
|
||||
|
||||
public CityDetailAdapter(LayoutInflater inflater, String title, @DrawableRes int imageDrawableRes, String[] details, String transitionNameBase) {
|
||||
this.inflater = inflater;
|
||||
this.title = title;
|
||||
this.imageDrawableRes = imageDrawableRes;
|
||||
this.details = details;
|
||||
imageViewTransitionName = inflater.getContext().getResources().getString(R.string.transition_tag_image_named, transitionNameBase);
|
||||
textViewTransitionName = inflater.getContext().getResources().getString(R.string.transition_tag_title_named, transitionNameBase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if (position == 0) {
|
||||
return VIEW_TYPE_HEADER;
|
||||
} else {
|
||||
return VIEW_TYPE_DETAIL;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
if (viewType == VIEW_TYPE_HEADER) {
|
||||
return new HeaderViewHolder(inflater.inflate(R.layout.row_city_header, parent, false));
|
||||
} else {
|
||||
return new DetailViewHolder(inflater.inflate(R.layout.row_city_detail, parent, false));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||
if (getItemViewType(position) == VIEW_TYPE_HEADER) {
|
||||
((HeaderViewHolder)holder).bind(imageDrawableRes, title, imageViewTransitionName, textViewTransitionName);
|
||||
} else {
|
||||
((DetailViewHolder)holder).bind(details[position - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return 1 + details.length;
|
||||
}
|
||||
|
||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
ButterKnife.bind(this, itemView);
|
||||
}
|
||||
}
|
||||
|
||||
static class HeaderViewHolder extends ViewHolder {
|
||||
|
||||
@BindView(R.id.image_view) ImageView imageView;
|
||||
@BindView(R.id.text_view) TextView textView;
|
||||
|
||||
public HeaderViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
}
|
||||
|
||||
void bind(@DrawableRes int imageDrawableRes, String title, String imageTransitionName, String textViewTransitionName) {
|
||||
imageView.setImageResource(imageDrawableRes);
|
||||
textView.setText(title);
|
||||
|
||||
ViewCompat.setTransitionName(imageView, imageTransitionName);
|
||||
ViewCompat.setTransitionName(textView, textViewTransitionName);
|
||||
}
|
||||
}
|
||||
|
||||
static class DetailViewHolder extends ViewHolder {
|
||||
|
||||
@BindView(R.id.text_view) TextView textView;
|
||||
|
||||
public DetailViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
}
|
||||
|
||||
void bind(String detail) {
|
||||
textView.setText(detail);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import com.bluelinelabs.conductor.demo.R
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController
|
||||
import com.bluelinelabs.conductor.demo.databinding.ControllerCityDetailBinding
|
||||
import com.bluelinelabs.conductor.demo.databinding.RowCityDetailBinding
|
||||
import com.bluelinelabs.conductor.demo.databinding.RowCityHeaderBinding
|
||||
import com.bluelinelabs.conductor.demo.util.viewBinding
|
||||
|
||||
class CityDetailController(args: Bundle) : BaseController(R.layout.controller_city_detail, args) {
|
||||
private val binding: ControllerCityDetailBinding by viewBinding(ControllerCityDetailBinding::bind)
|
||||
|
||||
override val title = args.getString(KEY_TITLE)!!
|
||||
|
||||
constructor(@DrawableRes image: Int, title: String) : this(
|
||||
bundleOf(
|
||||
KEY_TITLE to title,
|
||||
KEY_IMAGE to image
|
||||
)
|
||||
)
|
||||
|
||||
override fun onViewCreated(view: View) {
|
||||
binding.recyclerView.setHasFixedSize(true)
|
||||
binding.recyclerView.layoutManager = LinearLayoutManager(view.context)
|
||||
binding.recyclerView.adapter = CityDetailAdapter(
|
||||
inflater = LayoutInflater.from(view.context),
|
||||
title = title,
|
||||
imageDrawableRes = args.getInt(KEY_IMAGE),
|
||||
details = LIST_ROWS,
|
||||
transitionNameBase = title
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val KEY_TITLE = "CityDetailController.title"
|
||||
private const val KEY_IMAGE = "CityDetailController.image"
|
||||
|
||||
private val LIST_ROWS = listOf(
|
||||
"• This is a city.",
|
||||
"• There's some cool stuff about it.",
|
||||
"• But really this is just a demo, not a city guide app.",
|
||||
"• This demo is meant to show some nice transitions.",
|
||||
"• You should have seen some sweet shared element transitions using the ImageView and the TextView in the \"header\" above.",
|
||||
"• This transition utilized some callbacks to ensure all the necessary rows in the RecyclerView were laid about before the transition occurred.",
|
||||
"• Just adding some more lines so it scrolls now...\n\n\n\n\n\n\nThe end."
|
||||
)
|
||||
}
|
||||
|
||||
class CityDetailAdapter(
|
||||
private val inflater: LayoutInflater,
|
||||
private val title: String,
|
||||
@DrawableRes private val imageDrawableRes: Int,
|
||||
private val details: List<String>,
|
||||
private val transitionNameBase: String
|
||||
) : RecyclerView.Adapter<ViewHolder<*>>() {
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return if (position == 0) VIEW_TYPE_HEADER else VIEW_TYPE_DETAIL
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder<*> {
|
||||
return if (viewType == VIEW_TYPE_HEADER) {
|
||||
HeaderViewHolder(RowCityHeaderBinding.inflate(inflater, parent, false))
|
||||
} else {
|
||||
DetailViewHolder(RowCityDetailBinding.inflate(inflater, parent, false))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder<*>, position: Int) {
|
||||
when (holder) {
|
||||
is HeaderViewHolder -> {
|
||||
holder.bind(
|
||||
imageDrawableRes = imageDrawableRes,
|
||||
title = title,
|
||||
imageTransitionName = inflater.context.resources.getString(R.string.transition_tag_image_named, transitionNameBase),
|
||||
textViewTransitionName = inflater.context.resources.getString(R.string.transition_tag_title_named, transitionNameBase)
|
||||
)
|
||||
}
|
||||
is DetailViewHolder -> {
|
||||
holder.bind(details[position - 1])
|
||||
}
|
||||
else -> {
|
||||
throw IllegalStateException("Invalid view holder class ${holder.javaClass.canonicalName}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount() = details.size + 1
|
||||
|
||||
companion object {
|
||||
private const val VIEW_TYPE_HEADER = 0
|
||||
private const val VIEW_TYPE_DETAIL = 1
|
||||
}
|
||||
}
|
||||
|
||||
open class ViewHolder<Binding : ViewBinding>(binding: Binding) : RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
class HeaderViewHolder(private val binding: RowCityHeaderBinding) : ViewHolder<RowCityHeaderBinding>(binding) {
|
||||
fun bind(@DrawableRes imageDrawableRes: Int, title: String?, imageTransitionName: String?, textViewTransitionName: String?) {
|
||||
binding.imageView.setImageResource(imageDrawableRes)
|
||||
binding.textView.text = title
|
||||
binding.imageView.transitionName = imageTransitionName
|
||||
binding.textView.transitionName = textViewTransitionName
|
||||
}
|
||||
}
|
||||
|
||||
class DetailViewHolder(private val binding: RowCityDetailBinding) : ViewHolder<RowCityDetailBinding>(binding) {
|
||||
fun bind(detail: String) {
|
||||
binding.textView.text = detail
|
||||
}
|
||||
}
|
||||
}
|
||||
-168
@@ -1,168 +0,0 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers;
|
||||
|
||||
import android.graphics.PorterDuff.Mode;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bluelinelabs.conductor.RouterTransaction;
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.changehandler.CityGridSharedElementTransitionChangeHandler;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
import com.bluelinelabs.conductor.demo.util.BundleBuilder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
|
||||
public class CityGridController extends BaseController {
|
||||
|
||||
private static final String KEY_TITLE = "CityGridController.title";
|
||||
private static final String KEY_DOT_COLOR = "CityGridController.dotColor";
|
||||
private static final String KEY_FROM_POSITION = "CityGridController.position";
|
||||
|
||||
private static final CityModel[] CITY_MODELS = new CityModel[] {
|
||||
new CityModel(R.drawable.chicago, "Chicago"),
|
||||
new CityModel(R.drawable.jakarta, "Jakarta"),
|
||||
new CityModel(R.drawable.london, "London"),
|
||||
new CityModel(R.drawable.sao_paulo, "Sao Paulo"),
|
||||
new CityModel(R.drawable.tokyo, "Tokyo")
|
||||
};
|
||||
|
||||
@BindView(R.id.tv_title) TextView tvTitle;
|
||||
@BindView(R.id.img_dot) ImageView imgDot;
|
||||
@BindView(R.id.recycler_view) RecyclerView recyclerView;
|
||||
|
||||
private String title;
|
||||
private int dotColor;
|
||||
private int fromPosition;
|
||||
|
||||
public CityGridController(String title, int dotColor, int fromPosition) {
|
||||
this(new BundleBuilder(new Bundle())
|
||||
.putString(KEY_TITLE, title)
|
||||
.putInt(KEY_DOT_COLOR, dotColor)
|
||||
.putInt(KEY_FROM_POSITION, fromPosition)
|
||||
.build());
|
||||
}
|
||||
|
||||
public CityGridController(Bundle args) {
|
||||
super(args);
|
||||
title = getArgs().getString(KEY_TITLE);
|
||||
dotColor = getArgs().getInt(KEY_DOT_COLOR);
|
||||
fromPosition = getArgs().getInt(KEY_FROM_POSITION);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
return inflater.inflate(R.layout.controller_city_grid, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onViewBound(@NonNull View view) {
|
||||
super.onViewBound(view);
|
||||
|
||||
tvTitle.setText(title);
|
||||
imgDot.getDrawable().setColorFilter(ContextCompat.getColor(getActivity(), dotColor), Mode.SRC_ATOP);
|
||||
|
||||
ViewCompat.setTransitionName(tvTitle, getResources().getString(R.string.transition_tag_title_indexed, fromPosition));
|
||||
ViewCompat.setTransitionName(imgDot, getResources().getString(R.string.transition_tag_dot_indexed, fromPosition));
|
||||
|
||||
recyclerView.setHasFixedSize(true);
|
||||
recyclerView.setLayoutManager(new GridLayoutManager(view.getContext(), 2));
|
||||
recyclerView.setAdapter(new CityGridAdapter(LayoutInflater.from(view.getContext()), CITY_MODELS));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
return "Shared Element Demos";
|
||||
}
|
||||
|
||||
void onModelRowClick(CityModel model) {
|
||||
String imageTransitionName = getResources().getString(R.string.transition_tag_image_named, model.title);
|
||||
String titleTransitionName = getResources().getString(R.string.transition_tag_title_named, model.title);
|
||||
|
||||
List<String> names = new ArrayList<>();
|
||||
names.add(imageTransitionName);
|
||||
names.add(titleTransitionName);
|
||||
|
||||
getRouter().pushController(RouterTransaction.with(new CityDetailController(model.drawableRes, model.title))
|
||||
.pushChangeHandler(new CityGridSharedElementTransitionChangeHandler(names))
|
||||
.popChangeHandler(new CityGridSharedElementTransitionChangeHandler(names)));
|
||||
}
|
||||
|
||||
class CityGridAdapter extends RecyclerView.Adapter<CityGridAdapter.ViewHolder> {
|
||||
|
||||
private final LayoutInflater inflater;
|
||||
private final CityModel[] items;
|
||||
|
||||
public CityGridAdapter(LayoutInflater inflater, CityModel[] items) {
|
||||
this.inflater = inflater;
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
return new ViewHolder(inflater.inflate(R.layout.row_city_grid, parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||
holder.bind(items[position]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return items.length;
|
||||
}
|
||||
|
||||
class ViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
@BindView(R.id.tv_title) TextView textView;
|
||||
@BindView(R.id.img_city) ImageView imageView;
|
||||
private CityModel model;
|
||||
|
||||
public ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
ButterKnife.bind(this, itemView);
|
||||
}
|
||||
|
||||
void bind(CityModel item) {
|
||||
model = item;
|
||||
imageView.setImageResource(item.drawableRes);
|
||||
textView.setText(item.title);
|
||||
|
||||
ViewCompat.setTransitionName(textView, getResources().getString(R.string.transition_tag_title_named, model.title));
|
||||
ViewCompat.setTransitionName(imageView, getResources().getString(R.string.transition_tag_image_named, model.title));
|
||||
}
|
||||
|
||||
@OnClick(R.id.row_root)
|
||||
void onRowClick() {
|
||||
onModelRowClick(model);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static class CityModel {
|
||||
@DrawableRes int drawableRes;
|
||||
String title;
|
||||
|
||||
public CityModel(@DrawableRes int drawableRes, String title) {
|
||||
this.drawableRes = drawableRes;
|
||||
this.title = title;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers
|
||||
|
||||
import android.graphics.PorterDuff
|
||||
import android.os.Bundle
|
||||
import android.support.annotation.DrawableRes
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bluelinelabs.conductor.RouterTransaction
|
||||
import com.bluelinelabs.conductor.demo.R
|
||||
import com.bluelinelabs.conductor.demo.changehandler.CityGridSharedElementTransitionChangeHandler
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController
|
||||
import com.bluelinelabs.conductor.demo.databinding.ControllerCityGridBinding
|
||||
import com.bluelinelabs.conductor.demo.databinding.RowCityGridBinding
|
||||
import com.bluelinelabs.conductor.demo.util.viewBinding
|
||||
|
||||
class CityGridController(args: Bundle) : BaseController(R.layout.controller_city_grid, args) {
|
||||
private val binding: ControllerCityGridBinding by viewBinding(ControllerCityGridBinding::bind)
|
||||
|
||||
override val title = "Shared Element Demos"
|
||||
|
||||
constructor(title: String?, dotColor: Int, fromPosition: Int) : this(
|
||||
bundleOf(
|
||||
KEY_TITLE to title,
|
||||
KEY_DOT_COLOR to dotColor,
|
||||
KEY_FROM_POSITION to fromPosition
|
||||
)
|
||||
)
|
||||
|
||||
override fun onViewCreated(view: View) {
|
||||
super.onViewCreated(view)
|
||||
|
||||
binding.title.text = args.getString(KEY_TITLE)!!
|
||||
binding.dot.drawable.setColorFilter(ContextCompat.getColor(view.context, args.getInt(KEY_DOT_COLOR)), PorterDuff.Mode.SRC_ATOP)
|
||||
|
||||
binding.title.transitionName = view.resources.getString(R.string.transition_tag_title_indexed, args.getInt(KEY_FROM_POSITION))
|
||||
binding.dot.transitionName = view.resources.getString(R.string.transition_tag_dot_indexed, args.getInt(KEY_FROM_POSITION))
|
||||
|
||||
binding.recyclerView.setHasFixedSize(true)
|
||||
binding.recyclerView.layoutManager = GridLayoutManager(view.context, 2)
|
||||
binding.recyclerView.adapter = CityGridAdapter(LayoutInflater.from(view.context), CITY_MODELS, ::onModelRowClick)
|
||||
}
|
||||
|
||||
private fun onModelRowClick(model: CityModel) {
|
||||
val names = listOf(
|
||||
resources!!.getString(R.string.transition_tag_title_named, model.title),
|
||||
resources!!.getString(R.string.transition_tag_image_named, model.title)
|
||||
)
|
||||
|
||||
router.pushController(
|
||||
RouterTransaction.with(CityDetailController(model.drawableRes, model.title))
|
||||
.pushChangeHandler(CityGridSharedElementTransitionChangeHandler(names))
|
||||
.popChangeHandler(CityGridSharedElementTransitionChangeHandler(names))
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val KEY_TITLE = "CityGridController.title"
|
||||
private const val KEY_DOT_COLOR = "CityGridController.dotColor"
|
||||
private const val KEY_FROM_POSITION = "CityGridController.position"
|
||||
|
||||
private val CITY_MODELS = arrayOf(
|
||||
CityModel(R.drawable.chicago, "Chicago"),
|
||||
CityModel(R.drawable.jakarta, "Jakarta"),
|
||||
CityModel(R.drawable.london, "London"),
|
||||
CityModel(R.drawable.sao_paulo, "Sao Paulo"),
|
||||
CityModel(R.drawable.tokyo, "Tokyo")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class CityModel(@DrawableRes val drawableRes: Int, val title: String)
|
||||
|
||||
private class CityGridAdapter(
|
||||
private val inflater: LayoutInflater,
|
||||
private val items: Array<CityModel>,
|
||||
private val modelClickListener: (CityModel) -> Unit
|
||||
) : RecyclerView.Adapter<CityGridAdapter.ViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return ViewHolder(
|
||||
RowCityGridBinding.inflate(inflater, parent, false),
|
||||
modelClickListener
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
holder.bind(items[position])
|
||||
}
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
|
||||
class ViewHolder(
|
||||
private val binding: RowCityGridBinding,
|
||||
private val modelClickListener: (CityModel) -> Unit
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(item: CityModel) {
|
||||
binding.image.setImageResource(item.drawableRes)
|
||||
binding.title.text = item.title
|
||||
|
||||
binding.image.transitionName = itemView.resources.getString(R.string.transition_tag_image_named, item.title)
|
||||
binding.image.transitionName = itemView.resources.getString(R.string.transition_tag_title_named, item.title)
|
||||
|
||||
itemView.setOnClickListener { modelClickListener(item) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers;
|
||||
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
import com.bluelinelabs.conductor.demo.util.BundleBuilder;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.OnClick;
|
||||
|
||||
public class DialogController extends BaseController {
|
||||
|
||||
private static final String KEY_TITLE = "DialogController.title";
|
||||
private static final String KEY_DESCRIPTION = "DialogController.description";
|
||||
|
||||
@BindView(R.id.tv_title) TextView tvTitle;
|
||||
@BindView(R.id.tv_description) TextView tvDescription;
|
||||
|
||||
public DialogController(CharSequence title, CharSequence description) {
|
||||
this(new BundleBuilder(new Bundle())
|
||||
.putCharSequence(KEY_TITLE, title)
|
||||
.putCharSequence(KEY_DESCRIPTION, description)
|
||||
.build());
|
||||
}
|
||||
|
||||
public DialogController(Bundle args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
return inflater.inflate(R.layout.controller_dialog, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewBound(@NonNull View view) {
|
||||
super.onViewBound(view);
|
||||
tvTitle.setText(getArgs().getCharSequence(KEY_TITLE));
|
||||
tvDescription.setText(getArgs().getCharSequence(KEY_DESCRIPTION));
|
||||
tvDescription.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
}
|
||||
|
||||
@OnClick({R.id.dismiss, R.id.dialog_window})
|
||||
public void dismissDialog() {
|
||||
getRouter().popController(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleBack() {
|
||||
return super.handleBack();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers
|
||||
|
||||
import android.os.Bundle
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.view.View
|
||||
import androidx.core.os.bundleOf
|
||||
import com.bluelinelabs.conductor.demo.R
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController
|
||||
import com.bluelinelabs.conductor.demo.databinding.ControllerDialogBinding
|
||||
import com.bluelinelabs.conductor.demo.util.viewBinding
|
||||
|
||||
class DialogController(args: Bundle) : BaseController(R.layout.controller_dialog, args) {
|
||||
private val binding: ControllerDialogBinding by viewBinding(ControllerDialogBinding::bind)
|
||||
|
||||
constructor(title: CharSequence, description: CharSequence) : this(
|
||||
bundleOf(
|
||||
KEY_TITLE to title,
|
||||
KEY_DESCRIPTION to description
|
||||
)
|
||||
)
|
||||
|
||||
override fun onViewCreated(view: View) {
|
||||
binding.title.text = args.getCharSequence(KEY_TITLE)
|
||||
binding.description.text = args.getCharSequence(KEY_DESCRIPTION)
|
||||
binding.description.movementMethod = LinkMovementMethod.getInstance()
|
||||
|
||||
binding.dismiss.setOnClickListener { router.popController(this) }
|
||||
binding.dialogBackground.setOnClickListener { router.popController(this) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val KEY_TITLE = "DialogController.title"
|
||||
private const val KEY_DESCRIPTION = "DialogController.description"
|
||||
}
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.changehandler.ScaleFadeChangeHandler;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
import com.bluelinelabs.conductor.demo.widget.ElasticDragDismissFrameLayout;
|
||||
import com.bluelinelabs.conductor.demo.widget.ElasticDragDismissFrameLayout.ElasticDragDismissCallback;
|
||||
|
||||
@TargetApi(VERSION_CODES.LOLLIPOP)
|
||||
public class DragDismissController extends BaseController {
|
||||
|
||||
private final ElasticDragDismissCallback dragDismissListener = new ElasticDragDismissCallback() {
|
||||
@Override
|
||||
public void onDragDismissed() {
|
||||
overridePopHandler(new ScaleFadeChangeHandler());
|
||||
getRouter().popController(DragDismissController.this);
|
||||
}
|
||||
};
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
return inflater.inflate(R.layout.controller_drag_dismiss, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onViewBound(@NonNull View view) {
|
||||
((ElasticDragDismissFrameLayout)view).addListener(dragDismissListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroyView(@NonNull View view) {
|
||||
super.onDestroyView(view);
|
||||
|
||||
((ElasticDragDismissFrameLayout)view).removeListener(dragDismissListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
return "Drag to Dismiss";
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers
|
||||
|
||||
import android.view.View
|
||||
import com.bluelinelabs.conductor.demo.R
|
||||
import com.bluelinelabs.conductor.demo.changehandler.ScaleFadeChangeHandler
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController
|
||||
import com.bluelinelabs.conductor.demo.widget.ElasticDragDismissFrameLayout
|
||||
import com.bluelinelabs.conductor.demo.widget.ElasticDragDismissFrameLayout.ElasticDragDismissCallback
|
||||
|
||||
class DragDismissController : BaseController(R.layout.controller_drag_dismiss) {
|
||||
override val title = "Drag to Dismiss"
|
||||
|
||||
private val dragDismissListener: ElasticDragDismissCallback = object : ElasticDragDismissCallback() {
|
||||
override fun onDragDismissed() {
|
||||
overridePopHandler(ScaleFadeChangeHandler())
|
||||
router.popController(this@DragDismissController)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View) {
|
||||
(view as ElasticDragDismissFrameLayout).addListener(dragDismissListener)
|
||||
}
|
||||
|
||||
override fun onDestroyView(view: View) {
|
||||
super.onDestroyView(view)
|
||||
(view as ElasticDragDismissFrameLayout).removeListener(dragDismissListener)
|
||||
}
|
||||
}
|
||||
-147
@@ -1,147 +0,0 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers;
|
||||
|
||||
import android.graphics.PorterDuff.Mode;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.ColorRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
import com.bluelinelabs.conductor.ControllerChangeType;
|
||||
import com.bluelinelabs.conductor.RouterTransaction;
|
||||
import com.bluelinelabs.conductor.changehandler.FadeChangeHandler;
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
|
||||
import androidx.annotation.ColorRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
|
||||
public class ExternalModulesController extends BaseController {
|
||||
|
||||
private enum DemoModel {
|
||||
AUTODISPOSE("Autodispose", R.color.purple_300),
|
||||
RX_LIFECYCLE_2("Rx Lifecycle 2", R.color.blue_grey_300),
|
||||
ARCH_LIFECYCLE("Arch Components Lifecycle", R.color.orange_300);
|
||||
|
||||
String title;
|
||||
@ColorRes int color;
|
||||
|
||||
DemoModel(String title, @ColorRes int color) {
|
||||
this.title = title;
|
||||
this.color = color;
|
||||
}
|
||||
}
|
||||
|
||||
@BindView(R.id.recycler_view) RecyclerView recyclerView;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
return inflater.inflate(R.layout.controller_additional_modules, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onViewBound(@NonNull View view) {
|
||||
super.onViewBound(view);
|
||||
|
||||
recyclerView.setHasFixedSize(true);
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(view.getContext()));
|
||||
recyclerView.setAdapter(new AdditionalModulesAdapter(LayoutInflater.from(view.getContext()), DemoModel.values()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onChangeStarted(@NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
if (changeType.isEnter) {
|
||||
setTitle();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
return "External Module Demos";
|
||||
}
|
||||
|
||||
void onModelRowClick(DemoModel model) {
|
||||
switch (model) {
|
||||
case AUTODISPOSE:
|
||||
getRouter().pushController(RouterTransaction.with(new AutodisposeController())
|
||||
.pushChangeHandler(new FadeChangeHandler())
|
||||
.popChangeHandler(new FadeChangeHandler()));
|
||||
break;
|
||||
case RX_LIFECYCLE_2:
|
||||
getRouter().pushController(RouterTransaction.with(new RxLifecycle2Controller())
|
||||
.pushChangeHandler(new FadeChangeHandler())
|
||||
.popChangeHandler(new FadeChangeHandler()));
|
||||
break;
|
||||
case ARCH_LIFECYCLE:
|
||||
getRouter().pushController(RouterTransaction.with(new ArchLifecycleController())
|
||||
.pushChangeHandler(new FadeChangeHandler())
|
||||
.popChangeHandler(new FadeChangeHandler()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
class AdditionalModulesAdapter extends RecyclerView.Adapter<AdditionalModulesAdapter.ViewHolder> {
|
||||
|
||||
private final LayoutInflater inflater;
|
||||
private final DemoModel[] items;
|
||||
|
||||
public AdditionalModulesAdapter(LayoutInflater inflater, DemoModel[] items) {
|
||||
this.inflater = inflater;
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
return new ViewHolder(inflater.inflate(R.layout.row_home, parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||
holder.bind(items[position]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return items.length;
|
||||
}
|
||||
|
||||
class ViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
@BindView(R.id.tv_title) TextView tvTitle;
|
||||
@BindView(R.id.img_dot) ImageView imgDot;
|
||||
private DemoModel model;
|
||||
|
||||
public ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
ButterKnife.bind(this, itemView);
|
||||
}
|
||||
|
||||
void bind(DemoModel item) {
|
||||
model = item;
|
||||
tvTitle.setText(item.title);
|
||||
imgDot.getDrawable().setColorFilter(ContextCompat.getColor(getActivity(), item.color), Mode.SRC_ATOP);
|
||||
}
|
||||
|
||||
@OnClick(R.id.row_root)
|
||||
void onRowClick() {
|
||||
onModelRowClick(model);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers
|
||||
|
||||
import android.graphics.PorterDuff
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bluelinelabs.conductor.RouterTransaction.Companion.with
|
||||
import com.bluelinelabs.conductor.changehandler.FadeChangeHandler
|
||||
import com.bluelinelabs.conductor.demo.R
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController
|
||||
import com.bluelinelabs.conductor.demo.databinding.ControllerAdditionalModulesBinding
|
||||
import com.bluelinelabs.conductor.demo.databinding.RowHomeBinding
|
||||
import com.bluelinelabs.conductor.demo.util.viewBinding
|
||||
|
||||
class ExternalModulesController : BaseController(R.layout.controller_additional_modules) {
|
||||
private val binding: ControllerAdditionalModulesBinding by viewBinding(ControllerAdditionalModulesBinding::bind)
|
||||
|
||||
override val title = "External Module Demos"
|
||||
|
||||
override fun onViewCreated(view: View) {
|
||||
super.onViewCreated(view)
|
||||
|
||||
binding.recyclerView.setHasFixedSize(true)
|
||||
binding.recyclerView.layoutManager = LinearLayoutManager(view.context)
|
||||
binding.recyclerView.adapter = AdditionalModulesAdapter(
|
||||
LayoutInflater.from(view.context),
|
||||
ModuleModel.values(),
|
||||
::onModelRowClick
|
||||
)
|
||||
}
|
||||
|
||||
private fun onModelRowClick(model: ModuleModel) {
|
||||
when (model) {
|
||||
ModuleModel.AUTODISPOSE -> router.pushController(
|
||||
with(AutodisposeController())
|
||||
.pushChangeHandler(FadeChangeHandler())
|
||||
.popChangeHandler(FadeChangeHandler())
|
||||
)
|
||||
ModuleModel.RX_LIFECYCLE_2 -> router.pushController(
|
||||
with(RxLifecycle2Controller())
|
||||
.pushChangeHandler(FadeChangeHandler())
|
||||
.popChangeHandler(FadeChangeHandler())
|
||||
)
|
||||
ModuleModel.ARCH_LIFECYCLE -> router.pushController(
|
||||
with(ArchLifecycleController())
|
||||
.pushChangeHandler(FadeChangeHandler())
|
||||
.popChangeHandler(FadeChangeHandler())
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum class ModuleModel(val title: String, @ColorRes val color: Int) {
|
||||
AUTODISPOSE("Autodispose", R.color.purple_300),
|
||||
RX_LIFECYCLE_2("Rx Lifecycle 2", R.color.blue_grey_300),
|
||||
ARCH_LIFECYCLE("Arch Components Lifecycle", R.color.orange_300);
|
||||
}
|
||||
|
||||
private class AdditionalModulesAdapter(
|
||||
private val inflater: LayoutInflater,
|
||||
private val items: Array<ModuleModel>,
|
||||
private val modelClickListener: (ModuleModel) -> Unit
|
||||
) : RecyclerView.Adapter<AdditionalModulesAdapter.ViewHolder>() {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return ViewHolder(RowHomeBinding.inflate(inflater, parent, false), modelClickListener)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
holder.bind(items[position])
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return items.size
|
||||
}
|
||||
|
||||
class ViewHolder(
|
||||
private val binding: RowHomeBinding,
|
||||
private val modelClickListener: (ModuleModel) -> Unit
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
fun bind(item: ModuleModel) {
|
||||
binding.title.text = item.title
|
||||
binding.dot.drawable.setColorFilter(ContextCompat.getColor(itemView.context, item.color), PorterDuff.Mode.SRC_ATOP)
|
||||
itemView.setOnClickListener { modelClickListener(item) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,280 +0,0 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.PorterDuff.Mode;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.SpannableString;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.AbsoluteSizeSpan;
|
||||
import android.text.style.URLSpan;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.ColorRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
import com.bluelinelabs.conductor.ControllerChangeType;
|
||||
import com.bluelinelabs.conductor.RouterTransaction;
|
||||
import com.bluelinelabs.conductor.changehandler.FadeChangeHandler;
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.changehandler.ArcFadeMoveChangeHandler;
|
||||
import com.bluelinelabs.conductor.demo.changehandler.FabToDialogTransitionChangeHandler;
|
||||
import com.bluelinelabs.conductor.demo.controllers.NavigationDemoController.DisplayUpMode;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
|
||||
public class HomeController extends BaseController {
|
||||
|
||||
private enum DemoModel {
|
||||
NAVIGATION("Navigation Demos", R.color.red_300),
|
||||
TRANSITIONS("Transition Demos", R.color.blue_grey_300),
|
||||
SHARED_ELEMENT_TRANSITIONS("Shared Element Demos", R.color.purple_300),
|
||||
CHILD_CONTROLLERS("Child Controllers", R.color.orange_300),
|
||||
VIEW_PAGER("ViewPager", R.color.green_300),
|
||||
TARGET_CONTROLLER("Target Controller", R.color.pink_300),
|
||||
MULTIPLE_CHILD_ROUTERS("Multiple Child Routers", R.color.deep_orange_300),
|
||||
MASTER_DETAIL("Master Detail", R.color.grey_300),
|
||||
DRAG_DISMISS("Drag Dismiss", R.color.lime_300),
|
||||
EXTERNAL_MODULES("Bonus Modules", R.color.teal_300);
|
||||
|
||||
String title;
|
||||
@ColorRes int color;
|
||||
|
||||
DemoModel(String title, @ColorRes int color) {
|
||||
this.title = title;
|
||||
this.color = color;
|
||||
}
|
||||
}
|
||||
|
||||
private static final String KEY_FAB_VISIBILITY = "HomeController.fabVisibility";
|
||||
|
||||
@BindView(R.id.recycler_view) RecyclerView recyclerView;
|
||||
@BindView(R.id.fab) View fab;
|
||||
|
||||
public HomeController() {
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
return inflater.inflate(R.layout.controller_home, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onViewBound(@NonNull View view) {
|
||||
super.onViewBound(view);
|
||||
|
||||
recyclerView.setHasFixedSize(true);
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(view.getContext()));
|
||||
recyclerView.setAdapter(new HomeAdapter(LayoutInflater.from(view.getContext()), DemoModel.values()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveViewState(@NonNull View view, @NonNull Bundle outState) {
|
||||
super.onSaveViewState(view, outState);
|
||||
outState.putInt(KEY_FAB_VISIBILITY, fab.getVisibility());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreViewState(@NonNull View view, @NonNull Bundle savedViewState) {
|
||||
super.onRestoreViewState(view, savedViewState);
|
||||
|
||||
//noinspection WrongConstant
|
||||
fab.setVisibility(savedViewState.getInt(KEY_FAB_VISIBILITY));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
inflater.inflate(R.menu.home, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onChangeStarted(@NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
setOptionsMenuHidden(!changeType.isEnter);
|
||||
|
||||
if (changeType.isEnter) {
|
||||
setTitle();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
if (item.getItemId() == R.id.about) {
|
||||
onFabClicked(false);
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
return "Conductor Demos";
|
||||
}
|
||||
|
||||
@OnClick(R.id.fab)
|
||||
public void onFabClicked() {
|
||||
onFabClicked(true);
|
||||
}
|
||||
|
||||
private void onFabClicked(boolean fromFab) {
|
||||
SpannableString details = new SpannableString("A small, yet full-featured framework that allows building View-based Android applications");
|
||||
details.setSpan(new AbsoluteSizeSpan(16, true), 0, details.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
|
||||
final String url = "https://github.com/bluelinelabs/Conductor";
|
||||
SpannableString link = new SpannableString(url);
|
||||
link.setSpan(new URLSpan(url) {
|
||||
@Override
|
||||
public void onClick(View widget) {
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
|
||||
}
|
||||
}, 0, link.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
|
||||
SpannableStringBuilder description = new SpannableStringBuilder();
|
||||
description.append(details);
|
||||
description.append("\n\n");
|
||||
description.append(link);
|
||||
|
||||
ControllerChangeHandler pushHandler = fromFab ? new FabToDialogTransitionChangeHandler() : new FadeChangeHandler(false);
|
||||
ControllerChangeHandler popHandler = fromFab ? new FabToDialogTransitionChangeHandler() : new FadeChangeHandler();
|
||||
|
||||
getRouter()
|
||||
.pushController(RouterTransaction.with(new DialogController("Conductor", description))
|
||||
.pushChangeHandler(pushHandler)
|
||||
.popChangeHandler(popHandler));
|
||||
|
||||
}
|
||||
|
||||
void onModelRowClick(DemoModel model, int position) {
|
||||
switch (model) {
|
||||
case NAVIGATION:
|
||||
getRouter().pushController(RouterTransaction.with(new NavigationDemoController(0, DisplayUpMode.SHOW_FOR_CHILDREN_ONLY))
|
||||
.pushChangeHandler(new FadeChangeHandler())
|
||||
.popChangeHandler(new FadeChangeHandler())
|
||||
.tag(NavigationDemoController.TAG_UP_TRANSACTION)
|
||||
);
|
||||
break;
|
||||
case TRANSITIONS:
|
||||
getRouter().pushController(TransitionDemoController.getRouterTransaction(0, this));
|
||||
break;
|
||||
case TARGET_CONTROLLER:
|
||||
getRouter().pushController(
|
||||
RouterTransaction.with(new TargetDisplayController())
|
||||
.pushChangeHandler(new FadeChangeHandler())
|
||||
.popChangeHandler(new FadeChangeHandler()));
|
||||
break;
|
||||
case VIEW_PAGER:
|
||||
getRouter().pushController(RouterTransaction.with(new PagerController())
|
||||
.pushChangeHandler(new FadeChangeHandler())
|
||||
.popChangeHandler(new FadeChangeHandler()));
|
||||
break;
|
||||
case CHILD_CONTROLLERS:
|
||||
getRouter().pushController(RouterTransaction.with(new ParentController())
|
||||
.pushChangeHandler(new FadeChangeHandler())
|
||||
.popChangeHandler(new FadeChangeHandler()));
|
||||
break;
|
||||
case SHARED_ELEMENT_TRANSITIONS:
|
||||
String titleSharedElementName = getResources().getString(R.string.transition_tag_title_indexed, position);
|
||||
String dotSharedElementName = getResources().getString(R.string.transition_tag_dot_indexed, position);
|
||||
|
||||
getRouter().pushController(RouterTransaction.with(new CityGridController(model.title, model.color, position))
|
||||
.pushChangeHandler(new ArcFadeMoveChangeHandler(titleSharedElementName, dotSharedElementName))
|
||||
.popChangeHandler(new ArcFadeMoveChangeHandler(titleSharedElementName, dotSharedElementName)));
|
||||
break;
|
||||
case DRAG_DISMISS:
|
||||
getRouter().pushController(RouterTransaction.with(new DragDismissController())
|
||||
.pushChangeHandler(new FadeChangeHandler(false))
|
||||
.popChangeHandler(new FadeChangeHandler()));
|
||||
break;
|
||||
case EXTERNAL_MODULES:
|
||||
getRouter().pushController(RouterTransaction.with(new ExternalModulesController())
|
||||
.pushChangeHandler(new HorizontalChangeHandler())
|
||||
.popChangeHandler(new HorizontalChangeHandler()));
|
||||
break;
|
||||
case MULTIPLE_CHILD_ROUTERS:
|
||||
getRouter().pushController(RouterTransaction.with(new MultipleChildRouterController())
|
||||
.pushChangeHandler(new FadeChangeHandler())
|
||||
.popChangeHandler(new FadeChangeHandler()));
|
||||
break;
|
||||
case MASTER_DETAIL:
|
||||
getRouter().pushController(RouterTransaction.with(new MasterDetailListController())
|
||||
.pushChangeHandler(new FadeChangeHandler())
|
||||
.popChangeHandler(new FadeChangeHandler()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.ViewHolder> {
|
||||
|
||||
private final LayoutInflater inflater;
|
||||
private final DemoModel[] items;
|
||||
|
||||
public HomeAdapter(LayoutInflater inflater, DemoModel[] items) {
|
||||
this.inflater = inflater;
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
return new ViewHolder(inflater.inflate(R.layout.row_home, parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||
holder.bind(position, items[position]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return items.length;
|
||||
}
|
||||
|
||||
class ViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
@BindView(R.id.tv_title) TextView tvTitle;
|
||||
@BindView(R.id.img_dot) ImageView imgDot;
|
||||
private DemoModel model;
|
||||
private int position;
|
||||
|
||||
public ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
ButterKnife.bind(this, itemView);
|
||||
}
|
||||
|
||||
void bind(int position, DemoModel item) {
|
||||
model = item;
|
||||
tvTitle.setText(item.title);
|
||||
imgDot.getDrawable().setColorFilter(ContextCompat.getColor(getActivity(), item.color), Mode.SRC_ATOP);
|
||||
this.position = position;
|
||||
|
||||
ViewCompat.setTransitionName(tvTitle, getResources().getString(R.string.transition_tag_title_indexed, position));
|
||||
ViewCompat.setTransitionName(imgDot, getResources().getString(R.string.transition_tag_dot_indexed, position));
|
||||
}
|
||||
|
||||
@OnClick(R.id.row_root)
|
||||
void onRowClick() {
|
||||
onModelRowClick(model, position);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers
|
||||
|
||||
import android.content.Intent
|
||||
import android.graphics.PorterDuff
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.SpannableString
|
||||
import android.text.Spanned
|
||||
import android.text.style.AbsoluteSizeSpan
|
||||
import android.text.style.URLSpan
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.text.buildSpannedString
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bluelinelabs.conductor.RouterTransaction
|
||||
import com.bluelinelabs.conductor.changehandler.FadeChangeHandler
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
|
||||
import com.bluelinelabs.conductor.demo.R
|
||||
import com.bluelinelabs.conductor.demo.changehandler.ArcFadeMoveChangeHandler
|
||||
import com.bluelinelabs.conductor.demo.changehandler.FabToDialogTransitionChangeHandler
|
||||
import com.bluelinelabs.conductor.demo.controllers.NavigationDemoController.DisplayUpMode
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController
|
||||
import com.bluelinelabs.conductor.demo.databinding.ControllerHomeBinding
|
||||
import com.bluelinelabs.conductor.demo.databinding.RowHomeBinding
|
||||
import com.bluelinelabs.conductor.demo.util.viewBinding
|
||||
|
||||
class HomeController : BaseController(R.layout.controller_home) {
|
||||
private val binding: ControllerHomeBinding by viewBinding(ControllerHomeBinding::bind)
|
||||
|
||||
override val title = "Conductor Demos"
|
||||
|
||||
override fun onViewCreated(view: View) {
|
||||
binding.recyclerView.setHasFixedSize(true)
|
||||
binding.recyclerView.layoutManager = LinearLayoutManager(view.context)
|
||||
binding.recyclerView.adapter = HomeAdapter(
|
||||
LayoutInflater.from(view.context),
|
||||
DemoModel.values(),
|
||||
::onModelRowClick
|
||||
)
|
||||
|
||||
binding.fab.setOnClickListener { showAboutDialog(fromFab = true) }
|
||||
}
|
||||
|
||||
override fun configureToolbar(toolbar: Toolbar) {
|
||||
super.configureToolbar(toolbar)
|
||||
|
||||
toolbar.setOnMenuItemClickListener {
|
||||
if (it.itemId == R.id.about) {
|
||||
showAboutDialog(fromFab = false)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun configureMenu(toolbar: Toolbar) {
|
||||
super.configureMenu(toolbar)
|
||||
|
||||
toolbar.inflateMenu(R.menu.home)
|
||||
}
|
||||
|
||||
override fun onSaveViewState(view: View, outState: Bundle) {
|
||||
super.onSaveViewState(view, outState)
|
||||
outState.putInt(KEY_FAB_VISIBILITY, binding.fab.visibility)
|
||||
}
|
||||
|
||||
override fun onRestoreViewState(view: View, savedViewState: Bundle) {
|
||||
super.onRestoreViewState(view, savedViewState)
|
||||
binding.fab.visibility = savedViewState.getInt(KEY_FAB_VISIBILITY)
|
||||
}
|
||||
|
||||
private fun onModelRowClick(model: DemoModel, position: Int) {
|
||||
when (model) {
|
||||
DemoModel.NAVIGATION -> {
|
||||
router.pushController(
|
||||
RouterTransaction.with(NavigationDemoController(0, DisplayUpMode.SHOW_FOR_CHILDREN_ONLY))
|
||||
.pushChangeHandler(FadeChangeHandler())
|
||||
.popChangeHandler(FadeChangeHandler())
|
||||
.tag(NavigationDemoController.TAG_UP_TRANSACTION)
|
||||
)
|
||||
}
|
||||
DemoModel.TRANSITIONS -> {
|
||||
router.pushController(TransitionDemoController.getRouterTransaction(0, this))
|
||||
}
|
||||
DemoModel.TARGET_CONTROLLER -> {
|
||||
router.pushController(
|
||||
RouterTransaction.with(TargetDisplayController())
|
||||
.pushChangeHandler(FadeChangeHandler())
|
||||
.popChangeHandler(FadeChangeHandler())
|
||||
)
|
||||
}
|
||||
DemoModel.VIEW_PAGER -> {
|
||||
router.pushController(
|
||||
RouterTransaction.with(ViewPagerController())
|
||||
.pushChangeHandler(FadeChangeHandler())
|
||||
.popChangeHandler(FadeChangeHandler())
|
||||
)
|
||||
}
|
||||
DemoModel.VIEW_PAGER_2 -> {
|
||||
router.pushController(
|
||||
RouterTransaction.with(ViewPager2Controller())
|
||||
.pushChangeHandler(FadeChangeHandler())
|
||||
.popChangeHandler(FadeChangeHandler())
|
||||
)
|
||||
}
|
||||
DemoModel.CHILD_CONTROLLERS -> {
|
||||
router.pushController(
|
||||
RouterTransaction.with(ParentController())
|
||||
.pushChangeHandler(FadeChangeHandler())
|
||||
.popChangeHandler(FadeChangeHandler())
|
||||
)
|
||||
}
|
||||
DemoModel.SHARED_ELEMENT_TRANSITIONS -> {
|
||||
val titleSharedElementName = resources!!.getString(R.string.transition_tag_title_indexed, position)
|
||||
val dotSharedElementName = resources!!.getString(R.string.transition_tag_dot_indexed, position)
|
||||
router.pushController(
|
||||
RouterTransaction.with(CityGridController(model.title, model.color, position))
|
||||
.pushChangeHandler(ArcFadeMoveChangeHandler(titleSharedElementName, dotSharedElementName))
|
||||
.popChangeHandler(ArcFadeMoveChangeHandler(titleSharedElementName, dotSharedElementName))
|
||||
)
|
||||
}
|
||||
DemoModel.DRAG_DISMISS -> {
|
||||
router.pushController(
|
||||
RouterTransaction.with(DragDismissController())
|
||||
.pushChangeHandler(FadeChangeHandler(false))
|
||||
.popChangeHandler(FadeChangeHandler())
|
||||
)
|
||||
}
|
||||
DemoModel.EXTERNAL_MODULES -> {
|
||||
router.pushController(
|
||||
RouterTransaction.with(ExternalModulesController())
|
||||
.pushChangeHandler(HorizontalChangeHandler())
|
||||
.popChangeHandler(HorizontalChangeHandler())
|
||||
)
|
||||
}
|
||||
DemoModel.MULTIPLE_CHILD_ROUTERS -> {
|
||||
router.pushController(
|
||||
RouterTransaction.with(MultipleChildRouterController())
|
||||
.pushChangeHandler(FadeChangeHandler())
|
||||
.popChangeHandler(FadeChangeHandler())
|
||||
)
|
||||
}
|
||||
DemoModel.MASTER_DETAIL -> {
|
||||
router.pushController(
|
||||
RouterTransaction.with(MasterDetailListController())
|
||||
.pushChangeHandler(FadeChangeHandler())
|
||||
.popChangeHandler(FadeChangeHandler())
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showAboutDialog(fromFab: Boolean) {
|
||||
val details = SpannableString("A small, yet full-featured framework that allows building View-based Android applications").apply {
|
||||
setSpan(AbsoluteSizeSpan(16, true), 0, length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
|
||||
}
|
||||
|
||||
val link = SpannableString(CONDUCTOR_URL).apply {
|
||||
setSpan(object : URLSpan(CONDUCTOR_URL) {
|
||||
override fun onClick(widget: View) {
|
||||
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
|
||||
}
|
||||
}, 0, length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
|
||||
}
|
||||
|
||||
val description = buildSpannedString {
|
||||
append(details)
|
||||
append("\n\n")
|
||||
append(link)
|
||||
}
|
||||
|
||||
val pushHandler = if (fromFab) FabToDialogTransitionChangeHandler() else FadeChangeHandler(false)
|
||||
val popHandler = if (fromFab) FabToDialogTransitionChangeHandler() else FadeChangeHandler()
|
||||
router.pushController(
|
||||
RouterTransaction.with(DialogController("Conductor", description))
|
||||
.pushChangeHandler(pushHandler)
|
||||
.popChangeHandler(popHandler)
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val KEY_FAB_VISIBILITY = "HomeController.fabVisibility"
|
||||
private const val CONDUCTOR_URL = "https://github.com/bluelinelabs/Conductor"
|
||||
}
|
||||
}
|
||||
|
||||
private enum class DemoModel(val title: String, @ColorRes val color: Int) {
|
||||
NAVIGATION("Navigation Demos", R.color.red_300),
|
||||
TRANSITIONS("Transition Demos", R.color.blue_grey_300),
|
||||
SHARED_ELEMENT_TRANSITIONS("Shared Element Demos", R.color.purple_300),
|
||||
CHILD_CONTROLLERS("Child Controllers", R.color.orange_300),
|
||||
VIEW_PAGER("ViewPager", R.color.green_300),
|
||||
VIEW_PAGER_2("ViewPager2", R.color.pink_300),
|
||||
TARGET_CONTROLLER("Target Controller", R.color.deep_orange_300),
|
||||
MULTIPLE_CHILD_ROUTERS("Multiple Child Routers", R.color.grey_300),
|
||||
MASTER_DETAIL("Master Detail", R.color.lime_300),
|
||||
DRAG_DISMISS("Drag Dismiss", R.color.teal_300),
|
||||
EXTERNAL_MODULES("Bonus Modules", R.color.deep_purple_300)
|
||||
}
|
||||
|
||||
private class HomeAdapter(
|
||||
private val inflater: LayoutInflater,
|
||||
private val items: Array<DemoModel>,
|
||||
private val modelClickListener: (DemoModel, Int) -> Unit
|
||||
) : RecyclerView.Adapter<HomeAdapter.ViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return ViewHolder(
|
||||
RowHomeBinding.inflate(inflater, parent, false),
|
||||
modelClickListener
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
holder.bind(position, items[position])
|
||||
}
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
|
||||
class ViewHolder(
|
||||
private val binding: RowHomeBinding,
|
||||
private val modelClickListener: (DemoModel, Int) -> Unit
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(position: Int, item: DemoModel) {
|
||||
binding.title.text = item.title
|
||||
binding.dot.drawable.setColorFilter(
|
||||
ContextCompat.getColor(binding.root.context, item.color),
|
||||
PorterDuff.Mode.SRC_ATOP
|
||||
)
|
||||
binding.root.setOnClickListener { modelClickListener(item, position) }
|
||||
|
||||
binding.title.transitionName =
|
||||
binding.root.resources.getString(R.string.transition_tag_title_indexed, position)
|
||||
binding.dot.transitionName =
|
||||
binding.root.resources.getString(R.string.transition_tag_dot_indexed, position)
|
||||
}
|
||||
}
|
||||
}
|
||||
-158
@@ -1,158 +0,0 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bluelinelabs.conductor.RouterTransaction;
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
|
||||
public class MasterDetailListController extends BaseController {
|
||||
|
||||
private static final String KEY_SELECTED_INDEX = "MasterDetailListController.selectedIndex";
|
||||
|
||||
public enum DetailItemModel {
|
||||
ONE("Item 1", "This is a quick demo of master/detail flow using Conductor. In portrait mode you'll see a standard list. In landscape, you'll see a two-pane layout.", R.color.green_300),
|
||||
TWO("Item 2", "This is another item.", R.color.cyan_300),
|
||||
THREE("Item 3", "Wow, a 3rd item!", R.color.deep_purple_300);
|
||||
|
||||
String title;
|
||||
String detail;
|
||||
int backgroundColor;
|
||||
|
||||
DetailItemModel(String title, String detail, int backgroundColor) {
|
||||
this.title = title;
|
||||
this.detail = detail;
|
||||
this.backgroundColor = backgroundColor;
|
||||
}
|
||||
}
|
||||
|
||||
@BindView(R.id.recycler_view) RecyclerView recyclerView;
|
||||
@Nullable @BindView(R.id.detail_container) ViewGroup detailContainer;
|
||||
|
||||
private int selectedIndex;
|
||||
private boolean twoPaneView;
|
||||
|
||||
@Override
|
||||
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
return inflater.inflate(R.layout.controller_master_detail_list, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onViewBound(@NonNull View view) {
|
||||
super.onViewBound(view);
|
||||
|
||||
recyclerView.setHasFixedSize(true);
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(view.getContext()));
|
||||
recyclerView.setAdapter(new DetailItemAdapter(LayoutInflater.from(view.getContext()), DetailItemModel.values()));
|
||||
|
||||
twoPaneView = (detailContainer != null);
|
||||
if (twoPaneView) {
|
||||
onRowSelected(selectedIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
|
||||
outState.putInt(KEY_SELECTED_INDEX, selectedIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
|
||||
selectedIndex = savedInstanceState.getInt(KEY_SELECTED_INDEX);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
return "Master/Detail Flow";
|
||||
}
|
||||
|
||||
void onRowSelected(int index) {
|
||||
selectedIndex = index;
|
||||
|
||||
DetailItemModel model = DetailItemModel.values()[index];
|
||||
ChildController controller = new ChildController(model.detail, model.backgroundColor, true);
|
||||
|
||||
if (twoPaneView) {
|
||||
getChildRouter(detailContainer).setRoot(RouterTransaction.with(controller));
|
||||
} else {
|
||||
getRouter().pushController(RouterTransaction.with(controller)
|
||||
.pushChangeHandler(new HorizontalChangeHandler())
|
||||
.popChangeHandler(new HorizontalChangeHandler()));
|
||||
}
|
||||
}
|
||||
|
||||
class DetailItemAdapter extends RecyclerView.Adapter<DetailItemAdapter.ViewHolder> {
|
||||
|
||||
private final LayoutInflater inflater;
|
||||
private final DetailItemModel[] items;
|
||||
|
||||
public DetailItemAdapter(LayoutInflater inflater, DetailItemModel[] items) {
|
||||
this.inflater = inflater;
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
return new ViewHolder(inflater.inflate(R.layout.row_detail_item, parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||
holder.bind(items[position], position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return items.length;
|
||||
}
|
||||
|
||||
class ViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
@BindView(R.id.row_root) View root;
|
||||
@BindView(R.id.tv_title) TextView tvTitle;
|
||||
private int position;
|
||||
|
||||
public ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
ButterKnife.bind(this, itemView);
|
||||
}
|
||||
|
||||
void bind(DetailItemModel item, int position) {
|
||||
tvTitle.setText(item.title);
|
||||
this.position = position;
|
||||
|
||||
if (twoPaneView && position == selectedIndex) {
|
||||
root.setBackgroundColor(ContextCompat.getColor(root.getContext(), R.color.grey_400));
|
||||
} else {
|
||||
root.setBackgroundColor(ContextCompat.getColor(root.getContext(), android.R.color.transparent));
|
||||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.row_root)
|
||||
void onRowClick() {
|
||||
onRowSelected(position);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bluelinelabs.conductor.RouterTransaction.Companion.with
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
|
||||
import com.bluelinelabs.conductor.demo.R
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController
|
||||
import com.bluelinelabs.conductor.demo.databinding.ControllerMasterDetailListBinding
|
||||
import com.bluelinelabs.conductor.demo.databinding.RowDetailItemBinding
|
||||
import com.bluelinelabs.conductor.demo.util.viewBinding
|
||||
|
||||
class MasterDetailListController : BaseController(R.layout.controller_master_detail_list) {
|
||||
private val binding: ControllerMasterDetailListBinding by viewBinding(ControllerMasterDetailListBinding::bind)
|
||||
|
||||
override val title = "Master/Detail Flow"
|
||||
|
||||
private lateinit var adapter: DetailItemAdapter
|
||||
private var selectedIndex = 0
|
||||
private var twoPaneView = false
|
||||
|
||||
private val dataModels = mutableListOf(
|
||||
DetailItemModel(
|
||||
"Item 1",
|
||||
"This is a quick demo of master/detail flow using Conductor. In portrait mode you'll see a standard list. In landscape, you'll see a two-pane layout.",
|
||||
R.color.green_300
|
||||
),
|
||||
DetailItemModel(
|
||||
"Item 2",
|
||||
"This is another item.",
|
||||
R.color.cyan_300
|
||||
),
|
||||
DetailItemModel(
|
||||
"Item 3",
|
||||
"Wow, a 3rd item!",
|
||||
R.color.deep_purple_300
|
||||
)
|
||||
)
|
||||
|
||||
override fun onViewCreated(view: View) {
|
||||
adapter = DetailItemAdapter(LayoutInflater.from(view.context), dataModels, ::onRowSelected)
|
||||
|
||||
binding.recyclerView.setHasFixedSize(true)
|
||||
binding.recyclerView.layoutManager = LinearLayoutManager(view.context)
|
||||
binding.recyclerView.adapter = adapter
|
||||
twoPaneView = binding.detailContainer != null
|
||||
if (twoPaneView) {
|
||||
onRowSelected(selectedIndex)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putInt(KEY_SELECTED_INDEX, selectedIndex)
|
||||
}
|
||||
|
||||
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||
super.onRestoreInstanceState(savedInstanceState)
|
||||
selectedIndex = savedInstanceState.getInt(KEY_SELECTED_INDEX)
|
||||
}
|
||||
|
||||
private fun onRowSelected(index: Int) {
|
||||
dataModels[selectedIndex] = dataModels[selectedIndex].copy(selected = false)
|
||||
selectedIndex = index
|
||||
dataModels[selectedIndex] = dataModels[selectedIndex].copy(selected = true)
|
||||
|
||||
val model = dataModels[selectedIndex]
|
||||
val controller = ChildController(model.detail, model.backgroundColor, true)
|
||||
if (twoPaneView) {
|
||||
getChildRouter(binding.detailContainer!!).setRoot(with(controller))
|
||||
} else {
|
||||
router.pushController(
|
||||
with(controller)
|
||||
.pushChangeHandler(HorizontalChangeHandler())
|
||||
.popChangeHandler(HorizontalChangeHandler())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val KEY_SELECTED_INDEX = "MasterDetailListController.selectedIndex"
|
||||
}
|
||||
}
|
||||
|
||||
data class DetailItemModel(val title: String, val detail: String, val backgroundColor: Int, val selected: Boolean = false)
|
||||
|
||||
class DetailItemAdapter(
|
||||
private val inflater: LayoutInflater,
|
||||
private val items: List<DetailItemModel>,
|
||||
private val rowClickListener: (Int) -> Unit
|
||||
) : RecyclerView.Adapter<DetailItemAdapter.ViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return ViewHolder(RowDetailItemBinding.inflate(inflater, parent, false)) { position ->
|
||||
rowClickListener(position)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
holder.bind(items[position], position)
|
||||
}
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
|
||||
class ViewHolder(
|
||||
private val binding: RowDetailItemBinding,
|
||||
private val rowClickListener: (Int) -> Unit
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(item: DetailItemModel, position: Int) {
|
||||
binding.title.text = item.title
|
||||
|
||||
if (item.selected) {
|
||||
itemView.setBackgroundColor(ContextCompat.getColor(itemView.context, R.color.grey_400))
|
||||
} else {
|
||||
itemView.setBackgroundColor(ContextCompat.getColor(itemView.context, android.R.color.transparent))
|
||||
}
|
||||
|
||||
itemView.setOnClickListener { rowClickListener(position) }
|
||||
}
|
||||
}
|
||||
}
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.Router;
|
||||
import com.bluelinelabs.conductor.RouterTransaction;
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.controllers.NavigationDemoController.DisplayUpMode;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
|
||||
import butterknife.BindViews;
|
||||
|
||||
public class MultipleChildRouterController extends BaseController {
|
||||
|
||||
@BindViews({R.id.container_0, R.id.container_1, R.id.container_2}) ViewGroup[] childContainers;
|
||||
|
||||
@Override
|
||||
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
return inflater.inflate(R.layout.controller_multiple_child_routers, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onViewBound(@NonNull View view) {
|
||||
super.onViewBound(view);
|
||||
|
||||
for (ViewGroup childContainer : childContainers) {
|
||||
Router childRouter = getChildRouter(childContainer).setPopsLastView(false);
|
||||
if (!childRouter.hasRootController()) {
|
||||
childRouter.setRoot(RouterTransaction.with(new NavigationDemoController(0, DisplayUpMode.HIDE)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
return "Child Router Demo";
|
||||
}
|
||||
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers
|
||||
|
||||
import android.view.View
|
||||
import com.bluelinelabs.conductor.RouterTransaction
|
||||
import com.bluelinelabs.conductor.demo.R
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController
|
||||
import com.bluelinelabs.conductor.demo.databinding.ControllerMultipleChildRoutersBinding
|
||||
import com.bluelinelabs.conductor.demo.util.viewBinding
|
||||
|
||||
class MultipleChildRouterController : BaseController(R.layout.controller_multiple_child_routers) {
|
||||
private val binding: ControllerMultipleChildRoutersBinding by viewBinding(ControllerMultipleChildRoutersBinding::bind)
|
||||
|
||||
override val title = "Child Router Demo"
|
||||
|
||||
override fun onViewCreated(view: View) {
|
||||
super.onViewCreated(view)
|
||||
|
||||
val childContainers = listOf(binding.container0, binding.container1, binding.container2)
|
||||
|
||||
childContainers.forEach { container ->
|
||||
val childRouter = getChildRouter(container).setPopsLastView(false)
|
||||
if (!childRouter.hasRootController()) {
|
||||
childRouter.setRoot(RouterTransaction.with(NavigationDemoController(0, NavigationDemoController.DisplayUpMode.HIDE)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-121
@@ -1,121 +0,0 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
import com.bluelinelabs.conductor.ControllerChangeType;
|
||||
import com.bluelinelabs.conductor.RouterTransaction;
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
import com.bluelinelabs.conductor.demo.util.BundleBuilder;
|
||||
import com.bluelinelabs.conductor.demo.util.ColorUtil;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.OnClick;
|
||||
|
||||
public class NavigationDemoController extends BaseController {
|
||||
|
||||
public enum DisplayUpMode {
|
||||
SHOW,
|
||||
SHOW_FOR_CHILDREN_ONLY,
|
||||
HIDE;
|
||||
|
||||
private DisplayUpMode getDisplayUpModeForChild() {
|
||||
switch (this) {
|
||||
case HIDE:
|
||||
return HIDE;
|
||||
default:
|
||||
return SHOW;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static final String TAG_UP_TRANSACTION = "NavigationDemoController.up";
|
||||
|
||||
private static final String KEY_INDEX = "NavigationDemoController.index";
|
||||
private static final String KEY_DISPLAY_UP_MODE = "NavigationDemoController.displayUpMode";
|
||||
|
||||
@BindView(R.id.tv_title) TextView tvTitle;
|
||||
|
||||
private int index;
|
||||
private DisplayUpMode displayUpMode;
|
||||
|
||||
public NavigationDemoController(int index, DisplayUpMode displayUpMode) {
|
||||
this(new BundleBuilder(new Bundle())
|
||||
.putInt(KEY_INDEX, index)
|
||||
.putInt(KEY_DISPLAY_UP_MODE, displayUpMode.ordinal())
|
||||
.build());
|
||||
}
|
||||
|
||||
public NavigationDemoController(Bundle args) {
|
||||
super(args);
|
||||
index = args.getInt(KEY_INDEX);
|
||||
displayUpMode = DisplayUpMode.values()[args.getInt(KEY_DISPLAY_UP_MODE)];
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
return inflater.inflate(R.layout.controller_navigation_demo, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onViewBound(@NonNull View view) {
|
||||
super.onViewBound(view);
|
||||
|
||||
if (displayUpMode != DisplayUpMode.SHOW) {
|
||||
view.findViewById(R.id.btn_up).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
view.setBackgroundColor(ColorUtil.getMaterialColor(getResources(), index));
|
||||
tvTitle.setText(getResources().getString(R.string.navigation_title, index));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onChangeEnded(@NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
super.onChangeEnded(changeHandler, changeType);
|
||||
|
||||
setButtonsEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onChangeStarted(@NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
super.onChangeStarted(changeHandler, changeType);
|
||||
|
||||
setButtonsEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
return "Navigation Demos";
|
||||
}
|
||||
|
||||
private void setButtonsEnabled(boolean enabled) {
|
||||
final View view = getView();
|
||||
if (view != null) {
|
||||
view.findViewById(R.id.btn_next).setEnabled(enabled);
|
||||
view.findViewById(R.id.btn_up).setEnabled(enabled);
|
||||
view.findViewById(R.id.btn_pop_to_root).setEnabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.btn_next) void onNextClicked() {
|
||||
getRouter().pushController(RouterTransaction.with(new NavigationDemoController(index + 1, displayUpMode.getDisplayUpModeForChild()))
|
||||
.pushChangeHandler(new HorizontalChangeHandler())
|
||||
.popChangeHandler(new HorizontalChangeHandler()));
|
||||
}
|
||||
|
||||
@OnClick(R.id.btn_up) void onUpClicked() {
|
||||
getRouter().popToTag(TAG_UP_TRANSACTION);
|
||||
}
|
||||
|
||||
@OnClick(R.id.btn_pop_to_root) void onPopToRootClicked() {
|
||||
getRouter().popToRoot();
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.isVisible
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||
import com.bluelinelabs.conductor.ControllerChangeType
|
||||
import com.bluelinelabs.conductor.RouterTransaction
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
|
||||
import com.bluelinelabs.conductor.demo.R
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController
|
||||
import com.bluelinelabs.conductor.demo.databinding.ControllerNavigationDemoBinding
|
||||
import com.bluelinelabs.conductor.demo.util.getMaterialColor
|
||||
import com.bluelinelabs.conductor.demo.util.viewBinding
|
||||
|
||||
class NavigationDemoController(args: Bundle) : BaseController(R.layout.controller_navigation_demo, args) {
|
||||
private val binding: ControllerNavigationDemoBinding by viewBinding(ControllerNavigationDemoBinding::bind)
|
||||
|
||||
private val index = args.getInt(KEY_INDEX)
|
||||
private val displayUpMode = DisplayUpMode.values()[args.getInt(KEY_DISPLAY_UP_MODE)]
|
||||
|
||||
override val title = "Navigation Demos"
|
||||
|
||||
constructor(index: Int, displayUpMode: DisplayUpMode) : this(
|
||||
bundleOf(
|
||||
KEY_INDEX to index,
|
||||
KEY_DISPLAY_UP_MODE to displayUpMode.ordinal
|
||||
)
|
||||
)
|
||||
|
||||
override fun onViewCreated(view: View) {
|
||||
binding.root.setBackgroundColor(view.resources.getMaterialColor(index))
|
||||
binding.goUp.isVisible = displayUpMode == DisplayUpMode.SHOW
|
||||
binding.title.text = view.resources.getString(R.string.navigation_title, index)
|
||||
|
||||
binding.popToRoot.setOnClickListener { router.popToRoot() }
|
||||
binding.goUp.setOnClickListener { router.popToTag(TAG_UP_TRANSACTION) }
|
||||
binding.goToNext.setOnClickListener {
|
||||
router.pushController(
|
||||
RouterTransaction.with(NavigationDemoController(index + 1, displayUpMode.displayUpModeForChild))
|
||||
.pushChangeHandler(HorizontalChangeHandler())
|
||||
.popChangeHandler(HorizontalChangeHandler())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onChangeEnded(
|
||||
changeHandler: ControllerChangeHandler,
|
||||
changeType: ControllerChangeType
|
||||
) {
|
||||
super.onChangeEnded(changeHandler, changeType)
|
||||
if (changeType.isEnter) {
|
||||
setButtonsEnabled(true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onChangeStarted(
|
||||
changeHandler: ControllerChangeHandler,
|
||||
changeType: ControllerChangeType
|
||||
) {
|
||||
super.onChangeStarted(changeHandler, changeType)
|
||||
setButtonsEnabled(false)
|
||||
}
|
||||
|
||||
private fun setButtonsEnabled(enabled: Boolean) {
|
||||
binding.goToNext.isEnabled = enabled
|
||||
binding.goUp.isEnabled = enabled
|
||||
binding.popToRoot.isEnabled = enabled
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG_UP_TRANSACTION = "NavigationDemoController.up"
|
||||
|
||||
private const val KEY_INDEX = "NavigationDemoController.index"
|
||||
private const val KEY_DISPLAY_UP_MODE = "NavigationDemoController.displayUpMode"
|
||||
}
|
||||
|
||||
enum class DisplayUpMode {
|
||||
SHOW, SHOW_FOR_CHILDREN_ONLY, HIDE;
|
||||
|
||||
val displayUpModeForChild: DisplayUpMode
|
||||
get() = when (this) {
|
||||
HIDE -> HIDE
|
||||
else -> SHOW
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
import com.bluelinelabs.conductor.Router;
|
||||
import com.bluelinelabs.conductor.RouterTransaction;
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
import com.bluelinelabs.conductor.viewpager.RouterPagerAdapter;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import butterknife.BindView;
|
||||
|
||||
public class PagerController extends BaseController {
|
||||
|
||||
private int[] PAGE_COLORS = new int[]{R.color.green_300, R.color.cyan_300, R.color.deep_purple_300, R.color.lime_300, R.color.red_300};
|
||||
|
||||
@BindView(R.id.tab_layout) TabLayout tabLayout;
|
||||
@BindView(R.id.view_pager) ViewPager viewPager;
|
||||
|
||||
private final RouterPagerAdapter pagerAdapter;
|
||||
|
||||
public PagerController() {
|
||||
pagerAdapter = new RouterPagerAdapter(this) {
|
||||
@Override
|
||||
public void configureRouter(@NonNull Router router, int position) {
|
||||
if (!router.hasRootController()) {
|
||||
Controller page = new ChildController(String.format(Locale.getDefault(), "Child #%d (Swipe to see more)", position), PAGE_COLORS[position], true);
|
||||
router.setRoot(RouterTransaction.with(page));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return PAGE_COLORS.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
return "Page " + position;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
return inflater.inflate(R.layout.controller_pager, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onViewBound(@NonNull View view) {
|
||||
super.onViewBound(view);
|
||||
viewPager.setAdapter(pagerAdapter);
|
||||
tabLayout.setupWithViewPager(viewPager);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroyView(@NonNull View view) {
|
||||
if (!getActivity().isChangingConfigurations()) {
|
||||
viewPager.setAdapter(null);
|
||||
}
|
||||
tabLayout.setupWithViewPager(null);
|
||||
super.onDestroyView(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
return "ViewPager Demo";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers;
|
||||
|
||||
import androidx.annotation.IdRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
import com.bluelinelabs.conductor.ControllerChangeType;
|
||||
import com.bluelinelabs.conductor.Router;
|
||||
import com.bluelinelabs.conductor.RouterTransaction;
|
||||
import com.bluelinelabs.conductor.changehandler.FadeChangeHandler;
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
import com.bluelinelabs.conductor.demo.util.ColorUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ParentController extends BaseController {
|
||||
|
||||
private static final int NUMBER_OF_CHILDREN = 5;
|
||||
private boolean finishing;
|
||||
private boolean hasShownAll;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
return inflater.inflate(R.layout.controller_parent, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onChangeEnded(@NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
super.onChangeEnded(changeHandler, changeType);
|
||||
|
||||
if (changeType == ControllerChangeType.PUSH_ENTER) {
|
||||
addChild(0);
|
||||
}
|
||||
}
|
||||
|
||||
private void addChild(final int index) {
|
||||
@IdRes final int frameId = getResources().getIdentifier("child_content_" + (index + 1), "id", getActivity().getPackageName());
|
||||
final ViewGroup container = (ViewGroup)getView().findViewById(frameId);
|
||||
final Router childRouter = getChildRouter(container).setPopsLastView(true);
|
||||
|
||||
if (!childRouter.hasRootController()) {
|
||||
ChildController childController = new ChildController("Child Controller #" + index, ColorUtil.getMaterialColor(getResources(), index), false);
|
||||
|
||||
childController.addLifecycleListener(new LifecycleListener() {
|
||||
@Override
|
||||
public void onChangeEnd(@NonNull Controller controller, @NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
childRouter.setRoot(RouterTransaction.with(childController)
|
||||
.pushChangeHandler(new FadeChangeHandler())
|
||||
.popChangeHandler(new FadeChangeHandler()));
|
||||
}
|
||||
}
|
||||
|
||||
private void removeChild(int index) {
|
||||
List<Router> childRouters = getChildRouters();
|
||||
if (index < childRouters.size()) {
|
||||
removeChildRouter(childRouters.get(index));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleBack() {
|
||||
int childControllers = 0;
|
||||
for (Router childRouter : getChildRouters()) {
|
||||
if (childRouter.hasRootController()) {
|
||||
childControllers++;
|
||||
}
|
||||
}
|
||||
|
||||
if (childControllers != NUMBER_OF_CHILDREN || finishing) {
|
||||
return true;
|
||||
} else {
|
||||
finishing = true;
|
||||
return super.handleBack();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
return "Parent/Child Demo";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers
|
||||
|
||||
import com.bluelinelabs.conductor.Controller
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||
import com.bluelinelabs.conductor.ControllerChangeType
|
||||
import com.bluelinelabs.conductor.RouterTransaction
|
||||
import com.bluelinelabs.conductor.changehandler.FadeChangeHandler
|
||||
import com.bluelinelabs.conductor.demo.R
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController
|
||||
import com.bluelinelabs.conductor.demo.databinding.ControllerParentBinding
|
||||
import com.bluelinelabs.conductor.demo.util.getMaterialColor
|
||||
import com.bluelinelabs.conductor.demo.util.viewBinding
|
||||
|
||||
|
||||
class ParentController : BaseController(R.layout.controller_parent) {
|
||||
private val binding: ControllerParentBinding by viewBinding(ControllerParentBinding::bind)
|
||||
|
||||
override val title = "Parent/Child Demo"
|
||||
|
||||
private var hasShownAll = false
|
||||
private var animatingOut = false
|
||||
|
||||
override fun onChangeEnded(
|
||||
changeHandler: ControllerChangeHandler,
|
||||
changeType: ControllerChangeType
|
||||
) {
|
||||
if (changeType == ControllerChangeType.PUSH_ENTER) {
|
||||
addChild(0)
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleBack(): Boolean {
|
||||
val childControllers = childRouters.filter { it.hasRootController() }.size
|
||||
|
||||
return if (childControllers != NUMBER_OF_CHILDREN || animatingOut) {
|
||||
true
|
||||
} else {
|
||||
animatingOut = true
|
||||
super.handleBack()
|
||||
}
|
||||
}
|
||||
|
||||
private fun addChild(index: Int) {
|
||||
val container = when (index) {
|
||||
0 -> binding.childContent1
|
||||
1 -> binding.childContent2
|
||||
2 -> binding.childContent3
|
||||
3 -> binding.childContent4
|
||||
4 -> binding.childContent5
|
||||
else -> throw IllegalStateException("Invalid child index $index")
|
||||
}
|
||||
|
||||
val childRouter = getChildRouter(container).setPopsLastView(true)
|
||||
if (!childRouter.hasRootController()) {
|
||||
val child = ChildController(
|
||||
title = "Child Controller #$index",
|
||||
backgroundColor = resources!!.getMaterialColor(index),
|
||||
colorIsResId = false
|
||||
)
|
||||
child.addLifecycleListener(object : LifecycleListener() {
|
||||
override fun onChangeEnd(
|
||||
controller: Controller,
|
||||
changeHandler: ControllerChangeHandler,
|
||||
changeType: ControllerChangeType
|
||||
) {
|
||||
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 {
|
||||
router.popController(this@ParentController)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
childRouter.setRoot(
|
||||
RouterTransaction.with(child)
|
||||
.pushChangeHandler(FadeChangeHandler())
|
||||
.popChangeHandler(FadeChangeHandler())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeChild(index: Int) {
|
||||
val childRouters = childRouters
|
||||
if (index < childRouters.size) {
|
||||
removeChildRouter(childRouters[index])
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val NUMBER_OF_CHILDREN = 5
|
||||
}
|
||||
}
|
||||
-138
@@ -1,138 +0,0 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
import com.bluelinelabs.conductor.ControllerChangeType;
|
||||
import com.bluelinelabs.conductor.RouterTransaction;
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
|
||||
import com.bluelinelabs.conductor.demo.ActionBarProvider;
|
||||
import com.bluelinelabs.conductor.demo.DemoApplication;
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.rxlifecycle2.ControllerEvent;
|
||||
import com.bluelinelabs.conductor.rxlifecycle2.RxController;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
import butterknife.Unbinder;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.functions.Action;
|
||||
import io.reactivex.functions.Consumer;
|
||||
|
||||
// Shamelessly borrowed from the official RxLifecycle demo by Trello and adapted for Conductor Controllers
|
||||
// instead of Activities or Fragments.
|
||||
public class RxLifecycle2Controller extends RxController {
|
||||
|
||||
private static final String TAG = "RxLifecycleController";
|
||||
|
||||
@BindView(R.id.tv_title) TextView tvTitle;
|
||||
|
||||
private Unbinder unbinder;
|
||||
private boolean hasExited;
|
||||
|
||||
public RxLifecycle2Controller() {
|
||||
Observable.interval(1, TimeUnit.SECONDS)
|
||||
.doOnDispose(() -> Log.i(TAG, "Disposing from constructor"))
|
||||
.compose(this.<Long>bindUntilEvent(ControllerEvent.DESTROY))
|
||||
.subscribe(num -> Log.i(TAG, "Started in constructor, running until onDestroy(): " + num));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container, @Nullable Bundle savedViewState) {
|
||||
Log.i(TAG, "onCreateView() called");
|
||||
|
||||
View view = inflater.inflate(R.layout.controller_lifecycle, container, false);
|
||||
view.setBackgroundColor(ContextCompat.getColor(container.getContext(), R.color.blue_grey_300));
|
||||
unbinder = ButterKnife.bind(this, view);
|
||||
|
||||
tvTitle.setText(getResources().getString(R.string.rxlifecycle_title, TAG));
|
||||
|
||||
Observable.interval(1, TimeUnit.SECONDS)
|
||||
.doOnDispose(() -> Log.i(TAG, "Disposing from onCreateView()"))
|
||||
.compose(this.<Long>bindUntilEvent(ControllerEvent.DESTROY_VIEW))
|
||||
.subscribe(num -> Log.i(TAG, "Started in onCreateView(), running until onDestroyView(): " + num));
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttach(@NonNull View view) {
|
||||
super.onAttach(view);
|
||||
|
||||
Log.i(TAG, "onAttach() called");
|
||||
|
||||
(((ActionBarProvider)getActivity()).getSupportActionBar()).setTitle("RxLifecycle2 Demo");
|
||||
|
||||
Observable.interval(1, TimeUnit.SECONDS)
|
||||
.doOnDispose(() -> Log.i(TAG, "Disposing from onAttach()"))
|
||||
.compose(this.<Long>bindUntilEvent(ControllerEvent.DETACH))
|
||||
.subscribe(num -> Log.i(TAG, "Started in onAttach(), running until onDetach(): " + num));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroyView(@NonNull View view) {
|
||||
super.onDestroyView(view);
|
||||
|
||||
Log.i(TAG, "onDestroyView() called");
|
||||
|
||||
unbinder.unbind();
|
||||
unbinder = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetach(@NonNull View view) {
|
||||
super.onDetach(view);
|
||||
|
||||
Log.i(TAG, "onDetach() called");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
Log.i(TAG, "onDestroy() called");
|
||||
|
||||
if (hasExited) {
|
||||
DemoApplication.refWatcher.watch(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onChangeEnded(@NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
super.onChangeEnded(changeHandler, changeType);
|
||||
|
||||
hasExited = !changeType.isEnter;
|
||||
if (isDestroyed()) {
|
||||
DemoApplication.refWatcher.watch(this);
|
||||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.btn_next_release_view) void onNextWithReleaseClicked() {
|
||||
setRetainViewMode(RetainViewMode.RELEASE_DETACH);
|
||||
|
||||
getRouter().pushController(RouterTransaction.with(new TextController("Logcat should now report that the observables from onAttach() and onViewBound() have been disposed of, while the constructor observable is still running."))
|
||||
.pushChangeHandler(new HorizontalChangeHandler())
|
||||
.popChangeHandler(new HorizontalChangeHandler()));
|
||||
}
|
||||
|
||||
@OnClick(R.id.btn_next_retain_view) void onNextWithRetainClicked() {
|
||||
setRetainViewMode(RetainViewMode.RETAIN_DETACH);
|
||||
|
||||
getRouter().pushController(RouterTransaction.with(new TextController("Logcat should now report that the observables from onAttach() has been disposed of, while the constructor and onViewBound() observables are still running."))
|
||||
.pushChangeHandler(new HorizontalChangeHandler())
|
||||
.popChangeHandler(new HorizontalChangeHandler()));
|
||||
}
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||
import com.bluelinelabs.conductor.ControllerChangeType
|
||||
import com.bluelinelabs.conductor.RouterTransaction.Companion.with
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
|
||||
import com.bluelinelabs.conductor.demo.DemoApplication
|
||||
import com.bluelinelabs.conductor.demo.R
|
||||
import com.bluelinelabs.conductor.demo.ToolbarProvider
|
||||
import com.bluelinelabs.conductor.demo.databinding.ControllerLifecycleBinding
|
||||
import com.bluelinelabs.conductor.rxlifecycle2.ControllerEvent
|
||||
import com.bluelinelabs.conductor.rxlifecycle2.RxController
|
||||
import io.reactivex.Observable
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
// Shamelessly borrowed from the official RxLifecycle demo by Trello and adapted for Conductor Controllers
|
||||
// instead of Activities or Fragments.
|
||||
class RxLifecycle2Controller : RxController() {
|
||||
|
||||
private var hasExited = false
|
||||
|
||||
init {
|
||||
Observable.interval(1, TimeUnit.SECONDS)
|
||||
.doOnDispose { Log.i(TAG, "Disposing from constructor") }
|
||||
.compose(bindUntilEvent(ControllerEvent.DESTROY))
|
||||
.subscribe { num: Long ->
|
||||
Log.i(TAG, "Started in constructor, running until onDestroy(): $num")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
savedViewState: Bundle?
|
||||
): View {
|
||||
Log.i(TAG, "onCreateView() called")
|
||||
val binding = ControllerLifecycleBinding.inflate(inflater, container, false)
|
||||
binding.title.text = binding.root.resources.getString(R.string.rxlifecycle_title, TAG)
|
||||
|
||||
binding.nextReleaseView.setOnClickListener {
|
||||
retainViewMode = RetainViewMode.RELEASE_DETACH
|
||||
router.pushController(
|
||||
with(TextController("Logcat should now report that the observables from onAttach() and onViewBound() have been disposed of, while the constructor observable is still running."))
|
||||
.pushChangeHandler(HorizontalChangeHandler())
|
||||
.popChangeHandler(HorizontalChangeHandler())
|
||||
)
|
||||
}
|
||||
|
||||
binding.nextRetainView.setOnClickListener {
|
||||
retainViewMode = RetainViewMode.RETAIN_DETACH
|
||||
router.pushController(
|
||||
with(TextController("Logcat should now report that the observables from onAttach() has been disposed of, while the constructor and onViewBound() observables are still running."))
|
||||
.pushChangeHandler(HorizontalChangeHandler())
|
||||
.popChangeHandler(HorizontalChangeHandler())
|
||||
)
|
||||
}
|
||||
|
||||
Observable.interval(1, TimeUnit.SECONDS)
|
||||
.doOnDispose { Log.i(TAG, "Disposing from onCreateView()") }
|
||||
.compose(bindUntilEvent(ControllerEvent.DESTROY_VIEW))
|
||||
.subscribe { num: Long ->
|
||||
Log.i(TAG, "Started in onCreateView(), running until onDestroyView(): $num")
|
||||
}
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onAttach(view: View) {
|
||||
super.onAttach(view)
|
||||
Log.i(TAG, "onAttach() called")
|
||||
|
||||
(activity as ToolbarProvider).toolbar.title = "RxLifecycle2 Demo"
|
||||
Observable.interval(1, TimeUnit.SECONDS)
|
||||
.doOnDispose { Log.i(TAG, "Disposing from onAttach()") }
|
||||
.compose(bindUntilEvent(ControllerEvent.DETACH))
|
||||
.subscribe { num: Long ->
|
||||
Log.i(TAG, "Started in onAttach(), running until onDetach(): $num")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView(view: View) {
|
||||
super.onDestroyView(view)
|
||||
Log.i(TAG, "onDestroyView() called")
|
||||
}
|
||||
|
||||
override fun onDetach(view: View) {
|
||||
super.onDetach(view)
|
||||
Log.i(TAG, "onDetach() called")
|
||||
}
|
||||
|
||||
public override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
Log.i(TAG, "onDestroy() called")
|
||||
if (hasExited) {
|
||||
DemoApplication.refWatcher.watch(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onChangeEnded(
|
||||
changeHandler: ControllerChangeHandler,
|
||||
changeType: ControllerChangeType
|
||||
) {
|
||||
super.onChangeEnded(changeHandler, changeType)
|
||||
hasExited = !changeType.isEnter
|
||||
if (isDestroyed) {
|
||||
DemoApplication.refWatcher.watch(this)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "RxLifecycleController"
|
||||
}
|
||||
|
||||
}
|
||||
-119
@@ -1,119 +0,0 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.MediaStore;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bluelinelabs.conductor.RouterTransaction;
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.controllers.TargetTitleEntryController.TargetTitleEntryControllerListener;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.OnClick;
|
||||
|
||||
public class TargetDisplayController extends BaseController implements TargetTitleEntryControllerListener {
|
||||
|
||||
private static final int REQUEST_SELECT_IMAGE = 126;
|
||||
|
||||
private static final String KEY_SELECTED_TEXT = "TargetDisplayController.selectedText";
|
||||
private static final String KEY_SELECTED_IMAGE = "TargetDisplayController.selectedImage";
|
||||
|
||||
@BindView(R.id.tv_selection) TextView tvSelection;
|
||||
@BindView(R.id.image_view) ImageView imageView;
|
||||
|
||||
private String selectedText;
|
||||
private Uri imageUri;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
return inflater.inflate(R.layout.controller_target_display, container, false);
|
||||
}
|
||||
|
||||
@OnClick(R.id.btn_pick_title) void launchTitlePicker() {
|
||||
getRouter().pushController(RouterTransaction.with(new TargetTitleEntryController(this))
|
||||
.pushChangeHandler(new HorizontalChangeHandler())
|
||||
.popChangeHandler(new HorizontalChangeHandler()));
|
||||
}
|
||||
|
||||
@OnClick(R.id.btn_pick_image) void launchImagePicker() {
|
||||
Intent intent = new Intent(Intent.ACTION_GET_CONTENT, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
|
||||
intent.setType("image/*");
|
||||
intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
|
||||
startActivityForResult(Intent.createChooser(intent, "Select Image"), REQUEST_SELECT_IMAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTitlePicked(String option) {
|
||||
selectedText = option;
|
||||
setTextView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == REQUEST_SELECT_IMAGE && resultCode == Activity.RESULT_OK) {
|
||||
imageUri = data.getData();
|
||||
setImageView();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onViewBound(@NonNull View view) {
|
||||
super.onViewBound(view);
|
||||
setTextView();
|
||||
setImageView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putString(KEY_SELECTED_TEXT, selectedText);
|
||||
outState.putString(KEY_SELECTED_IMAGE, imageUri != null ? imageUri.toString() : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
selectedText = savedInstanceState.getString(KEY_SELECTED_TEXT);
|
||||
|
||||
String uriString = savedInstanceState.getString(KEY_SELECTED_IMAGE);
|
||||
if (!TextUtils.isEmpty(uriString)) {
|
||||
imageUri = Uri.parse(uriString);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
return "Target Controller Demo";
|
||||
}
|
||||
|
||||
private void setImageView() {
|
||||
Picasso.with(getActivity())
|
||||
.load(imageUri)
|
||||
.fit()
|
||||
.centerCrop()
|
||||
.into(imageView);
|
||||
}
|
||||
|
||||
private void setTextView() {
|
||||
if (tvSelection != null) {
|
||||
if (!TextUtils.isEmpty(selectedText)) {
|
||||
tvSelection.setText(selectedText);
|
||||
} else {
|
||||
tvSelection.setText("Press pick title to set this title, or pick image to fill in the image view.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import android.view.View
|
||||
import com.bluelinelabs.conductor.RouterTransaction.Companion.with
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
|
||||
import com.bluelinelabs.conductor.demo.R
|
||||
import com.bluelinelabs.conductor.demo.controllers.TargetTitleEntryController.TargetTitleEntryControllerListener
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController
|
||||
import com.bluelinelabs.conductor.demo.databinding.ControllerTargetDisplayBinding
|
||||
import com.bluelinelabs.conductor.demo.util.viewBinding
|
||||
import com.squareup.picasso.Picasso
|
||||
|
||||
class TargetDisplayController : BaseController(R.layout.controller_target_display), TargetTitleEntryControllerListener {
|
||||
private val binding: ControllerTargetDisplayBinding by viewBinding(ControllerTargetDisplayBinding::bind)
|
||||
|
||||
override val title = "Target Controller Demo"
|
||||
|
||||
private var selectedText: String? = null
|
||||
private var imageUri: Uri? = null
|
||||
|
||||
override fun onViewCreated(view: View) {
|
||||
super.onViewCreated(view)
|
||||
|
||||
setTextView()
|
||||
setImageView()
|
||||
|
||||
binding.pickTitleButton.setOnClickListener {
|
||||
router.pushController(
|
||||
with(TargetTitleEntryController(this))
|
||||
.pushChangeHandler(HorizontalChangeHandler())
|
||||
.popChangeHandler(HorizontalChangeHandler())
|
||||
)
|
||||
}
|
||||
|
||||
binding.pickImageButton.setOnClickListener {
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
|
||||
intent.type = "image/*"
|
||||
intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true)
|
||||
startActivityForResult(Intent.createChooser(intent, "Select Image"), REQUEST_SELECT_IMAGE)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTitlePicked(option: String?) {
|
||||
selectedText = option
|
||||
setTextView()
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (requestCode == REQUEST_SELECT_IMAGE && resultCode == Activity.RESULT_OK) {
|
||||
imageUri = data?.data
|
||||
setImageView()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putString(KEY_SELECTED_TEXT, selectedText)
|
||||
outState.putString(KEY_SELECTED_IMAGE, if (imageUri != null) imageUri.toString() else null)
|
||||
}
|
||||
|
||||
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||
super.onRestoreInstanceState(savedInstanceState)
|
||||
selectedText = savedInstanceState.getString(KEY_SELECTED_TEXT)
|
||||
val uriString = savedInstanceState.getString(KEY_SELECTED_IMAGE)
|
||||
if (!uriString.isNullOrEmpty()) {
|
||||
imageUri = Uri.parse(uriString)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setImageView() {
|
||||
view ?: return
|
||||
|
||||
Picasso.with(activity)
|
||||
.load(imageUri)
|
||||
.fit()
|
||||
.centerCrop()
|
||||
.into(binding.imageView)
|
||||
}
|
||||
|
||||
private fun setTextView() {
|
||||
view ?: return
|
||||
|
||||
if (!selectedText.isNullOrEmpty()) {
|
||||
binding.selection.text = selectedText
|
||||
} else {
|
||||
binding.selection.text = "Press pick title to set this title, or pick image to fill in the image view."
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val REQUEST_SELECT_IMAGE = 126
|
||||
private const val KEY_SELECTED_TEXT = "TargetDisplayController.selectedText"
|
||||
private const val KEY_SELECTED_IMAGE = "TargetDisplayController.selectedImage"
|
||||
}
|
||||
}
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.EditText;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.OnClick;
|
||||
|
||||
public class TargetTitleEntryController extends BaseController {
|
||||
|
||||
public interface TargetTitleEntryControllerListener {
|
||||
void onTitlePicked(String option);
|
||||
}
|
||||
|
||||
@BindView(R.id.edit_text) EditText editText;
|
||||
|
||||
public <T extends Controller & TargetTitleEntryControllerListener> TargetTitleEntryController(T targetController) {
|
||||
setTargetController(targetController);
|
||||
}
|
||||
|
||||
public TargetTitleEntryController() { }
|
||||
|
||||
@Override
|
||||
protected void onDetach(@NonNull View view) {
|
||||
InputMethodManager imm = (InputMethodManager) editText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
return inflater.inflate(R.layout.controller_target_title_entry, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
return "Target Controller Demo";
|
||||
}
|
||||
|
||||
@OnClick(R.id.btn_use_title) void optionPicked() {
|
||||
Controller targetController = getTargetController();
|
||||
if (targetController != null) {
|
||||
((TargetTitleEntryControllerListener)targetController).onTitlePicked(editText.getText().toString());
|
||||
getRouter().popController(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import com.bluelinelabs.conductor.Controller
|
||||
import com.bluelinelabs.conductor.demo.R
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController
|
||||
import com.bluelinelabs.conductor.demo.databinding.ControllerTargetTitleEntryBinding
|
||||
import com.bluelinelabs.conductor.demo.util.viewBinding
|
||||
|
||||
class TargetTitleEntryController() : BaseController(R.layout.controller_target_title_entry) {
|
||||
private val binding: ControllerTargetTitleEntryBinding by viewBinding(ControllerTargetTitleEntryBinding::bind)
|
||||
|
||||
override val title = "Target Controller Demo"
|
||||
|
||||
constructor(targetController: TargetTitleEntryControllerListener) : this() {
|
||||
check(targetController is Controller)
|
||||
setTargetController(targetController)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View) {
|
||||
super.onViewCreated(view)
|
||||
|
||||
binding.useTitleButton.setOnClickListener {
|
||||
targetController?.let { listener ->
|
||||
(listener as TargetTitleEntryControllerListener).onTitlePicked(binding.editText.text.toString())
|
||||
router.popController(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetach(view: View) {
|
||||
val imm = binding.editText.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
imm.hideSoftInputFromWindow(binding.editText.windowToken, 0)
|
||||
}
|
||||
|
||||
interface TargetTitleEntryControllerListener {
|
||||
fun onTitlePicked(option: String?)
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
import com.bluelinelabs.conductor.demo.util.BundleBuilder;
|
||||
|
||||
import butterknife.BindView;
|
||||
|
||||
public class TextController extends BaseController {
|
||||
|
||||
private static final String KEY_TEXT = "TextController.text";
|
||||
|
||||
@BindView(R.id.text_view) TextView textView;
|
||||
|
||||
public TextController(String text) {
|
||||
this(new BundleBuilder(new Bundle())
|
||||
.putString(KEY_TEXT, text)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
public TextController(Bundle args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
return inflater.inflate(R.layout.controller_text, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewBound(@NonNull View view) {
|
||||
super.onViewBound(view);
|
||||
textView.setText(getArgs().getString(KEY_TEXT));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.os.bundleOf
|
||||
import com.bluelinelabs.conductor.demo.R
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController
|
||||
import com.bluelinelabs.conductor.demo.databinding.ControllerTextBinding
|
||||
import com.bluelinelabs.conductor.demo.util.viewBinding
|
||||
|
||||
class TextController(args: Bundle) : BaseController(R.layout.controller_text, args) {
|
||||
private val binding: ControllerTextBinding by viewBinding(ControllerTextBinding::bind)
|
||||
|
||||
constructor(text: String) : this(bundleOf(KEY_TEXT to text))
|
||||
|
||||
override fun onViewCreated(view: View) {
|
||||
binding.textView.text = args.getString(KEY_TEXT)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val KEY_TEXT = "TextController.text"
|
||||
}
|
||||
}
|
||||
-146
@@ -1,146 +0,0 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers;
|
||||
|
||||
import android.content.res.ColorStateList;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.ColorRes;
|
||||
import androidx.annotation.LayoutRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import butterknife.BindView;
|
||||
import butterknife.OnClick;
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
import com.bluelinelabs.conductor.RouterTransaction;
|
||||
import com.bluelinelabs.conductor.changehandler.FadeChangeHandler;
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
|
||||
import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler;
|
||||
import com.bluelinelabs.conductor.demo.R;
|
||||
import com.bluelinelabs.conductor.demo.changehandler.ArcFadeMoveChangeHandler;
|
||||
import com.bluelinelabs.conductor.demo.changehandler.CircularRevealChangeHandlerCompat;
|
||||
import com.bluelinelabs.conductor.demo.changehandler.FlipChangeHandler;
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController;
|
||||
import com.bluelinelabs.conductor.demo.util.BundleBuilder;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
|
||||
public class TransitionDemoController extends BaseController {
|
||||
|
||||
private static final String KEY_INDEX = "TransitionDemoController.index";
|
||||
|
||||
public enum TransitionDemo {
|
||||
VERTICAL("Vertical Slide Animation", R.layout.controller_transition_demo, R.color.blue_grey_300),
|
||||
CIRCULAR("Circular Reveal Animation", R.layout.controller_transition_demo, R.color.red_300),
|
||||
FADE("Fade Animation", R.layout.controller_transition_demo, R.color.blue_300),
|
||||
FLIP("Flip Animation", R.layout.controller_transition_demo, R.color.deep_orange_300),
|
||||
HORIZONTAL("Horizontal Slide Animation", R.layout.controller_transition_demo, R.color.green_300),
|
||||
ARC_FADE("Arc/Fade Shared Element Transition", R.layout.controller_transition_demo_shared, 0),
|
||||
ARC_FADE_RESET("Arc/Fade Shared Element Transition", R.layout.controller_transition_demo, R.color.pink_300);
|
||||
|
||||
String title;
|
||||
int layoutId;
|
||||
int colorId;
|
||||
|
||||
TransitionDemo(String title, @LayoutRes int layoutId, @ColorRes int colorId) {
|
||||
this.title = title;
|
||||
this.layoutId = layoutId;
|
||||
this.colorId = colorId;
|
||||
}
|
||||
|
||||
public static TransitionDemo fromIndex(int index) {
|
||||
return TransitionDemo.values()[index];
|
||||
}
|
||||
}
|
||||
|
||||
@BindView(R.id.tv_title) TextView tvTitle;
|
||||
@BindView(R.id.btn_next) FloatingActionButton btnNext;
|
||||
@BindView(R.id.transition_root) View containerView;
|
||||
|
||||
private TransitionDemo transitionDemo;
|
||||
|
||||
public TransitionDemoController(int index) {
|
||||
this(new BundleBuilder(new Bundle())
|
||||
.putInt(KEY_INDEX, index)
|
||||
.build());
|
||||
}
|
||||
|
||||
public TransitionDemoController(Bundle args) {
|
||||
super(args);
|
||||
transitionDemo = TransitionDemo.fromIndex(args.getInt(KEY_INDEX));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
return inflater.inflate(transitionDemo.layoutId, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onViewBound(@NonNull View view) {
|
||||
super.onViewBound(view);
|
||||
View bgView = view.findViewById(R.id.bg_view);
|
||||
if (transitionDemo.colorId != 0 && bgView != null) {
|
||||
bgView.setBackgroundColor(ContextCompat.getColor(getActivity(), transitionDemo.colorId));
|
||||
}
|
||||
|
||||
final int nextIndex = transitionDemo.ordinal() + 1;
|
||||
int buttonColor = 0;
|
||||
if (nextIndex < TransitionDemo.values().length) {
|
||||
buttonColor = TransitionDemo.fromIndex(nextIndex).colorId;
|
||||
}
|
||||
if (buttonColor == 0) {
|
||||
buttonColor = TransitionDemo.fromIndex(0).colorId;
|
||||
}
|
||||
|
||||
btnNext.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor(getActivity(), buttonColor)));
|
||||
tvTitle.setText(transitionDemo.title);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
return "Transition Demos";
|
||||
}
|
||||
|
||||
@OnClick(R.id.btn_next) void onNextClicked() {
|
||||
final int nextIndex = transitionDemo.ordinal() + 1;
|
||||
|
||||
if (nextIndex < TransitionDemo.values().length) {
|
||||
getRouter().pushController(getRouterTransaction(nextIndex, this));
|
||||
} else {
|
||||
getRouter().popToRoot();
|
||||
}
|
||||
}
|
||||
|
||||
public ControllerChangeHandler getChangeHandler(Controller from) {
|
||||
switch (transitionDemo) {
|
||||
case VERTICAL:
|
||||
return new VerticalChangeHandler();
|
||||
case CIRCULAR:
|
||||
TransitionDemoController demoController = (TransitionDemoController) from;
|
||||
return new CircularRevealChangeHandlerCompat(demoController.btnNext, demoController.containerView);
|
||||
case FADE:
|
||||
return new FadeChangeHandler();
|
||||
case FLIP:
|
||||
return new FlipChangeHandler();
|
||||
case ARC_FADE:
|
||||
return new ArcFadeMoveChangeHandler(from.getResources().getString(R.string.transition_tag_dot), from.getResources().getString(R.string.transition_tag_title));
|
||||
case ARC_FADE_RESET:
|
||||
return new ArcFadeMoveChangeHandler(from.getResources().getString(R.string.transition_tag_dot), from.getResources().getString(R.string.transition_tag_title));
|
||||
case HORIZONTAL:
|
||||
return new HorizontalChangeHandler();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static RouterTransaction getRouterTransaction(int index, Controller fromController) {
|
||||
TransitionDemoController toController = new TransitionDemoController(index);
|
||||
|
||||
return RouterTransaction.with(toController)
|
||||
.pushChangeHandler(toController.getChangeHandler(fromController))
|
||||
.popChangeHandler(toController.getChangeHandler(fromController));
|
||||
}
|
||||
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.annotation.DimenRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import com.bluelinelabs.conductor.Controller
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||
import com.bluelinelabs.conductor.RouterTransaction
|
||||
import com.bluelinelabs.conductor.changehandler.FadeChangeHandler
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
|
||||
import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler
|
||||
import com.bluelinelabs.conductor.demo.R
|
||||
import com.bluelinelabs.conductor.demo.changehandler.ArcFadeMoveChangeHandler
|
||||
import com.bluelinelabs.conductor.demo.changehandler.CircularRevealChangeHandlerCompat
|
||||
import com.bluelinelabs.conductor.demo.changehandler.FlipChangeHandler
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController
|
||||
import com.bluelinelabs.conductor.demo.databinding.ControllerTransitionDemoBinding
|
||||
import com.bluelinelabs.conductor.demo.util.viewBinding
|
||||
|
||||
class TransitionDemoController(args: Bundle) : BaseController(R.layout.controller_transition_demo, args) {
|
||||
private val binding: ControllerTransitionDemoBinding by viewBinding(ControllerTransitionDemoBinding::bind)
|
||||
|
||||
override val title = "Transition Demos"
|
||||
|
||||
private val demo = TransitionDemo.values()[args.getInt(KEY_INDEX)]
|
||||
|
||||
constructor(index: Int) : this(bundleOf(KEY_INDEX to index))
|
||||
|
||||
override fun onViewCreated(view: View) {
|
||||
super.onViewCreated(view)
|
||||
|
||||
if (demo.layout == TransactionDemoLayout.SHARED) {
|
||||
binding.bgView.isGone = true
|
||||
}
|
||||
|
||||
binding.title.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin = view.resources.getDimension(demo.layout.topMargin).toInt() }
|
||||
binding.next.updateLayoutParams<FrameLayout.LayoutParams> { gravity = demo.layout.fabGravity }
|
||||
|
||||
if (demo.colorId != 0) {
|
||||
binding.bgView.setBackgroundColor(ContextCompat.getColor(activity!!, demo.colorId))
|
||||
}
|
||||
|
||||
val nextIndex = demo.ordinal + 1
|
||||
val buttonColor = if (nextIndex < TransitionDemo.values().size) {
|
||||
TransitionDemo.values()[nextIndex].colorId
|
||||
} else {
|
||||
R.color.blue_grey_300
|
||||
}
|
||||
|
||||
binding.next.backgroundTintList = ContextCompat.getColorStateList(view.context, buttonColor)
|
||||
binding.title.text = demo.title
|
||||
|
||||
binding.next.setOnClickListener {
|
||||
if (nextIndex < TransitionDemo.values().size) {
|
||||
router.pushController(getRouterTransaction(nextIndex, this))
|
||||
} else {
|
||||
router.popToRoot()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getChangeHandler(from: Controller): ControllerChangeHandler {
|
||||
return when (demo) {
|
||||
TransitionDemo.VERTICAL -> VerticalChangeHandler()
|
||||
TransitionDemo.CIRCULAR -> {
|
||||
val demoController = from as TransitionDemoController
|
||||
CircularRevealChangeHandlerCompat(demoController.binding.next, demoController.binding.transitionRoot)
|
||||
}
|
||||
TransitionDemo.FADE -> FadeChangeHandler()
|
||||
TransitionDemo.FLIP -> FlipChangeHandler()
|
||||
TransitionDemo.ARC_FADE -> ArcFadeMoveChangeHandler(
|
||||
from.resources!!.getString(R.string.transition_tag_dot), from.resources!!.getString(R.string.transition_tag_title)
|
||||
)
|
||||
TransitionDemo.ARC_FADE_RESET -> ArcFadeMoveChangeHandler(
|
||||
from.resources!!.getString(R.string.transition_tag_dot), from.resources!!.getString(R.string.transition_tag_title)
|
||||
)
|
||||
TransitionDemo.HORIZONTAL -> HorizontalChangeHandler()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val KEY_INDEX = "TransitionDemoController.index"
|
||||
|
||||
fun getRouterTransaction(index: Int, fromController: Controller): RouterTransaction {
|
||||
val toController = TransitionDemoController(index)
|
||||
return RouterTransaction.with(toController)
|
||||
.pushChangeHandler(toController.getChangeHandler(fromController))
|
||||
.popChangeHandler(toController.getChangeHandler(fromController))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class TransactionDemoLayout(@DimenRes val topMargin: Int, val fabGravity: Int) {
|
||||
STANDARD(R.dimen.transition_margin_top_standard, Gravity.BOTTOM or Gravity.END),
|
||||
SHARED(R.dimen.transition_margin_top_shared, Gravity.CENTER)
|
||||
}
|
||||
|
||||
enum class TransitionDemo(val title: String, val layout: TransactionDemoLayout, @ColorRes val colorId: Int) {
|
||||
VERTICAL(
|
||||
"Vertical Slide Animation",
|
||||
TransactionDemoLayout.STANDARD,
|
||||
R.color.blue_grey_300
|
||||
),
|
||||
CIRCULAR(
|
||||
"Circular Reveal Animation",
|
||||
TransactionDemoLayout.STANDARD,
|
||||
R.color.red_300
|
||||
),
|
||||
FADE("Fade Animation",
|
||||
TransactionDemoLayout.STANDARD,
|
||||
R.color.blue_300
|
||||
),
|
||||
FLIP(
|
||||
"Flip Animation",
|
||||
TransactionDemoLayout.STANDARD,
|
||||
R.color.deep_orange_300
|
||||
),
|
||||
HORIZONTAL(
|
||||
"Horizontal Slide Animation",
|
||||
TransactionDemoLayout.STANDARD,
|
||||
R.color.green_300
|
||||
),
|
||||
ARC_FADE(
|
||||
"Arc/Fade Shared Element Transition",
|
||||
TransactionDemoLayout.SHARED,
|
||||
R.color.blue_grey_300
|
||||
),
|
||||
ARC_FADE_RESET(
|
||||
"Arc/Fade Shared Element Transition",
|
||||
TransactionDemoLayout.STANDARD,
|
||||
R.color.pink_300
|
||||
)
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers
|
||||
|
||||
import android.view.View
|
||||
import com.bluelinelabs.conductor.Controller
|
||||
import com.bluelinelabs.conductor.Router
|
||||
import com.bluelinelabs.conductor.RouterTransaction.Companion.with
|
||||
import com.bluelinelabs.conductor.demo.R
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController
|
||||
import com.bluelinelabs.conductor.demo.databinding.ControllerViewPager2Binding
|
||||
import com.bluelinelabs.conductor.demo.util.viewBinding
|
||||
import com.bluelinelabs.conductor.viewpager2.RouterStateAdapter
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import java.util.*
|
||||
|
||||
class ViewPager2Controller : BaseController(R.layout.controller_view_pager2) {
|
||||
private val binding: ControllerViewPager2Binding by viewBinding(ControllerViewPager2Binding::bind)
|
||||
|
||||
override val title = "ViewPager2 Demo"
|
||||
|
||||
private val pageColors = intArrayOf(R.color.green_300, R.color.cyan_300, R.color.deep_purple_300, R.color.lime_300, R.color.red_300)
|
||||
|
||||
private lateinit var tabLayoutMediator: TabLayoutMediator
|
||||
|
||||
private val pagerAdapter = object : RouterStateAdapter(this) {
|
||||
override fun configureRouter(router: Router, position: Int) {
|
||||
if (!router.hasRootController()) {
|
||||
val page: Controller = ChildController(
|
||||
title = String.format(Locale.getDefault(), "Child #%d (Swipe to see more)", position),
|
||||
backgroundColor = pageColors[position],
|
||||
colorIsResId = true
|
||||
)
|
||||
router.setRoot(with(page))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount() = pageColors.size
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View) {
|
||||
binding.viewPager.adapter = pagerAdapter
|
||||
tabLayoutMediator = TabLayoutMediator(binding.tabLayout, binding.viewPager) { tab, position ->
|
||||
tab.text = "Page $position"
|
||||
}
|
||||
tabLayoutMediator.attach()
|
||||
}
|
||||
|
||||
override fun onDestroyView(view: View) {
|
||||
if (!activity!!.isChangingConfigurations) {
|
||||
binding.viewPager.adapter = null
|
||||
}
|
||||
tabLayoutMediator.detach()
|
||||
super.onDestroyView(view)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers
|
||||
|
||||
import android.view.View
|
||||
import com.bluelinelabs.conductor.Controller
|
||||
import com.bluelinelabs.conductor.Router
|
||||
import com.bluelinelabs.conductor.RouterTransaction.Companion.with
|
||||
import com.bluelinelabs.conductor.demo.R
|
||||
import com.bluelinelabs.conductor.demo.controllers.base.BaseController
|
||||
import com.bluelinelabs.conductor.demo.databinding.ControllerViewPagerBinding
|
||||
import com.bluelinelabs.conductor.demo.util.viewBinding
|
||||
import com.bluelinelabs.conductor.viewpager.RouterPagerAdapter
|
||||
import java.util.*
|
||||
|
||||
class ViewPagerController : BaseController(R.layout.controller_view_pager) {
|
||||
private val binding: ControllerViewPagerBinding by viewBinding(ControllerViewPagerBinding::bind)
|
||||
|
||||
override val title = "ViewPager Demo"
|
||||
|
||||
private val pageColors = intArrayOf(R.color.green_300, R.color.cyan_300, R.color.deep_purple_300, R.color.lime_300, R.color.red_300)
|
||||
|
||||
private val pagerAdapter = object : RouterPagerAdapter(this) {
|
||||
override fun configureRouter(router: Router, position: Int) {
|
||||
if (!router.hasRootController()) {
|
||||
val page: Controller = ChildController(
|
||||
title = String.format(Locale.getDefault(), "Child #%d (Swipe to see more)", position),
|
||||
backgroundColor = pageColors[position],
|
||||
colorIsResId = true
|
||||
)
|
||||
router.setRoot(with(page))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getCount() = pageColors.size
|
||||
|
||||
override fun getPageTitle(position: Int) = "Page $position"
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View) {
|
||||
binding.viewPager.adapter = pagerAdapter
|
||||
binding.tabLayout.setupWithViewPager(binding.viewPager)
|
||||
}
|
||||
|
||||
override fun onDestroyView(view: View) {
|
||||
if (!activity!!.isChangingConfigurations) {
|
||||
binding.viewPager.adapter = null
|
||||
}
|
||||
binding.tabLayout.setupWithViewPager(null)
|
||||
super.onDestroyView(view)
|
||||
}
|
||||
}
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers.base;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import android.view.View;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
import com.bluelinelabs.conductor.demo.ActionBarProvider;
|
||||
|
||||
public abstract class BaseController extends RefWatchingController {
|
||||
|
||||
protected BaseController() { }
|
||||
|
||||
protected BaseController(Bundle args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
// Note: This is just a quick demo of how an ActionBar *can* be accessed, not necessarily how it *should*
|
||||
// be accessed. In a production app, this would use Dagger instead.
|
||||
protected ActionBar getActionBar() {
|
||||
ActionBarProvider actionBarProvider = ((ActionBarProvider)getActivity());
|
||||
return actionBarProvider != null ? actionBarProvider.getSupportActionBar() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttach(@NonNull View view) {
|
||||
setTitle();
|
||||
super.onAttach(view);
|
||||
}
|
||||
|
||||
protected void setTitle() {
|
||||
Controller parentController = getParentController();
|
||||
while (parentController != null) {
|
||||
if (parentController instanceof BaseController && ((BaseController)parentController).getTitle() != null) {
|
||||
return;
|
||||
}
|
||||
parentController = parentController.getParentController();
|
||||
}
|
||||
|
||||
String title = getTitle();
|
||||
ActionBar actionBar = getActionBar();
|
||||
if (title != null && actionBar != null) {
|
||||
actionBar.setTitle(title);
|
||||
}
|
||||
}
|
||||
|
||||
protected String getTitle() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers.base
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import com.bluelinelabs.conductor.Controller
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||
import com.bluelinelabs.conductor.ControllerChangeType
|
||||
import com.bluelinelabs.conductor.demo.ToolbarProvider
|
||||
|
||||
abstract class BaseController(
|
||||
@LayoutRes private val layoutRes: Int,
|
||||
args: Bundle? = null
|
||||
) : RefWatchingController(args) {
|
||||
|
||||
// Note: This is just a quick demo of how an ActionBar *can* be accessed, not necessarily how it *should*
|
||||
// be accessed. In a production app, this would use Dagger instead.
|
||||
protected val toolbar: Toolbar?
|
||||
get() = (activity as? ToolbarProvider)?.toolbar
|
||||
|
||||
protected open val title: String? = null
|
||||
|
||||
init {
|
||||
addLifecycleListener(object : LifecycleListener() {
|
||||
override fun postCreateView(controller: Controller, view: View) {
|
||||
onViewCreated(view)
|
||||
|
||||
toolbar?.let { configureToolbar(it) }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
savedViewState: Bundle?
|
||||
): View {
|
||||
return inflater.inflate(layoutRes, container, false)
|
||||
}
|
||||
|
||||
open fun onViewCreated(view: View) = Unit
|
||||
|
||||
override fun onAttach(view: View) {
|
||||
toolbar?.let { configureToolbar(it) }
|
||||
super.onAttach(view)
|
||||
}
|
||||
|
||||
override fun onChangeStarted(
|
||||
changeHandler: ControllerChangeHandler,
|
||||
changeType: ControllerChangeType
|
||||
) {
|
||||
super.onChangeStarted(changeHandler, changeType)
|
||||
|
||||
if (changeType.isEnter) {
|
||||
toolbar?.let { configureMenu(it) }
|
||||
}
|
||||
}
|
||||
|
||||
open fun configureToolbar(toolbar: Toolbar) {
|
||||
val title = title ?: return
|
||||
|
||||
var parentController = parentController
|
||||
while (parentController != null) {
|
||||
if (parentController is BaseController && parentController.title != null) {
|
||||
return
|
||||
}
|
||||
parentController = parentController.parentController
|
||||
}
|
||||
|
||||
toolbar.title = title
|
||||
}
|
||||
|
||||
open fun configureMenu(toolbar: Toolbar) {
|
||||
toolbar.menu.clear()
|
||||
}
|
||||
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers.base;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Unbinder;
|
||||
|
||||
public abstract class ButterKnifeController extends Controller {
|
||||
|
||||
private Unbinder unbinder;
|
||||
|
||||
protected ButterKnifeController() { }
|
||||
protected ButterKnifeController(Bundle args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
protected abstract View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container);
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container, @Nullable Bundle savedViewState) {
|
||||
View view = inflateView(inflater, container);
|
||||
unbinder = ButterKnife.bind(this, view);
|
||||
onViewBound(view);
|
||||
return view;
|
||||
}
|
||||
|
||||
protected void onViewBound(@NonNull View view) { }
|
||||
|
||||
@Override
|
||||
protected void onDestroyView(@NonNull View view) {
|
||||
super.onDestroyView(view);
|
||||
unbinder.unbind();
|
||||
unbinder = null;
|
||||
}
|
||||
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers.base;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler;
|
||||
import com.bluelinelabs.conductor.ControllerChangeType;
|
||||
import com.bluelinelabs.conductor.demo.DemoApplication;
|
||||
|
||||
public abstract class RefWatchingController extends ButterKnifeController {
|
||||
|
||||
protected RefWatchingController() { }
|
||||
protected RefWatchingController(Bundle args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
private boolean hasExited;
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
if (hasExited) {
|
||||
DemoApplication.refWatcher.watch(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onChangeEnded(@NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
|
||||
super.onChangeEnded(changeHandler, changeType);
|
||||
|
||||
hasExited = !changeType.isEnter;
|
||||
if (isDestroyed()) {
|
||||
DemoApplication.refWatcher.watch(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.bluelinelabs.conductor.demo.controllers.base
|
||||
|
||||
import android.os.Bundle
|
||||
import com.bluelinelabs.conductor.Controller
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||
import com.bluelinelabs.conductor.ControllerChangeType
|
||||
import com.bluelinelabs.conductor.demo.DemoApplication
|
||||
|
||||
abstract class RefWatchingController(args: Bundle?) : Controller(args) {
|
||||
|
||||
private var hasExited = false
|
||||
|
||||
public override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
|
||||
if (hasExited) {
|
||||
DemoApplication.refWatcher.watch(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onChangeEnded(
|
||||
changeHandler: ControllerChangeHandler,
|
||||
changeType: ControllerChangeType
|
||||
) {
|
||||
super.onChangeEnded(changeHandler, changeType)
|
||||
|
||||
hasExited = !changeType.isEnter
|
||||
if (isDestroyed) {
|
||||
DemoApplication.refWatcher.watch(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,20 +21,17 @@ import android.animation.TimeInterpolator;
|
||||
import android.annotation.TargetApi;
|
||||
import android.os.Build;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.FloatProperty;
|
||||
import android.util.IntProperty;
|
||||
import android.util.Property;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.interpolator.view.animation.FastOutLinearInInterpolator;
|
||||
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
|
||||
import androidx.interpolator.view.animation.LinearOutSlowInInterpolator;
|
||||
import androidx.transition.Transition;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Utility methods for working with animations.
|
||||
*/
|
||||
@@ -79,109 +76,6 @@ public class AnimUtils {
|
||||
return linear;
|
||||
}
|
||||
|
||||
/**
|
||||
* Linear interpolate between a and b with parameter t.
|
||||
*/
|
||||
public static float lerp(float a, float b, float t) {
|
||||
return a + (b - a) * t;
|
||||
}
|
||||
|
||||
/**
|
||||
* A delegate for creating a {@link Property} of <code>int</code> type.
|
||||
*/
|
||||
public static abstract class IntProp<T> {
|
||||
|
||||
public final String name;
|
||||
|
||||
public IntProp(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public abstract void set(T object, int value);
|
||||
public abstract int get(T object);
|
||||
}
|
||||
|
||||
/**
|
||||
* The animation framework has an optimization for <code>Properties</code> of type
|
||||
* <code>int</code> but it was only made public in API24, so wrap the impl in our own type
|
||||
* and conditionally create the appropriate type, delegating the implementation.
|
||||
*/
|
||||
public static <T> Property<T, Integer> createIntProperty(final IntProp<T> impl) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
return new IntProperty<T>(impl.name) {
|
||||
@Override
|
||||
public Integer get(T object) {
|
||||
return impl.get(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(T object, int value) {
|
||||
impl.set(object, value);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return new Property<T, Integer>(Integer.class, impl.name) {
|
||||
@Override
|
||||
public Integer get(T object) {
|
||||
return impl.get(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(T object, Integer value) {
|
||||
impl.set(object, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A delegate for creating a {@link Property} of <code>float</code> type.
|
||||
*/
|
||||
public static abstract class FloatProp<T> {
|
||||
|
||||
public final String name;
|
||||
|
||||
protected FloatProp(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public abstract void set(T object, float value);
|
||||
public abstract float get(T object);
|
||||
}
|
||||
|
||||
/**
|
||||
* The animation framework has an optimization for <code>Properties</code> of type
|
||||
* <code>float</code> but it was only made public in API24, so wrap the impl in our own type
|
||||
* and conditionally create the appropriate type, delegating the implementation.
|
||||
*/
|
||||
public static <T> Property<T, Float> createFloatProperty(final FloatProp<T> impl) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
return new FloatProperty<T>(impl.name) {
|
||||
@Override
|
||||
public Float get(T object) {
|
||||
return impl.get(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(T object, float value) {
|
||||
impl.set(object, value);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return new Property<T, Float>(Float.class, impl.name) {
|
||||
@Override
|
||||
public Float get(T object) {
|
||||
return impl.get(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(T object, Float value) {
|
||||
impl.set(object, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* https://halfthought.wordpress.com/2014/11/07/reveal-transition/
|
||||
* <p/>
|
||||
|
||||
@@ -1,172 +0,0 @@
|
||||
package com.bluelinelabs.conductor.demo.util;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class BundleBuilder {
|
||||
|
||||
private final Bundle bundle;
|
||||
|
||||
public BundleBuilder(Bundle bundle) {
|
||||
this.bundle = bundle;
|
||||
}
|
||||
|
||||
public BundleBuilder putAll(Bundle bundle) {
|
||||
this.bundle.putAll(bundle);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putBoolean(String key, boolean value) {
|
||||
bundle.putBoolean(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putBooleanArray(String key, boolean[] value) {
|
||||
bundle.putBooleanArray(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putDouble(String key, double value) {
|
||||
bundle.putDouble(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putDoubleArray(String key, double[] value) {
|
||||
bundle.putDoubleArray(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putLong(String key, long value) {
|
||||
bundle.putLong(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putLongArray(String key, long[] value) {
|
||||
bundle.putLongArray(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putString(String key, String value) {
|
||||
bundle.putString(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putStringArray(String key, String[] value) {
|
||||
bundle.putStringArray(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putBundle(String key, Bundle value) {
|
||||
bundle.putBundle(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putByte(String key, byte value) {
|
||||
bundle.putByte(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putByteArray(String key, byte[] value) {
|
||||
bundle.putByteArray(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putChar(String key, char value) {
|
||||
bundle.putChar(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putCharArray(String key, char[] value) {
|
||||
bundle.putCharArray(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putCharSequence(String key, CharSequence value) {
|
||||
bundle.putCharSequence(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putCharSequenceArray(String key, CharSequence[] value) {
|
||||
bundle.putCharSequenceArray(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putCharSequenceArrayList(String key, ArrayList<CharSequence> value) {
|
||||
bundle.putCharSequenceArrayList(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putInt(String key, int value) {
|
||||
bundle.putInt(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putIntArray(String key, int[] value) {
|
||||
bundle.putIntArray(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putFloat(String key, float value) {
|
||||
bundle.putFloat(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putFloatArray(String key, float[] value) {
|
||||
bundle.putFloatArray(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putIntegerArrayList(String key, ArrayList<Integer> value) {
|
||||
bundle.putIntegerArrayList(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putParcelable(String key, Parcelable value) {
|
||||
bundle.putParcelable(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putParcelableArray(String key, Parcelable[] value) {
|
||||
bundle.putParcelableArray(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putParcelableArrayList(String key, ArrayList<? extends Parcelable> value) {
|
||||
bundle.putParcelableArrayList(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putSerializable(String key, Serializable value) {
|
||||
bundle.putSerializable(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putShort(String key, short value) {
|
||||
bundle.putShort(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putShortArray(String key, short[] value) {
|
||||
bundle.putShortArray(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putSparseParcelableArray(String key, SparseArray<? extends Parcelable> value) {
|
||||
bundle.putSparseParcelableArray(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BundleBuilder putStringArrayList(String key, ArrayList<String> value) {
|
||||
bundle.putStringArrayList(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Bundle build() {
|
||||
return bundle;
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user