diff --git a/README.md b/README.md index ccba5115..9bbb67b8 100644 --- a/README.md +++ b/README.md @@ -76,36 +76,36 @@ Base modules integration: ```groovy dependencies { ... - compile 'com.arello-mobile:moxy:1.4.4' - provided 'com.arello-mobile:moxy-compiler:1.4.4' + compile 'com.arello-mobile:moxy:1.4.5' + provided 'com.arello-mobile:moxy-compiler:1.4.5' } ``` If you want to see generated code, use `apt` instead of `provided` dependency type: ```groovy dependencies { ... - apt 'com.arello-mobile:moxy-compiler:1.4.4' + apt 'com.arello-mobile:moxy-compiler:1.4.5' } ``` Note: if you use gradle plugin verion 2.2.2 and above, so you can use `annotationProcessor` instead of `apt`: ```groovy dependencies { ... - annotationProcessor 'com.arello-mobile:moxy-compiler:1.4.4' + annotationProcessor 'com.arello-mobile:moxy-compiler:1.4.5' } ``` For additional base view classes `MvpActivity` and `MvpFragment` add this: ```groovy dependencies { ... - compile 'com.arello-mobile:moxy-android:1.4.4' + compile 'com.arello-mobile:moxy-android:1.4.5' } ``` If you are planing to use AppCompat, then you can use `MvpAppCompatActivity` and `MvpAppCompatFragment`. Then add this: ```groovy dependencies { ... - compile 'com.arello-mobile:moxy-app-compat:1.4.4' + compile 'com.arello-mobile:moxy-app-compat:1.4.5' compile 'com.android.support:appcompat-v7:$support_version' } ``` @@ -114,7 +114,7 @@ If you are using kotlin, use `kapt` instead of `provided`/`apt` dependency type ```groovy dependencies { ... - kapt 'com.arello-mobile:moxy-compiler:1.4.4' + kapt 'com.arello-mobile:moxy-compiler:1.4.5' } kapt { generateStubs = true diff --git a/build.gradle b/build.gradle index 23c94a7e..aa5a2b95 100644 --- a/build.gradle +++ b/build.gradle @@ -19,8 +19,8 @@ allprojects { } ext { - targetVersionCode = 37 - targetVersionName = "1.4.4" + targetVersionCode = 38 + targetVersionName = "1.4.5" } task clean(type: Delete) { diff --git a/moxy-android/src/main/java/com/arellomobile/mvp/MvpActivity.java b/moxy-android/src/main/java/com/arellomobile/mvp/MvpActivity.java index 86368a70..8dc9516c 100644 --- a/moxy-android/src/main/java/com/arellomobile/mvp/MvpActivity.java +++ b/moxy-android/src/main/java/com/arellomobile/mvp/MvpActivity.java @@ -21,6 +21,13 @@ protected void onCreate(Bundle savedInstanceState) { getMvpDelegate().onCreate(savedInstanceState); } + @Override + protected void onStart() { + super.onStart(); + + getMvpDelegate().onAttach(); + } + @Override protected void onResume() { super.onResume(); diff --git a/moxy-android/src/main/java/com/arellomobile/mvp/MvpFragment.java b/moxy-android/src/main/java/com/arellomobile/mvp/MvpFragment.java index 6ef71e70..cd8cc3fe 100644 --- a/moxy-android/src/main/java/com/arellomobile/mvp/MvpFragment.java +++ b/moxy-android/src/main/java/com/arellomobile/mvp/MvpFragment.java @@ -25,6 +25,15 @@ public void onCreate(Bundle savedInstanceState) { getMvpDelegate().onCreate(savedInstanceState); } + @Override + public void onStart() { + super.onStart(); + + mIsStateSaved = false; + + getMvpDelegate().onAttach(); + } + public void onResume() { super.onResume(); diff --git a/moxy-android/stub-android/src/main/java/android/app/Fragment.java b/moxy-android/stub-android/src/main/java/android/app/Fragment.java index cd4612ba..cdc32bf6 100644 --- a/moxy-android/stub-android/src/main/java/android/app/Fragment.java +++ b/moxy-android/stub-android/src/main/java/android/app/Fragment.java @@ -12,6 +12,10 @@ public void onCreate(Bundle savedInstanceState) { throw new RuntimeException("Stub!"); } + public void onStart() { + throw new RuntimeException("Stub!"); + } + public void onResume() { throw new RuntimeException("Stub!"); } diff --git a/moxy-app-compat/src/main/java/com/arellomobile/mvp/MvpAppCompatActivity.java b/moxy-app-compat/src/main/java/com/arellomobile/mvp/MvpAppCompatActivity.java index c9efd7d2..ff8d7408 100644 --- a/moxy-app-compat/src/main/java/com/arellomobile/mvp/MvpAppCompatActivity.java +++ b/moxy-app-compat/src/main/java/com/arellomobile/mvp/MvpAppCompatActivity.java @@ -22,6 +22,13 @@ protected void onCreate(Bundle savedInstanceState) { getMvpDelegate().onCreate(savedInstanceState); } + @Override + protected void onStart() { + super.onStart(); + + getMvpDelegate().onAttach(); + } + @Override protected void onResume() { super.onResume(); diff --git a/moxy-app-compat/src/main/java/com/arellomobile/mvp/MvpAppCompatFragment.java b/moxy-app-compat/src/main/java/com/arellomobile/mvp/MvpAppCompatFragment.java index df61bfe1..baf7ea78 100644 --- a/moxy-app-compat/src/main/java/com/arellomobile/mvp/MvpAppCompatFragment.java +++ b/moxy-app-compat/src/main/java/com/arellomobile/mvp/MvpAppCompatFragment.java @@ -23,6 +23,15 @@ public void onCreate(Bundle savedInstanceState) { getMvpDelegate().onCreate(savedInstanceState); } + @Override + public void onStart() { + super.onStart(); + + mIsStateSaved = false; + + getMvpDelegate().onAttach(); + } + public void onResume() { super.onResume(); diff --git a/moxy-app-compat/stub-appcompat/src/main/java/android/support/v4/app/Fragment.java b/moxy-app-compat/stub-appcompat/src/main/java/android/support/v4/app/Fragment.java index 55fd5a95..d998d564 100644 --- a/moxy-app-compat/stub-appcompat/src/main/java/android/support/v4/app/Fragment.java +++ b/moxy-app-compat/stub-appcompat/src/main/java/android/support/v4/app/Fragment.java @@ -13,6 +13,10 @@ public void onCreate(Bundle savedInstanceState) { throw new RuntimeException("Stub!"); } + public void onStart() { + throw new RuntimeException("Stub!"); + } + public void onResume() { throw new RuntimeException("Stub!"); } diff --git a/moxy-app-compat/stub-appcompat/src/main/java/android/support/v7/app/AppCompatActivity.java b/moxy-app-compat/stub-appcompat/src/main/java/android/support/v7/app/AppCompatActivity.java index 4042450c..e437006a 100644 --- a/moxy-app-compat/stub-appcompat/src/main/java/android/support/v7/app/AppCompatActivity.java +++ b/moxy-app-compat/stub-appcompat/src/main/java/android/support/v7/app/AppCompatActivity.java @@ -13,6 +13,10 @@ protected void onCreate(Bundle savedInstanceState) { throw new RuntimeException("Stub!"); } + protected void onStart() { + throw new RuntimeException("Stub!"); + } + protected void onResume() { throw new RuntimeException("Stub!"); } diff --git a/moxy/src/main/java/com/arellomobile/mvp/MvpDelegate.java b/moxy/src/main/java/com/arellomobile/mvp/MvpDelegate.java index 52bf5c64..864b46c5 100644 --- a/moxy/src/main/java/com/arellomobile/mvp/MvpDelegate.java +++ b/moxy/src/main/java/com/arellomobile/mvp/MvpDelegate.java @@ -133,6 +133,10 @@ public void onAttach() { */ public void onDetach() { for (MvpPresenter presenter : mPresenters) { + if (!mIsAttached && !presenter.getAttachedViews().contains(mDelegated)) { + continue; + } + presenter.detachView(mDelegated); } diff --git a/sample-github/build.gradle b/sample-github/build.gradle index 7869f08a..88998c6c 100644 --- a/sample-github/build.gradle +++ b/sample-github/build.gradle @@ -68,7 +68,7 @@ dependencies { testCompile "org.hamcrest:hamcrest-all:1.3" testCompile "org.robolectric:robolectric:3.1-rc1" - compile 'com.arello-mobile:moxy:1.4.4' - compile 'com.arello-mobile:moxy-app-compat:1.4.4' - annotationProcessor 'com.arello-mobile:moxy-compiler:1.4.4' + compile 'com.arello-mobile:moxy:1.4.5' + compile 'com.arello-mobile:moxy-app-compat:1.4.5' + annotationProcessor 'com.arello-mobile:moxy-compiler:1.4.5' } \ No newline at end of file diff --git a/sample-github/src/main/AndroidManifest.xml b/sample-github/src/main/AndroidManifest.xml index 025c2c42..50ae06a6 100644 --- a/sample-github/src/main/AndroidManifest.xml +++ b/sample-github/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ - + diff --git a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/common/Utils.java b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/common/Utils.java new file mode 100644 index 00000000..2147f4f6 --- /dev/null +++ b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/common/Utils.java @@ -0,0 +1,19 @@ +package com.arellomobile.mvp.sample.github.common; + +import rx.Observable; +import rx.android.schedulers.AndroidSchedulers; +import rx.schedulers.Schedulers; + +/** + * Date: 11.01.2017 + * Time: 16:39 + * + * @author Yuri Shmakov + */ + +public class Utils { + public static Observable.Transformer applySchedulers() { + return observable -> observable.subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + } +} diff --git a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/presenters/HomePresenter.java b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/presenters/HomePresenter.java index f139f25b..a31df3a9 100644 --- a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/presenters/HomePresenter.java +++ b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/presenters/HomePresenter.java @@ -16,6 +16,8 @@ public class HomePresenter extends MvpPresenter { public void onRepositorySelection(int position, Repository repository) { getViewState().showDetailsContainer(); + getViewState().setSelection(position); + getViewState().showDetails(position, repository); } } diff --git a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/presenters/RepositoriesPresenter.java b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/presenters/RepositoriesPresenter.java index 96aba1de..d45fc2ce 100644 --- a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/presenters/RepositoriesPresenter.java +++ b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/presenters/RepositoriesPresenter.java @@ -2,18 +2,18 @@ import java.util.List; +import com.arellomobile.mvp.InjectViewState; import com.arellomobile.mvp.sample.github.app.GithubApi; import com.arellomobile.mvp.sample.github.app.GithubApp; +import com.arellomobile.mvp.sample.github.common.Utils; import com.arellomobile.mvp.sample.github.mvp.GithubService; import com.arellomobile.mvp.sample.github.mvp.models.Repository; import com.arellomobile.mvp.sample.github.mvp.views.RepositoriesView; -import com.arellomobile.mvp.InjectViewState; import javax.inject.Inject; import rx.Observable; import rx.Subscription; -import rx.android.schedulers.AndroidSchedulers; /** * Date: 22.01.2016 @@ -54,14 +54,14 @@ private void loadData(int page, boolean isPageLoading, boolean isRefreshing) { } mIsInLoading = true; - getViewState().hideError(); getViewState().onStartLoading(); showProgress(isPageLoading, isRefreshing); final Observable> observable = mGithubService.getUserRepos("JakeWharton", page, GithubApi.PAGE_SIZE); - Subscription subscription = observable.observeOn(AndroidSchedulers.mainThread()) + Subscription subscription = observable + .compose(Utils.applySchedulers()) .subscribe(repositories -> { onLoadingFinish(isPageLoading, isRefreshing); onLoadingSuccess(isPageLoading, repositories); @@ -117,7 +117,7 @@ private void hideProgress(boolean isPageLoading, boolean isRefreshing) { } } - public void closeError() { + public void onErrorCancel() { getViewState().hideError(); } } \ No newline at end of file diff --git a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/presenters/RepositoryLikesPresenter.java b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/presenters/RepositoryLikesPresenter.java index bafcc243..acd3dc49 100644 --- a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/presenters/RepositoryLikesPresenter.java +++ b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/presenters/RepositoryLikesPresenter.java @@ -4,12 +4,8 @@ import java.util.List; import java.util.concurrent.TimeUnit; -import com.arellomobile.mvp.sample.github.app.GithubApp; -import com.arellomobile.mvp.sample.github.mvp.views.RepositoryLikesView; import com.arellomobile.mvp.InjectViewState; -import com.squareup.otto.Bus; - -import javax.inject.Inject; +import com.arellomobile.mvp.sample.github.mvp.views.RepositoryLikesView; import rx.Observable; import rx.Subscription; @@ -26,18 +22,9 @@ public class RepositoryLikesPresenter extends BasePresenter { public static final String TAG = "RepositoryLikesPresenter"; - @Inject - Bus mBus; - private List mInProgress = new ArrayList<>(); private List mLikedIds = new ArrayList<>(); - public RepositoryLikesPresenter() { - GithubApp.getAppComponent().inject(this); - - mBus.register(this); - } - public void toggleLike(int id) { if (mInProgress.contains(id)) { return; @@ -91,10 +78,4 @@ private void onFail(int id) { mInProgress.remove(Integer.valueOf(id)); getViewState().updateLikes(mInProgress, mLikedIds); } - - @Override - public void onDestroy() { - super.onDestroy(); - mBus.unregister(this); - } } diff --git a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/presenters/RepositoryPresenter.java b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/presenters/RepositoryPresenter.java index 1b315f71..e3a38891 100644 --- a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/presenters/RepositoryPresenter.java +++ b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/presenters/RepositoryPresenter.java @@ -15,24 +15,22 @@ */ @InjectViewState public class RepositoryPresenter extends MvpPresenter { - private boolean mIsInitialized = false; + private Repository mRepository; private List mInProgress; private List mLikedIds; - public RepositoryPresenter() { + public RepositoryPresenter(Repository repository) { super(); - } - - public void setRepository(Repository repository) { - if (mIsInitialized) { - return; - } - mIsInitialized = true; mRepository = repository; + } + + @Override + protected void onFirstViewAttach() { + super.onFirstViewAttach(); - getViewState().showRepository(repository); + getViewState().showRepository(mRepository); updateLikes(mInProgress, mLikedIds); } diff --git a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/presenters/RepositoryWidgetPresenter.java b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/presenters/RepositoryWidgetPresenter.java deleted file mode 100644 index 6e24894a..00000000 --- a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/presenters/RepositoryWidgetPresenter.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.arellomobile.mvp.sample.github.mvp.presenters; - -import java.util.List; - -import android.util.Log; - -import com.arellomobile.mvp.InjectViewState; -import com.arellomobile.mvp.MvpPresenter; -import com.arellomobile.mvp.sample.github.mvp.models.Repository; -import com.arellomobile.mvp.sample.github.mvp.views.RepositoryView; - -/** - * Date: 27.01.2016 - * Time: 21:12 - * - * @author Yuri Shmakov - */ -@InjectViewState -public class RepositoryWidgetPresenter extends MvpPresenter { - private boolean mIsInitialized = false; - private Repository mRepository; - private List mInProgress; - private List mLikedIds; - - public RepositoryWidgetPresenter() { - super(); - - Log.i("***-***", "Create new presenter"); - } - - public void setRepository(Repository repository) { - if (mIsInitialized) { - return; - } - mIsInitialized = true; - - mRepository = repository; - - getViewState().showRepository(repository); - - updateLikes(mInProgress, mLikedIds); - } - - public void updateLikes(List inProgress, List likedIds) { - mInProgress = inProgress; - mLikedIds = likedIds; - - if (mRepository == null || mInProgress == null || mLikedIds == null) { - return; - } - - boolean isInProgress = inProgress.contains(mRepository.getId()); - boolean isLiked = likedIds.contains(mRepository.getId()); - - getViewState().updateLike(isInProgress, isLiked); - } - - @Override - public void onDestroy() { - super.onDestroy(); - } -} diff --git a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/presenters/SignInPresenter.java b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/presenters/SignInPresenter.java index 10758b7c..bd0b5ca4 100644 --- a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/presenters/SignInPresenter.java +++ b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/presenters/SignInPresenter.java @@ -3,19 +3,17 @@ import android.text.TextUtils; import android.util.Base64; +import com.arellomobile.mvp.InjectViewState; import com.arellomobile.mvp.sample.github.R; import com.arellomobile.mvp.sample.github.app.GithubApp; +import com.arellomobile.mvp.sample.github.common.Utils; import com.arellomobile.mvp.sample.github.mvp.GithubService; import com.arellomobile.mvp.sample.github.mvp.common.AuthUtils; -import com.arellomobile.mvp.sample.github.mvp.models.User; import com.arellomobile.mvp.sample.github.mvp.views.SignInView; -import com.arellomobile.mvp.InjectViewState; import javax.inject.Inject; -import rx.Observable; import rx.Subscription; -import rx.android.schedulers.AndroidSchedulers; /** * Date: 15.01.2016 @@ -26,55 +24,54 @@ @InjectViewState public class SignInPresenter extends BasePresenter { - @Inject - GithubService mGithubService; + @Inject + GithubService mGithubService; - public SignInPresenter() { - GithubApp.getAppComponent().inject(this); - } + public SignInPresenter() { + GithubApp.getAppComponent().inject(this); + } - public void signIn(String email, String password) { + public void signIn(String email, String password) { - Integer emailError = null; - Integer passwordError = null; + Integer emailError = null; + Integer passwordError = null; - getViewState().showError(null, null); + getViewState().hideFormError(); - if (TextUtils.isEmpty(email)) { - emailError = R.string.error_field_required; - } + if (TextUtils.isEmpty(email)) { + emailError = R.string.error_field_required; + } - if (TextUtils.isEmpty(password)) { - passwordError = R.string.error_invalid_password; - } + if (TextUtils.isEmpty(password)) { + passwordError = R.string.error_invalid_password; + } - if (emailError != null || passwordError != null) { - getViewState().showError(emailError, passwordError); - return; - } + if (emailError != null || passwordError != null) { + getViewState().showFormError(emailError, passwordError); + return; + } - getViewState().showProgress(); + getViewState().startSignIn(); - String credentials = String.format("%s:%s", email, password); + String credentials = String.format("%s:%s", email, password); - final String token = "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP); + final String token = "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP); - Observable userObservable = mGithubService.signIn(token) - .doOnNext(user -> AuthUtils.setToken(token)); + Subscription subscription = mGithubService.signIn(token) + .doOnNext(user -> AuthUtils.setToken(token)) + .compose(Utils.applySchedulers()) + .subscribe(user -> { + getViewState().finishSignIn(); + getViewState().successSignIn(); + }, exception -> { + getViewState().finishSignIn(); + getViewState().failedSignIn(exception.getMessage()); + }); - Subscription subscription = userObservable - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(user -> { - getViewState().hideProgress(); - getViewState().successSignIn(); - }, exception -> { - getViewState().hideProgress(); - getViewState().showError(exception.getMessage()); - }); - unsubscribeOnDestroy(subscription); - } + unsubscribeOnDestroy(subscription); + } - public void onErrorCancel() { - getViewState().hideError(); - } + public void onErrorCancel() { + getViewState().hideError(); + } } diff --git a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/presenters/SplashPresenter.java b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/presenters/SplashPresenter.java index 032e035f..7183263d 100644 --- a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/presenters/SplashPresenter.java +++ b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/presenters/SplashPresenter.java @@ -2,28 +2,24 @@ import android.text.TextUtils; +import com.arellomobile.mvp.MvpPresenter; import com.arellomobile.mvp.sample.github.mvp.common.AuthUtils; import com.arellomobile.mvp.sample.github.mvp.views.SplashView; -import com.arellomobile.mvp.MvpPresenter; - -import rx.Observable; -import rx.Subscription; /** + * This presenter's View doesn't want ViewState. + * * Date: 18.01.2016 * Time: 15:38 * * @author Yuri Shmakov */ -public class SplashPresenter extends BasePresenter { - public void checkAuthorized() { - final Observable getTokenObservable = Observable.create(subscriber -> subscriber.onNext(AuthUtils.getToken())); +public class SplashPresenter extends MvpPresenter { + + @Override + public void attachView(SplashView view) { + super.attachView(view); - Subscription subscription = getTokenObservable.subscribe(token -> { - for (SplashView splashView : getAttachedViews()) { - splashView.setAuthorized(!TextUtils.isEmpty(token)); - } - }); - unsubscribeOnDestroy(subscription); + view.setAuthorized(!TextUtils.isEmpty(AuthUtils.getToken())); } } diff --git a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/views/HomeView.java b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/views/HomeView.java index 670ebe4a..9482e2e8 100644 --- a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/views/HomeView.java +++ b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/views/HomeView.java @@ -1,8 +1,9 @@ package com.arellomobile.mvp.sample.github.mvp.views; -import com.arellomobile.mvp.sample.github.mvp.models.Repository; import com.arellomobile.mvp.MvpView; -import com.arellomobile.mvp.viewstate.strategy.SkipStrategy; +import com.arellomobile.mvp.sample.github.mvp.models.Repository; +import com.arellomobile.mvp.viewstate.strategy.AddToEndSingleStrategy; +import com.arellomobile.mvp.viewstate.strategy.OneExecutionStateStrategy; import com.arellomobile.mvp.viewstate.strategy.StateStrategyType; /** @@ -11,9 +12,12 @@ * * @author Yuri Shmakov */ +@StateStrategyType(AddToEndSingleStrategy.class) public interface HomeView extends MvpView { - @StateStrategyType(SkipStrategy.class) - void showDetails(int position, Repository repository); - void showDetailsContainer(); + + void setSelection(int position); + + @StateStrategyType(OneExecutionStateStrategy.class) + void showDetails(int position, Repository repository); } diff --git a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/views/SignInView.java b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/views/SignInView.java index 67407472..36c6f622 100644 --- a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/views/SignInView.java +++ b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/mvp/views/SignInView.java @@ -13,15 +13,17 @@ */ @StateStrategyType(AddToEndSingleStrategy.class) public interface SignInView extends MvpView { - void showProgress(); + void startSignIn(); - void hideProgress(); + void finishSignIn(); - void showError(String message); + void failedSignIn(String message); void hideError(); - void showError(Integer emailError, Integer passwordError); + void hideFormError(); + + void showFormError(Integer emailError, Integer passwordError); @StateStrategyType(SkipStrategy.class) void successSignIn(); diff --git a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/ui/activities/HomeActivity.java b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/ui/activities/HomeActivity.java index aeb47e28..e27f442c 100644 --- a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/ui/activities/HomeActivity.java +++ b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/ui/activities/HomeActivity.java @@ -5,7 +5,6 @@ import android.app.AlertDialog; import android.content.Intent; import android.os.Bundle; -import android.support.v4.app.FragmentTransaction; import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuInflater; @@ -78,11 +77,6 @@ protected void onCreate(Bundle savedInstanceState) { mHomePresenter.onRepositorySelection(position, mRepositoriesAdapter.getItem(position)); }); - - mErrorDialog = new AlertDialog.Builder(this) - .setTitle(R.string.app_name) - .setOnCancelListener(dialog -> mRepositoriesPresenter.closeError()) - .create(); } @Override @@ -145,14 +139,19 @@ public void hideListProgress() { @Override public void showError(String message) { - mErrorDialog.setMessage(message); - mErrorDialog.show(); + mErrorDialog = new AlertDialog.Builder(this) + .setTitle(R.string.app_name) + .setMessage(message) + .setOnCancelListener(dialog -> mRepositoriesPresenter.onErrorCancel()) + .show(); } @Override public void hideError() { - mErrorDialog.hide(); + if (mErrorDialog != null && mErrorDialog.isShowing()) { + mErrorDialog.hide(); + } } @Override @@ -175,22 +174,30 @@ public void showDetailsContainer() { } @Override - public void showDetails(int position, Repository repository) { - DetailsFragment detailsFragment = (DetailsFragment) getSupportFragmentManager().findFragmentById(R.id.activity_home_frame_layout_details); + public void setSelection(int position) { + mRepositoriesAdapter.setSelection(position); + } - FragmentTransaction transaction = getSupportFragmentManager() + @Override + public void showDetails(int position, Repository repository) { + getSupportFragmentManager() .beginTransaction() - .replace(R.id.activity_home_frame_layout_details, DetailsFragment.getInstance(repository)); - - if (detailsFragment != null) { - transaction.addToBackStack(null); - } - - transaction.commit(); + .replace(R.id.activity_home_frame_layout_details, DetailsFragment.getInstance(repository)) + .commit(); } @Override public void onScrollToBottom() { mRepositoriesPresenter.loadNextRepositories(mRepositoriesAdapter.getRepositoriesCount()); } + + @Override + protected void onDestroy() { + if (mErrorDialog != null && mErrorDialog.isShowing()) { + mErrorDialog.setOnCancelListener(null); + mErrorDialog.dismiss(); + } + + super.onDestroy(); + } } \ No newline at end of file diff --git a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/ui/activities/SignInActivity.java b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/ui/activities/SignInActivity.java index 67b9e8dc..47266ccb 100644 --- a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/ui/activities/SignInActivity.java +++ b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/ui/activities/SignInActivity.java @@ -1,14 +1,12 @@ package com.arellomobile.mvp.sample.github.ui.activities; import android.app.AlertDialog; -import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.view.inputmethod.EditorInfo; import android.widget.Button; import android.widget.EditText; -import android.widget.LinearLayout; import com.arellomobile.mvp.MvpAppCompatActivity; import com.arellomobile.mvp.presenter.InjectPresenter; @@ -22,7 +20,7 @@ /** * A login screen that offers login via email/password. */ -public class SignInActivity extends MvpAppCompatActivity implements SignInView, DialogInterface.OnCancelListener { +public class SignInActivity extends MvpAppCompatActivity implements SignInView { @InjectPresenter SignInPresenter mSignInPresenter; @@ -32,8 +30,6 @@ public class SignInActivity extends MvpAppCompatActivity implements SignInView, EditText mPasswordView; @BindView(R.id.email_sign_in_button) Button mSignInButton; - @BindView(R.id.activity_sign_in_linear_layout_container) - LinearLayout mContainer; @BindView(R.id.login_form) View mLoginFormView; @BindView(R.id.login_progress) @@ -57,11 +53,6 @@ protected void onCreate(Bundle savedInstanceState) { }); mSignInButton.setOnClickListener(view -> attemptLogin()); - - mErrorDialog = new AlertDialog.Builder(this) - .setTitle(R.string.app_name) - .setOnCancelListener(this) - .create(); } private void attemptLogin() { @@ -69,12 +60,12 @@ private void attemptLogin() { } @Override - public void showProgress() { + public void startSignIn() { toggleProgressVisibility(true); } @Override - public void hideProgress() { + public void finishSignIn() { toggleProgressVisibility(false); } @@ -84,29 +75,31 @@ private void toggleProgressVisibility(final boolean show) { } @Override - public void showError(String message) { - mErrorDialog.setMessage(message); - mErrorDialog.show(); + public void failedSignIn(String message) { + mErrorDialog = new AlertDialog.Builder(this) + .setTitle(R.string.app_name) + .setMessage(message) + .setOnCancelListener(dialog -> mSignInPresenter.onErrorCancel()) + .show(); } @Override - public void onCancel(DialogInterface dialog) { - mSignInPresenter.onErrorCancel(); + public void hideError() { + if (mErrorDialog != null && mErrorDialog.isShowing()) { + mErrorDialog.cancel(); + } } @Override - public void hideError() { - mErrorDialog.cancel(); + public void hideFormError() { + mEmailView.setError(null); + mPasswordView.setError(null); } @Override - public void showError(Integer emailError, Integer passwordError) { - if (emailError != null) { - mEmailView.setError(getString(emailError)); - } - if (passwordError != null) { - mPasswordView.setError(getString(passwordError)); - } + public void showFormError(Integer emailError, Integer passwordError) { + mEmailView.setError(emailError == null ? null : getString(emailError)); + mPasswordView.setError(passwordError == null ? null : getString(passwordError)); } @Override @@ -119,7 +112,7 @@ public void successSignIn() { @Override protected void onDestroy() { - if (mErrorDialog != null) { + if (mErrorDialog != null && mErrorDialog.isShowing()) { mErrorDialog.setOnCancelListener(null); mErrorDialog.dismiss(); } diff --git a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/ui/activities/SplashActivity.java b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/ui/activities/SplashActivity.java index 13c79466..6a6f0950 100644 --- a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/ui/activities/SplashActivity.java +++ b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/ui/activities/SplashActivity.java @@ -9,6 +9,7 @@ import com.arellomobile.mvp.sample.github.mvp.views.SplashView; public class SplashActivity extends MvpAppCompatActivity implements SplashView { + @InjectPresenter SplashPresenter mSplashPresenter; @@ -16,20 +17,13 @@ public class SplashActivity extends MvpAppCompatActivity implements SplashView { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - // By default view attaches to presenter in onStart() method, + // By default view attaches to presenter in onResume() method, // but we attach it manually for earlier check authorization state. getMvpDelegate().onAttach(); - - mSplashPresenter.checkAuthorized(); } @Override public void setAuthorized(boolean isAuthorized) { - startActivityForResult(new Intent(this, isAuthorized ? HomeActivity.class : SignInActivity.class), 0); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - finish(); + startActivity(new Intent(this, isAuthorized ? HomeActivity.class : SignInActivity.class)); } } diff --git a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/ui/adapters/RepositoriesAdapter.java b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/ui/adapters/RepositoriesAdapter.java index 82d96f96..2e7fc092 100644 --- a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/ui/adapters/RepositoriesAdapter.java +++ b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/ui/adapters/RepositoriesAdapter.java @@ -10,17 +10,16 @@ import android.widget.ProgressBar; import android.widget.TextView; +import com.arellomobile.mvp.MvpDelegate; +import com.arellomobile.mvp.presenter.InjectPresenter; +import com.arellomobile.mvp.presenter.PresenterType; +import com.arellomobile.mvp.presenter.ProvidePresenter; import com.arellomobile.mvp.sample.github.R; import com.arellomobile.mvp.sample.github.mvp.models.Repository; import com.arellomobile.mvp.sample.github.mvp.presenters.RepositoryLikesPresenter; import com.arellomobile.mvp.sample.github.mvp.presenters.RepositoryPresenter; import com.arellomobile.mvp.sample.github.mvp.views.RepositoryLikesView; import com.arellomobile.mvp.sample.github.mvp.views.RepositoryView; -import com.arellomobile.mvp.MvpDelegate; -import com.arellomobile.mvp.presenter.InjectPresenter; -import com.arellomobile.mvp.presenter.PresenterType; -import com.arellomobile.mvp.presenter.ProvidePresenter; -import com.arellomobile.mvp.presenter.ProvidePresenterTag; import butterknife.BindView; import butterknife.ButterKnife; @@ -33,7 +32,7 @@ */ public class RepositoriesAdapter extends MvpBaseAdapter implements RepositoryLikesView { public static final int REPOSITORY_VIEW_TYPE = 0; - public static final int PROGRESS_VIEW_TYPE = 1; + private static final int PROGRESS_VIEW_TYPE = 1; @InjectPresenter(type = PresenterType.WEAK, tag = RepositoryLikesPresenter.TAG) RepositoryLikesPresenter mRepositoryLikesPresenter; @@ -140,31 +139,24 @@ public View getView(int position, View convertView, ViewGroup parent) { public class RepositoryHolder implements RepositoryView { - @InjectPresenter(type = PresenterType.WEAK) + @InjectPresenter RepositoryPresenter mRepositoryPresenter; private Repository mRepository; - @ProvidePresenterTag(presenterClass = RepositoryPresenter.class, type = PresenterType.WEAK) - String provideRepositoryPresenterTag() { - return String.valueOf(mRepository.getId()); - } - - @ProvidePresenter(type = PresenterType.WEAK) - RepositoryPresenter provideRepositoryPresenter() { - RepositoryPresenter repositoryPresenter = new RepositoryPresenter(); - repositoryPresenter.setRepository(mRepository); - return repositoryPresenter; - } - - View view; - @BindView(R.id.item_repository_text_view_name) TextView nameTextView; @BindView(R.id.item_repository_image_button_like) ImageButton likeImageButton; + View view; + private MvpDelegate mMvpDelegate; + @ProvidePresenter + RepositoryPresenter provideRepositoryPresenter() { + return new RepositoryPresenter(mRepository); + } + RepositoryHolder(View view) { this.view = view; diff --git a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/ui/fragments/DetailsFragment.java b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/ui/fragments/DetailsFragment.java index 5392e5ee..cace34a1 100644 --- a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/ui/fragments/DetailsFragment.java +++ b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/ui/fragments/DetailsFragment.java @@ -13,7 +13,6 @@ import com.arellomobile.mvp.presenter.InjectPresenter; import com.arellomobile.mvp.presenter.PresenterType; import com.arellomobile.mvp.presenter.ProvidePresenter; -import com.arellomobile.mvp.presenter.ProvidePresenterTag; import com.arellomobile.mvp.sample.github.R; import com.arellomobile.mvp.sample.github.mvp.models.Repository; import com.arellomobile.mvp.sample.github.mvp.presenters.RepositoryLikesPresenter; @@ -34,32 +33,25 @@ public class DetailsFragment extends MvpAppCompatFragment implements RepositoryView, RepositoryLikesView { public static final String ARGS_REPOSITORY = "argsRepository"; - @InjectPresenter(type = PresenterType.WEAK) + @InjectPresenter RepositoryPresenter mRepositoryPresenter; - - private Repository mRepository; - - @ProvidePresenterTag(presenterClass = RepositoryPresenter.class, type = PresenterType.WEAK) - String provideRepositoryPresenterTag() { - mRepository = (Repository) getArguments().get(ARGS_REPOSITORY); - return String.valueOf(mRepository.getId()); - } - - @ProvidePresenter(type = PresenterType.WEAK) - RepositoryPresenter provideRepositoryPresenter() { - RepositoryPresenter repositoryPresenter = new RepositoryPresenter(); - repositoryPresenter.setRepository(mRepository); - return repositoryPresenter; - } - @InjectPresenter(type = PresenterType.WEAK, tag = RepositoryLikesPresenter.TAG) RepositoryLikesPresenter mRepositoryLikesPresenter; + private Repository mRepository; + @BindView(R.id.fragment_repository_details_text_view_title) RepositoryWidget mTitleTextView; @BindView(R.id.fragment_repository_details_image_button_like) ImageButton mLikeImageButton; + @ProvidePresenter + RepositoryPresenter provideRepositoryPresenter() { + mRepository = (Repository) getArguments().get(ARGS_REPOSITORY); + + return new RepositoryPresenter(mRepository); + } + public static DetailsFragment getInstance(Repository repository) { DetailsFragment fragment = new DetailsFragment(); @@ -86,10 +78,6 @@ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { mLikeImageButton.setOnClickListener(likeImageButton -> mRepositoryLikesPresenter.toggleLike(mRepository.getId())); } - public void setRepository(Repository repository) { - mRepositoryPresenter.setRepository(repository); - } - @Override public void updateLikes(List inProgress, List likedIds) { mRepositoryPresenter.updateLikes(inProgress, likedIds); @@ -99,7 +87,7 @@ public void updateLikes(List inProgress, List likedIds) { public void showRepository(Repository repository) { mRepository = repository; - mTitleTextView.setRepository(getMvpDelegate(), repository); + mTitleTextView.initWidget(getMvpDelegate(), repository); } @Override diff --git a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/ui/views/RepositoryWidget.java b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/ui/views/RepositoryWidget.java index 457659f9..9840ccc1 100644 --- a/sample-github/src/main/java/com/arellomobile/mvp/sample/github/ui/views/RepositoryWidget.java +++ b/sample-github/src/main/java/com/arellomobile/mvp/sample/github/ui/views/RepositoryWidget.java @@ -10,7 +10,7 @@ import com.arellomobile.mvp.presenter.InjectPresenter; import com.arellomobile.mvp.presenter.ProvidePresenter; import com.arellomobile.mvp.sample.github.mvp.models.Repository; -import com.arellomobile.mvp.sample.github.mvp.presenters.RepositoryWidgetPresenter; +import com.arellomobile.mvp.sample.github.mvp.presenters.RepositoryPresenter; import com.arellomobile.mvp.sample.github.mvp.views.RepositoryView; /** @@ -28,7 +28,7 @@ public class RepositoryWidget extends TextView implements RepositoryView { private Repository mRepository; @InjectPresenter - RepositoryWidgetPresenter mRepositoryPresenter; + RepositoryPresenter mRepositoryPresenter; public RepositoryWidget(Context context) { super(context); @@ -48,18 +48,16 @@ public RepositoryWidget(Context context, AttributeSet attrs, int defStyleAttr, i } @ProvidePresenter - RepositoryWidgetPresenter provideRepositoryPresenter() { - return new RepositoryWidgetPresenter(); + RepositoryPresenter provideRepositoryPresenter() { + return new RepositoryPresenter(mRepository); } - public void setRepository(MvpDelegate parentDelegate, Repository repository) { + public void initWidget(MvpDelegate parentDelegate, Repository repository) { mParentDelegate = parentDelegate; mRepository = repository; getMvpDelegate().onCreate(); getMvpDelegate().onAttach(); - - mRepositoryPresenter.setRepository(mRepository); } public MvpDelegate getMvpDelegate() { diff --git a/sample-github/src/main/res/layout/activity_home.xml b/sample-github/src/main/res/layout/activity_home.xml index 120cbcad..82f4fa04 100644 --- a/sample-github/src/main/res/layout/activity_home.xml +++ b/sample-github/src/main/res/layout/activity_home.xml @@ -34,15 +34,16 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:choiceMode="singleChoice" + android:dividerHeight="1dp" tools:ignore="NestedScrolling" /> + android:text="No repositories" + android:visibility="gone" /> + android:visibility="gone"> + + + + diff --git a/sample-github/src/test/java/com/arellomobile/mvp/sample/github/mvp/presenters/HomePresenterTest.java b/sample-github/src/test/java/com/arellomobile/mvp/sample/github/mvp/presenters/HomePresenterTest.java index 6e54f05a..80bfef5a 100644 --- a/sample-github/src/test/java/com/arellomobile/mvp/sample/github/mvp/presenters/HomePresenterTest.java +++ b/sample-github/src/test/java/com/arellomobile/mvp/sample/github/mvp/presenters/HomePresenterTest.java @@ -9,35 +9,36 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; + import static org.mockito.Mockito.verify; public final class HomePresenterTest { - @Mock - HomeView homeView; - - @Mock - HomeView$$State homeViewState; - - private HomePresenter presenter; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - presenter = new HomePresenter(); - presenter.attachView(homeView); - presenter.setViewState(homeViewState); - } - - @Test - public void details_shouldShowDetailsContainer() { - Repository emptyRepository = emptyRepository(); - presenter.onRepositorySelection(0, emptyRepository); - verify(homeViewState).showDetailsContainer(); - verify(homeViewState).showDetails(0, emptyRepository); - } - - private Repository emptyRepository() { - return new Repository(); - } + @Mock + HomeView homeView; + + @Mock + HomeView$$State homeViewState; + + private HomePresenter presenter; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + presenter = new HomePresenter(); + presenter.attachView(homeView); + presenter.setViewState(homeViewState); + } + + @Test + public void details_shouldShowDetailsContainer() { + Repository emptyRepository = emptyRepository(); + presenter.onRepositorySelection(0, emptyRepository); + verify(homeViewState).showDetailsContainer(); + verify(homeViewState).showDetails(0, emptyRepository); + } + + private Repository emptyRepository() { + return new Repository(); + } } \ No newline at end of file diff --git a/sample-github/src/test/java/com/arellomobile/mvp/sample/github/mvp/presenters/RepositoriesPresenterTest.java b/sample-github/src/test/java/com/arellomobile/mvp/sample/github/mvp/presenters/RepositoriesPresenterTest.java index e789406a..96ad96dc 100644 --- a/sample-github/src/test/java/com/arellomobile/mvp/sample/github/mvp/presenters/RepositoriesPresenterTest.java +++ b/sample-github/src/test/java/com/arellomobile/mvp/sample/github/mvp/presenters/RepositoriesPresenterTest.java @@ -1,6 +1,7 @@ package com.arellomobile.mvp.sample.github.mvp.presenters; -import android.content.Context; +import java.util.ArrayList; +import java.util.List; import com.arellomobile.mvp.sample.github.di.AppComponent; import com.arellomobile.mvp.sample.github.mvp.GithubService; @@ -15,16 +16,17 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import org.robolectric.RuntimeEnvironment; - -import java.util.ArrayList; -import java.util.List; import rx.Observable; +import rx.Scheduler; +import rx.android.plugins.RxAndroidPlugins; +import rx.android.plugins.RxAndroidSchedulersHook; +import rx.plugins.RxJavaPlugins; +import rx.plugins.RxJavaSchedulersHook; +import rx.schedulers.Schedulers; + -import static org.junit.Assert.*; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.never; @@ -34,86 +36,97 @@ @RunWith(GithubSampleTestRunner.class) public class RepositoriesPresenterTest { - @Rule - public TestComponentRule testComponentRule = new TestComponentRule(testAppComponent()); - - @Mock - GithubService githubService; - - @Mock - RepositoriesView$$State repositoriesViewState; - - private RepositoriesPresenter presenter; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - presenter = new RepositoriesPresenter(); - presenter.setViewState(repositoriesViewState); - } - - @Test - public void repositories_shouldCloseError() { - presenter.closeError(); - verify(repositoriesViewState).hideError(); - } - - - @Test - public void repositories_shouldOnAttachLoadAndShowRepositores() { - List repositories = repositores(); - when(githubService.getUserRepos(anyString(), anyInt(), anyInt())).thenReturn(Observable.just(repositories)); - - presenter.onFirstViewAttach(); - verify(repositoriesViewState).hideError(); - verify(repositoriesViewState).onStartLoading(); - verify(repositoriesViewState, never()).showRefreshing(); - verify(repositoriesViewState).showListProgress(); - verify(repositoriesViewState).onFinishLoading(); - verify(repositoriesViewState).setRepositories(repositories, false); - } - - @Test - public void repositories_shouldCorrectLoadNextRepositories() { - List repositories = repositores(); - when(githubService.getUserRepos(anyString(), anyInt(), anyInt())).thenReturn(Observable.just(repositories)); - - presenter.loadNextRepositories(10); - verify(repositoriesViewState).hideError(); - verify(repositoriesViewState).onStartLoading(); - verify(repositoriesViewState, never()).showListProgress(); - verify(repositoriesViewState, never()).hideListProgress(); - verify(repositoriesViewState).onFinishLoading(); - verify(repositoriesViewState).addRepositories(repositories, false); - } - - @Test - public void repositories_shouldShowErrorIfSomeExceptionHappended() { - RuntimeException someError = new RuntimeException(); - when(githubService.getUserRepos(anyString(), anyInt(), anyInt())).thenReturn(Observable.error(someError)); - - presenter.loadNextRepositories(10); - verify(repositoriesViewState).hideError(); - verify(repositoriesViewState).onStartLoading(); - verify(repositoriesViewState).onFinishLoading(); - verify(repositoriesViewState).showError(someError.toString()); - } - - - private List repositores() { - List repositories = new ArrayList<>(); - for(int i = 0; i < 10; i++) { - repositories.add(new Repository()); - } - return repositories; - } - - private AppComponent testAppComponent() { - return new TestComponent() { - @Override public void inject(RepositoriesPresenter presenter) { - presenter.mGithubService = githubService; - } - }; - } + @Mock + GithubService githubService; + @Rule + public TestComponentRule testComponentRule = new TestComponentRule(testAppComponent()); + @Mock + RepositoriesView$$State repositoriesViewState; + + private RepositoriesPresenter presenter; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + presenter = new RepositoriesPresenter(); + presenter.setViewState(repositoriesViewState); + + RxJavaPlugins.getInstance().reset(); + RxJavaPlugins.getInstance().registerSchedulersHook(new RxJavaSchedulersHook() { + @Override + public Scheduler getIOScheduler() { + return Schedulers.immediate(); + } + }); + RxAndroidPlugins.getInstance().reset(); + RxAndroidPlugins.getInstance().registerSchedulersHook(new RxAndroidSchedulersHook() { + @Override + public Scheduler getMainThreadScheduler() { + return Schedulers.immediate(); + } + }); + } + + @Test + public void repositories_shouldCloseError() { + presenter.onErrorCancel(); + verify(repositoriesViewState).hideError(); + } + + + @Test + public void repositories_shouldOnAttachLoadAndShowRepositores() { + List repositories = repositores(); + when(githubService.getUserRepos(anyString(), anyInt(), anyInt())).thenReturn(Observable.just(repositories)); + + presenter.onFirstViewAttach(); + verify(repositoriesViewState).onStartLoading(); + verify(repositoriesViewState, never()).showRefreshing(); + verify(repositoriesViewState).showListProgress(); + verify(repositoriesViewState).onFinishLoading(); + verify(repositoriesViewState).setRepositories(repositories, false); + } + + @Test + public void repositories_shouldCorrectLoadNextRepositories() { + List repositories = repositores(); + when(githubService.getUserRepos(anyString(), anyInt(), anyInt())).thenReturn(Observable.just(repositories)); + + presenter.loadNextRepositories(10); + verify(repositoriesViewState).onStartLoading(); + verify(repositoriesViewState, never()).showListProgress(); + verify(repositoriesViewState, never()).hideListProgress(); + verify(repositoriesViewState).onFinishLoading(); + verify(repositoriesViewState).addRepositories(repositories, false); + } + + @Test + public void repositories_shouldShowErrorIfSomeExceptionHappended() { + RuntimeException someError = new RuntimeException(); + when(githubService.getUserRepos(anyString(), anyInt(), anyInt())).thenReturn(Observable.error(someError)); + + presenter.loadNextRepositories(10); + verify(repositoriesViewState).onStartLoading(); + verify(repositoriesViewState).onFinishLoading(); + verify(repositoriesViewState).showError(someError.toString()); + } + + + private List repositores() { + List repositories = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + repositories.add(new Repository()); + } + return repositories; + } + + private AppComponent testAppComponent() { + return new TestComponent() { + @Override + public void inject(RepositoriesPresenter presenter) { + presenter.mGithubService = githubService; + } + }; + } } \ No newline at end of file diff --git a/sample-github/src/test/java/com/arellomobile/mvp/sample/github/mvp/presenters/RepositoryPresenterTest.java b/sample-github/src/test/java/com/arellomobile/mvp/sample/github/mvp/presenters/RepositoryPresenterTest.java index 7954d929..9facdaf4 100644 --- a/sample-github/src/test/java/com/arellomobile/mvp/sample/github/mvp/presenters/RepositoryPresenterTest.java +++ b/sample-github/src/test/java/com/arellomobile/mvp/sample/github/mvp/presenters/RepositoryPresenterTest.java @@ -1,5 +1,7 @@ package com.arellomobile.mvp.sample.github.mvp.presenters; +import java.util.Collections; + import com.arellomobile.mvp.sample.github.mvp.models.Repository; import com.arellomobile.mvp.sample.github.mvp.views.RepositoryView$$State; import com.arellomobile.mvp.sample.github.test.GithubSampleTestRunner; @@ -10,63 +12,55 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import java.util.Collections; -import static org.junit.Assert.*; import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyList; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @RunWith(GithubSampleTestRunner.class) public class RepositoryPresenterTest { - @Rule - public TestComponentRule testComponentRule = new TestComponentRule(); + @Rule + public TestComponentRule testComponentRule = new TestComponentRule(); + + @Mock + RepositoryView$$State repositoryViewState; + + private RepositoryPresenter presenter; + private Repository repository; - @Mock - RepositoryView$$State repositoryViewState; + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); - private RepositoryPresenter presenter; + repository = new Repository(); - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - presenter = new RepositoryPresenter(); - presenter.setViewState(repositoryViewState); - } + presenter = new RepositoryPresenter(repository); + presenter.setViewState(repositoryViewState); + } - @Test - public void repository_shouldShowRepository() { - Repository repository = repository(); - presenter.setRepository(repository); - verify(repositoryViewState).showRepository(repository); - verify(repositoryViewState, never()).updateLike(anyBoolean(), anyBoolean()); - } + @Test + public void repository_shouldShwRepository() { + presenter.onFirstViewAttach(); - @Test - public void repository_shouldSetLikeTrueForRepository() { - presenter.setRepository(repository()); - presenter.updateLikes(Collections.singletonList(0), Collections.singletonList(0)); + verify(repositoryViewState).showRepository(repository); + verify(repositoryViewState, never()).updateLike(anyBoolean(), anyBoolean()); + } - verify(repositoryViewState).updateLike(true, true); - } + @Test + public void repository_shouldSetLikeTrueForRepository() { + presenter.updateLikes(Collections.singletonList(0), Collections.singletonList(0)); - @Test - public void repository_shouldSetLikeFalseForRepository() { - presenter.setRepository(repository()); - presenter.updateLikes(Collections.singletonList(1), Collections.singletonList(1)); + verify(repositoryViewState).updateLike(true, true); + } - verify(repositoryViewState).updateLike(false, false); - } + @Test + public void repository_shouldSetLikeFalseForRepository() { + presenter.updateLikes(Collections.singletonList(1), Collections.singletonList(1)); - private Repository repository() { - return new Repository(); - } + verify(repositoryViewState).updateLike(false, false); + } } \ No newline at end of file diff --git a/sample-github/src/test/java/com/arellomobile/mvp/sample/github/mvp/presenters/SignInPresenterTest.java b/sample-github/src/test/java/com/arellomobile/mvp/sample/github/mvp/presenters/SignInPresenterTest.java index 1aa37ff3..ae43b8e3 100644 --- a/sample-github/src/test/java/com/arellomobile/mvp/sample/github/mvp/presenters/SignInPresenterTest.java +++ b/sample-github/src/test/java/com/arellomobile/mvp/sample/github/mvp/presenters/SignInPresenterTest.java @@ -1,5 +1,7 @@ package com.arellomobile.mvp.sample.github.mvp.presenters; +import java.util.concurrent.TimeUnit; + import android.content.Context; import android.util.Base64; @@ -23,6 +25,13 @@ import org.robolectric.RuntimeEnvironment; import rx.Observable; +import rx.Scheduler; +import rx.android.plugins.RxAndroidPlugins; +import rx.android.plugins.RxAndroidSchedulersHook; +import rx.plugins.RxJavaPlugins; +import rx.plugins.RxJavaSchedulersHook; +import rx.schedulers.Schedulers; + import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.verify; @@ -31,89 +40,116 @@ @RunWith(GithubSampleTestRunner.class) public final class SignInPresenterTest { - @Rule - public TestComponentRule testComponentRule = new TestComponentRule(testAppComponent()); - - @Mock - GithubService githubService; - - @Mock - SignInView$$State signInViewState; - - private SignInPresenter presenter; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - presenter = new SignInPresenter(); - presenter.setViewState(signInViewState); - } - - @Test - public void signin_shouldSignSuccessfull() { - String token = token(); - when(githubService.signIn(token)).thenReturn(Observable.just(new User())); - - presenter.signIn(email(), password()); - - Assert.assertEquals(token, AuthUtils.getToken()); - isSignInAndHideShowProgressCalled(); - verify(signInViewState).successSignIn(); - } - - @Test - public void signin_shouldShowError() { - when(githubService.signIn(token())).thenReturn(Observable.error(new Throwable())); - - presenter.signIn(email(), password()); - - Assert.assertEquals("", ""); - - isSignInAndHideShowProgressCalled(); - verify(signInViewState).showError(anyString()); - } - - @Test - public void signin_shouldShowPasswordAndEmailEmptyErros() { - presenter.signIn(null, null); - verify(signInViewState).showError(R.string.error_field_required, R.string.error_invalid_password); - } - - @Test - public void signin_shouldOnErrorCancel() { - presenter.onErrorCancel(); - verify(signInViewState).hideError(); - } - - private void isSignInAndHideShowProgressCalled() { - verify(signInViewState).showError(null, null); - verify(signInViewState).showProgress(); - verify(signInViewState).hideProgress(); - verify(githubService).signIn(token()); - } - - private AppComponent testAppComponent() { - return new TestComponent() { - @Override public Context getContext() { - return RuntimeEnvironment.application; - } - - @Override public void inject(SignInPresenter presenter) { - presenter.mGithubService = githubService; - } - }; - } - - private String token() { - String credentials = String.format("%s:%s", email(), password()); - return "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP); - } - - private String email() { - return "test@test.ru"; - } - - private String password() { - return "password"; - } + @Mock + GithubService githubService; + @Rule + public TestComponentRule testComponentRule = new TestComponentRule(testAppComponent()); + @Mock + SignInView$$State signInViewState; + + private SignInPresenter presenter; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + presenter = new SignInPresenter(); + presenter.setViewState(signInViewState); + + RxJavaPlugins.getInstance().reset(); + RxJavaPlugins.getInstance().registerSchedulersHook(new RxJavaSchedulersHook() { + @Override + public Scheduler getIOScheduler() { + return Schedulers.immediate(); + } + }); + RxAndroidPlugins.getInstance().reset(); + RxAndroidPlugins.getInstance().registerSchedulersHook(new RxAndroidSchedulersHook() { + @Override + public Scheduler getMainThreadScheduler() { + return Schedulers.immediate(); + } + }); + } + + @Test + public void signin_shouldSignSuccessfull() { + String token = token(); + when(githubService.signIn(token)).thenReturn(Observable.just(new User())); + + presenter.signIn(email(), password()); + + try { + TimeUnit.MILLISECONDS.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + Assert.assertEquals(token, AuthUtils.getToken()); + isSignInAndHideShowProgressCalled(); + verify(signInViewState).successSignIn(); + } + + @Test + public void signin_shouldShowError() { + when(githubService.signIn(token())).thenReturn(Observable.error(new Throwable())); + + presenter.signIn(email(), password()); + + try { + TimeUnit.MILLISECONDS.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + Assert.assertEquals("", ""); + + isSignInAndHideShowProgressCalled(); + verify(signInViewState).failedSignIn(anyString()); + } + + @Test + public void signin_shouldShowPasswordAndEmailEmptyErros() { + presenter.signIn(null, null); + verify(signInViewState).showFormError(R.string.error_field_required, R.string.error_invalid_password); + } + + @Test + public void signin_shouldOnErrorCancel() { + presenter.onErrorCancel(); + verify(signInViewState).hideError(); + } + + private void isSignInAndHideShowProgressCalled() { + verify(signInViewState).hideFormError(); + verify(signInViewState).startSignIn(); + verify(signInViewState).finishSignIn(); + verify(githubService).signIn(token()); + } + + private AppComponent testAppComponent() { + return new TestComponent() { + @Override + public Context getContext() { + return RuntimeEnvironment.application; + } + + @Override + public void inject(SignInPresenter presenter) { + presenter.mGithubService = githubService; + } + }; + } + + private String token() { + String credentials = String.format("%s:%s", email(), password()); + return "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP); + } + + private String email() { + return "test@test.ru"; + } + + private String password() { + return "password"; + } } \ No newline at end of file diff --git a/sample-github/src/test/java/com/arellomobile/mvp/sample/github/mvp/presenters/SignOutPresenterTest.java b/sample-github/src/test/java/com/arellomobile/mvp/sample/github/mvp/presenters/SignOutPresenterTest.java index 224102c7..c40a82d5 100644 --- a/sample-github/src/test/java/com/arellomobile/mvp/sample/github/mvp/presenters/SignOutPresenterTest.java +++ b/sample-github/src/test/java/com/arellomobile/mvp/sample/github/mvp/presenters/SignOutPresenterTest.java @@ -12,33 +12,34 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; + import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.verify; @RunWith(GithubSampleTestRunner.class) public final class SignOutPresenterTest { - @Rule - public TestComponentRule testComponentRule = new TestComponentRule(); + @Rule + public TestComponentRule testComponentRule = new TestComponentRule(); - @Mock - SignOutView$$State signOutViewState; + @Mock + SignOutView$$State signOutViewState; - private SignOutPresenter presenter; + private SignOutPresenter presenter; - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - presenter = new SignOutPresenter(); - presenter.setViewState(signOutViewState); - AuthUtils.setToken("some token"); - } + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + presenter = new SignOutPresenter(); + presenter.setViewState(signOutViewState); + AuthUtils.setToken("some token"); + } - @Test - public void signout_shouldSingOut() { - presenter.signOut(); + @Test + public void signout_shouldSingOut() { + presenter.signOut(); - verify(signOutViewState).signOut(); - assertEquals("", AuthUtils.getToken()); - } + verify(signOutViewState).signOut(); + assertEquals("", AuthUtils.getToken()); + } } \ No newline at end of file diff --git a/sample-github/src/test/java/com/arellomobile/mvp/sample/github/mvp/presenters/SplashPresenterTest.java b/sample-github/src/test/java/com/arellomobile/mvp/sample/github/mvp/presenters/SplashPresenterTest.java index 60ab196f..ef5344ba 100644 --- a/sample-github/src/test/java/com/arellomobile/mvp/sample/github/mvp/presenters/SplashPresenterTest.java +++ b/sample-github/src/test/java/com/arellomobile/mvp/sample/github/mvp/presenters/SplashPresenterTest.java @@ -12,38 +12,39 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; + import static org.mockito.Mockito.verify; @RunWith(GithubSampleTestRunner.class) public class SplashPresenterTest { - @Rule - public TestComponentRule testComponentRule = new TestComponentRule(); - - @Mock - SplashView splashView; - - private SplashPresenter presenter; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - presenter = new SplashPresenter(); - presenter.getAttachedViews().add(splashView); - } - - @Test - public void splash_shouldAuthorizedStateFalse() { - AuthUtils.setToken(null); - presenter.checkAuthorized(); - verify(splashView).setAuthorized(false); - } - - @Test - public void splash_shouldAuthorizedStateTrue() { - AuthUtils.setToken("token"); - presenter.checkAuthorized(); - verify(splashView).setAuthorized(true); - } + @Rule + public TestComponentRule testComponentRule = new TestComponentRule(); + + @Mock + SplashView splashView; + + private SplashPresenter presenter; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + presenter = new SplashPresenter(); + presenter.getAttachedViews().add(splashView); + } + + @Test + public void splash_shouldAuthorizedStateFalse() { + AuthUtils.setToken(null); + presenter.attachView(splashView); + verify(splashView).setAuthorized(false); + } + + @Test + public void splash_shouldAuthorizedStateTrue() { + AuthUtils.setToken("token"); + presenter.attachView(splashView); + verify(splashView).setAuthorized(true); + } } diff --git a/sample-github/src/test/java/com/arellomobile/mvp/sample/github/test/GithubSampleTestRunner.java b/sample-github/src/test/java/com/arellomobile/mvp/sample/github/test/GithubSampleTestRunner.java index f3b32cce..4b79cd69 100644 --- a/sample-github/src/test/java/com/arellomobile/mvp/sample/github/test/GithubSampleTestRunner.java +++ b/sample-github/src/test/java/com/arellomobile/mvp/sample/github/test/GithubSampleTestRunner.java @@ -1,5 +1,7 @@ package com.arellomobile.mvp.sample.github.test; +import java.lang.reflect.Method; + import android.support.annotation.NonNull; import com.arellomobile.mvp.sample.github.BuildConfig; @@ -8,31 +10,29 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; -import java.lang.reflect.Method; - public class GithubSampleTestRunner extends RobolectricTestRunner { - private static final int SDK_EMULATE_LEVEL = 23; - - public GithubSampleTestRunner(Class testClass) throws InitializationError { - super(testClass); - } - - @Override - public Config getConfig(@NonNull Method method) { - final Config defaultConfig = super.getConfig(method); - return new Config.Implementation( - new int[]{SDK_EMULATE_LEVEL}, - defaultConfig.manifest(), - defaultConfig.qualifiers(), - defaultConfig.packageName(), - defaultConfig.resourceDir(), - defaultConfig.assetDir(), - defaultConfig.shadows(), - defaultConfig.instrumentedPackages(), - defaultConfig.application(), - defaultConfig.libraries(), - defaultConfig.constants() == Void.class ? BuildConfig.class : defaultConfig.constants() - ); - } + private static final int SDK_EMULATE_LEVEL = 23; + + public GithubSampleTestRunner(Class testClass) throws InitializationError { + super(testClass); + } + + @Override + public Config getConfig(@NonNull Method method) { + final Config defaultConfig = super.getConfig(method); + return new Config.Implementation( + new int[]{SDK_EMULATE_LEVEL}, + defaultConfig.manifest(), + defaultConfig.qualifiers(), + defaultConfig.packageName(), + defaultConfig.resourceDir(), + defaultConfig.assetDir(), + defaultConfig.shadows(), + defaultConfig.instrumentedPackages(), + defaultConfig.application(), + defaultConfig.libraries(), + defaultConfig.constants() == Void.class ? BuildConfig.class : defaultConfig.constants() + ); + } } diff --git a/sample-github/src/test/java/com/arellomobile/mvp/sample/github/test/TestComponent.java b/sample-github/src/test/java/com/arellomobile/mvp/sample/github/test/TestComponent.java index b0c85d66..ce6b14f4 100644 --- a/sample-github/src/test/java/com/arellomobile/mvp/sample/github/test/TestComponent.java +++ b/sample-github/src/test/java/com/arellomobile/mvp/sample/github/test/TestComponent.java @@ -12,27 +12,33 @@ import org.robolectric.RuntimeEnvironment; public class TestComponent implements AppComponent { - @Override public Context getContext() { - return RuntimeEnvironment.application; - } + @Override + public Context getContext() { + return RuntimeEnvironment.application; + } - @Override public GithubService getAuthService() { - return null; - } + @Override + public GithubService getAuthService() { + return null; + } - @Override public Bus getBus() { - return null; - } + @Override + public Bus getBus() { + return null; + } - @Override public void inject(SignInPresenter presenter) { + @Override + public void inject(SignInPresenter presenter) { - } + } - @Override public void inject(RepositoriesPresenter repositoriesPresenter) { + @Override + public void inject(RepositoriesPresenter repositoriesPresenter) { - } + } - @Override public void inject(RepositoryLikesPresenter repositoryLikesPresenter) { + @Override + public void inject(RepositoryLikesPresenter repositoryLikesPresenter) { - } + } } diff --git a/sample-github/src/test/java/com/arellomobile/mvp/sample/github/test/TestComponentRule.java b/sample-github/src/test/java/com/arellomobile/mvp/sample/github/test/TestComponentRule.java index 0c892e72..8fd27388 100644 --- a/sample-github/src/test/java/com/arellomobile/mvp/sample/github/test/TestComponentRule.java +++ b/sample-github/src/test/java/com/arellomobile/mvp/sample/github/test/TestComponentRule.java @@ -1,11 +1,9 @@ package com.arellomobile.mvp.sample.github.test; -import android.app.Application; import android.support.annotation.NonNull; import com.arellomobile.mvp.sample.github.app.GithubApp; import com.arellomobile.mvp.sample.github.di.AppComponent; -import com.arellomobile.mvp.sample.github.di.modules.GithubModule; import org.junit.rules.TestRule; import org.junit.runner.Description; @@ -13,22 +11,24 @@ public class TestComponentRule implements TestRule { - private AppComponent appComponent; - - public TestComponentRule() { - appComponent = new TestComponent(); - } - - public TestComponentRule(@NonNull AppComponent component) { - this.appComponent = component; - } - - @Override public Statement apply(Statement base, Description description) { - return new Statement() { - @Override public void evaluate() throws Throwable { - GithubApp.setAppComponent(appComponent); - base.evaluate(); - } - }; - } + private AppComponent appComponent; + + public TestComponentRule() { + appComponent = new TestComponent(); + } + + public TestComponentRule(@NonNull AppComponent component) { + this.appComponent = component; + } + + @Override + public Statement apply(Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + GithubApp.setAppComponent(appComponent); + base.evaluate(); + } + }; + } } diff --git a/sample-kotlin/build.gradle b/sample-kotlin/build.gradle index de938442..9c8843c0 100644 --- a/sample-kotlin/build.gradle +++ b/sample-kotlin/build.gradle @@ -40,9 +40,9 @@ android { dependencies { compile 'com.android.support:appcompat-v7:25.0.0' - compile 'com.arello-mobile:moxy:1.4.4' - compile 'com.arello-mobile:moxy-app-compat:1.4.4' - kapt 'com.arello-mobile:moxy-compiler:1.4.4' + compile 'com.arello-mobile:moxy:1.4.5' + compile 'com.arello-mobile:moxy-app-compat:1.4.5' + kapt 'com.arello-mobile:moxy-compiler:1.4.5' compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" }