From 31ea46d34606211bbeb32f226cd9e690eb4d46a1 Mon Sep 17 00:00:00 2001 From: Fernando Cejas Date: Sun, 22 May 2016 12:41:54 +0200 Subject: [PATCH 01/32] Bump Android build tools and frodo versions. --- build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 54470a60..5fd4dd6e 100644 --- a/build.gradle +++ b/build.gradle @@ -7,9 +7,9 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:1.5.0' + classpath 'com.android.tools.build:gradle:2.1.0' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' - classpath "com.fernandocejas.frodo:frodo-plugin:0.8.1" + classpath "com.fernandocejas.frodo:frodo-plugin:0.8.3" } } @@ -25,7 +25,7 @@ allprojects { task wrapper(type: Wrapper) { description 'Creates the gradle wrapper.' - gradleVersion '2.10' + gradleVersion '2.12' } task runDomainUnitTests(dependsOn: [':domain:test']) { From fcdcedfa535a2b63c755ee517eeef53e582ed42c Mon Sep 17 00:00:00 2001 From: Fernando Cejas Date: Sun, 22 May 2016 12:42:13 +0200 Subject: [PATCH 02/32] Add frodo configuration block to project. --- presentation/build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/presentation/build.gradle b/presentation/build.gradle index c6b32b06..be2b2abb 100644 --- a/presentation/build.gradle +++ b/presentation/build.gradle @@ -2,6 +2,10 @@ apply plugin: 'com.android.application' apply plugin: 'com.neenbedankt.android-apt' apply plugin: 'com.fernandocejas.frodo' +frodo { + enabled = true +} + android { def globalConfiguration = rootProject.extensions.getByName("ext") From d6615c6c05421cfcfe12a17ad10a4a9359d93ff2 Mon Sep 17 00:00:00 2001 From: Fernando Cejas Date: Fri, 1 Jul 2016 15:22:44 +0200 Subject: [PATCH 03/32] Collapse exceptions and assigned error Message on RepositoryErrorBundle class. --- .../android10/sample/data/cache/FileManager.java | 13 ++++--------- .../data/exception/RepositoryErrorBundle.java | 5 +++-- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/cache/FileManager.java b/data/src/main/java/com/fernandocejas/android10/sample/data/cache/FileManager.java index 670ee93f..2cdd43b4 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/cache/FileManager.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/cache/FileManager.java @@ -19,7 +19,6 @@ import android.content.SharedPreferences; import java.io.BufferedReader; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; @@ -48,12 +47,8 @@ public void writeToFile(File file, String fileContent) { FileWriter writer = new FileWriter(file); writer.write(fileContent); writer.close(); - } catch (FileNotFoundException e) { - e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); - } finally { - } } } @@ -78,8 +73,6 @@ public String readFileContent(File file) { } bufferedReader.close(); fileReader.close(); - } catch (FileNotFoundException e) { - e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } @@ -105,12 +98,14 @@ public boolean exists(File file) { * * @param directory The directory which its content will be deleted. */ - public void clearDirectory(File directory) { + public boolean clearDirectory(File directory) { + boolean result = false; if (directory.exists()) { for (File file : directory.listFiles()) { - file.delete(); + result = file.delete(); } } + return result; } /** diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundle.java b/data/src/main/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundle.java index c51144c4..89c6f37a 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundle.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundle.java @@ -16,6 +16,7 @@ package com.fernandocejas.android10.sample.data.exception; import com.fernandocejas.android10.sample.domain.exception.ErrorBundle; +import com.fernandocejas.frodo.core.strings.Strings; /** * Wrapper around Exceptions used to manage errors in the repository. @@ -35,9 +36,9 @@ public Exception getException() { @Override public String getErrorMessage() { - String message = ""; + String message = Strings.EMPTY; if (this.exception != null) { - this.exception.getMessage(); + message = this.exception.getMessage(); } return message; } From 51e57bd62108348841ddd70324dd73f47d7bd70b Mon Sep 17 00:00:00 2001 From: Fernando Cejas Date: Fri, 1 Jul 2016 15:34:58 +0200 Subject: [PATCH 04/32] Minor refactor. --- .../sample/data/exception/RepositoryErrorBundle.java | 5 ++--- .../fernandocejas/android10/sample/data/net/RestApiImpl.java | 4 ++-- .../data/repository/datasource/CloudUserDataStore.java | 4 ++-- .../sample/data/repository/datasource/DiskUserDataStore.java | 4 ++-- .../android10/sample/data/ApplicationTestCase.java | 2 +- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundle.java b/data/src/main/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundle.java index 89c6f37a..c232adbe 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundle.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundle.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -16,7 +16,6 @@ package com.fernandocejas.android10.sample.data.exception; import com.fernandocejas.android10.sample.domain.exception.ErrorBundle; -import com.fernandocejas.frodo.core.strings.Strings; /** * Wrapper around Exceptions used to manage errors in the repository. @@ -36,7 +35,7 @@ public Exception getException() { @Override public String getErrorMessage() { - String message = Strings.EMPTY; + String message = ""; if (this.exception != null) { message = this.exception.getMessage(); } diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApiImpl.java b/data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApiImpl.java index 6b15518b..1450000f 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApiImpl.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApiImpl.java @@ -92,11 +92,11 @@ public RestApiImpl(Context context, UserEntityJsonMapper userEntityJsonMapper) { } private String getUserEntitiesFromApi() throws MalformedURLException { - return ApiConnection.createGET(RestApi.API_URL_GET_USER_LIST).requestSyncCall(); + return ApiConnection.createGET(API_URL_GET_USER_LIST).requestSyncCall(); } private String getUserDetailsFromApi(int userId) throws MalformedURLException { - String apiUrl = RestApi.API_URL_GET_USER_DETAILS + userId + ".json"; + String apiUrl = API_URL_GET_USER_DETAILS + userId + ".json"; return ApiConnection.createGET(apiUrl).requestSyncCall(); } diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/CloudUserDataStore.java b/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/CloudUserDataStore.java index 579995bd..a5eda5ad 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/CloudUserDataStore.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/CloudUserDataStore.java @@ -25,7 +25,7 @@ /** * {@link UserDataStore} implementation based on connections to the api (Cloud). */ -public class CloudUserDataStore implements UserDataStore { +class CloudUserDataStore implements UserDataStore { private final RestApi restApi; private final UserCache userCache; @@ -42,7 +42,7 @@ public class CloudUserDataStore implements UserDataStore { * @param restApi The {@link RestApi} implementation to use. * @param userCache A {@link UserCache} to cache data retrieved from the api. */ - public CloudUserDataStore(RestApi restApi, UserCache userCache) { + CloudUserDataStore(RestApi restApi, UserCache userCache) { this.restApi = restApi; this.userCache = userCache; } diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/DiskUserDataStore.java b/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/DiskUserDataStore.java index 5c5aaae2..fb02b4ad 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/DiskUserDataStore.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/DiskUserDataStore.java @@ -23,7 +23,7 @@ /** * {@link UserDataStore} implementation based on file system data store. */ -public class DiskUserDataStore implements UserDataStore { +class DiskUserDataStore implements UserDataStore { private final UserCache userCache; @@ -32,7 +32,7 @@ public class DiskUserDataStore implements UserDataStore { * * @param userCache A {@link UserCache} to cache data retrieved from the api. */ - public DiskUserDataStore(UserCache userCache) { + DiskUserDataStore(UserCache userCache) { this.userCache = userCache; } diff --git a/data/src/test/java/com/fernandocejas/android10/sample/data/ApplicationTestCase.java b/data/src/test/java/com/fernandocejas/android10/sample/data/ApplicationTestCase.java index 72c06075..fc17086e 100644 --- a/data/src/test/java/com/fernandocejas/android10/sample/data/ApplicationTestCase.java +++ b/data/src/test/java/com/fernandocejas/android10/sample/data/ApplicationTestCase.java @@ -24,5 +24,5 @@ * Inherit from this class to create a test. */ @RunWith(RobolectricGradleTestRunner.class) -@Config(constants = BuildConfig.class, application = ApplicationStub.class) +@Config(constants = BuildConfig.class, application = ApplicationStub.class, sdk = 21) public abstract class ApplicationTestCase {} From 657d523adaf0366bdceb341a4f7700c66bfcb3c8 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaitsev Date: Wed, 24 Feb 2016 03:45:26 +0200 Subject: [PATCH 05/32] make domain and data modules dependent on javax.inject instead of dagger 2 --- buildsystem/dependencies.gradle | 3 +++ data/build.gradle | 3 +-- domain/build.gradle | 3 +-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/buildsystem/dependencies.gradle b/buildsystem/dependencies.gradle index aedb3ab5..81b6fb2c 100644 --- a/buildsystem/dependencies.gradle +++ b/buildsystem/dependencies.gradle @@ -18,6 +18,7 @@ ext { rxJavaVersion = '1.0.14' rxAndroidVersion = '1.0.1' javaxAnnotationVersion = '1.0' + javaxInjectVersion = '1' gsonVersion = '2.3' okHttpVersion = '2.5.0' androidAnnotationsVersion = '21.0.3' @@ -56,6 +57,7 @@ ext { daggerCompiler: "com.google.dagger:dagger-compiler:${daggerVersion}", dagger: "com.google.dagger:dagger:${daggerVersion}", javaxAnnotation: "javax.annotation:jsr250-api:${javaxAnnotationVersion}", + javaxInject: "javax.inject:javax.inject:${javaxInjectVersion}", rxJava: "io.reactivex:rxjava:${rxJavaVersion}", ] @@ -72,6 +74,7 @@ ext { rxJava: "io.reactivex:rxjava:${rxJavaVersion}", rxAndroid: "io.reactivex:rxandroid:${rxAndroidVersion}", javaxAnnotation: "javax.annotation:jsr250-api:${javaxAnnotationVersion}", + javaxInject: "javax.inject:javax.inject:${javaxInjectVersion}", androidAnnotations: "com.android.support:support-annotations:${androidAnnotationsVersion}" ] diff --git a/data/build.gradle b/data/build.gradle index 8d358887..1ee552e2 100644 --- a/data/build.gradle +++ b/data/build.gradle @@ -53,9 +53,8 @@ dependencies { def testDependencies = rootProject.ext.dataTestDependencies compile project(':domain') - apt dataDependencies.daggerCompiler provided dataDependencies.javaxAnnotation - compile dataDependencies.dagger + compile dataDependencies.javaxInject compile dataDependencies.okHttp compile dataDependencies.gson compile dataDependencies.rxJava diff --git a/domain/build.gradle b/domain/build.gradle index e9c9e8c6..84ecd764 100644 --- a/domain/build.gradle +++ b/domain/build.gradle @@ -19,10 +19,9 @@ dependencies { def domainDependencies = rootProject.ext.domainDependencies def domainTestDependencies = rootProject.ext.domainTestDependencies - provided domainDependencies.daggerCompiler provided domainDependencies.javaxAnnotation - compile domainDependencies.dagger + compile domainDependencies.javaxInject compile domainDependencies.rxJava testCompile domainTestDependencies.junit From 7f7c1b42dcc68b4b7be764220f8fa056b9ff8299 Mon Sep 17 00:00:00 2001 From: Rashiq Date: Tue, 5 Jul 2016 20:28:34 -0400 Subject: [PATCH 06/32] Fix some small things --- .../android10/sample/data/cache/UserCacheImpl.java | 4 ++-- .../sample/data/repository/UserDataRepository.java | 8 ++------ .../data/repository/datasource/UserDataStoreFactory.java | 6 ++---- .../android10/sample/domain/interactor/UseCase.java | 6 +++--- .../sample/presentation/view/activity/BaseActivity.java | 2 +- 5 files changed, 10 insertions(+), 16 deletions(-) diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCacheImpl.java b/data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCacheImpl.java index a7252f48..32367fb4 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCacheImpl.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCacheImpl.java @@ -80,10 +80,10 @@ public UserCacheImpl(Context context, JsonSerializer userCacheSerializer, @Override public void put(UserEntity userEntity) { if (userEntity != null) { - File userEntitiyFile = this.buildFile(userEntity.getUserId()); + File userEntityFile = this.buildFile(userEntity.getUserId()); if (!isCached(userEntity.getUserId())) { String jsonString = this.serializer.serialize(userEntity); - this.executeAsynchronously(new CacheWriter(this.fileManager, userEntitiyFile, + this.executeAsynchronously(new CacheWriter(this.fileManager, userEntityFile, jsonString)); setLastCacheUpdateTimeMillis(); } diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/repository/UserDataRepository.java b/data/src/main/java/com/fernandocejas/android10/sample/data/repository/UserDataRepository.java index 24f125fd..35112eb6 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/repository/UserDataRepository.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/repository/UserDataRepository.java @@ -47,18 +47,14 @@ public UserDataRepository(UserDataStoreFactory dataStoreFactory, this.userEntityDataMapper = userEntityDataMapper; } - @SuppressWarnings("Convert2MethodRef") @Override public Observable> users() { //we always get all users from the cloud final UserDataStore userDataStore = this.userDataStoreFactory.createCloudDataStore(); - return userDataStore.userEntityList() - .map(userEntities -> this.userEntityDataMapper.transform(userEntities)); + return userDataStore.userEntityList().map(this.userEntityDataMapper::transform); } - @SuppressWarnings("Convert2MethodRef") @Override public Observable user(int userId) { final UserDataStore userDataStore = this.userDataStoreFactory.create(userId); - return userDataStore.userEntityDetails(userId) - .map(userEntity -> this.userEntityDataMapper.transform(userEntity)); + return userDataStore.userEntityDetails(userId).map(this.userEntityDataMapper::transform); } } diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStoreFactory.java b/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStoreFactory.java index 59e397e1..90c979e2 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStoreFactory.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStoreFactory.java @@ -16,6 +16,7 @@ package com.fernandocejas.android10.sample.data.repository.datasource; import android.content.Context; +import android.support.annotation.NonNull; import com.fernandocejas.android10.sample.data.cache.UserCache; import com.fernandocejas.android10.sample.data.entity.mapper.UserEntityJsonMapper; import com.fernandocejas.android10.sample.data.net.RestApi; @@ -33,10 +34,7 @@ public class UserDataStoreFactory { private final UserCache userCache; @Inject - public UserDataStoreFactory(Context context, UserCache userCache) { - if (context == null || userCache == null) { - throw new IllegalArgumentException("Constructor parameters cannot be null!!!"); - } + public UserDataStoreFactory(@NonNull Context context, @NonNull UserCache userCache) { this.context = context.getApplicationContext(); this.userCache = userCache; } diff --git a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/UseCase.java b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/UseCase.java index 24e9d010..5bb63005 100644 --- a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/UseCase.java +++ b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/UseCase.java @@ -52,15 +52,15 @@ protected UseCase(ThreadExecutor threadExecutor, /** * Executes the current use case. * - * @param UseCaseSubscriber The guy who will be listen to the observable build + * @param useCaseSubscriber The guy who will be listen to the observable build * with {@link #buildUseCaseObservable()}. */ @SuppressWarnings("unchecked") - public void execute(Subscriber UseCaseSubscriber) { + public void execute(Subscriber useCaseSubscriber) { this.subscription = this.buildUseCaseObservable() .subscribeOn(Schedulers.from(threadExecutor)) .observeOn(postExecutionThread.getScheduler()) - .subscribe(UseCaseSubscriber); + .subscribe(useCaseSubscriber); } /** 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/view/activity/BaseActivity.java index 716e2915..23ad8b5e 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/view/activity/BaseActivity.java @@ -41,7 +41,7 @@ protected void addFragment(int containerViewId, Fragment fragment) { * @return {@link com.fernandocejas.android10.sample.presentation.internal.di.components.ApplicationComponent} */ protected ApplicationComponent getApplicationComponent() { - return ((AndroidApplication)getApplication()).getApplicationComponent(); + return ((AndroidApplication) getApplication()).getApplicationComponent(); } /** From 18091858d841974069216b4ffe2b58a4de4ef4f2 Mon Sep 17 00:00:00 2001 From: Fernando Cejas Date: Fri, 22 Jul 2016 15:36:54 +0200 Subject: [PATCH 07/32] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 112b2e29..ddcfa8d1 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ https://github.com/android10/java-code-styles License -------- - Copyright 2014 Fernando Cejas + Copyright 2016 Fernando Cejas Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 4026deb29ca4ca7cb2bc50e93aebbc01118c3002 Mon Sep 17 00:00:00 2001 From: Fernando Cejas Date: Mon, 1 Aug 2016 18:02:49 +0200 Subject: [PATCH 08/32] Remove useless dependencies --- buildsystem/dependencies.gradle | 2 -- 1 file changed, 2 deletions(-) diff --git a/buildsystem/dependencies.gradle b/buildsystem/dependencies.gradle index 81b6fb2c..3e550717 100644 --- a/buildsystem/dependencies.gradle +++ b/buildsystem/dependencies.gradle @@ -54,8 +54,6 @@ ext { ] domainDependencies = [ - daggerCompiler: "com.google.dagger:dagger-compiler:${daggerVersion}", - dagger: "com.google.dagger:dagger:${daggerVersion}", javaxAnnotation: "javax.annotation:jsr250-api:${javaxAnnotationVersion}", javaxInject: "javax.inject:javax.inject:${javaxInjectVersion}", rxJava: "io.reactivex:rxjava:${rxJavaVersion}", From 076b4c794316b0e8c0542a7f51fbb38d3d679bca Mon Sep 17 00:00:00 2001 From: Fernando Cejas Date: Thu, 22 Sep 2016 23:19:23 +0200 Subject: [PATCH 09/32] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ddcfa8d1..c72f0985 100644 --- a/README.md +++ b/README.md @@ -13,15 +13,15 @@ This is a sample app that is part of a blog post I have written about how to arc Clean architecture ----------------- -![http://fernandocejas.com/2015/07/18/architecting-android-the-evolution/](http://fernandocejas.com/wp-content/uploads/2014/09/clean_architecture1.png) +![http://fernandocejas.com/2015/07/18/architecting-android-the-evolution/](https://github.com/android10/Sample-Data/blob/master/Android-CleanArchitecture/clean_architecture.png) Architectural approach ----------------- -![http://fernandocejas.com/2015/07/18/architecting-android-the-evolution/](http://fernandocejas.com/wp-content/uploads/2014/09/clean_architecture_android.png) +![http://fernandocejas.com/2015/07/18/architecting-android-the-evolution/](https://github.com/android10/Sample-Data/blob/master/Android-CleanArchitecture/clean_architecture_layers.png) Architectural reactive approach ----------------- -![http://fernandocejas.com/2015/07/18/architecting-android-the-evolution/](http://fernandocejas.com/wp-content/uploads/2015/07/clean_architecture_evolution.png) +![http://fernandocejas.com/2015/07/18/architecting-android-the-evolution/](https://github.com/android10/Sample-Data/blob/master/Android-CleanArchitecture/clean_architecture_layers_details.png) Local Development ----------------- @@ -64,6 +64,6 @@ License limitations under the License. -![http://www.fernandocejas.com](http://www.android10.org/myimages/android10_logo_big_github.png) +![http://www.fernandocejas.com](https://github.com/android10/Sample-Data/blob/master/android10/android10_logo_big.png) [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Android--CleanArchitecture-brightgreen.svg?style=flat)](https://android-arsenal.com/details/3/909) From 6e59ee86d22f52178b1ffb81d5e22a92e9e8f62b Mon Sep 17 00:00:00 2001 From: Fernando Cejas Date: Mon, 19 Dec 2016 16:21:15 -0300 Subject: [PATCH 10/32] Refactor and reformat unit tests. --- .../sample/data/ApplicationTestCase.java | 19 +++++++++-- .../exception/RepositoryErrorBundleTest.java | 9 +++--- .../repository/UserDataRepositoryTest.java | 8 ++--- .../datasource/CloudUserDataStoreTest.java | 8 ++--- .../datasource/DiskUserDataStoreTest.java | 8 ++--- .../datasource/UserDataStoreFactoryTest.java | 8 ++--- .../exception/DefaultErrorBundleTest.java | 12 +++---- .../domain/interactor/GetUserDetailsTest.java | 5 +-- .../domain/interactor/GetUserListTest.java | 5 +-- .../sample/domain/interactor/UseCaseTest.java | 9 +++--- .../test/mapper/UserModelDataMapperTest.java | 6 ++-- .../presenter/UserDetailsPresenterTest.java | 32 ++++++++----------- .../test/presenter/UserListPresenterTest.java | 29 ++++++++--------- 13 files changed, 84 insertions(+), 74 deletions(-) diff --git a/data/src/test/java/com/fernandocejas/android10/sample/data/ApplicationTestCase.java b/data/src/test/java/com/fernandocejas/android10/sample/data/ApplicationTestCase.java index fc17086e..680fca12 100644 --- a/data/src/test/java/com/fernandocejas/android10/sample/data/ApplicationTestCase.java +++ b/data/src/test/java/com/fernandocejas/android10/sample/data/ApplicationTestCase.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -15,8 +15,13 @@ */ package com.fernandocejas.android10.sample.data; +import android.content.Context; +import org.junit.Rule; +import org.junit.rules.TestRule; import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricGradleTestRunner; +import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; /** @@ -25,4 +30,14 @@ */ @RunWith(RobolectricGradleTestRunner.class) @Config(constants = BuildConfig.class, application = ApplicationStub.class, sdk = 21) -public abstract class ApplicationTestCase {} +public abstract class ApplicationTestCase { + + @Rule public TestRule injectMocksRule = (base, description) -> { + MockitoAnnotations.initMocks(ApplicationTestCase.this); + return base; + }; + + public static Context context() { + return RuntimeEnvironment.application; + } +} diff --git a/data/src/test/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundleTest.java b/data/src/test/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundleTest.java index 277f7ad7..517613b4 100644 --- a/data/src/test/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundleTest.java +++ b/data/src/test/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundleTest.java @@ -18,25 +18,26 @@ import com.fernandocejas.android10.sample.data.ApplicationTestCase; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.runners.MockitoJUnitRunner; import static org.mockito.Mockito.verify; +@RunWith(MockitoJUnitRunner.class) public class RepositoryErrorBundleTest extends ApplicationTestCase { private RepositoryErrorBundle repositoryErrorBundle; - @Mock - private Exception mockException; + @Mock private Exception mockException; @Before public void setUp() { - MockitoAnnotations.initMocks(this); repositoryErrorBundle = new RepositoryErrorBundle(mockException); } @Test + @SuppressWarnings("ThrowableResultOfMethodCallIgnored") public void testGetErrorMessageInteraction() { repositoryErrorBundle.getErrorMessage(); diff --git a/data/src/test/java/com/fernandocejas/android10/sample/data/repository/UserDataRepositoryTest.java b/data/src/test/java/com/fernandocejas/android10/sample/data/repository/UserDataRepositoryTest.java index 70752055..64335319 100644 --- a/data/src/test/java/com/fernandocejas/android10/sample/data/repository/UserDataRepositoryTest.java +++ b/data/src/test/java/com/fernandocejas/android10/sample/data/repository/UserDataRepositoryTest.java @@ -15,7 +15,6 @@ */ package com.fernandocejas.android10.sample.data.repository; -import com.fernandocejas.android10.sample.data.ApplicationTestCase; import com.fernandocejas.android10.sample.data.entity.UserEntity; import com.fernandocejas.android10.sample.data.entity.mapper.UserEntityDataMapper; import com.fernandocejas.android10.sample.data.repository.datasource.UserDataStore; @@ -27,15 +26,17 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.runners.MockitoJUnitRunner; import rx.Observable; import static org.mockito.BDDMockito.given; import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.verify; -public class UserDataRepositoryTest extends ApplicationTestCase { +@RunWith(MockitoJUnitRunner.class) +public class UserDataRepositoryTest { private static final int FAKE_USER_ID = 123; @@ -52,7 +53,6 @@ public class UserDataRepositoryTest extends ApplicationTestCase { @Before public void setUp() { - MockitoAnnotations.initMocks(this); userDataRepository = new UserDataRepository(mockUserDataStoreFactory, mockUserEntityDataMapper); diff --git a/data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/CloudUserDataStoreTest.java b/data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/CloudUserDataStoreTest.java index b54e58e1..1bc23ec7 100644 --- a/data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/CloudUserDataStoreTest.java +++ b/data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/CloudUserDataStoreTest.java @@ -15,20 +15,21 @@ */ package com.fernandocejas.android10.sample.data.repository.datasource; -import com.fernandocejas.android10.sample.data.ApplicationTestCase; import com.fernandocejas.android10.sample.data.cache.UserCache; import com.fernandocejas.android10.sample.data.entity.UserEntity; import com.fernandocejas.android10.sample.data.net.RestApi; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.runners.MockitoJUnitRunner; import rx.Observable; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.verify; -public class CloudUserDataStoreTest extends ApplicationTestCase { +@RunWith(MockitoJUnitRunner.class) +public class CloudUserDataStoreTest { private static final int FAKE_USER_ID = 765; @@ -39,7 +40,6 @@ public class CloudUserDataStoreTest extends ApplicationTestCase { @Before public void setUp() { - MockitoAnnotations.initMocks(this); cloudUserDataStore = new CloudUserDataStore(mockRestApi, mockUserCache); } diff --git a/data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/DiskUserDataStoreTest.java b/data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/DiskUserDataStoreTest.java index 6c7bca5d..6a649142 100644 --- a/data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/DiskUserDataStoreTest.java +++ b/data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/DiskUserDataStoreTest.java @@ -15,18 +15,19 @@ */ package com.fernandocejas.android10.sample.data.repository.datasource; -import com.fernandocejas.android10.sample.data.ApplicationTestCase; import com.fernandocejas.android10.sample.data.cache.UserCache; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.runners.MockitoJUnitRunner; import static org.mockito.Mockito.verify; -public class DiskUserDataStoreTest extends ApplicationTestCase { +@RunWith(MockitoJUnitRunner.class) +public class DiskUserDataStoreTest { private static final int FAKE_USER_ID = 11; @@ -38,7 +39,6 @@ public class DiskUserDataStoreTest extends ApplicationTestCase { @Before public void setUp() { - MockitoAnnotations.initMocks(this); diskUserDataStore = new DiskUserDataStore(mockUserCache); } diff --git a/data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStoreFactoryTest.java b/data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStoreFactoryTest.java index b8c644a4..6c560a0a 100644 --- a/data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStoreFactoryTest.java +++ b/data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStoreFactoryTest.java @@ -20,7 +20,6 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; import static org.hamcrest.CoreMatchers.instanceOf; @@ -36,14 +35,11 @@ public class UserDataStoreFactoryTest extends ApplicationTestCase { private UserDataStoreFactory userDataStoreFactory; - @Mock - private UserCache mockUserCache; + @Mock private UserCache mockUserCache; @Before public void setUp() { - MockitoAnnotations.initMocks(this); - userDataStoreFactory = - new UserDataStoreFactory(RuntimeEnvironment.application, mockUserCache); + userDataStoreFactory = new UserDataStoreFactory(RuntimeEnvironment.application, mockUserCache); } @Test diff --git a/domain/src/test/java/com/fernandocejas/android10/sample/domain/exception/DefaultErrorBundleTest.java b/domain/src/test/java/com/fernandocejas/android10/sample/domain/exception/DefaultErrorBundleTest.java index 1ba00434..0e33d6e8 100644 --- a/domain/src/test/java/com/fernandocejas/android10/sample/domain/exception/DefaultErrorBundleTest.java +++ b/domain/src/test/java/com/fernandocejas/android10/sample/domain/exception/DefaultErrorBundleTest.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -17,20 +17,20 @@ import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.runners.MockitoJUnitRunner; import static org.mockito.Mockito.verify; +@RunWith(MockitoJUnitRunner.class) public class DefaultErrorBundleTest { private DefaultErrorBundle defaultErrorBundle; - @Mock - private Exception mockException; + @Mock private Exception mockException; @Before public void setUp() { - MockitoAnnotations.initMocks(this); defaultErrorBundle = new DefaultErrorBundle(mockException); } @@ -40,4 +40,4 @@ public void testGetErrorMessageInteraction() { verify(mockException).getMessage(); } -} \ No newline at end of file +} diff --git a/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetailsTest.java b/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetailsTest.java index 8e730cb2..f0c119d7 100644 --- a/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetailsTest.java +++ b/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetailsTest.java @@ -20,13 +20,15 @@ import com.fernandocejas.android10.sample.domain.repository.UserRepository; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.runners.MockitoJUnitRunner; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; +@RunWith(MockitoJUnitRunner.class) public class GetUserDetailsTest { private static final int FAKE_USER_ID = 123; @@ -39,7 +41,6 @@ public class GetUserDetailsTest { @Before public void setUp() { - MockitoAnnotations.initMocks(this); getUserDetails = new GetUserDetails(FAKE_USER_ID, mockUserRepository, mockThreadExecutor, mockPostExecutionThread); } diff --git a/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserListTest.java b/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserListTest.java index 0e1565ff..0ea91659 100644 --- a/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserListTest.java +++ b/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserListTest.java @@ -20,13 +20,15 @@ import com.fernandocejas.android10.sample.domain.repository.UserRepository; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.runners.MockitoJUnitRunner; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; +@RunWith(MockitoJUnitRunner.class) public class GetUserListTest { private GetUserList getUserList; @@ -37,7 +39,6 @@ public class GetUserListTest { @Before public void setUp() { - MockitoAnnotations.initMocks(this); getUserList = new GetUserList(mockUserRepository, mockThreadExecutor, mockPostExecutionThread); } diff --git a/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/UseCaseTest.java b/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/UseCaseTest.java index e8d20444..7f290e33 100644 --- a/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/UseCaseTest.java +++ b/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/UseCaseTest.java @@ -19,8 +19,9 @@ import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.runners.MockitoJUnitRunner; import rx.Observable; import rx.Subscriber; import rx.observers.TestSubscriber; @@ -30,6 +31,7 @@ import static org.hamcrest.core.Is.is; import static org.mockito.BDDMockito.given; +@RunWith(MockitoJUnitRunner.class) public class UseCaseTest { private UseCaseTestClass useCase; @@ -39,7 +41,6 @@ public class UseCaseTest { @Before public void setUp() { - MockitoAnnotations.initMocks(this); this.useCase = new UseCaseTestClass(mockThreadExecutor, mockPostExecutionThread); } @@ -67,7 +68,7 @@ public void testSubscriptionWhenExecutingUseCase() { private static class UseCaseTestClass extends UseCase { - protected UseCaseTestClass( + UseCaseTestClass( ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) { super(threadExecutor, postExecutionThread); @@ -81,4 +82,4 @@ protected UseCaseTestClass( super.execute(UseCaseSubscriber); } } -} \ No newline at end of file +} diff --git a/presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/mapper/UserModelDataMapperTest.java b/presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/mapper/UserModelDataMapperTest.java index 50a1e33b..bb48c6fc 100644 --- a/presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/mapper/UserModelDataMapperTest.java +++ b/presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/mapper/UserModelDataMapperTest.java @@ -31,7 +31,7 @@ public class UserModelDataMapperTest extends TestCase { private static final int FAKE_USER_ID = 123; - private static final String FAKE_FULLNAME = "Tony Stark"; + private static final String FAKE_FULL_NAME = "Tony Stark"; private UserModelDataMapper userModelDataMapper; @@ -46,7 +46,7 @@ public void testTransformUser() { assertThat(userModel, is(instanceOf(UserModel.class))); assertThat(userModel.getUserId(), is(FAKE_USER_ID)); - assertThat(userModel.getFullName(), is(FAKE_FULLNAME)); + assertThat(userModel.getFullName(), is(FAKE_FULL_NAME)); } public void testTransformUserCollection() { @@ -66,7 +66,7 @@ public void testTransformUserCollection() { private User createFakeUser() { User user = new User(FAKE_USER_ID); - user.setFullName(FAKE_FULLNAME); + user.setFullName(FAKE_FULL_NAME); return user; } 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 c4009696..07d8bc31 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 @@ -16,42 +16,38 @@ package com.fernandocejas.android10.sample.test.presenter; import android.content.Context; -import android.test.AndroidTestCase; import com.fernandocejas.android10.sample.domain.interactor.GetUserDetails; 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 org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.runners.MockitoJUnitRunner; import rx.Subscriber; import static org.mockito.BDDMockito.given; import static org.mockito.Matchers.any; import static org.mockito.Mockito.verify; -public class UserDetailsPresenterTest extends AndroidTestCase { - - private static final int FAKE_USER_ID = 123; +@RunWith(MockitoJUnitRunner.class) +public class UserDetailsPresenterTest { private UserDetailsPresenter userDetailsPresenter; - @Mock - private Context mockContext; - @Mock - private UserDetailsView mockUserDetailsView; - @Mock - private GetUserDetails mockGetUserDetails; - @Mock - private UserModelDataMapper mockUserModelDataMapper; + @Mock private Context mockContext; + @Mock private UserDetailsView mockUserDetailsView; + @Mock private GetUserDetails mockGetUserDetails; + @Mock private UserModelDataMapper mockUserModelDataMapper; - @Override protected void setUp() throws Exception { - super.setUp(); - MockitoAnnotations.initMocks(this); - userDetailsPresenter = new UserDetailsPresenter(mockGetUserDetails, - mockUserModelDataMapper); + @Before + public void setUp() { + userDetailsPresenter = new UserDetailsPresenter(mockGetUserDetails, mockUserModelDataMapper); userDetailsPresenter.setView(mockUserDetailsView); } + @Test public void testUserDetailsPresenterInitialize() { given(mockUserDetailsView.context()).willReturn(mockContext); 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 f8a98c8e..e02d3fc2 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 @@ -16,39 +16,38 @@ package com.fernandocejas.android10.sample.test.presenter; import android.content.Context; -import android.test.AndroidTestCase; 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 org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.runners.MockitoJUnitRunner; import rx.Subscriber; import static org.mockito.BDDMockito.given; import static org.mockito.Matchers.any; import static org.mockito.Mockito.verify; -public class UserListPresenterTest extends AndroidTestCase { +@RunWith(MockitoJUnitRunner.class) +public class UserListPresenterTest { private UserListPresenter userListPresenter; - @Mock - private Context mockContext; - @Mock - private UserListView mockUserListView; - @Mock - private GetUserList mockGetUserList; - @Mock - private UserModelDataMapper mockUserModelDataMapper; - - @Override protected void setUp() throws Exception { - super.setUp(); - MockitoAnnotations.initMocks(this); + @Mock private Context mockContext; + @Mock private UserListView mockUserListView; + @Mock private GetUserList mockGetUserList; + @Mock private UserModelDataMapper mockUserModelDataMapper; + + @Before + public void setUp() { userListPresenter = new UserListPresenter(mockGetUserList, mockUserModelDataMapper); userListPresenter.setView(mockUserListView); } + @Test public void testUserListPresenterInitialize() { given(mockUserListView.context()).willReturn(mockContext); From 38f47c888cf5c285cd8dda6836c7e369377fec51 Mon Sep 17 00:00:00 2001 From: Fernando Cejas Date: Mon, 19 Dec 2016 16:56:08 -0300 Subject: [PATCH 11/32] - Minor Refactor. - Improve class scoping. --- build.gradle | 5 -- .../sample/data/cache/FileManager.java | 16 +++---- .../sample/data/cache/UserCacheImpl.java | 8 ++-- .../data/cache/serializer/JsonSerializer.java | 8 ++-- .../entity/mapper/UserEntityDataMapper.java | 9 ++-- .../entity/mapper/UserEntityJsonMapper.java | 22 ++------- .../exception/NetworkConnectionException.java | 8 ---- .../data/exception/RepositoryErrorBundle.java | 4 +- .../data/exception/UserNotFoundException.java | 15 +----- .../sample/data/executor/JobExecutor.java | 34 +++---------- .../sample/data/net/ApiConnection.java | 6 +-- .../data/repository/UserDataRepository.java | 2 +- .../datasource/UserDataStoreFactory.java | 2 +- .../android10/sample/domain/User.java | 2 +- .../sample/domain/interactor/GetUserList.java | 2 +- .../sample/presentation/UIThread.java | 2 +- .../di/components/ActivityComponent.java | 2 +- .../sample/presentation/model/UserModel.java | 2 +- .../view/activity/BaseActivity.java | 2 +- .../view/adapter/UsersAdapter.java | 4 +- .../view/component/AutoLoadImageView.java | 48 +++---------------- 21 files changed, 51 insertions(+), 152 deletions(-) diff --git a/build.gradle b/build.gradle index 5fd4dd6e..0762131e 100644 --- a/build.gradle +++ b/build.gradle @@ -23,11 +23,6 @@ allprojects { } } -task wrapper(type: Wrapper) { - description 'Creates the gradle wrapper.' - gradleVersion '2.12' -} - task runDomainUnitTests(dependsOn: [':domain:test']) { description 'Run unit tests for the domain layer.' } diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/cache/FileManager.java b/data/src/main/java/com/fernandocejas/android10/sample/data/cache/FileManager.java index 2cdd43b4..3d939e47 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/cache/FileManager.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/cache/FileManager.java @@ -32,7 +32,7 @@ public class FileManager { @Inject - public FileManager() {} + FileManager() {} /** * Writes a file to Disk. @@ -41,7 +41,7 @@ public FileManager() {} * * @param file The file to write to Disk. */ - public void writeToFile(File file, String fileContent) { + void writeToFile(File file, String fileContent) { if (!file.exists()) { try { FileWriter writer = new FileWriter(file); @@ -61,7 +61,7 @@ public void writeToFile(File file, String fileContent) { * @param file The file to read from. * @return A string with the content of the file. */ - public String readFileContent(File file) { + String readFileContent(File file) { StringBuilder fileContentBuilder = new StringBuilder(); if (file.exists()) { String stringLine; @@ -69,7 +69,7 @@ public String readFileContent(File file) { FileReader fileReader = new FileReader(file); BufferedReader bufferedReader = new BufferedReader(fileReader); while ((stringLine = bufferedReader.readLine()) != null) { - fileContentBuilder.append(stringLine + "\n"); + fileContentBuilder.append(stringLine).append("\n"); } bufferedReader.close(); fileReader.close(); @@ -87,7 +87,7 @@ public String readFileContent(File file) { * @param file The file to check existence. * @return true if this file exists, false otherwise. */ - public boolean exists(File file) { + boolean exists(File file) { return file.exists(); } @@ -98,7 +98,7 @@ public boolean exists(File file) { * * @param directory The directory which its content will be deleted. */ - public boolean clearDirectory(File directory) { + boolean clearDirectory(File directory) { boolean result = false; if (directory.exists()) { for (File file : directory.listFiles()) { @@ -116,7 +116,7 @@ public boolean clearDirectory(File directory) { * @param key A string for the key that will be used to retrieve the value in the future. * @param value A long representing the value to be inserted. */ - public void writeToPreferences(Context context, String preferenceFileName, String key, + void writeToPreferences(Context context, String preferenceFileName, String key, long value) { SharedPreferences sharedPreferences = context.getSharedPreferences(preferenceFileName, @@ -134,7 +134,7 @@ public void writeToPreferences(Context context, String preferenceFileName, Strin * @param key A key that will be used to retrieve the value from the preference file. * @return A long representing the value retrieved from the preferences file. */ - public long getFromPreferences(Context context, String preferenceFileName, String key) { + long getFromPreferences(Context context, String preferenceFileName, String key) { SharedPreferences sharedPreferences = context.getSharedPreferences(preferenceFileName, Context.MODE_PRIVATE); return sharedPreferences.getLong(key, 0); diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCacheImpl.java b/data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCacheImpl.java index 32367fb4..9538dc3a 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCacheImpl.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCacheImpl.java @@ -51,7 +51,7 @@ public class UserCacheImpl implements UserCache { * @param fileManager {@link FileManager} for saving serialized objects to the file system. */ @Inject - public UserCacheImpl(Context context, JsonSerializer userCacheSerializer, + UserCacheImpl(Context context, JsonSerializer userCacheSerializer, FileManager fileManager, ThreadExecutor executor) { if (context == null || userCacheSerializer == null || fileManager == null || executor == null) { throw new IllegalArgumentException("Invalid null parameter"); @@ -91,8 +91,8 @@ public UserCacheImpl(Context context, JsonSerializer userCacheSerializer, } @Override public boolean isCached(int userId) { - File userEntitiyFile = this.buildFile(userId); - return this.fileManager.exists(userEntitiyFile); + final File userEntityFile = this.buildFile(userId); + return this.fileManager.exists(userEntityFile); } @Override public boolean isExpired() { @@ -119,7 +119,7 @@ public UserCacheImpl(Context context, JsonSerializer userCacheSerializer, * @return A valid file. */ private File buildFile(int userId) { - StringBuilder fileNameBuilder = new StringBuilder(); + final StringBuilder fileNameBuilder = new StringBuilder(); fileNameBuilder.append(this.cacheDir.getPath()); fileNameBuilder.append(File.separator); fileNameBuilder.append(DEFAULT_FILE_NAME); diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/cache/serializer/JsonSerializer.java b/data/src/main/java/com/fernandocejas/android10/sample/data/cache/serializer/JsonSerializer.java index 6e053187..61d35779 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/cache/serializer/JsonSerializer.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/cache/serializer/JsonSerializer.java @@ -29,7 +29,7 @@ public class JsonSerializer { private final Gson gson = new Gson(); @Inject - public JsonSerializer() {} + JsonSerializer() {} /** * Serialize an object to Json. @@ -37,8 +37,7 @@ public JsonSerializer() {} * @param userEntity {@link UserEntity} to serialize. */ public String serialize(UserEntity userEntity) { - String jsonString = gson.toJson(userEntity, UserEntity.class); - return jsonString; + return gson.toJson(userEntity, UserEntity.class); } /** @@ -48,7 +47,6 @@ public String serialize(UserEntity userEntity) { * @return {@link UserEntity} */ public UserEntity deserialize(String jsonString) { - UserEntity userEntity = gson.fromJson(jsonString, UserEntity.class); - return userEntity; + return gson.fromJson(jsonString, UserEntity.class); } } diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityDataMapper.java b/data/src/main/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityDataMapper.java index 1ecbdcdc..e2bde175 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityDataMapper.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityDataMapper.java @@ -31,7 +31,7 @@ public class UserEntityDataMapper { @Inject - public UserEntityDataMapper() {} + UserEntityDataMapper() {} /** * Transform a {@link UserEntity} into an {@link User}. @@ -49,7 +49,6 @@ public User transform(UserEntity userEntity) { user.setFollowers(userEntity.getFollowers()); user.setEmail(userEntity.getEmail()); } - return user; } @@ -60,15 +59,13 @@ public User transform(UserEntity userEntity) { * @return {@link User} if valid {@link UserEntity} otherwise null. */ public List transform(Collection userEntityCollection) { - List userList = new ArrayList<>(20); - User user; + final List userList = new ArrayList<>(20); for (UserEntity userEntity : userEntityCollection) { - user = transform(userEntity); + final User user = transform(userEntity); if (user != null) { userList.add(user); } } - return userList; } } diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityJsonMapper.java b/data/src/main/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityJsonMapper.java index 8e8dca90..1e344a34 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityJsonMapper.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityJsonMapper.java @@ -43,14 +43,8 @@ public UserEntityJsonMapper() { * @throws com.google.gson.JsonSyntaxException if the json string is not a valid json structure. */ public UserEntity transformUserEntity(String userJsonResponse) throws JsonSyntaxException { - try { - Type userEntityType = new TypeToken() {}.getType(); - UserEntity userEntity = this.gson.fromJson(userJsonResponse, userEntityType); - - return userEntity; - } catch (JsonSyntaxException jsonException) { - throw jsonException; - } + final Type userEntityType = new TypeToken() {}.getType(); + return this.gson.fromJson(userJsonResponse, userEntityType); } /** @@ -62,15 +56,7 @@ public UserEntity transformUserEntity(String userJsonResponse) throws JsonSyntax */ public List transformUserEntityCollection(String userListJsonResponse) throws JsonSyntaxException { - - List userEntityCollection; - try { - Type listOfUserEntityType = new TypeToken>() {}.getType(); - userEntityCollection = this.gson.fromJson(userListJsonResponse, listOfUserEntityType); - - return userEntityCollection; - } catch (JsonSyntaxException jsonException) { - throw jsonException; - } + final Type listOfUserEntityType = new TypeToken>() {}.getType(); + return this.gson.fromJson(userListJsonResponse, listOfUserEntityType); } } diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/exception/NetworkConnectionException.java b/data/src/main/java/com/fernandocejas/android10/sample/data/exception/NetworkConnectionException.java index 987a106b..a02ea8f4 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/exception/NetworkConnectionException.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/exception/NetworkConnectionException.java @@ -24,14 +24,6 @@ public NetworkConnectionException() { super(); } - public NetworkConnectionException(final String message) { - super(message); - } - - public NetworkConnectionException(final String message, final Throwable cause) { - super(message, cause); - } - public NetworkConnectionException(final Throwable cause) { super(cause); } diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundle.java b/data/src/main/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundle.java index c232adbe..a4ec28a2 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundle.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundle.java @@ -20,11 +20,11 @@ /** * Wrapper around Exceptions used to manage errors in the repository. */ -public class RepositoryErrorBundle implements ErrorBundle { +class RepositoryErrorBundle implements ErrorBundle { private final Exception exception; - public RepositoryErrorBundle(Exception exception) { + RepositoryErrorBundle(Exception exception) { this.exception = exception; } diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/exception/UserNotFoundException.java b/data/src/main/java/com/fernandocejas/android10/sample/data/exception/UserNotFoundException.java index 8667eaa4..3865818d 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/exception/UserNotFoundException.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/exception/UserNotFoundException.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -19,20 +19,7 @@ * Exception throw by the application when a User search can't return a valid result. */ public class UserNotFoundException extends Exception { - public UserNotFoundException() { super(); } - - public UserNotFoundException(final String message) { - super(message); - } - - public UserNotFoundException(final String message, final Throwable cause) { - super(message, cause); - } - - public UserNotFoundException(final Throwable cause) { - super(cause); - } } diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/executor/JobExecutor.java b/data/src/main/java/com/fernandocejas/android10/sample/data/executor/JobExecutor.java index c82177d0..f0963cec 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/executor/JobExecutor.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/executor/JobExecutor.java @@ -15,8 +15,8 @@ */ package com.fernandocejas.android10.sample.data.executor; +import android.support.annotation.NonNull; import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor; -import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; @@ -29,43 +29,23 @@ */ @Singleton public class JobExecutor implements ThreadExecutor { - - private static final int INITIAL_POOL_SIZE = 3; - private static final int MAX_POOL_SIZE = 5; - - // Sets the amount of time an idle thread waits before terminating - private static final int KEEP_ALIVE_TIME = 10; - - // Sets the Time Unit to seconds - private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS; - - private final BlockingQueue workQueue; - private final ThreadPoolExecutor threadPoolExecutor; - private final ThreadFactory threadFactory; - @Inject - public JobExecutor() { - this.workQueue = new LinkedBlockingQueue<>(); - this.threadFactory = new JobThreadFactory(); - this.threadPoolExecutor = new ThreadPoolExecutor(INITIAL_POOL_SIZE, MAX_POOL_SIZE, - KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, this.workQueue, this.threadFactory); + JobExecutor() { + this.threadPoolExecutor = new ThreadPoolExecutor(3, 5, 10, TimeUnit.SECONDS, + new LinkedBlockingQueue<>(), new JobThreadFactory()); } - @Override public void execute(Runnable runnable) { - if (runnable == null) { - throw new IllegalArgumentException("Runnable to execute cannot be null"); - } + @Override public void execute(@NonNull Runnable runnable) { this.threadPoolExecutor.execute(runnable); } private static class JobThreadFactory implements ThreadFactory { - private static final String THREAD_NAME = "android_"; private int counter = 0; - @Override public Thread newThread(Runnable runnable) { - return new Thread(runnable, THREAD_NAME + counter++); + @Override public Thread newThread(@NonNull Runnable runnable) { + return new Thread(runnable, "android_" + counter++); } } } diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/net/ApiConnection.java b/data/src/main/java/com/fernandocejas/android10/sample/data/net/ApiConnection.java index b5e153b7..7cf859d2 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/net/ApiConnection.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/net/ApiConnection.java @@ -29,7 +29,7 @@ * Implements {@link java.util.concurrent.Callable} so when executed asynchronously can * return a value. */ -public class ApiConnection implements Callable { +class ApiConnection implements Callable { private static final String CONTENT_TYPE_LABEL = "Content-Type"; private static final String CONTENT_TYPE_VALUE_JSON = "application/json; charset=utf-8"; @@ -41,7 +41,7 @@ private ApiConnection(String url) throws MalformedURLException { this.url = new URL(url); } - public static ApiConnection createGET(String url) throws MalformedURLException { + static ApiConnection createGET(String url) throws MalformedURLException { return new ApiConnection(url); } @@ -52,7 +52,7 @@ public static ApiConnection createGET(String url) throws MalformedURLException { * @return A string response */ @Nullable - public String requestSyncCall() { + String requestSyncCall() { connectToApi(); return response; } diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/repository/UserDataRepository.java b/data/src/main/java/com/fernandocejas/android10/sample/data/repository/UserDataRepository.java index 35112eb6..80a7f3fb 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/repository/UserDataRepository.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/repository/UserDataRepository.java @@ -41,7 +41,7 @@ public class UserDataRepository implements UserRepository { * @param userEntityDataMapper {@link UserEntityDataMapper}. */ @Inject - public UserDataRepository(UserDataStoreFactory dataStoreFactory, + UserDataRepository(UserDataStoreFactory dataStoreFactory, UserEntityDataMapper userEntityDataMapper) { this.userDataStoreFactory = dataStoreFactory; this.userEntityDataMapper = userEntityDataMapper; diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStoreFactory.java b/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStoreFactory.java index 90c979e2..72b8f9cf 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStoreFactory.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStoreFactory.java @@ -34,7 +34,7 @@ public class UserDataStoreFactory { private final UserCache userCache; @Inject - public UserDataStoreFactory(@NonNull Context context, @NonNull UserCache userCache) { + UserDataStoreFactory(@NonNull Context context, @NonNull UserCache userCache) { this.context = context.getApplicationContext(); this.userCache = userCache; } diff --git a/domain/src/main/java/com/fernandocejas/android10/sample/domain/User.java b/domain/src/main/java/com/fernandocejas/android10/sample/domain/User.java index d62a021d..d807960c 100644 --- a/domain/src/main/java/com/fernandocejas/android10/sample/domain/User.java +++ b/domain/src/main/java/com/fernandocejas/android10/sample/domain/User.java @@ -77,7 +77,7 @@ public void setFollowers(int followers) { } @Override public String toString() { - StringBuilder stringBuilder = new StringBuilder(); + final StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("***** User Details *****\n"); stringBuilder.append("id=" + this.getUserId() + "\n"); diff --git a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java index 0780ff3d..27464bee 100644 --- a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java +++ b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java @@ -31,7 +31,7 @@ public class GetUserList extends UseCase { private final UserRepository userRepository; @Inject - public GetUserList(UserRepository userRepository, ThreadExecutor threadExecutor, + GetUserList(UserRepository userRepository, ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) { super(threadExecutor, postExecutionThread); this.userRepository = userRepository; diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/UIThread.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/UIThread.java index bd4ceca4..05f13d22 100644 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/UIThread.java +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/UIThread.java @@ -29,7 +29,7 @@ public class UIThread implements PostExecutionThread { @Inject - public UIThread() {} + UIThread() {} @Override public Scheduler getScheduler() { return AndroidSchedulers.mainThread(); diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/components/ActivityComponent.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/components/ActivityComponent.java index a7ffc0d9..f39e89eb 100644 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/components/ActivityComponent.java +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/components/ActivityComponent.java @@ -29,7 +29,7 @@ */ @PerActivity @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) -public interface ActivityComponent { +interface ActivityComponent { //Exposed to sub-graphs. Activity activity(); } diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/model/UserModel.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/model/UserModel.java index d13ae2d4..20289507 100644 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/model/UserModel.java +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/model/UserModel.java @@ -77,7 +77,7 @@ public void setFollowers(int followers) { } @Override public String toString() { - StringBuilder stringBuilder = new StringBuilder(); + final StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("***** User Model Details *****\n"); stringBuilder.append("id=" + this.getUserId() + "\n"); 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/view/activity/BaseActivity.java index 23ad8b5e..70ee8ca0 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/view/activity/BaseActivity.java @@ -30,7 +30,7 @@ protected void onCreate(Bundle savedInstanceState) { * @param fragment The fragment to be added. */ protected void addFragment(int containerViewId, Fragment fragment) { - FragmentTransaction fragmentTransaction = this.getFragmentManager().beginTransaction(); + final FragmentTransaction fragmentTransaction = this.getFragmentManager().beginTransaction(); fragmentTransaction.add(containerViewId, fragment); fragmentTransaction.commit(); } 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/view/adapter/UsersAdapter.java index 110a1002..3b13dc58 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/view/adapter/UsersAdapter.java @@ -34,7 +34,7 @@ public interface OnItemClickListener { private OnItemClickListener onItemClickListener; @Inject - public UsersAdapter(Context context) { + UsersAdapter(Context context) { this.layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); this.usersCollection = Collections.emptyList(); @@ -84,7 +84,7 @@ private void validateUsersCollection(Collection usersCollection) { static class UserViewHolder extends RecyclerView.ViewHolder { @Bind(R.id.title) TextView textViewTitle; - public UserViewHolder(View itemView) { + UserViewHolder(View itemView) { super(itemView); ButterKnife.bind(this, itemView); } 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/view/component/AutoLoadImageView.java index c45e8d6c..45cecd3d 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/view/component/AutoLoadImageView.java @@ -17,10 +17,8 @@ import android.util.Log; import android.widget.ImageView; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; -import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; @@ -83,25 +81,6 @@ public void setImageUrl(final String imageUrl) { } } - /** - * Set a place holder used for loading when an image is being downloaded from the internet. - * - * @param resourceId The resource id to use as a place holder. - */ - public void setImagePlaceHolder(int resourceId) { - this.imagePlaceHolderResId = resourceId; - this.loadImagePlaceHolder(); - } - - /** - * Invalidate the internal cache by evicting all cached elements. - */ - public void invalidateImageCache() { - if (this.cache != null) { - this.cache.evictAll(); - } - } - /** * Loads and image from the internet (and cache it) or from the internal cache. * @@ -195,9 +174,9 @@ private void cacheBitmap(Bitmap bitmap, String fileName) { private boolean isThereInternetConnection() { boolean isConnected; - ConnectivityManager connectivityManager = + final ConnectivityManager connectivityManager = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE); - NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); + final NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); isConnected = (networkInfo != null && networkInfo.isConnectedOrConnecting()); return isConnected; @@ -238,14 +217,12 @@ interface Callback { */ void download(String imageUrl, Callback callback) { try { - URLConnection conn = new URL(imageUrl).openConnection(); + final URLConnection conn = new URL(imageUrl).openConnection(); conn.connect(); - Bitmap bitmap = BitmapFactory.decodeStream(conn.getInputStream()); + final Bitmap bitmap = BitmapFactory.decodeStream(conn.getInputStream()); if (callback != null) { callback.onImageDownloaded(bitmap); } - } catch (MalformedURLException e) { - reportError(callback); } catch (IOException e) { reportError(callback); } @@ -298,32 +275,19 @@ synchronized Bitmap get(String fileName) { * @param fileName A string representing the name of the file to be cached. */ synchronized void put(Bitmap bitmap, String fileName) { - File file = buildFileFromFilename(fileName); + final File file = buildFileFromFilename(fileName); if (!file.exists()) { try { - FileOutputStream fileOutputStream = new FileOutputStream(file); + final FileOutputStream fileOutputStream = new FileOutputStream(file); bitmap.compress(Bitmap.CompressFormat.PNG, 90, fileOutputStream); fileOutputStream.flush(); fileOutputStream.close(); - } catch (FileNotFoundException e) { - Log.e(TAG, e.getMessage()); } catch (IOException e) { Log.e(TAG, e.getMessage()); } } } - /** - * Invalidate and expire the cache. - */ - void evictAll() { - if (cacheDir.exists()) { - for (File file : cacheDir.listFiles()) { - file.delete(); - } - } - } - /** * Creates a file name from an image url * From 133220e6568604ef261415acb752026618dc658f Mon Sep 17 00:00:00 2001 From: Fernando Cejas Date: Mon, 19 Dec 2016 16:57:03 -0300 Subject: [PATCH 12/32] Change Api url address. --- .../com/fernandocejas/android10/sample/data/net/RestApi.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApi.java b/data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApi.java index f99d8f52..42bc4c93 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApi.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApi.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -23,7 +23,8 @@ * RestApi for retrieving data from the network. */ public interface RestApi { - String API_BASE_URL = "http://www.android10.org/myapi/"; + String API_BASE_URL = + "https://raw.githubusercontent.com/android10/Sample-Data/master/Android-CleanArchitecture/"; /** Api url for getting all users */ String API_URL_GET_USER_LIST = API_BASE_URL + "users.json"; From c3e55a510704dc525aac8a1184731e4f5da87c6e Mon Sep 17 00:00:00 2001 From: Fernando Cejas Date: Mon, 19 Dec 2016 17:03:00 -0300 Subject: [PATCH 13/32] Remove dead code. --- .../sample/data/entity/UserEntity.java | 31 ------------------- .../datasource/UserDataStoreFactory.java | 4 +-- .../android10/sample/domain/User.java | 15 --------- .../mapper/UserModelDataMapper.java | 2 +- .../sample/presentation/model/UserModel.java | 17 +--------- 5 files changed, 4 insertions(+), 65 deletions(-) diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/entity/UserEntity.java b/data/src/main/java/com/fernandocejas/android10/sample/data/entity/UserEntity.java index 8d8fb1fc..550e9acf 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/entity/UserEntity.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/entity/UserEntity.java @@ -56,10 +56,6 @@ public String getCoverUrl() { return coverUrl; } - public void setCoverUrl(String coverUrl) { - this.coverUrl = coverUrl; - } - public String getFullname() { return fullname; } @@ -72,38 +68,11 @@ public String getDescription() { return description; } - public void setDescription(String description) { - this.description = description; - } - public int getFollowers() { return followers; } - public void setFollowers(int followers) { - this.followers = followers; - } - public String getEmail() { return email; } - - public void setEmail(String email) { - this.email = email; - } - - @Override public String toString() { - StringBuilder stringBuilder = new StringBuilder(); - - stringBuilder.append("***** User Entity Details *****\n"); - stringBuilder.append("id=" + this.getUserId() + "\n"); - stringBuilder.append("cover url=" + this.getCoverUrl() + "\n"); - stringBuilder.append("fullname=" + this.getFullname() + "\n"); - stringBuilder.append("email=" + this.getEmail() + "\n"); - stringBuilder.append("description=" + this.getDescription() + "\n"); - stringBuilder.append("followers=" + this.getFollowers() + "\n"); - stringBuilder.append("*******************************"); - - return stringBuilder.toString(); - } } diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStoreFactory.java b/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStoreFactory.java index 72b8f9cf..defc85df 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStoreFactory.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStoreFactory.java @@ -58,8 +58,8 @@ public UserDataStore create(int userId) { * Create {@link UserDataStore} to retrieve data from the Cloud. */ public UserDataStore createCloudDataStore() { - UserEntityJsonMapper userEntityJsonMapper = new UserEntityJsonMapper(); - RestApi restApi = new RestApiImpl(this.context, userEntityJsonMapper); + final UserEntityJsonMapper userEntityJsonMapper = new UserEntityJsonMapper(); + final RestApi restApi = new RestApiImpl(this.context, userEntityJsonMapper); return new CloudUserDataStore(restApi, this.userCache); } diff --git a/domain/src/main/java/com/fernandocejas/android10/sample/domain/User.java b/domain/src/main/java/com/fernandocejas/android10/sample/domain/User.java index d807960c..21bf5378 100644 --- a/domain/src/main/java/com/fernandocejas/android10/sample/domain/User.java +++ b/domain/src/main/java/com/fernandocejas/android10/sample/domain/User.java @@ -75,19 +75,4 @@ public int getFollowers() { public void setFollowers(int followers) { this.followers = followers; } - - @Override public String toString() { - final StringBuilder stringBuilder = new StringBuilder(); - - stringBuilder.append("***** User Details *****\n"); - stringBuilder.append("id=" + this.getUserId() + "\n"); - stringBuilder.append("cover url=" + this.getCoverUrl() + "\n"); - stringBuilder.append("fullname=" + this.getFullName() + "\n"); - stringBuilder.append("email=" + this.getEmail() + "\n"); - stringBuilder.append("description=" + this.getDescription() + "\n"); - stringBuilder.append("followers=" + this.getFollowers() + "\n"); - stringBuilder.append("*******************************"); - - return stringBuilder.toString(); - } } diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mapper/UserModelDataMapper.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mapper/UserModelDataMapper.java index 3c64bac3..489df6d9 100644 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mapper/UserModelDataMapper.java +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mapper/UserModelDataMapper.java @@ -43,7 +43,7 @@ public UserModel transform(User user) { if (user == null) { throw new IllegalArgumentException("Cannot transform a null value"); } - UserModel userModel = new UserModel(user.getUserId()); + final UserModel userModel = new UserModel(user.getUserId()); userModel.setCoverUrl(user.getCoverUrl()); userModel.setFullName(user.getFullName()); userModel.setEmail(user.getEmail()); diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/model/UserModel.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/model/UserModel.java index 20289507..fd6d25b0 100644 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/model/UserModel.java +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/model/UserModel.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -75,19 +75,4 @@ public int getFollowers() { public void setFollowers(int followers) { this.followers = followers; } - - @Override public String toString() { - final StringBuilder stringBuilder = new StringBuilder(); - - stringBuilder.append("***** User Model Details *****\n"); - stringBuilder.append("id=" + this.getUserId() + "\n"); - stringBuilder.append("cover url=" + this.getCoverUrl() + "\n"); - stringBuilder.append("fullname=" + this.getFullName() + "\n"); - stringBuilder.append("email=" + this.getEmail() + "\n"); - stringBuilder.append("description=" + this.getDescription() + "\n"); - stringBuilder.append("followers=" + this.getFollowers() + "\n"); - stringBuilder.append("*******************************"); - - return stringBuilder.toString(); - } } From 7339de70e1d691b8f24b9ca0472536dc24838b96 Mon Sep 17 00:00:00 2001 From: Fernando Cejas Date: Mon, 19 Dec 2016 17:15:30 -0300 Subject: [PATCH 14/32] Rename JsonSerializer to Serializer and make it generic. --- .../sample/data/cache/UserCacheImpl.java | 31 +++++++++---------- .../{JsonSerializer.java => Serializer.java} | 21 ++++++------- ...erializerTest.java => SerializerTest.java} | 14 ++++----- 3 files changed, 31 insertions(+), 35 deletions(-) rename data/src/main/java/com/fernandocejas/android10/sample/data/cache/serializer/{JsonSerializer.java => Serializer.java} (64%) rename data/src/test/java/com/fernandocejas/android10/sample/data/cache/serializer/{JsonSerializerTest.java => SerializerTest.java} (82%) diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCacheImpl.java b/data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCacheImpl.java index 9538dc3a..e53d5a7d 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCacheImpl.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCacheImpl.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -16,7 +16,7 @@ package com.fernandocejas.android10.sample.data.cache; import android.content.Context; -import com.fernandocejas.android10.sample.data.cache.serializer.JsonSerializer; +import com.fernandocejas.android10.sample.data.cache.serializer.Serializer; import com.fernandocejas.android10.sample.data.entity.UserEntity; import com.fernandocejas.android10.sample.data.exception.UserNotFoundException; import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor; @@ -39,7 +39,7 @@ public class UserCacheImpl implements UserCache { private final Context context; private final File cacheDir; - private final JsonSerializer serializer; + private final Serializer serializer; private final FileManager fileManager; private final ThreadExecutor threadExecutor; @@ -47,27 +47,27 @@ public class UserCacheImpl implements UserCache { * Constructor of the class {@link UserCacheImpl}. * * @param context A - * @param userCacheSerializer {@link JsonSerializer} for object serialization. + * @param serializer {@link Serializer} for object serialization. * @param fileManager {@link FileManager} for saving serialized objects to the file system. */ - @Inject - UserCacheImpl(Context context, JsonSerializer userCacheSerializer, + @Inject UserCacheImpl(Context context, Serializer serializer, FileManager fileManager, ThreadExecutor executor) { - if (context == null || userCacheSerializer == null || fileManager == null || executor == null) { + if (context == null || serializer == null || fileManager == null || executor == null) { throw new IllegalArgumentException("Invalid null parameter"); } this.context = context.getApplicationContext(); this.cacheDir = this.context.getCacheDir(); - this.serializer = userCacheSerializer; + this.serializer = serializer; this.fileManager = fileManager; this.threadExecutor = executor; } @Override public Observable get(final int userId) { return Observable.create(subscriber -> { - File userEntityFile = UserCacheImpl.this.buildFile(userId); - String fileContent = UserCacheImpl.this.fileManager.readFileContent(userEntityFile); - UserEntity userEntity = UserCacheImpl.this.serializer.deserialize(fileContent); + final File userEntityFile = UserCacheImpl.this.buildFile(userId); + final String fileContent = UserCacheImpl.this.fileManager.readFileContent(userEntityFile); + final UserEntity userEntity = + UserCacheImpl.this.serializer.deserialize(fileContent, UserEntity.class); if (userEntity != null) { subscriber.onNext(userEntity); @@ -80,11 +80,10 @@ public class UserCacheImpl implements UserCache { @Override public void put(UserEntity userEntity) { if (userEntity != null) { - File userEntityFile = this.buildFile(userEntity.getUserId()); + final File userEntityFile = this.buildFile(userEntity.getUserId()); if (!isCached(userEntity.getUserId())) { - String jsonString = this.serializer.serialize(userEntity); - this.executeAsynchronously(new CacheWriter(this.fileManager, userEntityFile, - jsonString)); + final String jsonString = this.serializer.serialize(userEntity, UserEntity.class); + this.executeAsynchronously(new CacheWriter(this.fileManager, userEntityFile, jsonString)); setLastCacheUpdateTimeMillis(); } } @@ -132,7 +131,7 @@ private File buildFile(int userId) { * Set in millis, the last time the cache was accessed. */ private void setLastCacheUpdateTimeMillis() { - long currentMillis = System.currentTimeMillis(); + final long currentMillis = System.currentTimeMillis(); this.fileManager.writeToPreferences(this.context, SETTINGS_FILE_NAME, SETTINGS_KEY_LAST_CACHE_UPDATE, currentMillis); } diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/cache/serializer/JsonSerializer.java b/data/src/main/java/com/fernandocejas/android10/sample/data/cache/serializer/Serializer.java similarity index 64% rename from data/src/main/java/com/fernandocejas/android10/sample/data/cache/serializer/JsonSerializer.java rename to data/src/main/java/com/fernandocejas/android10/sample/data/cache/serializer/Serializer.java index 61d35779..f9a9c100 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/cache/serializer/JsonSerializer.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/cache/serializer/Serializer.java @@ -15,38 +15,35 @@ */ package com.fernandocejas.android10.sample.data.cache.serializer; -import com.fernandocejas.android10.sample.data.entity.UserEntity; import com.google.gson.Gson; import javax.inject.Inject; import javax.inject.Singleton; /** - * Class user as Serializer/Deserializer for user entities. + * Json Serializer/Deserializer. */ @Singleton -public class JsonSerializer { +public class Serializer { private final Gson gson = new Gson(); - @Inject - JsonSerializer() {} + @Inject Serializer() {} /** * Serialize an object to Json. * - * @param userEntity {@link UserEntity} to serialize. + * @param object to serialize. */ - public String serialize(UserEntity userEntity) { - return gson.toJson(userEntity, UserEntity.class); + public String serialize(Object object, Class clazz) { + return gson.toJson(object, clazz); } /** * Deserialize a json representation of an object. * - * @param jsonString A json string to deserialize. - * @return {@link UserEntity} + * @param string A json string to deserialize. */ - public UserEntity deserialize(String jsonString) { - return gson.fromJson(jsonString, UserEntity.class); + public T deserialize(String string, Class clazz) { + return gson.fromJson(string, clazz); } } diff --git a/data/src/test/java/com/fernandocejas/android10/sample/data/cache/serializer/JsonSerializerTest.java b/data/src/test/java/com/fernandocejas/android10/sample/data/cache/serializer/SerializerTest.java similarity index 82% rename from data/src/test/java/com/fernandocejas/android10/sample/data/cache/serializer/JsonSerializerTest.java rename to data/src/test/java/com/fernandocejas/android10/sample/data/cache/serializer/SerializerTest.java index da15891a..68383665 100644 --- a/data/src/test/java/com/fernandocejas/android10/sample/data/cache/serializer/JsonSerializerTest.java +++ b/data/src/test/java/com/fernandocejas/android10/sample/data/cache/serializer/SerializerTest.java @@ -24,7 +24,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; -public class JsonSerializerTest extends ApplicationTestCase { +public class SerializerTest extends ApplicationTestCase { private static final String JSON_RESPONSE = "{\n" + " \"id\": 1,\n" @@ -35,18 +35,18 @@ public class JsonSerializerTest extends ApplicationTestCase { + " \"email\": \"jcooper@babbleset.edu\"\n" + "}"; - private JsonSerializer jsonSerializer; + private Serializer serializer; @Before public void setUp() { - jsonSerializer = new JsonSerializer(); + serializer = new Serializer(); } @Test public void testSerializeHappyCase() { - UserEntity userEntityOne = jsonSerializer.deserialize(JSON_RESPONSE); - String jsonString = jsonSerializer.serialize(userEntityOne); - UserEntity userEntityTwo = jsonSerializer.deserialize(jsonString); + final UserEntity userEntityOne = serializer.deserialize(JSON_RESPONSE, UserEntity.class); + final String jsonString = serializer.serialize(userEntityOne, UserEntity.class); + final UserEntity userEntityTwo = serializer.deserialize(jsonString, UserEntity.class); assertThat(userEntityOne.getUserId(), is(userEntityTwo.getUserId())); assertThat(userEntityOne.getFullname(), is(equalTo(userEntityTwo.getFullname()))); @@ -55,7 +55,7 @@ public void testSerializeHappyCase() { @Test public void testDesearializeHappyCase() { - UserEntity userEntity = jsonSerializer.deserialize(JSON_RESPONSE); + final UserEntity userEntity = serializer.deserialize(JSON_RESPONSE, UserEntity.class); assertThat(userEntity.getUserId(), is(1)); assertThat(userEntity.getFullname(), is("Simon Hill")); From 6f9cf3a6feb1920d09ccaf184a665384ae8e640f Mon Sep 17 00:00:00 2001 From: Fernando Cejas Date: Wed, 21 Dec 2016 17:29:10 -0300 Subject: [PATCH 15/32] Bump dependencies: - Gradle. - Android Build tools. - Robolectric: fix failing tests. --- build.gradle | 2 +- buildsystem/dependencies.gradle | 6 +- .../sample/data/ApplicationTestCase.java | 9 ++- .../sample/data/cache/FileManagerTest.java | 13 ++-- gradle/wrapper/gradle-wrapper.jar | Bin 53637 -> 54227 bytes gradle/wrapper/gradle-wrapper.properties | 4 +- gradlew | 68 ++++++++++-------- gradlew.bat | 14 ++-- 8 files changed, 61 insertions(+), 55 deletions(-) diff --git a/build.gradle b/build.gradle index 0762131e..14114ab4 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:2.1.0' + classpath 'com.android.tools.build:gradle:2.2.2' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' classpath "com.fernandocejas.frodo:frodo-plugin:0.8.3" } diff --git a/buildsystem/dependencies.gradle b/buildsystem/dependencies.gradle index 3e550717..5414bf7d 100644 --- a/buildsystem/dependencies.gradle +++ b/buildsystem/dependencies.gradle @@ -6,13 +6,13 @@ allprojects { ext { //Android - androidBuildToolsVersion = "23.0.1" + androidBuildToolsVersion = "24.0.1" androidMinSdkVersion = 15 androidTargetSdkVersion = 21 androidCompileSdkVersion = 21 //Libraries - daggerVersion = '2.0.2' + daggerVersion = '2.8' butterKnifeVersion = '7.0.1' recyclerViewVersion = '21.0.3' rxJavaVersion = '1.0.14' @@ -24,7 +24,7 @@ ext { androidAnnotationsVersion = '21.0.3' //Testing - robolectricVersion = '3.0' + robolectricVersion = '3.1.1' jUnitVersion = '4.12' assertJVersion = '1.7.1' mockitoVersion = '1.9.5' diff --git a/data/src/test/java/com/fernandocejas/android10/sample/data/ApplicationTestCase.java b/data/src/test/java/com/fernandocejas/android10/sample/data/ApplicationTestCase.java index 680fca12..d8426bcb 100644 --- a/data/src/test/java/com/fernandocejas/android10/sample/data/ApplicationTestCase.java +++ b/data/src/test/java/com/fernandocejas/android10/sample/data/ApplicationTestCase.java @@ -16,11 +16,12 @@ package com.fernandocejas.android10.sample.data; import android.content.Context; +import java.io.File; import org.junit.Rule; import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricGradleTestRunner; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; @@ -28,7 +29,7 @@ * Base class for Robolectric data layer tests. * Inherit from this class to create a test. */ -@RunWith(RobolectricGradleTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, application = ApplicationStub.class, sdk = 21) public abstract class ApplicationTestCase { @@ -40,4 +41,8 @@ public abstract class ApplicationTestCase { public static Context context() { return RuntimeEnvironment.application; } + + public static File cacheDir() { + return RuntimeEnvironment.application.getCacheDir(); + } } diff --git a/data/src/test/java/com/fernandocejas/android10/sample/data/cache/FileManagerTest.java b/data/src/test/java/com/fernandocejas/android10/sample/data/cache/FileManagerTest.java index f71f8a8f..8f0cb488 100644 --- a/data/src/test/java/com/fernandocejas/android10/sample/data/cache/FileManagerTest.java +++ b/data/src/test/java/com/fernandocejas/android10/sample/data/cache/FileManagerTest.java @@ -20,7 +20,6 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.robolectric.RuntimeEnvironment; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; @@ -29,18 +28,16 @@ public class FileManagerTest extends ApplicationTestCase { private FileManager fileManager; - private File cacheDir; @Before public void setUp() { fileManager = new FileManager(); - cacheDir = RuntimeEnvironment.application.getCacheDir(); } @After public void tearDown() { - if (cacheDir != null) { - fileManager.clearDirectory(cacheDir); + if (cacheDir() != null) { + fileManager.clearDirectory(cacheDir()); } } @@ -66,9 +63,7 @@ public void testFileContent() { } private File createDummyFile() { - String dummyFilePath = cacheDir.getPath() + File.separator + "dumyFile"; - File dummyFile = new File(dummyFilePath); - - return dummyFile; + String dummyFilePath = cacheDir().getPath() + File.separator + "dummyFile"; + return new File(dummyFilePath); } } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 05ef575b0cd0173fc735f2857ce4bd594ce4f6bd..51288f9c2f05faf8d42e1a751a387ca7923882c3 100644 GIT binary patch delta 24335 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 delta 23646 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 L2;^bMS;7AUu<72c diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 32e96f4c..437bbca6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Jan 19 23:59:31 CET 2016 +#Wed Dec 21 17:11:04 ART 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.2.1-bin.zip diff --git a/gradlew b/gradlew index 9d82f789..4453ccea 100755 --- a/gradlew +++ b/gradlew @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ############################################################################## ## @@ -6,12 +6,30 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -30,6 +48,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,26 +59,11 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -85,7 +89,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -150,11 +154,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save ( ) { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index aec99730..e95643d6 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -8,14 +8,14 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -46,10 +46,9 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,11 +59,6 @@ set _SKIP=2 if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line From 4b381713f9c0a431c08b009cc97befb01a9770d9 Mon Sep 17 00:00:00 2001 From: Fernando Cejas Date: Wed, 21 Dec 2016 18:34:18 -0300 Subject: [PATCH 16/32] Bump CI build tools. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0f3726f4..44d4f9a5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,8 @@ jdk: oraclejdk8 android: components: - - build-tools-23.0.1 - - android-21 + - build-tools-24.0.1 + - android-23 script: ./gradlew build From 29fcd0930239de616bbad5614c5e2685b2ed6d88 Mon Sep 17 00:00:00 2001 From: Fernando Cejas Date: Wed, 21 Dec 2016 18:39:54 -0300 Subject: [PATCH 17/32] Rollback travis configuration. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 44d4f9a5..0f3726f4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,8 @@ jdk: oraclejdk8 android: components: - - build-tools-24.0.1 - - android-23 + - build-tools-23.0.1 + - android-21 script: ./gradlew build From f433db9bad2034b5020424bd3c6d1c88b87e94a6 Mon Sep 17 00:00:00 2001 From: Fernando Cejas Date: Thu, 22 Dec 2016 09:35:05 -0300 Subject: [PATCH 18/32] Setup travis to run with build tools 24 --- .travis.yml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0f3726f4..43655f80 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,18 @@ jdk: oraclejdk8 android: components: - - build-tools-23.0.1 - - android-21 + - tools + - platform-tools + - tools + - build-tools-24.0.1 + - android-23 + - extra-google-m2repository + - extra-android-m2repository + +licenses: + - 'android-sdk-preview-license-.+' + - 'android-sdk-license-.+' + - 'google-gdk-license-.+' script: ./gradlew build From 988e02b25b4a8ef66d27bd5146d8c50413aa34fe Mon Sep 17 00:00:00 2001 From: Fernando Cejas Date: Thu, 22 Dec 2016 17:53:19 -0300 Subject: [PATCH 19/32] Refactor UserModule to use constant for injection rather than string literals. --- .../android10/sample/domain/interactor/GetUserDetails.java | 2 ++ .../android10/sample/domain/interactor/GetUserList.java | 2 ++ .../sample/presentation/internal/di/modules/UserModule.java | 6 +++--- .../sample/presentation/presenter/UserDetailsPresenter.java | 3 ++- .../sample/presentation/presenter/UserListPresenter.java | 3 ++- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetails.java b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetails.java index 134fc4d0..3767b09b 100644 --- a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetails.java +++ b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetails.java @@ -28,6 +28,8 @@ */ public class GetUserDetails extends UseCase { + public static final String NAME = "userDetails"; + private final int userId; private final UserRepository userRepository; diff --git a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java index 27464bee..d98eebe7 100644 --- a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java +++ b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java @@ -28,6 +28,8 @@ */ public class GetUserList extends UseCase { + public static final String NAME = "userList"; + private final UserRepository userRepository; @Inject diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/modules/UserModule.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/modules/UserModule.java index 3f12d332..32f8c218 100644 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/modules/UserModule.java +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/modules/UserModule.java @@ -40,14 +40,14 @@ public UserModule(int userId) { this.userId = userId; } - @Provides @PerActivity @Named("userList") UseCase provideGetUserListUseCase( + @Provides @PerActivity @Named(GetUserList.NAME) UseCase provideGetUserListUseCase( GetUserList getUserList) { return getUserList; } - @Provides @PerActivity @Named("userDetails") UseCase provideGetUserDetailsUseCase( + @Provides @PerActivity @Named(GetUserDetails.NAME) UseCase provideGetUserDetailsUseCase( UserRepository userRepository, ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) { return new GetUserDetails(userId, userRepository, threadExecutor, postExecutionThread); } -} \ No newline at end of file +} 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 index e10967c0..367911f4 100644 --- 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 @@ -20,6 +20,7 @@ import com.fernandocejas.android10.sample.domain.exception.DefaultErrorBundle; import com.fernandocejas.android10.sample.domain.exception.ErrorBundle; import com.fernandocejas.android10.sample.domain.interactor.DefaultSubscriber; +import com.fernandocejas.android10.sample.domain.interactor.GetUserDetails; import com.fernandocejas.android10.sample.domain.interactor.UseCase; import com.fernandocejas.android10.sample.presentation.exception.ErrorMessageFactory; import com.fernandocejas.android10.sample.presentation.internal.di.PerActivity; @@ -43,7 +44,7 @@ public class UserDetailsPresenter implements Presenter { private final UserModelDataMapper userModelDataMapper; @Inject - public UserDetailsPresenter(@Named("userDetails") UseCase getUserDetailsUseCase, + public UserDetailsPresenter(@Named(GetUserDetails.NAME) UseCase getUserDetailsUseCase, UserModelDataMapper userModelDataMapper) { this.getUserDetailsUseCase = getUserDetailsUseCase; this.userModelDataMapper = userModelDataMapper; 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 index 72cf4b51..7b4ccb84 100644 --- 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 @@ -20,6 +20,7 @@ import com.fernandocejas.android10.sample.domain.exception.DefaultErrorBundle; import com.fernandocejas.android10.sample.domain.exception.ErrorBundle; import com.fernandocejas.android10.sample.domain.interactor.DefaultSubscriber; +import com.fernandocejas.android10.sample.domain.interactor.GetUserList; import com.fernandocejas.android10.sample.domain.interactor.UseCase; import com.fernandocejas.android10.sample.presentation.exception.ErrorMessageFactory; import com.fernandocejas.android10.sample.presentation.internal.di.PerActivity; @@ -44,7 +45,7 @@ public class UserListPresenter implements Presenter { private final UserModelDataMapper userModelDataMapper; @Inject - public UserListPresenter(@Named("userList") UseCase getUserListUserCase, + public UserListPresenter(@Named(GetUserList.NAME) UseCase getUserListUserCase, UserModelDataMapper userModelDataMapper) { this.getUserListUseCase = getUserListUserCase; this.userModelDataMapper = userModelDataMapper; From dfd61c29170b74298780ebb7cc991e0b2edbdfe6 Mon Sep 17 00:00:00 2001 From: Fernando Cejas Date: Fri, 23 Dec 2016 15:22:42 -0300 Subject: [PATCH 20/32] Dynamic parameters in use cases. --- buildsystem/dependencies.gradle | 5 +- domain/build.gradle | 4 +- .../domain/interactor/GetUserDetails.java | 21 +++++--- .../sample/domain/interactor/GetUserList.java | 3 +- .../sample/domain/interactor/Params.java | 50 +++++++++++++++++++ .../sample/domain/interactor/UseCase.java | 17 ++++--- .../android10/sample/domain/UserTest.java | 7 ++- .../domain/interactor/GetUserDetailsTest.java | 36 +++++++++++-- .../domain/interactor/GetUserListTest.java | 15 +++++- .../sample/domain/interactor/ParamsTest.java | 49 ++++++++++++++++++ .../sample/domain/interactor/UseCaseTest.java | 22 ++++---- .../presenter/UserDetailsPresenterTest.java | 7 ++- .../test/presenter/UserListPresenterTest.java | 3 +- .../internal/di/modules/UserModule.java | 8 +-- .../presenter/UserDetailsPresenter.java | 25 ++++------ .../presenter/UserListPresenter.java | 3 +- .../view/activity/UserDetailsActivity.java | 4 +- .../view/fragment/UserDetailsFragment.java | 23 ++++++++- 18 files changed, 233 insertions(+), 69 deletions(-) create mode 100644 domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/Params.java create mode 100644 domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/ParamsTest.java diff --git a/buildsystem/dependencies.gradle b/buildsystem/dependencies.gradle index 5414bf7d..a75a85e2 100644 --- a/buildsystem/dependencies.gradle +++ b/buildsystem/dependencies.gradle @@ -22,6 +22,7 @@ ext { gsonVersion = '2.3' okHttpVersion = '2.5.0' androidAnnotationsVersion = '21.0.3' + arrowVersion = '1.0.0' //Testing robolectricVersion = '3.1.1' @@ -42,7 +43,7 @@ ext { recyclerView: "com.android.support:recyclerview-v7:${recyclerViewVersion}", rxJava: "io.reactivex:rxjava:${rxJavaVersion}", rxAndroid: "io.reactivex:rxandroid:${rxAndroidVersion}", - javaxAnnotation: "javax.annotation:jsr250-api:${javaxAnnotationVersion}", + javaxAnnotation: "javax.annotation:jsr250-api:${javaxAnnotationVersion}" ] presentationTestDependencies = [ @@ -57,11 +58,13 @@ ext { javaxAnnotation: "javax.annotation:jsr250-api:${javaxAnnotationVersion}", javaxInject: "javax.inject:javax.inject:${javaxInjectVersion}", rxJava: "io.reactivex:rxjava:${rxJavaVersion}", + arrow: "com.fernandocejas:arrow:${arrowVersion}" ] domainTestDependencies = [ junit: "junit:junit:${jUnitVersion}", mockito: "org.mockito:mockito-core:${mockitoVersion}", + assertj: "org.assertj:assertj-core:${assertJVersion}" ] dataDependencies = [ diff --git a/domain/build.gradle b/domain/build.gradle index 84ecd764..894a22d3 100644 --- a/domain/build.gradle +++ b/domain/build.gradle @@ -23,7 +23,9 @@ dependencies { compile domainDependencies.javaxInject compile domainDependencies.rxJava + compile domainDependencies.arrow testCompile domainTestDependencies.junit testCompile domainTestDependencies.mockito -} \ No newline at end of file + testCompile domainTestDependencies.assertj +} diff --git a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetails.java b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetails.java index 3767b09b..e4f1b3bc 100644 --- a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetails.java +++ b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetails.java @@ -19,6 +19,8 @@ import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread; import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor; import com.fernandocejas.android10.sample.domain.repository.UserRepository; +import com.fernandocejas.arrow.annotations.VisibleForTesting; +import com.fernandocejas.arrow.optional.Optional; import javax.inject.Inject; import rx.Observable; @@ -29,19 +31,26 @@ public class GetUserDetails extends UseCase { public static final String NAME = "userDetails"; + public static final String PARAM_USER_ID_KEY = "userId"; + + @VisibleForTesting + static final int PARAM_USER_ID_DEFAULT_VALUE = -1; - private final int userId; private final UserRepository userRepository; @Inject - public GetUserDetails(int userId, UserRepository userRepository, - ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) { + public GetUserDetails(UserRepository userRepository, ThreadExecutor threadExecutor, + PostExecutionThread postExecutionThread) { super(threadExecutor, postExecutionThread); - this.userId = userId; this.userRepository = userRepository; } - @Override protected Observable buildUseCaseObservable() { - return this.userRepository.user(this.userId); + @Override protected Observable buildUseCaseObservable(Optional params) { + if (params.isPresent()) { + final int userId = params.get().getInt(PARAM_USER_ID_KEY, PARAM_USER_ID_DEFAULT_VALUE); + return this.userRepository.user(userId); + } else { + return Observable.empty(); + } } } diff --git a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java index d98eebe7..b63d340f 100644 --- a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java +++ b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java @@ -19,6 +19,7 @@ import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread; import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor; import com.fernandocejas.android10.sample.domain.repository.UserRepository; +import com.fernandocejas.arrow.optional.Optional; import javax.inject.Inject; import rx.Observable; @@ -39,7 +40,7 @@ public class GetUserList extends UseCase { this.userRepository = userRepository; } - @Override public Observable buildUseCaseObservable() { + @Override public Observable buildUseCaseObservable(Optional params) { return this.userRepository.users(); } } diff --git a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/Params.java b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/Params.java new file mode 100644 index 00000000..b2113e02 --- /dev/null +++ b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/Params.java @@ -0,0 +1,50 @@ +/** + * Copyright (C) 2016 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.domain.interactor; + +import java.util.HashMap; +import java.util.Map; + +/** + * Class backed by a Map, used to pass parameters to {@link UseCase} instances. + */ +public final class Params { + public static final Params EMPTY = Params.create(); + + private final Map parameters = new HashMap<>(); + + private Params() {} + + public static Params create() { + return new Params(); + } + + public void putInt(String key, int value) { + parameters.put(key, value); + } + + int getInt(String key, int defaultValue) { + final Object object = parameters.get(key); + if (object == null) { + return defaultValue; + } + try { + return (int) object; + } catch (ClassCastException e) { + return defaultValue; + } + } +} diff --git a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/UseCase.java b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/UseCase.java index 5bb63005..36888a93 100644 --- a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/UseCase.java +++ b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/UseCase.java @@ -17,8 +17,9 @@ import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread; import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor; -import rx.Subscriber; +import com.fernandocejas.arrow.optional.Optional; import rx.Observable; +import rx.Subscriber; import rx.Subscription; import rx.schedulers.Schedulers; import rx.subscriptions.Subscriptions; @@ -38,8 +39,7 @@ public abstract class UseCase { private Subscription subscription = Subscriptions.empty(); - protected UseCase(ThreadExecutor threadExecutor, - PostExecutionThread postExecutionThread) { + protected UseCase(ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) { this.threadExecutor = threadExecutor; this.postExecutionThread = postExecutionThread; } @@ -47,17 +47,18 @@ protected UseCase(ThreadExecutor threadExecutor, /** * Builds an {@link rx.Observable} which will be used when executing the current {@link UseCase}. */ - protected abstract Observable buildUseCaseObservable(); + protected abstract Observable buildUseCaseObservable(Optional params); /** * Executes the current use case. * - * @param useCaseSubscriber The guy who will be listen to the observable build - * with {@link #buildUseCaseObservable()}. + * @param useCaseSubscriber Subscriber which will be listening to the observable build + * by {@link #buildUseCaseObservable(Optional)} ()} method. + * @param params Parameters used to build execute this use case. */ @SuppressWarnings("unchecked") - public void execute(Subscriber useCaseSubscriber) { - this.subscription = this.buildUseCaseObservable() + public void execute(Subscriber useCaseSubscriber, Params params) { + this.subscription = this.buildUseCaseObservable(Optional.of(params)) .subscribeOn(Schedulers.from(threadExecutor)) .observeOn(postExecutionThread.getScheduler()) .subscribe(useCaseSubscriber); diff --git a/domain/src/test/java/com/fernandocejas/android10/sample/domain/UserTest.java b/domain/src/test/java/com/fernandocejas/android10/sample/domain/UserTest.java index ea45784a..e321d410 100644 --- a/domain/src/test/java/com/fernandocejas/android10/sample/domain/UserTest.java +++ b/domain/src/test/java/com/fernandocejas/android10/sample/domain/UserTest.java @@ -18,8 +18,7 @@ import org.junit.Before; import org.junit.Test; -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; public class UserTest { @@ -34,8 +33,8 @@ public void setUp() { @Test public void testUserConstructorHappyCase() { - int userId = user.getUserId(); + final int userId = user.getUserId(); - assertThat(userId, is(FAKE_USER_ID)); + assertThat(userId).isEqualTo(FAKE_USER_ID); } } diff --git a/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetailsTest.java b/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetailsTest.java index f0c119d7..feaa0374 100644 --- a/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetailsTest.java +++ b/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetailsTest.java @@ -18,12 +18,15 @@ import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread; import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor; import com.fernandocejas.android10.sample.domain.repository.UserRepository; +import com.fernandocejas.arrow.optional.Optional; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; +import rx.Observable; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; @@ -31,7 +34,7 @@ @RunWith(MockitoJUnitRunner.class) public class GetUserDetailsTest { - private static final int FAKE_USER_ID = 123; + private static final int USER_ID = 123; private GetUserDetails getUserDetails; @@ -41,15 +44,38 @@ public class GetUserDetailsTest { @Before public void setUp() { - getUserDetails = new GetUserDetails(FAKE_USER_ID, mockUserRepository, - mockThreadExecutor, mockPostExecutionThread); + getUserDetails = new GetUserDetails(mockUserRepository, mockThreadExecutor, + mockPostExecutionThread); } @Test public void testGetUserDetailsUseCaseObservableHappyCase() { - getUserDetails.buildUseCaseObservable(); + final Params params = Params.create(); + params.putInt(GetUserDetails.PARAM_USER_ID_KEY, USER_ID); - verify(mockUserRepository).user(FAKE_USER_ID); + getUserDetails.buildUseCaseObservable(Optional.of(params)); + + verify(mockUserRepository).user(USER_ID); + verifyNoMoreInteractions(mockUserRepository); + verifyZeroInteractions(mockPostExecutionThread); + verifyZeroInteractions(mockThreadExecutor); + } + + @Test + public void testShouldReturnEmptyObservableWhenNoParameters() { + final Observable observable = getUserDetails.buildUseCaseObservable(Optional.absent()); + + assertThat(observable).isEqualTo(Observable.empty()); + verifyZeroInteractions(mockUserRepository); + verifyZeroInteractions(mockPostExecutionThread); + verifyZeroInteractions(mockThreadExecutor); + } + + @Test + public void testShouldUseDefaultUserIdValueWhenNoUserIdParameter() { + getUserDetails.buildUseCaseObservable(Optional.of(Params.create())); + + verify(mockUserRepository).user(GetUserDetails.PARAM_USER_ID_DEFAULT_VALUE); verifyNoMoreInteractions(mockUserRepository); verifyZeroInteractions(mockPostExecutionThread); verifyZeroInteractions(mockThreadExecutor); diff --git a/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserListTest.java b/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserListTest.java index 0ea91659..73cf0e14 100644 --- a/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserListTest.java +++ b/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserListTest.java @@ -18,12 +18,14 @@ import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread; import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor; import com.fernandocejas.android10.sample.domain.repository.UserRepository; +import com.fernandocejas.arrow.optional.Optional; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; @@ -45,11 +47,22 @@ public void setUp() { @Test public void testGetUserListUseCaseObservableHappyCase() { - getUserList.buildUseCaseObservable(); + getUserList.buildUseCaseObservable(Optional.of(Params.EMPTY)); verify(mockUserRepository).users(); verifyNoMoreInteractions(mockUserRepository); verifyZeroInteractions(mockThreadExecutor); verifyZeroInteractions(mockPostExecutionThread); } + + @Test + @SuppressWarnings("unchecked") + public void testThereShouldNotBeAnyInteractionWithParams() { + Optional params = mock(Optional.class); + + getUserList.buildUseCaseObservable(params); + + verify(mockUserRepository).users(); + verifyZeroInteractions(params); + } } diff --git a/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/ParamsTest.java b/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/ParamsTest.java new file mode 100644 index 00000000..4d95afc1 --- /dev/null +++ b/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/ParamsTest.java @@ -0,0 +1,49 @@ +/** + * Copyright (C) 2016 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.domain.interactor; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(MockitoJUnitRunner.class) +public class ParamsTest { + + private Params params; + + @Before + public void setUp() { + params = Params.create(); + } + + @Test + public void testShouldReturnIntValue() { + params.putInt("key01", 3); + + assertThat(params.getInt("key01", 5)).isEqualTo(3); + } + + @Test + public void testShouldReturnIntDefaultValue() { + params.putInt("key01", 3); + params.putInt("key02", 4); + + assertThat(params.getInt("key03", 5)).isEqualTo(5); + } +} diff --git a/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/UseCaseTest.java b/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/UseCaseTest.java index 7f290e33..291cc2be 100644 --- a/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/UseCaseTest.java +++ b/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/UseCaseTest.java @@ -17,6 +17,7 @@ import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread; import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor; +import com.fernandocejas.arrow.optional.Optional; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -27,8 +28,7 @@ import rx.observers.TestSubscriber; import rx.schedulers.TestScheduler; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.Is.is; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; @RunWith(MockitoJUnitRunner.class) @@ -51,35 +51,33 @@ public void testBuildUseCaseObservableReturnCorrectResult() { TestScheduler testScheduler = new TestScheduler(); given(mockPostExecutionThread.getScheduler()).willReturn(testScheduler); - useCase.execute(testSubscriber); + useCase.execute(testSubscriber, Params.EMPTY); - assertThat(testSubscriber.getOnNextEvents().size(), is(0)); + assertThat(testSubscriber.getOnNextEvents().size()).isZero(); } @Test public void testSubscriptionWhenExecutingUseCase() { TestSubscriber testSubscriber = new TestSubscriber<>(); - useCase.execute(testSubscriber); + useCase.execute(testSubscriber, Params.EMPTY); useCase.unsubscribe(); - assertThat(testSubscriber.isUnsubscribed(), is(true)); + assertThat(testSubscriber.isUnsubscribed()).isTrue(); } private static class UseCaseTestClass extends UseCase { - UseCaseTestClass( - ThreadExecutor threadExecutor, - PostExecutionThread postExecutionThread) { + UseCaseTestClass(ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) { super(threadExecutor, postExecutionThread); } - @Override protected Observable buildUseCaseObservable() { + @Override protected Observable buildUseCaseObservable(Optional params) { return Observable.empty(); } - @Override public void execute(Subscriber UseCaseSubscriber) { - super.execute(UseCaseSubscriber); + @Override public void execute(Subscriber useCaseSubscriber, Params params) { + super.execute(useCaseSubscriber, Params.EMPTY); } } } 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 07d8bc31..e2e0c536 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 @@ -27,6 +27,7 @@ import org.mockito.runners.MockitoJUnitRunner; import rx.Subscriber; +import com.fernandocejas.android10.sample.domain.interactor.Params; import static org.mockito.BDDMockito.given; import static org.mockito.Matchers.any; import static org.mockito.Mockito.verify; @@ -34,6 +35,8 @@ @RunWith(MockitoJUnitRunner.class) public class UserDetailsPresenterTest { + private static final int USER_ID = 1; + private UserDetailsPresenter userDetailsPresenter; @Mock private Context mockContext; @@ -51,10 +54,10 @@ public void setUp() { public void testUserDetailsPresenterInitialize() { given(mockUserDetailsView.context()).willReturn(mockContext); - userDetailsPresenter.initialize(); + userDetailsPresenter.initialize(USER_ID); verify(mockUserDetailsView).hideRetry(); verify(mockUserDetailsView).showLoading(); - verify(mockGetUserDetails).execute(any(Subscriber.class)); + verify(mockGetUserDetails).execute(any(Subscriber.class), any(Params.class)); } } 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 e02d3fc2..eabe55ec 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 @@ -17,6 +17,7 @@ import android.content.Context; import com.fernandocejas.android10.sample.domain.interactor.GetUserList; +import com.fernandocejas.android10.sample.domain.interactor.Params; import com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper; import com.fernandocejas.android10.sample.presentation.presenter.UserListPresenter; import com.fernandocejas.android10.sample.presentation.view.UserListView; @@ -55,6 +56,6 @@ public void testUserListPresenterInitialize() { verify(mockUserListView).hideRetry(); verify(mockUserListView).showLoading(); - verify(mockGetUserList).execute(any(Subscriber.class)); + verify(mockGetUserList).execute(any(Subscriber.class), any(Params.class)); } } diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/modules/UserModule.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/modules/UserModule.java index 32f8c218..94cbebd8 100644 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/modules/UserModule.java +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/modules/UserModule.java @@ -32,14 +32,8 @@ @Module public class UserModule { - private int userId = -1; - public UserModule() {} - public UserModule(int userId) { - this.userId = userId; - } - @Provides @PerActivity @Named(GetUserList.NAME) UseCase provideGetUserListUseCase( GetUserList getUserList) { return getUserList; @@ -48,6 +42,6 @@ public UserModule(int userId) { @Provides @PerActivity @Named(GetUserDetails.NAME) UseCase provideGetUserDetailsUseCase( UserRepository userRepository, ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) { - return new GetUserDetails(userId, userRepository, threadExecutor, postExecutionThread); + return new GetUserDetails(userRepository, threadExecutor, postExecutionThread); } } 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 index 367911f4..48869308 100644 --- 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 @@ -22,6 +22,7 @@ import com.fernandocejas.android10.sample.domain.interactor.DefaultSubscriber; import com.fernandocejas.android10.sample.domain.interactor.GetUserDetails; import com.fernandocejas.android10.sample.domain.interactor.UseCase; +import com.fernandocejas.android10.sample.domain.interactor.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; @@ -64,19 +65,19 @@ public void setView(@NonNull UserDetailsView view) { } /** - * Initializes the presenter by start retrieving user details. + * Initializes the presenter by showing/hiding proper views + * and retrieving user details. */ - public void initialize() { - this.loadUserDetails(); - } - - /** - * Loads user details. - */ - private void loadUserDetails() { + public void initialize(int userId) { this.hideViewRetry(); this.showViewLoading(); - this.getUserDetails(); + this.getUserDetails(userId); + } + + private void getUserDetails(int userId) { + final Params params = Params.create(); + params.putInt(GetUserDetails.PARAM_USER_ID_KEY, userId); + this.getUserDetailsUseCase.execute(new UserDetailsSubscriber(), params); } private void showViewLoading() { @@ -106,10 +107,6 @@ private void showUserDetailsInView(User user) { this.viewDetailsView.renderUser(userModel); } - private void getUserDetails() { - this.getUserDetailsUseCase.execute(new UserDetailsSubscriber()); - } - @RxLogSubscriber private final class UserDetailsSubscriber extends DefaultSubscriber { 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 index 7b4ccb84..55a5e145 100644 --- 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 @@ -22,6 +22,7 @@ import com.fernandocejas.android10.sample.domain.interactor.DefaultSubscriber; import com.fernandocejas.android10.sample.domain.interactor.GetUserList; import com.fernandocejas.android10.sample.domain.interactor.UseCase; +import com.fernandocejas.android10.sample.domain.interactor.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; @@ -113,7 +114,7 @@ private void showUsersCollectionInView(Collection usersCollection) { } private void getUserList() { - this.getUserListUseCase.execute(new UserListSubscriber()); + this.getUserListUseCase.execute(new UserListSubscriber(), Params.EMPTY); } private final class UserListSubscriber extends DefaultSubscriber> { 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/view/activity/UserDetailsActivity.java index fb2ae486..f13ecf22 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/view/activity/UserDetailsActivity.java @@ -13,7 +13,6 @@ 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.internal.di.modules.UserModule; import com.fernandocejas.android10.sample.presentation.view.fragment.UserDetailsFragment; /** @@ -55,7 +54,7 @@ public static Intent getCallingIntent(Context context, int userId) { private void initializeActivity(Bundle savedInstanceState) { if (savedInstanceState == null) { this.userId = getIntent().getIntExtra(INTENT_EXTRA_PARAM_USER_ID, -1); - addFragment(R.id.fragmentContainer, new UserDetailsFragment()); + addFragment(R.id.fragmentContainer, UserDetailsFragment.forUser(userId)); } else { this.userId = savedInstanceState.getInt(INSTANCE_STATE_PARAM_USER_ID); } @@ -65,7 +64,6 @@ private void initializeInjector() { this.userComponent = DaggerUserComponent.builder() .applicationComponent(getApplicationComponent()) .activityModule(getActivityModule()) - .userModule(new UserModule(this.userId)) .build(); } 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 index e5c51334..95d09a6d 100644 --- 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 @@ -21,12 +21,14 @@ 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; @@ -39,6 +41,14 @@ public class UserDetailsFragment extends BaseFragment implements UserDetailsView @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); } @@ -120,14 +130,23 @@ public UserDetailsFragment() { } /** - * Loads all users. + * Load user details. */ private void loadUserDetails() { if (this.userDetailsPresenter != null) { - this.userDetailsPresenter.initialize(); + 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(); From ca067b1006c2d020c6a24ccfd53db0f343e3177f Mon Sep 17 00:00:00 2001 From: Fernando Cejas Date: Fri, 23 Dec 2016 15:31:19 -0300 Subject: [PATCH 21/32] Remove frodo for now. --- build.gradle | 1 - data/build.gradle | 1 - .../fernandocejas/android10/sample/data/net/RestApiImpl.java | 3 --- presentation/build.gradle | 5 ----- .../sample/presentation/presenter/UserDetailsPresenter.java | 2 -- 5 files changed, 12 deletions(-) diff --git a/build.gradle b/build.gradle index 14114ab4..f1963057 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,6 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:2.2.2' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' - classpath "com.fernandocejas.frodo:frodo-plugin:0.8.3" } } diff --git a/data/build.gradle b/data/build.gradle index 1ee552e2..cebd214b 100644 --- a/data/build.gradle +++ b/data/build.gradle @@ -9,7 +9,6 @@ buildscript { apply plugin: 'com.android.library' apply plugin: 'com.neenbedankt.android-apt' -apply plugin: 'com.fernandocejas.frodo' apply plugin: 'me.tatarka.retrolambda' android { diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApiImpl.java b/data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApiImpl.java index 1450000f..717ac5c1 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApiImpl.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApiImpl.java @@ -21,7 +21,6 @@ import com.fernandocejas.android10.sample.data.entity.UserEntity; import com.fernandocejas.android10.sample.data.entity.mapper.UserEntityJsonMapper; import com.fernandocejas.android10.sample.data.exception.NetworkConnectionException; -import com.fernandocejas.frodo.annotation.RxLogObservable; import java.net.MalformedURLException; import java.util.List; import rx.Observable; @@ -48,7 +47,6 @@ public RestApiImpl(Context context, UserEntityJsonMapper userEntityJsonMapper) { this.userEntityJsonMapper = userEntityJsonMapper; } - @RxLogObservable @Override public Observable> userEntityList() { return Observable.create(subscriber -> { if (isThereInternetConnection()) { @@ -70,7 +68,6 @@ public RestApiImpl(Context context, UserEntityJsonMapper userEntityJsonMapper) { }); } - @RxLogObservable @Override public Observable userEntityById(final int userId) { return Observable.create(subscriber -> { if (isThereInternetConnection()) { diff --git a/presentation/build.gradle b/presentation/build.gradle index be2b2abb..d22cda2e 100644 --- a/presentation/build.gradle +++ b/presentation/build.gradle @@ -1,10 +1,5 @@ apply plugin: 'com.android.application' apply plugin: 'com.neenbedankt.android-apt' -apply plugin: 'com.fernandocejas.frodo' - -frodo { - enabled = true -} android { def globalConfiguration = rootProject.extensions.getByName("ext") 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 index 48869308..3fdcaa53 100644 --- 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 @@ -28,7 +28,6 @@ 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 com.fernandocejas.frodo.annotation.RxLogSubscriber; import javax.inject.Inject; import javax.inject.Named; @@ -107,7 +106,6 @@ private void showUserDetailsInView(User user) { this.viewDetailsView.renderUser(userModel); } - @RxLogSubscriber private final class UserDetailsSubscriber extends DefaultSubscriber { @Override public void onCompleted() { From 729ec11ccacbf942247365a426733a46ce9d1b54 Mon Sep 17 00:00:00 2001 From: Fernando Cejas Date: Fri, 23 Dec 2016 16:57:10 -0300 Subject: [PATCH 22/32] Migrate to RxJava 2. --- buildsystem/dependencies.gradle | 14 +++--- .../sample/data/cache/UserCache.java | 4 +- .../sample/data/cache/UserCacheImpl.java | 10 ++-- .../android10/sample/data/net/RestApi.java | 6 +-- .../sample/data/net/RestApiImpl.java | 26 +++++----- .../data/repository/UserDataRepository.java | 2 +- .../datasource/CloudUserDataStore.java | 11 +---- .../datasource/DiskUserDataStore.java | 2 +- .../repository/datasource/UserDataStore.java | 6 +-- .../repository/UserDataRepositoryTest.java | 2 +- .../datasource/CloudUserDataStoreTest.java | 2 +- .../domain/executor/PostExecutionThread.java | 2 +- ...ltSubscriber.java => DefaultObserver.java} | 12 +++-- .../domain/interactor/GetUserDetails.java | 2 +- .../sample/domain/interactor/GetUserList.java | 2 +- .../sample/domain/interactor/UseCase.java | 48 +++++++++++-------- .../domain/repository/UserRepository.java | 6 +-- .../domain/interactor/GetUserDetailsTest.java | 2 +- .../sample/domain/interactor/UseCaseTest.java | 47 +++++++++++------- .../presenter/UserDetailsPresenterTest.java | 4 +- .../test/presenter/UserListPresenterTest.java | 4 +- .../sample/presentation/UIThread.java | 6 +-- .../presenter/UserDetailsPresenter.java | 10 ++-- .../presenter/UserListPresenter.java | 10 ++-- 24 files changed, 129 insertions(+), 111 deletions(-) rename domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/{DefaultSubscriber.java => DefaultObserver.java} (72%) diff --git a/buildsystem/dependencies.gradle b/buildsystem/dependencies.gradle index a75a85e2..d1d535ef 100644 --- a/buildsystem/dependencies.gradle +++ b/buildsystem/dependencies.gradle @@ -15,8 +15,8 @@ ext { daggerVersion = '2.8' butterKnifeVersion = '7.0.1' recyclerViewVersion = '21.0.3' - rxJavaVersion = '1.0.14' - rxAndroidVersion = '1.0.1' + rxJavaVersion = '2.0.2' + rxAndroidVersion = '2.0.1' javaxAnnotationVersion = '1.0' javaxInjectVersion = '1' gsonVersion = '2.3' @@ -41,8 +41,8 @@ ext { dagger: "com.google.dagger:dagger:${daggerVersion}", butterKnife: "com.jakewharton:butterknife:${butterKnifeVersion}", recyclerView: "com.android.support:recyclerview-v7:${recyclerViewVersion}", - rxJava: "io.reactivex:rxjava:${rxJavaVersion}", - rxAndroid: "io.reactivex:rxandroid:${rxAndroidVersion}", + rxJava: "io.reactivex.rxjava2:rxjava:${rxJavaVersion}", + rxAndroid: "io.reactivex.rxjava2:rxandroid:${rxAndroidVersion}", javaxAnnotation: "javax.annotation:jsr250-api:${javaxAnnotationVersion}" ] @@ -57,7 +57,7 @@ ext { domainDependencies = [ javaxAnnotation: "javax.annotation:jsr250-api:${javaxAnnotationVersion}", javaxInject: "javax.inject:javax.inject:${javaxInjectVersion}", - rxJava: "io.reactivex:rxjava:${rxJavaVersion}", + rxJava: "io.reactivex.rxjava2:rxjava:${rxJavaVersion}", arrow: "com.fernandocejas:arrow:${arrowVersion}" ] @@ -72,8 +72,8 @@ ext { dagger: "com.google.dagger:dagger:${daggerVersion}", okHttp: "com.squareup.okhttp:okhttp:${okHttpVersion}", gson: "com.google.code.gson:gson:${gsonVersion}", - rxJava: "io.reactivex:rxjava:${rxJavaVersion}", - rxAndroid: "io.reactivex:rxandroid:${rxAndroidVersion}", + rxJava: "io.reactivex.rxjava2:rxjava:${rxJavaVersion}", + rxAndroid: "io.reactivex.rxjava2:rxandroid:${rxAndroidVersion}", javaxAnnotation: "javax.annotation:jsr250-api:${javaxAnnotationVersion}", javaxInject: "javax.inject:javax.inject:${javaxInjectVersion}", androidAnnotations: "com.android.support:support-annotations:${androidAnnotationsVersion}" diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCache.java b/data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCache.java index 302cb760..1fe53a04 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCache.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCache.java @@ -16,14 +16,14 @@ package com.fernandocejas.android10.sample.data.cache; import com.fernandocejas.android10.sample.data.entity.UserEntity; -import rx.Observable; +import io.reactivex.Observable; /** * An interface representing a user Cache. */ public interface UserCache { /** - * Gets an {@link rx.Observable} which will emit a {@link UserEntity}. + * Gets an {@link Observable} which will emit a {@link UserEntity}. * * @param userId The user id to retrieve data. */ diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCacheImpl.java b/data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCacheImpl.java index e53d5a7d..cc4351e0 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCacheImpl.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCacheImpl.java @@ -20,10 +20,10 @@ import com.fernandocejas.android10.sample.data.entity.UserEntity; import com.fernandocejas.android10.sample.data.exception.UserNotFoundException; import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor; +import io.reactivex.Observable; import java.io.File; import javax.inject.Inject; import javax.inject.Singleton; -import rx.Observable; /** * {@link UserCache} implementation. @@ -63,17 +63,17 @@ public class UserCacheImpl implements UserCache { } @Override public Observable get(final int userId) { - return Observable.create(subscriber -> { + return Observable.create(emitter -> { final File userEntityFile = UserCacheImpl.this.buildFile(userId); final String fileContent = UserCacheImpl.this.fileManager.readFileContent(userEntityFile); final UserEntity userEntity = UserCacheImpl.this.serializer.deserialize(fileContent, UserEntity.class); if (userEntity != null) { - subscriber.onNext(userEntity); - subscriber.onCompleted(); + emitter.onNext(userEntity); + emitter.onComplete(); } else { - subscriber.onError(new UserNotFoundException()); + emitter.onError(new UserNotFoundException()); } }); } diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApi.java b/data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApi.java index 42bc4c93..004acd6a 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApi.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApi.java @@ -16,8 +16,8 @@ package com.fernandocejas.android10.sample.data.net; import com.fernandocejas.android10.sample.data.entity.UserEntity; +import io.reactivex.Observable; import java.util.List; -import rx.Observable; /** * RestApi for retrieving data from the network. @@ -32,12 +32,12 @@ public interface RestApi { String API_URL_GET_USER_DETAILS = API_BASE_URL + "user_"; /** - * Retrieves an {@link rx.Observable} which will emit a List of {@link UserEntity}. + * Retrieves an {@link Observable} which will emit a List of {@link UserEntity}. */ Observable> userEntityList(); /** - * Retrieves an {@link rx.Observable} which will emit a {@link UserEntity}. + * Retrieves an {@link Observable} which will emit a {@link UserEntity}. * * @param userId The user id used to get user data. */ diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApiImpl.java b/data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApiImpl.java index 717ac5c1..279fe6f4 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApiImpl.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApiImpl.java @@ -21,9 +21,9 @@ import com.fernandocejas.android10.sample.data.entity.UserEntity; import com.fernandocejas.android10.sample.data.entity.mapper.UserEntityJsonMapper; import com.fernandocejas.android10.sample.data.exception.NetworkConnectionException; +import io.reactivex.Observable; import java.net.MalformedURLException; import java.util.List; -import rx.Observable; /** * {@link RestApi} implementation for retrieving data from the network. @@ -48,42 +48,42 @@ public RestApiImpl(Context context, UserEntityJsonMapper userEntityJsonMapper) { } @Override public Observable> userEntityList() { - return Observable.create(subscriber -> { + return Observable.create(emitter -> { if (isThereInternetConnection()) { try { String responseUserEntities = getUserEntitiesFromApi(); if (responseUserEntities != null) { - subscriber.onNext(userEntityJsonMapper.transformUserEntityCollection( + emitter.onNext(userEntityJsonMapper.transformUserEntityCollection( responseUserEntities)); - subscriber.onCompleted(); + emitter.onComplete(); } else { - subscriber.onError(new NetworkConnectionException()); + emitter.onError(new NetworkConnectionException()); } } catch (Exception e) { - subscriber.onError(new NetworkConnectionException(e.getCause())); + emitter.onError(new NetworkConnectionException(e.getCause())); } } else { - subscriber.onError(new NetworkConnectionException()); + emitter.onError(new NetworkConnectionException()); } }); } @Override public Observable userEntityById(final int userId) { - return Observable.create(subscriber -> { + return Observable.create(emitter -> { if (isThereInternetConnection()) { try { String responseUserDetails = getUserDetailsFromApi(userId); if (responseUserDetails != null) { - subscriber.onNext(userEntityJsonMapper.transformUserEntity(responseUserDetails)); - subscriber.onCompleted(); + emitter.onNext(userEntityJsonMapper.transformUserEntity(responseUserDetails)); + emitter.onComplete(); } else { - subscriber.onError(new NetworkConnectionException()); + emitter.onError(new NetworkConnectionException()); } } catch (Exception e) { - subscriber.onError(new NetworkConnectionException(e.getCause())); + emitter.onError(new NetworkConnectionException(e.getCause())); } } else { - subscriber.onError(new NetworkConnectionException()); + emitter.onError(new NetworkConnectionException()); } }); } diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/repository/UserDataRepository.java b/data/src/main/java/com/fernandocejas/android10/sample/data/repository/UserDataRepository.java index 80a7f3fb..edb0de0a 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/repository/UserDataRepository.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/repository/UserDataRepository.java @@ -20,10 +20,10 @@ import com.fernandocejas.android10.sample.data.repository.datasource.UserDataStoreFactory; import com.fernandocejas.android10.sample.domain.User; import com.fernandocejas.android10.sample.domain.repository.UserRepository; +import io.reactivex.Observable; import java.util.List; import javax.inject.Inject; import javax.inject.Singleton; -import rx.Observable; /** * {@link UserRepository} for retrieving user data. diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/CloudUserDataStore.java b/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/CloudUserDataStore.java index a5eda5ad..e681ecd7 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/CloudUserDataStore.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/CloudUserDataStore.java @@ -18,9 +18,8 @@ import com.fernandocejas.android10.sample.data.cache.UserCache; import com.fernandocejas.android10.sample.data.entity.UserEntity; import com.fernandocejas.android10.sample.data.net.RestApi; +import io.reactivex.Observable; import java.util.List; -import rx.Observable; -import rx.functions.Action1; /** * {@link UserDataStore} implementation based on connections to the api (Cloud). @@ -30,12 +29,6 @@ class CloudUserDataStore implements UserDataStore { private final RestApi restApi; private final UserCache userCache; - private final Action1 saveToCacheAction = userEntity -> { - if (userEntity != null) { - CloudUserDataStore.this.userCache.put(userEntity); - } - }; - /** * Construct a {@link UserDataStore} based on connections to the api (Cloud). * @@ -52,6 +45,6 @@ class CloudUserDataStore implements UserDataStore { } @Override public Observable userEntityDetails(final int userId) { - return this.restApi.userEntityById(userId).doOnNext(saveToCacheAction); + return this.restApi.userEntityById(userId).doOnNext(CloudUserDataStore.this.userCache::put); } } diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/DiskUserDataStore.java b/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/DiskUserDataStore.java index fb02b4ad..d638032c 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/DiskUserDataStore.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/DiskUserDataStore.java @@ -17,8 +17,8 @@ import com.fernandocejas.android10.sample.data.cache.UserCache; import com.fernandocejas.android10.sample.data.entity.UserEntity; +import io.reactivex.Observable; import java.util.List; -import rx.Observable; /** * {@link UserDataStore} implementation based on file system data store. diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStore.java b/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStore.java index 744bae1e..ac4f9fbc 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStore.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStore.java @@ -16,20 +16,20 @@ package com.fernandocejas.android10.sample.data.repository.datasource; import com.fernandocejas.android10.sample.data.entity.UserEntity; +import io.reactivex.Observable; import java.util.List; -import rx.Observable; /** * Interface that represents a data store from where data is retrieved. */ public interface UserDataStore { /** - * Get an {@link rx.Observable} which will emit a List of {@link UserEntity}. + * Get an {@link Observable} which will emit a List of {@link UserEntity}. */ Observable> userEntityList(); /** - * Get an {@link rx.Observable} which will emit a {@link UserEntity} by its id. + * Get an {@link Observable} which will emit a {@link UserEntity} by its id. * * @param userId The id to retrieve user data. */ diff --git a/data/src/test/java/com/fernandocejas/android10/sample/data/repository/UserDataRepositoryTest.java b/data/src/test/java/com/fernandocejas/android10/sample/data/repository/UserDataRepositoryTest.java index 64335319..ef596f65 100644 --- a/data/src/test/java/com/fernandocejas/android10/sample/data/repository/UserDataRepositoryTest.java +++ b/data/src/test/java/com/fernandocejas/android10/sample/data/repository/UserDataRepositoryTest.java @@ -20,6 +20,7 @@ import com.fernandocejas.android10.sample.data.repository.datasource.UserDataStore; import com.fernandocejas.android10.sample.data.repository.datasource.UserDataStoreFactory; import com.fernandocejas.android10.sample.domain.User; +import io.reactivex.Observable; import java.util.ArrayList; import java.util.List; import org.junit.Before; @@ -29,7 +30,6 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; -import rx.Observable; import static org.mockito.BDDMockito.given; import static org.mockito.Matchers.anyInt; diff --git a/data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/CloudUserDataStoreTest.java b/data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/CloudUserDataStoreTest.java index 1bc23ec7..a47a2e4e 100644 --- a/data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/CloudUserDataStoreTest.java +++ b/data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/CloudUserDataStoreTest.java @@ -18,12 +18,12 @@ import com.fernandocejas.android10.sample.data.cache.UserCache; import com.fernandocejas.android10.sample.data.entity.UserEntity; import com.fernandocejas.android10.sample.data.net.RestApi; +import io.reactivex.Observable; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; -import rx.Observable; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.verify; diff --git a/domain/src/main/java/com/fernandocejas/android10/sample/domain/executor/PostExecutionThread.java b/domain/src/main/java/com/fernandocejas/android10/sample/domain/executor/PostExecutionThread.java index a69f2859..6ed498cb 100644 --- a/domain/src/main/java/com/fernandocejas/android10/sample/domain/executor/PostExecutionThread.java +++ b/domain/src/main/java/com/fernandocejas/android10/sample/domain/executor/PostExecutionThread.java @@ -15,7 +15,7 @@ */ package com.fernandocejas.android10.sample.domain.executor; -import rx.Scheduler; +import io.reactivex.Scheduler; /** * Thread abstraction created to change the execution context from any thread to any other thread. diff --git a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/DefaultSubscriber.java b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/DefaultObserver.java similarity index 72% rename from domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/DefaultSubscriber.java rename to domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/DefaultObserver.java index 81abea5c..4bcd3e49 100644 --- a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/DefaultSubscriber.java +++ b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/DefaultObserver.java @@ -15,19 +15,21 @@ */ package com.fernandocejas.android10.sample.domain.interactor; +import io.reactivex.observers.DisposableObserver; + /** - * Default subscriber base class to be used whenever you want default error handling. + * Default {@link DisposableObserver} base class to be used whenever you want default error handling. */ -public class DefaultSubscriber extends rx.Subscriber { - @Override public void onCompleted() { +public class DefaultObserver extends DisposableObserver { + @Override public void onNext(T t) { // no-op by default. } - @Override public void onError(Throwable e) { + @Override public void onComplete() { // no-op by default. } - @Override public void onNext(T t) { + @Override public void onError(Throwable exception) { // no-op by default. } } diff --git a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetails.java b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetails.java index e4f1b3bc..d45edcde 100644 --- a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetails.java +++ b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetails.java @@ -21,8 +21,8 @@ import com.fernandocejas.android10.sample.domain.repository.UserRepository; import com.fernandocejas.arrow.annotations.VisibleForTesting; import com.fernandocejas.arrow.optional.Optional; +import io.reactivex.Observable; import javax.inject.Inject; -import rx.Observable; /** * This class is an implementation of {@link UseCase} that represents a use case for diff --git a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java index b63d340f..f1988f62 100644 --- a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java +++ b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java @@ -20,8 +20,8 @@ import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor; import com.fernandocejas.android10.sample.domain.repository.UserRepository; import com.fernandocejas.arrow.optional.Optional; +import io.reactivex.Observable; import javax.inject.Inject; -import rx.Observable; /** * This class is an implementation of {@link UseCase} that represents a use case for diff --git a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/UseCase.java b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/UseCase.java index 36888a93..f656656d 100644 --- a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/UseCase.java +++ b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/UseCase.java @@ -17,59 +17,69 @@ import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread; import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor; +import com.fernandocejas.arrow.checks.Preconditions; import com.fernandocejas.arrow.optional.Optional; -import rx.Observable; -import rx.Subscriber; -import rx.Subscription; -import rx.schedulers.Schedulers; -import rx.subscriptions.Subscriptions; +import io.reactivex.Observable; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.disposables.Disposable; +import io.reactivex.observers.DisposableObserver; +import io.reactivex.schedulers.Schedulers; /** * Abstract class for a Use Case (Interactor in terms of Clean Architecture). * This interface represents a execution unit for different use cases (this means any use case * in the application should implement this contract). * - * By convention each UseCase implementation will return the result using a {@link rx.Subscriber} + * By convention each UseCase implementation will return the result using a {@link DisposableObserver} * that will execute its job in a background thread and will post the result in the UI thread. */ public abstract class UseCase { private final ThreadExecutor threadExecutor; private final PostExecutionThread postExecutionThread; + private final CompositeDisposable disposables; - private Subscription subscription = Subscriptions.empty(); - - protected UseCase(ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) { + UseCase(ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) { this.threadExecutor = threadExecutor; this.postExecutionThread = postExecutionThread; + this.disposables = new CompositeDisposable(); } /** - * Builds an {@link rx.Observable} which will be used when executing the current {@link UseCase}. + * Builds an {@link Observable} which will be used when executing the current {@link UseCase}. */ protected abstract Observable buildUseCaseObservable(Optional params); /** * Executes the current use case. * - * @param useCaseSubscriber Subscriber which will be listening to the observable build + * @param observer {@link DisposableObserver} which will be listening to the observable build * by {@link #buildUseCaseObservable(Optional)} ()} method. * @param params Parameters used to build execute this use case. */ @SuppressWarnings("unchecked") - public void execute(Subscriber useCaseSubscriber, Params params) { - this.subscription = this.buildUseCaseObservable(Optional.of(params)) + public void execute(DisposableObserver observer, Params params) { + final Observable observable = this.buildUseCaseObservable(Optional.of(params)) .subscribeOn(Schedulers.from(threadExecutor)) - .observeOn(postExecutionThread.getScheduler()) - .subscribe(useCaseSubscriber); + .observeOn(postExecutionThread.getScheduler()); + addDisposable(observable.subscribeWith(observer)); } /** - * Unsubscribes from current {@link rx.Subscription}. + * Dispose from current {@link CompositeDisposable}. */ - public void unsubscribe() { - if (!subscription.isUnsubscribed()) { - subscription.unsubscribe(); + public void dispose() { + if (!disposables.isDisposed()) { + disposables.dispose(); } } + + /** + * Dispose from current {@link CompositeDisposable}. + */ + private void addDisposable(Disposable disposable) { + Preconditions.checkNotNull(disposable); + Preconditions.checkNotNull(disposables); + disposables.add(disposable); + } } diff --git a/domain/src/main/java/com/fernandocejas/android10/sample/domain/repository/UserRepository.java b/domain/src/main/java/com/fernandocejas/android10/sample/domain/repository/UserRepository.java index 69c40a03..29362a00 100644 --- a/domain/src/main/java/com/fernandocejas/android10/sample/domain/repository/UserRepository.java +++ b/domain/src/main/java/com/fernandocejas/android10/sample/domain/repository/UserRepository.java @@ -16,20 +16,20 @@ package com.fernandocejas.android10.sample.domain.repository; import com.fernandocejas.android10.sample.domain.User; +import io.reactivex.Observable; import java.util.List; -import rx.Observable; /** * Interface that represents a Repository for getting {@link User} related data. */ public interface UserRepository { /** - * Get an {@link rx.Observable} which will emit a List of {@link User}. + * Get an {@link Observable} which will emit a List of {@link User}. */ Observable> users(); /** - * Get an {@link rx.Observable} which will emit a {@link User}. + * Get an {@link Observable} which will emit a {@link User}. * * @param userId The user id used to retrieve user data. */ diff --git a/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetailsTest.java b/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetailsTest.java index feaa0374..2379205c 100644 --- a/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetailsTest.java +++ b/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetailsTest.java @@ -19,12 +19,12 @@ import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor; import com.fernandocejas.android10.sample.domain.repository.UserRepository; import com.fernandocejas.arrow.optional.Optional; +import io.reactivex.Observable; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; -import rx.Observable; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.verify; diff --git a/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/UseCaseTest.java b/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/UseCaseTest.java index 291cc2be..482a8d44 100644 --- a/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/UseCaseTest.java +++ b/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/UseCaseTest.java @@ -18,15 +18,14 @@ import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread; import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor; import com.fernandocejas.arrow.optional.Optional; +import io.reactivex.Observable; +import io.reactivex.observers.DisposableObserver; +import io.reactivex.schedulers.TestScheduler; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; -import rx.Observable; -import rx.Subscriber; -import rx.observers.TestSubscriber; -import rx.schedulers.TestScheduler; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; @@ -36,34 +35,32 @@ public class UseCaseTest { private UseCaseTestClass useCase; + private TestDisposableObserver testObserver; + @Mock private ThreadExecutor mockThreadExecutor; @Mock private PostExecutionThread mockPostExecutionThread; @Before public void setUp() { this.useCase = new UseCaseTestClass(mockThreadExecutor, mockPostExecutionThread); + this.testObserver = new TestDisposableObserver<>(); + given(mockPostExecutionThread.getScheduler()).willReturn(new TestScheduler()); } @Test @SuppressWarnings("unchecked") public void testBuildUseCaseObservableReturnCorrectResult() { - TestSubscriber testSubscriber = new TestSubscriber<>(); - TestScheduler testScheduler = new TestScheduler(); - given(mockPostExecutionThread.getScheduler()).willReturn(testScheduler); - - useCase.execute(testSubscriber, Params.EMPTY); + useCase.execute(testObserver, Params.EMPTY); - assertThat(testSubscriber.getOnNextEvents().size()).isZero(); + assertThat(testObserver.valuesCount).isZero(); } @Test public void testSubscriptionWhenExecutingUseCase() { - TestSubscriber testSubscriber = new TestSubscriber<>(); - - useCase.execute(testSubscriber, Params.EMPTY); - useCase.unsubscribe(); + useCase.execute(testObserver, Params.EMPTY); + useCase.dispose(); - assertThat(testSubscriber.isUnsubscribed()).isTrue(); + assertThat(testObserver.isDisposed()).isTrue(); } private static class UseCaseTestClass extends UseCase { @@ -76,8 +73,24 @@ private static class UseCaseTestClass extends UseCase { return Observable.empty(); } - @Override public void execute(Subscriber useCaseSubscriber, Params params) { - super.execute(useCaseSubscriber, Params.EMPTY); + @Override public void execute(DisposableObserver observer, Params params) { + super.execute(observer, Params.EMPTY); + } + } + + private static class TestDisposableObserver extends DisposableObserver { + private int valuesCount = 0; + + @Override public void onNext(T value) { + valuesCount++; + } + + @Override public void onError(Throwable e) { + // no-op by default. + } + + @Override public void onComplete() { + // no-op by default. } } } 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 e2e0c536..74673b72 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 @@ -20,12 +20,12 @@ 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 io.reactivex.observers.DisposableObserver; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; -import rx.Subscriber; import com.fernandocejas.android10.sample.domain.interactor.Params; import static org.mockito.BDDMockito.given; @@ -58,6 +58,6 @@ public void testUserDetailsPresenterInitialize() { verify(mockUserDetailsView).hideRetry(); verify(mockUserDetailsView).showLoading(); - verify(mockGetUserDetails).execute(any(Subscriber.class), any(Params.class)); + verify(mockGetUserDetails).execute(any(DisposableObserver.class), any(Params.class)); } } 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 eabe55ec..2d3637fb 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 @@ -21,12 +21,12 @@ 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 io.reactivex.observers.DisposableObserver; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; -import rx.Subscriber; import static org.mockito.BDDMockito.given; import static org.mockito.Matchers.any; @@ -56,6 +56,6 @@ public void testUserListPresenterInitialize() { verify(mockUserListView).hideRetry(); verify(mockUserListView).showLoading(); - verify(mockGetUserList).execute(any(Subscriber.class), any(Params.class)); + verify(mockGetUserList).execute(any(DisposableObserver.class), any(Params.class)); } } diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/UIThread.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/UIThread.java index 05f13d22..1a91a34e 100644 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/UIThread.java +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/UIThread.java @@ -16,13 +16,13 @@ package com.fernandocejas.android10.sample.presentation; import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread; +import io.reactivex.Scheduler; +import io.reactivex.android.schedulers.AndroidSchedulers; import javax.inject.Inject; import javax.inject.Singleton; -import rx.Scheduler; -import rx.android.schedulers.AndroidSchedulers; /** - * MainThread (UI Thread) implementation based on a {@link rx.Scheduler} + * MainThread (UI Thread) implementation based on a {@link Scheduler} * which will execute actions on the Android UI thread */ @Singleton 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 index 3fdcaa53..78827fe4 100644 --- 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 @@ -19,7 +19,7 @@ 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.DefaultSubscriber; +import com.fernandocejas.android10.sample.domain.interactor.DefaultObserver; import com.fernandocejas.android10.sample.domain.interactor.GetUserDetails; import com.fernandocejas.android10.sample.domain.interactor.UseCase; import com.fernandocejas.android10.sample.domain.interactor.Params; @@ -59,7 +59,7 @@ public void setView(@NonNull UserDetailsView view) { @Override public void pause() {} @Override public void destroy() { - this.getUserDetailsUseCase.unsubscribe(); + this.getUserDetailsUseCase.dispose(); this.viewDetailsView = null; } @@ -76,7 +76,7 @@ public void initialize(int userId) { private void getUserDetails(int userId) { final Params params = Params.create(); params.putInt(GetUserDetails.PARAM_USER_ID_KEY, userId); - this.getUserDetailsUseCase.execute(new UserDetailsSubscriber(), params); + this.getUserDetailsUseCase.execute(new UserDetailsObserver(), params); } private void showViewLoading() { @@ -106,9 +106,9 @@ private void showUserDetailsInView(User user) { this.viewDetailsView.renderUser(userModel); } - private final class UserDetailsSubscriber extends DefaultSubscriber { + private final class UserDetailsObserver extends DefaultObserver { - @Override public void onCompleted() { + @Override public void onComplete() { UserDetailsPresenter.this.hideViewLoading(); } 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 index 55a5e145..4d67717e 100644 --- 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 @@ -19,7 +19,7 @@ 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.DefaultSubscriber; +import com.fernandocejas.android10.sample.domain.interactor.DefaultObserver; import com.fernandocejas.android10.sample.domain.interactor.GetUserList; import com.fernandocejas.android10.sample.domain.interactor.UseCase; import com.fernandocejas.android10.sample.domain.interactor.Params; @@ -61,7 +61,7 @@ public void setView(@NonNull UserListView view) { @Override public void pause() {} @Override public void destroy() { - this.getUserListUseCase.unsubscribe(); + this.getUserListUseCase.dispose(); this.viewListView = null; } @@ -114,12 +114,12 @@ private void showUsersCollectionInView(Collection usersCollection) { } private void getUserList() { - this.getUserListUseCase.execute(new UserListSubscriber(), Params.EMPTY); + this.getUserListUseCase.execute(new UserListObserver(), Params.EMPTY); } - private final class UserListSubscriber extends DefaultSubscriber> { + private final class UserListObserver extends DefaultObserver> { - @Override public void onCompleted() { + @Override public void onComplete() { UserListPresenter.this.hideViewLoading(); } From 70eeacfab0de957ef23c47dead2aff0593c0b708 Mon Sep 17 00:00:00 2001 From: Fernando Cejas Date: Sat, 24 Dec 2016 19:07:55 -0300 Subject: [PATCH 23/32] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c72f0985..dee9aa03 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ This is a sample app that is part of a blog post I have written about how to arc [Tasting Dagger 2 on Android](http://fernandocejas.com/2015/04/11/tasting-dagger-2-on-android/) +[Clean Architecture…Dynamic Parameters in Use Cases](http://fernandocejas.com/2016/12/24/clean-architecture-dynamic-parameters-in-use-cases/) + [Demo video of this sample](http://youtu.be/XSjV4sG3ni0) Clean architecture From b64a353acbff561fb731479ffbec024b6b0d0034 Mon Sep 17 00:00:00 2001 From: Fernando Cejas Date: Mon, 26 Dec 2016 14:03:46 -0300 Subject: [PATCH 24/32] Generify UseCase --- .../domain/interactor/GetUserDetails.java | 7 +++---- .../sample/domain/interactor/GetUserList.java | 7 +++---- .../sample/domain/interactor/UseCase.java | 9 ++++----- .../sample/domain/interactor/UseCaseTest.java | 2 +- .../internal/di/modules/UserModule.java | 20 ------------------- .../presenter/UserDetailsPresenter.java | 6 ++---- .../presenter/UserListPresenter.java | 6 ++---- 7 files changed, 15 insertions(+), 42 deletions(-) diff --git a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetails.java b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetails.java index d45edcde..c43fc09e 100644 --- a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetails.java +++ b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetails.java @@ -28,9 +28,8 @@ * This class is an implementation of {@link UseCase} that represents a use case for * retrieving data related to an specific {@link User}. */ -public class GetUserDetails extends UseCase { +public class GetUserDetails extends UseCase { - public static final String NAME = "userDetails"; public static final String PARAM_USER_ID_KEY = "userId"; @VisibleForTesting @@ -39,13 +38,13 @@ public class GetUserDetails extends UseCase { private final UserRepository userRepository; @Inject - public GetUserDetails(UserRepository userRepository, ThreadExecutor threadExecutor, + GetUserDetails(UserRepository userRepository, ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) { super(threadExecutor, postExecutionThread); this.userRepository = userRepository; } - @Override protected Observable buildUseCaseObservable(Optional params) { + @Override protected Observable buildUseCaseObservable(Optional params) { if (params.isPresent()) { final int userId = params.get().getInt(PARAM_USER_ID_KEY, PARAM_USER_ID_DEFAULT_VALUE); return this.userRepository.user(userId); diff --git a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java index f1988f62..a092b042 100644 --- a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java +++ b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java @@ -21,15 +21,14 @@ import com.fernandocejas.android10.sample.domain.repository.UserRepository; import com.fernandocejas.arrow.optional.Optional; import io.reactivex.Observable; +import java.util.List; import javax.inject.Inject; /** * This class is an implementation of {@link UseCase} that represents a use case for * retrieving a collection of all {@link User}. */ -public class GetUserList extends UseCase { - - public static final String NAME = "userList"; +public class GetUserList extends UseCase> { private final UserRepository userRepository; @@ -40,7 +39,7 @@ public class GetUserList extends UseCase { this.userRepository = userRepository; } - @Override public Observable buildUseCaseObservable(Optional params) { + @Override public Observable> buildUseCaseObservable(Optional params) { return this.userRepository.users(); } } diff --git a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/UseCase.java b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/UseCase.java index f656656d..3f801d79 100644 --- a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/UseCase.java +++ b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/UseCase.java @@ -33,7 +33,7 @@ * By convention each UseCase implementation will return the result using a {@link DisposableObserver} * that will execute its job in a background thread and will post the result in the UI thread. */ -public abstract class UseCase { +public abstract class UseCase { private final ThreadExecutor threadExecutor; private final PostExecutionThread postExecutionThread; @@ -48,7 +48,7 @@ public abstract class UseCase { /** * Builds an {@link Observable} which will be used when executing the current {@link UseCase}. */ - protected abstract Observable buildUseCaseObservable(Optional params); + abstract Observable buildUseCaseObservable(Optional params); /** * Executes the current use case. @@ -57,9 +57,8 @@ public abstract class UseCase { * by {@link #buildUseCaseObservable(Optional)} ()} method. * @param params Parameters used to build execute this use case. */ - @SuppressWarnings("unchecked") - public void execute(DisposableObserver observer, Params params) { - final Observable observable = this.buildUseCaseObservable(Optional.of(params)) + public void execute(DisposableObserver observer, Params params) { + final Observable observable = this.buildUseCaseObservable(Optional.of(params)) .subscribeOn(Schedulers.from(threadExecutor)) .observeOn(postExecutionThread.getScheduler()); addDisposable(observable.subscribeWith(observer)); diff --git a/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/UseCaseTest.java b/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/UseCaseTest.java index 482a8d44..a2fa2c07 100644 --- a/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/UseCaseTest.java +++ b/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/UseCaseTest.java @@ -63,7 +63,7 @@ public void testSubscriptionWhenExecutingUseCase() { assertThat(testObserver.isDisposed()).isTrue(); } - private static class UseCaseTestClass extends UseCase { + private static class UseCaseTestClass extends UseCase { UseCaseTestClass(ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) { super(threadExecutor, postExecutionThread); diff --git a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/modules/UserModule.java b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/modules/UserModule.java index 94cbebd8..be255a0f 100644 --- a/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/modules/UserModule.java +++ b/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/modules/UserModule.java @@ -15,16 +15,7 @@ */ package com.fernandocejas.android10.sample.presentation.internal.di.modules; -import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread; -import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor; -import com.fernandocejas.android10.sample.domain.interactor.GetUserDetails; -import com.fernandocejas.android10.sample.domain.interactor.GetUserList; -import com.fernandocejas.android10.sample.domain.interactor.UseCase; -import com.fernandocejas.android10.sample.domain.repository.UserRepository; -import com.fernandocejas.android10.sample.presentation.internal.di.PerActivity; import dagger.Module; -import dagger.Provides; -import javax.inject.Named; /** * Dagger module that provides user related collaborators. @@ -33,15 +24,4 @@ public class UserModule { public UserModule() {} - - @Provides @PerActivity @Named(GetUserList.NAME) UseCase provideGetUserListUseCase( - GetUserList getUserList) { - return getUserList; - } - - @Provides @PerActivity @Named(GetUserDetails.NAME) UseCase provideGetUserDetailsUseCase( - UserRepository userRepository, ThreadExecutor threadExecutor, - PostExecutionThread postExecutionThread) { - return new GetUserDetails(userRepository, threadExecutor, postExecutionThread); - } } 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 index 78827fe4..96218f68 100644 --- 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 @@ -21,7 +21,6 @@ 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.UseCase; import com.fernandocejas.android10.sample.domain.interactor.Params; import com.fernandocejas.android10.sample.presentation.exception.ErrorMessageFactory; import com.fernandocejas.android10.sample.presentation.internal.di.PerActivity; @@ -29,7 +28,6 @@ import com.fernandocejas.android10.sample.presentation.model.UserModel; import com.fernandocejas.android10.sample.presentation.view.UserDetailsView; import javax.inject.Inject; -import javax.inject.Named; /** * {@link Presenter} that controls communication between views and models of the presentation @@ -40,11 +38,11 @@ public class UserDetailsPresenter implements Presenter { private UserDetailsView viewDetailsView; - private final UseCase getUserDetailsUseCase; + private final GetUserDetails getUserDetailsUseCase; private final UserModelDataMapper userModelDataMapper; @Inject - public UserDetailsPresenter(@Named(GetUserDetails.NAME) UseCase getUserDetailsUseCase, + public UserDetailsPresenter(GetUserDetails getUserDetailsUseCase, UserModelDataMapper userModelDataMapper) { this.getUserDetailsUseCase = getUserDetailsUseCase; this.userModelDataMapper = userModelDataMapper; 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 index 4d67717e..2208a44d 100644 --- 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 @@ -21,7 +21,6 @@ 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.domain.interactor.UseCase; import com.fernandocejas.android10.sample.domain.interactor.Params; import com.fernandocejas.android10.sample.presentation.exception.ErrorMessageFactory; import com.fernandocejas.android10.sample.presentation.internal.di.PerActivity; @@ -31,7 +30,6 @@ import java.util.Collection; import java.util.List; import javax.inject.Inject; -import javax.inject.Named; /** * {@link Presenter} that controls communication between views and models of the presentation @@ -42,11 +40,11 @@ public class UserListPresenter implements Presenter { private UserListView viewListView; - private final UseCase getUserListUseCase; + private final GetUserList getUserListUseCase; private final UserModelDataMapper userModelDataMapper; @Inject - public UserListPresenter(@Named(GetUserList.NAME) UseCase getUserListUserCase, + public UserListPresenter(GetUserList getUserListUserCase, UserModelDataMapper userModelDataMapper) { this.getUserListUseCase = getUserListUserCase; this.userModelDataMapper = userModelDataMapper; From 8c78f8f148ace6874b9c5c12adca1fced0f89b4f Mon Sep 17 00:00:00 2001 From: Fernando Cejas Date: Mon, 26 Dec 2016 16:21:57 -0300 Subject: [PATCH 25/32] Fix tests and generify use case parameters. --- .../repository/UserDataRepositoryTest.java | 9 +--- .../domain/interactor/GetUserDetails.java | 31 ++++++------ .../sample/domain/interactor/GetUserList.java | 5 +- .../sample/domain/interactor/Params.java | 50 ------------------- .../sample/domain/interactor/UseCase.java | 12 ++--- .../domain/interactor/GetUserDetailsTest.java | 33 ++++-------- .../domain/interactor/GetUserListTest.java | 15 +----- .../sample/domain/interactor/ParamsTest.java | 49 ------------------ .../sample/domain/interactor/UseCaseTest.java | 28 ++++++++--- .../presenter/UserDetailsPresenterTest.java | 3 +- .../test/presenter/UserListPresenterTest.java | 4 +- .../presenter/UserDetailsPresenter.java | 6 +-- .../presenter/UserListPresenter.java | 3 +- 13 files changed, 64 insertions(+), 184 deletions(-) delete mode 100644 domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/Params.java delete mode 100644 domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/ParamsTest.java diff --git a/data/src/test/java/com/fernandocejas/android10/sample/data/repository/UserDataRepositoryTest.java b/data/src/test/java/com/fernandocejas/android10/sample/data/repository/UserDataRepositoryTest.java index ef596f65..1eac1d06 100644 --- a/data/src/test/java/com/fernandocejas/android10/sample/data/repository/UserDataRepositoryTest.java +++ b/data/src/test/java/com/fernandocejas/android10/sample/data/repository/UserDataRepositoryTest.java @@ -24,9 +24,7 @@ import java.util.ArrayList; import java.util.List; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; @@ -48,14 +46,9 @@ public class UserDataRepositoryTest { @Mock private UserEntity mockUserEntity; @Mock private User mockUser; - @Rule - public ExpectedException expectedException = ExpectedException.none(); - @Before public void setUp() { - userDataRepository = new UserDataRepository(mockUserDataStoreFactory, - mockUserEntityDataMapper); - + userDataRepository = new UserDataRepository(mockUserDataStoreFactory, mockUserEntityDataMapper); given(mockUserDataStoreFactory.create(anyInt())).willReturn(mockUserDataStore); given(mockUserDataStoreFactory.createCloudDataStore()).willReturn(mockUserDataStore); } diff --git a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetails.java b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetails.java index c43fc09e..377d0c1d 100644 --- a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetails.java +++ b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetails.java @@ -19,8 +19,7 @@ import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread; import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor; import com.fernandocejas.android10.sample.domain.repository.UserRepository; -import com.fernandocejas.arrow.annotations.VisibleForTesting; -import com.fernandocejas.arrow.optional.Optional; +import com.fernandocejas.arrow.checks.Preconditions; import io.reactivex.Observable; import javax.inject.Inject; @@ -28,12 +27,7 @@ * This class is an implementation of {@link UseCase} that represents a use case for * retrieving data related to an specific {@link User}. */ -public class GetUserDetails extends UseCase { - - public static final String PARAM_USER_ID_KEY = "userId"; - - @VisibleForTesting - static final int PARAM_USER_ID_DEFAULT_VALUE = -1; +public class GetUserDetails extends UseCase { private final UserRepository userRepository; @@ -44,12 +38,21 @@ public class GetUserDetails extends UseCase { this.userRepository = userRepository; } - @Override protected Observable buildUseCaseObservable(Optional params) { - if (params.isPresent()) { - final int userId = params.get().getInt(PARAM_USER_ID_KEY, PARAM_USER_ID_DEFAULT_VALUE); - return this.userRepository.user(userId); - } else { - return Observable.empty(); + @Override Observable buildUseCaseObservable(Params params) { + Preconditions.checkNotNull(params); + return this.userRepository.user(params.userId); + } + + public static final class Params { + + private final int userId; + + private Params(int userId) { + this.userId = userId; + } + + public static Params forUser(int userId) { + return new Params(userId); } } } diff --git a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java index a092b042..e43ce75e 100644 --- a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java +++ b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java @@ -19,7 +19,6 @@ import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread; import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor; import com.fernandocejas.android10.sample.domain.repository.UserRepository; -import com.fernandocejas.arrow.optional.Optional; import io.reactivex.Observable; import java.util.List; import javax.inject.Inject; @@ -28,7 +27,7 @@ * This class is an implementation of {@link UseCase} that represents a use case for * retrieving a collection of all {@link User}. */ -public class GetUserList extends UseCase> { +public class GetUserList extends UseCase, Void> { private final UserRepository userRepository; @@ -39,7 +38,7 @@ public class GetUserList extends UseCase> { this.userRepository = userRepository; } - @Override public Observable> buildUseCaseObservable(Optional params) { + @Override Observable> buildUseCaseObservable(Void unused) { return this.userRepository.users(); } } diff --git a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/Params.java b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/Params.java deleted file mode 100644 index b2113e02..00000000 --- a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/Params.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright (C) 2016 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.domain.interactor; - -import java.util.HashMap; -import java.util.Map; - -/** - * Class backed by a Map, used to pass parameters to {@link UseCase} instances. - */ -public final class Params { - public static final Params EMPTY = Params.create(); - - private final Map parameters = new HashMap<>(); - - private Params() {} - - public static Params create() { - return new Params(); - } - - public void putInt(String key, int value) { - parameters.put(key, value); - } - - int getInt(String key, int defaultValue) { - final Object object = parameters.get(key); - if (object == null) { - return defaultValue; - } - try { - return (int) object; - } catch (ClassCastException e) { - return defaultValue; - } - } -} diff --git a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/UseCase.java b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/UseCase.java index 3f801d79..e71f9145 100644 --- a/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/UseCase.java +++ b/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/UseCase.java @@ -18,7 +18,6 @@ import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread; import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor; import com.fernandocejas.arrow.checks.Preconditions; -import com.fernandocejas.arrow.optional.Optional; import io.reactivex.Observable; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.Disposable; @@ -33,7 +32,7 @@ * By convention each UseCase implementation will return the result using a {@link DisposableObserver} * that will execute its job in a background thread and will post the result in the UI thread. */ -public abstract class UseCase { +public abstract class UseCase { private final ThreadExecutor threadExecutor; private final PostExecutionThread postExecutionThread; @@ -48,17 +47,18 @@ public abstract class UseCase { /** * Builds an {@link Observable} which will be used when executing the current {@link UseCase}. */ - abstract Observable buildUseCaseObservable(Optional params); + abstract Observable buildUseCaseObservable(Params params); /** * Executes the current use case. * * @param observer {@link DisposableObserver} which will be listening to the observable build - * by {@link #buildUseCaseObservable(Optional)} ()} method. - * @param params Parameters used to build execute this use case. + * by {@link #buildUseCaseObservable(Params)} ()} method. + * @param params Parameters (Optional) used to build/execute this use case. */ public void execute(DisposableObserver observer, Params params) { - final Observable observable = this.buildUseCaseObservable(Optional.of(params)) + Preconditions.checkNotNull(observer); + final Observable observable = this.buildUseCaseObservable(params) .subscribeOn(Schedulers.from(threadExecutor)) .observeOn(postExecutionThread.getScheduler()); addDisposable(observable.subscribeWith(observer)); diff --git a/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetailsTest.java b/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetailsTest.java index 2379205c..01baab94 100644 --- a/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetailsTest.java +++ b/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetailsTest.java @@ -17,16 +17,16 @@ import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread; import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor; +import com.fernandocejas.android10.sample.domain.interactor.GetUserDetails.Params; import com.fernandocejas.android10.sample.domain.repository.UserRepository; -import com.fernandocejas.arrow.optional.Optional; -import io.reactivex.Observable; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; -import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; @@ -42,6 +42,8 @@ public class GetUserDetailsTest { @Mock private ThreadExecutor mockThreadExecutor; @Mock private PostExecutionThread mockPostExecutionThread; + @Rule public ExpectedException expectedException = ExpectedException.none(); + @Before public void setUp() { getUserDetails = new GetUserDetails(mockUserRepository, mockThreadExecutor, @@ -50,10 +52,7 @@ public void setUp() { @Test public void testGetUserDetailsUseCaseObservableHappyCase() { - final Params params = Params.create(); - params.putInt(GetUserDetails.PARAM_USER_ID_KEY, USER_ID); - - getUserDetails.buildUseCaseObservable(Optional.of(params)); + getUserDetails.buildUseCaseObservable(Params.forUser(USER_ID)); verify(mockUserRepository).user(USER_ID); verifyNoMoreInteractions(mockUserRepository); @@ -62,22 +61,8 @@ public void testGetUserDetailsUseCaseObservableHappyCase() { } @Test - public void testShouldReturnEmptyObservableWhenNoParameters() { - final Observable observable = getUserDetails.buildUseCaseObservable(Optional.absent()); - - assertThat(observable).isEqualTo(Observable.empty()); - verifyZeroInteractions(mockUserRepository); - verifyZeroInteractions(mockPostExecutionThread); - verifyZeroInteractions(mockThreadExecutor); - } - - @Test - public void testShouldUseDefaultUserIdValueWhenNoUserIdParameter() { - getUserDetails.buildUseCaseObservable(Optional.of(Params.create())); - - verify(mockUserRepository).user(GetUserDetails.PARAM_USER_ID_DEFAULT_VALUE); - verifyNoMoreInteractions(mockUserRepository); - verifyZeroInteractions(mockPostExecutionThread); - verifyZeroInteractions(mockThreadExecutor); + public void testShouldFailWhenNoOrEmptyParameters() { + expectedException.expect(NullPointerException.class); + getUserDetails.buildUseCaseObservable(null); } } diff --git a/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserListTest.java b/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserListTest.java index 73cf0e14..13694063 100644 --- a/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserListTest.java +++ b/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserListTest.java @@ -18,14 +18,12 @@ import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread; import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor; import com.fernandocejas.android10.sample.domain.repository.UserRepository; -import com.fernandocejas.arrow.optional.Optional; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; @@ -47,22 +45,11 @@ public void setUp() { @Test public void testGetUserListUseCaseObservableHappyCase() { - getUserList.buildUseCaseObservable(Optional.of(Params.EMPTY)); + getUserList.buildUseCaseObservable(null); verify(mockUserRepository).users(); verifyNoMoreInteractions(mockUserRepository); verifyZeroInteractions(mockThreadExecutor); verifyZeroInteractions(mockPostExecutionThread); } - - @Test - @SuppressWarnings("unchecked") - public void testThereShouldNotBeAnyInteractionWithParams() { - Optional params = mock(Optional.class); - - getUserList.buildUseCaseObservable(params); - - verify(mockUserRepository).users(); - verifyZeroInteractions(params); - } } diff --git a/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/ParamsTest.java b/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/ParamsTest.java deleted file mode 100644 index 4d95afc1..00000000 --- a/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/ParamsTest.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright (C) 2016 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.domain.interactor; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.runners.MockitoJUnitRunner; - -import static org.assertj.core.api.Assertions.assertThat; - -@RunWith(MockitoJUnitRunner.class) -public class ParamsTest { - - private Params params; - - @Before - public void setUp() { - params = Params.create(); - } - - @Test - public void testShouldReturnIntValue() { - params.putInt("key01", 3); - - assertThat(params.getInt("key01", 5)).isEqualTo(3); - } - - @Test - public void testShouldReturnIntDefaultValue() { - params.putInt("key01", 3); - params.putInt("key02", 4); - - assertThat(params.getInt("key03", 5)).isEqualTo(5); - } -} diff --git a/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/UseCaseTest.java b/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/UseCaseTest.java index a2fa2c07..b8882001 100644 --- a/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/UseCaseTest.java +++ b/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/UseCaseTest.java @@ -17,12 +17,13 @@ import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread; import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor; -import com.fernandocejas.arrow.optional.Optional; import io.reactivex.Observable; import io.reactivex.observers.DisposableObserver; import io.reactivex.schedulers.TestScheduler; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; @@ -35,11 +36,13 @@ public class UseCaseTest { private UseCaseTestClass useCase; - private TestDisposableObserver testObserver; + private TestDisposableObserver testObserver; @Mock private ThreadExecutor mockThreadExecutor; @Mock private PostExecutionThread mockPostExecutionThread; + @Rule public ExpectedException expectedException = ExpectedException.none(); + @Before public void setUp() { this.useCase = new UseCaseTestClass(mockThreadExecutor, mockPostExecutionThread); @@ -48,7 +51,6 @@ public void setUp() { } @Test - @SuppressWarnings("unchecked") public void testBuildUseCaseObservableReturnCorrectResult() { useCase.execute(testObserver, Params.EMPTY); @@ -63,18 +65,25 @@ public void testSubscriptionWhenExecutingUseCase() { assertThat(testObserver.isDisposed()).isTrue(); } - private static class UseCaseTestClass extends UseCase { + @Test + public void testShouldFailWhenExecuteWithNullObserver() { + expectedException.expect(NullPointerException.class); + useCase.execute(null, Params.EMPTY); + } + + private static class UseCaseTestClass extends UseCase { UseCaseTestClass(ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) { super(threadExecutor, postExecutionThread); } - @Override protected Observable buildUseCaseObservable(Optional params) { + @Override Observable buildUseCaseObservable(Params params) { return Observable.empty(); } - @Override public void execute(DisposableObserver observer, Params params) { - super.execute(observer, Params.EMPTY); + @Override + public void execute(DisposableObserver observer, Params params) { + super.execute(observer, params); } } @@ -93,4 +102,9 @@ private static class TestDisposableObserver extends DisposableObserver { // no-op by default. } } + + private static class Params { + private static Params EMPTY = new Params(); + private Params() {} + } } 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 74673b72..e8360659 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 @@ -17,6 +17,7 @@ import android.content.Context; 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; @@ -27,7 +28,6 @@ import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; -import com.fernandocejas.android10.sample.domain.interactor.Params; import static org.mockito.BDDMockito.given; import static org.mockito.Matchers.any; import static org.mockito.Mockito.verify; @@ -51,6 +51,7 @@ public void setUp() { } @Test + @SuppressWarnings("unchecked") public void testUserDetailsPresenterInitialize() { given(mockUserDetailsView.context()).willReturn(mockContext); 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 2d3637fb..35c99855 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 @@ -17,7 +17,6 @@ import android.content.Context; import com.fernandocejas.android10.sample.domain.interactor.GetUserList; -import com.fernandocejas.android10.sample.domain.interactor.Params; import com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper; import com.fernandocejas.android10.sample.presentation.presenter.UserListPresenter; import com.fernandocejas.android10.sample.presentation.view.UserListView; @@ -49,6 +48,7 @@ public void setUp() { } @Test + @SuppressWarnings("unchecked") public void testUserListPresenterInitialize() { given(mockUserListView.context()).willReturn(mockContext); @@ -56,6 +56,6 @@ public void testUserListPresenterInitialize() { verify(mockUserListView).hideRetry(); verify(mockUserListView).showLoading(); - verify(mockGetUserList).execute(any(DisposableObserver.class), any(Params.class)); + verify(mockGetUserList).execute(any(DisposableObserver.class), any(Void.class)); } } 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 index 96218f68..4e3c3bf7 100644 --- 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 @@ -21,7 +21,7 @@ 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.Params; +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; @@ -72,9 +72,7 @@ public void initialize(int userId) { } private void getUserDetails(int userId) { - final Params params = Params.create(); - params.putInt(GetUserDetails.PARAM_USER_ID_KEY, userId); - this.getUserDetailsUseCase.execute(new UserDetailsObserver(), params); + this.getUserDetailsUseCase.execute(new UserDetailsObserver(), Params.forUser(userId)); } private void showViewLoading() { 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 index 2208a44d..f17a460d 100644 --- 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 @@ -21,7 +21,6 @@ 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.domain.interactor.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; @@ -112,7 +111,7 @@ private void showUsersCollectionInView(Collection usersCollection) { } private void getUserList() { - this.getUserListUseCase.execute(new UserListObserver(), Params.EMPTY); + this.getUserListUseCase.execute(new UserListObserver(), null); } private final class UserListObserver extends DefaultObserver> { From ae30300e1c2b1edbb096ad42ae66fc34c4979bb1 Mon Sep 17 00:00:00 2001 From: Fernando Cejas Date: Sun, 8 Jan 2017 19:30:18 -0300 Subject: [PATCH 26/32] Some cleanup. --- .../android10/sample/data/cache/FileManager.java | 15 +++++++-------- .../data/cache/serializer/SerializerTest.java | 6 ++++-- .../entity/mapper/UserEntityDataMapperTest.java | 6 ++++-- .../entity/mapper/UserEntityJsonMapperTest.java | 6 ++++-- .../data/exception/RepositoryErrorBundleTest.java | 3 +-- 5 files changed, 20 insertions(+), 16 deletions(-) diff --git a/data/src/main/java/com/fernandocejas/android10/sample/data/cache/FileManager.java b/data/src/main/java/com/fernandocejas/android10/sample/data/cache/FileManager.java index 3d939e47..62d99d76 100644 --- a/data/src/main/java/com/fernandocejas/android10/sample/data/cache/FileManager.java +++ b/data/src/main/java/com/fernandocejas/android10/sample/data/cache/FileManager.java @@ -44,7 +44,7 @@ public class FileManager { void writeToFile(File file, String fileContent) { if (!file.exists()) { try { - FileWriter writer = new FileWriter(file); + final FileWriter writer = new FileWriter(file); writer.write(fileContent); writer.close(); } catch (IOException e) { @@ -62,12 +62,12 @@ void writeToFile(File file, String fileContent) { * @return A string with the content of the file. */ String readFileContent(File file) { - StringBuilder fileContentBuilder = new StringBuilder(); + final StringBuilder fileContentBuilder = new StringBuilder(); if (file.exists()) { String stringLine; try { - FileReader fileReader = new FileReader(file); - BufferedReader bufferedReader = new BufferedReader(fileReader); + final FileReader fileReader = new FileReader(file); + final BufferedReader bufferedReader = new BufferedReader(fileReader); while ((stringLine = bufferedReader.readLine()) != null) { fileContentBuilder.append(stringLine).append("\n"); } @@ -77,7 +77,6 @@ String readFileContent(File file) { e.printStackTrace(); } } - return fileContentBuilder.toString(); } @@ -119,9 +118,9 @@ boolean clearDirectory(File directory) { void writeToPreferences(Context context, String preferenceFileName, String key, long value) { - SharedPreferences sharedPreferences = context.getSharedPreferences(preferenceFileName, + final SharedPreferences sharedPreferences = context.getSharedPreferences(preferenceFileName, Context.MODE_PRIVATE); - SharedPreferences.Editor editor = sharedPreferences.edit(); + final SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putLong(key, value); editor.apply(); } @@ -135,7 +134,7 @@ void writeToPreferences(Context context, String preferenceFileName, String key, * @return A long representing the value retrieved from the preferences file. */ long getFromPreferences(Context context, String preferenceFileName, String key) { - SharedPreferences sharedPreferences = context.getSharedPreferences(preferenceFileName, + final SharedPreferences sharedPreferences = context.getSharedPreferences(preferenceFileName, Context.MODE_PRIVATE); return sharedPreferences.getLong(key, 0); } diff --git a/data/src/test/java/com/fernandocejas/android10/sample/data/cache/serializer/SerializerTest.java b/data/src/test/java/com/fernandocejas/android10/sample/data/cache/serializer/SerializerTest.java index 68383665..d55bd3c3 100644 --- a/data/src/test/java/com/fernandocejas/android10/sample/data/cache/serializer/SerializerTest.java +++ b/data/src/test/java/com/fernandocejas/android10/sample/data/cache/serializer/SerializerTest.java @@ -15,16 +15,18 @@ */ package com.fernandocejas.android10.sample.data.cache.serializer; -import com.fernandocejas.android10.sample.data.ApplicationTestCase; import com.fernandocejas.android10.sample.data.entity.UserEntity; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; -public class SerializerTest extends ApplicationTestCase { +@RunWith(MockitoJUnitRunner.class) +public class SerializerTest { private static final String JSON_RESPONSE = "{\n" + " \"id\": 1,\n" diff --git a/data/src/test/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityDataMapperTest.java b/data/src/test/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityDataMapperTest.java index 19a4db56..963d28e6 100644 --- a/data/src/test/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityDataMapperTest.java +++ b/data/src/test/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityDataMapperTest.java @@ -15,7 +15,6 @@ */ package com.fernandocejas.android10.sample.data.entity.mapper; -import com.fernandocejas.android10.sample.data.ApplicationTestCase; import com.fernandocejas.android10.sample.data.entity.UserEntity; import com.fernandocejas.android10.sample.domain.User; import java.util.ArrayList; @@ -23,13 +22,16 @@ import java.util.List; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.mock; -public class UserEntityDataMapperTest extends ApplicationTestCase { +@RunWith(MockitoJUnitRunner.class) +public class UserEntityDataMapperTest { private static final int FAKE_USER_ID = 123; private static final String FAKE_FULLNAME = "Tony Stark"; diff --git a/data/src/test/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityJsonMapperTest.java b/data/src/test/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityJsonMapperTest.java index f834f52d..a6eb3fcc 100644 --- a/data/src/test/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityJsonMapperTest.java +++ b/data/src/test/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityJsonMapperTest.java @@ -15,7 +15,6 @@ */ package com.fernandocejas.android10.sample.data.entity.mapper; -import com.fernandocejas.android10.sample.data.ApplicationTestCase; import com.fernandocejas.android10.sample.data.entity.UserEntity; import com.google.gson.JsonSyntaxException; import java.util.Collection; @@ -23,12 +22,15 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -public class UserEntityJsonMapperTest extends ApplicationTestCase { +@RunWith(MockitoJUnitRunner.class) +public class UserEntityJsonMapperTest { private static final String JSON_RESPONSE_USER_DETAILS = "{\n" + " \"id\": 1,\n" diff --git a/data/src/test/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundleTest.java b/data/src/test/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundleTest.java index 517613b4..d2f3d697 100644 --- a/data/src/test/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundleTest.java +++ b/data/src/test/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundleTest.java @@ -15,7 +15,6 @@ */ package com.fernandocejas.android10.sample.data.exception; -import com.fernandocejas.android10.sample.data.ApplicationTestCase; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -25,7 +24,7 @@ import static org.mockito.Mockito.verify; @RunWith(MockitoJUnitRunner.class) -public class RepositoryErrorBundleTest extends ApplicationTestCase { +public class RepositoryErrorBundleTest { private RepositoryErrorBundle repositoryErrorBundle; From 2822604ae0b36da2e5c163cf0972289c8c02bfd6 Mon Sep 17 00:00:00 2001 From: Li Xingming Date: Tue, 21 Nov 2017 11:58:06 +0800 Subject: [PATCH 27/32] update gradles to make the build pass on Android Studio 3.0 --- build.gradle | 5 +++-- buildsystem/dependencies.gradle | 12 ++++++------ data/build.gradle | 6 +++++- gradle/wrapper/gradle-wrapper.properties | 2 +- presentation/build.gradle | 9 +++++++-- 5 files changed, 22 insertions(+), 12 deletions(-) diff --git a/build.gradle b/build.gradle index f1963057..7bb03471 100644 --- a/build.gradle +++ b/build.gradle @@ -5,10 +5,11 @@ buildscript { repositories { jcenter() mavenCentral() + google() } dependencies { - classpath 'com.android.tools.build:gradle:2.2.2' - classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' + classpath 'com.android.tools.build:gradle:3.0.0' +// classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' } } diff --git a/buildsystem/dependencies.gradle b/buildsystem/dependencies.gradle index d1d535ef..ba467503 100644 --- a/buildsystem/dependencies.gradle +++ b/buildsystem/dependencies.gradle @@ -6,22 +6,22 @@ allprojects { ext { //Android - androidBuildToolsVersion = "24.0.1" + androidBuildToolsVersion = "27.0.1" androidMinSdkVersion = 15 - androidTargetSdkVersion = 21 - androidCompileSdkVersion = 21 + androidTargetSdkVersion = 26 + androidCompileSdkVersion = 26 //Libraries daggerVersion = '2.8' butterKnifeVersion = '7.0.1' - recyclerViewVersion = '21.0.3' + recyclerViewVersion = '25.4.0' rxJavaVersion = '2.0.2' rxAndroidVersion = '2.0.1' javaxAnnotationVersion = '1.0' javaxInjectVersion = '1' gsonVersion = '2.3' okHttpVersion = '2.5.0' - androidAnnotationsVersion = '21.0.3' + androidAnnotationsVersion = '25.4.0' arrowVersion = '1.0.0' //Testing @@ -30,7 +30,7 @@ ext { assertJVersion = '1.7.1' mockitoVersion = '1.9.5' dexmakerVersion = '1.0' - espressoVersion = '2.0' + espressoVersion = '3.0.1' testingSupportLibVersion = '0.1' //Development diff --git a/data/build.gradle b/data/build.gradle index cebd214b..0f824872 100644 --- a/data/build.gradle +++ b/data/build.gradle @@ -8,7 +8,7 @@ buildscript { } apply plugin: 'com.android.library' -apply plugin: 'com.neenbedankt.android-apt' +//apply plugin: 'com.neenbedankt.android-apt' apply plugin: 'me.tatarka.retrolambda' android { @@ -65,3 +65,7 @@ dependencies { testCompile testDependencies.mockito testCompile testDependencies.robolectric } + +repositories { + google() +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 437bbca6..0ca07476 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.2.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip diff --git a/presentation/build.gradle b/presentation/build.gradle index d22cda2e..957f6f89 100644 --- a/presentation/build.gradle +++ b/presentation/build.gradle @@ -1,5 +1,5 @@ apply plugin: 'com.android.application' -apply plugin: 'com.neenbedankt.android-apt' +//apply plugin: 'com.neenbedankt.android-apt' android { def globalConfiguration = rootProject.extensions.getByName("ext") @@ -65,9 +65,10 @@ dependencies { compile project(':domain') compile project(':data') - apt presentationDependencies.daggerCompiler + annotationProcessor presentationDependencies.daggerCompiler compile presentationDependencies.dagger compile presentationDependencies.butterKnife + annotationProcessor presentationDependencies.butterKnife compile presentationDependencies.recyclerView compile presentationDependencies.rxJava compile presentationDependencies.rxAndroid @@ -82,3 +83,7 @@ dependencies { //Development compile developmentDependencies.leakCanary } + +repositories { + google() +} \ No newline at end of file From b8059779efb72f8ad257b3395149d65d16184dac Mon Sep 17 00:00:00 2001 From: Fernando Cejas Date: Wed, 7 Feb 2018 13:23:26 +0100 Subject: [PATCH 28/32] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dee9aa03..ddaaaf68 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ https://github.com/android10/java-code-styles License -------- - Copyright 2016 Fernando Cejas + Copyright 2018 Fernando Cejas Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 42491ea00c815c02bbd735898b71a4cdcbe0622e Mon Sep 17 00:00:00 2001 From: Li Xingming Date: Sun, 11 Feb 2018 16:45:33 +0800 Subject: [PATCH 29/32] update build.gradle to clean warnings. update retrolambda to 3.7.0 --- build.gradle | 2 +- data/build.gradle | 26 +++++++++++++------------- domain/build.gradle | 12 ++++++------ presentation/build.gradle | 28 ++++++++++++++-------------- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/build.gradle b/build.gradle index 7bb03471..d951c72d 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.0' + classpath 'com.android.tools.build:gradle:3.0.1' // classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' } } diff --git a/data/build.gradle b/data/build.gradle index 0f824872..e1f55a6a 100644 --- a/data/build.gradle +++ b/data/build.gradle @@ -3,7 +3,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'me.tatarka:gradle-retrolambda:3.2.3' + classpath 'me.tatarka:gradle-retrolambda:3.7.0' } } @@ -51,19 +51,19 @@ dependencies { def dataDependencies = rootProject.ext.dataDependencies def testDependencies = rootProject.ext.dataTestDependencies - compile project(':domain') - provided dataDependencies.javaxAnnotation - compile dataDependencies.javaxInject - compile dataDependencies.okHttp - compile dataDependencies.gson - compile dataDependencies.rxJava - compile dataDependencies.rxAndroid - compile dataDependencies.androidAnnotations + implementation project(':domain') + compileOnly dataDependencies.javaxAnnotation + implementation dataDependencies.javaxInject + implementation dataDependencies.okHttp + implementation dataDependencies.gson + implementation dataDependencies.rxJava + implementation dataDependencies.rxAndroid + implementation dataDependencies.androidAnnotations - testCompile testDependencies.junit - testCompile testDependencies.assertj - testCompile testDependencies.mockito - testCompile testDependencies.robolectric + testImplementation testDependencies.junit + testImplementation testDependencies.assertj + testImplementation testDependencies.mockito + testImplementation testDependencies.robolectric } repositories { diff --git a/domain/build.gradle b/domain/build.gradle index 894a22d3..f647f20c 100644 --- a/domain/build.gradle +++ b/domain/build.gradle @@ -19,13 +19,13 @@ dependencies { def domainDependencies = rootProject.ext.domainDependencies def domainTestDependencies = rootProject.ext.domainTestDependencies - provided domainDependencies.javaxAnnotation + compileOnly domainDependencies.javaxAnnotation - compile domainDependencies.javaxInject - compile domainDependencies.rxJava + implementation domainDependencies.javaxInject + implementation domainDependencies.rxJava compile domainDependencies.arrow - testCompile domainTestDependencies.junit - testCompile domainTestDependencies.mockito - testCompile domainTestDependencies.assertj + testImplementation domainTestDependencies.junit + testImplementation domainTestDependencies.mockito + testImplementation domainTestDependencies.assertj } diff --git a/presentation/build.gradle b/presentation/build.gradle index 957f6f89..08206665 100644 --- a/presentation/build.gradle +++ b/presentation/build.gradle @@ -62,26 +62,26 @@ dependencies { def presentationTestDependencies = rootProject.ext.presentationTestDependencies def developmentDependencies = rootProject.ext.developmentDependencies - compile project(':domain') - compile project(':data') + implementation project(':domain') + implementation project(':data') annotationProcessor presentationDependencies.daggerCompiler - compile presentationDependencies.dagger - compile presentationDependencies.butterKnife + implementation presentationDependencies.dagger + implementation presentationDependencies.butterKnife annotationProcessor presentationDependencies.butterKnife - compile presentationDependencies.recyclerView - compile presentationDependencies.rxJava - compile presentationDependencies.rxAndroid - provided presentationDependencies.javaxAnnotation + implementation presentationDependencies.recyclerView + implementation presentationDependencies.rxJava + implementation presentationDependencies.rxAndroid + compileOnly presentationDependencies.javaxAnnotation - androidTestCompile presentationTestDependencies.mockito - androidTestCompile presentationTestDependencies.dexmaker - androidTestCompile presentationTestDependencies.dexmakerMockito - androidTestCompile presentationTestDependencies.espresso - androidTestCompile presentationTestDependencies.testingSupportLib + androidTestImplementation presentationTestDependencies.mockito + androidTestImplementation presentationTestDependencies.dexmaker + androidTestImplementation presentationTestDependencies.dexmakerMockito + androidTestImplementation presentationTestDependencies.espresso + androidTestImplementation presentationTestDependencies.testingSupportLib //Development - compile developmentDependencies.leakCanary + implementation developmentDependencies.leakCanary } repositories { From d094fae3c922ac9d353a568de8b1064644bae6d6 Mon Sep 17 00:00:00 2001 From: Li Xingming Date: Sun, 11 Feb 2018 18:24:12 +0800 Subject: [PATCH 30/32] udpate .travis.yml due to updating of dependencies.gradle --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 43655f80..3cae64da 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,8 @@ android: - tools - platform-tools - tools - - build-tools-24.0.1 - - android-23 + - build-tools-27.0.1 + - android-26 - extra-google-m2repository - extra-android-m2repository From c678d1ca51015754a10162813d2f45fd129a4d75 Mon Sep 17 00:00:00 2001 From: Fernando Cejas Date: Wed, 9 May 2018 21:06:14 +0200 Subject: [PATCH 31/32] Update README.md --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ddaaaf68..79abe060 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,11 @@ -Android-CleanArchitecture [![Build Status](https://travis-ci.org/android10/Android-CleanArchitecture.svg?branch=master)](https://travis-ci.org/android10/Android-CleanArchitecture) +Android-CleanArchitecture ========================= +## New version available written in Kotlin: +[Architecting Android… Reloaded](https://fernandocejas.com/2018/05/07/architecting-android-reloaded/) + +Introduction +----------------- This is a sample app that is part of a blog post I have written about how to architect android application using the Uncle Bob's clean architecture approach. [Architecting Android…The clean way?](http://fernandocejas.com/2014/09/03/architecting-android-the-clean-way/) From 4dda6bcd66f6292625ae1dbaf41e0e8431f91e28 Mon Sep 17 00:00:00 2001 From: Fernando Cejas Date: Fri, 31 Aug 2018 01:35:28 +0200 Subject: [PATCH 32/32] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 79abe060..a060ce7e 100644 --- a/README.md +++ b/README.md @@ -74,3 +74,5 @@ License ![http://www.fernandocejas.com](https://github.com/android10/Sample-Data/blob/master/android10/android10_logo_big.png) [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Android--CleanArchitecture-brightgreen.svg?style=flat)](https://android-arsenal.com/details/3/909) + +Buy Me A Coffee