Skip to content

Commit

Permalink
Merging 3.1.0 into master
Browse files Browse the repository at this point in the history
  • Loading branch information
sockeqwe committed Nov 28, 2017
2 parents 5eea068 + fcfefec commit 52dfe22
Show file tree
Hide file tree
Showing 89 changed files with 2,671 additions and 876 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ android:
components:
- tools
- platform-tools
- build-tools-26.0.0
- build-tools-26.0.1
- extra-android-m2repository
- android-25
- sys-img-armeabi-v7a-android-17
Expand Down
8 changes: 4 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ buildscript {
}
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
classpath 'com.android.tools.build:gradle:3.0.0'
// classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.5.2'
classpath 'me.tatarka:gradle-retrolambda:3.5.0'
//classpath 'me.tatarka:gradle-retrolambda:3.5.0'
}
}

Expand Down Expand Up @@ -40,8 +40,8 @@ allprojects {
ext {
minSdk = 14
targetSdk = 25
buildToolsVersion = '26.0.0'
compileSdkVersion = 25
buildToolsVersion = '26.0.2'
compileSdkVersion = 26

javaSourceCompatibility = JavaVersion.VERSION_1_7
javaTargetCompatibility = JavaVersion.VERSION_1_7
Expand Down
8 changes: 5 additions & 3 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryErro
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true

VERSION_NAME=3.0.5-SNAPSHOT
VERSION_CODE=305
VERSION_NAME=3.1.0-SNAPSHOT
VERSION_CODE=310
GROUP=com.hannesdorfmann.mosby3

POM_DESCRIPTION=A Model-View-Presenter library for Android apps
Expand All @@ -31,4 +31,6 @@ 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=hannesdorfmann
POM_DEVELOPER_NAME=Hannes Dorfmann
POM_DEVELOPER_NAME=Hannes Dorfmann

android.enableAapt2=false
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.2.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.2.1-bin.zip
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@
* viewState is a object (typically a POJO) that holds all the data the view needs to display</li>
* </ul>
*
* By using {@link #intent(ViewIntentBinder)} and {@link #subscribeViewState(Observable, * ViewStateConsumer)}
* By using {@link #intent(ViewIntentBinder)} and {@link #subscribeViewState(Observable, *
* ViewStateConsumer)}
* a relay will be established between the view and this presenter that allows the view to be
* temporarily detached, without unsubscribing the underlying reactive business logic workflow and
* without causing memory leaks (caused by recreation of the view).
Expand Down Expand Up @@ -117,7 +118,8 @@ protected interface ViewIntentBinder<V extends MvpView, I> {
* This "binder" is responsible to bind the view state to the currently attached view.
* This typically "renders" the view.
*
* Typically this is used in {@link #bindIntents()} with {@link MviBasePresenter#subscribeViewState(Observable, * ViewStateConsumer)}
* Typically this is used in {@link #bindIntents()} with {@link MviBasePresenter#subscribeViewState(Observable,
* * ViewStateConsumer)}
* like this:
* <pre><code>
* Observable<MyViewState> viewState = ... ;
Expand Down Expand Up @@ -265,23 +267,11 @@ protected Observable<VS> getViewStateObservable() {
bindIntentActually(view, intentRelayBinderPair);
}


viewAttachedFirstTime = false;
}

@Override @CallSuper public void detachView(boolean retainInstance) {
if (!retainInstance) {
if (viewStateDisposable != null) {
// Cancel the overall observable stream
viewStateDisposable.dispose();
}

unbindIntents();
reset();
// TODO should we re emit the inital state? What if no initial state has been set.
// TODO should we rather throw an exception if presenter is reused after view has been detached permanently
}

@Override @CallSuper public void detachView() {
detachView(true);
if (viewRelayConsumerDisposable != null) {
// Cancel subscription from View to viewState Relay
viewRelayConsumerDisposable.dispose();
Expand All @@ -295,6 +285,26 @@ protected Observable<VS> getViewStateObservable() {
}
}

@Override @CallSuper public void destroy() {
detachView(false);
if (viewStateDisposable != null) {
// Cancel the overall observable stream
viewStateDisposable.dispose();
}

unbindIntents();
reset();
// TODO should we re emit the inital state? What if no initial state has been set.
// TODO should we rather throw an exception if presenter is reused after view has been detached permanently

}

/**
* {@inheritDoc}
*/
@Deprecated @Override @CallSuper public void detachView(boolean retainInstance) {
}

/**
* This is called when the View has been detached permantently (view is destroyed permanently)
* to reset the internal state of this Presenter to be ready for being reused (even thought
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,12 @@ public class MviBasePresenterTest {
};

presenter.attachView(view);
presenter.detachView(true);
presenter.detachView();
presenter.attachView(view);
presenter.detachView(true);
presenter.detachView();
presenter.attachView(view);
presenter.detachView(false);
presenter.detachView();
presenter.destroy();

Assert.assertEquals(1, bindInvocations.get());
Assert.assertEquals(1, unbindInvocations.get());
Expand Down Expand Up @@ -108,7 +109,7 @@ public Observable<String> bind(@NonNull KeepUndelyingSubscriptionsView view) {
Assert.assertEquals(Arrays.asList("1 Intent", "2 Intent"), intentsData);

// Detach View temporarily
presenter.detachView(true);
presenter.detachView();
Assert.assertFalse(view.anIntent.hasObservers());
businessLogic.onNext("3 bl");
Assert.assertEquals(Arrays.asList("1 bl", "2 bl"), view.renderedModels);
Expand All @@ -126,7 +127,8 @@ public Observable<String> bind(@NonNull KeepUndelyingSubscriptionsView view) {
Assert.assertEquals(Arrays.asList("1 bl", "2 bl", "4 bl", "5 bl"), view.renderedModels);

// Detach View permanently
presenter.detachView(false);
presenter.detachView();
presenter.destroy();
Assert.assertFalse(businessLogic.hasObservers()); // No more observers
Assert.assertFalse(view.anIntent.hasObservers()); // No more observers
view.anIntent.onNext("This will never be delivered to presenter");
Expand Down Expand Up @@ -154,11 +156,13 @@ public Observable<String> bind(@NonNull KeepUndelyingSubscriptionsView view) {
};

presenter.attachView(view);
presenter.detachView(false);
presenter.detachView();
presenter.destroy();
presenter.attachView(view);
presenter.detachView(true);
presenter.detachView();
presenter.attachView(view);
presenter.detachView(false);
presenter.detachView();
presenter.destroy();

Assert.assertEquals(2, bindInvocations.get());
Assert.assertEquals(2, unbindInvocations.get());
Expand Down
2 changes: 2 additions & 0 deletions mvi-integration-test/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ dependencies {
androidTestCompile 'com.android.support.test:runner:' + rootProject.ext.androidSupportTestVersion
androidTestCompile 'com.android.support.test:rules:' + rootProject.ext.androidSupportTestVersion
androidTestCompile 'junit:junit:' + rootProject.ext.junitVersion
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile 'com.android.support:design:25.3.1'
}

configurations.all {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright 2017 Hannes Dorfmann.
*
* 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.
*
*/

package com.hannesdorfmann.mosby3.mvi.backstack;

import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import com.hannesdorfmann.mosby3.FragmentMviDelegateImpl;
import com.hannesdorfmann.mosby3.mvi.integrationtest.backstack.BackstackActivity;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(AndroidJUnit4.class) public class BackstackActivityTest {

@Rule public ActivityTestRule<BackstackActivity> rule =
new ActivityTestRule<>(BackstackActivity.class);

@Test public void testConfigChange() throws Exception {

FragmentMviDelegateImpl.DEBUG = true;


Thread.sleep(1000);
Assert.assertEquals(1, BackstackActivity.createFirstPresenterCalls.get());
Assert.assertEquals(1, BackstackActivity.firstPresenter.attachViewCalls.get());
Assert.assertEquals(1, BackstackActivity.firstPresenter.bindIntentCalls.get());

//
// Screen orientation change
//
BackstackActivity.rotateToLandscape();
Thread.sleep(1000);

Assert.assertEquals(1, BackstackActivity.firstPresenter.detachViewCalls.get());
Assert.assertEquals(0, BackstackActivity.firstPresenter.unbindIntentCalls.get());
Assert.assertEquals(1, BackstackActivity.createFirstPresenterCalls.get());
Assert.assertEquals(2, BackstackActivity.firstPresenter.attachViewCalls.get());
Assert.assertEquals(1, BackstackActivity.firstPresenter.bindIntentCalls.get());

//
// Navigate to next fragment
//
Thread.sleep(1000);
BackstackActivity.navigateToSecondFragment();
Thread.sleep(1000);

Assert.assertEquals(2, BackstackActivity.firstPresenter.detachViewCalls.get());
Assert.assertEquals(0, BackstackActivity.firstPresenter.unbindIntentCalls.get());
Assert.assertEquals(1, BackstackActivity.createFirstPresenterCalls.get());
Assert.assertEquals(2, BackstackActivity.firstPresenter.attachViewCalls.get());
Assert.assertEquals(1, BackstackActivity.firstPresenter.bindIntentCalls.get());

//
// Check Second Fragment
//
Assert.assertEquals(1, BackstackActivity.createSecondPresenterCalls.get());
Assert.assertEquals(1, BackstackActivity.secondPresenter.attachViewCalls.get());
Assert.assertEquals(1, BackstackActivity.secondPresenter.bindIntentCalls.get());

//
// Screen orientation change
//
BackstackActivity.rotateToPortrait();
Thread.sleep(1000);

Assert.assertEquals(1, BackstackActivity.secondPresenter.detachViewCalls.get());
Assert.assertEquals(0, BackstackActivity.secondPresenter.unbindIntentCalls.get());
Assert.assertEquals(1, BackstackActivity.createSecondPresenterCalls.get());
Assert.assertEquals(2, BackstackActivity.secondPresenter.attachViewCalls.get());
Assert.assertEquals(1, BackstackActivity.secondPresenter.bindIntentCalls.get());

//
// Press back button --> Finish second fragment
//
BackstackActivity.pressBackButton();
Thread.sleep(1000);
Assert.assertEquals(2, BackstackActivity.secondPresenter.detachViewCalls.get());
Assert.assertEquals(1, BackstackActivity.secondPresenter.unbindIntentCalls.get());

//
// First Fragment restored from backstack
//
Assert.assertEquals(1, BackstackActivity.createFirstPresenterCalls.get());
Assert.assertEquals(3, BackstackActivity.firstPresenter.attachViewCalls.get());
Assert.assertEquals(1, BackstackActivity.firstPresenter.bindIntentCalls.get());

// Press back button --> finishes the activity
BackstackActivity.pressBackButton();
Thread.sleep(1000);

}

@AfterClass public static void afterActivityFinished() {
Assert.assertEquals(3, BackstackActivity.firstPresenter.detachViewCalls.get());
Assert.assertEquals(1, BackstackActivity.firstPresenter.unbindIntentCalls.get());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import android.content.pm.ActivityInfo;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.util.Log;
import com.hannesdorfmann.mosby3.ViewGroupMviDelegateImpl;
import com.hannesdorfmann.mosby3.mvi.integrationtest.lifecycle.LifecycleTestPresenter;
import org.junit.AfterClass;
import org.junit.Assert;
Expand All @@ -43,6 +45,7 @@
Assert.assertEquals(1, portraitActivity.createPresenterInvokations);
Assert.assertEquals(1, presenter.attachViewInvokations);
Assert.assertTrue(presenter.attachedView == portraitActivity);
Assert.assertEquals(1, presenter.bindIntentInvocations);

Thread.sleep(1000);

Expand All @@ -53,18 +56,23 @@
Thread.sleep(1000);

Assert.assertEquals(1, presenter.detachViewInvokations);
Assert.assertTrue(presenter.onDettachViewRetainInstance);

Assert.assertEquals(1, presenter.bindIntentInvocations);
Assert.assertEquals(0, presenter.unbindIntentInvocations);
Assert.assertEquals(0, presenter.destoryInvoations);
Assert.assertEquals(1, MviLifecycleActivity.createPresenterInvokations);
Assert.assertEquals(2, presenter.attachViewInvokations);
Assert.assertTrue(presenter.attachedView != portraitActivity);
Assert.assertTrue(presenter.attachedView != portraitActivity && presenter.attachedView != null);

// Press back button --> Finishes Activity
MviLifecycleActivity.pressBackButton();
Thread.sleep(1000);
}

@AfterClass public static void checkPresenterNotRetained() {

// TODO is there a better way to test after onDestroy() has been called?
Assert.assertNotNull(presenter);
Assert.assertEquals(2, presenter.detachViewInvokations);
Assert.assertFalse(presenter.onDettachViewRetainInstance);
Assert.assertEquals(1, presenter.unbindIntentInvocations);
Assert.assertEquals(1, presenter.destoryInvoations);
}
}
Loading

0 comments on commit 52dfe22

Please sign in to comment.