├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── build.gradle
├── buildsystem
├── ci.gradle
├── debug.keystore
└── dependencies.gradle
├── data
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ └── java
│ │ └── com
│ │ └── fernandocejas
│ │ └── android10
│ │ └── sample
│ │ └── data
│ │ ├── cache
│ │ ├── FileManager.java
│ │ ├── UserCache.java
│ │ ├── UserCacheImpl.java
│ │ └── serializer
│ │ │ └── JsonSerializer.java
│ │ ├── datasource
│ │ ├── CloudUserDataStore.java
│ │ ├── DiskUserDataStore.java
│ │ ├── UserDataStore.java
│ │ └── UserDataStoreFactory.java
│ │ ├── dto
│ │ └── User.java
│ │ ├── entity
│ │ ├── UserEntity.java
│ │ └── mapper
│ │ │ ├── UserEntityDataMapper.java
│ │ │ └── UserEntityJsonMapper.java
│ │ ├── exception
│ │ ├── DefaultErrorBundle.java
│ │ ├── ErrorBundle.java
│ │ ├── NetworkConnectionException.java
│ │ ├── RepositoryErrorBundle.java
│ │ └── UserNotFoundException.java
│ │ ├── executor
│ │ ├── JobExecutor.java
│ │ ├── PostExecutionThread.java
│ │ ├── ThreadExecutor.java
│ │ └── UIThread.java
│ │ └── net
│ │ ├── ApiConnection.java
│ │ ├── RestApi.java
│ │ └── RestApiImpl.java
│ └── test
│ └── java
│ └── com
│ └── fernandocejas
│ └── android10
│ └── sample
│ └── data
│ ├── ApplicationStub.java
│ ├── ApplicationTestCase.java
│ ├── cache
│ ├── FileManagerTest.java
│ └── serializer
│ │ └── JsonSerializerTest.java
│ ├── datasource
│ ├── CloudUserDataStoreTest.java
│ ├── DiskUserDataStoreTest.java
│ └── UserDataStoreFactoryTest.java
│ ├── dto
│ └── UserTest.java
│ ├── entity
│ └── mapper
│ │ ├── UserEntityDataMapperTest.java
│ │ └── UserEntityJsonMapperTest.java
│ └── exception
│ ├── DefaultErrorBundleTest.java
│ └── RepositoryErrorBundleTest.java
├── domain
├── build.gradle
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ └── java
│ │ └── com
│ │ └── fernandocejas
│ │ └── android10
│ │ └── sample
│ │ └── domain
│ │ └── interactor
│ │ ├── DefaultSubscriber.java
│ │ ├── GetUserDetails.java
│ │ ├── GetUserList.java
│ │ ├── UseCase.java
│ │ └── repository
│ │ ├── UserDataRepository.java
│ │ └── UserRepository.java
│ └── test
│ └── java
│ └── com
│ └── fernandocejas
│ └── android10
│ └── sample
│ └── domain
│ └── interactor
│ ├── GetUserDetailsTest.java
│ ├── GetUserListTest.java
│ ├── UseCaseTest.java
│ └── repository
│ └── UserDataRepositoryTest.java
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── presentation
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── fernandocejas
│ │ └── android10
│ │ └── sample
│ │ └── test
│ │ ├── exception
│ │ └── ErrorMessageFactoryTest.java
│ │ ├── mapper
│ │ └── UserModelDataMapperTest.java
│ │ ├── view
│ │ └── activity
│ │ │ ├── UserDetailsActivityTest.java
│ │ │ └── UserListActivityTest.java
│ │ └── viewmodel
│ │ ├── UserDetailsViewModelTest.java
│ │ └── UserListViewModelTest.java
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── fernandocejas
│ │ └── android10
│ │ └── sample
│ │ └── presentation
│ │ ├── AndroidApplication.java
│ │ ├── exception
│ │ └── ErrorMessageFactory.java
│ │ ├── mapper
│ │ └── UserModelDataMapper.java
│ │ ├── model
│ │ └── UserModel.java
│ │ ├── navigation
│ │ └── ActivityNavigator.java
│ │ ├── view
│ │ ├── activity
│ │ │ ├── BaseActivity.java
│ │ │ ├── MainActivity.java
│ │ │ ├── UserDetailsActivity.java
│ │ │ └── UserListActivity.java
│ │ ├── adapter
│ │ │ ├── UsersAdapter.java
│ │ │ └── UsersLayoutManager.java
│ │ ├── fragment
│ │ │ ├── BaseFragment.java
│ │ │ ├── UserDetailsFragment.java
│ │ │ └── UserListFragment.java
│ │ └── widget
│ │ │ └── AutoLoadImageView.java
│ │ └── viewmodel
│ │ ├── HomeViewModel.java
│ │ ├── LoadingViewModel.java
│ │ ├── UserDetailsViewModel.java
│ │ ├── UserListViewModel.java
│ │ └── ViewModel.java
│ └── res
│ ├── drawable-hdpi
│ ├── ic_launcher.png
│ └── logo.png
│ ├── drawable-mdpi
│ └── ic_launcher.png
│ ├── drawable-xhdpi
│ └── ic_launcher.png
│ ├── drawable-xxhdpi
│ └── ic_launcher.png
│ ├── drawable
│ └── selector_item_user.xml
│ ├── layout
│ ├── activity_user_details.xml
│ ├── fragment_user_details.xml
│ ├── fragment_user_list.xml
│ ├── home_activity.xml
│ ├── row_user.xml
│ ├── user_list_activity.xml
│ ├── view_progress.xml
│ ├── view_retry.xml
│ └── view_user_details.xml
│ └── values
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows thumbnail db
2 | Thumbs.db
3 |
4 | # OSX files
5 | .DS_Store
6 |
7 | # built application files
8 | *.apk
9 | *.ap_
10 |
11 | # files for the dex VM
12 | *.dex
13 |
14 | # Java class files
15 | *.class
16 |
17 | # generated files
18 | bin/
19 | gen/
20 | build/
21 |
22 | # Local configuration file (sdk path, etc)
23 | local.properties
24 |
25 | # Eclipse project files
26 | .classpath
27 | .project
28 |
29 | # Android Studio
30 | .idea
31 | .gradle
32 | /*/local.properties
33 | /*/out
34 | /*/*/build
35 | build
36 | /*/*/production
37 | *.iml
38 | *.iws
39 | *.ipr
40 | *~
41 | /npm-debug.log
42 | *.swp
43 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: android
2 | jdk: oraclejdk7
3 |
4 | android:
5 | components:
6 | - build-tools-21.1.2
7 | - android-19
8 |
9 | script:
10 | ./gradlew build
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | MVVM_Android-CleanArchitecture
2 | =========================
3 |
4 | 此项目由 [Android-CleanArchitecture](https://github.com/android10/Android-CleanArchitecture) fork 而来, 展现层(Presentation tier)重构成由 MVVM 模式实现,并去除 di(Dagger) 和 lambda。
5 |
6 | 项目对应博客文章:[MVVM_Android-CleanArchitecture](http://rocko.xyz/2015/11/07/MVVM_Android-CleanArchitecture/)
7 |
8 |
9 | Clean architecture
10 | -----------------
11 | 
12 |
13 | Architectural approach
14 | -----------------
15 | 
16 |
17 | Architectural reactive approach
18 | -----------------
19 | 
20 |
21 | Local Development
22 | -----------------
23 |
24 | Here are some useful Gradle/adb commands for executing this example:
25 |
26 | * `./gradlew clean build` - Build the entire example and execute unit and integration tests plus lint check.
27 | * `./gradlew installDebug` - Install the debug apk on the current connected device.
28 | * `./gradlew runUnitTests` - Execute domain and data layer tests (both unit and integration).
29 | * `./gradlew runAcceptanceTests` - Execute espresso and instrumentation acceptance tests.
30 |
31 |
32 | License
33 | --------
34 | ```
35 | Copyright 2014 Fernando Cejas
36 |
37 | Licensed under the Apache License, Version 2.0 (the "License");
38 | you may not use this file except in compliance with the License.
39 | You may obtain a copy of the License at
40 |
41 | http://www.apache.org/licenses/LICENSE-2.0
42 |
43 | Unless required by applicable law or agreed to in writing, software
44 | distributed under the License is distributed on an "AS IS" BASIS,
45 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
46 | See the License for the specific language governing permissions and
47 | limitations under the License.
48 | ```
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | apply from: 'buildsystem/ci.gradle'
2 | apply from: 'buildsystem/dependencies.gradle'
3 |
4 | buildscript {
5 | repositories {
6 | jcenter()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:1.3.0'
10 | classpath 'com.android.databinding:dataBinder:1.0-rc1'
11 | }
12 | }
13 |
14 | allprojects {
15 | ext {
16 | androidApplicationId = 'com.fernanependocejas.android10.sample.presentation'
17 | androidVersionCode = 1
18 | androidVersionName = "1.0"
19 |
20 | testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner"
21 | testApplicationId = 'com.fernandocejas.android10.sample.presentation.test'
22 | }
23 | }
24 |
25 | task wrapper(type: Wrapper) {
26 | description 'Creates the gradle wrapper.'
27 | gradleVersion '2.9'
28 | }
29 |
30 | task runDomainUnitTests(dependsOn: [':domain:test']) {
31 | description 'Run unit tests for the domain layer.'
32 | }
33 |
34 | task runDataUnitTests(dependsOn: [':data:cleanTestDebug', ':data:testDebug']) {
35 | description 'Run unit tests for the data layer.'
36 | }
37 |
38 | task runUnitTests(dependsOn: ['runDomainUnitTests', 'runDataUnitTests']) {
39 | description 'Run unit tests for both domain and data layers.'
40 | }
41 |
42 | task runAcceptanceTests(dependsOn: [':presentation:connectedAndroidTest']) {
43 | description 'Run application acceptance tests.'
44 | }
45 |
46 |
--------------------------------------------------------------------------------
/buildsystem/ci.gradle:
--------------------------------------------------------------------------------
1 | def ciServer = 'TRAVIS'
2 | def executingOnCI = "true".equals(System.getenv(ciServer))
3 |
4 | // Since for CI we always do full clean builds, we don't want to pre-dex
5 | // See http://tools.android.com/tech-docs/new-build-system/tips
6 | subprojects {
7 | project.plugins.whenPluginAdded { plugin ->
8 | if ('com.android.build.gradle.AppPlugin'.equals(plugin.class.name) ||
9 | 'com.android.build.gradle.LibraryPlugin'.equals(plugin.class.name)) {
10 | project.android.dexOptions.preDexLibraries = !executingOnCI
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/buildsystem/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhengxiaopeng/MVVM_Android-CleanArchitecture/ab6b934fdb93bf47daa37c7c6d541497f6d4d951/buildsystem/debug.keystore
--------------------------------------------------------------------------------
/buildsystem/dependencies.gradle:
--------------------------------------------------------------------------------
1 | allprojects {
2 | repositories {
3 | jcenter()
4 | }
5 | }
6 |
7 | ext {
8 | //Android
9 | androidBuildToolsVersion = "23.0.1"
10 | androidMinSdkVersion = 15
11 | androidTargetSdkVersion = 23
12 | androidCompileSdkVersion = 23
13 |
14 | //Libraries
15 | recyclerViewVersion = '23.0.1'
16 | rxJavaVersion = '1.0.14'
17 | rxAndroidVersion = '1.0.1'
18 | gsonVersion = '2.3'
19 | okHttpVersion = '2.5.0'
20 | androidAnnotationsVersion = '23.0.1'
21 |
22 | //Testing
23 | robolectricVersion = '3.0'
24 | jUnitVersion = '4.12'
25 | assertJVersion = '1.7.1'
26 | mockitoVersion = '1.9.5'
27 | dexmakerVersion = '1.0'
28 | runnerVersion = "0.4.1"
29 | rulesVersion = "0.4.1"
30 | espressoVersion = "2.2.1"
31 | espressoIntentVersion = espressoVersion
32 | espressoContrib = espressoVersion
33 |
34 | presentationDependencies = [
35 | recyclerView: "com.android.support:recyclerview-v7:${recyclerViewVersion}",
36 | rxJava: "io.reactivex:rxjava:${rxJavaVersion}",
37 | rxAndroid: "io.reactivex:rxandroid:${rxAndroidVersion}",
38 | ]
39 |
40 | presentationTestDependencies = [
41 | mockito: "org.mockito:mockito-core:${mockitoVersion}",
42 | dexmaker: "com.google.dexmaker:dexmaker:${dexmakerVersion}",
43 | dexmakerMockito: "com.google.dexmaker:dexmaker-mockito:${dexmakerVersion}",
44 | espresso: "com.android.support.test.espresso:espresso-core:${espressoVersion}",
45 | espressoIntent: "com.android.support.test.espresso:espresso-intents:${espressoIntentVersion}",
46 | espressoContrib: "com.android.support.test.espresso:espresso-contrib:${espressoContrib}",
47 | runner: "com.android.support.test:runner:${runnerVersion}",
48 | rules: "com.android.support.test:rules:${rulesVersion}",
49 | // Force usage of support annotations in the test app, since it is internally used by the runner module.
50 | androidAnnotations: "com.android.support:support-annotations:${androidAnnotationsVersion}",
51 |
52 | ]
53 |
54 | domainDependencies = [
55 | rxJava: "io.reactivex:rxjava:${rxJavaVersion}",
56 | ]
57 |
58 | domainTestDependencies = [
59 | junit: "junit:junit:${jUnitVersion}",
60 | mockito: "org.mockito:mockito-core:${mockitoVersion}",
61 | ]
62 |
63 | dataDependencies = [
64 | okHttp: "com.squareup.okhttp:okhttp:${okHttpVersion}",
65 | gson: "com.google.code.gson:gson:${gsonVersion}",
66 | rxJava: "io.reactivex:rxjava:${rxJavaVersion}",
67 | rxAndroid: "io.reactivex:rxandroid:${rxAndroidVersion}",
68 | androidAnnotations: "com.android.support:support-annotations:${androidAnnotationsVersion}"
69 | ]
70 |
71 | dataTestDependencies = [
72 | junit: "junit:junit:${jUnitVersion}",
73 | assertj: "org.assertj:assertj-core:${assertJVersion}",
74 | mockito: "org.mockito:mockito-core:${mockitoVersion}",
75 | robolectric: "org.robolectric:robolectric:${robolectricVersion}",
76 | ]
77 | }
--------------------------------------------------------------------------------
/data/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/data/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | def globalConfiguration = rootProject.extensions.getByName("ext")
5 |
6 | compileSdkVersion globalConfiguration.getAt("androidCompileSdkVersion")
7 | buildToolsVersion globalConfiguration.getAt("androidBuildToolsVersion")
8 |
9 | defaultConfig {
10 | minSdkVersion globalConfiguration.getAt("androidMinSdkVersion")
11 | targetSdkVersion globalConfiguration.getAt("androidTargetSdkVersion")
12 | versionCode globalConfiguration.getAt("androidVersionCode")
13 | }
14 |
15 | compileOptions {
16 | sourceCompatibility JavaVersion.VERSION_1_7
17 | targetCompatibility JavaVersion.VERSION_1_7
18 | }
19 |
20 | packagingOptions {
21 | exclude 'LICENSE.txt'
22 | exclude 'META-INF/DEPENDENCIES'
23 | exclude 'META-INF/ASL2.0'
24 | exclude 'META-INF/NOTICE'
25 | exclude 'META-INF/LICENSE'
26 | }
27 |
28 | lintOptions {
29 | quiet true
30 | abortOnError false
31 | ignoreWarnings true
32 | disable 'InvalidPackage' // Some libraries have issues with this
33 | disable 'OldTargetApi' // Due to Robolectric that modifies the manifest when running tests
34 | }
35 | }
36 |
37 | dependencies {
38 | def dataDependencies = rootProject.ext.dataDependencies
39 | def testDependencies = rootProject.ext.dataTestDependencies
40 |
41 | compile dataDependencies.okHttp
42 | compile dataDependencies.gson
43 | compile dataDependencies.rxJava
44 | compile dataDependencies.rxAndroid
45 | compile dataDependencies.androidAnnotations
46 |
47 | testCompile testDependencies.junit
48 | testCompile testDependencies.assertj
49 | testCompile testDependencies.mockito
50 | testCompile testDependencies.robolectric
51 | }
52 |
--------------------------------------------------------------------------------
/data/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/fcejas/Software/SDKs/android-sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/data/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/data/src/main/java/com/fernandocejas/android10/sample/data/cache/FileManager.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.cache;
17 |
18 | import android.content.Context;
19 | import android.content.SharedPreferences;
20 |
21 | import java.io.BufferedReader;
22 | import java.io.File;
23 | import java.io.FileNotFoundException;
24 | import java.io.FileReader;
25 | import java.io.FileWriter;
26 | import java.io.IOException;
27 |
28 | /**
29 | * Helper class to do operations on regular files/directories.
30 | */
31 | public class FileManager {
32 |
33 | public FileManager() {
34 | }
35 |
36 | /**
37 | * Writes a file to Disk.
38 | * This is an I/O operation and this method executes in the main thread, so it is recommended to
39 | * perform this operation using another thread.
40 | *
41 | * @param file The file to write to Disk.
42 | */
43 | public void writeToFile(File file, String fileContent) {
44 | if (!file.exists()) {
45 | try {
46 | FileWriter writer = new FileWriter(file);
47 | writer.write(fileContent);
48 | writer.close();
49 | } catch (FileNotFoundException e) {
50 | e.printStackTrace();
51 | } catch (IOException e) {
52 | e.printStackTrace();
53 | } finally {
54 |
55 | }
56 | }
57 | }
58 |
59 | /**
60 | * Reads a content from a file.
61 | * This is an I/O operation and this method executes in the main thread, so it is recommended to
62 | * perform the operation using another thread.
63 | *
64 | * @param file The file to read from.
65 | * @return A string with the content of the file.
66 | */
67 | public String readFileContent(File file) {
68 | StringBuilder fileContentBuilder = new StringBuilder();
69 | if (file.exists()) {
70 | String stringLine;
71 | try {
72 | FileReader fileReader = new FileReader(file);
73 | BufferedReader bufferedReader = new BufferedReader(fileReader);
74 | while ((stringLine = bufferedReader.readLine()) != null) {
75 | fileContentBuilder.append(stringLine + "\n");
76 | }
77 | bufferedReader.close();
78 | fileReader.close();
79 | } catch (FileNotFoundException e) {
80 | e.printStackTrace();
81 | } catch (IOException e) {
82 | e.printStackTrace();
83 | }
84 | }
85 |
86 | return fileContentBuilder.toString();
87 | }
88 |
89 | /**
90 | * Returns a boolean indicating whether this file can be found on the underlying file system.
91 | *
92 | * @param file The file to check existence.
93 | * @return true if this file exists, false otherwise.
94 | */
95 | public boolean exists(File file) {
96 | return file.exists();
97 | }
98 |
99 | /**
100 | * Warning: Deletes the content of a directory.
101 | * This is an I/O operation and this method executes in the main thread, so it is recommended to
102 | * perform the operation using another thread.
103 | *
104 | * @param directory The directory which its content will be deleted.
105 | */
106 | public void clearDirectory(File directory) {
107 | if (directory.exists()) {
108 | for (File file : directory.listFiles()) {
109 | file.delete();
110 | }
111 | }
112 | }
113 |
114 | /**
115 | * Write a value to a user preferences file.
116 | *
117 | * @param context {@link android.content.Context} to retrieve android user preferences.
118 | * @param preferenceFileName A file name reprensenting where data will be written to.
119 | * @param key A string for the key that will be used to retrieve the value in the future.
120 | * @param value A long representing the value to be inserted.
121 | */
122 | public void writeToPreferences(Context context, String preferenceFileName, String key,
123 | long value) {
124 |
125 | SharedPreferences sharedPreferences = context.getSharedPreferences(preferenceFileName,
126 | Context.MODE_PRIVATE);
127 | SharedPreferences.Editor editor = sharedPreferences.edit();
128 | editor.putLong(key, value);
129 | editor.apply();
130 | }
131 |
132 | /**
133 | * Get a value from a user preferences file.
134 | *
135 | * @param context {@link android.content.Context} to retrieve android user preferences.
136 | * @param preferenceFileName A file name representing where data will be get from.
137 | * @param key A key that will be used to retrieve the value from the preference file.
138 | * @return A long representing the value retrieved from the preferences file.
139 | */
140 | public long getFromPreferences(Context context, String preferenceFileName, String key) {
141 | SharedPreferences sharedPreferences = context.getSharedPreferences(preferenceFileName,
142 | Context.MODE_PRIVATE);
143 | return sharedPreferences.getLong(key, 0);
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCache.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.cache;
17 |
18 | import com.fernandocejas.android10.sample.data.entity.UserEntity;
19 | import rx.Observable;
20 |
21 | /**
22 | * An interface representing a user Cache.
23 | */
24 | public interface UserCache {
25 | /**
26 | * Gets an {@link rx.Observable} which will emit a {@link UserEntity}.
27 | *
28 | * @param userId The user id to retrieve data.
29 | */
30 | Observable get(final int userId);
31 |
32 | /**
33 | * Puts and element into the cache.
34 | *
35 | * @param userEntity Element to insert in the cache.
36 | */
37 | void put(UserEntity userEntity);
38 |
39 | /**
40 | * Checks if an element (User) exists in the cache.
41 | *
42 | * @param userId The id used to look for inside the cache.
43 | * @return true if the element is cached, otherwise false.
44 | */
45 | boolean isCached(final int userId);
46 |
47 | /**
48 | * Checks if the cache is expired.
49 | *
50 | * @return true, the cache is expired, otherwise false.
51 | */
52 | boolean isExpired();
53 |
54 | /**
55 | * Evict all elements of the cache.
56 | */
57 | void evictAll();
58 | }
59 |
--------------------------------------------------------------------------------
/data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCacheImpl.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.cache;
17 |
18 | import android.content.Context;
19 |
20 | import com.fernandocejas.android10.sample.data.cache.serializer.JsonSerializer;
21 | import com.fernandocejas.android10.sample.data.entity.UserEntity;
22 | import com.fernandocejas.android10.sample.data.exception.UserNotFoundException;
23 | import com.fernandocejas.android10.sample.data.executor.JobExecutor;
24 | import com.fernandocejas.android10.sample.data.executor.ThreadExecutor;
25 |
26 | import java.io.File;
27 |
28 | import rx.Observable;
29 | import rx.Subscriber;
30 |
31 | /**
32 | * {@link UserCache} implementation.
33 | */
34 | public class UserCacheImpl implements UserCache {
35 |
36 | private static final String SETTINGS_FILE_NAME = "com.fernandocejas.android10.SETTINGS";
37 | private static final String SETTINGS_KEY_LAST_CACHE_UPDATE = "last_cache_update";
38 |
39 | private static final String DEFAULT_FILE_NAME = "user_";
40 | private static final long EXPIRATION_TIME = 60 * 10 * 1000;
41 |
42 | private final Context context;
43 | private final File cacheDir;
44 | private JsonSerializer serializer;
45 | private FileManager fileManager;
46 | private ThreadExecutor threadExecutor;
47 |
48 | /**
49 | * Constructor of the class {@link UserCacheImpl}.
50 | *
51 | *
52 | * UserCacheSerializer {@link JsonSerializer} for object serialization.
53 | * {@link FileManager} for saving serialized objects to the file system.
54 | */
55 | public UserCacheImpl(Context applicationContext) {
56 | this(applicationContext, new JsonSerializer(), new FileManager(), new JobExecutor());
57 | }
58 |
59 | /**
60 | * Constructor of the class {@link UserCacheImpl}.
61 | *
62 | * @param context A
63 | * @param userCacheSerializer {@link JsonSerializer} for object serialization.
64 | * @param fileManager {@link FileManager} for saving serialized objects to the file system.
65 | */
66 | public UserCacheImpl(Context context, JsonSerializer userCacheSerializer,
67 | FileManager fileManager, ThreadExecutor executor) {
68 | if (context == null || userCacheSerializer == null || fileManager == null || executor == null) {
69 | throw new IllegalArgumentException("Invalid null parameter");
70 | }
71 | this.context = context.getApplicationContext();
72 | this.cacheDir = this.context.getCacheDir();
73 | this.serializer = userCacheSerializer;
74 | this.fileManager = fileManager;
75 | this.threadExecutor = executor;
76 | }
77 |
78 | public void setSerializer(JsonSerializer serializer) {
79 | this.serializer = serializer;
80 | }
81 |
82 | public void setFileManager(FileManager fileManager) {
83 | this.fileManager = fileManager;
84 | }
85 |
86 | public void setThreadExecutor(ThreadExecutor threadExecutor) {
87 | this.threadExecutor = threadExecutor;
88 | }
89 |
90 | @Override
91 | public synchronized Observable get(final int userId) {
92 | return Observable.create(new Observable.OnSubscribe() {
93 | @Override
94 | public void call(Subscriber super UserEntity> subscriber) {
95 | File userEntityFile = UserCacheImpl.this.buildFile(userId);
96 | String fileContent = UserCacheImpl.this.fileManager.readFileContent(userEntityFile);
97 | UserEntity userEntity = UserCacheImpl.this.serializer.deserialize(fileContent);
98 |
99 | if (userEntity != null) {
100 | subscriber.onNext(userEntity);
101 | subscriber.onCompleted();
102 | } else {
103 | subscriber.onError(new UserNotFoundException());
104 | }
105 | }
106 | });
107 | }
108 |
109 | @Override
110 | public synchronized void put(UserEntity userEntity) {
111 | if (userEntity != null) {
112 | File userEntitiyFile = this.buildFile(userEntity.getUserId());
113 | if (!isCached(userEntity.getUserId())) {
114 | String jsonString = this.serializer.serialize(userEntity);
115 | this.executeAsynchronously(new CacheWriter(this.fileManager, userEntitiyFile,
116 | jsonString));
117 | setLastCacheUpdateTimeMillis();
118 | }
119 | }
120 | }
121 |
122 | @Override
123 | public boolean isCached(int userId) {
124 | File userEntitiyFile = this.buildFile(userId);
125 | return this.fileManager.exists(userEntitiyFile);
126 | }
127 |
128 | @Override
129 | public boolean isExpired() {
130 | long currentTime = System.currentTimeMillis();
131 | long lastUpdateTime = this.getLastCacheUpdateTimeMillis();
132 |
133 | boolean expired = ((currentTime - lastUpdateTime) > EXPIRATION_TIME);
134 |
135 | if (expired) {
136 | this.evictAll();
137 | }
138 |
139 | return expired;
140 | }
141 |
142 | @Override
143 | public synchronized void evictAll() {
144 | this.executeAsynchronously(new CacheEvictor(this.fileManager, this.cacheDir));
145 | }
146 |
147 | /**
148 | * Build a file, used to be inserted in the disk cache.
149 | *
150 | * @param userId The id user to build the file.
151 | * @return A valid file.
152 | */
153 | private File buildFile(int userId) {
154 | StringBuilder fileNameBuilder = new StringBuilder();
155 | fileNameBuilder.append(this.cacheDir.getPath());
156 | fileNameBuilder.append(File.separator);
157 | fileNameBuilder.append(DEFAULT_FILE_NAME);
158 | fileNameBuilder.append(userId);
159 |
160 | return new File(fileNameBuilder.toString());
161 | }
162 |
163 | /**
164 | * Set in millis, the last time the cache was accessed.
165 | */
166 | private void setLastCacheUpdateTimeMillis() {
167 | long currentMillis = System.currentTimeMillis();
168 | this.fileManager.writeToPreferences(this.context, SETTINGS_FILE_NAME,
169 | SETTINGS_KEY_LAST_CACHE_UPDATE, currentMillis);
170 | }
171 |
172 | /**
173 | * Get in millis, the last time the cache was accessed.
174 | */
175 | private long getLastCacheUpdateTimeMillis() {
176 | return this.fileManager.getFromPreferences(this.context, SETTINGS_FILE_NAME,
177 | SETTINGS_KEY_LAST_CACHE_UPDATE);
178 | }
179 |
180 | /**
181 | * Executes a {@link Runnable} in another Thread.
182 | *
183 | * @param runnable {@link Runnable} to execute
184 | */
185 | private void executeAsynchronously(Runnable runnable) {
186 | this.threadExecutor.execute(runnable);
187 | }
188 |
189 | /**
190 | * {@link Runnable} class for writing to disk.
191 | */
192 | private static class CacheWriter implements Runnable {
193 | private final FileManager fileManager;
194 | private final File fileToWrite;
195 | private final String fileContent;
196 |
197 | CacheWriter(FileManager fileManager, File fileToWrite, String fileContent) {
198 | this.fileManager = fileManager;
199 | this.fileToWrite = fileToWrite;
200 | this.fileContent = fileContent;
201 | }
202 |
203 | @Override
204 | public void run() {
205 | this.fileManager.writeToFile(fileToWrite, fileContent);
206 | }
207 | }
208 |
209 | /**
210 | * {@link Runnable} class for evicting all the cached files
211 | */
212 | private static class CacheEvictor implements Runnable {
213 | private final FileManager fileManager;
214 | private final File cacheDir;
215 |
216 | CacheEvictor(FileManager fileManager, File cacheDir) {
217 | this.fileManager = fileManager;
218 | this.cacheDir = cacheDir;
219 | }
220 |
221 | @Override
222 | public void run() {
223 | this.fileManager.clearDirectory(this.cacheDir);
224 | }
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/data/src/main/java/com/fernandocejas/android10/sample/data/cache/serializer/JsonSerializer.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.cache.serializer;
17 |
18 | import com.fernandocejas.android10.sample.data.entity.UserEntity;
19 | import com.google.gson.Gson;
20 |
21 | /**
22 | * Class user as Serializer/Deserializer for user entities.
23 | */
24 | public class JsonSerializer {
25 |
26 | private final Gson gson = new Gson();
27 |
28 | public JsonSerializer() {}
29 |
30 | /**
31 | * Serialize an object to Json.
32 | *
33 | * @param userEntity {@link UserEntity} to serialize.
34 | */
35 | public String serialize(UserEntity userEntity) {
36 | String jsonString = gson.toJson(userEntity, UserEntity.class);
37 | return jsonString;
38 | }
39 |
40 | /**
41 | * Deserialize a json representation of an object.
42 | *
43 | * @param jsonString A json string to deserialize.
44 | * @return {@link UserEntity}
45 | */
46 | public UserEntity deserialize(String jsonString) {
47 | UserEntity userEntity = gson.fromJson(jsonString, UserEntity.class);
48 | return userEntity;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/data/src/main/java/com/fernandocejas/android10/sample/data/datasource/CloudUserDataStore.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.datasource;
17 |
18 | import com.fernandocejas.android10.sample.data.cache.UserCache;
19 | import com.fernandocejas.android10.sample.data.entity.UserEntity;
20 | import com.fernandocejas.android10.sample.data.net.RestApi;
21 |
22 | import java.util.List;
23 |
24 | import rx.Observable;
25 | import rx.functions.Action1;
26 |
27 | /**
28 | * {@link UserDataStore} implementation based on connections to the api (Cloud).
29 | */
30 | public class CloudUserDataStore implements UserDataStore {
31 |
32 | private final RestApi restApi;
33 | private final UserCache userCache;
34 |
35 | private final Action1 saveToCacheAction = new Action1() {
36 | @Override
37 | public void call(UserEntity userEntity) {
38 | if (userEntity != null) {
39 | CloudUserDataStore.this.userCache.put(userEntity);
40 | }
41 | }
42 | };
43 |
44 | /**
45 | * Construct a {@link UserDataStore} based on connections to the api (Cloud).
46 | *
47 | * @param restApi The {@link RestApi} implementation to use.
48 | * @param userCache A {@link UserCache} to cache data retrieved from the api.
49 | */
50 | public CloudUserDataStore(RestApi restApi, UserCache userCache) {
51 | this.restApi = restApi;
52 | this.userCache = userCache;
53 | }
54 |
55 | @Override
56 | public Observable> userEntityList() {
57 | return this.restApi.userEntityList();
58 | }
59 |
60 | @Override
61 | public Observable userEntityDetails(final int userId) {
62 | return this.restApi.userEntityById(userId)
63 | .doOnNext(saveToCacheAction);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/data/src/main/java/com/fernandocejas/android10/sample/data/datasource/DiskUserDataStore.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.datasource;
17 |
18 | import com.fernandocejas.android10.sample.data.cache.UserCache;
19 | import com.fernandocejas.android10.sample.data.entity.UserEntity;
20 | import java.util.List;
21 | import rx.Observable;
22 |
23 | /**
24 | * {@link UserDataStore} implementation based on file system data store.
25 | */
26 | public class DiskUserDataStore implements UserDataStore {
27 |
28 | private final UserCache userCache;
29 |
30 | /**
31 | * Construct a {@link UserDataStore} based file system data store.
32 | *
33 | * @param userCache A {@link UserCache} to cache data retrieved from the api.
34 | */
35 | public DiskUserDataStore(UserCache userCache) {
36 | this.userCache = userCache;
37 | }
38 |
39 | @Override public Observable> userEntityList() {
40 | //TODO: implement simple cache for storing/retrieving collections of users.
41 | throw new UnsupportedOperationException("Operation is not available!!!");
42 | }
43 |
44 | @Override public Observable userEntityDetails(final int userId) {
45 | return this.userCache.get(userId);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/data/src/main/java/com/fernandocejas/android10/sample/data/datasource/UserDataStore.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.datasource;
17 |
18 | import com.fernandocejas.android10.sample.data.entity.UserEntity;
19 | import java.util.List;
20 | import rx.Observable;
21 |
22 | /**
23 | * Interface that represents a data store from where data is retrieved.
24 | */
25 | public interface UserDataStore {
26 | /**
27 | * Get an {@link rx.Observable} which will emit a List of {@link UserEntity}.
28 | */
29 | Observable> userEntityList();
30 |
31 | /**
32 | * Get an {@link rx.Observable} which will emit a {@link UserEntity} by its id.
33 | *
34 | * @param userId The id to retrieve user data.
35 | */
36 | Observable userEntityDetails(final int userId);
37 | }
38 |
--------------------------------------------------------------------------------
/data/src/main/java/com/fernandocejas/android10/sample/data/datasource/UserDataStoreFactory.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.datasource;
17 |
18 | import android.content.Context;
19 |
20 | import com.fernandocejas.android10.sample.data.cache.UserCache;
21 | import com.fernandocejas.android10.sample.data.cache.UserCacheImpl;
22 | import com.fernandocejas.android10.sample.data.entity.mapper.UserEntityJsonMapper;
23 | import com.fernandocejas.android10.sample.data.net.RestApi;
24 | import com.fernandocejas.android10.sample.data.net.RestApiImpl;
25 |
26 | /**
27 | * Factory that creates different implementations of {@link UserDataStore}.
28 | */
29 | public class UserDataStoreFactory {
30 |
31 | private final Context context ;
32 | private UserCache userCache;
33 |
34 | public UserDataStoreFactory(Context applicationContext) {
35 | this(applicationContext, new UserCacheImpl(applicationContext));
36 | }
37 |
38 | public UserDataStoreFactory(Context context, UserCache userCache) {
39 | if (context == null || userCache == null) {
40 | throw new IllegalArgumentException("Constructor parameters cannot be null!!!");
41 | }
42 | this.context = context.getApplicationContext();
43 | this.userCache = userCache;
44 | }
45 |
46 | public void setUserCache(UserCache userCache) {
47 | this.userCache = userCache;
48 | }
49 |
50 | /**
51 | * Create {@link UserDataStore} from a user id.
52 | */
53 | public UserDataStore create(int userId) {
54 | UserDataStore userDataStore;
55 |
56 | if (!this.userCache.isExpired() && this.userCache.isCached(userId)) {
57 | userDataStore = new DiskUserDataStore(this.userCache);
58 | } else {
59 | userDataStore = createCloudDataStore();
60 | }
61 |
62 | return userDataStore;
63 | }
64 |
65 | /**
66 | * Create {@link UserDataStore} to retrieve data from the Cloud.
67 | */
68 | public UserDataStore createCloudDataStore() {
69 | UserEntityJsonMapper userEntityJsonMapper = new UserEntityJsonMapper();
70 | RestApi restApi = new RestApiImpl(this.context, userEntityJsonMapper);
71 |
72 | return new CloudUserDataStore(restApi, this.userCache);
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/data/src/main/java/com/fernandocejas/android10/sample/data/dto/User.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.dto;
17 |
18 | /**
19 | * Class that represents a User in the domain layer.
20 | */
21 | public class User{
22 |
23 | private final int userId;
24 |
25 | public User(int userId) {
26 | this.userId = userId;
27 | }
28 |
29 | private String coverUrl;
30 | private String fullName;
31 | private String email;
32 | private String description;
33 | private int followers;
34 |
35 | public int getUserId() {
36 | return userId;
37 | }
38 |
39 | public String getCoverUrl() {
40 | return coverUrl;
41 | }
42 |
43 | public void setCoverUrl(String coverUrl) {
44 | this.coverUrl = coverUrl;
45 | }
46 |
47 | public String getFullName() {
48 | return fullName;
49 | }
50 |
51 | public void setFullName(String fullName) {
52 | this.fullName = fullName;
53 | }
54 |
55 | public String getEmail() {
56 | return email;
57 | }
58 |
59 | public void setEmail(String email) {
60 | this.email = email;
61 | }
62 |
63 | public String getDescription() {
64 | return description;
65 | }
66 |
67 | public void setDescription(String description) {
68 | this.description = description;
69 | }
70 |
71 | public int getFollowers() {
72 | return followers;
73 | }
74 |
75 | public void setFollowers(int followers) {
76 | this.followers = followers;
77 | }
78 |
79 | @Override public String toString() {
80 | StringBuilder stringBuilder = new StringBuilder();
81 |
82 | stringBuilder.append("***** User Details *****\n");
83 | stringBuilder.append("id=" + this.getUserId() + "\n");
84 | stringBuilder.append("cover url=" + this.getCoverUrl() + "\n");
85 | stringBuilder.append("fullname=" + this.getFullName() + "\n");
86 | stringBuilder.append("email=" + this.getEmail() + "\n");
87 | stringBuilder.append("description=" + this.getDescription() + "\n");
88 | stringBuilder.append("followers=" + this.getFollowers() + "\n");
89 | stringBuilder.append("*******************************");
90 |
91 | return stringBuilder.toString();
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/data/src/main/java/com/fernandocejas/android10/sample/data/entity/UserEntity.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.entity;
17 |
18 | import com.google.gson.annotations.SerializedName;
19 |
20 | /**
21 | * User Entity used in the data layer.
22 | */
23 | public class UserEntity {
24 |
25 | @SerializedName("id")
26 | private int userId;
27 |
28 | @SerializedName("cover_url")
29 | private String coverUrl;
30 |
31 | @SerializedName("full_name")
32 | private String fullname;
33 |
34 | @SerializedName("description")
35 | private String description;
36 |
37 | @SerializedName("followers")
38 | private int followers;
39 |
40 | @SerializedName("email")
41 | private String email;
42 |
43 | public UserEntity() {
44 | //empty
45 | }
46 |
47 | public int getUserId() {
48 | return userId;
49 | }
50 |
51 | public void setUserId(int userId) {
52 | this.userId = userId;
53 | }
54 |
55 | public String getCoverUrl() {
56 | return coverUrl;
57 | }
58 |
59 | public void setCoverUrl(String coverUrl) {
60 | this.coverUrl = coverUrl;
61 | }
62 |
63 | public String getFullname() {
64 | return fullname;
65 | }
66 |
67 | public void setFullname(String fullname) {
68 | this.fullname = fullname;
69 | }
70 |
71 | public String getDescription() {
72 | return description;
73 | }
74 |
75 | public void setDescription(String description) {
76 | this.description = description;
77 | }
78 |
79 | public int getFollowers() {
80 | return followers;
81 | }
82 |
83 | public void setFollowers(int followers) {
84 | this.followers = followers;
85 | }
86 |
87 | public String getEmail() {
88 | return email;
89 | }
90 |
91 | public void setEmail(String email) {
92 | this.email = email;
93 | }
94 |
95 | @Override public String toString() {
96 | StringBuilder stringBuilder = new StringBuilder();
97 |
98 | stringBuilder.append("***** User Entity Details *****\n");
99 | stringBuilder.append("id=" + this.getUserId() + "\n");
100 | stringBuilder.append("cover url=" + this.getCoverUrl() + "\n");
101 | stringBuilder.append("fullname=" + this.getFullname() + "\n");
102 | stringBuilder.append("email=" + this.getEmail() + "\n");
103 | stringBuilder.append("description=" + this.getDescription() + "\n");
104 | stringBuilder.append("followers=" + this.getFollowers() + "\n");
105 | stringBuilder.append("*******************************");
106 |
107 | return stringBuilder.toString();
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/data/src/main/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityDataMapper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.entity.mapper;
17 |
18 | import com.fernandocejas.android10.sample.data.dto.User;
19 | import com.fernandocejas.android10.sample.data.entity.UserEntity;
20 |
21 | import java.util.ArrayList;
22 | import java.util.Collection;
23 | import java.util.List;
24 |
25 | /**
26 | * Mapper class used to transform {@link UserEntity} (in the data layer) to {@link User} in the
27 | * domain layer.
28 | */
29 | public class UserEntityDataMapper {
30 |
31 | public UserEntityDataMapper() {
32 | }
33 |
34 | /**
35 | * Transform a {@link UserEntity} into an {@link User}.
36 | *
37 | * @param userEntity Object to be transformed.
38 | * @return {@link User} if valid {@link UserEntity} otherwise null.
39 | */
40 | public User transform(UserEntity userEntity) {
41 | User user = null;
42 | if (userEntity != null) {
43 | user = new User(userEntity.getUserId());
44 | user.setCoverUrl(userEntity.getCoverUrl());
45 | user.setFullName(userEntity.getFullname());
46 | user.setDescription(userEntity.getDescription());
47 | user.setFollowers(userEntity.getFollowers());
48 | user.setEmail(userEntity.getEmail());
49 | }
50 |
51 | return user;
52 | }
53 |
54 | /**
55 | * Transform a List of {@link UserEntity} into a Collection of {@link User}.
56 | *
57 | * @param userEntityCollection Object Collection to be transformed.
58 | * @return {@link User} if valid {@link UserEntity} otherwise null.
59 | */
60 | public List transform(Collection userEntityCollection) {
61 | List userList = new ArrayList<>(20);
62 | User user;
63 | for (UserEntity userEntity : userEntityCollection) {
64 | user = transform(userEntity);
65 | if (user != null) {
66 | userList.add(user);
67 | }
68 | }
69 |
70 | return userList;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/data/src/main/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityJsonMapper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.entity.mapper;
17 |
18 | import com.fernandocejas.android10.sample.data.entity.UserEntity;
19 | import com.google.gson.Gson;
20 | import com.google.gson.JsonSyntaxException;
21 | import com.google.gson.reflect.TypeToken;
22 | import java.lang.reflect.Type;
23 | import java.util.List;
24 |
25 | /**
26 | * Class used to transform from Strings representing json to valid objects.
27 | */
28 | public class UserEntityJsonMapper {
29 |
30 | private final Gson gson;
31 |
32 | public UserEntityJsonMapper() {
33 | this.gson = new Gson();
34 | }
35 |
36 | /**
37 | * Transform from valid json string to {@link UserEntity}.
38 | *
39 | * @param userJsonResponse A json representing a user profile.
40 | * @return {@link UserEntity}.
41 | * @throws com.google.gson.JsonSyntaxException if the json string is not a valid json structure.
42 | */
43 | public UserEntity transformUserEntity(String userJsonResponse) throws JsonSyntaxException {
44 | try {
45 | Type userEntityType = new TypeToken() {}.getType();
46 | UserEntity userEntity = this.gson.fromJson(userJsonResponse, userEntityType);
47 |
48 | return userEntity;
49 | } catch (JsonSyntaxException jsonException) {
50 | throw jsonException;
51 | }
52 | }
53 |
54 | /**
55 | * Transform from valid json string to List of {@link UserEntity}.
56 | *
57 | * @param userListJsonResponse A json representing a collection of users.
58 | * @return List of {@link UserEntity}.
59 | * @throws com.google.gson.JsonSyntaxException if the json string is not a valid json structure.
60 | */
61 | public List transformUserEntityCollection(String userListJsonResponse)
62 | throws JsonSyntaxException {
63 |
64 | List userEntityCollection;
65 | try {
66 | Type listOfUserEntityType = new TypeToken>() {}.getType();
67 | userEntityCollection = this.gson.fromJson(userListJsonResponse, listOfUserEntityType);
68 |
69 | return userEntityCollection;
70 | } catch (JsonSyntaxException jsonException) {
71 | throw jsonException;
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/data/src/main/java/com/fernandocejas/android10/sample/data/exception/DefaultErrorBundle.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.exception;
17 |
18 | /**
19 | * Wrapper around Exceptions used to manage default errors.
20 | */
21 | public class DefaultErrorBundle implements ErrorBundle {
22 |
23 | private static final String DEFAULT_ERROR_MSG = "Unknown error";
24 |
25 | private final Exception exception;
26 |
27 | public DefaultErrorBundle(Exception exception) {
28 | this.exception = exception;
29 | }
30 |
31 | @Override
32 | public Exception getException() {
33 | return exception;
34 | }
35 |
36 | @Override
37 | public String getErrorMessage() {
38 | return (exception != null) ? this.exception.getMessage() : DEFAULT_ERROR_MSG;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/data/src/main/java/com/fernandocejas/android10/sample/data/exception/ErrorBundle.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.exception;
17 |
18 | /**
19 | * Interface to represent a wrapper around an {@link Exception} to manage errors.
20 | */
21 | public interface ErrorBundle {
22 | Exception getException();
23 |
24 | String getErrorMessage();
25 | }
26 |
--------------------------------------------------------------------------------
/data/src/main/java/com/fernandocejas/android10/sample/data/exception/NetworkConnectionException.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.exception;
17 |
18 | /**
19 | * Exception throw by the application when a there is a network connection exception.
20 | */
21 | public class NetworkConnectionException extends Exception {
22 |
23 | public NetworkConnectionException() {
24 | super();
25 | }
26 |
27 | public NetworkConnectionException(final String message) {
28 | super(message);
29 | }
30 |
31 | public NetworkConnectionException(final String message, final Throwable cause) {
32 | super(message, cause);
33 | }
34 |
35 | public NetworkConnectionException(final Throwable cause) {
36 | super(cause);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/data/src/main/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundle.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.exception;
17 |
18 |
19 | /**
20 | * Wrapper around Exceptions used to manage errors in the repository.
21 | */
22 | public class RepositoryErrorBundle implements ErrorBundle {
23 |
24 | private final Exception exception;
25 |
26 | public RepositoryErrorBundle(Exception exception) {
27 | this.exception = exception;
28 | }
29 |
30 | @Override
31 | public Exception getException() {
32 | return exception;
33 | }
34 |
35 | @Override
36 | public String getErrorMessage() {
37 | String message = "";
38 | if (this.exception != null) {
39 | this.exception.getMessage();
40 | }
41 | return message;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/data/src/main/java/com/fernandocejas/android10/sample/data/exception/UserNotFoundException.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.exception;
17 |
18 | /**
19 | * Exception throw by the application when a User search can't return a valid result.
20 | */
21 | public class UserNotFoundException extends Exception {
22 |
23 | public UserNotFoundException() {
24 | super();
25 | }
26 |
27 | public UserNotFoundException(final String message) {
28 | super(message);
29 | }
30 |
31 | public UserNotFoundException(final String message, final Throwable cause) {
32 | super(message, cause);
33 | }
34 |
35 | public UserNotFoundException(final Throwable cause) {
36 | super(cause);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/data/src/main/java/com/fernandocejas/android10/sample/data/executor/JobExecutor.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.executor;
17 |
18 |
19 | import java.util.concurrent.BlockingQueue;
20 | import java.util.concurrent.LinkedBlockingQueue;
21 | import java.util.concurrent.ThreadFactory;
22 | import java.util.concurrent.ThreadPoolExecutor;
23 | import java.util.concurrent.TimeUnit;
24 |
25 | /**
26 | * Decorated {@link java.util.concurrent.ThreadPoolExecutor}
27 | */
28 | public class JobExecutor implements ThreadExecutor {
29 |
30 | private static final int INITIAL_POOL_SIZE = 3;
31 | private static final int MAX_POOL_SIZE = 5;
32 |
33 | // Sets the amount of time an idle thread waits before terminating
34 | private static final int KEEP_ALIVE_TIME = 10;
35 |
36 | // Sets the Time Unit to seconds
37 | private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
38 |
39 | private final BlockingQueue workQueue;
40 |
41 | private final ThreadPoolExecutor threadPoolExecutor;
42 |
43 | private final ThreadFactory threadFactory;
44 |
45 | public JobExecutor() {
46 | this.workQueue = new LinkedBlockingQueue<>();
47 | this.threadFactory = new JobThreadFactory();
48 | this.threadPoolExecutor = new ThreadPoolExecutor(INITIAL_POOL_SIZE, MAX_POOL_SIZE,
49 | KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, this.workQueue, this.threadFactory);
50 | }
51 |
52 | @Override
53 | public void execute(Runnable runnable) {
54 | if (runnable == null) {
55 | throw new IllegalArgumentException("Runnable to execute cannot be null");
56 | }
57 | this.threadPoolExecutor.execute(runnable);
58 | }
59 |
60 | private static class JobThreadFactory implements ThreadFactory {
61 | private static final String THREAD_NAME = "android_";
62 | private int counter = 0;
63 |
64 | @Override
65 | public Thread newThread(Runnable runnable) {
66 | return new Thread(runnable, THREAD_NAME + counter);
67 | }
68 | }
69 | }
--------------------------------------------------------------------------------
/data/src/main/java/com/fernandocejas/android10/sample/data/executor/PostExecutionThread.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.executor;
17 |
18 | import rx.Scheduler;
19 |
20 | /**
21 | * Thread abstraction created to change the execution context from any thread to any other thread.
22 | * Useful to encapsulate a UI Thread for example, since some job will be done in background, an
23 | * implementation of this interface will change context and update the UI.
24 | */
25 | public interface PostExecutionThread {
26 | Scheduler getScheduler();
27 | }
28 |
--------------------------------------------------------------------------------
/data/src/main/java/com/fernandocejas/android10/sample/data/executor/ThreadExecutor.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.executor;
17 |
18 | import java.util.concurrent.Executor;
19 |
20 | /**
21 | * Executor implementation can be based on different frameworks or techniques of asynchronous
22 | * execution, but every implementation will execute the
23 | * {@link com.fernandocejas.android10.sample.domain.interactor.UseCase} out of the UI thread.
24 | */
25 | public interface ThreadExecutor extends Executor {}
26 |
--------------------------------------------------------------------------------
/data/src/main/java/com/fernandocejas/android10/sample/data/executor/UIThread.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.executor;
17 |
18 |
19 | import rx.Scheduler;
20 | import rx.android.schedulers.AndroidSchedulers;
21 |
22 | /**
23 | * MainThread (UI Thread) implementation based on a {@link rx.Scheduler}
24 | * which will execute actions on the Android UI thread
25 | */
26 | public class UIThread implements PostExecutionThread {
27 |
28 | public UIThread() {
29 | }
30 |
31 | @Override
32 | public Scheduler getScheduler() {
33 | return AndroidSchedulers.mainThread();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/data/src/main/java/com/fernandocejas/android10/sample/data/net/ApiConnection.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.net;
17 |
18 | import android.support.annotation.Nullable;
19 | import com.squareup.okhttp.OkHttpClient;
20 | import com.squareup.okhttp.Request;
21 | import java.io.IOException;
22 | import java.net.MalformedURLException;
23 | import java.net.URL;
24 | import java.util.concurrent.Callable;
25 | import java.util.concurrent.TimeUnit;
26 |
27 | /**
28 | * Api Connection class used to retrieve data from the cloud.
29 | * Implements {@link java.util.concurrent.Callable} so when executed asynchronously can
30 | * return a value.
31 | */
32 | public class ApiConnection implements Callable {
33 |
34 | private static final String CONTENT_TYPE_LABEL = "Content-Type";
35 | private static final String CONTENT_TYPE_VALUE_JSON = "application/json; charset=utf-8";
36 |
37 | private URL url;
38 | private String response;
39 |
40 | private ApiConnection(String url) throws MalformedURLException {
41 | this.url = new URL(url);
42 | }
43 |
44 | public static ApiConnection createGET(String url) throws MalformedURLException {
45 | return new ApiConnection(url);
46 | }
47 |
48 | /**
49 | * Do a request to an api synchronously.
50 | * It should not be executed in the main thread of the application.
51 | *
52 | * @return A string response
53 | */
54 | @Nullable
55 | public String requestSyncCall() {
56 | connectToApi();
57 | return response;
58 | }
59 |
60 | private void connectToApi() {
61 | OkHttpClient okHttpClient = this.createClient();
62 | final Request request = new Request.Builder()
63 | .url(this.url)
64 | .addHeader(CONTENT_TYPE_LABEL, CONTENT_TYPE_VALUE_JSON)
65 | .get()
66 | .build();
67 |
68 | try {
69 | this.response = okHttpClient.newCall(request).execute().body().string();
70 | } catch (IOException e) {
71 | e.printStackTrace();
72 | }
73 | }
74 |
75 | private OkHttpClient createClient() {
76 | final OkHttpClient okHttpClient = new OkHttpClient();
77 | okHttpClient.setReadTimeout(10000, TimeUnit.MILLISECONDS);
78 | okHttpClient.setConnectTimeout(15000, TimeUnit.MILLISECONDS);
79 |
80 | return okHttpClient;
81 | }
82 |
83 | @Override public String call() throws Exception {
84 | return requestSyncCall();
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApi.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.net;
17 |
18 | import com.fernandocejas.android10.sample.data.entity.UserEntity;
19 | import java.util.List;
20 | import rx.Observable;
21 |
22 | /**
23 | * RestApi for retrieving data from the network.
24 | */
25 | public interface RestApi {
26 | String API_BASE_URL = "http://www.android10.org/myapi/";
27 |
28 | /** Api url for getting all users */
29 | String API_URL_GET_USER_LIST = API_BASE_URL + "users.json";
30 | /** Api url for getting a user profile: Remember to concatenate id + 'json' */
31 | String API_URL_GET_USER_DETAILS = API_BASE_URL + "user_";
32 |
33 | /**
34 | * Retrieves an {@link rx.Observable} which will emit a List of {@link UserEntity}.
35 | */
36 | Observable> userEntityList();
37 |
38 | /**
39 | * Retrieves an {@link rx.Observable} which will emit a {@link UserEntity}.
40 | *
41 | * @param userId The user id used to get user data.
42 | */
43 | Observable userEntityById(final int userId);
44 | }
45 |
--------------------------------------------------------------------------------
/data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApiImpl.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.net;
17 |
18 | import android.content.Context;
19 | import android.net.ConnectivityManager;
20 | import android.net.NetworkInfo;
21 | import com.fernandocejas.android10.sample.data.entity.UserEntity;
22 | import com.fernandocejas.android10.sample.data.entity.mapper.UserEntityJsonMapper;
23 | import com.fernandocejas.android10.sample.data.exception.NetworkConnectionException;
24 | import java.net.MalformedURLException;
25 | import java.util.List;
26 | import rx.Observable;
27 | import rx.Subscriber;
28 |
29 | /**
30 | * {@link RestApi} implementation for retrieving data from the network.
31 | */
32 | public class RestApiImpl implements RestApi {
33 |
34 | private final Context context;
35 | private final UserEntityJsonMapper userEntityJsonMapper;
36 |
37 | /**
38 | * Constructor of the class
39 | *
40 | * @param context {@link android.content.Context}.
41 | * @param userEntityJsonMapper {@link UserEntityJsonMapper}.
42 | */
43 | public RestApiImpl(Context context, UserEntityJsonMapper userEntityJsonMapper) {
44 | if (context == null || userEntityJsonMapper == null) {
45 | throw new IllegalArgumentException("The constructor parameters cannot be null!!!");
46 | }
47 | this.context = context.getApplicationContext();
48 | this.userEntityJsonMapper = userEntityJsonMapper;
49 | }
50 |
51 | @Override public Observable> userEntityList() {
52 | return Observable.create(new Observable.OnSubscribe>() {
53 | @Override public void call(Subscriber super List> subscriber) {
54 |
55 | if (isThereInternetConnection()) {
56 | try {
57 | String responseUserEntities = getUserEntitiesFromApi();
58 | if (responseUserEntities != null) {
59 | subscriber.onNext(userEntityJsonMapper.transformUserEntityCollection(
60 | responseUserEntities));
61 | subscriber.onCompleted();
62 | } else {
63 | subscriber.onError(new NetworkConnectionException());
64 | }
65 | } catch (Exception e) {
66 | subscriber.onError(new NetworkConnectionException(e.getCause()));
67 | }
68 | } else {
69 | subscriber.onError(new NetworkConnectionException());
70 | }
71 | }
72 | });
73 | }
74 |
75 | @Override public Observable userEntityById(final int userId) {
76 | return Observable.create(new Observable.OnSubscribe() {
77 | @Override public void call(Subscriber super UserEntity> subscriber) {
78 |
79 | if (isThereInternetConnection()) {
80 | try {
81 | String responseUserDetails = getUserDetailsFromApi(userId);
82 | if (responseUserDetails != null) {
83 | subscriber.onNext(userEntityJsonMapper.transformUserEntity(responseUserDetails));
84 | subscriber.onCompleted();
85 | } else {
86 | subscriber.onError(new NetworkConnectionException());
87 | }
88 | } catch (Exception e) {
89 | subscriber.onError(new NetworkConnectionException(e.getCause()));
90 | }
91 | } else {
92 | subscriber.onError(new NetworkConnectionException());
93 | }
94 | }
95 | });
96 | }
97 |
98 | private String getUserEntitiesFromApi() throws MalformedURLException {
99 | return ApiConnection.createGET(RestApi.API_URL_GET_USER_LIST).requestSyncCall();
100 | }
101 |
102 | private String getUserDetailsFromApi(int userId) throws MalformedURLException {
103 | String apiUrl = RestApi.API_URL_GET_USER_DETAILS + userId + ".json";
104 | return ApiConnection.createGET(apiUrl).requestSyncCall();
105 | }
106 |
107 | /**
108 | * Checks if the device has any active internet connection.
109 | *
110 | * @return true device with internet connection, otherwise false.
111 | */
112 | private boolean isThereInternetConnection() {
113 | boolean isConnected;
114 |
115 | ConnectivityManager connectivityManager =
116 | (ConnectivityManager) this.context.getSystemService(Context.CONNECTIVITY_SERVICE);
117 | NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
118 | isConnected = (networkInfo != null && networkInfo.isConnectedOrConnecting());
119 |
120 | return isConnected;
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/data/src/test/java/com/fernandocejas/android10/sample/data/ApplicationStub.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 android10.org Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data;
17 |
18 | import android.app.Application;
19 |
20 | public class ApplicationStub extends Application {}
21 |
--------------------------------------------------------------------------------
/data/src/test/java/com/fernandocejas/android10/sample/data/ApplicationTestCase.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data;
17 |
18 | import org.junit.runner.RunWith;
19 | import org.robolectric.RobolectricGradleTestRunner;
20 | import org.robolectric.annotation.Config;
21 |
22 | /**
23 | * Base class for Robolectric data layer tests.
24 | * Inherit from this class to create a test.
25 | */
26 | @RunWith(RobolectricGradleTestRunner.class)
27 | @Config(constants = BuildConfig.class, application = ApplicationStub.class, sdk = 21)
28 | public abstract class ApplicationTestCase {}
29 |
--------------------------------------------------------------------------------
/data/src/test/java/com/fernandocejas/android10/sample/data/cache/FileManagerTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.cache;
17 |
18 | import com.fernandocejas.android10.sample.data.ApplicationTestCase;
19 | import java.io.File;
20 | import org.junit.After;
21 | import org.junit.Before;
22 | import org.junit.Test;
23 | import org.robolectric.RuntimeEnvironment;
24 |
25 | import static org.hamcrest.CoreMatchers.equalTo;
26 | import static org.hamcrest.CoreMatchers.is;
27 | import static org.hamcrest.MatcherAssert.assertThat;
28 |
29 | public class FileManagerTest extends ApplicationTestCase {
30 |
31 | private FileManager fileManager;
32 | private File cacheDir;
33 |
34 | @Before
35 | public void setUp() {
36 | fileManager = new FileManager();
37 | cacheDir = RuntimeEnvironment.application.getCacheDir();
38 | }
39 |
40 | @After
41 | public void tearDown() {
42 | if (cacheDir != null) {
43 | fileManager.clearDirectory(cacheDir);
44 | }
45 | }
46 |
47 | @Test
48 | public void testWriteToFile() {
49 | File fileToWrite = createDummyFile();
50 | String fileContent = "content";
51 |
52 | fileManager.writeToFile(fileToWrite, fileContent);
53 |
54 | assertThat(fileToWrite.exists(), is(true));
55 | }
56 |
57 | @Test
58 | public void testFileContent() {
59 | File fileToWrite = createDummyFile();
60 | String fileContent = "content\n";
61 |
62 | fileManager.writeToFile(fileToWrite, fileContent);
63 | String expectedContent = fileManager.readFileContent(fileToWrite);
64 |
65 | assertThat(expectedContent, is(equalTo(fileContent)));
66 | }
67 |
68 | private File createDummyFile() {
69 | String dummyFilePath = cacheDir.getPath() + File.separator + "dumyFile";
70 | File dummyFile = new File(dummyFilePath);
71 |
72 | return dummyFile;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/data/src/test/java/com/fernandocejas/android10/sample/data/cache/serializer/JsonSerializerTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.cache.serializer;
17 |
18 | import com.fernandocejas.android10.sample.data.ApplicationTestCase;
19 | import com.fernandocejas.android10.sample.data.entity.UserEntity;
20 | import org.junit.Before;
21 | import org.junit.Test;
22 |
23 | import static org.hamcrest.CoreMatchers.equalTo;
24 | import static org.hamcrest.CoreMatchers.is;
25 | import static org.junit.Assert.assertThat;
26 |
27 | public class JsonSerializerTest extends ApplicationTestCase {
28 |
29 | private static final String JSON_RESPONSE = "{\n"
30 | + " \"id\": 1,\n"
31 | + " \"cover_url\": \"http://www.android10.org/myapi/cover_1.jpg\",\n"
32 | + " \"full_name\": \"Simon Hill\",\n"
33 | + " \"description\": \"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\",\n"
34 | + " \"followers\": 7484,\n"
35 | + " \"email\": \"jcooper@babbleset.edu\"\n"
36 | + "}";
37 |
38 | private JsonSerializer jsonSerializer;
39 |
40 | @Before
41 | public void setUp() {
42 | jsonSerializer = new JsonSerializer();
43 | }
44 |
45 | @Test
46 | public void testSerializeHappyCase() {
47 | UserEntity userEntityOne = jsonSerializer.deserialize(JSON_RESPONSE);
48 | String jsonString = jsonSerializer.serialize(userEntityOne);
49 | UserEntity userEntityTwo = jsonSerializer.deserialize(jsonString);
50 |
51 | assertThat(userEntityOne.getUserId(), is(userEntityTwo.getUserId()));
52 | assertThat(userEntityOne.getFullname(), is(equalTo(userEntityTwo.getFullname())));
53 | assertThat(userEntityOne.getFollowers(), is(userEntityTwo.getFollowers()));
54 | }
55 |
56 | @Test
57 | public void testDesearializeHappyCase() {
58 | UserEntity userEntity = jsonSerializer.deserialize(JSON_RESPONSE);
59 |
60 | assertThat(userEntity.getUserId(), is(1));
61 | assertThat(userEntity.getFullname(), is("Simon Hill"));
62 | assertThat(userEntity.getFollowers(), is(7484));
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/data/src/test/java/com/fernandocejas/android10/sample/data/datasource/CloudUserDataStoreTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.datasource;
17 |
18 | import com.fernandocejas.android10.sample.data.ApplicationTestCase;
19 | import com.fernandocejas.android10.sample.data.cache.UserCache;
20 | import com.fernandocejas.android10.sample.data.entity.UserEntity;
21 | import com.fernandocejas.android10.sample.data.net.RestApi;
22 | import org.junit.Before;
23 | import org.junit.Test;
24 | import org.mockito.Mock;
25 | import org.mockito.MockitoAnnotations;
26 | import rx.Observable;
27 |
28 | import static org.mockito.BDDMockito.given;
29 | import static org.mockito.Mockito.verify;
30 |
31 | public class CloudUserDataStoreTest extends ApplicationTestCase {
32 |
33 | private static final int FAKE_USER_ID = 765;
34 |
35 | private CloudUserDataStore cloudUserDataStore;
36 |
37 | @Mock private RestApi mockRestApi;
38 | @Mock private UserCache mockUserCache;
39 |
40 | @Before
41 | public void setUp() {
42 | MockitoAnnotations.initMocks(this);
43 | cloudUserDataStore = new CloudUserDataStore(mockRestApi, mockUserCache);
44 | }
45 |
46 | @Test
47 | public void testGetUserEntityListFromApi() {
48 | cloudUserDataStore.userEntityList();
49 | verify(mockRestApi).userEntityList();
50 | }
51 |
52 | @Test
53 | public void testGetUserEntityDetailsFromApi() {
54 | UserEntity fakeUserEntity = new UserEntity();
55 | Observable fakeObservable = Observable.just(fakeUserEntity);
56 | given(mockRestApi.userEntityById(FAKE_USER_ID)).willReturn(fakeObservable);
57 |
58 | cloudUserDataStore.userEntityDetails(FAKE_USER_ID);
59 |
60 | verify(mockRestApi).userEntityById(FAKE_USER_ID);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/data/src/test/java/com/fernandocejas/android10/sample/data/datasource/DiskUserDataStoreTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.datasource;
17 |
18 | import com.fernandocejas.android10.sample.data.ApplicationTestCase;
19 | import com.fernandocejas.android10.sample.data.cache.UserCache;
20 | import org.junit.Before;
21 | import org.junit.Rule;
22 | import org.junit.Test;
23 | import org.junit.rules.ExpectedException;
24 | import org.mockito.Mock;
25 | import org.mockito.MockitoAnnotations;
26 |
27 | import static org.mockito.Mockito.verify;
28 |
29 | public class DiskUserDataStoreTest extends ApplicationTestCase {
30 |
31 | private static final int FAKE_USER_ID = 11;
32 |
33 | private DiskUserDataStore diskUserDataStore;
34 |
35 | @Mock private UserCache mockUserCache;
36 |
37 | @Rule public ExpectedException expectedException = ExpectedException.none();
38 |
39 | @Before
40 | public void setUp() {
41 | MockitoAnnotations.initMocks(this);
42 | diskUserDataStore = new DiskUserDataStore(mockUserCache);
43 | }
44 |
45 | @Test
46 | public void testGetUserEntityListUnsupported() {
47 | expectedException.expect(UnsupportedOperationException.class);
48 | diskUserDataStore.userEntityList();
49 | }
50 |
51 | @Test
52 | public void testGetUserEntityDetailesFromCache() {
53 | diskUserDataStore.userEntityDetails(FAKE_USER_ID);
54 | verify(mockUserCache).get(FAKE_USER_ID);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/data/src/test/java/com/fernandocejas/android10/sample/data/datasource/UserDataStoreFactoryTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.datasource;
17 |
18 | import com.fernandocejas.android10.sample.data.ApplicationTestCase;
19 | import com.fernandocejas.android10.sample.data.cache.UserCache;
20 | import org.junit.Before;
21 | import org.junit.Test;
22 | import org.mockito.Mock;
23 | import org.mockito.MockitoAnnotations;
24 | import org.robolectric.RuntimeEnvironment;
25 |
26 | import static org.hamcrest.CoreMatchers.instanceOf;
27 | import static org.hamcrest.CoreMatchers.is;
28 | import static org.hamcrest.CoreMatchers.notNullValue;
29 | import static org.hamcrest.MatcherAssert.assertThat;
30 | import static org.mockito.BDDMockito.given;
31 | import static org.mockito.Mockito.verify;
32 |
33 | public class UserDataStoreFactoryTest extends ApplicationTestCase {
34 |
35 | private static final int FAKE_USER_ID = 11;
36 |
37 | private UserDataStoreFactory userDataStoreFactory;
38 |
39 | @Mock
40 | private UserCache mockUserCache;
41 |
42 | @Before
43 | public void setUp() {
44 | MockitoAnnotations.initMocks(this);
45 | userDataStoreFactory =
46 | new UserDataStoreFactory(RuntimeEnvironment.application, mockUserCache);
47 | }
48 |
49 | @Test
50 | public void testCreateDiskDataStore() {
51 | given(mockUserCache.isCached(FAKE_USER_ID)).willReturn(true);
52 | given(mockUserCache.isExpired()).willReturn(false);
53 |
54 | UserDataStore userDataStore = userDataStoreFactory.create(FAKE_USER_ID);
55 |
56 | assertThat(userDataStore, is(notNullValue()));
57 | assertThat(userDataStore, is(instanceOf(DiskUserDataStore.class)));
58 |
59 | verify(mockUserCache).isCached(FAKE_USER_ID);
60 | verify(mockUserCache).isExpired();
61 | }
62 |
63 | @Test
64 | public void testCreateCloudDataStore() {
65 | given(mockUserCache.isExpired()).willReturn(true);
66 | given(mockUserCache.isCached(FAKE_USER_ID)).willReturn(false);
67 |
68 | UserDataStore userDataStore = userDataStoreFactory.create(FAKE_USER_ID);
69 |
70 | assertThat(userDataStore, is(notNullValue()));
71 | assertThat(userDataStore, is(instanceOf(CloudUserDataStore.class)));
72 |
73 | verify(mockUserCache).isExpired();
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/data/src/test/java/com/fernandocejas/android10/sample/data/dto/UserTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.dto;
17 |
18 | import org.junit.Before;
19 | import org.junit.Test;
20 |
21 | import static org.hamcrest.core.Is.is;
22 | import static org.junit.Assert.assertThat;
23 |
24 | public class UserTest {
25 |
26 | private static final int FAKE_USER_ID = 8;
27 |
28 | private User user;
29 |
30 | @Before
31 | public void setUp() {
32 | user = new User(FAKE_USER_ID);
33 | }
34 |
35 | @Test
36 | public void testUserConstructorHappyCase() {
37 | int userId = user.getUserId();
38 |
39 | assertThat(userId, is(FAKE_USER_ID));
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/data/src/test/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityDataMapperTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.entity.mapper;
17 |
18 | import com.fernandocejas.android10.sample.data.ApplicationTestCase;
19 | import com.fernandocejas.android10.sample.data.dto.User;
20 | import com.fernandocejas.android10.sample.data.entity.UserEntity;
21 |
22 | import org.junit.Before;
23 | import org.junit.Test;
24 |
25 | import java.util.ArrayList;
26 | import java.util.Collection;
27 | import java.util.List;
28 |
29 | import static org.hamcrest.CoreMatchers.instanceOf;
30 | import static org.hamcrest.CoreMatchers.is;
31 | import static org.hamcrest.MatcherAssert.assertThat;
32 | import static org.mockito.Mockito.mock;
33 |
34 | public class UserEntityDataMapperTest extends ApplicationTestCase {
35 |
36 | private static final int FAKE_USER_ID = 123;
37 | private static final String FAKE_FULLNAME = "Tony Stark";
38 |
39 | private UserEntityDataMapper userEntityDataMapper;
40 |
41 | @Before
42 | public void setUp() throws Exception {
43 | userEntityDataMapper = new UserEntityDataMapper();
44 | }
45 |
46 | @Test
47 | public void testTransformUserEntity() {
48 | UserEntity userEntity = createFakeUserEntity();
49 | User user = userEntityDataMapper.transform(userEntity);
50 |
51 | assertThat(user, is(instanceOf(User.class)));
52 | assertThat(user.getUserId(), is(FAKE_USER_ID));
53 | assertThat(user.getFullName(), is(FAKE_FULLNAME));
54 | }
55 |
56 | @Test
57 | public void testTransformUserEntityCollection() {
58 | UserEntity mockUserEntityOne = mock(UserEntity.class);
59 | UserEntity mockUserEntityTwo = mock(UserEntity.class);
60 |
61 | List userEntityList = new ArrayList(5);
62 | userEntityList.add(mockUserEntityOne);
63 | userEntityList.add(mockUserEntityTwo);
64 |
65 | Collection userCollection = userEntityDataMapper.transform(userEntityList);
66 |
67 | assertThat(userCollection.toArray()[0], is(instanceOf(User.class)));
68 | assertThat(userCollection.toArray()[1], is(instanceOf(User.class)));
69 | assertThat(userCollection.size(), is(2));
70 | }
71 |
72 | private UserEntity createFakeUserEntity() {
73 | UserEntity userEntity = new UserEntity();
74 | userEntity.setUserId(FAKE_USER_ID);
75 | userEntity.setFullname(FAKE_FULLNAME);
76 |
77 | return userEntity;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/data/src/test/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityJsonMapperTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.entity.mapper;
17 |
18 | import com.fernandocejas.android10.sample.data.ApplicationTestCase;
19 | import com.fernandocejas.android10.sample.data.entity.UserEntity;
20 | import com.google.gson.JsonSyntaxException;
21 | import java.util.Collection;
22 | import org.junit.Before;
23 | import org.junit.Rule;
24 | import org.junit.Test;
25 | import org.junit.rules.ExpectedException;
26 |
27 | import static org.hamcrest.CoreMatchers.equalTo;
28 | import static org.hamcrest.CoreMatchers.is;
29 | import static org.hamcrest.MatcherAssert.assertThat;
30 |
31 | public class UserEntityJsonMapperTest extends ApplicationTestCase {
32 |
33 | private static final String JSON_RESPONSE_USER_DETAILS = "{\n"
34 | + " \"id\": 1,\n"
35 | + " \"cover_url\": \"http://www.android10.org/myapi/cover_1.jpg\",\n"
36 | + " \"full_name\": \"Simon Hill\",\n"
37 | + " \"description\": \"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\",\n"
38 | + " \"followers\": 7484,\n"
39 | + " \"email\": \"jcooper@babbleset.edu\"\n"
40 | + "}";
41 |
42 | private static final String JSON_RESPONSE_USER_COLLECTION = "[{\n"
43 | + " \"id\": 1,\n"
44 | + " \"full_name\": \"Simon Hill\",\n"
45 | + " \"followers\": 7484\n"
46 | + "}, {\n"
47 | + " \"id\": 12,\n"
48 | + " \"full_name\": \"Pedro Garcia\",\n"
49 | + " \"followers\": 1381\n"
50 | + "}]";
51 |
52 | private UserEntityJsonMapper userEntityJsonMapper;
53 |
54 | @Rule
55 | public ExpectedException expectedException = ExpectedException.none();
56 |
57 | @Before
58 | public void setUp() {
59 | userEntityJsonMapper = new UserEntityJsonMapper();
60 | }
61 |
62 | @Test
63 | public void testTransformUserEntityHappyCase() {
64 | UserEntity userEntity = userEntityJsonMapper.transformUserEntity(JSON_RESPONSE_USER_DETAILS);
65 |
66 | assertThat(userEntity.getUserId(), is(1));
67 | assertThat(userEntity.getFullname(), is(equalTo("Simon Hill")));
68 | assertThat(userEntity.getEmail(), is(equalTo("jcooper@babbleset.edu")));
69 | }
70 |
71 | @Test
72 | public void testTransformUserEntityCollectionHappyCase() {
73 | Collection userEntityCollection =
74 | userEntityJsonMapper.transformUserEntityCollection(
75 | JSON_RESPONSE_USER_COLLECTION);
76 |
77 | assertThat(((UserEntity) userEntityCollection.toArray()[0]).getUserId(), is(1));
78 | assertThat(((UserEntity) userEntityCollection.toArray()[1]).getUserId(), is(12));
79 | assertThat(userEntityCollection.size(), is(2));
80 | }
81 |
82 | @Test
83 | public void testTransformUserEntityNotValidResponse() {
84 | expectedException.expect(JsonSyntaxException.class);
85 | userEntityJsonMapper.transformUserEntity("ironman");
86 | }
87 |
88 | @Test
89 | public void testTransformUserEntityCollectionNotValidResponse() {
90 | expectedException.expect(JsonSyntaxException.class);
91 | userEntityJsonMapper.transformUserEntityCollection("Tony Stark");
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/data/src/test/java/com/fernandocejas/android10/sample/data/exception/DefaultErrorBundleTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.exception;
17 |
18 | import org.junit.Before;
19 | import org.junit.Test;
20 | import org.mockito.Mock;
21 | import org.mockito.MockitoAnnotations;
22 |
23 | import static org.mockito.Mockito.verify;
24 |
25 | public class DefaultErrorBundleTest {
26 | private DefaultErrorBundle defaultErrorBundle;
27 |
28 | @Mock
29 | private Exception mockException;
30 |
31 | @Before
32 | public void setUp() {
33 | MockitoAnnotations.initMocks(this);
34 | defaultErrorBundle = new DefaultErrorBundle(mockException);
35 | }
36 |
37 | @Test
38 | public void testGetErrorMessageInteraction() {
39 | defaultErrorBundle.getErrorMessage();
40 |
41 | verify(mockException).getMessage();
42 | }
43 | }
--------------------------------------------------------------------------------
/data/src/test/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundleTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.data.exception;
17 |
18 | import com.fernandocejas.android10.sample.data.ApplicationTestCase;
19 | import org.junit.Before;
20 | import org.junit.Test;
21 | import org.mockito.Mock;
22 | import org.mockito.MockitoAnnotations;
23 |
24 | import static org.mockito.Mockito.verify;
25 |
26 | public class RepositoryErrorBundleTest extends ApplicationTestCase {
27 |
28 | private RepositoryErrorBundle repositoryErrorBundle;
29 |
30 | @Mock
31 | private Exception mockException;
32 |
33 | @Before
34 | public void setUp() {
35 | MockitoAnnotations.initMocks(this);
36 | repositoryErrorBundle = new RepositoryErrorBundle(mockException);
37 | }
38 |
39 | @Test
40 | public void testGetErrorMessageInteraction() {
41 | repositoryErrorBundle.getErrorMessage();
42 |
43 | verify(mockException).getMessage();
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/domain/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | def globalConfiguration = rootProject.extensions.getByName("ext")
5 |
6 | compileSdkVersion globalConfiguration.getAt("androidCompileSdkVersion")
7 | buildToolsVersion globalConfiguration.getAt("androidBuildToolsVersion")
8 |
9 | defaultConfig {
10 | minSdkVersion globalConfiguration.getAt("androidMinSdkVersion")
11 | targetSdkVersion globalConfiguration.getAt("androidTargetSdkVersion")
12 | versionCode globalConfiguration.getAt("androidVersionCode")
13 | }
14 |
15 | compileOptions {
16 | sourceCompatibility JavaVersion.VERSION_1_7
17 | targetCompatibility JavaVersion.VERSION_1_7
18 | }
19 |
20 | packagingOptions {
21 | exclude 'LICENSE.txt'
22 | exclude 'META-INF/DEPENDENCIES'
23 | exclude 'META-INF/ASL2.0'
24 | exclude 'META-INF/NOTICE'
25 | exclude 'META-INF/LICENSE'
26 | }
27 |
28 | lintOptions {
29 | quiet true
30 | abortOnError false
31 | ignoreWarnings true
32 | disable 'InvalidPackage' // Some libraries have issues with this
33 | disable 'OldTargetApi' // Due to Robolectric that modifies the manifest when running tests
34 | }
35 | }
36 | dependencies {
37 | def testDependencies = rootProject.ext.domainTestDependencies
38 |
39 | compile project(':data')
40 |
41 | testCompile testDependencies.junit
42 | testCompile testDependencies.mockito
43 | }
--------------------------------------------------------------------------------
/domain/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/DefaultSubscriber.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.domain.interactor;
17 |
18 | /**
19 | * Default subscriber base class to be used whenever you want default error handling.
20 | */
21 | public class DefaultSubscriber extends rx.Subscriber {
22 | @Override public void onCompleted() {
23 | // no-op by default.
24 | }
25 |
26 | @Override public void onError(Throwable e) {
27 | // no-op by default.
28 | }
29 |
30 | @Override public void onNext(T t) {
31 | // no-op by default.
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetails.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.domain.interactor;
17 |
18 |
19 | import android.content.Context;
20 |
21 | import com.fernandocejas.android10.sample.domain.interactor.repository.UserDataRepository;
22 | import com.fernandocejas.android10.sample.domain.interactor.repository.UserRepository;
23 |
24 | import rx.Observable;
25 |
26 | /**
27 | * This class is an implementation of {@link UseCase} that represents a use case for
28 | * retrieving data related to an specific {@link com.fernandocejas.android10.sample.data.dto.User}.
29 | */
30 | public class GetUserDetails extends UseCase {
31 |
32 | int userId;
33 | UserRepository userRepository;
34 |
35 | public GetUserDetails(Context appContext) {
36 | this(0, new UserDataRepository(appContext));
37 | }
38 |
39 | public GetUserDetails(int userId, Context appContext) {
40 | this(userId, new UserDataRepository(appContext));
41 | }
42 |
43 | public GetUserDetails(int userId, UserRepository userRepository) {
44 | this.userId = userId;
45 | this.userRepository = userRepository;
46 | }
47 |
48 | public void setUserRepository(UserRepository userRepository) {
49 | this.userRepository = userRepository;
50 | }
51 |
52 | public int getUserId() {
53 | return userId;
54 | }
55 |
56 | public void setUserId(int userId) {
57 | this.userId = userId;
58 | }
59 |
60 | @Override
61 | protected Observable buildUseCaseObservable() {
62 | return this.userRepository.user(this.userId);
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.domain.interactor;
17 |
18 |
19 | import android.content.Context;
20 |
21 | import com.fernandocejas.android10.sample.data.dto.User;
22 | import com.fernandocejas.android10.sample.domain.interactor.repository.UserDataRepository;
23 | import com.fernandocejas.android10.sample.domain.interactor.repository.UserRepository;
24 |
25 | import rx.Observable;
26 |
27 | /**
28 | * This class is an implementation of {@link UseCase} that represents a use case for
29 | * retrieving a collection of all {@link User}.
30 | */
31 | public class GetUserList extends UseCase {
32 |
33 | UserRepository userRepository;
34 |
35 | public GetUserList(Context appContext) {
36 | this(new UserDataRepository(appContext));
37 | }
38 |
39 | public GetUserList(UserRepository userRepository) {
40 | this.userRepository = userRepository;
41 | }
42 |
43 | public void setUserRepository(UserRepository userRepository) {
44 | this.userRepository = userRepository;
45 | }
46 |
47 | @Override
48 | public Observable buildUseCaseObservable() {
49 | return this.userRepository.users();
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/UseCase.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.domain.interactor;
17 |
18 | import com.fernandocejas.android10.sample.data.executor.JobExecutor;
19 | import com.fernandocejas.android10.sample.data.executor.PostExecutionThread;
20 | import com.fernandocejas.android10.sample.data.executor.ThreadExecutor;
21 | import com.fernandocejas.android10.sample.data.executor.UIThread;
22 |
23 | import rx.Observable;
24 | import rx.Subscriber;
25 | import rx.Subscription;
26 | import rx.schedulers.Schedulers;
27 | import rx.subscriptions.Subscriptions;
28 |
29 | /**
30 | * Abstract class for a Use Case (Interactor in terms of Clean Architecture).
31 | * This interface represents a execution unit for different use cases (this means any use case
32 | * in the application should implement this contract).
33 | *
34 | * By convention each UseCase implementation will return the result using a {@link rx.Subscriber}
35 | * that will execute its job in a background thread and will post the result in the UI thread.
36 | */
37 | public abstract class UseCase {
38 |
39 | ThreadExecutor threadExecutor = new JobExecutor();
40 | PostExecutionThread postExecutionThread = new UIThread();
41 |
42 | private Subscription subscription = Subscriptions.empty();
43 |
44 |
45 | public void setThreadExecutor(ThreadExecutor threadExecutor) {
46 | this.threadExecutor = threadExecutor;
47 | }
48 |
49 | public void setPostExecutionThread(PostExecutionThread postExecutionThread) {
50 | this.postExecutionThread = postExecutionThread;
51 | }
52 |
53 | /**
54 | * Builds an {@link rx.Observable} which will be used when executing the current {@link UseCase}.
55 | */
56 | protected abstract Observable buildUseCaseObservable();
57 |
58 | /**
59 | * Executes the current use case.
60 | *
61 | * @param UseCaseSubscriber The guy who will be listen to the observable build with {@link #buildUseCaseObservable()}.
62 | */
63 | @SuppressWarnings("unchecked")
64 | public void execute(Subscriber UseCaseSubscriber) {
65 | this.subscription = this.buildUseCaseObservable()
66 | .subscribeOn(Schedulers.from(threadExecutor))
67 | .observeOn(postExecutionThread.getScheduler())
68 | .subscribe(UseCaseSubscriber);
69 | }
70 |
71 | /**
72 | * Unsubscribes from current {@link rx.Subscription}.
73 | */
74 | public void unsubscribe() {
75 | if (!subscription.isUnsubscribed()) {
76 | subscription.unsubscribe();
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/repository/UserDataRepository.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.domain.interactor.repository;
17 |
18 | import android.content.Context;
19 |
20 | import com.fernandocejas.android10.sample.data.dto.User;
21 | import com.fernandocejas.android10.sample.data.entity.UserEntity;
22 | import com.fernandocejas.android10.sample.data.entity.mapper.UserEntityDataMapper;
23 | import com.fernandocejas.android10.sample.data.datasource.UserDataStore;
24 | import com.fernandocejas.android10.sample.data.datasource.UserDataStoreFactory;
25 |
26 | import java.util.List;
27 |
28 | import rx.Observable;
29 | import rx.functions.Func1;
30 |
31 | /**
32 | * {@link UserRepository} for retrieving user data.
33 | */
34 | public class UserDataRepository implements UserRepository {
35 |
36 | private UserDataStoreFactory userDataStoreFactory;
37 | private UserEntityDataMapper userEntityDataMapper;
38 |
39 | public UserDataRepository(Context appContext) {
40 | this(new UserDataStoreFactory(appContext), new UserEntityDataMapper());
41 | }
42 |
43 | /**
44 | * Constructs a {@link UserRepository}.
45 | *
46 | * @param dataStoreFactory A factory to construct different data source implementations.
47 | * @param userEntityDataMapper {@link UserEntityDataMapper}.
48 | */
49 | public UserDataRepository(UserDataStoreFactory dataStoreFactory,
50 | UserEntityDataMapper userEntityDataMapper) {
51 | this.userDataStoreFactory = dataStoreFactory;
52 | this.userEntityDataMapper = userEntityDataMapper;
53 | }
54 |
55 | public void setUserDataStoreFactory(UserDataStoreFactory userDataStoreFactory) {
56 | this.userDataStoreFactory = userDataStoreFactory;
57 | }
58 |
59 | public void setUserEntityDataMapper(UserEntityDataMapper userEntityDataMapper) {
60 | this.userEntityDataMapper = userEntityDataMapper;
61 | }
62 |
63 | @SuppressWarnings("Convert2MethodRef")
64 | @Override
65 | public Observable> users() {
66 | //we always get all users from the cloud
67 | final UserDataStore userDataStore = this.userDataStoreFactory.createCloudDataStore();
68 |
69 | return userDataStore.userEntityList()
70 | .map(new Func1, List>() {
71 | @Override
72 | public List call(List userEntities) {
73 | return userEntityDataMapper.transform(userEntities);
74 | }
75 | });
76 | }
77 |
78 | @SuppressWarnings("Convert2MethodRef")
79 | @Override
80 | public Observable user(int userId) {
81 | final UserDataStore userDataStore = this.userDataStoreFactory.create(userId);
82 | return userDataStore.userEntityDetails(userId)
83 | .map(new Func1() {
84 | @Override
85 | public User call(UserEntity userEntity) {
86 | return userEntityDataMapper.transform(userEntity);
87 | }
88 | });
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/repository/UserRepository.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.domain.interactor.repository;
17 |
18 |
19 | import com.fernandocejas.android10.sample.data.dto.User;
20 |
21 | import java.util.List;
22 |
23 | import rx.Observable;
24 |
25 | /**
26 | * Interface that represents a Repository for getting {@link User} related data.
27 | */
28 | public interface UserRepository {
29 | /**
30 | * Get an {@link Observable} which will emit a List of {@link User}.
31 | */
32 | Observable> users();
33 |
34 | /**
35 | * Get an {@link Observable} which will emit a {@link User}.
36 | *
37 | * @param userId The user id used to retrieve user data.
38 | */
39 | Observable user(final int userId);
40 | }
41 |
--------------------------------------------------------------------------------
/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetailsTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.domain.interactor;
17 |
18 | import com.fernandocejas.android10.sample.data.executor.PostExecutionThread;
19 | import com.fernandocejas.android10.sample.data.executor.ThreadExecutor;
20 | import com.fernandocejas.android10.sample.domain.interactor.repository.UserRepository;
21 |
22 | import org.junit.Before;
23 | import org.junit.Test;
24 | import org.mockito.Mock;
25 | import org.mockito.MockitoAnnotations;
26 |
27 | import static org.mockito.Mockito.verify;
28 | import static org.mockito.Mockito.verifyNoMoreInteractions;
29 | import static org.mockito.Mockito.verifyZeroInteractions;
30 |
31 | public class GetUserDetailsTest {
32 |
33 | private static final int FAKE_USER_ID = 123;
34 |
35 | private GetUserDetails getUserDetails;
36 |
37 | @Mock
38 | private UserRepository mockUserRepository;
39 | @Mock
40 | private ThreadExecutor mockThreadExecutor;
41 | @Mock
42 | private PostExecutionThread mockPostExecutionThread;
43 |
44 | @Before
45 | public void setUp() {
46 | MockitoAnnotations.initMocks(this);
47 | getUserDetails = new GetUserDetails(FAKE_USER_ID, mockUserRepository);
48 | getUserDetails.setThreadExecutor(mockThreadExecutor);
49 | getUserDetails.setPostExecutionThread(mockPostExecutionThread);
50 |
51 | }
52 |
53 | @Test
54 | public void testGetUserDetailsUseCaseObservableHappyCase() {
55 | getUserDetails.buildUseCaseObservable();
56 |
57 | verify(mockUserRepository).user(FAKE_USER_ID);
58 | verifyNoMoreInteractions(mockUserRepository);
59 | verifyZeroInteractions(mockPostExecutionThread);
60 | verifyZeroInteractions(mockThreadExecutor);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserListTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.domain.interactor;
17 |
18 | import com.fernandocejas.android10.sample.data.executor.PostExecutionThread;
19 | import com.fernandocejas.android10.sample.data.executor.ThreadExecutor;
20 | import com.fernandocejas.android10.sample.domain.interactor.repository.UserRepository;
21 |
22 | import org.junit.Before;
23 | import org.junit.Test;
24 | import org.mockito.Mock;
25 | import org.mockito.MockitoAnnotations;
26 |
27 | import static org.mockito.Mockito.verify;
28 | import static org.mockito.Mockito.verifyNoMoreInteractions;
29 | import static org.mockito.Mockito.verifyZeroInteractions;
30 |
31 | public class GetUserListTest {
32 |
33 | private GetUserList getUserList;
34 |
35 | @Mock private ThreadExecutor mockThreadExecutor;
36 | @Mock private PostExecutionThread mockPostExecutionThread;
37 | @Mock private UserRepository mockUserRepository;
38 |
39 | @Before
40 | public void setUp() {
41 | MockitoAnnotations.initMocks(this);
42 | getUserList = new GetUserList(mockUserRepository);
43 | }
44 |
45 | @Test
46 | public void testGetUserListUseCaseObservableHappyCase() {
47 | getUserList.buildUseCaseObservable();
48 |
49 | verify(mockUserRepository).users();
50 | verifyNoMoreInteractions(mockUserRepository);
51 | verifyZeroInteractions(mockThreadExecutor);
52 | verifyZeroInteractions(mockPostExecutionThread);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/UseCaseTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.domain.interactor;
17 |
18 | import com.fernandocejas.android10.sample.data.executor.PostExecutionThread;
19 | import com.fernandocejas.android10.sample.data.executor.ThreadExecutor;
20 |
21 | import org.junit.Before;
22 | import org.junit.Test;
23 | import org.mockito.Mock;
24 | import org.mockito.MockitoAnnotations;
25 |
26 | import rx.Observable;
27 | import rx.Subscriber;
28 | import rx.observers.TestSubscriber;
29 | import rx.schedulers.TestScheduler;
30 |
31 | import static org.hamcrest.MatcherAssert.assertThat;
32 | import static org.hamcrest.core.Is.is;
33 | import static org.mockito.BDDMockito.given;
34 |
35 | public class UseCaseTest {
36 |
37 | private UseCaseTestClass useCase;
38 |
39 | @Mock private ThreadExecutor mockThreadExecutor;
40 | @Mock private PostExecutionThread mockPostExecutionThread;
41 |
42 | @Before
43 | public void setUp() {
44 | MockitoAnnotations.initMocks(this);
45 | this.useCase = new UseCaseTestClass();
46 | this.useCase.setThreadExecutor(mockThreadExecutor);
47 | this.useCase.setPostExecutionThread(mockPostExecutionThread);
48 | }
49 |
50 | @Test
51 | @SuppressWarnings("unchecked")
52 | public void testBuildUseCaseObservableReturnCorrectResult() {
53 | TestSubscriber testSubscriber = new TestSubscriber<>();
54 | TestScheduler testScheduler = new TestScheduler();
55 | given(mockPostExecutionThread.getScheduler()).willReturn(testScheduler);
56 |
57 | useCase.execute(testSubscriber);
58 |
59 | assertThat(testSubscriber.getOnNextEvents().size(), is(0));
60 | }
61 |
62 | @Test
63 | public void testSubscriptionWhenExecutingUseCase() {
64 | TestSubscriber testSubscriber = new TestSubscriber<>();
65 |
66 | useCase.execute(testSubscriber);
67 | useCase.unsubscribe();
68 |
69 | assertThat(testSubscriber.isUnsubscribed(), is(true));
70 | }
71 |
72 | private static class UseCaseTestClass extends UseCase {
73 |
74 | @Override protected Observable buildUseCaseObservable() {
75 | return Observable.empty();
76 | }
77 |
78 | @Override public void execute(Subscriber UseCaseSubscriber) {
79 | super.execute(UseCaseSubscriber);
80 | }
81 | }
82 | }
--------------------------------------------------------------------------------
/domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/repository/UserDataRepositoryTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.domain.interactor.repository;
17 |
18 | import com.fernandocejas.android10.sample.data.dto.User;
19 | import com.fernandocejas.android10.sample.data.entity.UserEntity;
20 | import com.fernandocejas.android10.sample.data.entity.mapper.UserEntityDataMapper;
21 | import com.fernandocejas.android10.sample.data.datasource.UserDataStore;
22 | import com.fernandocejas.android10.sample.data.datasource.UserDataStoreFactory;
23 |
24 | import org.junit.Before;
25 | import org.junit.Rule;
26 | import org.junit.Test;
27 | import org.junit.rules.ExpectedException;
28 | import org.mockito.Mock;
29 | import org.mockito.MockitoAnnotations;
30 |
31 | import java.util.ArrayList;
32 | import java.util.List;
33 |
34 | import rx.Observable;
35 |
36 | import static org.mockito.BDDMockito.given;
37 | import static org.mockito.Matchers.anyInt;
38 | import static org.mockito.Mockito.verify;
39 |
40 | public class UserDataRepositoryTest{
41 |
42 | private static final int FAKE_USER_ID = 123;
43 |
44 | private UserDataRepository userDataRepository;
45 |
46 | @Mock private UserDataStoreFactory mockUserDataStoreFactory;
47 | @Mock private UserEntityDataMapper mockUserEntityDataMapper;
48 | @Mock private UserDataStore mockUserDataStore;
49 | @Mock private UserEntity mockUserEntity;
50 | @Mock private User mockUser;
51 |
52 | @Rule
53 | public ExpectedException expectedException = ExpectedException.none();
54 |
55 | @Before
56 | public void setUp() {
57 | MockitoAnnotations.initMocks(this);
58 | userDataRepository = new UserDataRepository(mockUserDataStoreFactory,
59 | mockUserEntityDataMapper);
60 |
61 | given(mockUserDataStoreFactory.create(anyInt())).willReturn(mockUserDataStore);
62 | given(mockUserDataStoreFactory.createCloudDataStore()).willReturn(mockUserDataStore);
63 | }
64 |
65 | @Test
66 | public void testGetUsersHappyCase() {
67 | List usersList = new ArrayList<>();
68 | usersList.add(new UserEntity());
69 | given(mockUserDataStore.userEntityList()).willReturn(Observable.just(usersList));
70 |
71 | userDataRepository.users();
72 |
73 | verify(mockUserDataStoreFactory).createCloudDataStore();
74 | verify(mockUserDataStore).userEntityList();
75 | }
76 |
77 | @Test
78 | public void testGetUserHappyCase() {
79 | UserEntity userEntity = new UserEntity();
80 | given(mockUserDataStore.userEntityDetails(FAKE_USER_ID)).willReturn(Observable.just(userEntity));
81 | userDataRepository.user(FAKE_USER_ID);
82 |
83 | verify(mockUserDataStoreFactory).create(FAKE_USER_ID);
84 | verify(mockUserDataStore).userEntityDetails(FAKE_USER_ID);
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | #Gradle configuration
2 | org.gradle.daemon=true
3 | org.gradle.jvmargs=-Dfile.encoding=UTF-8
4 | org.gradle.parallel=true
5 | org.gradle.configureondemand=true
6 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhengxiaopeng/MVVM_Android-CleanArchitecture/ab6b934fdb93bf47daa37c7c6d541497f6d4d951/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Aug 31 10:46:18 CEST 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.9-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/presentation/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/presentation/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'com.android.databinding'
3 |
4 | android {
5 | def globalConfiguration = rootProject.extensions.getByName("ext")
6 |
7 | compileSdkVersion globalConfiguration.getAt("androidCompileSdkVersion")
8 | buildToolsVersion globalConfiguration.getAt("androidBuildToolsVersion")
9 |
10 | defaultConfig {
11 | minSdkVersion globalConfiguration.getAt("androidMinSdkVersion")
12 | targetSdkVersion globalConfiguration.getAt("androidTargetSdkVersion")
13 |
14 | applicationId globalConfiguration.getAt("androidApplicationId")
15 | versionCode globalConfiguration.getAt("androidVersionCode")
16 | versionName globalConfiguration.getAt("androidVersionName")
17 | testInstrumentationRunner globalConfiguration.getAt("testInstrumentationRunner")
18 | testApplicationId globalConfiguration.getAt("testApplicationId")
19 |
20 | }
21 |
22 | compileOptions {
23 | sourceCompatibility JavaVersion.VERSION_1_7
24 | targetCompatibility JavaVersion.VERSION_1_7
25 | }
26 |
27 | packagingOptions {
28 | exclude 'LICENSE.txt'
29 | exclude 'META-INF/DEPENDENCIES'
30 | exclude 'META-INF/ASL2.0'
31 | exclude 'META-INF/NOTICE'
32 | exclude 'META-INF/LICENSE'
33 | }
34 |
35 | lintOptions {
36 | quiet true
37 | abortOnError false
38 | ignoreWarnings true
39 | disable 'InvalidPackage' //Some libraries have issues with this.
40 | disable 'OldTargetApi' //Lint gives this warning but SDK 20 would be Android L Beta.
41 | disable 'IconDensities' //For testing purpose. This is safe to remove.
42 | disable 'IconMissingDensityFolder' //For testing purpose. This is safe to remove.
43 | }
44 |
45 | signingConfigs {
46 | debug {
47 | storeFile file('../buildsystem/debug.keystore')
48 | storePassword 'android'
49 | keyAlias 'androiddebugkey'
50 | keyPassword 'android'
51 | }
52 | }
53 |
54 | buildTypes {
55 | debug {
56 | signingConfig signingConfigs.debug
57 | }
58 | }
59 | }
60 |
61 | dependencies {
62 | def presentationDependencies = rootProject.ext.presentationDependencies
63 | def presentationTestDependencies = rootProject.ext.presentationTestDependencies
64 |
65 | compile project(':domain')
66 |
67 | compile presentationDependencies.recyclerView
68 | compile presentationDependencies.rxJava
69 | compile presentationDependencies.rxAndroid
70 |
71 | androidTestCompile presentationTestDependencies.mockito
72 | androidTestCompile presentationTestDependencies.dexmaker
73 | androidTestCompile presentationTestDependencies.dexmakerMockito
74 | androidTestCompile presentationTestDependencies.espresso
75 | androidTestCompile presentationTestDependencies.espressoIntent
76 | androidTestCompile presentationTestDependencies.espressoContrib
77 |
78 | // androidTestCompile presentationTestDependencies.testingSupportLib
79 | androidTestCompile presentationTestDependencies.runner
80 | androidTestCompile presentationTestDependencies.rules
81 | androidTestCompile presentationTestDependencies.androidAnnotations
82 | }
83 |
--------------------------------------------------------------------------------
/presentation/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/fcejas/Software/SDKs/android-sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/exception/ErrorMessageFactoryTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.test.exception;
17 |
18 | import android.test.AndroidTestCase;
19 | import com.fernandocejas.android10.sample.data.exception.NetworkConnectionException;
20 | import com.fernandocejas.android10.sample.data.exception.UserNotFoundException;
21 | import com.fernandocejas.android10.sample.presentation.R;
22 | import com.fernandocejas.android10.sample.presentation.exception.ErrorMessageFactory;
23 |
24 | import static org.hamcrest.CoreMatchers.equalTo;
25 | import static org.hamcrest.CoreMatchers.is;
26 | import static org.hamcrest.MatcherAssert.assertThat;
27 |
28 | public class ErrorMessageFactoryTest extends AndroidTestCase {
29 |
30 | @Override protected void setUp() throws Exception {
31 | super.setUp();
32 | }
33 |
34 | public void testNetworkConnectionErrorMessage() {
35 | String expectedMessage = getContext().getString(R.string.exception_message_no_connection);
36 | String actualMessage = ErrorMessageFactory.create(getContext(),
37 | new NetworkConnectionException());
38 |
39 | assertThat(actualMessage, is(equalTo(expectedMessage)));
40 | }
41 |
42 | public void testUserNotFoundErrorMessage() {
43 | String expectedMessage = getContext().getString(R.string.exception_message_user_not_found);
44 | String actualMessage = ErrorMessageFactory.create(getContext(), new UserNotFoundException());
45 |
46 | assertThat(actualMessage, is(equalTo(expectedMessage)));
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/mapper/UserModelDataMapperTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.test.mapper;
17 |
18 | import com.fernandocejas.android10.sample.data.dto.User;
19 | import com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper;
20 | import com.fernandocejas.android10.sample.presentation.model.UserModel;
21 |
22 | import junit.framework.TestCase;
23 |
24 | import java.util.ArrayList;
25 | import java.util.Collection;
26 | import java.util.List;
27 |
28 | import static org.hamcrest.CoreMatchers.instanceOf;
29 | import static org.hamcrest.CoreMatchers.is;
30 | import static org.hamcrest.MatcherAssert.assertThat;
31 | import static org.mockito.Mockito.mock;
32 |
33 | public class UserModelDataMapperTest extends TestCase {
34 |
35 | private static final int FAKE_USER_ID = 123;
36 | private static final String FAKE_FULLNAME = "Tony Stark";
37 |
38 | private UserModelDataMapper userModelDataMapper;
39 |
40 | @Override protected void setUp() throws Exception {
41 | super.setUp();
42 | userModelDataMapper = new UserModelDataMapper();
43 | }
44 |
45 | public void testTransformUser() {
46 | User user = createFakeUser();
47 | UserModel userModel = userModelDataMapper.transformUser(user);
48 |
49 | assertThat(userModel, is(instanceOf(UserModel.class)));
50 | assertThat(userModel.getUserId(), is(FAKE_USER_ID));
51 | assertThat(userModel.getFullName(), is(FAKE_FULLNAME));
52 | }
53 |
54 | public void testTransformUserCollection() {
55 | User mockUserOne = mock(User.class);
56 | User mockUserTwo = mock(User.class);
57 |
58 | List userList = new ArrayList(5);
59 | userList.add(mockUserOne);
60 | userList.add(mockUserTwo);
61 |
62 | Collection userModelList = userModelDataMapper.transformUsers(userList);
63 |
64 | assertThat(userModelList.toArray()[0], is(instanceOf(UserModel.class)));
65 | assertThat(userModelList.toArray()[1], is(instanceOf(UserModel.class)));
66 | assertThat(userModelList.size(), is(2));
67 | }
68 |
69 | private User createFakeUser() {
70 | User user = new User(FAKE_USER_ID);
71 | user.setFullName(FAKE_FULLNAME);
72 |
73 | return user;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/view/activity/UserDetailsActivityTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.test.view.activity;
17 |
18 | import android.app.Fragment;
19 | import android.content.Intent;
20 | import android.test.ActivityInstrumentationTestCase2;
21 | import com.fernandocejas.android10.sample.presentation.R;
22 | import com.fernandocejas.android10.sample.presentation.view.activity.UserDetailsActivity;
23 |
24 | import static android.support.test.espresso.Espresso.onView;
25 | import static android.support.test.espresso.assertion.ViewAssertions.matches;
26 | import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
27 | import static android.support.test.espresso.matcher.ViewMatchers.withId;
28 | import static android.support.test.espresso.matcher.ViewMatchers.withText;
29 | import static org.hamcrest.CoreMatchers.is;
30 | import static org.hamcrest.CoreMatchers.notNullValue;
31 | import static org.hamcrest.MatcherAssert.assertThat;
32 | import static org.hamcrest.Matchers.not;
33 |
34 | public class UserDetailsActivityTest extends ActivityInstrumentationTestCase2 {
35 |
36 | private static final int FAKE_USER_ID = 10;
37 |
38 | private UserDetailsActivity userDetailsActivity;
39 |
40 | public UserDetailsActivityTest() {
41 | super(UserDetailsActivity.class);
42 | }
43 |
44 | @Override protected void setUp() throws Exception {
45 | super.setUp();
46 | this.setActivityIntent(createTargetIntent());
47 | this.userDetailsActivity = getActivity();
48 | }
49 |
50 | @Override protected void tearDown() throws Exception {
51 | super.tearDown();
52 | }
53 |
54 | public void testContainsUserDetailsFragment() {
55 | Fragment userDetailsFragment =
56 | userDetailsActivity.getFragmentManager().findFragmentById(R.id.fl_fragment);
57 | assertThat(userDetailsFragment, is(notNullValue()));
58 | }
59 |
60 | public void testContainsProperTitle() {
61 | String actualTitle = this.userDetailsActivity.getTitle().toString().trim();
62 |
63 | assertThat(actualTitle, is("User Details"));
64 | }
65 |
66 | public void testLoadUserHappyCaseViews() {
67 | onView(withId(R.id.rl_retry)).check(matches(not(isDisplayed())));
68 | onView(withId(R.id.rl_progress)).check(matches(not(isDisplayed())));
69 |
70 | onView(withId(R.id.tv_fullname)).check(matches(isDisplayed()));
71 | onView(withId(R.id.tv_email)).check(matches(isDisplayed()));
72 | onView(withId(R.id.tv_description)).check(matches(isDisplayed()));
73 | }
74 |
75 | public void testLoadUserHappyCaseData() {
76 | onView(withId(R.id.tv_fullname)).check(matches(withText("John Sanchez")));
77 | onView(withId(R.id.tv_email)).check(matches(withText("dmedina@katz.edu")));
78 | onView(withId(R.id.tv_followers)).check(matches(withText("4523")));
79 | }
80 |
81 | private Intent createTargetIntent() {
82 | Intent intentLaunchActivity =
83 | UserDetailsActivity.getCallingIntent(getInstrumentation().getTargetContext(), FAKE_USER_ID);
84 |
85 | return intentLaunchActivity;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/view/activity/UserListActivityTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.test.view.activity;
17 |
18 | import android.app.Fragment;
19 | import android.content.Intent;
20 | import android.test.ActivityInstrumentationTestCase2;
21 | import com.fernandocejas.android10.sample.presentation.R;
22 | import com.fernandocejas.android10.sample.presentation.view.activity.UserListActivity;
23 |
24 | import static org.hamcrest.CoreMatchers.is;
25 | import static org.hamcrest.CoreMatchers.notNullValue;
26 | import static org.hamcrest.MatcherAssert.assertThat;
27 |
28 | public class UserListActivityTest extends ActivityInstrumentationTestCase2 {
29 |
30 | private UserListActivity userListActivity;
31 |
32 | public UserListActivityTest() {
33 | super(UserListActivity.class);
34 | }
35 |
36 | @Override protected void setUp() throws Exception {
37 | super.setUp();
38 | this.setActivityIntent(createTargetIntent());
39 | userListActivity = getActivity();
40 | }
41 |
42 | @Override protected void tearDown() throws Exception {
43 | super.tearDown();
44 | }
45 |
46 | public void testContainsUserListFragment() {
47 | Fragment userListFragment =
48 | userListActivity.getFragmentManager().findFragmentById(R.id.fragmentUserList);
49 | assertThat(userListFragment, is(notNullValue()));
50 | }
51 |
52 | public void testContainsProperTitle() {
53 | String actualTitle = this.userListActivity.getTitle().toString().trim();
54 |
55 | assertThat(actualTitle, is("Users List"));
56 | }
57 |
58 | private Intent createTargetIntent() {
59 | Intent intentLaunchActivity =
60 | UserListActivity.getCallingIntent(getInstrumentation().getTargetContext());
61 |
62 | return intentLaunchActivity;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/viewmodel/UserDetailsViewModelTest.java:
--------------------------------------------------------------------------------
1 | package com.fernandocejas.android10.sample.test.viewmodel;
2 |
3 | import android.support.test.rule.ActivityTestRule;
4 | import android.support.test.runner.AndroidJUnit4;
5 |
6 | import com.fernandocejas.android10.sample.data.dto.User;
7 | import com.fernandocejas.android10.sample.presentation.R;
8 | import com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper;
9 | import com.fernandocejas.android10.sample.presentation.view.activity.UserDetailsActivity;
10 | import com.fernandocejas.android10.sample.presentation.view.fragment.UserDetailsFragment;
11 |
12 | import org.junit.Before;
13 | import org.junit.Rule;
14 | import org.junit.Test;
15 | import org.junit.runner.RunWith;
16 |
17 | import static android.support.test.espresso.Espresso.onView;
18 | import static android.support.test.espresso.assertion.ViewAssertions.matches;
19 | import static android.support.test.espresso.matcher.ViewMatchers.withId;
20 | import static android.support.test.espresso.matcher.ViewMatchers.withText;
21 |
22 | /**
23 | * Created by rocko on 15-11-6.
24 | */
25 | @RunWith(AndroidJUnit4.class)
26 | public class UserDetailsViewModelTest {
27 |
28 | private final static int FAKE_USER_ID = 1;
29 | private final static String FAKE_USER_NAME = "FAKE_USER_NAME";
30 | private final static String FAKE_EMAIL = "FAKE_EMAIL@fake.com";
31 | private final static String FAKE_DESCRIPTION = "FAKE_DESCRIPTION";
32 | private final static int FAKE_FOLLOWERS = 300;
33 |
34 | private User fakeUser;
35 | private UserDetailsFragment detailsFragment;
36 | private UserModelDataMapper userModelDataMapper;
37 |
38 |
39 | @Rule
40 | public ActivityTestRule mActivityRule = new ActivityTestRule(UserDetailsActivity.class);
41 |
42 | @Before
43 | public void setUp() {
44 | fakeUser = makeFakeUser();
45 | userModelDataMapper = new UserModelDataMapper();
46 | detailsFragment = (UserDetailsFragment) mActivityRule.getActivity().getFragment(UserDetailsFragment.TAG);
47 | }
48 |
49 | @Test
50 | public void testShowUser() throws Exception {
51 | detailsFragment.getViewModel().showUserDetails(userModelDataMapper.transformUser(fakeUser));
52 | // UserDetailsBinding userDetailsBinding = detailsFragment.getBinding();
53 | onView(withId(R.id.tv_fullname)).check(matches(withText(FAKE_USER_NAME)));
54 | onView(withId(R.id.tv_email)).check(matches(withText(FAKE_EMAIL)));
55 | onView(withId(R.id.tv_followers)).check(matches(withText(String.valueOf(FAKE_FOLLOWERS))));
56 | onView(withId(R.id.tv_description)).check(matches(withText(FAKE_DESCRIPTION)));
57 | }
58 |
59 |
60 | private User makeFakeUser() {
61 | User fakeUser = new User(FAKE_USER_ID);
62 | fakeUser.setFullName(FAKE_USER_NAME);
63 | fakeUser.setEmail(FAKE_EMAIL);
64 | fakeUser.setFollowers(FAKE_FOLLOWERS);
65 | fakeUser.setDescription(FAKE_DESCRIPTION);
66 | return fakeUser;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/viewmodel/UserListViewModelTest.java:
--------------------------------------------------------------------------------
1 | package com.fernandocejas.android10.sample.test.viewmodel;
2 |
3 | import android.support.test.espresso.contrib.RecyclerViewActions;
4 | import android.support.test.espresso.intent.rule.IntentsTestRule;
5 | import android.support.test.runner.AndroidJUnit4;
6 | import android.test.suitebuilder.annotation.LargeTest;
7 |
8 | import com.fernandocejas.android10.sample.data.dto.User;
9 | import com.fernandocejas.android10.sample.presentation.R;
10 | import com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper;
11 | import com.fernandocejas.android10.sample.presentation.model.UserModel;
12 | import com.fernandocejas.android10.sample.presentation.view.activity.UserDetailsActivity;
13 | import com.fernandocejas.android10.sample.presentation.view.activity.UserListActivity;
14 | import com.fernandocejas.android10.sample.presentation.view.adapter.UsersAdapter;
15 | import com.fernandocejas.android10.sample.presentation.view.fragment.UserListFragment;
16 | import com.fernandocejas.android10.sample.presentation.viewmodel.UserListViewModel;
17 |
18 | import org.junit.Before;
19 | import org.junit.Rule;
20 | import org.junit.Test;
21 | import org.junit.runner.RunWith;
22 |
23 | import java.util.ArrayList;
24 | import java.util.Collection;
25 | import java.util.List;
26 |
27 | import static android.support.test.espresso.Espresso.onView;
28 | import static android.support.test.espresso.action.ViewActions.click;
29 | import static android.support.test.espresso.intent.Intents.intending;
30 | import static android.support.test.espresso.intent.matcher.IntentMatchers.hasExtra;
31 | import static android.support.test.espresso.matcher.ViewMatchers.withId;
32 | import static junit.framework.Assert.assertEquals;
33 |
34 | /**
35 | * Created by rocko on 15-11-6.
36 | */
37 | @RunWith(AndroidJUnit4.class)
38 | @LargeTest
39 | public class UserListViewModelTest {
40 |
41 | private UserModelDataMapper userModelDataMapper;
42 | private List fakeUsersList;
43 |
44 | private UserListFragment userListFragment;
45 | private UserListViewModel userListViewModel;
46 | private UsersAdapter usersAdapter;
47 |
48 | @Rule
49 | public IntentsTestRule mActivityRule = new IntentsTestRule<>(
50 | UserListActivity.class);
51 |
52 | @Before
53 | public void setUp() {
54 | // intending(not(isInternal())).respondWith(new Instrumentation.ActivityResult(Activity.RESULT_OK, null));
55 |
56 | userModelDataMapper = new UserModelDataMapper();
57 | fakeUsersList = makeFakeUsers();
58 |
59 | userListFragment = (UserListFragment) mActivityRule.getActivity().getFragmentManager().findFragmentById(R.id.fragmentUserList);
60 | userListViewModel = userListFragment.getViewModel();
61 |
62 | Collection userModelsCollection = userModelDataMapper.transformUsers(fakeUsersList);
63 | usersAdapter = new UsersAdapter(mActivityRule.getActivity(), userModelsCollection);
64 | usersAdapter.setOnItemClickListener(userListViewModel.onUserItemClick());
65 | userListViewModel.showContentList(usersAdapter);
66 | }
67 |
68 | @Test
69 | public void testShowContentList() throws Exception {
70 | assertEquals(fakeUsersList.size(), usersAdapter.getItemCount());
71 | }
72 |
73 | @Test
74 | public void testOnUserItemClick() throws Exception {
75 | User user = fakeUsersList.get(1);
76 |
77 | onView(withId(R.id.rv_users)).perform(RecyclerViewActions.actionOnItemAtPosition(1, click()));
78 |
79 | intending(hasExtra(UserDetailsActivity.INTENT_EXTRA_PARAM_USER_ID, user.getUserId() + ""));
80 | }
81 |
82 |
83 | private List makeFakeUsers() {
84 | List fakeUsersList = new ArrayList<>();
85 | for (int i = 0; i < 15; i++) {
86 | User user = new User(i);
87 | user.setFullName("NAME: " + i);
88 | user.setEmail(i + "@fake.com");
89 | user.setFollowers(i);
90 | user.setDescription("Description: " + i);
91 | fakeUsersList.add(user);
92 | }
93 |
94 | return fakeUsersList;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/presentation/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
14 |
15 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
27 |
28 |
29 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/AndroidApplication.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.presentation;
17 |
18 | import android.app.Activity;
19 | import android.app.Application;
20 | import android.support.annotation.NonNull;
21 |
22 | /**
23 | * Android Main Application
24 | */
25 | public class AndroidApplication extends Application {
26 |
27 | private static AndroidApplication instance;
28 |
29 | private Activity mCurrentActivity;
30 |
31 | @Override
32 | public void onCreate() {
33 | super.onCreate();
34 | instance = this;
35 | }
36 |
37 | public static AndroidApplication getContext() {
38 | return instance;
39 | }
40 |
41 | public static AndroidApplication getInstance() {
42 | return instance;
43 | }
44 |
45 | public Activity getCurrentActivity() {
46 | return mCurrentActivity;
47 | }
48 |
49 | public void setCurrentActivity(@NonNull Activity mCurrentActivity) {
50 | this.mCurrentActivity = mCurrentActivity;
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/exception/ErrorMessageFactory.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.presentation.exception;
17 |
18 | import android.content.Context;
19 | import com.fernandocejas.android10.sample.data.exception.NetworkConnectionException;
20 | import com.fernandocejas.android10.sample.data.exception.UserNotFoundException;
21 | import com.fernandocejas.android10.sample.presentation.R;
22 |
23 | /**
24 | * Factory used to create error messages from an Exception as a condition.
25 | */
26 | public class ErrorMessageFactory {
27 |
28 | private ErrorMessageFactory() {
29 | //empty
30 | }
31 |
32 | /**
33 | * Creates a String representing an error message.
34 | *
35 | * @param context Context needed to retrieve string resources.
36 | * @param exception An exception used as a condition to retrieve the correct error message.
37 | * @return {@link String} an error message.
38 | */
39 | public static String create(Context context, Exception exception) {
40 | String message = context.getString(R.string.exception_message_generic);
41 |
42 | if (exception instanceof NetworkConnectionException) {
43 | message = context.getString(R.string.exception_message_no_connection);
44 | } else if (exception instanceof UserNotFoundException) {
45 | message = context.getString(R.string.exception_message_user_not_found);
46 | }
47 |
48 | return message;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mapper/UserModelDataMapper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.presentation.mapper;
17 |
18 | import com.fernandocejas.android10.sample.data.dto.User;
19 | import com.fernandocejas.android10.sample.presentation.model.UserModel;
20 |
21 | import java.util.ArrayList;
22 | import java.util.Collection;
23 | import java.util.Collections;
24 |
25 | /**
26 | * Mapper class used to transform {@link User} (in the domain layer) to {@link UserModel} in the
27 | * presentation layer.
28 | */
29 | public class UserModelDataMapper {
30 |
31 | public UserModelDataMapper() {}
32 |
33 | /**
34 | * Transform a {@link User} into an {@link UserModel}.
35 | *
36 | * @param user Object to be transformed.
37 | * @return {@link UserModel}.
38 | */
39 | public UserModel transformUser(User user) {
40 | if (user == null) {
41 | throw new IllegalArgumentException("Cannot transform a null value");
42 | }
43 | UserModel userModel = new UserModel(user.getUserId());
44 | userModel.setCoverUrl(user.getCoverUrl());
45 | userModel.setFullName(user.getFullName());
46 | userModel.setEmail(user.getEmail());
47 | userModel.setDescription(user.getDescription());
48 | userModel.setFollowers(user.getFollowers());
49 |
50 | return userModel;
51 | }
52 |
53 | /**
54 | * Transform a Collection of {@link User} into a Collection of {@link UserModel}.
55 | *
56 | * @param usersCollection Objects to be transformed.
57 | * @return List of {@link UserModel}.
58 | */
59 | public Collection transformUsers(Collection usersCollection) {
60 | Collection userModelsCollection;
61 |
62 | if (usersCollection != null && !usersCollection.isEmpty()) {
63 | userModelsCollection = new ArrayList<>();
64 | for (User user : usersCollection) {
65 | userModelsCollection.add(transformUser(user));
66 | }
67 | } else {
68 | userModelsCollection = Collections.emptyList();
69 | }
70 |
71 | return userModelsCollection;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/model/UserModel.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android10.sample.presentation.model;
17 |
18 | /**
19 | * Class that represents a user in the presentation layer.
20 | */
21 | public class UserModel {
22 |
23 | private final int userId;
24 |
25 | public UserModel(int userId) {
26 | this.userId = userId;
27 | }
28 |
29 | private String coverUrl;
30 | private String fullName;
31 | private String email;
32 | private String description;
33 | private int followers;
34 |
35 | public int getUserId() {
36 | return userId;
37 | }
38 |
39 | public String getCoverUrl() {
40 | return coverUrl;
41 | }
42 |
43 | public void setCoverUrl(String coverUrl) {
44 | this.coverUrl = coverUrl;
45 | }
46 |
47 | public String getFullName() {
48 | return fullName;
49 | }
50 |
51 | public void setFullName(String fullName) {
52 | this.fullName = fullName;
53 | }
54 |
55 | public String getEmail() {
56 | return email;
57 | }
58 |
59 | public void setEmail(String email) {
60 | this.email = email;
61 | }
62 |
63 | public String getDescription() {
64 | return description;
65 | }
66 |
67 | public void setDescription(String description) {
68 | this.description = description;
69 | }
70 |
71 | public int getFollowers() {
72 | return followers;
73 | }
74 |
75 | public void setFollowers(int followers) {
76 | this.followers = followers;
77 | }
78 |
79 | @Override public String toString() {
80 | StringBuilder stringBuilder = new StringBuilder();
81 |
82 | stringBuilder.append("***** User Model Details *****\n");
83 | stringBuilder.append("id=" + this.getUserId() + "\n");
84 | stringBuilder.append("cover url=" + this.getCoverUrl() + "\n");
85 | stringBuilder.append("fullname=" + this.getFullName() + "\n");
86 | stringBuilder.append("email=" + this.getEmail() + "\n");
87 | stringBuilder.append("description=" + this.getDescription() + "\n");
88 | stringBuilder.append("followers=" + this.getFollowers() + "\n");
89 | stringBuilder.append("*******************************");
90 |
91 | return stringBuilder.toString();
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/navigation/ActivityNavigator.java:
--------------------------------------------------------------------------------
1 | package com.fernandocejas.android10.sample.presentation.navigation;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.support.annotation.NonNull;
7 |
8 | import com.fernandocejas.android10.sample.presentation.AndroidApplication;
9 |
10 | /**
11 | * Created by rocko on 15-11-3.
12 | * Activity Navigator
13 | */
14 | public class ActivityNavigator {
15 |
16 | /**
17 | * @param targetActivity
18 | */
19 | public static void navigateTo(@NonNull Class extends Activity> targetActivity) {
20 | navigateTo(targetActivity, new Intent(AndroidApplication.getInstance().getCurrentActivity(), targetActivity));
21 | }
22 |
23 | /**
24 | * @param targetActivity
25 | * @param intent
26 | */
27 | public static void navigateTo(@NonNull Class extends Activity> targetActivity, @NonNull Intent intent) {
28 | Activity currentActivity = AndroidApplication.getInstance().getCurrentActivity();
29 | navigateTo(currentActivity, targetActivity, intent);
30 | }
31 |
32 | /**
33 | * Used in onCreate(before onResume) method to ensure current activity is not null.
34 | *
35 | * @param context
36 | * @param targetActivity
37 | * @param intent
38 | */
39 | public static void navigateTo(@NonNull Context context, @NonNull Class extends Activity> targetActivity, @NonNull Intent intent) {
40 | context.startActivity(intent);
41 | }
42 |
43 | public static void finish() {
44 | AndroidApplication.getInstance().getCurrentActivity().finish();
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package com.fernandocejas.android10.sample.presentation.view.activity;
2 |
3 | import android.app.Activity;
4 | import android.app.Fragment;
5 | import android.app.FragmentTransaction;
6 | import android.databinding.ViewDataBinding;
7 | import android.os.Bundle;
8 | import android.support.annotation.NonNull;
9 |
10 | import com.fernandocejas.android10.sample.presentation.AndroidApplication;
11 | import com.fernandocejas.android10.sample.presentation.viewmodel.ViewModel;
12 |
13 | /**
14 | * Base {@link android.app.Activity} class for every Activity in this application.
15 | */
16 | public abstract class BaseActivity extends Activity {
17 |
18 | private VM viewModel;
19 | private B binding;
20 |
21 | @Override
22 | protected void onCreate(Bundle savedInstanceState) {
23 | super.onCreate(savedInstanceState);
24 | }
25 |
26 | @Override
27 | protected void onStart() {
28 | super.onStart();
29 | }
30 |
31 | @Override
32 | protected void onResume() {
33 | super.onResume();
34 |
35 | AndroidApplication.getInstance().setCurrentActivity(this);
36 | }
37 |
38 | @Override
39 | protected void onPause() {
40 | super.onPause();
41 |
42 | clearReferences();
43 | }
44 |
45 | @Override
46 | protected void onStop() {
47 | super.onStop();
48 | }
49 |
50 | @Override
51 | protected void onDestroy() {
52 | super.onDestroy();
53 |
54 | clearReferences();
55 | }
56 |
57 | @Override
58 | protected void onRestart() {
59 | super.onRestart();
60 | }
61 |
62 | /**
63 | * Adds a {@link Fragment} to this activity's layout.
64 | *
65 | * @param containerViewId The container view to where add the fragment.
66 | * @param fragment The fragment to be added.
67 | */
68 | protected void addFragment(int containerViewId, Fragment fragment, String tag) {
69 | FragmentTransaction fragmentTransaction = this.getFragmentManager().beginTransaction();
70 | fragmentTransaction.add(containerViewId, fragment, tag);
71 | fragmentTransaction.commit();
72 | }
73 |
74 | public T getFragment(String tag) {
75 | return (T) getFragmentManager().findFragmentByTag(tag);
76 | }
77 |
78 | public void setViewModel(@NonNull VM viewModel) {
79 | this.viewModel = viewModel;
80 | }
81 |
82 | public VM getViewModel() {
83 | if (viewModel == null) {
84 | throw new NullPointerException("You should setViewModel first!");
85 | }
86 | return viewModel;
87 | }
88 |
89 | public void setBinding(@NonNull B binding) {
90 | this.binding = binding;
91 | }
92 |
93 | public B getBinding() {
94 | if (binding == null) {
95 | throw new NullPointerException("You should setBinding first!");
96 | }
97 | return binding;
98 | }
99 |
100 |
101 | private void clearReferences(){
102 | Activity currActivity = AndroidApplication.getInstance().getCurrentActivity();
103 | if (this.equals(currActivity))
104 | AndroidApplication.getInstance().setCurrentActivity(null);
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.fernandocejas.android10.sample.presentation.view.activity;
2 |
3 | import android.databinding.DataBindingUtil;
4 | import android.os.Bundle;
5 | import android.text.Html;
6 | import android.text.method.LinkMovementMethod;
7 |
8 | import com.fernandocejas.android10.sample.presentation.HomeBinding;
9 | import com.fernandocejas.android10.sample.presentation.R;
10 | import com.fernandocejas.android10.sample.presentation.viewmodel.HomeViewModel;
11 |
12 | /**
13 | * Main application screen. This is the app entry point.
14 | */
15 | public class MainActivity extends BaseActivity {
16 |
17 | @Override
18 | protected void onCreate(Bundle savedInstanceState) {
19 | super.onCreate(savedInstanceState);
20 |
21 | setViewModel(new HomeViewModel());
22 | setBinding(DataBindingUtil.setContentView(this, R.layout.home_activity));
23 | getBinding().setViewModel(getViewModel());
24 |
25 | initWidget();
26 | }
27 |
28 | private void initWidget() {
29 | getBinding().linkTv.setText(Html.fromHtml(getResources().getString(R.string.url)));
30 | getBinding().linkTv.setMovementMethod(LinkMovementMethod.getInstance());
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/UserDetailsActivity.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2014 android10.org. All rights reserved.
3 | *
4 | * @author Fernando Cejas (the android10 coder)
5 | */
6 | package com.fernandocejas.android10.sample.presentation.view.activity;
7 |
8 | import android.content.Context;
9 | import android.content.Intent;
10 | import android.databinding.DataBindingUtil;
11 | import android.os.Bundle;
12 | import android.view.Window;
13 |
14 | import com.fernandocejas.android10.sample.presentation.R;
15 | import com.fernandocejas.android10.sample.presentation.view.fragment.UserDetailsFragment;
16 |
17 | /**
18 | * Activity that shows details of a certain user.
19 | */
20 | public class UserDetailsActivity extends BaseActivity {
21 |
22 | public static final String INTENT_EXTRA_PARAM_USER_ID = "org.android10.INTENT_PARAM_USER_ID";
23 | private static final String INSTANCE_STATE_PARAM_USER_ID = "org.android10.STATE_PARAM_USER_ID";
24 |
25 | private int userId;
26 |
27 | public static Intent getCallingIntent(Context context, int userId) {
28 | Intent callingIntent = new Intent(context, UserDetailsActivity.class);
29 | callingIntent.putExtra(INTENT_EXTRA_PARAM_USER_ID, userId);
30 |
31 | return callingIntent;
32 | }
33 |
34 | @Override
35 | protected void onCreate(Bundle savedInstanceState) {
36 | super.onCreate(savedInstanceState);
37 | requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
38 | DataBindingUtil.setContentView(this, R.layout.activity_user_details);
39 |
40 | this.initializeActivity(savedInstanceState);
41 | }
42 |
43 | @Override
44 | protected void onSaveInstanceState(Bundle outState) {
45 | if (outState != null) {
46 | outState.putInt(INSTANCE_STATE_PARAM_USER_ID, this.userId);
47 | }
48 | super.onSaveInstanceState(outState);
49 | }
50 |
51 | /**
52 | * Initializes this activity.
53 | */
54 | private void initializeActivity(Bundle savedInstanceState) {
55 | if (savedInstanceState == null) {
56 | this.userId = getIntent().getIntExtra(INTENT_EXTRA_PARAM_USER_ID, -1);
57 | addFragment(R.id.fl_fragment, UserDetailsFragment.newInstance(this.userId), UserDetailsFragment.TAG);
58 | } else {
59 | this.userId = savedInstanceState.getInt(INSTANCE_STATE_PARAM_USER_ID);
60 | }
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/UserListActivity.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2014 android10.org. All rights reserved.
3 | *
4 | * @author Fernando Cejas (the android10 coder)
5 | */
6 | package com.fernandocejas.android10.sample.presentation.view.activity;
7 |
8 | import android.content.Context;
9 | import android.content.Intent;
10 | import android.databinding.DataBindingUtil;
11 | import android.os.Bundle;
12 | import android.view.Window;
13 |
14 | import com.fernandocejas.android10.sample.presentation.R;
15 |
16 | /**
17 | * Activity that shows a list of Users.
18 | */
19 | public class UserListActivity extends BaseActivity{
20 |
21 | public static Intent getCallingIntent(Context context) {
22 | return new Intent(context, UserListActivity.class);
23 | }
24 |
25 | @Override
26 | protected void onCreate(Bundle savedInstanceState) {
27 | super.onCreate(savedInstanceState);
28 | requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
29 |
30 | DataBindingUtil.setContentView(this, R.layout.user_list_activity);
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/adapter/UsersAdapter.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2014 android10.org. All rights reserved.
3 | *
4 | * @author Fernando Cejas (the android10 coder)
5 | */
6 | package com.fernandocejas.android10.sample.presentation.view.adapter;
7 |
8 | import android.content.Context;
9 | import android.databinding.DataBindingUtil;
10 | import android.support.v7.widget.RecyclerView;
11 | import android.view.LayoutInflater;
12 | import android.view.View;
13 | import android.view.ViewGroup;
14 | import android.widget.TextView;
15 |
16 | import com.fernandocejas.android10.sample.presentation.R;
17 | import com.fernandocejas.android10.sample.presentation.RowUserBinding;
18 | import com.fernandocejas.android10.sample.presentation.model.UserModel;
19 |
20 | import java.util.Collection;
21 | import java.util.List;
22 |
23 |
24 | /**
25 | * Adaptar that manages a collection of {@link UserModel}.
26 | */
27 | public class UsersAdapter extends RecyclerView.Adapter {
28 |
29 | private RowUserBinding rowUserBinding;
30 |
31 | public interface OnItemClickListener {
32 | void onUserItemClicked(UserModel userModel);
33 | }
34 |
35 | private List usersCollection;
36 | private final LayoutInflater layoutInflater;
37 |
38 | private OnItemClickListener onItemClickListener;
39 |
40 | public UsersAdapter(Context context, Collection usersCollection) {
41 | this.validateUsersCollection(usersCollection);
42 | this.layoutInflater =
43 | (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
44 | this.usersCollection = (List) usersCollection;
45 | }
46 |
47 | @Override
48 | public int getItemCount() {
49 | return (this.usersCollection != null) ? this.usersCollection.size() : 0;
50 | }
51 |
52 | @Override
53 | public UserViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
54 | rowUserBinding = DataBindingUtil.inflate(layoutInflater, R.layout.row_user, parent, false);
55 | UserViewHolder userViewHolder = new UserViewHolder(rowUserBinding);
56 | return userViewHolder;
57 | }
58 |
59 | @Override
60 | public void onBindViewHolder(UserViewHolder holder, final int position) {
61 | final UserModel userModel = this.usersCollection.get(position);
62 | holder.textViewTitle.setText(userModel.getFullName());
63 | holder.itemView.setOnClickListener(new View.OnClickListener() {
64 | @Override
65 | public void onClick(View v) {
66 | if (UsersAdapter.this.onItemClickListener != null) {
67 | UsersAdapter.this.onItemClickListener.onUserItemClicked(userModel);
68 | }
69 | }
70 | });
71 | }
72 |
73 | @Override
74 | public long getItemId(int position) {
75 | return position;
76 | }
77 |
78 | public void setUsersCollection(Collection usersCollection) {
79 | this.validateUsersCollection(usersCollection);
80 | this.usersCollection = (List) usersCollection;
81 | this.notifyDataSetChanged();
82 | }
83 |
84 | public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
85 | this.onItemClickListener = onItemClickListener;
86 | }
87 |
88 | private void validateUsersCollection(Collection usersCollection) {
89 | if (usersCollection == null) {
90 | throw new IllegalArgumentException("The list cannot be null");
91 | }
92 | }
93 |
94 | static class UserViewHolder extends RecyclerView.ViewHolder {
95 | TextView textViewTitle;
96 |
97 | public UserViewHolder(RowUserBinding rowUserBinding) {
98 | super(rowUserBinding.getRoot());
99 | textViewTitle = rowUserBinding.title;
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/adapter/UsersLayoutManager.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2014 android10.org. All rights reserved.
3 | * @author Fernando Cejas (the android10 coder)
4 | */
5 | package com.fernandocejas.android10.sample.presentation.view.adapter;
6 |
7 | import android.content.Context;
8 | import android.support.v7.widget.LinearLayoutManager;
9 |
10 | /**
11 | * Layout manager to position items inside a {@link android.support.v7.widget.RecyclerView}.
12 | */
13 | public class UsersLayoutManager extends LinearLayoutManager {
14 | public UsersLayoutManager(Context context) {
15 | super(context);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/fragment/BaseFragment.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2014 android10.org. All rights reserved.
3 | *
4 | * @author Fernando Cejas (the android10 coder)
5 | */
6 | package com.fernandocejas.android10.sample.presentation.view.fragment;
7 |
8 | import android.app.Fragment;
9 | import android.databinding.ViewDataBinding;
10 | import android.os.Bundle;
11 | import android.support.annotation.NonNull;
12 | import android.widget.Toast;
13 |
14 | import com.fernandocejas.android10.sample.presentation.viewmodel.ViewModel;
15 |
16 | /**
17 | * Base {@link android.app.Fragment} class for every fragment in this application.
18 | */
19 | public abstract class BaseFragment extends Fragment {
20 |
21 | private VM viewModel;
22 | private B binding;
23 |
24 | @Override
25 | public void onCreate(Bundle savedInstanceState) {
26 | super.onCreate(savedInstanceState);
27 | setRetainInstance(true);
28 | }
29 |
30 | /**
31 | * Shows a {@link android.widget.Toast} message.
32 | *
33 | * @param message An string representing a message to be shown.
34 | */
35 | protected void showToastMessage(String message) {
36 | Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show();
37 | }
38 |
39 |
40 | public void setViewModel(@NonNull VM viewModel) {
41 | this.viewModel = viewModel;
42 | }
43 |
44 | public VM getViewModel() {
45 | if (viewModel == null) {
46 | throw new NullPointerException("You should setViewModel first!");
47 | }
48 | return viewModel;
49 | }
50 |
51 | public void setBinding(@NonNull B binding) {
52 | this.binding = binding;
53 | }
54 |
55 | public B getBinding() {
56 | if (binding == null) {
57 | throw new NullPointerException("You should setBinding first!");
58 | }
59 | return binding;
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/fragment/UserDetailsFragment.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2014 android10.org. All rights reserved.
3 | *
4 | * @author Fernando Cejas (the android10 coder)
5 | */
6 | package com.fernandocejas.android10.sample.presentation.view.fragment;
7 |
8 | import android.content.Context;
9 | import android.databinding.DataBindingUtil;
10 | import android.os.Bundle;
11 | import android.view.LayoutInflater;
12 | import android.view.View;
13 | import android.view.ViewGroup;
14 |
15 | import com.fernandocejas.android10.sample.presentation.R;
16 | import com.fernandocejas.android10.sample.presentation.UserDetailsBinding;
17 | import com.fernandocejas.android10.sample.presentation.viewmodel.UserDetailsViewModel;
18 |
19 | /**
20 | * Fragment that shows details of a certain user.
21 | */
22 | public class UserDetailsFragment extends BaseFragment {
23 |
24 | public final static String TAG = UserDetailsFragment.class.getSimpleName();
25 |
26 | private static final String ARGUMENT_KEY_USER_ID = "org.android10.ARGUMENT_USER_ID";
27 |
28 | private int userId;
29 |
30 | public UserDetailsFragment() {
31 | super();
32 |
33 | }
34 |
35 | public static UserDetailsFragment newInstance(int userId) {
36 | UserDetailsFragment userDetailsFragment = new UserDetailsFragment();
37 |
38 | Bundle argumentsBundle = new Bundle();
39 | argumentsBundle.putInt(ARGUMENT_KEY_USER_ID, userId);
40 | userDetailsFragment.setArguments(argumentsBundle);
41 |
42 | return userDetailsFragment;
43 | }
44 |
45 | @Override
46 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
47 | Bundle savedInstanceState) {
48 |
49 | setViewModel(new UserDetailsViewModel());
50 | setBinding(DataBindingUtil.inflate(inflater, R.layout.fragment_user_details, container, false));
51 | getBinding().setViewModel(getViewModel());
52 |
53 | return getBinding().getRoot();
54 | }
55 |
56 | @Override
57 | public void onActivityCreated(Bundle savedInstanceState) {
58 | super.onActivityCreated(savedInstanceState);
59 | this.initialize();
60 | }
61 |
62 | @Override
63 | public void onResume() {
64 | super.onResume();
65 | }
66 |
67 | @Override
68 | public void onPause() {
69 | super.onPause();
70 | }
71 |
72 | @Override
73 | public void onDestroyView() {
74 | super.onDestroyView();
75 | }
76 |
77 | @Override
78 | public void onDestroy() {
79 | super.onDestroy();
80 | }
81 |
82 | private void initialize() {
83 | this.userId = getArguments().getInt(ARGUMENT_KEY_USER_ID);
84 |
85 | getViewModel().loadUserDetailsCommand(userId);
86 | }
87 |
88 | @Override
89 | public Context getContext() {
90 | return getActivity().getApplicationContext();
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/fragment/UserListFragment.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2014 android10.org. All rights reserved.
3 | *
4 | * @author Fernando Cejas (the android10 coder)
5 | */
6 | package com.fernandocejas.android10.sample.presentation.view.fragment;
7 |
8 | import android.content.Context;
9 | import android.databinding.DataBindingUtil;
10 | import android.os.Bundle;
11 | import android.view.LayoutInflater;
12 | import android.view.View;
13 | import android.view.ViewGroup;
14 |
15 | import com.fernandocejas.android10.sample.presentation.R;
16 | import com.fernandocejas.android10.sample.presentation.UserListBinding;
17 | import com.fernandocejas.android10.sample.presentation.view.adapter.UsersLayoutManager;
18 | import com.fernandocejas.android10.sample.presentation.viewmodel.UserListViewModel;
19 |
20 | /**
21 | * Fragment that shows a list of Users.
22 | */
23 | public class UserListFragment extends BaseFragment {
24 |
25 | public final static String TAG = UserListFragment.class.getSimpleName();
26 |
27 | public UserListFragment() {
28 | super();
29 | }
30 |
31 | @Override
32 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
33 | Bundle savedInstanceState) {
34 |
35 | setViewModel(new UserListViewModel());
36 | setBinding(DataBindingUtil.inflate(inflater, R.layout.fragment_user_list, container, true));
37 | getBinding().setViewModel(getViewModel());
38 |
39 | setupUI();
40 |
41 | return getBinding().getRoot();
42 | }
43 |
44 | @Override
45 | public void onActivityCreated(Bundle savedInstanceState) {
46 | super.onActivityCreated(savedInstanceState);
47 | getViewModel().loadUsersCommand();
48 | }
49 |
50 | private void setupUI() {
51 | getBinding().rvUsers.setLayoutManager(new UsersLayoutManager(getActivity()));
52 | }
53 |
54 | @Override
55 | public Context getContext() {
56 | return this.getActivity().getApplicationContext();
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/viewmodel/HomeViewModel.java:
--------------------------------------------------------------------------------
1 | package com.fernandocejas.android10.sample.presentation.viewmodel;
2 |
3 | import android.view.View;
4 |
5 | import com.fernandocejas.android10.sample.presentation.navigation.ActivityNavigator;
6 | import com.fernandocejas.android10.sample.presentation.view.activity.UserListActivity;
7 |
8 | /**
9 | * Created by rocko on 15-11-5.
10 | */
11 | public class HomeViewModel extends ViewModel {
12 |
13 | @Command
14 | public View.OnClickListener onClickLoadData() {
15 | return new View.OnClickListener(){
16 | @Override
17 | public void onClick(View v) {
18 | ActivityNavigator.navigateTo(UserListActivity.class);
19 | }
20 | };
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/viewmodel/LoadingViewModel.java:
--------------------------------------------------------------------------------
1 | package com.fernandocejas.android10.sample.presentation.viewmodel;
2 |
3 | import android.databinding.ObservableBoolean;
4 | import android.view.View;
5 |
6 | /**
7 | * Created by rocko on 15-11-5.
8 | */
9 | public abstract class LoadingViewModel extends ViewModel {
10 | public final ObservableBoolean showRetry = new ObservableBoolean(false);
11 | public final ObservableBoolean showLoading = new ObservableBoolean(false);
12 |
13 | @BindView
14 | public void showLoading() {
15 | showRetry.set(false);
16 | showLoading.set(true);
17 | }
18 |
19 | @BindView
20 | public void showRetry() {
21 | showLoading.set(false);
22 | showRetry.set(true);
23 | }
24 |
25 | public abstract View.OnClickListener onRetryClick();
26 | }
27 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/viewmodel/UserDetailsViewModel.java:
--------------------------------------------------------------------------------
1 | package com.fernandocejas.android10.sample.presentation.viewmodel;
2 |
3 | import android.databinding.ObservableBoolean;
4 | import android.databinding.ObservableField;
5 | import android.view.View;
6 |
7 | import com.fernandocejas.android10.sample.data.dto.User;
8 | import com.fernandocejas.android10.sample.domain.interactor.DefaultSubscriber;
9 | import com.fernandocejas.android10.sample.domain.interactor.GetUserDetails;
10 | import com.fernandocejas.android10.sample.presentation.AndroidApplication;
11 | import com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper;
12 | import com.fernandocejas.android10.sample.presentation.model.UserModel;
13 |
14 | /**
15 | * Created by rocko on 15-11-5.
16 | */
17 | public class UserDetailsViewModel extends LoadingViewModel {
18 |
19 | public final ObservableBoolean showUserDetails = new ObservableBoolean(true);
20 | public final ObservableField userObs = new ObservableField<>();
21 |
22 | GetUserDetails getUserDetailsUseCase = new GetUserDetails(AndroidApplication.getContext());
23 | UserModelDataMapper userModelDataMapper = new UserModelDataMapper();
24 |
25 | @BindView
26 | @Override
27 | public void showLoading() {
28 | // super.showLoading(); // show Details
29 | showRetry.set(false);
30 | showLoading.set(true);
31 | showUserDetails.set(true);
32 | }
33 |
34 | @BindView
35 | @Override
36 | public void showRetry() {
37 | super.showRetry();
38 | showUserDetails.set(false);
39 | }
40 |
41 | @BindView
42 | public void showUserDetails(UserModel userModel) {
43 | showLoading.set(false);
44 | showRetry.set(false);
45 | showUserDetails.set(true);
46 | userObs.set(userModel);
47 | }
48 |
49 |
50 | @Command
51 | public void loadUserDetailsCommand(int userId) {
52 | showLoading();
53 | getUserDetailsUseCase.setUserId(userId);
54 | getUserDetailsUseCase.execute(new DefaultSubscriber(){
55 | @Override
56 | public void onNext(User user) {
57 | showUserDetails(userModelDataMapper.transformUser(user));
58 | }
59 |
60 | @Override
61 | public void onError(Throwable e) {
62 | showRetry();
63 | }
64 | });
65 | }
66 |
67 | @Override
68 | public View.OnClickListener onRetryClick() {
69 | return new View.OnClickListener() {
70 | @Override
71 | public void onClick(View v) {
72 | loadUserDetailsCommand(userObs.get().getUserId());
73 | }
74 | };
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/viewmodel/UserListViewModel.java:
--------------------------------------------------------------------------------
1 | package com.fernandocejas.android10.sample.presentation.viewmodel;
2 |
3 |
4 | import android.content.Intent;
5 | import android.databinding.ObservableBoolean;
6 | import android.databinding.ObservableField;
7 | import android.util.Log;
8 | import android.view.View;
9 |
10 | import com.fernandocejas.android10.sample.data.dto.User;
11 | import com.fernandocejas.android10.sample.domain.interactor.DefaultSubscriber;
12 | import com.fernandocejas.android10.sample.domain.interactor.GetUserList;
13 | import com.fernandocejas.android10.sample.domain.interactor.UseCase;
14 | import com.fernandocejas.android10.sample.presentation.AndroidApplication;
15 | import com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper;
16 | import com.fernandocejas.android10.sample.presentation.model.UserModel;
17 | import com.fernandocejas.android10.sample.presentation.navigation.ActivityNavigator;
18 | import com.fernandocejas.android10.sample.presentation.view.activity.UserDetailsActivity;
19 | import com.fernandocejas.android10.sample.presentation.view.adapter.UsersAdapter;
20 |
21 | import java.util.Collection;
22 | import java.util.List;
23 |
24 | /**
25 | * Created by rocko on 15-11-5.
26 | */
27 | public class UserListViewModel extends LoadingViewModel {
28 | private final static String TAG = UserListViewModel.class.getSimpleName();
29 |
30 | public final ObservableBoolean showContentList = new ObservableBoolean(false);
31 | public final ObservableField usersListAdapter = new ObservableField<>();
32 |
33 | UseCase getUserList = new GetUserList(AndroidApplication.getContext());
34 | UserModelDataMapper userModelDataMapper = new UserModelDataMapper();
35 |
36 |
37 | @BindView
38 | @Override
39 | public void showLoading() {
40 | super.showLoading();
41 | showContentList.set(false);
42 | }
43 |
44 | @BindView
45 | @Override
46 | public void showRetry() {
47 | super.showRetry();
48 | showContentList.set(false);
49 | }
50 |
51 | @BindView
52 | public void showContentList(UsersAdapter usersAdapter) {
53 | showLoading.set(false);
54 | showRetry.set(false);
55 | showContentList.set(true);
56 | usersListAdapter.set(usersAdapter);
57 | }
58 |
59 | @BindView
60 | public void showMoreContent() {
61 | // userAdapter
62 | }
63 |
64 | @Command
65 | public void loadUsersCommand() {
66 | if (showLoading.get()) {
67 | return;
68 | }
69 | showLoading();
70 | getUserList.execute(new DefaultSubscriber>() {
71 | @Override
72 | public void onNext(List users) {
73 | Collection userModelsCollection = userModelDataMapper.transformUsers(users);
74 | UsersAdapter usersAdapter = new UsersAdapter(AndroidApplication.getContext(), userModelsCollection);
75 | usersAdapter.setOnItemClickListener(onUserItemClick());
76 | showContentList(usersAdapter);
77 | }
78 |
79 | @Override
80 | public void onError(Throwable e) {
81 | showRetry();
82 | }
83 |
84 | });
85 | }
86 |
87 | @Override
88 | public View.OnClickListener onRetryClick() {
89 | return new View.OnClickListener() {
90 | @Override
91 | public void onClick(View v) {
92 | loadUsersCommand();
93 |
94 | }
95 | };
96 | }
97 |
98 | public UsersAdapter.OnItemClickListener onUserItemClick() {
99 | return new UsersAdapter.OnItemClickListener() {
100 | @Override
101 | public void onUserItemClicked(UserModel userModel) {
102 | Intent intent = UserDetailsActivity.getCallingIntent(AndroidApplication.getInstance().getCurrentActivity(), userModel.getUserId());
103 | ActivityNavigator.navigateTo(UserDetailsActivity.class, intent);
104 | }
105 | };
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/presentation/src/main/java/com/fernandocejas/android10/sample/presentation/viewmodel/ViewModel.java:
--------------------------------------------------------------------------------
1 | package com.fernandocejas.android10.sample.presentation.viewmodel;
2 |
3 | import android.databinding.BaseObservable;
4 |
5 | import java.lang.annotation.ElementType;
6 | import java.lang.annotation.Retention;
7 | import java.lang.annotation.RetentionPolicy;
8 | import java.lang.annotation.Target;
9 |
10 | /**
11 | * Created by rocko on 15-11-5.
12 | */
13 | public abstract class ViewModel extends BaseObservable{ // TODO: Need Context(Activity)?
14 |
15 | /* Just mark a method in ViewModel */
16 | @Target(ElementType.METHOD)
17 | @Retention(RetentionPolicy.SOURCE)
18 | protected @interface Command {
19 | }
20 |
21 | @Target(ElementType.METHOD)
22 | @Retention(RetentionPolicy.SOURCE)
23 | protected @interface BindView {
24 | }
25 |
26 | // ... InstanceState in ViewModel
27 |
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhengxiaopeng/MVVM_Android-CleanArchitecture/ab6b934fdb93bf47daa37c7c6d541497f6d4d951/presentation/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable-hdpi/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhengxiaopeng/MVVM_Android-CleanArchitecture/ab6b934fdb93bf47daa37c7c6d541497f6d4d951/presentation/src/main/res/drawable-hdpi/logo.png
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhengxiaopeng/MVVM_Android-CleanArchitecture/ab6b934fdb93bf47daa37c7c6d541497f6d4d951/presentation/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhengxiaopeng/MVVM_Android-CleanArchitecture/ab6b934fdb93bf47daa37c7c6d541497f6d4d951/presentation/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhengxiaopeng/MVVM_Android-CleanArchitecture/ab6b934fdb93bf47daa37c7c6d541497f6d4d951/presentation/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/presentation/src/main/res/drawable/selector_item_user.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/presentation/src/main/res/layout/activity_user_details.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/presentation/src/main/res/layout/fragment_user_details.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
10 |
11 |
15 |
16 |
20 |
21 |
25 |
26 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/presentation/src/main/res/layout/fragment_user_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
17 |
18 |
26 |
27 |
31 |
32 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/presentation/src/main/res/layout/home_activity.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
10 |
11 |
12 |
20 |
21 |
28 |
29 |
38 |
39 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/presentation/src/main/res/layout/row_user.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
17 |
18 |
27 |
28 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/presentation/src/main/res/layout/user_list_activity.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
10 |
11 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/presentation/src/main/res/layout/view_progress.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
14 |
20 |
21 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/presentation/src/main/res/layout/view_retry.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
12 |
13 |
14 |
19 |
20 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/presentation/src/main/res/layout/view_user_details.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
10 |
11 |
12 |
13 |
19 |
20 |
27 |
28 |
33 |
34 |
40 |
41 |
45 |
51 |
52 |
56 |
62 |
63 |
67 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/presentation/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
7 | 26sp
8 | 5dp
9 | 140dp
10 |
11 |
12 | 5dp
13 | 20sp
14 | 50dp
15 | 5dp
16 |
17 |
--------------------------------------------------------------------------------
/presentation/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | CleanArchitecture-MVVM
4 | <a href="http://rocko.xyz">http://rocko.xyz</a>
5 |
6 | Users List
7 | User Details
8 |
9 | Load Sample Data
10 | Retry
11 |
12 | Email:
13 | Followers:
14 | Description:
15 |
16 | Non accessible view
17 |
18 | //Exception messages
19 | There was an application error
20 | There is no internet connection
21 | Cannot retrieve user data. Check your internet connection
22 |
23 |
--------------------------------------------------------------------------------
/presentation/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
14 |
15 |
22 |
23 |
32 |
33 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':presentation'
2 | include ':domain'
3 | include ':data'
4 |
--------------------------------------------------------------------------------