Compare commits

...

31 Commits

Author SHA1 Message Date
Eric Kuck 6dfc5839cc Fixed maven central artifacts 2016-05-12 10:36:54 -05:00
Eric Kuck 8cba63328f Travis should now ignore branches that don't need to be built yet [ci skip] 2016-05-11 17:12:55 -05:00
Eric Kuck 531cc3ff58 Version bump 2016-05-11 17:07:41 -05:00
Eric Kuck c7de32584b Fixed memory leak in view pager demo controller 2016-05-09 18:01:00 -05:00
Eric Kuck 625d1f15b6 Updated sonatype auth (#43) 2016-05-02 10:13:01 -05:00
Hannes Dorfmann 251fc42f67 Changed deploy_snaphsot permission (#41) 2016-04-29 09:58:25 -05:00
Hannes Dorfmann 0bf8e47c48 Fixing snapshot dependencies (#38)
* Resolving inner dependencies

* Resolving inner dependencies

* oops
2016-04-28 17:01:43 -05:00
Eric Kuck 7784f74102 Fixed artifact typo 2016-04-28 10:46:22 -05:00
Eric Kuck bfacab984b Added some tests for lifecycle call ordering 2016-04-27 15:45:59 -05:00
Eric Kuck f8a05731d9 Now removes the view's OnAttachStateListener before removing the reference
Fixes ordering of external lifecycle callbacks
2016-04-27 15:11:54 -05:00
Eric Kuck 116b5066c9 Fixed issue where view was not re-created if its old view was still attached. 2016-04-26 14:33:37 -05:00
Eric Kuck 011adca579 Fixed a typo 2016-04-25 13:33:36 -05:00
adi1133 95de69a006 Make getControllerWithInstanceId search recursively (#30)
* Make getControllerWithInstanceId search recursively

* Deprecate getChildControllerWithInstanceId()

* Rename getControllerWithInstanceId to findController making it internal
2016-04-25 13:11:12 -05:00
Eric Kuck 6cea976d10 Added info about snapshot builds to readme 2016-04-19 16:15:39 -05:00
Eric Kuck c573a8961c fixed conflict 2016-04-19 16:07:12 -05:00
Eric Kuck 6c444fecfb Added nexus details to travis.yml 2016-04-19 15:59:20 -05:00
Eric Kuck 29d1fd1c7d Switched to sonatype for deployment + snapshots 2016-04-19 15:52:40 -05:00
Eric Kuck f6493507f4 Options menu now works for child controllers
Added more tests
2016-04-18 14:07:52 -05:00
Eric Kuck b012df262d Dependency version update 2016-04-16 14:35:39 -05:00
Eric Kuck e028ed42da Version bump 2016-04-16 14:22:38 -05:00
Eric Kuck 43dc561ac2 Fixed up some save/restore view state stuff (incl. tests) 2016-04-16 13:51:37 -05:00
Eric Kuck 217b55090a Added test for backgrounding the host activity 2016-04-16 13:18:24 -05:00
Eric Kuck 5c8b78e41d Fixed orientation change test 2016-04-16 12:25:45 -05:00
Eric Kuck 899dd70d50 Merge pull request #25 from sockeqwe/viewStateListener
added methods to lifecyclelistener for saving/restoring viewstate
2016-04-16 12:04:51 -05:00
Eric Kuck d945571d31 Improved controller lifecycle test coverage (and clarity) 2016-04-16 12:04:20 -05:00
Hannes Dorfmann b7a4386d22 Renamed parameter of onRestoreViewState() to savedViewState 2016-04-16 14:01:41 +02:00
Hannes Dorfmann b117307340 added methods to lifecyclelistener for saving/restoring viewstate 2016-04-15 19:52:43 +02:00
Eric Kuck b8ccf3623f Fixed onRestoreState call ordering for controllers with parent/child relationships 2016-04-14 14:20:56 -05:00
Eric Kuck 495145b72b Router, Activity, target controller, etc is all now available in onRestoreInstanceState 2016-04-13 15:39:09 -05:00
Eric Kuck 99e25d65f2 Merge pull request #20 from adi1133/develop
Fix crash caused by recreating static nested classes using reflection
2016-04-12 16:41:05 -05:00
Adi Pascu 2619d13c8d Fix crash caused by recreating static nested classes using reflection 2016-04-12 23:49:10 +03:00
31 changed files with 1489 additions and 399 deletions
+26
View File
@@ -0,0 +1,26 @@
#!/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
-1
View File
@@ -15,7 +15,6 @@ gen-external-apklibs
# Gradle
.gradle
build
gradle.properties
# Maven
target
+26
View File
@@ -9,3 +9,29 @@ android:
script:
- ./gradlew test
jdk:
- oraclejdk8
branches:
except:
- v2
- feature/nested_controllers
- feature/save_restore_lifecycle
before_install:
- chmod +x .buildscript/deploy_snapshot.sh
after_success:
- .buildscript/deploy_snapshot.sh
cache:
directories:
- $HOME/.gradle
sudo: false
env:
global:
- secure: SoRbxmNTxnGY9qPR5Z8HraRLhHrq7eJ2UaHMuiXDSxpLwUI0IMw+8+l59PNdy/C5ZXXrr6jo6cj+sn/4u6VNg74e2h9i//O+kYGvbGJUnBx8uo1INOrVenpzSnIgxbRLxyN3ZDp8YgwJMl8MSCLM1nj2OMzjNRY5EBnEw5h3qNXBs4Hyhpp2FxVk7dA2yLMUZdOpFKJIsqhH1ZHnCEnYrLlx5cVM9yoefFmJ3PptgumtV8ciBnp0lgDGy5nTykPh6zJBz4rAXgOr95WHvoqpyBRAUZIUEgw/vB5aF8/g+CX2gvTlJYF2N9LgJTNHMEwd+zJtmjM8JzkuCfTT3uMDD3JK5O8eNU03a/+9AkbKpK2+Pt829ZPdkObavXi+oJykCmD5IirukVXE9ushR2J+fM4VOvJinsANSI0zjzFpjZMplX63lfhNu/4lj3AWV2G4rkZd3vZQU+4AuhGQ469RA9BFqUJDIsiQQJwHEAWIqo9WNi6H4H8OhferACd2T3d5Y0O3s0EG5JfdADBPh9YDIkB2zEtGc3gGdxFzxVmH48BJViubAHlH4SgJn7gn69T9wyKmJ1M8F9ph/CdhSHT3kADRDELPEEVXCcANG/verCbyxMlAMXvLNKIGgHD+A0/z9QS1WduOOZwWd1mAuNuEg/rq2OB8SoDTv/BseHrXOpc=
- secure: WpqrbdAvNUFn5cM/Iu3zJOaDvT3jWGHCRwvxQCzX9F8iJeTggB5dB2rjgUDCx8LJ8UAt0VCeOcGtR1RT3EHyaHorN3NWeLcBFAHSz2sXv+2xGkspsXwjfygghZTCdYEzhhvmWlz9Ln4s4QJ2fBFZA07pG0jw4Cp1hSQiJ1WlKfDQezldj8D3pPwg1oOq4b5+HVucQ6+PPVwzGk2c3etwb5205L8H8flRjZrP95mFa5n/H3b/HFIsKX5p+CPNIKCrjBEmX0nHXiV0+g6lBQBV1iCwT56vfmN8Urm4KLId71iMpmvstDxlBBRQx3sz41vxIWGFn/oN7iXJI6XfzVFkyvrd9XAQLQFffq4KpN0REy1L3rjO46sYRXu1ycCP5VFVAAwKZn+o1q6xRjCuma2Qj4tqY754pwPNyzXnndFLO7hoN8KjOgV2nk75+XlRG8LhP356CHET62QBZgJ+sl+aFM3hhknsaEuDQywo8Uz4WZL0lPmYqm5BImQT9sTEF6uQNofg4gMy/uqgGhpLtseQW3PoJXB6dmD5JdNxlOalkGSQ+aI/q5QvR6ruIiuap66o4Bu+YTvHiS2hVzmldvMmLFsU1/zECSI6Fs/vkwRN55R9mbPROWi8SzvftYk9shkFMC5QC1FXA/CHqX1W5nl/HpMrs8R9uPhdZ1lifCiW8Rk=
+11 -4
View File
@@ -20,14 +20,21 @@ Conductor is architecture-agnostic and does not try to force any design decision
## Installation
```gradle
compile 'com.bluelinelabs:conductor:1.1.3'
compile 'com.bluelinelabs:conductor:1.1.6'
// If you want the components that go along with
// Android's support libraries (currently just a PagerAdapter):
compile 'com.bluelinelabs:conductor-support:1.1.3'
compile 'com.bluelinelabs:conductor-support:1.1.6'
// If you want RxJava/RxAndroid lifecycle support:
compile 'com.bluelinelabs:conductor-rxlifecycle:1.1.3'
compile 'com.bluelinelabs:conductor-rxlifecycle:1.1.6'
```
SNAPSHOT:
```gradle
compile 'com.bluelinelabs:conductor:1.1.7-SNAPSHOT'
compile 'com.bluelinelabs:conductor-support:1.1.7-SNAPSHOT'
compile 'com.bluelinelabs:conductor-rxlifecycle:1.1.7-SNAPSHOT'
```
## Components to Know
@@ -109,7 +116,7 @@ The lifecycle of a Controller is significantly simpler to understand than that o
`addChildController` can be called on a `Controller` in order to add nested `Controller`s. Child `Controller`s will receive all lifecycle callbacks that parents get.
### RxJava Lifecycle
If the RxLifecycle dependency has been added, there is an `RxController` available that can be used along with the standard [RxLifecycle library](https://github.com/trello/RxLifecycle). There is also a `ControllerLifecycleProvider` available if you do not wish to use this subclass.
If the RxLifecycle dependency has been added, there is an `RxController` available that can be used along with the standard [RxLifecycle library](https://github.com/trello/RxLifecycle). There is also a `ControllerLifecycleProvider` available if you do not wish to use this subclass.
## License
```
-101
View File
@@ -1,101 +0,0 @@
apply plugin: 'com.github.dcendents.android-maven'
apply plugin: 'com.jfrog.bintray'
group = 'com.bluelinelabs'
version = rootProject.ext.versionName
task sourcesJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier = 'sources'
}
task javadoc(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
exclude '**/R.java'
exclude '**/internal/**'
failOnError = false
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}
artifacts {
archives javadocJar
archives sourcesJar
}
if (project.hasProperty('pom_name')) {
install {
repositories.mavenInstaller {
pom.project {
name pom_name
description pom_description
url pom_url
packaging pom_packaging
groupId 'com.bluelinelabs'
artifactId project.hasProperty('artifactId') ? project.ext.artifactId : ''
organization {
name 'BlueLine Labs'
url 'http://bluelinelabs.com'
}
licenses {
license {
name 'The Apache Software License, Version 2.0'
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
distribution 'repo'
}
}
scm {
url pom_url
connection pom_git_connection
developerConnection pom_git_connection
}
developers {
developer {
id 'erickuck'
name 'Eric Kuck'
}
}
}
}
}
bintray {
user = project.hasProperty('bintray_username') ? bintray_username : ''
key = project.hasProperty('bintray_api_key') ? bintray_api_key : ''
configurations = ['archives']
dryRun = false
publish = false
pkg {
repo = 'bluelinelabs'
userOrg = 'bluelinelabs'
name = pom_name
desc = pom_description
websiteUrl = pom_url
issueTrackerUrl = pom_issue_tracker_url
vcsUrl = pom_url
licenses = ['Apache-2.0']
labels = pom_labels
version {
name = project.version
gpg {
sign = true
passphrase = project.hasProperty('bintray_gpg_passphrase') ? bintray_gpg_passphrase : ''
}
mavenCentralSync {
sync = false
user = project.hasProperty('maven_central_username') ? maven_central_username : ''
password = project.hasProperty('maven_central_password') ? maven_central_password : ''
close = '1'
}
}
}
}
}
+1 -6
View File
@@ -3,15 +3,10 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.0.0'
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3'
classpath 'com.android.tools.build:gradle:2.1.0'
}
}
plugins {
id "com.jfrog.bintray" version "1.5"
}
allprojects {
repositories {
jcenter()
+5 -4
View File
@@ -1,3 +1,6 @@
apply from: rootProject.file('dependencies.gradle')
apply from: rootProject.file('gradle-mvn-push.gradle')
apply plugin: 'com.android.library'
android {
@@ -16,8 +19,8 @@ android {
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
versionCode Integer.parseInt(project.VERSION_CODE)
versionName project.VERSION_NAME
}
}
@@ -31,5 +34,3 @@ dependencies {
ext.artifactId = 'conductor-rxlifecycle'
apply from: rootProject.file('dependencies.gradle')
apply from: rootProject.file('bll-gradle-push.gradle')
+3
View File
@@ -0,0 +1,3 @@
POM_NAME=Conductor RxLifecycle Extensions
POM_ARTIFACT_ID=conductor-rxlifecycle
POM_PACKAGING=aar
@@ -3,6 +3,7 @@ package com.bluelinelabs.conductor.rxlifecycle;
import android.support.annotation.CheckResult;
import android.support.annotation.NonNull;
import com.trello.rxlifecycle.LifecycleTransformer;
import com.trello.rxlifecycle.OutsideLifecycleException;
import com.trello.rxlifecycle.RxLifecycle;
@@ -20,7 +21,7 @@ public class RxControllerLifecycle {
*/
@NonNull
@CheckResult
public static <T> Observable.Transformer<T, T> bindController(@NonNull final Observable<ControllerEvent> lifecycle) {
public static <T> LifecycleTransformer<T> bindController(@NonNull final Observable<ControllerEvent> lifecycle) {
return RxLifecycle.bind(lifecycle, CONTROLLER_LIFECYCLE);
}
+5 -4
View File
@@ -1,3 +1,6 @@
apply from: rootProject.file('dependencies.gradle')
apply from: rootProject.file('gradle-mvn-push.gradle')
apply plugin: 'com.android.library'
android {
@@ -16,8 +19,8 @@ android {
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
versionCode Integer.parseInt(project.VERSION_CODE)
versionName project.VERSION_NAME
}
}
@@ -28,5 +31,3 @@ dependencies {
ext.artifactId = 'conductor-support'
apply from: rootProject.file('dependencies.gradle')
apply from: rootProject.file('bll-gradle-push.gradle')
+3
View File
@@ -0,0 +1,3 @@
POM_NAME=Conductor Support Extensions
POM_ARTIFACT_ID=conductor-support
POM_PACKAGING=aar
+3 -3
View File
@@ -26,8 +26,8 @@ android {
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
versionCode Integer.parseInt(project.VERSION_CODE)
versionName project.VERSION_NAME
}
}
@@ -67,4 +67,4 @@ project.afterEvaluate {
ext.artifactId = 'conductor'
apply from: rootProject.file('dependencies.gradle')
apply from: rootProject.file('bll-gradle-push.gradle')
apply from: rootProject.file('gradle-mvn-push.gradle')
+3
View File
@@ -0,0 +1,3 @@
POM_NAME=Conductor
POM_ARTIFACT_ID=conductor
POM_PACKAGING=aar
@@ -45,7 +45,7 @@ public abstract class Controller {
private static final String KEY_TARGET_INSTANCE_ID = "Controller.target.instanceId";
private static final String KEY_ARGS = "Controller.args";
private static final String KEY_NEEDS_ATTACH = "Controller.needsAttach";
private static final String KEY_REQUESTED_PERMISSIONS = "Controller.childControllers";
private static final String KEY_REQUESTED_PERMISSIONS = "Controller.requestedPermissions";
private static final String KEY_OVERRIDDEN_PUSH_HANDLER = "Controller.overriddenPushHandler";
private static final String KEY_OVERRIDDEN_POP_HANDLER = "Controller.overriddenPopHandler";
private static final String KEY_VIEW_STATE_HIERARCHY = "Controller.viewState.hierarchy";
@@ -54,6 +54,7 @@ public abstract class Controller {
private final Bundle mArgs;
private Bundle mViewState;
private Bundle mSavedInstanceState;
private boolean mIsBeingDestroyed;
private boolean mDestroyed;
private boolean mAttached;
@@ -66,9 +67,11 @@ public abstract class Controller {
private String mInstanceId;
private String mTargetInstanceId;
private boolean mNeedsAttach;
private boolean mHasSavedViewState;
private ControllerChangeHandler mOverriddenPushHandler;
private ControllerChangeHandler mOverriddenPopHandler;
private RetainViewMode mRetainViewMode = RetainViewMode.RELEASE_DETACH;
private OnAttachStateChangeListener mOnAttachStateChangeListener;
private final List<ChildControllerTransaction> mChildControllers = new ArrayList<>();
private final List<LifecycleListener> mLifecycleListeners = new ArrayList<>();
private final ArrayList<String> mRequestedPermissions = new ArrayList<>();
@@ -254,10 +257,11 @@ public abstract class Controller {
/**
* Returns the child Controller with the given instance id, if available.
*
* @param instanceId The instance ID being searched for
* @return The matching child Controller, if one exists
* @deprecated Use {@link #getChildController(String)} or {@link #getChildControllers()} instead.
*/
@Deprecated
public final Controller getChildControllerWithInstanceId(String instanceId) {
for (ControllerTransaction transaction : mChildControllers) {
if (transaction.controller.getInstanceId().equals(instanceId)) {
@@ -267,6 +271,24 @@ public abstract class Controller {
return null;
}
/**
* Returns the Controller with the given instance id, if available.
* May return the controller itself or a matching descendant
* @param instanceId The instance ID being searched for
* @return The matching Controller, if one exists
*/
final Controller findController(String instanceId) {
if (mInstanceId.equals(instanceId))
return this;
for (ControllerTransaction transaction : mChildControllers) {
Controller controllerWithId = transaction.controller.findController(instanceId);
if (controllerWithId != null)
return controllerWithId;
}
return null;
}
/**
* Returns all of this Controller's child Controllers
*/
@@ -646,15 +668,21 @@ public abstract class Controller {
}
final void setRouter(@NonNull Router router) {
mRouter = router;
if (mRouter != router) {
mRouter = router;
for (RouterRequiringFunc listener : mOnRouterSetListeners) {
listener.execute();
}
mOnRouterSetListeners.clear();
performOnRestoreInstanceState();
for (ChildControllerTransaction child : mChildControllers) {
child.controller.setRouter(router);
for (RouterRequiringFunc listener : mOnRouterSetListeners) {
listener.execute();
}
mOnRouterSetListeners.clear();
for (ChildControllerTransaction child : mChildControllers) {
child.controller.setRouter(router);
}
} else {
performOnRestoreInstanceState();
}
}
@@ -739,6 +767,8 @@ public abstract class Controller {
}
private void attach(@NonNull View view) {
mHasSavedViewState = false;
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
lifecycleListener.preAttach(this, view);
}
@@ -782,14 +812,14 @@ public abstract class Controller {
container.removeView(child.controller.getView());
}
}
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
lifecycleListener.postDetach(this, view);
}
if (removeViewRef) {
removeViewReference();
}
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
lifecycleListener.postDetach(this, view);
}
} else if (removeViewRef) {
removeViewReference();
}
@@ -797,7 +827,7 @@ public abstract class Controller {
private void removeViewReference() {
if (mView != null) {
if (!mIsBeingDestroyed) {
if (!mIsBeingDestroyed && !mHasSavedViewState) {
saveViewState(mView);
}
@@ -807,6 +837,7 @@ public abstract class Controller {
onDestroyView(mView);
mView.removeOnAttachStateChangeListener(mOnAttachStateChangeListener);
mView = null;
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
@@ -820,6 +851,11 @@ public abstract class Controller {
}
final View inflate(@NonNull ViewGroup parent) {
if (mView != null && mView.getParent() != null && mView.getParent() != parent) {
detach(mView, true);
removeViewReference();
}
if (mView == null) {
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
lifecycleListener.preCreateView(this);
@@ -827,9 +863,13 @@ public abstract class Controller {
mView = onCreateView(LayoutInflater.from(parent.getContext()), parent);
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
lifecycleListener.postCreateView(this, mView);
}
restoreViewState(mView);
mView.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
mOnAttachStateChangeListener = new OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
if (v == mView) {
@@ -843,11 +883,8 @@ public abstract class Controller {
mViewIsAttached = false;
detach(v, true);
}
});
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
lifecycleListener.postCreateView(this, mView);
}
};
mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
}
return mView;
@@ -892,6 +929,8 @@ public abstract class Controller {
}
final void saveViewState(@NonNull View view) {
mHasSavedViewState = true;
mViewState = new Bundle();
SparseArray<Parcelable> hierarchyState = new SparseArray<>();
@@ -907,6 +946,10 @@ public abstract class Controller {
child.controller.saveViewState(child.controller.mView);
}
}
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
lifecycleListener.onSaveViewState(this, mViewState);
}
}
final void restoreViewState(@NonNull View view) {
@@ -919,6 +962,10 @@ public abstract class Controller {
child.controller.restoreViewState(child.controller.mView);
}
}
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
lifecycleListener.onRestoreViewState(this, mViewState);
}
}
}
@@ -927,8 +974,12 @@ public abstract class Controller {
detach(mView, mIsBeingDestroyed);
}
if (!mHasSavedViewState && mView != null) {
saveViewState(mView);
}
Bundle outState = new Bundle();
outState.putString(KEY_CLASS_NAME, getClass().getCanonicalName());
outState.putString(KEY_CLASS_NAME, getClass().getName());
outState.putBundle(KEY_VIEW_STATE, mViewState);
outState.putBundle(KEY_ARGS, mArgs);
outState.putString(KEY_INSTANCE_ID, mInstanceId);
@@ -975,11 +1026,19 @@ public abstract class Controller {
addChildController(new ChildControllerTransaction(childBundle));
}
Bundle savedState = savedInstanceState.getBundle(KEY_SAVED_STATE);
onRestoreInstanceState(savedState);
mSavedInstanceState = savedInstanceState.getBundle(KEY_SAVED_STATE);
performOnRestoreInstanceState();
}
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
lifecycleListener.onRestoreInstanceState(this, savedState);
private void performOnRestoreInstanceState() {
if (mSavedInstanceState != null && mRouter != null) {
onRestoreInstanceState(mSavedInstanceState);
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
lifecycleListener.onRestoreInstanceState(this, mSavedInstanceState);
}
mSavedInstanceState = null;
}
}
@@ -1000,20 +1059,40 @@ public abstract class Controller {
}
final void createOptionsMenu(Menu menu, MenuInflater inflater) {
if (mAttached && mHasOptionsMenu && !mOptionsMenuHidden) {
onCreateOptionsMenu(menu, inflater);
if (mAttached) {
if (mHasOptionsMenu && !mOptionsMenuHidden) {
onCreateOptionsMenu(menu, inflater);
}
for (ChildControllerTransaction child : mChildControllers) {
child.controller.createOptionsMenu(menu, inflater);
}
}
}
final void prepareOptionsMenu(Menu menu) {
if (mAttached && mHasOptionsMenu && !mOptionsMenuHidden) {
onPrepareOptionsMenu(menu);
if (mAttached) {
if (mHasOptionsMenu && !mOptionsMenuHidden) {
onPrepareOptionsMenu(menu);
}
for (ChildControllerTransaction child : mChildControllers) {
child.controller.onPrepareOptionsMenu(menu);
}
}
}
final boolean optionsItemSelected(MenuItem item) {
if (mAttached && mHasOptionsMenu && !mOptionsMenuHidden) {
return onOptionsItemSelected(item);
if (mAttached) {
if (mHasOptionsMenu && !mOptionsMenuHidden && onOptionsItemSelected(item)) {
return true;
}
for (ChildControllerTransaction child : mChildControllers) {
if (child.controller.onOptionsItemSelected(item)) {
return true;
}
}
}
return false;
}
@@ -1074,6 +1153,9 @@ public abstract class Controller {
public void onSaveInstanceState(@NonNull Controller controller, @NonNull Bundle outState) { }
public void onRestoreInstanceState(@NonNull Controller controller, @NonNull Bundle savedInstanceState) { }
public void onSaveViewState(@NonNull Controller controller, @NonNull Bundle outState) { }
public void onRestoreViewState(@NonNull Controller controller, @NonNull Bundle savedViewState) { }
}
}
@@ -53,7 +53,7 @@ public abstract class ControllerChangeHandler {
final Bundle toBundle() {
Bundle bundle = new Bundle();
bundle.putString(KEY_CLASS_NAME, getClass().getCanonicalName());
bundle.putString(KEY_CLASS_NAME, getClass().getName());
Bundle savedState = new Bundle();
saveToBundle(savedState);
@@ -271,14 +271,10 @@ public class Router {
*/
public Controller getControllerWithInstanceId(String instanceId) {
for (ControllerTransaction transaction : mBackStack) {
if (transaction.controller.getInstanceId().equals(instanceId)) {
return transaction.controller;
} else {
Controller childWithId = transaction.controller.getChildControllerWithInstanceId(instanceId);
if (childWithId != null) {
return childWithId;
Controller controllerWithId = transaction.controller.findController(instanceId);
if (controllerWithId != null) {
return controllerWithId;
}
}
}
return null;
}
@@ -403,6 +399,11 @@ public class Router {
public final void onRestoreInstanceState(Bundle savedInstanceState) {
mBackStack.restoreInstanceState(savedInstanceState);
Iterator<RouterTransaction> backstackIterator = mBackStack.reverseIterator();
while (backstackIterator.hasNext()) {
backstackIterator.next().controller.setRouter(this);
}
}
public final void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
@@ -50,8 +50,8 @@ public class TransitionChangeHandlerCompat extends ControllerChangeHandler {
public void saveToBundle(@NonNull Bundle bundle) {
super.saveToBundle(bundle);
bundle.putString(KEY_TRANSITION_HANDLER_CLASS, mTransitionChangeHandler.getClass().getCanonicalName());
bundle.putString(KEY_FALLBACK_HANDLER_CLASS, mFallbackChangeHandler.getClass().getCanonicalName());
bundle.putString(KEY_TRANSITION_HANDLER_CLASS, mTransitionChangeHandler.getClass().getName());
bundle.putString(KEY_FALLBACK_HANDLER_CLASS, mFallbackChangeHandler.getClass().getName());
Bundle transitionBundle = new Bundle();
mTransitionChangeHandler.saveToBundle(transitionBundle);
@@ -61,13 +61,16 @@ public class LifecycleHandler extends Fragment implements ActivityLifecycleCallb
Router router = mRouterMap.get(getRouterHashKey(container));
if (router == null) {
router = new Router();
router.setHost(this, container);
if (savedInstanceState != null) {
router.onRestoreInstanceState(savedInstanceState);
}
mRouterMap.put(getRouterHashKey(container), router);
} else {
router.setHost(this, container);
}
router.setHost(this, container);
return router;
}
@@ -0,0 +1,175 @@
package com.bluelinelabs.conductor;
import android.os.Parcel;
import android.os.Parcelable;
public class CallState implements Parcelable {
public int changeStartCalls;
public int changeEndCalls;
public int createViewCalls;
public int attachCalls;
public int destroyViewCalls;
public int detachCalls;
public int destroyCalls;
public int saveInstanceStateCalls;
public int restoreInstanceStateCalls;
public int saveViewStateCalls;
public int restoreViewStateCalls;
public int onActivityResultCalls;
public int onRequestPermissionsResultCalls;
public int createOptionsMenuCalls;
public CallState() {
this(false);
}
public CallState(boolean setupForAddedController) {
if (setupForAddedController) {
changeStartCalls++;
changeEndCalls++;
createViewCalls++;
attachCalls++;
}
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
CallState callState = (CallState)o;
if (changeStartCalls != callState.changeStartCalls) {
return false;
}
if (changeEndCalls != callState.changeEndCalls) {
return false;
}
if (createViewCalls != callState.createViewCalls) {
return false;
}
if (attachCalls != callState.attachCalls) {
return false;
}
if (destroyViewCalls != callState.destroyViewCalls) {
return false;
}
if (detachCalls != callState.detachCalls) {
return false;
}
if (destroyCalls != callState.destroyCalls) {
return false;
}
if (saveInstanceStateCalls != callState.saveInstanceStateCalls) {
return false;
}
if (saveViewStateCalls != callState.saveViewStateCalls) {
return false;
}
if (restoreViewStateCalls != callState.restoreViewStateCalls) {
return false;
}
if (onActivityResultCalls != callState.onActivityResultCalls) {
return false;
}
if (onRequestPermissionsResultCalls != callState.onRequestPermissionsResultCalls) {
return false;
}
if (createOptionsMenuCalls != callState.createOptionsMenuCalls) {
return false;
}
return restoreInstanceStateCalls == callState.restoreInstanceStateCalls;
}
@Override
public int hashCode() {
int result = changeStartCalls;
result = 31 * result + changeEndCalls;
result = 31 * result + createViewCalls;
result = 31 * result + attachCalls;
result = 31 * result + destroyViewCalls;
result = 31 * result + detachCalls;
result = 31 * result + destroyCalls;
result = 31 * result + saveInstanceStateCalls;
result = 31 * result + restoreInstanceStateCalls;
result = 31 * result + saveViewStateCalls;
result = 31 * result + restoreViewStateCalls;
result = 31 * result + onActivityResultCalls;
result = 31 * result + onRequestPermissionsResultCalls;
result = 31 * result + createOptionsMenuCalls;
return result;
}
@Override
public String toString() {
return "\nCallState{" +
"\n changeStartCalls=" + changeStartCalls +
"\n changeEndCalls=" + changeEndCalls +
"\n createViewCalls=" + createViewCalls +
"\n attachCalls=" + attachCalls +
"\n destroyViewCalls=" + destroyViewCalls +
"\n detachCalls=" + detachCalls +
"\n destroyCalls=" + destroyCalls +
"\n saveInstanceStateCalls=" + saveInstanceStateCalls +
"\n restoreInstanceStateCalls=" + restoreInstanceStateCalls +
"\n saveViewStateCalls=" + saveViewStateCalls +
"\n restoreViewStateCalls=" + restoreViewStateCalls +
"\n onActivityResultCalls= " + onActivityResultCalls +
"\n onRequestPermissionsResultCalls= " + onRequestPermissionsResultCalls +
"\n createOptionsMenuCalls= " + createOptionsMenuCalls +
"}\n";
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(changeStartCalls);
out.writeInt(changeEndCalls);
out.writeInt(createViewCalls);
out.writeInt(attachCalls);
out.writeInt(destroyViewCalls);
out.writeInt(detachCalls);
out.writeInt(destroyCalls);
out.writeInt(saveInstanceStateCalls);
out.writeInt(restoreInstanceStateCalls);
out.writeInt(saveViewStateCalls);
out.writeInt(restoreViewStateCalls);
out.writeInt(onActivityResultCalls);
out.writeInt(onRequestPermissionsResultCalls);
out.writeInt(createOptionsMenuCalls);
}
public static final Parcelable.Creator<CallState> CREATOR = new Parcelable.Creator<CallState>() {
public CallState createFromParcel(Parcel in) {
CallState state = new CallState();
state.changeStartCalls = in.readInt();
state.changeEndCalls = in.readInt();
state.createViewCalls = in.readInt();
state.attachCalls = in.readInt();
state.destroyViewCalls = in.readInt();
state.detachCalls = in.readInt();
state.destroyCalls = in.readInt();
state.saveInstanceStateCalls = in.readInt();
state.restoreInstanceStateCalls = in.readInt();
state.saveViewStateCalls = in.readInt();
state.restoreViewStateCalls = in.readInt();
state.onActivityResultCalls = in.readInt();
state.onRequestPermissionsResultCalls = in.readInt();
state.createOptionsMenuCalls = in.readInt();
return state;
}
public CallState[] newArray(int size) {
return new CallState[size];
}
};
}
@@ -0,0 +1,627 @@
package com.bluelinelabs.conductor;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.bluelinelabs.conductor.Controller.LifecycleListener;
import com.bluelinelabs.conductor.ControllerChangeHandler.ControllerChangeCompletedListener;
import com.bluelinelabs.conductor.ControllerTransaction.ControllerChangeType;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.util.ActivityController;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class ControllerLifecycleTests {
private ActivityController<TestActivity> mActivityController;
private Router mRouter;
private CallState mCurrentCallState;
public void createActivityController(Bundle savedInstanceState) {
mActivityController = Robolectric.buildActivity(TestActivity.class).create(savedInstanceState).start();
@IdRes int containerId = 4;
FrameLayout routerContainer = new FrameLayout(mActivityController.get());
routerContainer.setId(containerId);
mRouter = Conductor.attachRouter(mActivityController.get(), routerContainer, savedInstanceState);
if (!mRouter.hasRootController()) {
mRouter.setRoot(new TestController());
}
}
@Before
public void setup() {
createActivityController(null);
mCurrentCallState = new CallState();
}
@Test
public void testNormalLifecycle() {
TestController controller = new TestController();
attachLifecycleListener(controller);
CallState expectedCallState = new CallState();
assertCalls(expectedCallState, controller);
mRouter.pushController(RouterTransaction.builder(controller)
.pushChangeHandler(getPushHandler(expectedCallState, controller))
.popChangeHandler(getPopHandler(expectedCallState, controller))
.build()
);
assertCalls(expectedCallState, controller);
mRouter.popCurrentController();
Assert.assertNull(controller.getView());
assertCalls(expectedCallState, controller);
}
@Test
public void testLifecycleWithActivityDestroy() {
TestController controller = new TestController();
attachLifecycleListener(controller);
CallState expectedCallState = new CallState();
assertCalls(expectedCallState, controller);
mRouter.pushController(RouterTransaction.builder(controller)
.pushChangeHandler(getPushHandler(expectedCallState, controller))
.build()
);
assertCalls(expectedCallState, controller);
mActivityController.pause();
assertCalls(expectedCallState, controller);
mActivityController.stop();
assertCalls(expectedCallState, controller);
mActivityController.destroy();
expectedCallState.detachCalls++;
expectedCallState.destroyViewCalls++;
expectedCallState.destroyCalls++;
assertCalls(expectedCallState, controller);
}
@Test
public void testLifecycleWithActivityConfigurationChange() {
TestController controller = new TestController();
attachLifecycleListener(controller);
CallState expectedCallState = new CallState();
assertCalls(expectedCallState, controller);
mRouter.pushController(RouterTransaction.builder(controller)
.pushChangeHandler(getPushHandler(expectedCallState, controller))
.tag("root")
.build()
);
assertCalls(expectedCallState, controller);
mActivityController.get().isChangingConfigurations = true;
Bundle bundle = new Bundle();
mActivityController.saveInstanceState(bundle);
expectedCallState.detachCalls++;
expectedCallState.saveViewStateCalls++;
expectedCallState.saveInstanceStateCalls++;
assertCalls(expectedCallState, controller);
mActivityController.pause();
assertCalls(expectedCallState, controller);
mActivityController.stop();
assertCalls(expectedCallState, controller);
mActivityController.destroy();
expectedCallState.destroyViewCalls++;
assertCalls(expectedCallState, controller);
createActivityController(bundle);
controller = (TestController)mRouter.getControllerWithTag("root");
expectedCallState.restoreInstanceStateCalls++;
expectedCallState.restoreViewStateCalls++;
expectedCallState.changeStartCalls++;
expectedCallState.changeEndCalls++;
expectedCallState.createViewCalls++;
// Lifecycle listener isn't attached during restore, grab the current views from the controller for this stuff...
mCurrentCallState.restoreInstanceStateCalls = controller.currentCallState.restoreInstanceStateCalls;
mCurrentCallState.restoreViewStateCalls = controller.currentCallState.restoreViewStateCalls;
mCurrentCallState.changeStartCalls = controller.currentCallState.changeStartCalls;
mCurrentCallState.changeEndCalls = controller.currentCallState.changeEndCalls;
mCurrentCallState.createViewCalls = controller.currentCallState.createViewCalls;
assertCalls(expectedCallState, controller);
mActivityController.resume();
assertCalls(expectedCallState, controller);
}
@Test
public void testLifecycleWithActivityBackground() {
TestController controller = new TestController();
attachLifecycleListener(controller);
CallState expectedCallState = new CallState();
assertCalls(expectedCallState, controller);
mRouter.pushController(RouterTransaction.builder(controller)
.pushChangeHandler(getPushHandler(expectedCallState, controller))
.build()
);
assertCalls(expectedCallState, controller);
mActivityController.pause();
Bundle bundle = new Bundle();
mActivityController.saveInstanceState(bundle);
expectedCallState.detachCalls++;
expectedCallState.saveInstanceStateCalls++;
expectedCallState.saveViewStateCalls++;
assertCalls(expectedCallState, controller);
mActivityController.resume();
expectedCallState.createViewCalls++;
expectedCallState.restoreViewStateCalls++;
}
@Test
public void testLifecycleCallOrder() {
final TestController testController = new TestController();
final CallState callState = new CallState();
testController.addLifecycleListener(new LifecycleListener() {
@Override
public void preCreateView(@NonNull Controller controller) {
callState.createViewCalls++;
Assert.assertEquals(1, callState.createViewCalls);
Assert.assertEquals(0, testController.currentCallState.createViewCalls);
Assert.assertEquals(0, callState.attachCalls);
Assert.assertEquals(0, testController.currentCallState.attachCalls);
Assert.assertEquals(0, callState.detachCalls);
Assert.assertEquals(0, testController.currentCallState.detachCalls);
Assert.assertEquals(0, callState.destroyViewCalls);
Assert.assertEquals(0, testController.currentCallState.destroyViewCalls);
Assert.assertEquals(0, callState.destroyCalls);
Assert.assertEquals(0, testController.currentCallState.destroyCalls);
}
@Override
public void postCreateView(@NonNull Controller controller, @NonNull View view) {
callState.createViewCalls++;
Assert.assertEquals(2, callState.createViewCalls);
Assert.assertEquals(1, testController.currentCallState.createViewCalls);
Assert.assertEquals(0, callState.attachCalls);
Assert.assertEquals(0, testController.currentCallState.attachCalls);
Assert.assertEquals(0, callState.detachCalls);
Assert.assertEquals(0, testController.currentCallState.detachCalls);
Assert.assertEquals(0, callState.destroyViewCalls);
Assert.assertEquals(0, testController.currentCallState.destroyViewCalls);
Assert.assertEquals(0, callState.destroyCalls);
Assert.assertEquals(0, testController.currentCallState.destroyCalls);
}
@Override
public void preAttach(@NonNull Controller controller, @NonNull View view) {
callState.attachCalls++;
Assert.assertEquals(2, callState.createViewCalls);
Assert.assertEquals(1, testController.currentCallState.createViewCalls);
Assert.assertEquals(1, callState.attachCalls);
Assert.assertEquals(0, testController.currentCallState.attachCalls);
Assert.assertEquals(0, callState.detachCalls);
Assert.assertEquals(0, testController.currentCallState.detachCalls);
Assert.assertEquals(0, callState.destroyViewCalls);
Assert.assertEquals(0, testController.currentCallState.destroyViewCalls);
Assert.assertEquals(0, callState.destroyCalls);
Assert.assertEquals(0, testController.currentCallState.destroyCalls);
}
@Override
public void postAttach(@NonNull Controller controller, @NonNull View view) {
callState.attachCalls++;
Assert.assertEquals(2, callState.createViewCalls);
Assert.assertEquals(1, testController.currentCallState.createViewCalls);
Assert.assertEquals(2, callState.attachCalls);
Assert.assertEquals(1, testController.currentCallState.attachCalls);
Assert.assertEquals(0, callState.detachCalls);
Assert.assertEquals(0, testController.currentCallState.detachCalls);
Assert.assertEquals(0, callState.destroyViewCalls);
Assert.assertEquals(0, testController.currentCallState.destroyViewCalls);
Assert.assertEquals(0, callState.destroyCalls);
Assert.assertEquals(0, testController.currentCallState.destroyCalls);
}
@Override
public void preDetach(@NonNull Controller controller, @NonNull View view) {
callState.detachCalls++;
Assert.assertEquals(2, callState.createViewCalls);
Assert.assertEquals(1, testController.currentCallState.createViewCalls);
Assert.assertEquals(2, callState.attachCalls);
Assert.assertEquals(1, testController.currentCallState.attachCalls);
Assert.assertEquals(1, callState.detachCalls);
Assert.assertEquals(0, testController.currentCallState.detachCalls);
Assert.assertEquals(0, callState.destroyViewCalls);
Assert.assertEquals(0, testController.currentCallState.destroyViewCalls);
Assert.assertEquals(0, callState.destroyCalls);
Assert.assertEquals(0, testController.currentCallState.destroyCalls);
}
@Override
public void postDetach(@NonNull Controller controller, @NonNull View view) {
callState.detachCalls++;
Assert.assertEquals(2, callState.createViewCalls);
Assert.assertEquals(1, testController.currentCallState.createViewCalls);
Assert.assertEquals(2, callState.attachCalls);
Assert.assertEquals(1, testController.currentCallState.attachCalls);
Assert.assertEquals(2, callState.detachCalls);
Assert.assertEquals(1, testController.currentCallState.detachCalls);
Assert.assertEquals(0, callState.destroyViewCalls);
Assert.assertEquals(0, testController.currentCallState.destroyViewCalls);
Assert.assertEquals(0, callState.destroyCalls);
Assert.assertEquals(0, testController.currentCallState.destroyCalls);
}
@Override
public void preDestroyView(@NonNull Controller controller, @NonNull View view) {
callState.destroyViewCalls++;
Assert.assertEquals(2, callState.createViewCalls);
Assert.assertEquals(1, testController.currentCallState.createViewCalls);
Assert.assertEquals(2, callState.attachCalls);
Assert.assertEquals(1, testController.currentCallState.attachCalls);
Assert.assertEquals(2, callState.detachCalls);
Assert.assertEquals(1, testController.currentCallState.detachCalls);
Assert.assertEquals(1, callState.destroyViewCalls);
Assert.assertEquals(0, testController.currentCallState.destroyViewCalls);
Assert.assertEquals(0, callState.destroyCalls);
Assert.assertEquals(0, testController.currentCallState.destroyCalls);
}
@Override
public void postDestroyView(@NonNull Controller controller) {
callState.destroyViewCalls++;
Assert.assertEquals(2, callState.createViewCalls);
Assert.assertEquals(1, testController.currentCallState.createViewCalls);
Assert.assertEquals(2, callState.attachCalls);
Assert.assertEquals(1, testController.currentCallState.attachCalls);
Assert.assertEquals(2, callState.detachCalls);
Assert.assertEquals(1, testController.currentCallState.detachCalls);
Assert.assertEquals(2, callState.destroyViewCalls);
Assert.assertEquals(1, testController.currentCallState.destroyViewCalls);
Assert.assertEquals(0, callState.destroyCalls);
Assert.assertEquals(0, testController.currentCallState.destroyCalls);
}
@Override
public void preDestroy(@NonNull Controller controller) {
callState.destroyCalls++;
Assert.assertEquals(2, callState.createViewCalls);
Assert.assertEquals(1, testController.currentCallState.createViewCalls);
Assert.assertEquals(2, callState.attachCalls);
Assert.assertEquals(1, testController.currentCallState.attachCalls);
Assert.assertEquals(2, callState.detachCalls);
Assert.assertEquals(1, testController.currentCallState.detachCalls);
Assert.assertEquals(2, callState.destroyViewCalls);
Assert.assertEquals(1, testController.currentCallState.destroyViewCalls);
Assert.assertEquals(1, callState.destroyCalls);
Assert.assertEquals(0, testController.currentCallState.destroyCalls);
}
@Override
public void postDestroy(@NonNull Controller controller) {
callState.destroyCalls++;
Assert.assertEquals(2, callState.createViewCalls);
Assert.assertEquals(1, testController.currentCallState.createViewCalls);
Assert.assertEquals(2, callState.attachCalls);
Assert.assertEquals(1, testController.currentCallState.attachCalls);
Assert.assertEquals(2, callState.detachCalls);
Assert.assertEquals(1, testController.currentCallState.detachCalls);
Assert.assertEquals(2, callState.destroyViewCalls);
Assert.assertEquals(1, testController.currentCallState.destroyViewCalls);
Assert.assertEquals(2, callState.destroyCalls);
Assert.assertEquals(1, testController.currentCallState.destroyCalls);
}
});
mRouter.pushController(RouterTransaction.builder(testController)
.pushChangeHandler(new ChangeHandler(new ChangeHandlerListener() {
@Override
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
container.addView(to);
ViewUtils.setAttached(to, true);
changeListener.onChangeCompleted();
}
}))
.popChangeHandler(new ChangeHandler(new ChangeHandlerListener() {
@Override
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
container.removeView(from);
ViewUtils.setAttached(from, false);
changeListener.onChangeCompleted();
}
}))
.build());
mRouter.popController(testController);
Assert.assertEquals(2, callState.createViewCalls);
Assert.assertEquals(2, callState.attachCalls);
Assert.assertEquals(2, callState.detachCalls);
Assert.assertEquals(2, callState.destroyViewCalls);
Assert.assertEquals(2, callState.destroyCalls);
}
@Test
public void testChildLifecycle() {
Controller parent = new TestController();
mRouter.pushController(RouterTransaction.builder(parent)
.pushChangeHandler(new ChangeHandler(new ChangeHandlerListener() {
@Override
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
container.addView(to);
ViewUtils.setAttached(to, true);
changeListener.onChangeCompleted();
}
}))
.build());
TestController child = new TestController();
attachLifecycleListener(child);
CallState expectedCallState = new CallState();
assertCalls(expectedCallState, child);
parent.addChildController(ChildControllerTransaction.builder(child, TestController.VIEW_ID)
.pushChangeHandler(getPushHandler(expectedCallState, child))
.popChangeHandler(getPopHandler(expectedCallState, child))
.build()
);
assertCalls(expectedCallState, child);
parent.removeChildController(child);
assertCalls(expectedCallState, child);
}
@Test
public void testChildLifecycle2() {
Controller parent = new TestController();
mRouter.pushController(RouterTransaction.builder(parent)
.pushChangeHandler(new ChangeHandler(new ChangeHandlerListener() {
@Override
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
container.addView(to);
ViewUtils.setAttached(to, true);
changeListener.onChangeCompleted();
}
}))
.popChangeHandler(new ChangeHandler(new ChangeHandlerListener() {
@Override
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
container.removeView(from);
ViewUtils.setAttached(from, false);
changeListener.onChangeCompleted();
}
}))
.build());
TestController child = new TestController();
attachLifecycleListener(child);
CallState expectedCallState = new CallState();
assertCalls(expectedCallState, child);
parent.addChildController(ChildControllerTransaction.builder(child, TestController.VIEW_ID)
.pushChangeHandler(getPushHandler(expectedCallState, child))
.popChangeHandler(getPopHandler(expectedCallState, child))
.build()
);
assertCalls(expectedCallState, child);
mRouter.popCurrentController();
ViewUtils.setAttached(child.getView(), false);
expectedCallState.detachCalls++;
expectedCallState.destroyViewCalls++;
expectedCallState.destroyCalls++;
assertCalls(expectedCallState, child);
}
private ChangeHandler getPushHandler(final CallState expectedCallState, final TestController controller) {
return new ChangeHandler(new ChangeHandlerListener() {
@Override
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
expectedCallState.changeStartCalls++;
expectedCallState.createViewCalls++;
assertCalls(expectedCallState, controller);
container.addView(to);
ViewUtils.setAttached(to, true);
expectedCallState.attachCalls++;
assertCalls(expectedCallState, controller);
changeListener.onChangeCompleted();
expectedCallState.changeEndCalls++;
assertCalls(expectedCallState, controller);
}
});
}
private ChangeHandler getPopHandler(final CallState expectedCallState, final TestController controller) {
return new ChangeHandler(new ChangeHandlerListener() {
@Override
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
expectedCallState.changeStartCalls++;
assertCalls(expectedCallState, controller);
container.removeView(from);
ViewUtils.setAttached(from, false);
expectedCallState.destroyViewCalls++;
expectedCallState.detachCalls++;
expectedCallState.destroyCalls++;
assertCalls(expectedCallState, controller);
changeListener.onChangeCompleted();
expectedCallState.changeEndCalls++;
assertCalls(expectedCallState, controller);
}
});
}
private void assertCalls(CallState callState, TestController controller) {
Assert.assertEquals("Expected call counts and controller call counts do not match.", callState, controller.currentCallState);
Assert.assertEquals("Expected call counts and lifecycle call counts do not match.", callState, mCurrentCallState);
}
private void attachLifecycleListener(Controller controller) {
controller.addLifecycleListener(new LifecycleListener() {
@Override
public void onChangeStart(@NonNull Controller controller, @NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
mCurrentCallState.changeStartCalls++;
}
@Override
public void onChangeEnd(@NonNull Controller controller, @NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
mCurrentCallState.changeEndCalls++;
}
@Override
public void postCreateView(@NonNull Controller controller, @NonNull View view) {
mCurrentCallState.createViewCalls++;
}
@Override
public void postAttach(@NonNull Controller controller, @NonNull View view) {
mCurrentCallState.attachCalls++;
}
@Override
public void postDestroyView(@NonNull Controller controller) {
mCurrentCallState.destroyViewCalls++;
}
@Override
public void postDetach(@NonNull Controller controller, @NonNull View view) {
mCurrentCallState.detachCalls++;
}
@Override
public void postDestroy(@NonNull Controller controller) {
mCurrentCallState.destroyCalls++;
}
@Override
public void onSaveInstanceState(@NonNull Controller controller, @NonNull Bundle outState) {
mCurrentCallState.saveInstanceStateCalls++;
}
@Override
public void onRestoreInstanceState(@NonNull Controller controller, @NonNull Bundle savedInstanceState) {
mCurrentCallState.restoreInstanceStateCalls++;
}
@Override
public void onSaveViewState(@NonNull Controller controller, @NonNull Bundle outState) {
mCurrentCallState.saveViewStateCalls++;
}
@Override
public void onRestoreViewState(@NonNull Controller controller, @NonNull Bundle savedViewState) {
mCurrentCallState.restoreViewStateCalls++;
}
});
}
interface ChangeHandlerListener {
void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener);
}
public static class ChangeHandler extends ControllerChangeHandler {
private ChangeHandlerListener mListener;
public ChangeHandler() { }
public ChangeHandler(ChangeHandlerListener listener) {
mListener = listener;
}
@Override
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
mListener.performChange(container, from, to, isPush, changeListener);
}
}
}
@@ -1,15 +1,13 @@
package com.bluelinelabs.conductor;
import android.app.Activity;
import android.support.annotation.NonNull;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.bluelinelabs.conductor.Controller.LifecycleListener;
import com.bluelinelabs.conductor.Controller.RetainViewMode;
import com.bluelinelabs.conductor.ControllerChangeHandler.ControllerChangeCompletedListener;
import com.bluelinelabs.conductor.ControllerTransaction.ControllerChangeType;
import org.junit.Assert;
import org.junit.Before;
@@ -27,148 +25,22 @@ public class ControllerTests {
private ActivityController<TestActivity> mActivityController;
private Router mRouter;
private int mChangeStartCalls;
private int mChangeEndCalls;
private int mCreateViewCalls;
private int mAttachCalls;
private int mDestroyViewCalls;
private int mDetachCalls;
private int mDestroyCalls;
public void createActivityController(Bundle savedInstanceState) {
mActivityController = Robolectric.buildActivity(TestActivity.class).create(savedInstanceState).start();
@IdRes int containerId = 4;
FrameLayout routerContainer = new FrameLayout(mActivityController.get());
routerContainer.setId(containerId);
mRouter = Conductor.attachRouter(mActivityController.get(), routerContainer, savedInstanceState);
if (!mRouter.hasRootController()) {
mRouter.setRoot(new TestController());
}
}
@Before
public void setup() {
mActivityController = Robolectric.buildActivity(TestActivity.class).create();
Activity activity = mActivityController.get();
mRouter = Conductor.attachRouter(activity, new FrameLayout(activity), null);
mRouter.setRoot(new TestController());
mChangeStartCalls = 0;
mChangeEndCalls = 0;
mCreateViewCalls = 0;
mAttachCalls = 0;
mDestroyViewCalls = 0;
mDestroyCalls = 0;
mDestroyCalls = 0;
}
@Test
public void testNormalLifecycle() {
Controller controller = new TestController();
attachLifecycleListener(controller);
assertCalls(0, 0, 0, 0, 0, 0, 0);
mRouter.pushController(RouterTransaction.builder(controller)
.pushChangeHandler(getPushHandler(0, 0, 0, 0, 0, 0, 0))
.popChangeHandler(getPopHandler(1, 1, 1, 1, 0, 0, 0))
.build()
);
assertCalls(1, 1, 1, 1, 0, 0, 0);
mRouter.popCurrentController();
Assert.assertNull(controller.getView());
assertCalls(2, 2, 1, 1, 1, 1, 1);
}
@Test
public void testLifecycleWithActivityDestroy() {
Controller controller = new TestController();
attachLifecycleListener(controller);
assertCalls(0, 0, 0, 0, 0, 0, 0);
mRouter.pushController(RouterTransaction.builder(controller)
.pushChangeHandler(getPushHandler(0, 0, 0, 0, 0, 0, 0))
.build()
);
assertCalls(1, 1, 1, 1, 0, 0, 0);
mActivityController.pause();
assertCalls(1, 1, 1, 1, 0, 0, 0);
mActivityController.stop();
assertCalls(1, 1, 1, 1, 0, 0, 0);
mActivityController.destroy();
assertCalls(1, 1, 1, 1, 1, 1, 1);
}
@Test
public void testChildLifecycle() {
Controller parent = new TestController();
mRouter.pushController(RouterTransaction.builder(parent)
.pushChangeHandler(new ChangeHandler(new ChangeHandlerListener() {
@Override
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
container.addView(to);
ViewUtils.setAttached(to, true);
changeListener.onChangeCompleted();
}
}))
.build());
Controller child = new TestController();
attachLifecycleListener(child);
assertCalls(0, 0, 0, 0, 0, 0, 0);
parent.addChildController(ChildControllerTransaction.builder(child, TestController.VIEW_ID)
.pushChangeHandler(getPushHandler(0, 0, 0, 0, 0, 0, 0))
.popChangeHandler(getPopHandler(1, 1, 1, 1, 0, 0, 0))
.build()
);
assertCalls(1, 1, 1, 1, 0, 0, 0);
parent.removeChildController(child);
assertCalls(2, 2, 1, 1, 1, 1, 1);
}
@Test
public void testChildLifecycle2() {
Controller parent = new TestController();
mRouter.pushController(RouterTransaction.builder(parent)
.pushChangeHandler(new ChangeHandler(new ChangeHandlerListener() {
@Override
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
container.addView(to);
ViewUtils.setAttached(to, true);
changeListener.onChangeCompleted();
}
}))
.popChangeHandler(new ChangeHandler(new ChangeHandlerListener() {
@Override
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
container.removeView(from);
ViewUtils.setAttached(from, false);
changeListener.onChangeCompleted();
}
}))
.build());
Controller child = new TestController();
attachLifecycleListener(child);
assertCalls(0, 0, 0, 0, 0, 0, 0);
parent.addChildController(ChildControllerTransaction.builder(child, TestController.VIEW_ID)
.pushChangeHandler(getPushHandler(0, 0, 0, 0, 0, 0, 0))
.popChangeHandler(getPopHandler(1, 1, 1, 1, 0, 0, 0))
.build()
);
assertCalls(1, 1, 1, 1, 0, 0, 0);
mRouter.popCurrentController();
ViewUtils.setAttached(child.getView(), false);
assertCalls(1, 1, 1, 1, 1, 1, 1);
createActivityController(null);
}
@Test
@@ -199,98 +71,205 @@ public class ControllerTests {
Assert.assertNull(controller.getView());
}
private ChangeHandler getPushHandler(final int changeStart, final int changeEnd, final int bindView, final int attach, final int unbindView, final int detach, final int destroy) {
return new ChangeHandler(new ChangeHandlerListener() {
@Override
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
assertCalls(changeStart + 1, changeEnd, bindView + 1, attach, unbindView, detach, destroy);
container.addView(to);
ViewUtils.setAttached(to, true);
assertCalls(changeStart + 1, changeEnd, bindView + 1, attach + 1, unbindView, detach, destroy);
changeListener.onChangeCompleted();
}
});
@Test
public void testActivityResult() {
TestController controller = new TestController();
CallState expectedCallState = new CallState(true);
mRouter.pushController(RouterTransaction.builder(controller).build());
ViewUtils.setAttached(controller.getView(), true);
// Ensure that calling onActivityResult w/o requesting a result doesn't do anything
mRouter.onActivityResult(1, Activity.RESULT_OK, null);
assertCalls(expectedCallState, controller);
// Ensure starting an activity for result gets us the result back
controller.startActivityForResult(new Intent("action"), 1);
mRouter.onActivityResult(1, Activity.RESULT_OK, null);
expectedCallState.onActivityResultCalls++;
assertCalls(expectedCallState, controller);
// Ensure requesting a result w/o calling startActivityForResult works
controller.registerForActivityResult(2);
mRouter.onActivityResult(2, Activity.RESULT_OK, null);
expectedCallState.onActivityResultCalls++;
assertCalls(expectedCallState, controller);
}
private ChangeHandler getPopHandler(final int changeStart, final int changeEnd, final int bindView, final int attach, final int unbindView, final int detach, final int destroy) {
return new ChangeHandler(new ChangeHandlerListener() {
@Override
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
assertCalls(changeStart + 1, changeEnd, bindView, attach, unbindView, detach, destroy);
container.removeView(from);
ViewUtils.setAttached(from, false);
assertCalls(changeStart + 1, changeEnd, bindView, attach, unbindView + 1, detach + 1, destroy + 1);
changeListener.onChangeCompleted();
}
});
@Test
public void testActivityResultForChild() {
TestController parent = new TestController();
TestController child = new TestController();
mRouter.pushController(RouterTransaction.builder(parent).build());
ViewUtils.setAttached(parent.getView(), true);
parent.addChildController(ChildControllerTransaction.builder(child, TestController.VIEW_ID).build());
ViewUtils.setAttached(child.getView(), true);
CallState childExpectedCallState = new CallState(true);
CallState parentExpectedCallState = new CallState(true);
// Ensure that calling onActivityResult w/o requesting a result doesn't do anything
mRouter.onActivityResult(1, Activity.RESULT_OK, null);
assertCalls(childExpectedCallState, child);
assertCalls(parentExpectedCallState, parent);
// Ensure starting an activity for result gets us the result back
child.startActivityForResult(new Intent("action"), 1);
mRouter.onActivityResult(1, Activity.RESULT_OK, null);
childExpectedCallState.onActivityResultCalls++;
assertCalls(childExpectedCallState, child);
assertCalls(parentExpectedCallState, parent);
// Ensure requesting a result w/o calling startActivityForResult works
child.registerForActivityResult(2);
mRouter.onActivityResult(2, Activity.RESULT_OK, null);
childExpectedCallState.onActivityResultCalls++;
assertCalls(childExpectedCallState, child);
assertCalls(parentExpectedCallState, parent);
}
private void assertCalls(int changeStart, int changeEnd, int bindView, int attach, int unbindView, int detach, int destroy) {
Assert.assertEquals(changeStart, mChangeStartCalls);
Assert.assertEquals(changeEnd, mChangeEndCalls);
Assert.assertEquals(bindView, mCreateViewCalls);
Assert.assertEquals(attach, mAttachCalls);
Assert.assertEquals(unbindView, mDestroyViewCalls);
Assert.assertEquals(detach, mDetachCalls);
Assert.assertEquals(destroy, mDestroyCalls);
@Test
public void testPermissionResult() {
final String[] requestedPermissions = new String[] {"test"};
TestController controller = new TestController();
CallState expectedCallState = new CallState(true);
mRouter.pushController(RouterTransaction.builder(controller).build());
ViewUtils.setAttached(controller.getView(), true);
// Ensure that calling handleRequestedPermission w/o requesting a result doesn't do anything
mRouter.onRequestPermissionsResult("anotherId", 1, requestedPermissions, new int[] {1});
assertCalls(expectedCallState, controller);
// Ensure requesting the permission gets us the result back
try {
controller.requestPermissions(requestedPermissions, 1);
} catch (NoSuchMethodError ignored) { }
mRouter.onRequestPermissionsResult(controller.getInstanceId(), 1, requestedPermissions, new int[] {1});
expectedCallState.onRequestPermissionsResultCalls++;
assertCalls(expectedCallState, controller);
}
private void attachLifecycleListener(Controller controller) {
controller.addLifecycleListener(new LifecycleListener() {
@Override
public void onChangeStart(@NonNull Controller controller, @NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
mChangeStartCalls++;
}
@Test
public void testPermissionResultForChild() {
final String[] requestedPermissions = new String[] {"test"};
@Override
public void onChangeEnd(@NonNull Controller controller, @NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
mChangeEndCalls++;
}
TestController parent = new TestController();
TestController child = new TestController();
@Override
public void postCreateView(@NonNull Controller controller, @NonNull View view) {
mCreateViewCalls++;
}
mRouter.pushController(RouterTransaction.builder(parent).build());
ViewUtils.setAttached(parent.getView(), true);
parent.addChildController(ChildControllerTransaction.builder(child, TestController.VIEW_ID).build());
ViewUtils.setAttached(child.getView(), true);
@Override
public void postAttach(@NonNull Controller controller, @NonNull View view) {
mAttachCalls++;
}
CallState childExpectedCallState = new CallState(true);
CallState parentExpectedCallState = new CallState(true);
@Override
public void postDestroyView(@NonNull Controller controller) {
mDestroyViewCalls++;
}
// Ensure that calling handleRequestedPermission w/o requesting a result doesn't do anything
mRouter.onRequestPermissionsResult("anotherId", 1, requestedPermissions, new int[] {1});
assertCalls(childExpectedCallState, child);
assertCalls(parentExpectedCallState, parent);
@Override
public void postDetach(@NonNull Controller controller, @NonNull View view) {
mDetachCalls++;
}
// Ensure requesting the permission gets us the result back
try {
child.requestPermissions(requestedPermissions, 1);
} catch (NoSuchMethodError ignored) { }
@Override
public void postDestroy(@NonNull Controller controller) {
mDestroyCalls++;
}
});
mRouter.onRequestPermissionsResult(child.getInstanceId(), 1, requestedPermissions, new int[] {1});
childExpectedCallState.onRequestPermissionsResultCalls++;
assertCalls(childExpectedCallState, child);
assertCalls(parentExpectedCallState, parent);
}
interface ChangeHandlerListener {
void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener);
@Test
public void testOptionsMenu() {
TestController controller = new TestController();
CallState expectedCallState = new CallState(true);
mRouter.pushController(RouterTransaction.builder(controller).build());
ViewUtils.setAttached(controller.getView(), true);
// Ensure that calling onCreateOptionsMenu w/o declaring that we have one doesn't do anything
mRouter.onCreateOptionsMenu(null, null);
assertCalls(expectedCallState, controller);
// Ensure calling onCreateOptionsMenu with a menu works
controller.setHasOptionsMenu(true);
// Ensure it'll still get called back next time onCreateOptionsMenu is called
mRouter.onCreateOptionsMenu(null, null);
expectedCallState.createOptionsMenuCalls++;
assertCalls(expectedCallState, controller);
// Ensure we stop getting them when we hide it
controller.setOptionsMenuHidden(true);
mRouter.onCreateOptionsMenu(null, null);
assertCalls(expectedCallState, controller);
// Ensure we get the callback them when we un-hide it
controller.setOptionsMenuHidden(false);
mRouter.onCreateOptionsMenu(null, null);
expectedCallState.createOptionsMenuCalls++;
assertCalls(expectedCallState, controller);
// Ensure we don't get the callback when we no longer have a menu
controller.setHasOptionsMenu(false);
mRouter.onCreateOptionsMenu(null, null);
assertCalls(expectedCallState, controller);
}
public static class ChangeHandler extends ControllerChangeHandler {
@Test
public void testOptionsMenuForChild() {
TestController parent = new TestController();
TestController child = new TestController();
private ChangeHandlerListener mListener;
mRouter.pushController(RouterTransaction.builder(parent).build());
ViewUtils.setAttached(parent.getView(), true);
parent.addChildController(ChildControllerTransaction.builder(child, TestController.VIEW_ID).build());
ViewUtils.setAttached(child.getView(), true);
public ChangeHandler() { }
CallState childExpectedCallState = new CallState(true);
CallState parentExpectedCallState = new CallState(true);
public ChangeHandler(ChangeHandlerListener listener) {
mListener = listener;
}
// Ensure that calling onCreateOptionsMenu w/o declaring that we have one doesn't do anything
mRouter.onCreateOptionsMenu(null, null);
assertCalls(childExpectedCallState, child);
assertCalls(parentExpectedCallState, parent);
@Override
public void performChange(@NonNull ViewGroup container, View from, View to, boolean isPush, @NonNull ControllerChangeCompletedListener changeListener) {
mListener.performChange(container, from, to, isPush, changeListener);
}
// Ensure calling onCreateOptionsMenu with a menu works
child.setHasOptionsMenu(true);
// Ensure it'll still get called back next time onCreateOptionsMenu is called
mRouter.onCreateOptionsMenu(null, null);
childExpectedCallState.createOptionsMenuCalls++;
assertCalls(childExpectedCallState, child);
assertCalls(parentExpectedCallState, parent);
// Ensure we stop getting them when we hide it
child.setOptionsMenuHidden(true);
mRouter.onCreateOptionsMenu(null, null);
assertCalls(childExpectedCallState, child);
assertCalls(parentExpectedCallState, parent);
// Ensure we get the callback them when we un-hide it
child.setOptionsMenuHidden(false);
mRouter.onCreateOptionsMenu(null, null);
childExpectedCallState.createOptionsMenuCalls++;
assertCalls(childExpectedCallState, child);
assertCalls(parentExpectedCallState, parent);
// Ensure we don't get the callback when we no longer have a menu
child.setHasOptionsMenu(false);
mRouter.onCreateOptionsMenu(null, null);
assertCalls(childExpectedCallState, child);
assertCalls(parentExpectedCallState, parent);
}
private void assertCalls(CallState callState, TestController controller) {
Assert.assertEquals("Expected call counts and controller call counts do not match.", callState, controller.currentCallState);
}
}
@@ -3,4 +3,12 @@ package com.bluelinelabs.conductor;
import android.app.Activity;
public class TestActivity extends Activity {
public boolean isChangingConfigurations = false;
@Override
public boolean isChangingConfigurations() {
return isChangingConfigurations;
}
}
@@ -1,24 +1,124 @@
package com.bluelinelabs.conductor;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.bluelinelabs.conductor.ControllerTransaction.ControllerChangeType;
public class TestController extends Controller {
@IdRes public static final int VIEW_ID = 2342;
public TestController() { }
private static final String KEY_CALL_STATE = "TestController.currentCallState";
public CallState currentCallState;
public TestController() {
currentCallState = new CallState();
}
@NonNull
@Override
protected View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
currentCallState.createViewCalls++;
View view = new FrameLayout(inflater.getContext());
view.setId(VIEW_ID);
return view;
}
@Override
protected void onChangeStarted(@NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
super.onChangeStarted(changeHandler, changeType);
currentCallState.changeStartCalls++;
}
@Override
protected void onChangeEnded(@NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
super.onChangeEnded(changeHandler, changeType);
currentCallState.changeEndCalls++;
}
@Override
protected void onAttach(@NonNull View view) {
super.onAttach(view);
currentCallState.attachCalls++;
}
@Override
protected void onDetach(@NonNull View view) {
super.onDetach(view);
currentCallState.detachCalls++;
}
@Override
protected void onDestroyView(View view) {
super.onDestroyView(view);
currentCallState.destroyViewCalls++;
}
@Override
protected void onDestroy() {
super.onDestroy();
currentCallState.destroyCalls++;
}
@Override
protected void onSaveViewState(@NonNull View view, @NonNull Bundle outState) {
super.onSaveViewState(view, outState);
currentCallState.saveViewStateCalls++;
}
@Override
protected void onRestoreViewState(@NonNull View view, @NonNull Bundle savedViewState) {
super.onRestoreViewState(view, savedViewState);
currentCallState.restoreViewStateCalls++;
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
currentCallState.saveInstanceStateCalls++;
outState.putParcelable(KEY_CALL_STATE, currentCallState);
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
currentCallState = savedInstanceState.getParcelable(KEY_CALL_STATE);
currentCallState.restoreInstanceStateCalls++;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
currentCallState.onActivityResultCalls++;
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
currentCallState.onRequestPermissionsResultCalls++;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
currentCallState.createOptionsMenuCalls++;
}
}
+6 -6
View File
@@ -14,6 +14,10 @@ android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
lintOptions {
abortOnError false
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
@@ -41,12 +45,8 @@ dependencies {
compile rootProject.ext.butterknife
compile 'com.bluelinelabs:conductor:' + rootProject.ext.publishedVersionName
compile 'com.bluelinelabs:conductor-support:' + rootProject.ext.publishedVersionName
compile 'com.bluelinelabs:conductor-rxlifecycle:' + rootProject.ext.publishedVersionName
// compile project(':conductor-support')
// compile project(':conductor-rxlifecycle')
compile project(':conductor-support')
compile project(':conductor-rxlifecycle')
debugCompile rootProject.ext.leakCanary
releaseCompile rootProject.ext.leakCanaryNoOp
@@ -24,7 +24,7 @@ public class PagerController extends BaseController {
@Bind(R.id.tab_layout) TabLayout mTabLayout;
@Bind(R.id.view_pager) ViewPager mViewPager;
private ControllerPagerAdapter mPagerAdapter;
private final ControllerPagerAdapter mPagerAdapter;
public PagerController() {
mPagerAdapter = new ControllerPagerAdapter(this) {
@@ -66,6 +66,12 @@ public class PagerController extends BaseController {
});
}
@Override
protected void onDestroyView(View view) {
mViewPager.setAdapter(null);
super.onDestroyView(view);
}
@NonNull
@Override
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
@@ -16,10 +16,10 @@
<ImageView
android:id="@+id/image_view"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_width="@dimen/display_target_image_size"
android:layout_height="@dimen/display_target_image_size"
android:layout_gravity="center_horizontal"
android:layout_margin="24dp"
android:layout_margin="@dimen/display_target_image_margin"
/>
<LinearLayout
+8
View File
@@ -0,0 +1,8 @@
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">24dp</dimen>
<dimen name="activity_vertical_margin">24dp</dimen>
<dimen name="display_target_image_size">120dp</dimen>
<dimen name="display_target_image_margin">16dp</dimen>
</resources>
+3
View File
@@ -2,4 +2,7 @@
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="display_target_image_size">200dp</dimen>
<dimen name="display_target_image_margin">24dp</dimen>
</resources>
+1 -5
View File
@@ -4,10 +4,6 @@ ext {
targetSdkVersion = 23
buildToolsVersion = '23.0.2'
versionCode = 1
versionName = '1.1.3'
publishedVersionName = '1.1.3'
supportV4 = 'com.android.support:support-v4:23.1.1'
supportDesign = 'com.android.support:design:23.1.1'
supportAnnotations = 'com.android.support:support-annotations:23.1.1'
@@ -20,7 +16,7 @@ ext {
rxJava = 'io.reactivex:rxjava:1.1.0'
rxAndroid = 'io.reactivex:rxandroid:1.1.0'
rxLifecycle = 'com.trello:rxlifecycle:0.5.0'
rxLifecycle = 'com.trello:rxlifecycle:0.6.0'
junit = 'junit:junit:4.11'
roboelectric = 'org.robolectric:robolectric:3.0'
+124
View File
@@ -0,0 +1,124 @@
/*
* Copyright 2013 Chris Banes
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
apply plugin: 'maven'
apply plugin: 'signing'
def isReleaseBuild() {
return VERSION_NAME.contains("SNAPSHOT") == false
}
def getReleaseRepositoryUrl() {
return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL :
"https://oss.sonatype.org/service/local/staging/deploy/maven2/"
}
def getSnapshotRepositoryUrl() {
return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL :
"https://oss.sonatype.org/content/repositories/snapshots/"
}
def getRepositoryUsername() {
return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : ""
}
def getRepositoryPassword() {
return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : ""
}
afterEvaluate { project ->
uploadArchives {
repositories {
mavenDeployer {
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
pom.groupId = GROUP
pom.artifactId = POM_ARTIFACT_ID
pom.version = VERSION_NAME
repository(url: getReleaseRepositoryUrl()) {
authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
}
snapshotRepository(url: getSnapshotRepositoryUrl()) {
authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
}
pom.project {
name POM_NAME
packaging POM_PACKAGING
description POM_DESCRIPTION
url POM_URL
scm {
url POM_SCM_URL
connection POM_SCM_CONNECTION
developerConnection POM_SCM_DEV_CONNECTION
}
licenses {
license {
name POM_LICENCE_NAME
url POM_LICENCE_URL
distribution POM_LICENCE_DIST
}
}
developers {
developer {
id POM_DEVELOPER_ID
name POM_DEVELOPER_NAME
}
}
}
// Resolve dependencies to other modules
pom.whenConfigured { pom ->
pom.dependencies.findAll { dep -> dep.groupId == rootProject.name }.collect { dep ->
dep.groupId = pom.groupId = project.GROUP
dep.version = pom.version = project.VERSION_NAME
}
}
}
}
}
signing {
required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") }
sign configurations.archives
}
task androidJavadocs(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
failOnError = false
}
task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
classifier = 'javadoc'
from androidJavadocs.destinationDir
}
task androidSourcesJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.sourceFiles
}
artifacts {
archives androidSourcesJar
archives androidJavadocsJar
}
}
+14
View File
@@ -0,0 +1,14 @@
VERSION_NAME=1.1.7-SNAPSHOT
VERSION_CODE=2
GROUP=com.bluelinelabs
POM_DESCRIPTION=A small, yet full-featured framework that allows building View-based Android applications
POM_URL=https://github.com/bluelinelabs/Conductor
POM_SCM_URL=https://github.com/bluelinelabs/Conductor
POM_SCM_CONNECTION=scm:git:https://github.com/bluelinelabs/Conductor
POM_SCM_DEV_CONNECTION=scm:git:https://github.com/bluelinelabs/Conductor
POM_LICENCE_NAME=The Apache Software License, Version 2.0
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
POM_LICENCE_DIST=repo
POM_DEVELOPER_ID=erickuck
POM_DEVELOPER_NAME=Eric Kuck