Compare commits

..

19 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
24 changed files with 1186 additions and 510 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.4'
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.4'
compile 'com.bluelinelabs:conductor-support:1.1.6'
// If you want RxJava/RxAndroid lifecycle support:
compile 'com.bluelinelabs:conductor-rxlifecycle:1.1.4'
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";
@@ -71,6 +71,7 @@ public abstract class Controller {
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<>();
@@ -256,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)) {
@@ -269,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
*/
@@ -792,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();
}
@@ -817,6 +837,7 @@ public abstract class Controller {
onDestroyView(mView);
mView.removeOnAttachStateChangeListener(mOnAttachStateChangeListener);
mView = null;
for (LifecycleListener lifecycleListener : mLifecycleListeners) {
@@ -830,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);
@@ -837,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) {
@@ -853,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;
@@ -1032,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;
}
@@ -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;
}
@@ -16,6 +16,22 @@ public class CallState implements Parcelable {
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) {
@@ -58,6 +74,15 @@ public class CallState implements Parcelable {
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;
}
@@ -74,6 +99,9 @@ public class CallState implements Parcelable {
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;
}
@@ -91,6 +119,9 @@ public class CallState implements Parcelable {
"\n restoreInstanceStateCalls=" + restoreInstanceStateCalls +
"\n saveViewStateCalls=" + saveViewStateCalls +
"\n restoreViewStateCalls=" + restoreViewStateCalls +
"\n onActivityResultCalls= " + onActivityResultCalls +
"\n onRequestPermissionsResultCalls= " + onRequestPermissionsResultCalls +
"\n createOptionsMenuCalls= " + createOptionsMenuCalls +
"}\n";
}
@@ -110,6 +141,9 @@ public class CallState implements Parcelable {
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>() {
@@ -127,6 +161,9 @@ public class CallState implements Parcelable {
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;
}
@@ -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,17 +1,13 @@
package com.bluelinelabs.conductor;
import android.app.Application;
import android.app.Activity;
import android.content.Intent;
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.Controller.RetainViewMode;
import com.bluelinelabs.conductor.ControllerChangeHandler.ControllerChangeCompletedListener;
import com.bluelinelabs.conductor.ControllerTransaction.ControllerChangeType;
import org.junit.Assert;
import org.junit.Before;
@@ -26,14 +22,11 @@ import org.robolectric.util.ActivityController;
@Config(manifest = Config.NONE)
public class ControllerTests {
private Application mApplication;
private ActivityController<TestActivity> mActivityController;
private Router mRouter;
private CallState mCurrentCallState;
public void createActivityController(Bundle savedInstanceState) {
mActivityController = Robolectric.buildActivity(TestActivity.class).create(savedInstanceState).withApplication(mApplication).start();
mActivityController = Robolectric.buildActivity(TestActivity.class).create(savedInstanceState).start();
@IdRes int containerId = 4;
FrameLayout routerContainer = new FrameLayout(mActivityController.get());
@@ -47,233 +40,7 @@ public class ControllerTests {
@Before
public void setup() {
mApplication = new Application();
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 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);
}
@Test
@@ -304,132 +71,205 @@ public class ControllerTests {
Assert.assertNull(controller.getView());
}
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);
@Test
public void testActivityResult() {
TestController controller = new TestController();
CallState expectedCallState = new CallState(true);
container.addView(to);
ViewUtils.setAttached(to, true);
mRouter.pushController(RouterTransaction.builder(controller).build());
ViewUtils.setAttached(controller.getView(), true);
expectedCallState.attachCalls++;
assertCalls(expectedCallState, controller);
// Ensure that calling onActivityResult w/o requesting a result doesn't do anything
mRouter.onActivityResult(1, Activity.RESULT_OK, null);
assertCalls(expectedCallState, controller);
changeListener.onChangeCompleted();
// 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);
expectedCallState.changeEndCalls++;
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 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);
@Test
public void testActivityResultForChild() {
TestController parent = new TestController();
TestController child = new TestController();
expectedCallState.destroyViewCalls++;
expectedCallState.detachCalls++;
expectedCallState.destroyCalls++;
assertCalls(expectedCallState, controller);
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);
changeListener.onChangeCompleted();
CallState childExpectedCallState = new CallState(true);
CallState parentExpectedCallState = new CallState(true);
expectedCallState.changeEndCalls++;
assertCalls(expectedCallState, controller);
}
});
// 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);
}
@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);
}
@Test
public void testPermissionResultForChild() {
final String[] requestedPermissions = new String[] {"test"};
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 handleRequestedPermission w/o requesting a result doesn't do anything
mRouter.onRequestPermissionsResult("anotherId", 1, requestedPermissions, new int[] {1});
assertCalls(childExpectedCallState, child);
assertCalls(parentExpectedCallState, parent);
// Ensure requesting the permission gets us the result back
try {
child.requestPermissions(requestedPermissions, 1);
} catch (NoSuchMethodError ignored) { }
mRouter.onRequestPermissionsResult(child.getInstanceId(), 1, requestedPermissions, new int[] {1});
childExpectedCallState.onRequestPermissionsResultCalls++;
assertCalls(childExpectedCallState, child);
assertCalls(parentExpectedCallState, parent);
}
@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);
}
@Test
public void testOptionsMenuForChild() {
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 onCreateOptionsMenu w/o declaring that we have one doesn't do anything
mRouter.onCreateOptionsMenu(null, null);
assertCalls(childExpectedCallState, child);
assertCalls(parentExpectedCallState, parent);
// 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);
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,9 +1,12 @@
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;
@@ -96,4 +99,26 @@ public class TestController extends Controller {
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) {
+1 -5
View File
@@ -4,10 +4,6 @@ ext {
targetSdkVersion = 23
buildToolsVersion = '23.0.2'
versionCode = 1
versionName = '1.1.4'
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