diff --git a/presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/presenter/UserDetailsPresenterTest.java b/presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/presenter/UserDetailsPresenterTest.java index e8360659..9af48b41 100644 --- a/presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/presenter/UserDetailsPresenterTest.java +++ b/presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/presenter/UserDetailsPresenterTest.java @@ -19,8 +19,8 @@ import com.fernandocejas.android10.sample.domain.interactor.GetUserDetails; import com.fernandocejas.android10.sample.domain.interactor.GetUserDetails.Params; import com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper; -import com.fernandocejas.android10.sample.presentation.presenter.UserDetailsPresenter; -import com.fernandocejas.android10.sample.presentation.view.UserDetailsView; +import com.fernandocejas.android10.sample.presentation.mvp.presenter.UserDetailsPresenter; +import com.fernandocejas.android10.sample.presentation.mvp.view.UserDetailsView; import io.reactivex.observers.DisposableObserver; import org.junit.Before; import org.junit.Test; diff --git a/presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/presenter/UserListPresenterTest.java b/presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/presenter/UserListPresenterTest.java index 35c99855..e6493083 100644 --- a/presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/presenter/UserListPresenterTest.java +++ b/presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/presenter/UserListPresenterTest.java @@ -18,8 +18,8 @@ import android.content.Context; import com.fernandocejas.android10.sample.domain.interactor.GetUserList; import com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper; -import com.fernandocejas.android10.sample.presentation.presenter.UserListPresenter; -import com.fernandocejas.android10.sample.presentation.view.UserListView; +import com.fernandocejas.android10.sample.presentation.mvp.presenter.UserListPresenter; +import com.fernandocejas.android10.sample.presentation.mvp.view.UserListView; import io.reactivex.observers.DisposableObserver; import org.junit.Before; import org.junit.Test; diff --git a/presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/view/activity/UserDetailsActivityTest.java b/presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/view/activity/UserDetailsActivityTest.java index 812c4e0d..9f2124b8 100644 --- a/presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/view/activity/UserDetailsActivityTest.java +++ b/presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/view/activity/UserDetailsActivityTest.java @@ -19,7 +19,7 @@ import android.content.Intent; import android.test.ActivityInstrumentationTestCase2; import com.fernandocejas.android10.sample.presentation.R; -import com.fernandocejas.android10.sample.presentation.view.activity.UserDetailsActivity; +import com.fernandocejas.android10.sample.presentation.ui.activity.UserDetailsActivity; import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.assertion.ViewAssertions.matches; diff --git a/presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/view/activity/UserListActivityTest.java b/presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/view/activity/UserListActivityTest.java index 187e6f8d..3ec8e3ca 100644 --- a/presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/view/activity/UserListActivityTest.java +++ b/presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/view/activity/UserListActivityTest.java @@ -19,7 +19,7 @@ import android.content.Intent; import android.test.ActivityInstrumentationTestCase2; import com.fernandocejas.android10.sample.presentation.R; -import com.fernandocejas.android10.sample.presentation.view.activity.UserListActivity; +import com.fernandocejas.android10.sample.presentation.ui.activity.UserListActivity; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; diff --git a/presentation/src/main/AndroidManifest.xml b/presentation/src/main/AndroidManifest.xml index 1aaea4ca..2fbe6601 100644 --- a/presentation/src/main/AndroidManifest.xml +++ b/presentation/src/main/AndroidManifest.xml @@ -13,7 +13,7 @@ android:theme="@style/AppTheme"> @@ -22,12 +22,12 @@ diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/components/ApplicationComponent.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/components/ApplicationComponent.java index 23e08b1d..a3e545ed 100644 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/components/ApplicationComponent.java +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/components/ApplicationComponent.java @@ -20,7 +20,7 @@ import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor; import com.fernandocejas.android10.sample.domain.repository.UserRepository; import com.fernandocejas.android10.sample.presentation.internal.di.modules.ApplicationModule; -import com.fernandocejas.android10.sample.presentation.view.activity.BaseActivity; +import com.fernandocejas.android10.sample.presentation.ui.activity.BaseActivity; import dagger.Component; import javax.inject.Singleton; diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/components/UserComponent.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/components/UserComponent.java index ef4872f5..3fad93a2 100644 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/components/UserComponent.java +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/components/UserComponent.java @@ -18,8 +18,8 @@ import com.fernandocejas.android10.sample.presentation.internal.di.PerActivity; import com.fernandocejas.android10.sample.presentation.internal.di.modules.ActivityModule; import com.fernandocejas.android10.sample.presentation.internal.di.modules.UserModule; -import com.fernandocejas.android10.sample.presentation.view.fragment.UserDetailsFragment; -import com.fernandocejas.android10.sample.presentation.view.fragment.UserListFragment; +import com.fernandocejas.android10.sample.presentation.ui.fragment.UserDetailsFragment; +import com.fernandocejas.android10.sample.presentation.ui.fragment.UserListFragment; import dagger.Component; /** diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/presenter/BasePresenter.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/presenter/BasePresenter.java new file mode 100644 index 00000000..f3b92d9b --- /dev/null +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/presenter/BasePresenter.java @@ -0,0 +1,38 @@ +package com.fernandocejas.android10.sample.presentation.mvp.presenter; + +import android.support.annotation.NonNull; + +import com.fernandocejas.android10.sample.presentation.mvp.view.View; + +public abstract class BasePresenter implements Presenter { + + protected VIEW view; + + @Override + public void attachView(@NonNull VIEW view) { + this.view = view; + onViewAttached(); + } + + @Override + public void detachView() { + onViewDetached(); + this.view = null; + } + + @Override + public void resume() { + } + + @Override + public void pause() { + } + + public abstract void refreshData(); + + protected void onViewAttached() { + } + + protected void onViewDetached() { + } +} diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/presenter/Presenter.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/presenter/Presenter.java new file mode 100644 index 00000000..d8e5a0c1 --- /dev/null +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/presenter/Presenter.java @@ -0,0 +1,53 @@ +/** + * Copyright (C) 2015 Fernando Cejas Open Source Project + *

+ * 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.fernandocejas.android10.sample.presentation.mvp.presenter; + +import com.fernandocejas.android10.sample.presentation.mvp.view.View; + +/** + * Interface representing a Presenter in a model view presenter (MVP) pattern. + */ +public interface Presenter { + + //TODO docs + void attachView(VIEW view); + + //TODO docs + void detachView(); + + /** + * Method that control the lifecycle of the view. It should be called in the view's + * (Activity or Fragment) onResume() method. + */ + void resume(); + + /** + * Method that control the lifecycle of the view. It should be called in the view's + * (Activity or Fragment) onPause() method. + */ + void pause(); + + + /** + * Method that control the lifecycle of the view. It should be called in the view's + * (Activity or Fragment) onDestroy() method. + */ + //void destroy(); + + + //TODO docs + void refreshData(); +} diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/presenter/UserDetailsPresenter.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/presenter/UserDetailsPresenter.java new file mode 100644 index 00000000..98987cd0 --- /dev/null +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/presenter/UserDetailsPresenter.java @@ -0,0 +1,107 @@ +/** + * Copyright (C) 2015 Fernando Cejas Open Source Project + *

+ * 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.fernandocejas.android10.sample.presentation.mvp.presenter; + +import com.fernandocejas.android10.sample.domain.User; +import com.fernandocejas.android10.sample.domain.exception.DefaultErrorBundle; +import com.fernandocejas.android10.sample.domain.exception.ErrorBundle; +import com.fernandocejas.android10.sample.domain.interactor.DefaultObserver; +import com.fernandocejas.android10.sample.domain.interactor.GetUserDetails; +import com.fernandocejas.android10.sample.domain.interactor.GetUserDetails.Params; +import com.fernandocejas.android10.sample.presentation.internal.di.PerActivity; +import com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper; +import com.fernandocejas.android10.sample.presentation.model.UserModel; +import com.fernandocejas.android10.sample.presentation.mvp.view.UserDetailsView; + +import javax.inject.Inject; + +/** + * {@link Presenter} that controls communication between views and models of the presentation + * layer. + */ +@PerActivity +public class UserDetailsPresenter extends BasePresenter { + + private final GetUserDetails getUserDetailsUseCase; + private final UserModelDataMapper userModelDataMapper; + + private int userId; + + @Inject + public UserDetailsPresenter(GetUserDetails getUserDetailsUseCase, + UserModelDataMapper userModelDataMapper) { + this.getUserDetailsUseCase = getUserDetailsUseCase; + this.userModelDataMapper = userModelDataMapper; + } + + @Override + protected void onViewAttached() { + super.onViewAttached(); + refreshData(); + } + + @Override + protected void onViewDetached() { + super.onViewDetached(); + this.getUserDetailsUseCase.dispose(); + } + + @Override + public void refreshData() { + view.hideRetry(); + view.showLoading(); + this.getUserDetails(userId); + } + + //TODO Ihor Vitruk: consider to use lombok library @Setter annotation + public void setUserId(int userId) { + this.userId = userId; + } + + private void getUserDetails(int userId) { + this.getUserDetailsUseCase.execute(new UserDetailsObserver(), Params.forUser(userId)); + } + + + private void showErrorMessage(ErrorBundle errorBundle) { + view.showError(errorBundle); + } + + private void showUserDetailsInView(User user) { + final UserModel userModel = this.userModelDataMapper.transform(user); + view.renderUser(userModel); + } + + private final class UserDetailsObserver extends DefaultObserver { + + @Override + public void onComplete() { + view.hideLoading(); + } + + @Override + public void onError(Throwable e) { + view.hideLoading(); + view.hideRetry(); + UserDetailsPresenter.this.showErrorMessage(new DefaultErrorBundle((Exception) e)); + } + + @Override + public void onNext(User user) { + UserDetailsPresenter.this.showUserDetailsInView(user); + } + } +} diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/presenter/UserListPresenter.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/presenter/UserListPresenter.java new file mode 100644 index 00000000..ca68abb6 --- /dev/null +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/presenter/UserListPresenter.java @@ -0,0 +1,108 @@ +/** + * Copyright (C) 2015 Fernando Cejas Open Source Project + *

+ * 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.fernandocejas.android10.sample.presentation.mvp.presenter; + +import com.fernandocejas.android10.sample.domain.User; +import com.fernandocejas.android10.sample.domain.exception.DefaultErrorBundle; +import com.fernandocejas.android10.sample.domain.interactor.DefaultObserver; +import com.fernandocejas.android10.sample.domain.interactor.GetUserList; +import com.fernandocejas.android10.sample.presentation.internal.di.PerActivity; +import com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper; +import com.fernandocejas.android10.sample.presentation.model.UserModel; +import com.fernandocejas.android10.sample.presentation.mvp.view.UserListView; + +import java.util.Collection; +import java.util.List; + +import javax.inject.Inject; + +/** + * {@link Presenter} that controls communication between views and models of the presentation + * layer. + */ +@PerActivity +public class UserListPresenter extends BasePresenter { + + private final GetUserList getUserListUseCase; + private final UserModelDataMapper userModelDataMapper; + + @Inject + public UserListPresenter(GetUserList getUserListUserCase, + UserModelDataMapper userModelDataMapper) { + this.getUserListUseCase = getUserListUserCase; + this.userModelDataMapper = userModelDataMapper; + } + + @Override + protected void onViewAttached() { + super.onViewAttached(); + refreshData(); + } + + @Override + protected void onViewDetached() { + super.onViewDetached(); + this.getUserListUseCase.dispose(); + } + + @Override + public void refreshData() { + this.loadUserList(); + } + + public void onUserClicked(UserModel userModel) { + view.viewUser(userModel); + } + + /** + * Loads all users. + */ + private void loadUserList() { + view.hideRetry(); + view.showLoading(); + this.getUserList(); + } + + private void showUsersCollectionInView(Collection usersCollection) { + final Collection userModelsCollection = + this.userModelDataMapper.transform(usersCollection); + view.renderUserList(userModelsCollection); + } + + private void getUserList() { + this.getUserListUseCase.execute(new UserListObserver(), null); + } + + private final class UserListObserver extends DefaultObserver> { + + @Override + public void onComplete() { + view.hideLoading(); + } + + @Override + public void onError(Throwable e) { + view.hideLoading(); + view.showError(new DefaultErrorBundle((Exception) e)); + view.showRetry(); + } + + @Override + public void onNext(List users) { + UserListPresenter.this.showUsersCollectionInView(users); + } + } +} diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/UserDetailsView.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/view/UserDetailsView.java similarity index 80% rename from presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/UserDetailsView.java rename to presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/view/UserDetailsView.java index a4f72f9f..de430f0e 100644 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/UserDetailsView.java +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/view/UserDetailsView.java @@ -2,7 +2,7 @@ * Copyright (C) 2014 android10.org. All rights reserved. * @author Fernando Cejas (the android10 coder) */ -package com.fernandocejas.android10.sample.presentation.view; +package com.fernandocejas.android10.sample.presentation.mvp.view; import com.fernandocejas.android10.sample.presentation.model.UserModel; @@ -10,7 +10,7 @@ * Interface representing a View in a model view presenter (MVP) pattern. * In this case is used as a view representing a user profile. */ -public interface UserDetailsView extends LoadDataView { +public interface UserDetailsView extends View { /** * Render a user in the UI. * diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/UserListView.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/view/UserListView.java similarity index 86% rename from presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/UserListView.java rename to presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/view/UserListView.java index 6a850775..7fd6a706 100644 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/UserListView.java +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/view/UserListView.java @@ -2,7 +2,7 @@ * Copyright (C) 2014 android10.org. All rights reserved. * @author Fernando Cejas (the android10 coder) */ -package com.fernandocejas.android10.sample.presentation.view; +package com.fernandocejas.android10.sample.presentation.mvp.view; import com.fernandocejas.android10.sample.presentation.model.UserModel; import java.util.Collection; @@ -11,7 +11,7 @@ * Interface representing a View in a model view presenter (MVP) pattern. * In this case is used as a view representing a list of {@link UserModel}. */ -public interface UserListView extends LoadDataView { +public interface UserListView extends View { /** * Render a user list in the UI. * diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/view/View.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/view/View.java new file mode 100644 index 00000000..208fa062 --- /dev/null +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/view/View.java @@ -0,0 +1,42 @@ +/** + * Copyright (C) 2014 android10.org. All rights reserved. + * + * @author Fernando Cejas (the android10 coder) + */ +package com.fernandocejas.android10.sample.presentation.mvp.view; + +import com.fernandocejas.android10.sample.domain.exception.ErrorBundle; + +/** + * Interface representing a View that will use to load data. + */ +public interface View { + /** + * Show a view with a progress bar indicating a loading process. + */ + void showLoading(); + + /** + * Hide a loading view. + */ + void hideLoading(); + + /** + * Show a retry view in case of an error when retrieving data. + */ + void showRetry(); + + /** + * Hide a retry view shown if there was an error when retrieving data. + */ + void hideRetry(); + + /** + * Show an error message + * + * @param message A string representing an error. + */ + void showError(String message); + + void showError(ErrorBundle errorBundle); +} diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/view/impl/UserDetailsViewImpl.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/view/impl/UserDetailsViewImpl.java new file mode 100644 index 00000000..c5817d12 --- /dev/null +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/view/impl/UserDetailsViewImpl.java @@ -0,0 +1,12 @@ +package com.fernandocejas.android10.sample.presentation.mvp.view.impl; + +import android.app.Fragment; + +import com.fernandocejas.android10.sample.presentation.mvp.view.UserDetailsView; + +public abstract class UserDetailsViewImpl extends ViewImpl implements UserDetailsView { + + public UserDetailsViewImpl(Fragment fragment) { + super(fragment); + } +} diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/view/impl/UserListViewImpl.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/view/impl/UserListViewImpl.java new file mode 100644 index 00000000..9ab040f4 --- /dev/null +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/view/impl/UserListViewImpl.java @@ -0,0 +1,12 @@ +package com.fernandocejas.android10.sample.presentation.mvp.view.impl; + +import android.app.Fragment; + +import com.fernandocejas.android10.sample.presentation.mvp.view.UserListView; + +public abstract class UserListViewImpl extends ViewImpl implements UserListView { + + public UserListViewImpl(Fragment fragment) { + super(fragment); + } +} diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/view/impl/ViewImpl.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/view/impl/ViewImpl.java new file mode 100644 index 00000000..25141df8 --- /dev/null +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mvp/view/impl/ViewImpl.java @@ -0,0 +1,125 @@ +/** + * Copyright (C) 2014 android10.org. All rights reserved. + * + * @author Fernando Cejas (the android10 coder) + */ +package com.fernandocejas.android10.sample.presentation.mvp.view.impl; + +import android.app.Activity; +import android.app.Fragment; +import android.app.Service; +import android.content.Context; +import android.widget.Toast; + +import com.fernandocejas.android10.sample.domain.exception.ErrorBundle; +import com.fernandocejas.android10.sample.presentation.exception.ErrorMessageFactory; +import com.fernandocejas.android10.sample.presentation.mvp.view.View; +import com.fernandocejas.android10.sample.presentation.ui.component.ProgressDialogHelper; + +public abstract class ViewImpl implements View { + + private Activity activity; + + private Fragment fragment; + + private android.view.View view; + + private Service service; + + private ProgressDialogHelper progressDialogHelper; + + public ViewImpl(Activity activity) { + this.activity = activity; + init(); + } + + public ViewImpl(Fragment fragment) { + this.fragment = fragment; + init(); + } + + public ViewImpl(android.view.View view) { + this.view = view; + init(); + } + + public ViewImpl(Service service) { + this.service = service; + init(); + } + + @Override + public void showLoading() { + Context context = getContext(); + if (context == null) { + return; + } + progressDialogHelper.showProgress(context); + } + + @Override + public void hideLoading() { + progressDialogHelper.hideProgress(); + } + + @Override + public void showRetry() { + Context context = getContext(); + if (context == null) { + return; + } + progressDialogHelper.showProgress(context); + } + + @Override + public void hideRetry() { + progressDialogHelper.hideProgress(); + } + + @Override + public void showError(String message) { + showToastMessage(message); + } + + @Override + public void showError(ErrorBundle errorBundle) { + Context context = getContext(); + if (context == null) { + return; + } + String errorMessage = ErrorMessageFactory.create(getContext(), + errorBundle.getException()); + showToastMessage(errorMessage); + } + + private void init() { + progressDialogHelper = new ProgressDialogHelper(); + } + + private Context getContext() { + if (activity != null) { + return activity; + } else if (fragment != null) { + return fragment.getActivity(); + } else if (view != null) { + return view.getContext(); + } else if (service != null) { + return service; + } + return null; + } + + /** + * Shows a {@link android.widget.Toast} message. + * + * @param message An string representing a message to be shown. + */ + //TODO replace with SnackBar? + private void showToastMessage(String message) { + Context context = getContext(); + if (context == null) { + return; + } + Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); + } +} diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/navigation/Navigator.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/navigation/Navigator.java index fb2577aa..17921fac 100644 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/navigation/Navigator.java +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/navigation/Navigator.java @@ -17,8 +17,8 @@ import android.content.Context; import android.content.Intent; -import com.fernandocejas.android10.sample.presentation.view.activity.UserDetailsActivity; -import com.fernandocejas.android10.sample.presentation.view.activity.UserListActivity; +import com.fernandocejas.android10.sample.presentation.ui.activity.UserDetailsActivity; +import com.fernandocejas.android10.sample.presentation.ui.activity.UserListActivity; import javax.inject.Inject; import javax.inject.Singleton; diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/presenter/Presenter.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/presenter/Presenter.java deleted file mode 100644 index 3768fe4f..00000000 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/presenter/Presenter.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (C) 2015 Fernando Cejas Open Source Project - * - * 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.fernandocejas.android10.sample.presentation.presenter; - -/** - * Interface representing a Presenter in a model view presenter (MVP) pattern. - */ -public interface Presenter { - /** - * Method that control the lifecycle of the view. It should be called in the view's - * (Activity or Fragment) onResume() method. - */ - void resume(); - - /** - * Method that control the lifecycle of the view. It should be called in the view's - * (Activity or Fragment) onPause() method. - */ - void pause(); - - /** - * Method that control the lifecycle of the view. It should be called in the view's - * (Activity or Fragment) onDestroy() method. - */ - void destroy(); -} diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/presenter/UserDetailsPresenter.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/presenter/UserDetailsPresenter.java deleted file mode 100644 index 4e3c3bf7..00000000 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/presenter/UserDetailsPresenter.java +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Copyright (C) 2015 Fernando Cejas Open Source Project - * - * 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.fernandocejas.android10.sample.presentation.presenter; - -import android.support.annotation.NonNull; -import com.fernandocejas.android10.sample.domain.User; -import com.fernandocejas.android10.sample.domain.exception.DefaultErrorBundle; -import com.fernandocejas.android10.sample.domain.exception.ErrorBundle; -import com.fernandocejas.android10.sample.domain.interactor.DefaultObserver; -import com.fernandocejas.android10.sample.domain.interactor.GetUserDetails; -import com.fernandocejas.android10.sample.domain.interactor.GetUserDetails.Params; -import com.fernandocejas.android10.sample.presentation.exception.ErrorMessageFactory; -import com.fernandocejas.android10.sample.presentation.internal.di.PerActivity; -import com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper; -import com.fernandocejas.android10.sample.presentation.model.UserModel; -import com.fernandocejas.android10.sample.presentation.view.UserDetailsView; -import javax.inject.Inject; - -/** - * {@link Presenter} that controls communication between views and models of the presentation - * layer. - */ -@PerActivity -public class UserDetailsPresenter implements Presenter { - - private UserDetailsView viewDetailsView; - - private final GetUserDetails getUserDetailsUseCase; - private final UserModelDataMapper userModelDataMapper; - - @Inject - public UserDetailsPresenter(GetUserDetails getUserDetailsUseCase, - UserModelDataMapper userModelDataMapper) { - this.getUserDetailsUseCase = getUserDetailsUseCase; - this.userModelDataMapper = userModelDataMapper; - } - - public void setView(@NonNull UserDetailsView view) { - this.viewDetailsView = view; - } - - @Override public void resume() {} - - @Override public void pause() {} - - @Override public void destroy() { - this.getUserDetailsUseCase.dispose(); - this.viewDetailsView = null; - } - - /** - * Initializes the presenter by showing/hiding proper views - * and retrieving user details. - */ - public void initialize(int userId) { - this.hideViewRetry(); - this.showViewLoading(); - this.getUserDetails(userId); - } - - private void getUserDetails(int userId) { - this.getUserDetailsUseCase.execute(new UserDetailsObserver(), Params.forUser(userId)); - } - - private void showViewLoading() { - this.viewDetailsView.showLoading(); - } - - private void hideViewLoading() { - this.viewDetailsView.hideLoading(); - } - - private void showViewRetry() { - this.viewDetailsView.showRetry(); - } - - private void hideViewRetry() { - this.viewDetailsView.hideRetry(); - } - - private void showErrorMessage(ErrorBundle errorBundle) { - String errorMessage = ErrorMessageFactory.create(this.viewDetailsView.context(), - errorBundle.getException()); - this.viewDetailsView.showError(errorMessage); - } - - private void showUserDetailsInView(User user) { - final UserModel userModel = this.userModelDataMapper.transform(user); - this.viewDetailsView.renderUser(userModel); - } - - private final class UserDetailsObserver extends DefaultObserver { - - @Override public void onComplete() { - UserDetailsPresenter.this.hideViewLoading(); - } - - @Override public void onError(Throwable e) { - UserDetailsPresenter.this.hideViewLoading(); - UserDetailsPresenter.this.showErrorMessage(new DefaultErrorBundle((Exception) e)); - UserDetailsPresenter.this.showViewRetry(); - } - - @Override public void onNext(User user) { - UserDetailsPresenter.this.showUserDetailsInView(user); - } - } -} diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/presenter/UserListPresenter.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/presenter/UserListPresenter.java deleted file mode 100644 index f17a460d..00000000 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/presenter/UserListPresenter.java +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Copyright (C) 2015 Fernando Cejas Open Source Project - * - * 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.fernandocejas.android10.sample.presentation.presenter; - -import android.support.annotation.NonNull; -import com.fernandocejas.android10.sample.domain.User; -import com.fernandocejas.android10.sample.domain.exception.DefaultErrorBundle; -import com.fernandocejas.android10.sample.domain.exception.ErrorBundle; -import com.fernandocejas.android10.sample.domain.interactor.DefaultObserver; -import com.fernandocejas.android10.sample.domain.interactor.GetUserList; -import com.fernandocejas.android10.sample.presentation.exception.ErrorMessageFactory; -import com.fernandocejas.android10.sample.presentation.internal.di.PerActivity; -import com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper; -import com.fernandocejas.android10.sample.presentation.model.UserModel; -import com.fernandocejas.android10.sample.presentation.view.UserListView; -import java.util.Collection; -import java.util.List; -import javax.inject.Inject; - -/** - * {@link Presenter} that controls communication between views and models of the presentation - * layer. - */ -@PerActivity -public class UserListPresenter implements Presenter { - - private UserListView viewListView; - - private final GetUserList getUserListUseCase; - private final UserModelDataMapper userModelDataMapper; - - @Inject - public UserListPresenter(GetUserList getUserListUserCase, - UserModelDataMapper userModelDataMapper) { - this.getUserListUseCase = getUserListUserCase; - this.userModelDataMapper = userModelDataMapper; - } - - public void setView(@NonNull UserListView view) { - this.viewListView = view; - } - - @Override public void resume() {} - - @Override public void pause() {} - - @Override public void destroy() { - this.getUserListUseCase.dispose(); - this.viewListView = null; - } - - /** - * Initializes the presenter by start retrieving the user list. - */ - public void initialize() { - this.loadUserList(); - } - - /** - * Loads all users. - */ - private void loadUserList() { - this.hideViewRetry(); - this.showViewLoading(); - this.getUserList(); - } - - public void onUserClicked(UserModel userModel) { - this.viewListView.viewUser(userModel); - } - - private void showViewLoading() { - this.viewListView.showLoading(); - } - - private void hideViewLoading() { - this.viewListView.hideLoading(); - } - - private void showViewRetry() { - this.viewListView.showRetry(); - } - - private void hideViewRetry() { - this.viewListView.hideRetry(); - } - - private void showErrorMessage(ErrorBundle errorBundle) { - String errorMessage = ErrorMessageFactory.create(this.viewListView.context(), - errorBundle.getException()); - this.viewListView.showError(errorMessage); - } - - private void showUsersCollectionInView(Collection usersCollection) { - final Collection userModelsCollection = - this.userModelDataMapper.transform(usersCollection); - this.viewListView.renderUserList(userModelsCollection); - } - - private void getUserList() { - this.getUserListUseCase.execute(new UserListObserver(), null); - } - - private final class UserListObserver extends DefaultObserver> { - - @Override public void onComplete() { - UserListPresenter.this.hideViewLoading(); - } - - @Override public void onError(Throwable e) { - UserListPresenter.this.hideViewLoading(); - UserListPresenter.this.showErrorMessage(new DefaultErrorBundle((Exception) e)); - UserListPresenter.this.showViewRetry(); - } - - @Override public void onNext(List users) { - UserListPresenter.this.showUsersCollectionInView(users); - } - } -} diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/BaseActivity.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/activity/BaseActivity.java similarity index 96% rename from presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/BaseActivity.java rename to presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/activity/BaseActivity.java index 70ee8ca0..b126ec66 100644 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/BaseActivity.java +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/activity/BaseActivity.java @@ -1,4 +1,4 @@ -package com.fernandocejas.android10.sample.presentation.view.activity; +package com.fernandocejas.android10.sample.presentation.ui.activity; import android.app.Activity; import android.app.Fragment; diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/MainActivity.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/activity/MainActivity.java similarity index 90% rename from presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/MainActivity.java rename to presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/activity/MainActivity.java index 9f1530b9..6c7a7de3 100644 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/MainActivity.java +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/activity/MainActivity.java @@ -1,4 +1,4 @@ -package com.fernandocejas.android10.sample.presentation.view.activity; +package com.fernandocejas.android10.sample.presentation.ui.activity; import android.os.Bundle; import android.widget.Button; diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/UserDetailsActivity.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/activity/UserDetailsActivity.java similarity index 93% rename from presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/UserDetailsActivity.java rename to presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/activity/UserDetailsActivity.java index f13ecf22..44dcda32 100644 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/UserDetailsActivity.java +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/activity/UserDetailsActivity.java @@ -3,7 +3,7 @@ * * @author Fernando Cejas (the android10 coder) */ -package com.fernandocejas.android10.sample.presentation.view.activity; +package com.fernandocejas.android10.sample.presentation.ui.activity; import android.content.Context; import android.content.Intent; @@ -13,7 +13,7 @@ import com.fernandocejas.android10.sample.presentation.internal.di.HasComponent; import com.fernandocejas.android10.sample.presentation.internal.di.components.DaggerUserComponent; import com.fernandocejas.android10.sample.presentation.internal.di.components.UserComponent; -import com.fernandocejas.android10.sample.presentation.view.fragment.UserDetailsFragment; +import com.fernandocejas.android10.sample.presentation.ui.fragment.UserDetailsFragment; /** * Activity that shows details of a certain user. diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/UserListActivity.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/activity/UserListActivity.java similarity index 91% rename from presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/UserListActivity.java rename to presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/activity/UserListActivity.java index fb1bdea8..6243a634 100644 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/UserListActivity.java +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/activity/UserListActivity.java @@ -3,7 +3,7 @@ * * @author Fernando Cejas (the android10 coder) */ -package com.fernandocejas.android10.sample.presentation.view.activity; +package com.fernandocejas.android10.sample.presentation.ui.activity; import android.content.Context; import android.content.Intent; @@ -14,7 +14,7 @@ import com.fernandocejas.android10.sample.presentation.internal.di.components.DaggerUserComponent; import com.fernandocejas.android10.sample.presentation.internal.di.components.UserComponent; import com.fernandocejas.android10.sample.presentation.model.UserModel; -import com.fernandocejas.android10.sample.presentation.view.fragment.UserListFragment; +import com.fernandocejas.android10.sample.presentation.ui.fragment.UserListFragment; /** * Activity that shows a list of Users. diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/adapter/UsersAdapter.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/adapter/UsersAdapter.java similarity index 97% rename from presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/adapter/UsersAdapter.java rename to presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/adapter/UsersAdapter.java index 3b13dc58..b00cc435 100644 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/adapter/UsersAdapter.java +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/adapter/UsersAdapter.java @@ -2,7 +2,7 @@ * Copyright (C) 2014 android10.org. All rights reserved. * @author Fernando Cejas (the android10 coder) */ -package com.fernandocejas.android10.sample.presentation.view.adapter; +package com.fernandocejas.android10.sample.presentation.ui.adapter; import android.content.Context; import android.support.v7.widget.RecyclerView; diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/adapter/UsersLayoutManager.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/adapter/UsersLayoutManager.java similarity index 86% rename from presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/adapter/UsersLayoutManager.java rename to presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/adapter/UsersLayoutManager.java index a7facf5b..4174c299 100644 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/adapter/UsersLayoutManager.java +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/adapter/UsersLayoutManager.java @@ -2,7 +2,7 @@ * Copyright (C) 2014 android10.org. All rights reserved. * @author Fernando Cejas (the android10 coder) */ -package com.fernandocejas.android10.sample.presentation.view.adapter; +package com.fernandocejas.android10.sample.presentation.ui.adapter; import android.content.Context; import android.support.v7.widget.LinearLayoutManager; diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/component/AutoLoadImageView.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/component/AutoLoadImageView.java similarity index 99% rename from presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/component/AutoLoadImageView.java rename to presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/component/AutoLoadImageView.java index 45cecd3d..57a52e7b 100644 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/component/AutoLoadImageView.java +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/component/AutoLoadImageView.java @@ -3,7 +3,7 @@ * * @author Fernando Cejas (the android10 coder) */ -package com.fernandocejas.android10.sample.presentation.view.component; +package com.fernandocejas.android10.sample.presentation.ui.component; import android.app.Activity; import android.content.Context; diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/component/ProgressDialogHelper.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/component/ProgressDialogHelper.java new file mode 100644 index 00000000..87b0a670 --- /dev/null +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/component/ProgressDialogHelper.java @@ -0,0 +1,51 @@ +package com.fernandocejas.android10.sample.presentation.ui.component; + +import android.app.ProgressDialog; +import android.content.Context; + +public class ProgressDialogHelper { + + private ProgressDialog dialog; + + private volatile int progressesCount = 0; + + public void showProgress(Context context) { + showProgress(context, null); + } + + public void showProgress(Context context, String message) { + showProgress(context, message, null); + } + + public void showProgress(Context context, String message, String title) { + if (context == null) { + return; + } + + if (!inProgress()) { + dialog = new ProgressDialog(context); + if (message != null) dialog.setMessage(message); + if (title != null) dialog.setTitle(title); + dialog.setIndeterminate(true); + dialog.setCanceledOnTouchOutside(false); + dialog.show(); + } + + progressesCount++; + } + + public void hideProgress() { + progressesCount--; + if (progressesCount <= 0) { + if (dialog != null && dialog.isShowing()) { + dialog.dismiss(); + } + progressesCount = 0; + } + + } + + private boolean inProgress() { + return dialog != null && dialog.isShowing(); + } +} diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/fragment/BaseFragment.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/fragment/BaseFragment.java new file mode 100644 index 00000000..6f9c1679 --- /dev/null +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/fragment/BaseFragment.java @@ -0,0 +1,66 @@ +/** + * Copyright (C) 2014 android10.org. All rights reserved. + * + * @author Fernando Cejas (the android10 coder) + */ +package com.fernandocejas.android10.sample.presentation.ui.fragment; + +import android.app.Fragment; +import android.os.Bundle; + +import com.fernandocejas.android10.sample.presentation.internal.di.HasComponent; +import com.fernandocejas.android10.sample.presentation.mvp.presenter.Presenter; +import com.fernandocejas.android10.sample.presentation.mvp.view.View; + +/** + * Base {@link android.app.Fragment} class for every fragment in this application. + */ +public abstract class BaseFragment, VIEW extends View> extends Fragment { + + protected PRESENTER presenter; + + protected VIEW view; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + presenter = initPresenter(); + view = initView(); + } + + @Override + public void onViewCreated(android.view.View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + presenter.attachView(this.view); + } + + @Override + public void onResume() { + super.onResume(); + presenter.resume(); + } + + @Override + public void onPause() { + super.onPause(); + presenter.pause(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + presenter.detachView(); + } + + /** + * Gets a component for dependency injection by its type. + */ + @SuppressWarnings("unchecked") + protected C getComponent(Class componentType) { + return componentType.cast(((HasComponent) getActivity()).getComponent()); + } + + protected abstract PRESENTER initPresenter(); + + protected abstract VIEW initView(); +} diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/fragment/UserDetailsFragment.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/fragment/UserDetailsFragment.java new file mode 100644 index 00000000..317bff84 --- /dev/null +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/fragment/UserDetailsFragment.java @@ -0,0 +1,147 @@ +/** + * Copyright (C) 2014 android10.org. All rights reserved. + * + * @author Fernando Cejas (the android10 coder) + */ +package com.fernandocejas.android10.sample.presentation.ui.fragment; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import com.fernandocejas.android10.sample.presentation.R; +import com.fernandocejas.android10.sample.presentation.internal.di.components.UserComponent; +import com.fernandocejas.android10.sample.presentation.model.UserModel; +import com.fernandocejas.android10.sample.presentation.mvp.presenter.UserDetailsPresenter; +import com.fernandocejas.android10.sample.presentation.mvp.view.UserDetailsView; +import com.fernandocejas.android10.sample.presentation.mvp.view.impl.UserDetailsViewImpl; +import com.fernandocejas.android10.sample.presentation.ui.component.AutoLoadImageView; +import com.fernandocejas.arrow.checks.Preconditions; + +import javax.inject.Inject; + +import butterknife.Bind; +import butterknife.ButterKnife; +import butterknife.OnClick; + +/** + * Fragment that shows details of a certain user. + */ +public class UserDetailsFragment extends BaseFragment { + + private static final String PARAM_USER_ID = "param_user_id"; + + @Inject + UserDetailsPresenter userDetailsPresenter; + + @Bind(R.id.iv_cover) + AutoLoadImageView iv_cover; + @Bind(R.id.tv_fullname) + TextView tv_fullname; + @Bind(R.id.tv_email) + TextView tv_email; + @Bind(R.id.tv_followers) + TextView tv_followers; + @Bind(R.id.tv_description) + TextView tv_description; + @Bind(R.id.rl_progress) + RelativeLayout rl_progress; + @Bind(R.id.rl_retry) + RelativeLayout rl_retry; + @Bind(R.id.bt_retry) + Button bt_retry; + + public static UserDetailsFragment forUser(int userId) { + final UserDetailsFragment userDetailsFragment = new UserDetailsFragment(); + final Bundle arguments = new Bundle(); + arguments.putInt(PARAM_USER_ID, userId); + userDetailsFragment.setArguments(arguments); + return userDetailsFragment; + } + + public UserDetailsFragment() { + setRetainInstance(true); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + this.getComponent(UserComponent.class).inject(this); + super.onCreate(savedInstanceState); + presenter.setUserId(currentUserId()); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + final View fragmentView = inflater.inflate(R.layout.fragment_user_details, container, false); + ButterKnife.bind(this, fragmentView); + return fragmentView; + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + ButterKnife.unbind(this); + } + + @Override + protected UserDetailsPresenter initPresenter() { + return userDetailsPresenter; + } + + @Override + protected UserDetailsView initView() { + return new UserDetailsViewImpl(this) { + @Override + public void renderUser(UserModel user) { + if (user != null) { + UserDetailsFragment.this.iv_cover.setImageUrl(user.getCoverUrl()); + UserDetailsFragment.this.tv_fullname.setText(user.getFullName()); + UserDetailsFragment.this.tv_email.setText(user.getEmail()); + UserDetailsFragment.this.tv_followers.setText(String.valueOf(user.getFollowers())); + UserDetailsFragment.this.tv_description.setText(user.getDescription()); + } + } + + @Override + public void showLoading() { + UserDetailsFragment.this.rl_progress.setVisibility(View.VISIBLE); + UserDetailsFragment.this.getActivity().setProgressBarIndeterminateVisibility(true); + } + + @Override + public void hideLoading() { + UserDetailsFragment.this.rl_progress.setVisibility(View.GONE); + UserDetailsFragment.this.getActivity().setProgressBarIndeterminateVisibility(false); + } + + @Override + public void showRetry() { + UserDetailsFragment.this.rl_retry.setVisibility(View.VISIBLE); + } + + @Override + public void hideRetry() { + UserDetailsFragment.this.rl_retry.setVisibility(View.GONE); + } + }; + } + + @OnClick(R.id.bt_retry) + void onButtonRetryClick() { + presenter.refreshData(); + } + + /** + * Get current user id from fragments arguments. + */ + private int currentUserId() { + final Bundle arguments = getArguments(); + Preconditions.checkNotNull(arguments, "Fragment arguments cannot be null"); + return arguments.getInt(PARAM_USER_ID); + } +} diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/fragment/UserListFragment.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/fragment/UserListFragment.java new file mode 100644 index 00000000..f5324504 --- /dev/null +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/ui/fragment/UserListFragment.java @@ -0,0 +1,182 @@ +/** + * Copyright (C) 2014 android10.org. All rights reserved. + * + * @author Fernando Cejas (the android10 coder) + */ +package com.fernandocejas.android10.sample.presentation.ui.fragment; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.RelativeLayout; + +import com.fernandocejas.android10.sample.presentation.R; +import com.fernandocejas.android10.sample.presentation.internal.di.components.UserComponent; +import com.fernandocejas.android10.sample.presentation.model.UserModel; +import com.fernandocejas.android10.sample.presentation.mvp.presenter.UserListPresenter; +import com.fernandocejas.android10.sample.presentation.mvp.view.UserListView; +import com.fernandocejas.android10.sample.presentation.mvp.view.impl.UserListViewImpl; +import com.fernandocejas.android10.sample.presentation.ui.adapter.UsersAdapter; +import com.fernandocejas.android10.sample.presentation.ui.adapter.UsersLayoutManager; + +import java.util.Collection; + +import javax.inject.Inject; + +import butterknife.Bind; +import butterknife.ButterKnife; +import butterknife.OnClick; + +/** + * Fragment that shows a list of Users. + */ +public class UserListFragment extends BaseFragment { + + /** + * Interface for listening user list events. + */ + public interface UserListListener { + void onUserClicked(final UserModel userModel); + } + + @Inject + UserListPresenter userListPresenter; + @Inject + UsersAdapter usersAdapter; + + @Bind(R.id.rv_users) + RecyclerView rv_users; + @Bind(R.id.rl_progress) + RelativeLayout rl_progress; + @Bind(R.id.rl_retry) + RelativeLayout rl_retry; + @Bind(R.id.bt_retry) + Button bt_retry; + + private UserListListener userListListener; + + public UserListFragment() { + setRetainInstance(true); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + if (activity instanceof UserListListener) { + this.userListListener = (UserListListener) activity; + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + this.getComponent(UserComponent.class).inject(this); + super.onCreate(savedInstanceState); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + final View fragmentView = inflater.inflate(R.layout.fragment_user_list, container, false); + ButterKnife.bind(this, fragmentView); + setupRecyclerView(); + return fragmentView; + } + + @Override + public void onResume() { + super.onResume(); + this.userListPresenter.resume(); + } + + @Override + public void onPause() { + super.onPause(); + this.userListPresenter.pause(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + rv_users.setAdapter(null); + ButterKnife.unbind(this); + } + + @Override + protected UserListPresenter initPresenter() { + return userListPresenter; + } + + @Override + protected UserListView initView() { + return new UserListViewImpl(this) { + + @Override + public void renderUserList(Collection userModelCollection) { + if (userModelCollection != null) { + UserListFragment.this.usersAdapter.setUsersCollection(userModelCollection); + } + } + + @Override + public void viewUser(UserModel userModel) { + if (UserListFragment.this.userListListener != null) { + UserListFragment.this.userListListener.onUserClicked(userModel); + } + } + + @Override + public void showLoading() { + UserListFragment.this.rl_progress.setVisibility(View.VISIBLE); + UserListFragment.this.getActivity().setProgressBarIndeterminateVisibility(true); + } + + @Override + public void hideLoading() { + UserListFragment.this.rl_progress.setVisibility(View.GONE); + UserListFragment.this.getActivity().setProgressBarIndeterminateVisibility(false); + } + + @Override + public void showRetry() { + UserListFragment.this.rl_retry.setVisibility(View.VISIBLE); + } + + @Override + public void hideRetry() { + UserListFragment.this.rl_retry.setVisibility(View.GONE); + } + }; + } + + @Override + public void onDetach() { + super.onDetach(); + this.userListListener = null; + } + + private void setupRecyclerView() { + this.usersAdapter.setOnItemClickListener(onItemClickListener); + this.rv_users.setLayoutManager(new UsersLayoutManager(getActivity())); + this.rv_users.setAdapter(usersAdapter); + } + + @OnClick(R.id.bt_retry) + void onButtonRetryClick() { + presenter.refreshData(); + } + + private UsersAdapter.OnItemClickListener onItemClickListener = + new UsersAdapter.OnItemClickListener() { + @Override + public void onUserItemClicked(UserModel userModel) { + if (UserListFragment.this.userListPresenter != null && userModel != null) { + UserListFragment.this.userListPresenter.onUserClicked(userModel); + } + } + }; +} diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/LoadDataView.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/LoadDataView.java deleted file mode 100644 index ebcfe095..00000000 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/LoadDataView.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (C) 2014 android10.org. All rights reserved. - * @author Fernando Cejas (the android10 coder) - */ -package com.fernandocejas.android10.sample.presentation.view; - -import android.content.Context; - -/** - * Interface representing a View that will use to load data. - */ -public interface LoadDataView { - /** - * Show a view with a progress bar indicating a loading process. - */ - void showLoading(); - - /** - * Hide a loading view. - */ - void hideLoading(); - - /** - * Show a retry view in case of an error when retrieving data. - */ - void showRetry(); - - /** - * Hide a retry view shown if there was an error when retrieving data. - */ - void hideRetry(); - - /** - * Show an error message - * - * @param message A string representing an error. - */ - void showError(String message); - - /** - * Get a {@link android.content.Context}. - */ - Context context(); -} diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/fragment/BaseFragment.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/fragment/BaseFragment.java deleted file mode 100644 index a312d134..00000000 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/fragment/BaseFragment.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (C) 2014 android10.org. All rights reserved. - * - * @author Fernando Cejas (the android10 coder) - */ -package com.fernandocejas.android10.sample.presentation.view.fragment; - -import android.app.Fragment; -import android.widget.Toast; -import com.fernandocejas.android10.sample.presentation.internal.di.HasComponent; - -/** - * Base {@link android.app.Fragment} class for every fragment in this application. - */ -public abstract class BaseFragment extends Fragment { - /** - * Shows a {@link android.widget.Toast} message. - * - * @param message An string representing a message to be shown. - */ - protected void showToastMessage(String message) { - Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show(); - } - - /** - * Gets a component for dependency injection by its type. - */ - @SuppressWarnings("unchecked") - protected C getComponent(Class componentType) { - return componentType.cast(((HasComponent) getActivity()).getComponent()); - } -} diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/fragment/UserDetailsFragment.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/fragment/UserDetailsFragment.java deleted file mode 100644 index 95d09a6d..00000000 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/fragment/UserDetailsFragment.java +++ /dev/null @@ -1,154 +0,0 @@ -/** - * Copyright (C) 2014 android10.org. All rights reserved. - * @author Fernando Cejas (the android10 coder) - */ -package com.fernandocejas.android10.sample.presentation.view.fragment; - -import android.content.Context; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.RelativeLayout; -import android.widget.TextView; -import butterknife.Bind; -import butterknife.ButterKnife; -import butterknife.OnClick; -import com.fernandocejas.android10.sample.presentation.R; -import com.fernandocejas.android10.sample.presentation.internal.di.components.UserComponent; -import com.fernandocejas.android10.sample.presentation.model.UserModel; -import com.fernandocejas.android10.sample.presentation.presenter.UserDetailsPresenter; -import com.fernandocejas.android10.sample.presentation.view.UserDetailsView; -import com.fernandocejas.android10.sample.presentation.view.component.AutoLoadImageView; -import com.fernandocejas.arrow.checks.Preconditions; -import javax.inject.Inject; - -/** - * Fragment that shows details of a certain user. - */ -public class UserDetailsFragment extends BaseFragment implements UserDetailsView { - private static final String PARAM_USER_ID = "param_user_id"; - - @Inject UserDetailsPresenter userDetailsPresenter; - - @Bind(R.id.iv_cover) AutoLoadImageView iv_cover; - @Bind(R.id.tv_fullname) TextView tv_fullname; - @Bind(R.id.tv_email) TextView tv_email; - @Bind(R.id.tv_followers) TextView tv_followers; - @Bind(R.id.tv_description) TextView tv_description; - @Bind(R.id.rl_progress) RelativeLayout rl_progress; - @Bind(R.id.rl_retry) RelativeLayout rl_retry; - @Bind(R.id.bt_retry) Button bt_retry; - - public static UserDetailsFragment forUser(int userId) { - final UserDetailsFragment userDetailsFragment = new UserDetailsFragment(); - final Bundle arguments = new Bundle(); - arguments.putInt(PARAM_USER_ID, userId); - userDetailsFragment.setArguments(arguments); - return userDetailsFragment; - } - - public UserDetailsFragment() { - setRetainInstance(true); - } - - @Override public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - this.getComponent(UserComponent.class).inject(this); - } - - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - final View fragmentView = inflater.inflate(R.layout.fragment_user_details, container, false); - ButterKnife.bind(this, fragmentView); - return fragmentView; - } - - @Override public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - this.userDetailsPresenter.setView(this); - if (savedInstanceState == null) { - this.loadUserDetails(); - } - } - - @Override public void onResume() { - super.onResume(); - this.userDetailsPresenter.resume(); - } - - @Override public void onPause() { - super.onPause(); - this.userDetailsPresenter.pause(); - } - - @Override public void onDestroyView() { - super.onDestroyView(); - ButterKnife.unbind(this); - } - - @Override public void onDestroy() { - super.onDestroy(); - this.userDetailsPresenter.destroy(); - } - - @Override public void renderUser(UserModel user) { - if (user != null) { - this.iv_cover.setImageUrl(user.getCoverUrl()); - this.tv_fullname.setText(user.getFullName()); - this.tv_email.setText(user.getEmail()); - this.tv_followers.setText(String.valueOf(user.getFollowers())); - this.tv_description.setText(user.getDescription()); - } - } - - @Override public void showLoading() { - this.rl_progress.setVisibility(View.VISIBLE); - this.getActivity().setProgressBarIndeterminateVisibility(true); - } - - @Override public void hideLoading() { - this.rl_progress.setVisibility(View.GONE); - this.getActivity().setProgressBarIndeterminateVisibility(false); - } - - @Override public void showRetry() { - this.rl_retry.setVisibility(View.VISIBLE); - } - - @Override public void hideRetry() { - this.rl_retry.setVisibility(View.GONE); - } - - @Override public void showError(String message) { - this.showToastMessage(message); - } - - @Override public Context context() { - return getActivity().getApplicationContext(); - } - - /** - * Load user details. - */ - private void loadUserDetails() { - if (this.userDetailsPresenter != null) { - this.userDetailsPresenter.initialize(currentUserId()); - } - } - - /** - * Get current user id from fragments arguments. - */ - private int currentUserId() { - final Bundle arguments = getArguments(); - Preconditions.checkNotNull(arguments, "Fragment arguments cannot be null"); - return arguments.getInt(PARAM_USER_ID); - } - - @OnClick(R.id.bt_retry) - void onButtonRetryClick() { - UserDetailsFragment.this.loadUserDetails(); - } -} diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/fragment/UserListFragment.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/fragment/UserListFragment.java deleted file mode 100644 index 6da47c9a..00000000 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/fragment/UserListFragment.java +++ /dev/null @@ -1,173 +0,0 @@ -/** - * Copyright (C) 2014 android10.org. All rights reserved. - * - * @author Fernando Cejas (the android10 coder) - */ -package com.fernandocejas.android10.sample.presentation.view.fragment; - -import android.app.Activity; -import android.content.Context; -import android.os.Bundle; -import android.support.v7.widget.RecyclerView; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.RelativeLayout; -import butterknife.Bind; -import butterknife.ButterKnife; -import butterknife.OnClick; -import com.fernandocejas.android10.sample.presentation.R; -import com.fernandocejas.android10.sample.presentation.internal.di.components.UserComponent; -import com.fernandocejas.android10.sample.presentation.model.UserModel; -import com.fernandocejas.android10.sample.presentation.presenter.UserListPresenter; -import com.fernandocejas.android10.sample.presentation.view.UserListView; -import com.fernandocejas.android10.sample.presentation.view.adapter.UsersAdapter; -import com.fernandocejas.android10.sample.presentation.view.adapter.UsersLayoutManager; -import java.util.Collection; -import javax.inject.Inject; - -/** - * Fragment that shows a list of Users. - */ -public class UserListFragment extends BaseFragment implements UserListView { - - /** - * Interface for listening user list events. - */ - public interface UserListListener { - void onUserClicked(final UserModel userModel); - } - - @Inject UserListPresenter userListPresenter; - @Inject UsersAdapter usersAdapter; - - @Bind(R.id.rv_users) RecyclerView rv_users; - @Bind(R.id.rl_progress) RelativeLayout rl_progress; - @Bind(R.id.rl_retry) RelativeLayout rl_retry; - @Bind(R.id.bt_retry) Button bt_retry; - - private UserListListener userListListener; - - public UserListFragment() { - setRetainInstance(true); - } - - @Override public void onAttach(Activity activity) { - super.onAttach(activity); - if (activity instanceof UserListListener) { - this.userListListener = (UserListListener) activity; - } - } - - @Override public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - this.getComponent(UserComponent.class).inject(this); - } - - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - final View fragmentView = inflater.inflate(R.layout.fragment_user_list, container, false); - ButterKnife.bind(this, fragmentView); - setupRecyclerView(); - return fragmentView; - } - - @Override public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - this.userListPresenter.setView(this); - if (savedInstanceState == null) { - this.loadUserList(); - } - } - - @Override public void onResume() { - super.onResume(); - this.userListPresenter.resume(); - } - - @Override public void onPause() { - super.onPause(); - this.userListPresenter.pause(); - } - - @Override public void onDestroyView() { - super.onDestroyView(); - rv_users.setAdapter(null); - ButterKnife.unbind(this); - } - - @Override public void onDestroy() { - super.onDestroy(); - this.userListPresenter.destroy(); - } - - @Override public void onDetach() { - super.onDetach(); - this.userListListener = null; - } - - @Override public void showLoading() { - this.rl_progress.setVisibility(View.VISIBLE); - this.getActivity().setProgressBarIndeterminateVisibility(true); - } - - @Override public void hideLoading() { - this.rl_progress.setVisibility(View.GONE); - this.getActivity().setProgressBarIndeterminateVisibility(false); - } - - @Override public void showRetry() { - this.rl_retry.setVisibility(View.VISIBLE); - } - - @Override public void hideRetry() { - this.rl_retry.setVisibility(View.GONE); - } - - @Override public void renderUserList(Collection userModelCollection) { - if (userModelCollection != null) { - this.usersAdapter.setUsersCollection(userModelCollection); - } - } - - @Override public void viewUser(UserModel userModel) { - if (this.userListListener != null) { - this.userListListener.onUserClicked(userModel); - } - } - - @Override public void showError(String message) { - this.showToastMessage(message); - } - - @Override public Context context() { - return this.getActivity().getApplicationContext(); - } - - private void setupRecyclerView() { - this.usersAdapter.setOnItemClickListener(onItemClickListener); - this.rv_users.setLayoutManager(new UsersLayoutManager(context())); - this.rv_users.setAdapter(usersAdapter); - } - - /** - * Loads all users. - */ - private void loadUserList() { - this.userListPresenter.initialize(); - } - - @OnClick(R.id.bt_retry) void onButtonRetryClick() { - UserListFragment.this.loadUserList(); - } - - private UsersAdapter.OnItemClickListener onItemClickListener = - new UsersAdapter.OnItemClickListener() { - @Override public void onUserItemClicked(UserModel userModel) { - if (UserListFragment.this.userListPresenter != null && userModel != null) { - UserListFragment.this.userListPresenter.onUserClicked(userModel); - } - } - }; -} diff --git a/presentation/src/main/res/layout/view_user_details.xml b/presentation/src/main/res/layout/view_user_details.xml index 9416c346..33b0a634 100644 --- a/presentation/src/main/res/layout/view_user_details.xml +++ b/presentation/src/main/res/layout/view_user_details.xml @@ -5,7 +5,7 @@ android:orientation="vertical" > -