Showing preview only (251K chars total). Download the full file or copy to clipboard to get everything.
Repository: android10/Android-CleanArchitecture
Branch: master
Commit: 8ed4222c537e
Files: 116
Total size: 214.4 KB
Directory structure:
gitextract_60j9_cvf/
├── .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/
│ │ │ └── Serializer.java
│ │ ├── entity/
│ │ │ ├── UserEntity.java
│ │ │ └── mapper/
│ │ │ ├── UserEntityDataMapper.java
│ │ │ └── UserEntityJsonMapper.java
│ │ ├── exception/
│ │ │ ├── NetworkConnectionException.java
│ │ │ ├── RepositoryErrorBundle.java
│ │ │ └── UserNotFoundException.java
│ │ ├── executor/
│ │ │ └── JobExecutor.java
│ │ ├── net/
│ │ │ ├── ApiConnection.java
│ │ │ ├── RestApi.java
│ │ │ └── RestApiImpl.java
│ │ └── repository/
│ │ ├── UserDataRepository.java
│ │ └── datasource/
│ │ ├── CloudUserDataStore.java
│ │ ├── DiskUserDataStore.java
│ │ ├── UserDataStore.java
│ │ └── UserDataStoreFactory.java
│ └── test/
│ └── java/
│ └── com/
│ └── fernandocejas/
│ └── android10/
│ └── sample/
│ └── data/
│ ├── ApplicationStub.java
│ ├── ApplicationTestCase.java
│ ├── cache/
│ │ ├── FileManagerTest.java
│ │ └── serializer/
│ │ └── SerializerTest.java
│ ├── entity/
│ │ └── mapper/
│ │ ├── UserEntityDataMapperTest.java
│ │ └── UserEntityJsonMapperTest.java
│ ├── exception/
│ │ └── RepositoryErrorBundleTest.java
│ └── repository/
│ ├── UserDataRepositoryTest.java
│ └── datasource/
│ ├── CloudUserDataStoreTest.java
│ ├── DiskUserDataStoreTest.java
│ └── UserDataStoreFactoryTest.java
├── domain/
│ ├── build.gradle
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── com/
│ │ └── fernandocejas/
│ │ └── android10/
│ │ └── sample/
│ │ └── domain/
│ │ ├── User.java
│ │ ├── exception/
│ │ │ ├── DefaultErrorBundle.java
│ │ │ └── ErrorBundle.java
│ │ ├── executor/
│ │ │ ├── PostExecutionThread.java
│ │ │ └── ThreadExecutor.java
│ │ ├── interactor/
│ │ │ ├── DefaultObserver.java
│ │ │ ├── GetUserDetails.java
│ │ │ ├── GetUserList.java
│ │ │ └── UseCase.java
│ │ └── repository/
│ │ └── UserRepository.java
│ └── test/
│ └── java/
│ └── com/
│ └── fernandocejas/
│ └── android10/
│ └── sample/
│ └── domain/
│ ├── UserTest.java
│ ├── exception/
│ │ └── DefaultErrorBundleTest.java
│ └── interactor/
│ ├── GetUserDetailsTest.java
│ ├── GetUserListTest.java
│ └── UseCaseTest.java
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.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
│ │ ├── presenter/
│ │ │ ├── UserDetailsPresenterTest.java
│ │ │ └── UserListPresenterTest.java
│ │ └── view/
│ │ └── activity/
│ │ ├── UserDetailsActivityTest.java
│ │ └── UserListActivityTest.java
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── com/
│ │ └── fernandocejas/
│ │ └── android10/
│ │ └── sample/
│ │ └── presentation/
│ │ ├── AndroidApplication.java
│ │ ├── UIThread.java
│ │ ├── exception/
│ │ │ └── ErrorMessageFactory.java
│ │ ├── internal/
│ │ │ └── di/
│ │ │ ├── HasComponent.java
│ │ │ ├── PerActivity.java
│ │ │ ├── components/
│ │ │ │ ├── ActivityComponent.java
│ │ │ │ ├── ApplicationComponent.java
│ │ │ │ └── UserComponent.java
│ │ │ └── modules/
│ │ │ ├── ActivityModule.java
│ │ │ ├── ApplicationModule.java
│ │ │ └── UserModule.java
│ │ ├── mapper/
│ │ │ └── UserModelDataMapper.java
│ │ ├── model/
│ │ │ └── UserModel.java
│ │ ├── navigation/
│ │ │ └── Navigator.java
│ │ ├── presenter/
│ │ │ ├── Presenter.java
│ │ │ ├── UserDetailsPresenter.java
│ │ │ └── UserListPresenter.java
│ │ └── view/
│ │ ├── LoadDataView.java
│ │ ├── UserDetailsView.java
│ │ ├── UserListView.java
│ │ ├── activity/
│ │ │ ├── BaseActivity.java
│ │ │ ├── MainActivity.java
│ │ │ ├── UserDetailsActivity.java
│ │ │ └── UserListActivity.java
│ │ ├── adapter/
│ │ │ ├── UsersAdapter.java
│ │ │ └── UsersLayoutManager.java
│ │ ├── component/
│ │ │ └── AutoLoadImageView.java
│ │ └── fragment/
│ │ ├── BaseFragment.java
│ │ ├── UserDetailsFragment.java
│ │ └── UserListFragment.java
│ └── res/
│ ├── drawable/
│ │ └── selector_item_user.xml
│ ├── layout/
│ │ ├── activity_layout.xml
│ │ ├── activity_main.xml
│ │ ├── fragment_user_details.xml
│ │ ├── fragment_user_list.xml
│ │ ├── row_user.xml
│ │ ├── view_progress.xml
│ │ ├── view_retry.xml
│ │ └── view_user_details.xml
│ └── values/
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
└── settings.gradle
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Windows thumbnail db
Thumbs.db
# OSX files
.DS_Store
# built application files
*.apk
*.ap_
# files for the dex VM
*.dex
# Java class files
*.class
# generated files
bin/
gen/
build/
# Local configuration file (sdk path, etc)
local.properties
# Eclipse project files
.classpath
.project
# Android Studio
.idea
.gradle
/*/local.properties
/*/out
/*/*/build
build
/*/*/production
*.iml
*.iws
*.ipr
*~
*.swp
================================================
FILE: .travis.yml
================================================
language: android
jdk: oraclejdk8
android:
components:
- tools
- platform-tools
- tools
- build-tools-27.0.1
- android-26
- extra-google-m2repository
- extra-android-m2repository
licenses:
- 'android-sdk-preview-license-.+'
- 'android-sdk-license-.+'
- 'google-gdk-license-.+'
script:
./gradlew build
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
Android-CleanArchitecture
=========================
## New version available written in Kotlin:
[Architecting Android… Reloaded](https://fernandocejas.com/2018/05/07/architecting-android-reloaded/)
Introduction
-----------------
This is a sample app that is part of a blog post I have written about how to architect android application using the Uncle Bob's clean architecture approach.
[Architecting Android…The clean way?](http://fernandocejas.com/2014/09/03/architecting-android-the-clean-way/)
[Architecting Android…The evolution](http://fernandocejas.com/2015/07/18/architecting-android-the-evolution/)
[Tasting Dagger 2 on Android](http://fernandocejas.com/2015/04/11/tasting-dagger-2-on-android/)
[Clean Architecture…Dynamic Parameters in Use Cases](http://fernandocejas.com/2016/12/24/clean-architecture-dynamic-parameters-in-use-cases/)
[Demo video of this sample](http://youtu.be/XSjV4sG3ni0)
Clean architecture
-----------------

Architectural approach
-----------------

Architectural reactive approach
-----------------

Local Development
-----------------
Here are some useful Gradle/adb commands for executing this example:
* `./gradlew clean build` - Build the entire example and execute unit and integration tests plus lint check.
* `./gradlew installDebug` - Install the debug apk on the current connected device.
* `./gradlew runUnitTests` - Execute domain and data layer tests (both unit and integration).
* `./gradlew runAcceptanceTests` - Execute espresso and instrumentation acceptance tests.
Discussions
-----------------
Refer to the issues section: https://github.com/android10/Android-CleanArchitecture/issues
Code style
-----------
Here you can download and install the java codestyle.
https://github.com/android10/java-code-styles
License
--------
Copyright 2018 Fernando Cejas
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

[](https://android-arsenal.com/details/3/909)
<a href="https://www.buymeacoffee.com/android10" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: auto !important;width: auto !important;" ></a>
================================================
FILE: build.gradle
================================================
apply from: 'buildsystem/ci.gradle'
apply from: 'buildsystem/dependencies.gradle'
buildscript {
repositories {
jcenter()
mavenCentral()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
// classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
}
}
allprojects {
ext {
androidApplicationId = 'com.fernanependocejas.android10.sample.presentation'
androidVersionCode = 1
androidVersionName = "1.0"
testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner"
testApplicationId = 'com.fernandocejas.android10.sample.presentation.test'
}
}
task runDomainUnitTests(dependsOn: [':domain:test']) {
description 'Run unit tests for the domain layer.'
}
task runDataUnitTests(dependsOn: [':data:cleanTestDebugUnitTest', ':data:testDebugUnitTest']) {
description 'Run unit tests for the data layer.'
}
task runUnitTests(dependsOn: ['runDomainUnitTests', 'runDataUnitTests']) {
description 'Run unit tests for both domain and data layers.'
}
task runAcceptanceTests(dependsOn: [':presentation:connectedAndroidTest']) {
description 'Run application acceptance tests.'
}
================================================
FILE: buildsystem/ci.gradle
================================================
def ciServer = 'TRAVIS'
def executingOnCI = "true".equals(System.getenv(ciServer))
// Since for CI we always do full clean builds, we don't want to pre-dex
// See http://tools.android.com/tech-docs/new-build-system/tips
subprojects {
project.plugins.whenPluginAdded { plugin ->
if ('com.android.build.gradle.AppPlugin'.equals(plugin.class.name) ||
'com.android.build.gradle.LibraryPlugin'.equals(plugin.class.name)) {
project.android.dexOptions.preDexLibraries = !executingOnCI
}
}
}
================================================
FILE: buildsystem/dependencies.gradle
================================================
allprojects {
repositories {
jcenter()
}
}
ext {
//Android
androidBuildToolsVersion = "27.0.1"
androidMinSdkVersion = 15
androidTargetSdkVersion = 26
androidCompileSdkVersion = 26
//Libraries
daggerVersion = '2.8'
butterKnifeVersion = '7.0.1'
recyclerViewVersion = '25.4.0'
rxJavaVersion = '2.0.2'
rxAndroidVersion = '2.0.1'
javaxAnnotationVersion = '1.0'
javaxInjectVersion = '1'
gsonVersion = '2.3'
okHttpVersion = '2.5.0'
androidAnnotationsVersion = '25.4.0'
arrowVersion = '1.0.0'
//Testing
robolectricVersion = '3.1.1'
jUnitVersion = '4.12'
assertJVersion = '1.7.1'
mockitoVersion = '1.9.5'
dexmakerVersion = '1.0'
espressoVersion = '3.0.1'
testingSupportLibVersion = '0.1'
//Development
leakCanaryVersion = '1.3.1'
presentationDependencies = [
daggerCompiler: "com.google.dagger:dagger-compiler:${daggerVersion}",
dagger: "com.google.dagger:dagger:${daggerVersion}",
butterKnife: "com.jakewharton:butterknife:${butterKnifeVersion}",
recyclerView: "com.android.support:recyclerview-v7:${recyclerViewVersion}",
rxJava: "io.reactivex.rxjava2:rxjava:${rxJavaVersion}",
rxAndroid: "io.reactivex.rxjava2:rxandroid:${rxAndroidVersion}",
javaxAnnotation: "javax.annotation:jsr250-api:${javaxAnnotationVersion}"
]
presentationTestDependencies = [
mockito: "org.mockito:mockito-core:${mockitoVersion}",
dexmaker: "com.google.dexmaker:dexmaker:${dexmakerVersion}",
dexmakerMockito: "com.google.dexmaker:dexmaker-mockito:${dexmakerVersion}",
espresso: "com.android.support.test.espresso:espresso-core:${espressoVersion}",
testingSupportLib: "com.android.support.test:testing-support-lib:${testingSupportLibVersion}",
]
domainDependencies = [
javaxAnnotation: "javax.annotation:jsr250-api:${javaxAnnotationVersion}",
javaxInject: "javax.inject:javax.inject:${javaxInjectVersion}",
rxJava: "io.reactivex.rxjava2:rxjava:${rxJavaVersion}",
arrow: "com.fernandocejas:arrow:${arrowVersion}"
]
domainTestDependencies = [
junit: "junit:junit:${jUnitVersion}",
mockito: "org.mockito:mockito-core:${mockitoVersion}",
assertj: "org.assertj:assertj-core:${assertJVersion}"
]
dataDependencies = [
daggerCompiler: "com.google.dagger:dagger-compiler:${daggerVersion}",
dagger: "com.google.dagger:dagger:${daggerVersion}",
okHttp: "com.squareup.okhttp:okhttp:${okHttpVersion}",
gson: "com.google.code.gson:gson:${gsonVersion}",
rxJava: "io.reactivex.rxjava2:rxjava:${rxJavaVersion}",
rxAndroid: "io.reactivex.rxjava2:rxandroid:${rxAndroidVersion}",
javaxAnnotation: "javax.annotation:jsr250-api:${javaxAnnotationVersion}",
javaxInject: "javax.inject:javax.inject:${javaxInjectVersion}",
androidAnnotations: "com.android.support:support-annotations:${androidAnnotationsVersion}"
]
dataTestDependencies = [
junit: "junit:junit:${jUnitVersion}",
assertj: "org.assertj:assertj-core:${assertJVersion}",
mockito: "org.mockito:mockito-core:${mockitoVersion}",
robolectric: "org.robolectric:robolectric:${robolectricVersion}",
]
developmentDependencies = [
leakCanary: "com.squareup.leakcanary:leakcanary-android:${leakCanaryVersion}",
]
}
================================================
FILE: data/.gitignore
================================================
/build
================================================
FILE: data/build.gradle
================================================
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'me.tatarka:gradle-retrolambda:3.7.0'
}
}
apply plugin: 'com.android.library'
//apply plugin: 'com.neenbedankt.android-apt'
apply plugin: 'me.tatarka.retrolambda'
android {
defaultPublishConfig "debug"
def globalConfiguration = rootProject.extensions.getByName("ext")
compileSdkVersion globalConfiguration.getAt("androidCompileSdkVersion")
buildToolsVersion globalConfiguration.getAt("androidBuildToolsVersion")
defaultConfig {
minSdkVersion globalConfiguration.getAt("androidMinSdkVersion")
targetSdkVersion globalConfiguration.getAt("androidTargetSdkVersion")
versionCode globalConfiguration.getAt("androidVersionCode")
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
packagingOptions {
exclude 'LICENSE.txt'
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/ASL2.0'
exclude 'META-INF/NOTICE'
exclude 'META-INF/LICENSE'
}
lintOptions {
quiet true
abortOnError false
ignoreWarnings true
disable 'InvalidPackage' // Some libraries have issues with this
disable 'OldTargetApi' // Due to Robolectric that modifies the manifest when running tests
}
}
dependencies {
def dataDependencies = rootProject.ext.dataDependencies
def testDependencies = rootProject.ext.dataTestDependencies
implementation project(':domain')
compileOnly dataDependencies.javaxAnnotation
implementation dataDependencies.javaxInject
implementation dataDependencies.okHttp
implementation dataDependencies.gson
implementation dataDependencies.rxJava
implementation dataDependencies.rxAndroid
implementation dataDependencies.androidAnnotations
testImplementation testDependencies.junit
testImplementation testDependencies.assertj
testImplementation testDependencies.mockito
testImplementation testDependencies.robolectric
}
repositories {
google()
}
================================================
FILE: data/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/fcejas/Software/SDKs/android-sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: data/src/main/AndroidManifest.xml
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.fernandocejas.android10.sample.data">
<application android:allowBackup="true" />
</manifest>
================================================
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/cache/FileManager.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.cache;
import android.content.Context;
import android.content.SharedPreferences;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* Helper class to do operations on regular files/directories.
*/
@Singleton
public class FileManager {
@Inject
FileManager() {}
/**
* Writes a file to Disk.
* This is an I/O operation and this method executes in the main thread, so it is recommended to
* perform this operation using another thread.
*
* @param file The file to write to Disk.
*/
void writeToFile(File file, String fileContent) {
if (!file.exists()) {
try {
final FileWriter writer = new FileWriter(file);
writer.write(fileContent);
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* Reads a content from a file.
* This is an I/O operation and this method executes in the main thread, so it is recommended to
* perform the operation using another thread.
*
* @param file The file to read from.
* @return A string with the content of the file.
*/
String readFileContent(File file) {
final StringBuilder fileContentBuilder = new StringBuilder();
if (file.exists()) {
String stringLine;
try {
final FileReader fileReader = new FileReader(file);
final BufferedReader bufferedReader = new BufferedReader(fileReader);
while ((stringLine = bufferedReader.readLine()) != null) {
fileContentBuilder.append(stringLine).append("\n");
}
bufferedReader.close();
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return fileContentBuilder.toString();
}
/**
* Returns a boolean indicating whether this file can be found on the underlying file system.
*
* @param file The file to check existence.
* @return true if this file exists, false otherwise.
*/
boolean exists(File file) {
return file.exists();
}
/**
* Warning: Deletes the content of a directory.
* This is an I/O operation and this method executes in the main thread, so it is recommended to
* perform the operation using another thread.
*
* @param directory The directory which its content will be deleted.
*/
boolean clearDirectory(File directory) {
boolean result = false;
if (directory.exists()) {
for (File file : directory.listFiles()) {
result = file.delete();
}
}
return result;
}
/**
* Write a value to a user preferences file.
*
* @param context {@link android.content.Context} to retrieve android user preferences.
* @param preferenceFileName A file name reprensenting where data will be written to.
* @param key A string for the key that will be used to retrieve the value in the future.
* @param value A long representing the value to be inserted.
*/
void writeToPreferences(Context context, String preferenceFileName, String key,
long value) {
final SharedPreferences sharedPreferences = context.getSharedPreferences(preferenceFileName,
Context.MODE_PRIVATE);
final SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putLong(key, value);
editor.apply();
}
/**
* Get a value from a user preferences file.
*
* @param context {@link android.content.Context} to retrieve android user preferences.
* @param preferenceFileName A file name representing where data will be get from.
* @param key A key that will be used to retrieve the value from the preference file.
* @return A long representing the value retrieved from the preferences file.
*/
long getFromPreferences(Context context, String preferenceFileName, String key) {
final SharedPreferences sharedPreferences = context.getSharedPreferences(preferenceFileName,
Context.MODE_PRIVATE);
return sharedPreferences.getLong(key, 0);
}
}
================================================
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCache.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.cache;
import com.fernandocejas.android10.sample.data.entity.UserEntity;
import io.reactivex.Observable;
/**
* An interface representing a user Cache.
*/
public interface UserCache {
/**
* Gets an {@link Observable} which will emit a {@link UserEntity}.
*
* @param userId The user id to retrieve data.
*/
Observable<UserEntity> get(final int userId);
/**
* Puts and element into the cache.
*
* @param userEntity Element to insert in the cache.
*/
void put(UserEntity userEntity);
/**
* Checks if an element (User) exists in the cache.
*
* @param userId The id used to look for inside the cache.
* @return true if the element is cached, otherwise false.
*/
boolean isCached(final int userId);
/**
* Checks if the cache is expired.
*
* @return true, the cache is expired, otherwise false.
*/
boolean isExpired();
/**
* Evict all elements of the cache.
*/
void evictAll();
}
================================================
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCacheImpl.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.cache;
import android.content.Context;
import com.fernandocejas.android10.sample.data.cache.serializer.Serializer;
import com.fernandocejas.android10.sample.data.entity.UserEntity;
import com.fernandocejas.android10.sample.data.exception.UserNotFoundException;
import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;
import io.reactivex.Observable;
import java.io.File;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* {@link UserCache} implementation.
*/
@Singleton
public class UserCacheImpl implements UserCache {
private static final String SETTINGS_FILE_NAME = "com.fernandocejas.android10.SETTINGS";
private static final String SETTINGS_KEY_LAST_CACHE_UPDATE = "last_cache_update";
private static final String DEFAULT_FILE_NAME = "user_";
private static final long EXPIRATION_TIME = 60 * 10 * 1000;
private final Context context;
private final File cacheDir;
private final Serializer serializer;
private final FileManager fileManager;
private final ThreadExecutor threadExecutor;
/**
* Constructor of the class {@link UserCacheImpl}.
*
* @param context A
* @param serializer {@link Serializer} for object serialization.
* @param fileManager {@link FileManager} for saving serialized objects to the file system.
*/
@Inject UserCacheImpl(Context context, Serializer serializer,
FileManager fileManager, ThreadExecutor executor) {
if (context == null || serializer == null || fileManager == null || executor == null) {
throw new IllegalArgumentException("Invalid null parameter");
}
this.context = context.getApplicationContext();
this.cacheDir = this.context.getCacheDir();
this.serializer = serializer;
this.fileManager = fileManager;
this.threadExecutor = executor;
}
@Override public Observable<UserEntity> get(final int userId) {
return Observable.create(emitter -> {
final File userEntityFile = UserCacheImpl.this.buildFile(userId);
final String fileContent = UserCacheImpl.this.fileManager.readFileContent(userEntityFile);
final UserEntity userEntity =
UserCacheImpl.this.serializer.deserialize(fileContent, UserEntity.class);
if (userEntity != null) {
emitter.onNext(userEntity);
emitter.onComplete();
} else {
emitter.onError(new UserNotFoundException());
}
});
}
@Override public void put(UserEntity userEntity) {
if (userEntity != null) {
final File userEntityFile = this.buildFile(userEntity.getUserId());
if (!isCached(userEntity.getUserId())) {
final String jsonString = this.serializer.serialize(userEntity, UserEntity.class);
this.executeAsynchronously(new CacheWriter(this.fileManager, userEntityFile, jsonString));
setLastCacheUpdateTimeMillis();
}
}
}
@Override public boolean isCached(int userId) {
final File userEntityFile = this.buildFile(userId);
return this.fileManager.exists(userEntityFile);
}
@Override public boolean isExpired() {
long currentTime = System.currentTimeMillis();
long lastUpdateTime = this.getLastCacheUpdateTimeMillis();
boolean expired = ((currentTime - lastUpdateTime) > EXPIRATION_TIME);
if (expired) {
this.evictAll();
}
return expired;
}
@Override public void evictAll() {
this.executeAsynchronously(new CacheEvictor(this.fileManager, this.cacheDir));
}
/**
* Build a file, used to be inserted in the disk cache.
*
* @param userId The id user to build the file.
* @return A valid file.
*/
private File buildFile(int userId) {
final StringBuilder fileNameBuilder = new StringBuilder();
fileNameBuilder.append(this.cacheDir.getPath());
fileNameBuilder.append(File.separator);
fileNameBuilder.append(DEFAULT_FILE_NAME);
fileNameBuilder.append(userId);
return new File(fileNameBuilder.toString());
}
/**
* Set in millis, the last time the cache was accessed.
*/
private void setLastCacheUpdateTimeMillis() {
final long currentMillis = System.currentTimeMillis();
this.fileManager.writeToPreferences(this.context, SETTINGS_FILE_NAME,
SETTINGS_KEY_LAST_CACHE_UPDATE, currentMillis);
}
/**
* Get in millis, the last time the cache was accessed.
*/
private long getLastCacheUpdateTimeMillis() {
return this.fileManager.getFromPreferences(this.context, SETTINGS_FILE_NAME,
SETTINGS_KEY_LAST_CACHE_UPDATE);
}
/**
* Executes a {@link Runnable} in another Thread.
*
* @param runnable {@link Runnable} to execute
*/
private void executeAsynchronously(Runnable runnable) {
this.threadExecutor.execute(runnable);
}
/**
* {@link Runnable} class for writing to disk.
*/
private static class CacheWriter implements Runnable {
private final FileManager fileManager;
private final File fileToWrite;
private final String fileContent;
CacheWriter(FileManager fileManager, File fileToWrite, String fileContent) {
this.fileManager = fileManager;
this.fileToWrite = fileToWrite;
this.fileContent = fileContent;
}
@Override public void run() {
this.fileManager.writeToFile(fileToWrite, fileContent);
}
}
/**
* {@link Runnable} class for evicting all the cached files
*/
private static class CacheEvictor implements Runnable {
private final FileManager fileManager;
private final File cacheDir;
CacheEvictor(FileManager fileManager, File cacheDir) {
this.fileManager = fileManager;
this.cacheDir = cacheDir;
}
@Override public void run() {
this.fileManager.clearDirectory(this.cacheDir);
}
}
}
================================================
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/cache/serializer/Serializer.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.cache.serializer;
import com.google.gson.Gson;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* Json Serializer/Deserializer.
*/
@Singleton
public class Serializer {
private final Gson gson = new Gson();
@Inject Serializer() {}
/**
* Serialize an object to Json.
*
* @param object to serialize.
*/
public String serialize(Object object, Class clazz) {
return gson.toJson(object, clazz);
}
/**
* Deserialize a json representation of an object.
*
* @param string A json string to deserialize.
*/
public <T> T deserialize(String string, Class<T> clazz) {
return gson.fromJson(string, clazz);
}
}
================================================
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/entity/UserEntity.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.entity;
import com.google.gson.annotations.SerializedName;
/**
* User Entity used in the data layer.
*/
public class UserEntity {
@SerializedName("id")
private int userId;
@SerializedName("cover_url")
private String coverUrl;
@SerializedName("full_name")
private String fullname;
@SerializedName("description")
private String description;
@SerializedName("followers")
private int followers;
@SerializedName("email")
private String email;
public UserEntity() {
//empty
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getCoverUrl() {
return coverUrl;
}
public String getFullname() {
return fullname;
}
public void setFullname(String fullname) {
this.fullname = fullname;
}
public String getDescription() {
return description;
}
public int getFollowers() {
return followers;
}
public String getEmail() {
return email;
}
}
================================================
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityDataMapper.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.entity.mapper;
import com.fernandocejas.android10.sample.data.entity.UserEntity;
import com.fernandocejas.android10.sample.domain.User;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* Mapper class used to transform {@link UserEntity} (in the data layer) to {@link User} in the
* domain layer.
*/
@Singleton
public class UserEntityDataMapper {
@Inject
UserEntityDataMapper() {}
/**
* Transform a {@link UserEntity} into an {@link User}.
*
* @param userEntity Object to be transformed.
* @return {@link User} if valid {@link UserEntity} otherwise null.
*/
public User transform(UserEntity userEntity) {
User user = null;
if (userEntity != null) {
user = new User(userEntity.getUserId());
user.setCoverUrl(userEntity.getCoverUrl());
user.setFullName(userEntity.getFullname());
user.setDescription(userEntity.getDescription());
user.setFollowers(userEntity.getFollowers());
user.setEmail(userEntity.getEmail());
}
return user;
}
/**
* Transform a List of {@link UserEntity} into a Collection of {@link User}.
*
* @param userEntityCollection Object Collection to be transformed.
* @return {@link User} if valid {@link UserEntity} otherwise null.
*/
public List<User> transform(Collection<UserEntity> userEntityCollection) {
final List<User> userList = new ArrayList<>(20);
for (UserEntity userEntity : userEntityCollection) {
final User user = transform(userEntity);
if (user != null) {
userList.add(user);
}
}
return userList;
}
}
================================================
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityJsonMapper.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.entity.mapper;
import com.fernandocejas.android10.sample.data.entity.UserEntity;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.List;
import javax.inject.Inject;
/**
* Class used to transform from Strings representing json to valid objects.
*/
public class UserEntityJsonMapper {
private final Gson gson;
@Inject
public UserEntityJsonMapper() {
this.gson = new Gson();
}
/**
* Transform from valid json string to {@link UserEntity}.
*
* @param userJsonResponse A json representing a user profile.
* @return {@link UserEntity}.
* @throws com.google.gson.JsonSyntaxException if the json string is not a valid json structure.
*/
public UserEntity transformUserEntity(String userJsonResponse) throws JsonSyntaxException {
final Type userEntityType = new TypeToken<UserEntity>() {}.getType();
return this.gson.fromJson(userJsonResponse, userEntityType);
}
/**
* Transform from valid json string to List of {@link UserEntity}.
*
* @param userListJsonResponse A json representing a collection of users.
* @return List of {@link UserEntity}.
* @throws com.google.gson.JsonSyntaxException if the json string is not a valid json structure.
*/
public List<UserEntity> transformUserEntityCollection(String userListJsonResponse)
throws JsonSyntaxException {
final Type listOfUserEntityType = new TypeToken<List<UserEntity>>() {}.getType();
return this.gson.fromJson(userListJsonResponse, listOfUserEntityType);
}
}
================================================
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/exception/NetworkConnectionException.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.exception;
/**
* Exception throw by the application when a there is a network connection exception.
*/
public class NetworkConnectionException extends Exception {
public NetworkConnectionException() {
super();
}
public NetworkConnectionException(final Throwable cause) {
super(cause);
}
}
================================================
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundle.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.exception;
import com.fernandocejas.android10.sample.domain.exception.ErrorBundle;
/**
* Wrapper around Exceptions used to manage errors in the repository.
*/
class RepositoryErrorBundle implements ErrorBundle {
private final Exception exception;
RepositoryErrorBundle(Exception exception) {
this.exception = exception;
}
@Override
public Exception getException() {
return exception;
}
@Override
public String getErrorMessage() {
String message = "";
if (this.exception != null) {
message = this.exception.getMessage();
}
return message;
}
}
================================================
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/exception/UserNotFoundException.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.exception;
/**
* Exception throw by the application when a User search can't return a valid result.
*/
public class UserNotFoundException extends Exception {
public UserNotFoundException() {
super();
}
}
================================================
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/executor/JobExecutor.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.executor;
import android.support.annotation.NonNull;
import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* Decorated {@link java.util.concurrent.ThreadPoolExecutor}
*/
@Singleton
public class JobExecutor implements ThreadExecutor {
private final ThreadPoolExecutor threadPoolExecutor;
@Inject
JobExecutor() {
this.threadPoolExecutor = new ThreadPoolExecutor(3, 5, 10, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(), new JobThreadFactory());
}
@Override public void execute(@NonNull Runnable runnable) {
this.threadPoolExecutor.execute(runnable);
}
private static class JobThreadFactory implements ThreadFactory {
private int counter = 0;
@Override public Thread newThread(@NonNull Runnable runnable) {
return new Thread(runnable, "android_" + counter++);
}
}
}
================================================
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/net/ApiConnection.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.net;
import android.support.annotation.Nullable;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
/**
* Api Connection class used to retrieve data from the cloud.
* Implements {@link java.util.concurrent.Callable} so when executed asynchronously can
* return a value.
*/
class ApiConnection implements Callable<String> {
private static final String CONTENT_TYPE_LABEL = "Content-Type";
private static final String CONTENT_TYPE_VALUE_JSON = "application/json; charset=utf-8";
private URL url;
private String response;
private ApiConnection(String url) throws MalformedURLException {
this.url = new URL(url);
}
static ApiConnection createGET(String url) throws MalformedURLException {
return new ApiConnection(url);
}
/**
* Do a request to an api synchronously.
* It should not be executed in the main thread of the application.
*
* @return A string response
*/
@Nullable
String requestSyncCall() {
connectToApi();
return response;
}
private void connectToApi() {
OkHttpClient okHttpClient = this.createClient();
final Request request = new Request.Builder()
.url(this.url)
.addHeader(CONTENT_TYPE_LABEL, CONTENT_TYPE_VALUE_JSON)
.get()
.build();
try {
this.response = okHttpClient.newCall(request).execute().body().string();
} catch (IOException e) {
e.printStackTrace();
}
}
private OkHttpClient createClient() {
final OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setReadTimeout(10000, TimeUnit.MILLISECONDS);
okHttpClient.setConnectTimeout(15000, TimeUnit.MILLISECONDS);
return okHttpClient;
}
@Override public String call() throws Exception {
return requestSyncCall();
}
}
================================================
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApi.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.net;
import com.fernandocejas.android10.sample.data.entity.UserEntity;
import io.reactivex.Observable;
import java.util.List;
/**
* RestApi for retrieving data from the network.
*/
public interface RestApi {
String API_BASE_URL =
"https://raw.githubusercontent.com/android10/Sample-Data/master/Android-CleanArchitecture/";
/** Api url for getting all users */
String API_URL_GET_USER_LIST = API_BASE_URL + "users.json";
/** Api url for getting a user profile: Remember to concatenate id + 'json' */
String API_URL_GET_USER_DETAILS = API_BASE_URL + "user_";
/**
* Retrieves an {@link Observable} which will emit a List of {@link UserEntity}.
*/
Observable<List<UserEntity>> userEntityList();
/**
* Retrieves an {@link Observable} which will emit a {@link UserEntity}.
*
* @param userId The user id used to get user data.
*/
Observable<UserEntity> userEntityById(final int userId);
}
================================================
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApiImpl.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.net;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import com.fernandocejas.android10.sample.data.entity.UserEntity;
import com.fernandocejas.android10.sample.data.entity.mapper.UserEntityJsonMapper;
import com.fernandocejas.android10.sample.data.exception.NetworkConnectionException;
import io.reactivex.Observable;
import java.net.MalformedURLException;
import java.util.List;
/**
* {@link RestApi} implementation for retrieving data from the network.
*/
public class RestApiImpl implements RestApi {
private final Context context;
private final UserEntityJsonMapper userEntityJsonMapper;
/**
* Constructor of the class
*
* @param context {@link android.content.Context}.
* @param userEntityJsonMapper {@link UserEntityJsonMapper}.
*/
public RestApiImpl(Context context, UserEntityJsonMapper userEntityJsonMapper) {
if (context == null || userEntityJsonMapper == null) {
throw new IllegalArgumentException("The constructor parameters cannot be null!!!");
}
this.context = context.getApplicationContext();
this.userEntityJsonMapper = userEntityJsonMapper;
}
@Override public Observable<List<UserEntity>> userEntityList() {
return Observable.create(emitter -> {
if (isThereInternetConnection()) {
try {
String responseUserEntities = getUserEntitiesFromApi();
if (responseUserEntities != null) {
emitter.onNext(userEntityJsonMapper.transformUserEntityCollection(
responseUserEntities));
emitter.onComplete();
} else {
emitter.onError(new NetworkConnectionException());
}
} catch (Exception e) {
emitter.onError(new NetworkConnectionException(e.getCause()));
}
} else {
emitter.onError(new NetworkConnectionException());
}
});
}
@Override public Observable<UserEntity> userEntityById(final int userId) {
return Observable.create(emitter -> {
if (isThereInternetConnection()) {
try {
String responseUserDetails = getUserDetailsFromApi(userId);
if (responseUserDetails != null) {
emitter.onNext(userEntityJsonMapper.transformUserEntity(responseUserDetails));
emitter.onComplete();
} else {
emitter.onError(new NetworkConnectionException());
}
} catch (Exception e) {
emitter.onError(new NetworkConnectionException(e.getCause()));
}
} else {
emitter.onError(new NetworkConnectionException());
}
});
}
private String getUserEntitiesFromApi() throws MalformedURLException {
return ApiConnection.createGET(API_URL_GET_USER_LIST).requestSyncCall();
}
private String getUserDetailsFromApi(int userId) throws MalformedURLException {
String apiUrl = API_URL_GET_USER_DETAILS + userId + ".json";
return ApiConnection.createGET(apiUrl).requestSyncCall();
}
/**
* Checks if the device has any active internet connection.
*
* @return true device with internet connection, otherwise false.
*/
private boolean isThereInternetConnection() {
boolean isConnected;
ConnectivityManager connectivityManager =
(ConnectivityManager) this.context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
isConnected = (networkInfo != null && networkInfo.isConnectedOrConnecting());
return isConnected;
}
}
================================================
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/repository/UserDataRepository.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.repository;
import com.fernandocejas.android10.sample.data.entity.mapper.UserEntityDataMapper;
import com.fernandocejas.android10.sample.data.repository.datasource.UserDataStore;
import com.fernandocejas.android10.sample.data.repository.datasource.UserDataStoreFactory;
import com.fernandocejas.android10.sample.domain.User;
import com.fernandocejas.android10.sample.domain.repository.UserRepository;
import io.reactivex.Observable;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* {@link UserRepository} for retrieving user data.
*/
@Singleton
public class UserDataRepository implements UserRepository {
private final UserDataStoreFactory userDataStoreFactory;
private final UserEntityDataMapper userEntityDataMapper;
/**
* Constructs a {@link UserRepository}.
*
* @param dataStoreFactory A factory to construct different data source implementations.
* @param userEntityDataMapper {@link UserEntityDataMapper}.
*/
@Inject
UserDataRepository(UserDataStoreFactory dataStoreFactory,
UserEntityDataMapper userEntityDataMapper) {
this.userDataStoreFactory = dataStoreFactory;
this.userEntityDataMapper = userEntityDataMapper;
}
@Override public Observable<List<User>> users() {
//we always get all users from the cloud
final UserDataStore userDataStore = this.userDataStoreFactory.createCloudDataStore();
return userDataStore.userEntityList().map(this.userEntityDataMapper::transform);
}
@Override public Observable<User> user(int userId) {
final UserDataStore userDataStore = this.userDataStoreFactory.create(userId);
return userDataStore.userEntityDetails(userId).map(this.userEntityDataMapper::transform);
}
}
================================================
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/CloudUserDataStore.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.repository.datasource;
import com.fernandocejas.android10.sample.data.cache.UserCache;
import com.fernandocejas.android10.sample.data.entity.UserEntity;
import com.fernandocejas.android10.sample.data.net.RestApi;
import io.reactivex.Observable;
import java.util.List;
/**
* {@link UserDataStore} implementation based on connections to the api (Cloud).
*/
class CloudUserDataStore implements UserDataStore {
private final RestApi restApi;
private final UserCache userCache;
/**
* Construct a {@link UserDataStore} based on connections to the api (Cloud).
*
* @param restApi The {@link RestApi} implementation to use.
* @param userCache A {@link UserCache} to cache data retrieved from the api.
*/
CloudUserDataStore(RestApi restApi, UserCache userCache) {
this.restApi = restApi;
this.userCache = userCache;
}
@Override public Observable<List<UserEntity>> userEntityList() {
return this.restApi.userEntityList();
}
@Override public Observable<UserEntity> userEntityDetails(final int userId) {
return this.restApi.userEntityById(userId).doOnNext(CloudUserDataStore.this.userCache::put);
}
}
================================================
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/DiskUserDataStore.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.repository.datasource;
import com.fernandocejas.android10.sample.data.cache.UserCache;
import com.fernandocejas.android10.sample.data.entity.UserEntity;
import io.reactivex.Observable;
import java.util.List;
/**
* {@link UserDataStore} implementation based on file system data store.
*/
class DiskUserDataStore implements UserDataStore {
private final UserCache userCache;
/**
* Construct a {@link UserDataStore} based file system data store.
*
* @param userCache A {@link UserCache} to cache data retrieved from the api.
*/
DiskUserDataStore(UserCache userCache) {
this.userCache = userCache;
}
@Override public Observable<List<UserEntity>> userEntityList() {
//TODO: implement simple cache for storing/retrieving collections of users.
throw new UnsupportedOperationException("Operation is not available!!!");
}
@Override public Observable<UserEntity> userEntityDetails(final int userId) {
return this.userCache.get(userId);
}
}
================================================
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStore.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.repository.datasource;
import com.fernandocejas.android10.sample.data.entity.UserEntity;
import io.reactivex.Observable;
import java.util.List;
/**
* Interface that represents a data store from where data is retrieved.
*/
public interface UserDataStore {
/**
* Get an {@link Observable} which will emit a List of {@link UserEntity}.
*/
Observable<List<UserEntity>> userEntityList();
/**
* Get an {@link Observable} which will emit a {@link UserEntity} by its id.
*
* @param userId The id to retrieve user data.
*/
Observable<UserEntity> userEntityDetails(final int userId);
}
================================================
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStoreFactory.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.repository.datasource;
import android.content.Context;
import android.support.annotation.NonNull;
import com.fernandocejas.android10.sample.data.cache.UserCache;
import com.fernandocejas.android10.sample.data.entity.mapper.UserEntityJsonMapper;
import com.fernandocejas.android10.sample.data.net.RestApi;
import com.fernandocejas.android10.sample.data.net.RestApiImpl;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* Factory that creates different implementations of {@link UserDataStore}.
*/
@Singleton
public class UserDataStoreFactory {
private final Context context;
private final UserCache userCache;
@Inject
UserDataStoreFactory(@NonNull Context context, @NonNull UserCache userCache) {
this.context = context.getApplicationContext();
this.userCache = userCache;
}
/**
* Create {@link UserDataStore} from a user id.
*/
public UserDataStore create(int userId) {
UserDataStore userDataStore;
if (!this.userCache.isExpired() && this.userCache.isCached(userId)) {
userDataStore = new DiskUserDataStore(this.userCache);
} else {
userDataStore = createCloudDataStore();
}
return userDataStore;
}
/**
* Create {@link UserDataStore} to retrieve data from the Cloud.
*/
public UserDataStore createCloudDataStore() {
final UserEntityJsonMapper userEntityJsonMapper = new UserEntityJsonMapper();
final RestApi restApi = new RestApiImpl(this.context, userEntityJsonMapper);
return new CloudUserDataStore(restApi, this.userCache);
}
}
================================================
FILE: data/src/test/java/com/fernandocejas/android10/sample/data/ApplicationStub.java
================================================
/**
* Copyright (C) 2015 android10.org Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data;
import android.app.Application;
public class ApplicationStub extends Application {}
================================================
FILE: data/src/test/java/com/fernandocejas/android10/sample/data/ApplicationTestCase.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data;
import android.content.Context;
import java.io.File;
import org.junit.Rule;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
/**
* Base class for Robolectric data layer tests.
* Inherit from this class to create a test.
*/
@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class, application = ApplicationStub.class, sdk = 21)
public abstract class ApplicationTestCase {
@Rule public TestRule injectMocksRule = (base, description) -> {
MockitoAnnotations.initMocks(ApplicationTestCase.this);
return base;
};
public static Context context() {
return RuntimeEnvironment.application;
}
public static File cacheDir() {
return RuntimeEnvironment.application.getCacheDir();
}
}
================================================
FILE: data/src/test/java/com/fernandocejas/android10/sample/data/cache/FileManagerTest.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.cache;
import com.fernandocejas.android10.sample.data.ApplicationTestCase;
import java.io.File;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
public class FileManagerTest extends ApplicationTestCase {
private FileManager fileManager;
@Before
public void setUp() {
fileManager = new FileManager();
}
@After
public void tearDown() {
if (cacheDir() != null) {
fileManager.clearDirectory(cacheDir());
}
}
@Test
public void testWriteToFile() {
File fileToWrite = createDummyFile();
String fileContent = "content";
fileManager.writeToFile(fileToWrite, fileContent);
assertThat(fileToWrite.exists(), is(true));
}
@Test
public void testFileContent() {
File fileToWrite = createDummyFile();
String fileContent = "content\n";
fileManager.writeToFile(fileToWrite, fileContent);
String expectedContent = fileManager.readFileContent(fileToWrite);
assertThat(expectedContent, is(equalTo(fileContent)));
}
private File createDummyFile() {
String dummyFilePath = cacheDir().getPath() + File.separator + "dummyFile";
return new File(dummyFilePath);
}
}
================================================
FILE: data/src/test/java/com/fernandocejas/android10/sample/data/cache/serializer/SerializerTest.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.cache.serializer;
import com.fernandocejas.android10.sample.data.entity.UserEntity;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
@RunWith(MockitoJUnitRunner.class)
public class SerializerTest {
private static final String JSON_RESPONSE = "{\n"
+ " \"id\": 1,\n"
+ " \"cover_url\": \"http://www.android10.org/myapi/cover_1.jpg\",\n"
+ " \"full_name\": \"Simon Hill\",\n"
+ " \"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"
+ " \"followers\": 7484,\n"
+ " \"email\": \"jcooper@babbleset.edu\"\n"
+ "}";
private Serializer serializer;
@Before
public void setUp() {
serializer = new Serializer();
}
@Test
public void testSerializeHappyCase() {
final UserEntity userEntityOne = serializer.deserialize(JSON_RESPONSE, UserEntity.class);
final String jsonString = serializer.serialize(userEntityOne, UserEntity.class);
final UserEntity userEntityTwo = serializer.deserialize(jsonString, UserEntity.class);
assertThat(userEntityOne.getUserId(), is(userEntityTwo.getUserId()));
assertThat(userEntityOne.getFullname(), is(equalTo(userEntityTwo.getFullname())));
assertThat(userEntityOne.getFollowers(), is(userEntityTwo.getFollowers()));
}
@Test
public void testDesearializeHappyCase() {
final UserEntity userEntity = serializer.deserialize(JSON_RESPONSE, UserEntity.class);
assertThat(userEntity.getUserId(), is(1));
assertThat(userEntity.getFullname(), is("Simon Hill"));
assertThat(userEntity.getFollowers(), is(7484));
}
}
================================================
FILE: data/src/test/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityDataMapperTest.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.entity.mapper;
import com.fernandocejas.android10.sample.data.entity.UserEntity;
import com.fernandocejas.android10.sample.domain.User;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.mock;
@RunWith(MockitoJUnitRunner.class)
public class UserEntityDataMapperTest {
private static final int FAKE_USER_ID = 123;
private static final String FAKE_FULLNAME = "Tony Stark";
private UserEntityDataMapper userEntityDataMapper;
@Before
public void setUp() throws Exception {
userEntityDataMapper = new UserEntityDataMapper();
}
@Test
public void testTransformUserEntity() {
UserEntity userEntity = createFakeUserEntity();
User user = userEntityDataMapper.transform(userEntity);
assertThat(user, is(instanceOf(User.class)));
assertThat(user.getUserId(), is(FAKE_USER_ID));
assertThat(user.getFullName(), is(FAKE_FULLNAME));
}
@Test
public void testTransformUserEntityCollection() {
UserEntity mockUserEntityOne = mock(UserEntity.class);
UserEntity mockUserEntityTwo = mock(UserEntity.class);
List<UserEntity> userEntityList = new ArrayList<UserEntity>(5);
userEntityList.add(mockUserEntityOne);
userEntityList.add(mockUserEntityTwo);
Collection<User> userCollection = userEntityDataMapper.transform(userEntityList);
assertThat(userCollection.toArray()[0], is(instanceOf(User.class)));
assertThat(userCollection.toArray()[1], is(instanceOf(User.class)));
assertThat(userCollection.size(), is(2));
}
private UserEntity createFakeUserEntity() {
UserEntity userEntity = new UserEntity();
userEntity.setUserId(FAKE_USER_ID);
userEntity.setFullname(FAKE_FULLNAME);
return userEntity;
}
}
================================================
FILE: data/src/test/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityJsonMapperTest.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.entity.mapper;
import com.fernandocejas.android10.sample.data.entity.UserEntity;
import com.google.gson.JsonSyntaxException;
import java.util.Collection;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
@RunWith(MockitoJUnitRunner.class)
public class UserEntityJsonMapperTest {
private static final String JSON_RESPONSE_USER_DETAILS = "{\n"
+ " \"id\": 1,\n"
+ " \"cover_url\": \"http://www.android10.org/myapi/cover_1.jpg\",\n"
+ " \"full_name\": \"Simon Hill\",\n"
+ " \"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"
+ " \"followers\": 7484,\n"
+ " \"email\": \"jcooper@babbleset.edu\"\n"
+ "}";
private static final String JSON_RESPONSE_USER_COLLECTION = "[{\n"
+ " \"id\": 1,\n"
+ " \"full_name\": \"Simon Hill\",\n"
+ " \"followers\": 7484\n"
+ "}, {\n"
+ " \"id\": 12,\n"
+ " \"full_name\": \"Pedro Garcia\",\n"
+ " \"followers\": 1381\n"
+ "}]";
private UserEntityJsonMapper userEntityJsonMapper;
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Before
public void setUp() {
userEntityJsonMapper = new UserEntityJsonMapper();
}
@Test
public void testTransformUserEntityHappyCase() {
UserEntity userEntity = userEntityJsonMapper.transformUserEntity(JSON_RESPONSE_USER_DETAILS);
assertThat(userEntity.getUserId(), is(1));
assertThat(userEntity.getFullname(), is(equalTo("Simon Hill")));
assertThat(userEntity.getEmail(), is(equalTo("jcooper@babbleset.edu")));
}
@Test
public void testTransformUserEntityCollectionHappyCase() {
Collection<UserEntity> userEntityCollection =
userEntityJsonMapper.transformUserEntityCollection(
JSON_RESPONSE_USER_COLLECTION);
assertThat(((UserEntity) userEntityCollection.toArray()[0]).getUserId(), is(1));
assertThat(((UserEntity) userEntityCollection.toArray()[1]).getUserId(), is(12));
assertThat(userEntityCollection.size(), is(2));
}
@Test
public void testTransformUserEntityNotValidResponse() {
expectedException.expect(JsonSyntaxException.class);
userEntityJsonMapper.transformUserEntity("ironman");
}
@Test
public void testTransformUserEntityCollectionNotValidResponse() {
expectedException.expect(JsonSyntaxException.class);
userEntityJsonMapper.transformUserEntityCollection("Tony Stark");
}
}
================================================
FILE: data/src/test/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundleTest.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.exception;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static org.mockito.Mockito.verify;
@RunWith(MockitoJUnitRunner.class)
public class RepositoryErrorBundleTest {
private RepositoryErrorBundle repositoryErrorBundle;
@Mock private Exception mockException;
@Before
public void setUp() {
repositoryErrorBundle = new RepositoryErrorBundle(mockException);
}
@Test
@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
public void testGetErrorMessageInteraction() {
repositoryErrorBundle.getErrorMessage();
verify(mockException).getMessage();
}
}
================================================
FILE: data/src/test/java/com/fernandocejas/android10/sample/data/repository/UserDataRepositoryTest.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.repository;
import com.fernandocejas.android10.sample.data.entity.UserEntity;
import com.fernandocejas.android10.sample.data.entity.mapper.UserEntityDataMapper;
import com.fernandocejas.android10.sample.data.repository.datasource.UserDataStore;
import com.fernandocejas.android10.sample.data.repository.datasource.UserDataStoreFactory;
import com.fernandocejas.android10.sample.domain.User;
import io.reactivex.Observable;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.verify;
@RunWith(MockitoJUnitRunner.class)
public class UserDataRepositoryTest {
private static final int FAKE_USER_ID = 123;
private UserDataRepository userDataRepository;
@Mock private UserDataStoreFactory mockUserDataStoreFactory;
@Mock private UserEntityDataMapper mockUserEntityDataMapper;
@Mock private UserDataStore mockUserDataStore;
@Mock private UserEntity mockUserEntity;
@Mock private User mockUser;
@Before
public void setUp() {
userDataRepository = new UserDataRepository(mockUserDataStoreFactory, mockUserEntityDataMapper);
given(mockUserDataStoreFactory.create(anyInt())).willReturn(mockUserDataStore);
given(mockUserDataStoreFactory.createCloudDataStore()).willReturn(mockUserDataStore);
}
@Test
public void testGetUsersHappyCase() {
List<UserEntity> usersList = new ArrayList<>();
usersList.add(new UserEntity());
given(mockUserDataStore.userEntityList()).willReturn(Observable.just(usersList));
userDataRepository.users();
verify(mockUserDataStoreFactory).createCloudDataStore();
verify(mockUserDataStore).userEntityList();
}
@Test
public void testGetUserHappyCase() {
UserEntity userEntity = new UserEntity();
given(mockUserDataStore.userEntityDetails(FAKE_USER_ID)).willReturn(Observable.just(userEntity));
userDataRepository.user(FAKE_USER_ID);
verify(mockUserDataStoreFactory).create(FAKE_USER_ID);
verify(mockUserDataStore).userEntityDetails(FAKE_USER_ID);
}
}
================================================
FILE: data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/CloudUserDataStoreTest.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.repository.datasource;
import com.fernandocejas.android10.sample.data.cache.UserCache;
import com.fernandocejas.android10.sample.data.entity.UserEntity;
import com.fernandocejas.android10.sample.data.net.RestApi;
import io.reactivex.Observable;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.verify;
@RunWith(MockitoJUnitRunner.class)
public class CloudUserDataStoreTest {
private static final int FAKE_USER_ID = 765;
private CloudUserDataStore cloudUserDataStore;
@Mock private RestApi mockRestApi;
@Mock private UserCache mockUserCache;
@Before
public void setUp() {
cloudUserDataStore = new CloudUserDataStore(mockRestApi, mockUserCache);
}
@Test
public void testGetUserEntityListFromApi() {
cloudUserDataStore.userEntityList();
verify(mockRestApi).userEntityList();
}
@Test
public void testGetUserEntityDetailsFromApi() {
UserEntity fakeUserEntity = new UserEntity();
Observable<UserEntity> fakeObservable = Observable.just(fakeUserEntity);
given(mockRestApi.userEntityById(FAKE_USER_ID)).willReturn(fakeObservable);
cloudUserDataStore.userEntityDetails(FAKE_USER_ID);
verify(mockRestApi).userEntityById(FAKE_USER_ID);
}
}
================================================
FILE: data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/DiskUserDataStoreTest.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.repository.datasource;
import com.fernandocejas.android10.sample.data.cache.UserCache;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static org.mockito.Mockito.verify;
@RunWith(MockitoJUnitRunner.class)
public class DiskUserDataStoreTest {
private static final int FAKE_USER_ID = 11;
private DiskUserDataStore diskUserDataStore;
@Mock private UserCache mockUserCache;
@Rule public ExpectedException expectedException = ExpectedException.none();
@Before
public void setUp() {
diskUserDataStore = new DiskUserDataStore(mockUserCache);
}
@Test
public void testGetUserEntityListUnsupported() {
expectedException.expect(UnsupportedOperationException.class);
diskUserDataStore.userEntityList();
}
@Test
public void testGetUserEntityDetailesFromCache() {
diskUserDataStore.userEntityDetails(FAKE_USER_ID);
verify(mockUserCache).get(FAKE_USER_ID);
}
}
================================================
FILE: data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStoreFactoryTest.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.data.repository.datasource;
import com.fernandocejas.android10.sample.data.ApplicationTestCase;
import com.fernandocejas.android10.sample.data.cache.UserCache;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.robolectric.RuntimeEnvironment;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.verify;
public class UserDataStoreFactoryTest extends ApplicationTestCase {
private static final int FAKE_USER_ID = 11;
private UserDataStoreFactory userDataStoreFactory;
@Mock private UserCache mockUserCache;
@Before
public void setUp() {
userDataStoreFactory = new UserDataStoreFactory(RuntimeEnvironment.application, mockUserCache);
}
@Test
public void testCreateDiskDataStore() {
given(mockUserCache.isCached(FAKE_USER_ID)).willReturn(true);
given(mockUserCache.isExpired()).willReturn(false);
UserDataStore userDataStore = userDataStoreFactory.create(FAKE_USER_ID);
assertThat(userDataStore, is(notNullValue()));
assertThat(userDataStore, is(instanceOf(DiskUserDataStore.class)));
verify(mockUserCache).isCached(FAKE_USER_ID);
verify(mockUserCache).isExpired();
}
@Test
public void testCreateCloudDataStore() {
given(mockUserCache.isExpired()).willReturn(true);
given(mockUserCache.isCached(FAKE_USER_ID)).willReturn(false);
UserDataStore userDataStore = userDataStoreFactory.create(FAKE_USER_ID);
assertThat(userDataStore, is(notNullValue()));
assertThat(userDataStore, is(instanceOf(CloudUserDataStore.class)));
verify(mockUserCache).isExpired();
}
}
================================================
FILE: domain/build.gradle
================================================
apply plugin: 'java'
//noinspection GroovyUnusedAssignment
sourceCompatibility = 1.7
//noinspection GroovyUnusedAssignment
targetCompatibility = 1.7
configurations {
provided
}
sourceSets {
main {
compileClasspath += configurations.provided
}
}
dependencies {
def domainDependencies = rootProject.ext.domainDependencies
def domainTestDependencies = rootProject.ext.domainTestDependencies
compileOnly domainDependencies.javaxAnnotation
implementation domainDependencies.javaxInject
implementation domainDependencies.rxJava
compile domainDependencies.arrow
testImplementation domainTestDependencies.junit
testImplementation domainTestDependencies.mockito
testImplementation domainTestDependencies.assertj
}
================================================
FILE: domain/src/main/java/com/fernandocejas/android10/sample/domain/User.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.domain;
/**
* Class that represents a User in the domain layer.
*/
public class User {
private final int userId;
public User(int userId) {
this.userId = userId;
}
private String coverUrl;
private String fullName;
private String email;
private String description;
private int followers;
public int getUserId() {
return userId;
}
public String getCoverUrl() {
return coverUrl;
}
public void setCoverUrl(String coverUrl) {
this.coverUrl = coverUrl;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public int getFollowers() {
return followers;
}
public void setFollowers(int followers) {
this.followers = followers;
}
}
================================================
FILE: domain/src/main/java/com/fernandocejas/android10/sample/domain/exception/DefaultErrorBundle.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.domain.exception;
/**
* Wrapper around Exceptions used to manage default errors.
*/
public class DefaultErrorBundle implements ErrorBundle {
private static final String DEFAULT_ERROR_MSG = "Unknown error";
private final Exception exception;
public DefaultErrorBundle(Exception exception) {
this.exception = exception;
}
@Override
public Exception getException() {
return exception;
}
@Override
public String getErrorMessage() {
return (exception != null) ? this.exception.getMessage() : DEFAULT_ERROR_MSG;
}
}
================================================
FILE: domain/src/main/java/com/fernandocejas/android10/sample/domain/exception/ErrorBundle.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.domain.exception;
/**
* Interface to represent a wrapper around an {@link java.lang.Exception} to manage errors.
*/
public interface ErrorBundle {
Exception getException();
String getErrorMessage();
}
================================================
FILE: domain/src/main/java/com/fernandocejas/android10/sample/domain/executor/PostExecutionThread.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.domain.executor;
import io.reactivex.Scheduler;
/**
* Thread abstraction created to change the execution context from any thread to any other thread.
* Useful to encapsulate a UI Thread for example, since some job will be done in background, an
* implementation of this interface will change context and update the UI.
*/
public interface PostExecutionThread {
Scheduler getScheduler();
}
================================================
FILE: domain/src/main/java/com/fernandocejas/android10/sample/domain/executor/ThreadExecutor.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.domain.executor;
import java.util.concurrent.Executor;
/**
* Executor implementation can be based on different frameworks or techniques of asynchronous
* execution, but every implementation will execute the
* {@link com.fernandocejas.android10.sample.domain.interactor.UseCase} out of the UI thread.
*/
public interface ThreadExecutor extends Executor {}
================================================
FILE: domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/DefaultObserver.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.domain.interactor;
import io.reactivex.observers.DisposableObserver;
/**
* Default {@link DisposableObserver} base class to be used whenever you want default error handling.
*/
public class DefaultObserver<T> extends DisposableObserver<T> {
@Override public void onNext(T t) {
// no-op by default.
}
@Override public void onComplete() {
// no-op by default.
}
@Override public void onError(Throwable exception) {
// no-op by default.
}
}
================================================
FILE: domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetails.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.domain.interactor;
import com.fernandocejas.android10.sample.domain.User;
import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread;
import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;
import com.fernandocejas.android10.sample.domain.repository.UserRepository;
import com.fernandocejas.arrow.checks.Preconditions;
import io.reactivex.Observable;
import javax.inject.Inject;
/**
* This class is an implementation of {@link UseCase} that represents a use case for
* retrieving data related to an specific {@link User}.
*/
public class GetUserDetails extends UseCase<User, GetUserDetails.Params> {
private final UserRepository userRepository;
@Inject
GetUserDetails(UserRepository userRepository, ThreadExecutor threadExecutor,
PostExecutionThread postExecutionThread) {
super(threadExecutor, postExecutionThread);
this.userRepository = userRepository;
}
@Override Observable<User> buildUseCaseObservable(Params params) {
Preconditions.checkNotNull(params);
return this.userRepository.user(params.userId);
}
public static final class Params {
private final int userId;
private Params(int userId) {
this.userId = userId;
}
public static Params forUser(int userId) {
return new Params(userId);
}
}
}
================================================
FILE: domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.domain.interactor;
import com.fernandocejas.android10.sample.domain.User;
import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread;
import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;
import com.fernandocejas.android10.sample.domain.repository.UserRepository;
import io.reactivex.Observable;
import java.util.List;
import javax.inject.Inject;
/**
* This class is an implementation of {@link UseCase} that represents a use case for
* retrieving a collection of all {@link User}.
*/
public class GetUserList extends UseCase<List<User>, Void> {
private final UserRepository userRepository;
@Inject
GetUserList(UserRepository userRepository, ThreadExecutor threadExecutor,
PostExecutionThread postExecutionThread) {
super(threadExecutor, postExecutionThread);
this.userRepository = userRepository;
}
@Override Observable<List<User>> buildUseCaseObservable(Void unused) {
return this.userRepository.users();
}
}
================================================
FILE: domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/UseCase.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.domain.interactor;
import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread;
import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;
import com.fernandocejas.arrow.checks.Preconditions;
import io.reactivex.Observable;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.observers.DisposableObserver;
import io.reactivex.schedulers.Schedulers;
/**
* Abstract class for a Use Case (Interactor in terms of Clean Architecture).
* This interface represents a execution unit for different use cases (this means any use case
* in the application should implement this contract).
*
* By convention each UseCase implementation will return the result using a {@link DisposableObserver}
* that will execute its job in a background thread and will post the result in the UI thread.
*/
public abstract class UseCase<T, Params> {
private final ThreadExecutor threadExecutor;
private final PostExecutionThread postExecutionThread;
private final CompositeDisposable disposables;
UseCase(ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) {
this.threadExecutor = threadExecutor;
this.postExecutionThread = postExecutionThread;
this.disposables = new CompositeDisposable();
}
/**
* Builds an {@link Observable} which will be used when executing the current {@link UseCase}.
*/
abstract Observable<T> buildUseCaseObservable(Params params);
/**
* Executes the current use case.
*
* @param observer {@link DisposableObserver} which will be listening to the observable build
* by {@link #buildUseCaseObservable(Params)} ()} method.
* @param params Parameters (Optional) used to build/execute this use case.
*/
public void execute(DisposableObserver<T> observer, Params params) {
Preconditions.checkNotNull(observer);
final Observable<T> observable = this.buildUseCaseObservable(params)
.subscribeOn(Schedulers.from(threadExecutor))
.observeOn(postExecutionThread.getScheduler());
addDisposable(observable.subscribeWith(observer));
}
/**
* Dispose from current {@link CompositeDisposable}.
*/
public void dispose() {
if (!disposables.isDisposed()) {
disposables.dispose();
}
}
/**
* Dispose from current {@link CompositeDisposable}.
*/
private void addDisposable(Disposable disposable) {
Preconditions.checkNotNull(disposable);
Preconditions.checkNotNull(disposables);
disposables.add(disposable);
}
}
================================================
FILE: domain/src/main/java/com/fernandocejas/android10/sample/domain/repository/UserRepository.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.domain.repository;
import com.fernandocejas.android10.sample.domain.User;
import io.reactivex.Observable;
import java.util.List;
/**
* Interface that represents a Repository for getting {@link User} related data.
*/
public interface UserRepository {
/**
* Get an {@link Observable} which will emit a List of {@link User}.
*/
Observable<List<User>> users();
/**
* Get an {@link Observable} which will emit a {@link User}.
*
* @param userId The user id used to retrieve user data.
*/
Observable<User> user(final int userId);
}
================================================
FILE: domain/src/test/java/com/fernandocejas/android10/sample/domain/UserTest.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.domain;
import org.junit.Before;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class UserTest {
private static final int FAKE_USER_ID = 8;
private User user;
@Before
public void setUp() {
user = new User(FAKE_USER_ID);
}
@Test
public void testUserConstructorHappyCase() {
final int userId = user.getUserId();
assertThat(userId).isEqualTo(FAKE_USER_ID);
}
}
================================================
FILE: domain/src/test/java/com/fernandocejas/android10/sample/domain/exception/DefaultErrorBundleTest.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.domain.exception;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static org.mockito.Mockito.verify;
@RunWith(MockitoJUnitRunner.class)
public class DefaultErrorBundleTest {
private DefaultErrorBundle defaultErrorBundle;
@Mock private Exception mockException;
@Before
public void setUp() {
defaultErrorBundle = new DefaultErrorBundle(mockException);
}
@Test
public void testGetErrorMessageInteraction() {
defaultErrorBundle.getErrorMessage();
verify(mockException).getMessage();
}
}
================================================
FILE: domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetailsTest.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.domain.interactor;
import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread;
import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;
import com.fernandocejas.android10.sample.domain.interactor.GetUserDetails.Params;
import com.fernandocejas.android10.sample.domain.repository.UserRepository;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
@RunWith(MockitoJUnitRunner.class)
public class GetUserDetailsTest {
private static final int USER_ID = 123;
private GetUserDetails getUserDetails;
@Mock private UserRepository mockUserRepository;
@Mock private ThreadExecutor mockThreadExecutor;
@Mock private PostExecutionThread mockPostExecutionThread;
@Rule public ExpectedException expectedException = ExpectedException.none();
@Before
public void setUp() {
getUserDetails = new GetUserDetails(mockUserRepository, mockThreadExecutor,
mockPostExecutionThread);
}
@Test
public void testGetUserDetailsUseCaseObservableHappyCase() {
getUserDetails.buildUseCaseObservable(Params.forUser(USER_ID));
verify(mockUserRepository).user(USER_ID);
verifyNoMoreInteractions(mockUserRepository);
verifyZeroInteractions(mockPostExecutionThread);
verifyZeroInteractions(mockThreadExecutor);
}
@Test
public void testShouldFailWhenNoOrEmptyParameters() {
expectedException.expect(NullPointerException.class);
getUserDetails.buildUseCaseObservable(null);
}
}
================================================
FILE: domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserListTest.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.domain.interactor;
import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread;
import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;
import com.fernandocejas.android10.sample.domain.repository.UserRepository;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
@RunWith(MockitoJUnitRunner.class)
public class GetUserListTest {
private GetUserList getUserList;
@Mock private ThreadExecutor mockThreadExecutor;
@Mock private PostExecutionThread mockPostExecutionThread;
@Mock private UserRepository mockUserRepository;
@Before
public void setUp() {
getUserList = new GetUserList(mockUserRepository, mockThreadExecutor,
mockPostExecutionThread);
}
@Test
public void testGetUserListUseCaseObservableHappyCase() {
getUserList.buildUseCaseObservable(null);
verify(mockUserRepository).users();
verifyNoMoreInteractions(mockUserRepository);
verifyZeroInteractions(mockThreadExecutor);
verifyZeroInteractions(mockPostExecutionThread);
}
}
================================================
FILE: domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/UseCaseTest.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.domain.interactor;
import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread;
import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;
import io.reactivex.Observable;
import io.reactivex.observers.DisposableObserver;
import io.reactivex.schedulers.TestScheduler;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
@RunWith(MockitoJUnitRunner.class)
public class UseCaseTest {
private UseCaseTestClass useCase;
private TestDisposableObserver<Object> testObserver;
@Mock private ThreadExecutor mockThreadExecutor;
@Mock private PostExecutionThread mockPostExecutionThread;
@Rule public ExpectedException expectedException = ExpectedException.none();
@Before
public void setUp() {
this.useCase = new UseCaseTestClass(mockThreadExecutor, mockPostExecutionThread);
this.testObserver = new TestDisposableObserver<>();
given(mockPostExecutionThread.getScheduler()).willReturn(new TestScheduler());
}
@Test
public void testBuildUseCaseObservableReturnCorrectResult() {
useCase.execute(testObserver, Params.EMPTY);
assertThat(testObserver.valuesCount).isZero();
}
@Test
public void testSubscriptionWhenExecutingUseCase() {
useCase.execute(testObserver, Params.EMPTY);
useCase.dispose();
assertThat(testObserver.isDisposed()).isTrue();
}
@Test
public void testShouldFailWhenExecuteWithNullObserver() {
expectedException.expect(NullPointerException.class);
useCase.execute(null, Params.EMPTY);
}
private static class UseCaseTestClass extends UseCase<Object, Params> {
UseCaseTestClass(ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) {
super(threadExecutor, postExecutionThread);
}
@Override Observable<Object> buildUseCaseObservable(Params params) {
return Observable.empty();
}
@Override
public void execute(DisposableObserver<Object> observer, Params params) {
super.execute(observer, params);
}
}
private static class TestDisposableObserver<T> extends DisposableObserver<T> {
private int valuesCount = 0;
@Override public void onNext(T value) {
valuesCount++;
}
@Override public void onError(Throwable e) {
// no-op by default.
}
@Override public void onComplete() {
// no-op by default.
}
}
private static class Params {
private static Params EMPTY = new Params();
private Params() {}
}
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Wed Dec 21 17:11:04 ART 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
================================================
FILE: gradle.properties
================================================
#Gradle configuration
org.gradle.daemon=true
org.gradle.jvmargs=-Dfile.encoding=UTF-8
org.gradle.parallel=true
org.gradle.configureondemand=true
================================================
FILE: gradlew
================================================
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save ( ) {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: presentation/.gitignore
================================================
/build
================================================
FILE: presentation/build.gradle
================================================
apply plugin: 'com.android.application'
//apply plugin: 'com.neenbedankt.android-apt'
android {
def globalConfiguration = rootProject.extensions.getByName("ext")
compileSdkVersion globalConfiguration.getAt("androidCompileSdkVersion")
buildToolsVersion globalConfiguration.getAt("androidBuildToolsVersion")
defaultConfig {
minSdkVersion globalConfiguration.getAt("androidMinSdkVersion")
targetSdkVersion globalConfiguration.getAt("androidTargetSdkVersion")
applicationId globalConfiguration.getAt("androidApplicationId")
versionCode globalConfiguration.getAt("androidVersionCode")
versionName globalConfiguration.getAt("androidVersionName")
testInstrumentationRunner globalConfiguration.getAt("testInstrumentationRunner")
testApplicationId globalConfiguration.getAt("testApplicationId")
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
packagingOptions {
exclude 'LICENSE.txt'
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/ASL2.0'
exclude 'META-INF/NOTICE'
exclude 'META-INF/LICENSE'
}
lintOptions {
quiet true
abortOnError false
ignoreWarnings true
disable 'InvalidPackage' //Some libraries have issues with this.
disable 'OldTargetApi' //Lint gives this warning but SDK 20 would be Android L Beta.
disable 'IconDensities' //For testing purpose. This is safe to remove.
disable 'IconMissingDensityFolder' //For testing purpose. This is safe to remove.
}
signingConfigs {
debug {
storeFile file('../buildsystem/debug.keystore')
storePassword 'android'
keyAlias 'androiddebugkey'
keyPassword 'android'
}
}
buildTypes {
debug {
signingConfig signingConfigs.debug
}
}
}
dependencies {
def presentationDependencies = rootProject.ext.presentationDependencies
def presentationTestDependencies = rootProject.ext.presentationTestDependencies
def developmentDependencies = rootProject.ext.developmentDependencies
implementation project(':domain')
implementation project(':data')
annotationProcessor presentationDependencies.daggerCompiler
implementation presentationDependencies.dagger
implementation presentationDependencies.butterKnife
annotationProcessor presentationDependencies.butterKnife
implementation presentationDependencies.recyclerView
implementation presentationDependencies.rxJava
implementation presentationDependencies.rxAndroid
compileOnly presentationDependencies.javaxAnnotation
androidTestImplementation presentationTestDependencies.mockito
androidTestImplementation presentationTestDependencies.dexmaker
androidTestImplementation presentationTestDependencies.dexmakerMockito
androidTestImplementation presentationTestDependencies.espresso
androidTestImplementation presentationTestDependencies.testingSupportLib
//Development
implementation developmentDependencies.leakCanary
}
repositories {
google()
}
================================================
FILE: presentation/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/fcejas/Software/SDKs/android-sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/exception/ErrorMessageFactoryTest.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.test.exception;
import android.test.AndroidTestCase;
import com.fernandocejas.android10.sample.data.exception.NetworkConnectionException;
import com.fernandocejas.android10.sample.data.exception.UserNotFoundException;
import com.fernandocejas.android10.sample.presentation.R;
import com.fernandocejas.android10.sample.presentation.exception.ErrorMessageFactory;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
public class ErrorMessageFactoryTest extends AndroidTestCase {
@Override protected void setUp() throws Exception {
super.setUp();
}
public void testNetworkConnectionErrorMessage() {
String expectedMessage = getContext().getString(R.string.exception_message_no_connection);
String actualMessage = ErrorMessageFactory.create(getContext(),
new NetworkConnectionException());
assertThat(actualMessage, is(equalTo(expectedMessage)));
}
public void testUserNotFoundErrorMessage() {
String expectedMessage = getContext().getString(R.string.exception_message_user_not_found);
String actualMessage = ErrorMessageFactory.create(getContext(), new UserNotFoundException());
assertThat(actualMessage, is(equalTo(expectedMessage)));
}
}
================================================
FILE: presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/mapper/UserModelDataMapperTest.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.test.mapper;
import com.fernandocejas.android10.sample.domain.User;
import com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper;
import com.fernandocejas.android10.sample.presentation.model.UserModel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import junit.framework.TestCase;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.mock;
public class UserModelDataMapperTest extends TestCase {
private static final int FAKE_USER_ID = 123;
private static final String FAKE_FULL_NAME = "Tony Stark";
private UserModelDataMapper userModelDataMapper;
@Override protected void setUp() throws Exception {
super.setUp();
userModelDataMapper = new UserModelDataMapper();
}
public void testTransformUser() {
User user = createFakeUser();
UserModel userModel = userModelDataMapper.transform(user);
assertThat(userModel, is(instanceOf(UserModel.class)));
assertThat(userModel.getUserId(), is(FAKE_USER_ID));
assertThat(userModel.getFullName(), is(FAKE_FULL_NAME));
}
public void testTransformUserCollection() {
User mockUserOne = mock(User.class);
User mockUserTwo = mock(User.class);
List<User> userList = new ArrayList<User>(5);
userList.add(mockUserOne);
userList.add(mockUserTwo);
Collection<UserModel> userModelList = userModelDataMapper.transform(userList);
assertThat(userModelList.toArray()[0], is(instanceOf(UserModel.class)));
assertThat(userModelList.toArray()[1], is(instanceOf(UserModel.class)));
assertThat(userModelList.size(), is(2));
}
private User createFakeUser() {
User user = new User(FAKE_USER_ID);
user.setFullName(FAKE_FULL_NAME);
return user;
}
}
================================================
FILE: presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/presenter/UserDetailsPresenterTest.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.test.presenter;
import android.content.Context;
import com.fernandocejas.android10.sample.domain.interactor.GetUserDetails;
import com.fernandocejas.android10.sample.domain.interactor.GetUserDetails.Params;
import com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper;
import com.fernandocejas.android10.sample.presentation.presenter.UserDetailsPresenter;
import com.fernandocejas.android10.sample.presentation.view.UserDetailsView;
import io.reactivex.observers.DisposableObserver;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.verify;
@RunWith(MockitoJUnitRunner.class)
public class UserDetailsPresenterTest {
private static final int USER_ID = 1;
private UserDetailsPresenter userDetailsPresenter;
@Mock private Context mockContext;
@Mock private UserDetailsView mockUserDetailsView;
@Mock private GetUserDetails mockGetUserDetails;
@Mock private UserModelDataMapper mockUserModelDataMapper;
@Before
public void setUp() {
userDetailsPresenter = new UserDetailsPresenter(mockGetUserDetails, mockUserModelDataMapper);
userDetailsPresenter.setView(mockUserDetailsView);
}
@Test
@SuppressWarnings("unchecked")
public void testUserDetailsPresenterInitialize() {
given(mockUserDetailsView.context()).willReturn(mockContext);
userDetailsPresenter.initialize(USER_ID);
verify(mockUserDetailsView).hideRetry();
verify(mockUserDetailsView).showLoading();
verify(mockGetUserDetails).execute(any(DisposableObserver.class), any(Params.class));
}
}
================================================
FILE: presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/presenter/UserListPresenterTest.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.test.presenter;
import android.content.Context;
import com.fernandocejas.android10.sample.domain.interactor.GetUserList;
import com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper;
import com.fernandocejas.android10.sample.presentation.presenter.UserListPresenter;
import com.fernandocejas.android10.sample.presentation.view.UserListView;
import io.reactivex.observers.DisposableObserver;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.verify;
@RunWith(MockitoJUnitRunner.class)
public class UserListPresenterTest {
private UserListPresenter userListPresenter;
@Mock private Context mockContext;
@Mock private UserListView mockUserListView;
@Mock private GetUserList mockGetUserList;
@Mock private UserModelDataMapper mockUserModelDataMapper;
@Before
public void setUp() {
userListPresenter = new UserListPresenter(mockGetUserList, mockUserModelDataMapper);
userListPresenter.setView(mockUserListView);
}
@Test
@SuppressWarnings("unchecked")
public void testUserListPresenterInitialize() {
given(mockUserListView.context()).willReturn(mockContext);
userListPresenter.initialize();
verify(mockUserListView).hideRetry();
verify(mockUserListView).showLoading();
verify(mockGetUserList).execute(any(DisposableObserver.class), any(Void.class));
}
}
================================================
FILE: presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/view/activity/UserDetailsActivityTest.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.test.view.activity;
import android.app.Fragment;
import android.content.Intent;
import android.test.ActivityInstrumentationTestCase2;
import com.fernandocejas.android10.sample.presentation.R;
import com.fernandocejas.android10.sample.presentation.view.activity.UserDetailsActivity;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.not;
public class UserDetailsActivityTest extends ActivityInstrumentationTestCase2<UserDetailsActivity> {
private static final int FAKE_USER_ID = 10;
private UserDetailsActivity userDetailsActivity;
public UserDetailsActivityTest() {
super(UserDetailsActivity.class);
}
@Override protected void setUp() throws Exception {
super.setUp();
this.setActivityIntent(createTargetIntent());
this.userDetailsActivity = getActivity();
}
@Override protected void tearDown() throws Exception {
super.tearDown();
}
public void testContainsUserDetailsFragment() {
Fragment userDetailsFragment =
userDetailsActivity.getFragmentManager().findFragmentById(R.id.fragmentContainer);
assertThat(userDetailsFragment, is(notNullValue()));
}
public void testContainsProperTitle() {
String actualTitle = this.userDetailsActivity.getTitle().toString().trim();
assertThat(actualTitle, is("User Details"));
}
public void testLoadUserHappyCaseViews() {
onView(withId(R.id.rl_retry)).check(matches(not(isDisplayed())));
onView(withId(R.id.rl_progress)).check(matches(not(isDisplayed())));
onView(withId(R.id.tv_fullname)).check(matches(isDisplayed()));
onView(withId(R.id.tv_email)).check(matches(isDisplayed()));
onView(withId(R.id.tv_description)).check(matches(isDisplayed()));
}
public void testLoadUserHappyCaseData() {
onView(withId(R.id.tv_fullname)).check(matches(withText("John Sanchez")));
onView(withId(R.id.tv_email)).check(matches(withText("dmedina@katz.edu")));
onView(withId(R.id.tv_followers)).check(matches(withText("4523")));
}
private Intent createTargetIntent() {
Intent intentLaunchActivity =
UserDetailsActivity.getCallingIntent(getInstrumentation().getTargetContext(), FAKE_USER_ID);
return intentLaunchActivity;
}
}
================================================
FILE: presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/view/activity/UserListActivityTest.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.test.view.activity;
import android.app.Fragment;
import android.content.Intent;
import android.test.ActivityInstrumentationTestCase2;
import com.fernandocejas.android10.sample.presentation.R;
import com.fernandocejas.android10.sample.presentation.view.activity.UserListActivity;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
public class UserListActivityTest extends ActivityInstrumentationTestCase2<UserListActivity> {
private UserListActivity userListActivity;
public UserListActivityTest() {
super(UserListActivity.class);
}
@Override protected void setUp() throws Exception {
super.setUp();
this.setActivityIntent(createTargetIntent());
userListActivity = getActivity();
}
@Override protected void tearDown() throws Exception {
super.tearDown();
}
public void testContainsUserListFragment() {
Fragment userListFragment =
userListActivity.getFragmentManager().findFragmentById(R.id.fragmentContainer);
assertThat(userListFragment, is(notNullValue()));
}
public void testContainsProperTitle() {
String actualTitle = this.userListActivity.getTitle().toString().trim();
assertThat(actualTitle, is("Users List"));
}
private Intent createTargetIntent() {
Intent intentLaunchActivity =
UserListActivity.getCallingIntent(getInstrumentation().getTargetContext());
return intentLaunchActivity;
}
}
================================================
FILE: presentation/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.fernandocejas.android10.sample.presentation">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:name=".AndroidApplication"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity
android:name=".view.activity.MainActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".view.activity.UserListActivity"
android:label="@string/activity_title_user_list">
</activity>
<activity
android:name=".view.activity.UserDetailsActivity"
android:label="@string/activity_title_user_details">
</activity>
</application>
</manifest>
================================================
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/AndroidApplication.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.presentation;
import android.app.Application;
import com.fernandocejas.android10.sample.presentation.internal.di.components.ApplicationComponent;
import com.fernandocejas.android10.sample.presentation.internal.di.components.DaggerApplicationComponent;
import com.fernandocejas.android10.sample.presentation.internal.di.modules.ApplicationModule;
import com.squareup.leakcanary.LeakCanary;
/**
* Android Main Application
*/
public class AndroidApplication extends Application {
private ApplicationComponent applicationComponent;
@Override public void onCreate() {
super.onCreate();
this.initializeInjector();
this.initializeLeakDetection();
}
private void initializeInjector() {
this.applicationComponent = DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(this))
.build();
}
public ApplicationComponent getApplicationComponent() {
return this.applicationComponent;
}
private void initializeLeakDetection() {
if (BuildConfig.DEBUG) {
LeakCanary.install(this);
}
}
}
================================================
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/UIThread.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.presentation;
import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread;
import io.reactivex.Scheduler;
import io.reactivex.android.schedulers.AndroidSchedulers;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* MainThread (UI Thread) implementation based on a {@link Scheduler}
* which will execute actions on the Android UI thread
*/
@Singleton
public class UIThread implements PostExecutionThread {
@Inject
UIThread() {}
@Override public Scheduler getScheduler() {
return AndroidSchedulers.mainThread();
}
}
================================================
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/exception/ErrorMessageFactory.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.presentation.exception;
import android.content.Context;
import com.fernandocejas.android10.sample.data.exception.NetworkConnectionException;
import com.fernandocejas.android10.sample.data.exception.UserNotFoundException;
import com.fernandocejas.android10.sample.presentation.R;
/**
* Factory used to create error messages from an Exception as a condition.
*/
public class ErrorMessageFactory {
private ErrorMessageFactory() {
//empty
}
/**
* Creates a String representing an error message.
*
* @param context Context needed to retrieve string resources.
* @param exception An exception used as a condition to retrieve the correct error message.
* @return {@link String} an error message.
*/
public static String create(Context context, Exception exception) {
String message = context.getString(R.string.exception_message_generic);
if (exception instanceof NetworkConnectionException) {
message = context.getString(R.string.exception_message_no_connection);
} else if (exception instanceof UserNotFoundException) {
message = context.getString(R.string.exception_message_user_not_found);
}
return message;
}
}
================================================
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/HasComponent.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.presentation.internal.di;
/**
* Interface representing a contract for clients that contains a component for dependency injection.
*/
public interface HasComponent<C> {
C getComponent();
}
================================================
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/PerActivity.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.presentation.internal.di;
import java.lang.annotation.Retention;
import javax.inject.Scope;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* A scoping annotation to permit objects whose lifetime should
* conform to the life of the activity to be memorized in the
* correct component.
*/
@Scope
@Retention(RUNTIME)
public @interface PerActivity {}
================================================
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/components/ActivityComponent.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.presentation.internal.di.components;
import android.app.Activity;
import com.fernandocejas.android10.sample.presentation.internal.di.PerActivity;
import com.fernandocejas.android10.sample.presentation.internal.di.modules.ActivityModule;
import dagger.Component;
/**
* A base component upon which fragment's components may depend.
* Activity-level components should extend this component.
*
* Subtypes of ActivityComponent should be decorated with annotation:
* {@link com.fernandocejas.android10.sample.presentation.internal.di.PerActivity}
*/
@PerActivity
@Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class)
interface ActivityComponent {
//Exposed to sub-graphs.
Activity activity();
}
================================================
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/components/ApplicationComponent.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.presentation.internal.di.components;
import android.content.Context;
import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread;
import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;
import com.fernandocejas.android10.sample.domain.repository.UserRepository;
import com.fernandocejas.android10.sample.presentation.internal.di.modules.ApplicationModule;
import com.fernandocejas.android10.sample.presentation.view.activity.BaseActivity;
import dagger.Component;
import javax.inject.Singleton;
/**
* A component whose lifetime is the life of the application.
*/
@Singleton // Constraints this component to one-per-application or unscoped bindings.
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
void inject(BaseActivity baseActivity);
//Exposed to sub-graphs.
Context context();
ThreadExecutor threadExecutor();
PostExecutionThread postExecutionThread();
UserRepository userRepository();
}
================================================
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/components/UserComponent.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.presentation.internal.di.components;
import com.fernandocejas.android10.sample.presentation.internal.di.PerActivity;
import com.fernandocejas.android10.sample.presentation.internal.di.modules.ActivityModule;
import com.fernandocejas.android10.sample.presentation.internal.di.modules.UserModule;
import com.fernandocejas.android10.sample.presentation.view.fragment.UserDetailsFragment;
import com.fernandocejas.android10.sample.presentation.view.fragment.UserListFragment;
import dagger.Component;
/**
* A scope {@link com.fernandocejas.android10.sample.presentation.internal.di.PerActivity} component.
* Injects user specific Fragments.
*/
@PerActivity
@Component(dependencies = ApplicationComponent.class, modules = {ActivityModule.class, UserModule.class})
public interface UserComponent extends ActivityComponent {
void inject(UserListFragment userListFragment);
void inject(UserDetailsFragment userDetailsFragment);
}
================================================
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/modules/ActivityModule.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.presentation.internal.di.modules;
import android.app.Activity;
import com.fernandocejas.android10.sample.presentation.internal.di.PerActivity;
import dagger.Module;
import dagger.Provides;
/**
* A module to wrap the Activity state and expose it to the graph.
*/
@Module
public class ActivityModule {
private final Activity activity;
public ActivityModule(Activity activity) {
this.activity = activity;
}
/**
* Expose the activity to dependents in the graph.
*/
@Provides @PerActivity Activity activity() {
return this.activity;
}
}
================================================
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/modules/ApplicationModule.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.presentation.internal.di.modules;
import android.content.Context;
import com.fernandocejas.android10.sample.data.cache.UserCache;
import com.fernandocejas.android10.sample.data.cache.UserCacheImpl;
import com.fernandocejas.android10.sample.data.executor.JobExecutor;
import com.fernandocejas.android10.sample.data.repository.UserDataRepository;
import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread;
import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;
import com.fernandocejas.android10.sample.domain.repository.UserRepository;
import com.fernandocejas.android10.sample.presentation.AndroidApplication;
import com.fernandocejas.android10.sample.presentation.UIThread;
import com.fernandocejas.android10.sample.presentation.navigation.Navigator;
import dagger.Module;
import dagger.Provides;
import javax.inject.Singleton;
/**
* Dagger module that provides objects which will live during the application lifecycle.
*/
@Module
public class ApplicationModule {
private final AndroidApplication application;
public ApplicationModule(AndroidApplication application) {
this.application = application;
}
@Provides @Singleton Context provideApplicationContext() {
return this.application;
}
@Provides @Singleton ThreadExecutor provideThreadExecutor(JobExecutor jobExecutor) {
return jobExecutor;
}
@Provides @Singleton PostExecutionThread providePostExecutionThread(UIThread uiThread) {
return uiThread;
}
@Provides @Singleton UserCache provideUserCache(UserCacheImpl userCache) {
return userCache;
}
@Provides @Singleton UserRepository provideUserRepository(UserDataRepository userDataRepository) {
return userDataRepository;
}
}
================================================
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/modules/UserModule.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.presentation.internal.di.modules;
import dagger.Module;
/**
* Dagger module that provides user related collaborators.
*/
@Module
public class UserModule {
public UserModule() {}
}
================================================
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mapper/UserModelDataMapper.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.presentation.mapper;
import com.fernandocejas.android10.sample.domain.User;
import com.fernandocejas.android10.sample.presentation.internal.di.PerActivity;
import com.fernandocejas.android10.sample.presentation.model.UserModel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import javax.inject.Inject;
/**
* Mapper class used to transform {@link User} (in the domain layer) to {@link UserModel} in the
* presentation layer.
*/
@PerActivity
public class UserModelDataMapper {
@Inject
public UserModelDataMapper() {}
/**
* Transform a {@link User} into an {@link UserModel}.
*
* @param user Object to be transformed.
* @return {@link UserModel}.
*/
public UserModel transform(User user) {
if (user == null) {
throw new IllegalArgumentException("Cannot transform a null value");
}
final UserModel userModel = new UserModel(user.getUserId());
userModel.setCoverUrl(user.getCoverUrl());
userModel.setFullName(user.getFullName());
userModel.setEmail(user.getEmail());
userModel.setDescription(user.getDescription());
userModel.setFollowers(user.getFollowers());
return userModel;
}
/**
* Transform a Collection of {@link User} into a Collection of {@link UserModel}.
*
* @param usersCollection Objects to be transformed.
* @return List of {@link UserModel}.
*/
public Collection<UserModel> transform(Collection<User> usersCollection) {
Collection<UserModel> userModelsCollection;
if (usersCollection != null && !usersCollection.isEmpty()) {
userModelsCollection = new ArrayList<>();
for (User user : usersCollection) {
userModelsCollection.add(transform(user));
}
} else {
userModelsCollection = Collections.emptyList();
}
return userModelsCollection;
}
}
================================================
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/model/UserModel.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.presentation.model;
/**
* Class that represents a user in the presentation layer.
*/
public class UserModel {
private final int userId;
public UserModel(int userId) {
this.userId = userId;
}
private String coverUrl;
private String fullName;
private String email;
private String description;
private int followers;
public int getUserId() {
return userId;
}
public String getCoverUrl() {
return coverUrl;
}
public void setCoverUrl(String coverUrl) {
this.coverUrl = coverUrl;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public int getFollowers() {
return followers;
}
public void setFollowers(int followers) {
this.followers = followers;
}
}
================================================
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/navigation/Navigator.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.presentation.navigation;
import android.content.Context;
import android.content.Intent;
import com.fernandocejas.android10.sample.presentation.view.activity.UserDetailsActivity;
import com.fernandocejas.android10.sample.presentation.view.activity.UserListActivity;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* Class used to navigate through the application.
*/
@Singleton
public class Navigator {
@Inject
public Navigator() {
//empty
}
/**
* Goes to the user list screen.
*
* @param context A Context needed to open the destiny activity.
*/
public void navigateToUserList(Context context) {
if (context != null) {
Intent intentToLaunch = UserListActivity.getCallingIntent(context);
context.startActivity(intentToLaunch);
}
}
/**
* Goes to the user details screen.
*
* @param context A Context needed to open the destiny activity.
*/
public void navigateToUserDetails(Context context, int userId) {
if (context != null) {
Intent intentToLaunch = UserDetailsActivity.getCallingIntent(context, userId);
context.startActivity(intentToLaunch);
}
}
}
================================================
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/presenter/Presenter.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.presentation.presenter;
/**
* Interface representing a Presenter in a model view presenter (MVP) pattern.
*/
public interface Presenter {
/**
* Method that control the lifecycle of the view. It should be called in the view's
* (Activity or Fragment) onResume() method.
*/
void resume();
/**
* Method that control the lifecycle of the view. It should be called in the view's
* (Activity or Fragment) onPause() method.
*/
void pause();
/**
* Method that control the lifecycle of the view. It should be called in the view's
* (Activity or Fragment) onDestroy() method.
*/
void destroy();
}
================================================
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/presenter/UserDetailsPresenter.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.presentation.presenter;
import android.support.annotation.NonNull;
import com.fernandocejas.android10.sample.domain.User;
import com.fernandocejas.android10.sample.domain.exception.DefaultErrorBundle;
import com.fernandocejas.android10.sample.domain.exception.ErrorBundle;
import com.fernandocejas.android10.sample.domain.interactor.DefaultObserver;
import com.fernandocejas.android10.sample.domain.interactor.GetUserDetails;
import com.fernandocejas.android10.sample.domain.interactor.GetUserDetails.Params;
import com.fernandocejas.android10.sample.presentation.exception.ErrorMessageFactory;
import com.fernandocejas.android10.sample.presentation.internal.di.PerActivity;
import com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper;
import com.fernandocejas.android10.sample.presentation.model.UserModel;
import com.fernandocejas.android10.sample.presentation.view.UserDetailsView;
import javax.inject.Inject;
/**
* {@link Presenter} that controls communication between views and models of the presentation
* layer.
*/
@PerActivity
public class UserDetailsPresenter implements Presenter {
private UserDetailsView viewDetailsView;
private final GetUserDetails getUserDetailsUseCase;
private final UserModelDataMapper userModelDataMapper;
@Inject
public UserDetailsPresenter(GetUserDetails getUserDetailsUseCase,
UserModelDataMapper userModelDataMapper) {
this.getUserDetailsUseCase = getUserDetailsUseCase;
this.userModelDataMapper = userModelDataMapper;
}
public void setView(@NonNull UserDetailsView view) {
this.viewDetailsView = view;
}
@Override public void resume() {}
@Override public void pause() {}
@Override public void destroy() {
this.getUserDetailsUseCase.dispose();
this.viewDetailsView = null;
}
/**
* Initializes the presenter by showing/hiding proper views
* and retrieving user details.
*/
public void initialize(int userId) {
this.hideViewRetry();
this.showViewLoading();
this.getUserDetails(userId);
}
private void getUserDetails(int userId) {
this.getUserDetailsUseCase.execute(new UserDetailsObserver(), Params.forUser(userId));
}
private void showViewLoading() {
this.viewDetailsView.showLoading();
}
private void hideViewLoading() {
this.viewDetailsView.hideLoading();
}
private void showViewRetry() {
this.viewDetailsView.showRetry();
}
private void hideViewRetry() {
this.viewDetailsView.hideRetry();
}
private void showErrorMessage(ErrorBundle errorBundle) {
String errorMessage = ErrorMessageFactory.create(this.viewDetailsView.context(),
errorBundle.getException());
this.viewDetailsView.showError(errorMessage);
}
private void showUserDetailsInView(User user) {
final UserModel userModel = this.userModelDataMapper.transform(user);
this.viewDetailsView.renderUser(userModel);
}
private final class UserDetailsObserver extends DefaultObserver<User> {
@Override public void onComplete() {
UserDetailsPresenter.this.hideViewLoading();
}
@Override public void onError(Throwable e) {
UserDetailsPresenter.this.hideViewLoading();
UserDetailsPresenter.this.showErrorMessage(new DefaultErrorBundle((Exception) e));
UserDetailsPresenter.this.showViewRetry();
}
@Override public void onNext(User user) {
UserDetailsPresenter.this.showUserDetailsInView(user);
}
}
}
================================================
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/presenter/UserListPresenter.java
================================================
/**
* Copyright (C) 2015 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.presentation.presenter;
import android.support.annotation.NonNull;
import com.fernandocejas.android10.sample.domain.User;
import com.fernandocejas.android10.sample.domain.exception.DefaultErrorBundle;
import com.fernandocejas.android10.sample.domain.exception.ErrorBundle;
import com.fernandocejas.android10.sample.domain.interactor.DefaultObserver;
import com.fernandocejas.android10.sample.domain.interactor.GetUserList;
import com.fernandocejas.android10.sample.presentation.exception.ErrorMessageFactory;
import com.fernandocejas.android10.sample.presentation.internal.di.PerActivity;
import com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper;
import com.fernandocejas.android10.sample.presentation.model.UserModel;
import com.fernandocejas.android10.sample.presentation.view.UserListView;
import java.util.Collection;
import java.util.List;
import javax.inject.Inject;
/**
* {@link Presenter} that controls communication between views and models of the presentation
* layer.
*/
@PerActivity
public class UserListPresenter implements Presenter {
private UserListView viewListView;
private final GetUserList getUserListUseCase;
private final UserModelDataMapper userModelDataMapper;
@Inject
public UserListPresenter(GetUserList getUserListUserCase,
UserModelDataMapper userModelDataMapper) {
this.getUserListUseCase = getUserListUserCase;
this.userModelDataMapper = userModelDataMapper;
}
public void setView(@NonNull UserListView view) {
this.viewListView = view;
}
@Override public void resume() {}
@Override public void pause() {}
@Override public void destroy() {
this.getUserListUseCase.dispose();
this.viewListView = null;
}
/**
* Initializes the presenter by start retrieving the user list.
*/
public void initialize() {
this.loadUserList();
}
/**
* Loads all users.
*/
private void loadUserList() {
this.hideViewRetry();
this.showViewLoading();
this.getUserList();
}
public void onUserClicked(UserModel userModel) {
this.viewListView.viewUser(userModel);
}
private void showViewLoading() {
this.viewListView.showLoading();
}
private void hideViewLoading() {
this.viewListView.hideLoading();
}
private void showViewRetry() {
this.viewListView.showRetry();
}
private void hideViewRetry() {
this.viewListView.hideRetry();
}
private void showErrorMessage(ErrorBundle errorBundle) {
String errorMessage = ErrorMessageFactory.create(this.viewListView.context(),
errorBundle.getException());
this.viewListView.showError(errorMessage);
}
private void showUsersCollectionInView(Collection<User> usersCollection) {
final Collection<UserModel> userModelsCollection =
this.userModelDataMapper.transform(usersCollection);
this.viewListView.renderUserList(userModelsCollection);
}
private void getUserList() {
this.getUserListUseCase.execute(new UserListObserver(), null);
}
private final class UserListObserver extends DefaultObserver<List<User>> {
@Override public void onComplete() {
UserListPresenter.this.hideViewLoading();
}
@Override public void onError(Throwable e) {
UserListPresenter.this.hideViewLoading();
UserListPresenter.this.showErrorMessage(new DefaultErrorBundle((Exception) e));
UserListPresenter.this.showViewRetry();
}
@Override public void onNext(List<User> users) {
UserListPresenter.this.showUsersCollectionInView(users);
}
}
}
================================================
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/LoadDataView.java
================================================
/**
* Copyright (C) 2014 android10.org. All rights reserved.
* @author Fernando Cejas (the android10 coder)
*/
package com.fernandocejas.android10.sample.presentation.view;
import android.content.Context;
/**
* Interface representing a View that will use to load data.
*/
public interface LoadDataView {
/**
* Show a view with a progress bar indicating a loading process.
*/
void showLoading();
/**
* Hide a loading view.
*/
void hideLoading();
/**
* Show a retry view in case of an error when retrieving data.
*/
void showRetry();
/**
* Hide a retry view shown if there was an error when retrieving data.
*/
void hideRetry();
/**
* Show an error message
*
* @param message A string representing an error.
*/
void showError(String message);
/**
* Get a {@link android.content.Context}.
*/
Context context();
}
================================================
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/UserDetailsView.java
================================================
/**
* Copyright (C) 2014 android10.org. All rights reserved.
* @author Fernando Cejas (the android10 coder)
*/
package com.fernandocejas.android10.sample.presentation.view;
import com.fernandocejas.android10.sample.presentation.model.UserModel;
/**
* Interface representing a View in a model view presenter (MVP) pattern.
* In this case is used as a view representing a user profile.
*/
public interface UserDetailsView extends LoadDataView {
/**
* Render a user in the UI.
*
* @param user The {@link UserModel} that will be shown.
*/
void renderUser(UserModel user);
}
================================================
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/UserListView.java
================================================
/**
* Copyright (C) 2014 android10.org. All rights reserved.
* @author Fernando Cejas (the android10 coder)
*/
package com.fernandocejas.android10.sample.presentation.view;
import com.fernandocejas.android10.sample.presentation.model.UserModel;
import java.util.Collection;
/**
* Interface representing a View in a model view presenter (MVP) pattern.
* In this case is used as a view representing a list of {@link UserModel}.
*/
public interface UserListView extends LoadDataView {
/**
* Render a user list in the UI.
*
* @param userModelCollection The collection of {@link UserModel} that will be shown.
*/
void renderUserList(Collection<UserModel> userModelCollection);
/**
* View a {@link UserModel} profile/details.
*
* @param userModel The user that will be shown.
*/
void viewUser(UserModel userModel);
}
================================================
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/BaseActivity.java
================================================
package com.fernandocejas.android10.sample.presentation.view.activity;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.os.Bundle;
import com.fernandocejas.android10.sample.presentation.AndroidApplication;
import com.fernandocejas.android10.sample.presentation.internal.di.components.ApplicationComponent;
import com.fernandocejas.android10.sample.presentation.internal.di.modules.ActivityModule;
import com.fernandocejas.android10.sample.presentation.navigation.Navigator;
import javax.inject.Inject;
/**
* Base {@link android.app.Activity} class for every Activity in this application.
*/
public abstract class BaseActivity extends Activity {
@Inject Navigator navigator;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.getApplicationComponent().inject(this);
}
/**
* Adds a {@link Fragment} to this activity's layout.
*
* @param containerViewId The container view to where add the fragment.
* @param fragment The fragment to be added.
*/
protected void addFragment(int containerViewId, Fragment fragment) {
final FragmentTransaction fragmentTransaction = this.getFragmentManager().beginTransaction();
fragmentTransaction.add(containerViewId, fragment);
fragmentTransaction.commit();
}
/**
* Get the Main Application component for dependency injection.
*
* @return {@link com.fernandocejas.android10.sample.presentation.internal.di.components.ApplicationComponent}
*/
protected ApplicationComponent getApplicationComponent() {
return ((AndroidApplication) getApplication()).getApplicationComponent();
}
/**
* Get an Activity module for dependency injection.
*
* @return {@link com.fernandocejas.android10.sample.presentation.internal.di.modules.ActivityModule}
*/
protected ActivityModule getActivityModule() {
return new ActivityModule(this);
}
}
================================================
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/MainActivity.java
================================================
package com.fernandocejas.android10.sample.presentation.view.activity;
import android.os.Bundle;
import android.widget.Button;
import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;
import com.fernandocejas.android10.sample.presentation.R;
/**
* Main application screen. This is the app entry point.
*/
public class MainActivity extends BaseActivity {
@Bind(R.id.btn_LoadData) Button btn_LoadData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
}
/**
* Goes to the user list screen.
*/
@OnClick(R.id.btn_LoadData)
void navigateToUserList() {
this.navigator.navigateToUserList(this);
}
}
================================================
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/UserDetailsActivity.java
================================================
/**
* Copyright (C) 2014 android10.org. All rights reserved.
*
* @author Fernando Cejas (the android10 coder)
*/
package com.fernandocejas.android10.sample.presentation.view.activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.Window;
import com.fernandocejas.android10.sample.presentation.R;
import com.fernandocejas.android10.sample.presentation.internal.di.HasComponent;
import com.fernandocejas.android10.sample.presentation.internal.di.components.DaggerUserComponent;
import com.fernandocejas.android10.sample.presentation.internal.di.components.UserComponent;
import com.fernandocejas.android10.sample.presentation.view.fragment.UserDetailsFragment;
/**
* Activity that shows details of a certain user.
*/
public class UserDetailsActivity extends BaseActivity implements HasComponent<UserComponent> {
private static final String INTENT_EXTRA_PARAM_USER_ID = "org.android10.INTENT_PARAM_USER_ID";
private static final String INSTANCE_STATE_PARAM_USER_ID = "org.android10.STATE_PARAM_USER_ID";
public static Intent getCallingIntent(Context context, int userId) {
Intent callingIntent = new Intent(context, UserDetailsActivity.class);
callingIntent.putExtra(INTENT_EXTRA_PARAM_USER_ID, userId);
return callingIntent;
}
private int userId;
private UserComponent userComponent;
@Override protected void onCreate(
gitextract_60j9_cvf/ ├── .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/ │ │ │ └── Serializer.java │ │ ├── entity/ │ │ │ ├── UserEntity.java │ │ │ └── mapper/ │ │ │ ├── UserEntityDataMapper.java │ │ │ └── UserEntityJsonMapper.java │ │ ├── exception/ │ │ │ ├── NetworkConnectionException.java │ │ │ ├── RepositoryErrorBundle.java │ │ │ └── UserNotFoundException.java │ │ ├── executor/ │ │ │ └── JobExecutor.java │ │ ├── net/ │ │ │ ├── ApiConnection.java │ │ │ ├── RestApi.java │ │ │ └── RestApiImpl.java │ │ └── repository/ │ │ ├── UserDataRepository.java │ │ └── datasource/ │ │ ├── CloudUserDataStore.java │ │ ├── DiskUserDataStore.java │ │ ├── UserDataStore.java │ │ └── UserDataStoreFactory.java │ └── test/ │ └── java/ │ └── com/ │ └── fernandocejas/ │ └── android10/ │ └── sample/ │ └── data/ │ ├── ApplicationStub.java │ ├── ApplicationTestCase.java │ ├── cache/ │ │ ├── FileManagerTest.java │ │ └── serializer/ │ │ └── SerializerTest.java │ ├── entity/ │ │ └── mapper/ │ │ ├── UserEntityDataMapperTest.java │ │ └── UserEntityJsonMapperTest.java │ ├── exception/ │ │ └── RepositoryErrorBundleTest.java │ └── repository/ │ ├── UserDataRepositoryTest.java │ └── datasource/ │ ├── CloudUserDataStoreTest.java │ ├── DiskUserDataStoreTest.java │ └── UserDataStoreFactoryTest.java ├── domain/ │ ├── build.gradle │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── com/ │ │ └── fernandocejas/ │ │ └── android10/ │ │ └── sample/ │ │ └── domain/ │ │ ├── User.java │ │ ├── exception/ │ │ │ ├── DefaultErrorBundle.java │ │ │ └── ErrorBundle.java │ │ ├── executor/ │ │ │ ├── PostExecutionThread.java │ │ │ └── ThreadExecutor.java │ │ ├── interactor/ │ │ │ ├── DefaultObserver.java │ │ │ ├── GetUserDetails.java │ │ │ ├── GetUserList.java │ │ │ └── UseCase.java │ │ └── repository/ │ │ └── UserRepository.java │ └── test/ │ └── java/ │ └── com/ │ └── fernandocejas/ │ └── android10/ │ └── sample/ │ └── domain/ │ ├── UserTest.java │ ├── exception/ │ │ └── DefaultErrorBundleTest.java │ └── interactor/ │ ├── GetUserDetailsTest.java │ ├── GetUserListTest.java │ └── UseCaseTest.java ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.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 │ │ ├── presenter/ │ │ │ ├── UserDetailsPresenterTest.java │ │ │ └── UserListPresenterTest.java │ │ └── view/ │ │ └── activity/ │ │ ├── UserDetailsActivityTest.java │ │ └── UserListActivityTest.java │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── com/ │ │ └── fernandocejas/ │ │ └── android10/ │ │ └── sample/ │ │ └── presentation/ │ │ ├── AndroidApplication.java │ │ ├── UIThread.java │ │ ├── exception/ │ │ │ └── ErrorMessageFactory.java │ │ ├── internal/ │ │ │ └── di/ │ │ │ ├── HasComponent.java │ │ │ ├── PerActivity.java │ │ │ ├── components/ │ │ │ │ ├── ActivityComponent.java │ │ │ │ ├── ApplicationComponent.java │ │ │ │ └── UserComponent.java │ │ │ └── modules/ │ │ │ ├── ActivityModule.java │ │ │ ├── ApplicationModule.java │ │ │ └── UserModule.java │ │ ├── mapper/ │ │ │ └── UserModelDataMapper.java │ │ ├── model/ │ │ │ └── UserModel.java │ │ ├── navigation/ │ │ │ └── Navigator.java │ │ ├── presenter/ │ │ │ ├── Presenter.java │ │ │ ├── UserDetailsPresenter.java │ │ │ └── UserListPresenter.java │ │ └── view/ │ │ ├── LoadDataView.java │ │ ├── UserDetailsView.java │ │ ├── UserListView.java │ │ ├── activity/ │ │ │ ├── BaseActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── UserDetailsActivity.java │ │ │ └── UserListActivity.java │ │ ├── adapter/ │ │ │ ├── UsersAdapter.java │ │ │ └── UsersLayoutManager.java │ │ ├── component/ │ │ │ └── AutoLoadImageView.java │ │ └── fragment/ │ │ ├── BaseFragment.java │ │ ├── UserDetailsFragment.java │ │ └── UserListFragment.java │ └── res/ │ ├── drawable/ │ │ └── selector_item_user.xml │ ├── layout/ │ │ ├── activity_layout.xml │ │ ├── activity_main.xml │ │ ├── fragment_user_details.xml │ │ ├── fragment_user_list.xml │ │ ├── row_user.xml │ │ ├── view_progress.xml │ │ ├── view_retry.xml │ │ └── view_user_details.xml │ └── values/ │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml └── settings.gradle
SYMBOL INDEX (478 symbols across 80 files)
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/cache/FileManager.java
class FileManager (line 31) | @Singleton
method FileManager (line 34) | @Inject
method writeToFile (line 44) | void writeToFile(File file, String fileContent) {
method readFileContent (line 64) | String readFileContent(File file) {
method exists (line 89) | boolean exists(File file) {
method clearDirectory (line 100) | boolean clearDirectory(File directory) {
method writeToPreferences (line 118) | void writeToPreferences(Context context, String preferenceFileName, St...
method getFromPreferences (line 136) | long getFromPreferences(Context context, String preferenceFileName, St...
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCache.java
type UserCache (line 24) | public interface UserCache {
method get (line 30) | Observable<UserEntity> get(final int userId);
method put (line 37) | void put(UserEntity userEntity);
method isCached (line 45) | boolean isCached(final int userId);
method isExpired (line 52) | boolean isExpired();
method evictAll (line 57) | void evictAll();
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCacheImpl.java
class UserCacheImpl (line 31) | @Singleton
method UserCacheImpl (line 53) | @Inject UserCacheImpl(Context context, Serializer serializer,
method get (line 65) | @Override public Observable<UserEntity> get(final int userId) {
method put (line 81) | @Override public void put(UserEntity userEntity) {
method isCached (line 92) | @Override public boolean isCached(int userId) {
method isExpired (line 97) | @Override public boolean isExpired() {
method evictAll (line 110) | @Override public void evictAll() {
method buildFile (line 120) | private File buildFile(int userId) {
method setLastCacheUpdateTimeMillis (line 133) | private void setLastCacheUpdateTimeMillis() {
method getLastCacheUpdateTimeMillis (line 142) | private long getLastCacheUpdateTimeMillis() {
method executeAsynchronously (line 152) | private void executeAsynchronously(Runnable runnable) {
class CacheWriter (line 159) | private static class CacheWriter implements Runnable {
method CacheWriter (line 164) | CacheWriter(FileManager fileManager, File fileToWrite, String fileCo...
method run (line 170) | @Override public void run() {
class CacheEvictor (line 178) | private static class CacheEvictor implements Runnable {
method CacheEvictor (line 182) | CacheEvictor(FileManager fileManager, File cacheDir) {
method run (line 187) | @Override public void run() {
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/cache/serializer/Serializer.java
class Serializer (line 25) | @Singleton
method Serializer (line 30) | @Inject Serializer() {}
method serialize (line 37) | public String serialize(Object object, Class clazz) {
method deserialize (line 46) | public <T> T deserialize(String string, Class<T> clazz) {
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/entity/UserEntity.java
class UserEntity (line 23) | public class UserEntity {
method UserEntity (line 43) | public UserEntity() {
method getUserId (line 47) | public int getUserId() {
method setUserId (line 51) | public void setUserId(int userId) {
method getCoverUrl (line 55) | public String getCoverUrl() {
method getFullname (line 59) | public String getFullname() {
method setFullname (line 63) | public void setFullname(String fullname) {
method getDescription (line 67) | public String getDescription() {
method getFollowers (line 71) | public int getFollowers() {
method getEmail (line 75) | public String getEmail() {
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityDataMapper.java
class UserEntityDataMapper (line 30) | @Singleton
method UserEntityDataMapper (line 33) | @Inject
method transform (line 42) | public User transform(UserEntity userEntity) {
method transform (line 61) | public List<User> transform(Collection<UserEntity> userEntityCollectio...
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityJsonMapper.java
class UserEntityJsonMapper (line 29) | public class UserEntityJsonMapper {
method UserEntityJsonMapper (line 33) | @Inject
method transformUserEntity (line 45) | public UserEntity transformUserEntity(String userJsonResponse) throws ...
method transformUserEntityCollection (line 57) | public List<UserEntity> transformUserEntityCollection(String userListJ...
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/exception/NetworkConnectionException.java
class NetworkConnectionException (line 21) | public class NetworkConnectionException extends Exception {
method NetworkConnectionException (line 23) | public NetworkConnectionException() {
method NetworkConnectionException (line 27) | public NetworkConnectionException(final Throwable cause) {
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundle.java
class RepositoryErrorBundle (line 23) | class RepositoryErrorBundle implements ErrorBundle {
method RepositoryErrorBundle (line 27) | RepositoryErrorBundle(Exception exception) {
method getException (line 31) | @Override
method getErrorMessage (line 36) | @Override
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/exception/UserNotFoundException.java
class UserNotFoundException (line 21) | public class UserNotFoundException extends Exception {
method UserNotFoundException (line 22) | public UserNotFoundException() {
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/executor/JobExecutor.java
class JobExecutor (line 30) | @Singleton
method JobExecutor (line 34) | @Inject
method execute (line 40) | @Override public void execute(@NonNull Runnable runnable) {
class JobThreadFactory (line 44) | private static class JobThreadFactory implements ThreadFactory {
method newThread (line 47) | @Override public Thread newThread(@NonNull Runnable runnable) {
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/net/ApiConnection.java
class ApiConnection (line 32) | class ApiConnection implements Callable<String> {
method ApiConnection (line 40) | private ApiConnection(String url) throws MalformedURLException {
method createGET (line 44) | static ApiConnection createGET(String url) throws MalformedURLException {
method requestSyncCall (line 54) | @Nullable
method connectToApi (line 60) | private void connectToApi() {
method createClient (line 75) | private OkHttpClient createClient() {
method call (line 83) | @Override public String call() throws Exception {
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApi.java
type RestApi (line 25) | public interface RestApi {
method userEntityList (line 37) | Observable<List<UserEntity>> userEntityList();
method userEntityById (line 44) | Observable<UserEntity> userEntityById(final int userId);
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApiImpl.java
class RestApiImpl (line 31) | public class RestApiImpl implements RestApi {
method RestApiImpl (line 42) | public RestApiImpl(Context context, UserEntityJsonMapper userEntityJso...
method userEntityList (line 50) | @Override public Observable<List<UserEntity>> userEntityList() {
method userEntityById (line 71) | @Override public Observable<UserEntity> userEntityById(final int userI...
method getUserEntitiesFromApi (line 91) | private String getUserEntitiesFromApi() throws MalformedURLException {
method getUserDetailsFromApi (line 95) | private String getUserDetailsFromApi(int userId) throws MalformedURLEx...
method isThereInternetConnection (line 105) | private boolean isThereInternetConnection() {
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/repository/UserDataRepository.java
class UserDataRepository (line 31) | @Singleton
method UserDataRepository (line 43) | @Inject
method users (line 50) | @Override public Observable<List<User>> users() {
method user (line 56) | @Override public Observable<User> user(int userId) {
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/CloudUserDataStore.java
class CloudUserDataStore (line 27) | class CloudUserDataStore implements UserDataStore {
method CloudUserDataStore (line 38) | CloudUserDataStore(RestApi restApi, UserCache userCache) {
method userEntityList (line 43) | @Override public Observable<List<UserEntity>> userEntityList() {
method userEntityDetails (line 47) | @Override public Observable<UserEntity> userEntityDetails(final int us...
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/DiskUserDataStore.java
class DiskUserDataStore (line 26) | class DiskUserDataStore implements UserDataStore {
method DiskUserDataStore (line 35) | DiskUserDataStore(UserCache userCache) {
method userEntityList (line 39) | @Override public Observable<List<UserEntity>> userEntityList() {
method userEntityDetails (line 44) | @Override public Observable<UserEntity> userEntityDetails(final int us...
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStore.java
type UserDataStore (line 25) | public interface UserDataStore {
method userEntityList (line 29) | Observable<List<UserEntity>> userEntityList();
method userEntityDetails (line 36) | Observable<UserEntity> userEntityDetails(final int userId);
FILE: data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStoreFactory.java
class UserDataStoreFactory (line 30) | @Singleton
method UserDataStoreFactory (line 36) | @Inject
method create (line 45) | public UserDataStore create(int userId) {
method createCloudDataStore (line 60) | public UserDataStore createCloudDataStore() {
FILE: data/src/test/java/com/fernandocejas/android10/sample/data/ApplicationStub.java
class ApplicationStub (line 20) | public class ApplicationStub extends Application {}
FILE: data/src/test/java/com/fernandocejas/android10/sample/data/ApplicationTestCase.java
class ApplicationTestCase (line 32) | @RunWith(RobolectricTestRunner.class)
method context (line 41) | public static Context context() {
method cacheDir (line 45) | public static File cacheDir() {
FILE: data/src/test/java/com/fernandocejas/android10/sample/data/cache/FileManagerTest.java
class FileManagerTest (line 28) | public class FileManagerTest extends ApplicationTestCase {
method setUp (line 32) | @Before
method tearDown (line 37) | @After
method testWriteToFile (line 44) | @Test
method testFileContent (line 54) | @Test
method createDummyFile (line 65) | private File createDummyFile() {
FILE: data/src/test/java/com/fernandocejas/android10/sample/data/cache/serializer/SerializerTest.java
class SerializerTest (line 28) | @RunWith(MockitoJUnitRunner.class)
method setUp (line 42) | @Before
method testSerializeHappyCase (line 47) | @Test
method testDesearializeHappyCase (line 58) | @Test
FILE: data/src/test/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityDataMapperTest.java
class UserEntityDataMapperTest (line 33) | @RunWith(MockitoJUnitRunner.class)
method setUp (line 41) | @Before
method testTransformUserEntity (line 46) | @Test
method testTransformUserEntityCollection (line 56) | @Test
method createFakeUserEntity (line 72) | private UserEntity createFakeUserEntity() {
FILE: data/src/test/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityJsonMapperTest.java
class UserEntityJsonMapperTest (line 32) | @RunWith(MockitoJUnitRunner.class)
method setUp (line 59) | @Before
method testTransformUserEntityHappyCase (line 64) | @Test
method testTransformUserEntityCollectionHappyCase (line 73) | @Test
method testTransformUserEntityNotValidResponse (line 84) | @Test
method testTransformUserEntityCollectionNotValidResponse (line 90) | @Test
FILE: data/src/test/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundleTest.java
class RepositoryErrorBundleTest (line 26) | @RunWith(MockitoJUnitRunner.class)
method setUp (line 33) | @Before
method testGetErrorMessageInteraction (line 38) | @Test
FILE: data/src/test/java/com/fernandocejas/android10/sample/data/repository/UserDataRepositoryTest.java
class UserDataRepositoryTest (line 36) | @RunWith(MockitoJUnitRunner.class)
method setUp (line 49) | @Before
method testGetUsersHappyCase (line 56) | @Test
method testGetUserHappyCase (line 68) | @Test
FILE: data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/CloudUserDataStoreTest.java
class CloudUserDataStoreTest (line 31) | @RunWith(MockitoJUnitRunner.class)
method setUp (line 41) | @Before
method testGetUserEntityListFromApi (line 46) | @Test
method testGetUserEntityDetailsFromApi (line 52) | @Test
FILE: data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/DiskUserDataStoreTest.java
class DiskUserDataStoreTest (line 29) | @RunWith(MockitoJUnitRunner.class)
method setUp (line 40) | @Before
method testGetUserEntityListUnsupported (line 45) | @Test
method testGetUserEntityDetailesFromCache (line 51) | @Test
FILE: data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStoreFactoryTest.java
class UserDataStoreFactoryTest (line 32) | public class UserDataStoreFactoryTest extends ApplicationTestCase {
method setUp (line 40) | @Before
method testCreateDiskDataStore (line 45) | @Test
method testCreateCloudDataStore (line 59) | @Test
FILE: domain/src/main/java/com/fernandocejas/android10/sample/domain/User.java
class User (line 21) | public class User {
method User (line 25) | public User(int userId) {
method getUserId (line 35) | public int getUserId() {
method getCoverUrl (line 39) | public String getCoverUrl() {
method setCoverUrl (line 43) | public void setCoverUrl(String coverUrl) {
method getFullName (line 47) | public String getFullName() {
method setFullName (line 51) | public void setFullName(String fullName) {
method getEmail (line 55) | public String getEmail() {
method setEmail (line 59) | public void setEmail(String email) {
method getDescription (line 63) | public String getDescription() {
method setDescription (line 67) | public void setDescription(String description) {
method getFollowers (line 71) | public int getFollowers() {
method setFollowers (line 75) | public void setFollowers(int followers) {
FILE: domain/src/main/java/com/fernandocejas/android10/sample/domain/exception/DefaultErrorBundle.java
class DefaultErrorBundle (line 21) | public class DefaultErrorBundle implements ErrorBundle {
method DefaultErrorBundle (line 27) | public DefaultErrorBundle(Exception exception) {
method getException (line 31) | @Override
method getErrorMessage (line 36) | @Override
FILE: domain/src/main/java/com/fernandocejas/android10/sample/domain/exception/ErrorBundle.java
type ErrorBundle (line 21) | public interface ErrorBundle {
method getException (line 22) | Exception getException();
method getErrorMessage (line 24) | String getErrorMessage();
FILE: domain/src/main/java/com/fernandocejas/android10/sample/domain/executor/PostExecutionThread.java
type PostExecutionThread (line 25) | public interface PostExecutionThread {
method getScheduler (line 26) | Scheduler getScheduler();
FILE: domain/src/main/java/com/fernandocejas/android10/sample/domain/executor/ThreadExecutor.java
type ThreadExecutor (line 25) | public interface ThreadExecutor extends Executor {}
FILE: domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/DefaultObserver.java
class DefaultObserver (line 23) | public class DefaultObserver<T> extends DisposableObserver<T> {
method onNext (line 24) | @Override public void onNext(T t) {
method onComplete (line 28) | @Override public void onComplete() {
method onError (line 32) | @Override public void onError(Throwable exception) {
FILE: domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetails.java
class GetUserDetails (line 30) | public class GetUserDetails extends UseCase<User, GetUserDetails.Params> {
method GetUserDetails (line 34) | @Inject
method buildUseCaseObservable (line 41) | @Override Observable<User> buildUseCaseObservable(Params params) {
class Params (line 46) | public static final class Params {
method Params (line 50) | private Params(int userId) {
method forUser (line 54) | public static Params forUser(int userId) {
FILE: domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java
class GetUserList (line 30) | public class GetUserList extends UseCase<List<User>, Void> {
method GetUserList (line 34) | @Inject
method buildUseCaseObservable (line 41) | @Override Observable<List<User>> buildUseCaseObservable(Void unused) {
FILE: domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/UseCase.java
class UseCase (line 35) | public abstract class UseCase<T, Params> {
method UseCase (line 41) | UseCase(ThreadExecutor threadExecutor, PostExecutionThread postExecuti...
method buildUseCaseObservable (line 50) | abstract Observable<T> buildUseCaseObservable(Params params);
method execute (line 59) | public void execute(DisposableObserver<T> observer, Params params) {
method dispose (line 70) | public void dispose() {
method addDisposable (line 79) | private void addDisposable(Disposable disposable) {
FILE: domain/src/main/java/com/fernandocejas/android10/sample/domain/repository/UserRepository.java
type UserRepository (line 25) | public interface UserRepository {
method users (line 29) | Observable<List<User>> users();
method user (line 36) | Observable<User> user(final int userId);
FILE: domain/src/test/java/com/fernandocejas/android10/sample/domain/UserTest.java
class UserTest (line 23) | public class UserTest {
method setUp (line 29) | @Before
method testUserConstructorHappyCase (line 34) | @Test
FILE: domain/src/test/java/com/fernandocejas/android10/sample/domain/exception/DefaultErrorBundleTest.java
class DefaultErrorBundleTest (line 26) | @RunWith(MockitoJUnitRunner.class)
method setUp (line 32) | @Before
method testGetErrorMessageInteraction (line 37) | @Test
FILE: domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetailsTest.java
class GetUserDetailsTest (line 34) | @RunWith(MockitoJUnitRunner.class)
method setUp (line 47) | @Before
method testGetUserDetailsUseCaseObservableHappyCase (line 53) | @Test
method testShouldFailWhenNoOrEmptyParameters (line 63) | @Test
FILE: domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserListTest.java
class GetUserListTest (line 31) | @RunWith(MockitoJUnitRunner.class)
method setUp (line 40) | @Before
method testGetUserListUseCaseObservableHappyCase (line 46) | @Test
FILE: domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/UseCaseTest.java
class UseCaseTest (line 34) | @RunWith(MockitoJUnitRunner.class)
method setUp (line 46) | @Before
method testBuildUseCaseObservableReturnCorrectResult (line 53) | @Test
method testSubscriptionWhenExecutingUseCase (line 60) | @Test
method testShouldFailWhenExecuteWithNullObserver (line 68) | @Test
class UseCaseTestClass (line 74) | private static class UseCaseTestClass extends UseCase<Object, Params> {
method UseCaseTestClass (line 76) | UseCaseTestClass(ThreadExecutor threadExecutor, PostExecutionThread ...
method buildUseCaseObservable (line 80) | @Override Observable<Object> buildUseCaseObservable(Params params) {
method execute (line 84) | @Override
class TestDisposableObserver (line 90) | private static class TestDisposableObserver<T> extends DisposableObser...
method onNext (line 93) | @Override public void onNext(T value) {
method onError (line 97) | @Override public void onError(Throwable e) {
method onComplete (line 101) | @Override public void onComplete() {
class Params (line 106) | private static class Params {
method Params (line 108) | private Params() {}
FILE: presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/exception/ErrorMessageFactoryTest.java
class ErrorMessageFactoryTest (line 28) | public class ErrorMessageFactoryTest extends AndroidTestCase {
method setUp (line 30) | @Override protected void setUp() throws Exception {
method testNetworkConnectionErrorMessage (line 34) | public void testNetworkConnectionErrorMessage() {
method testUserNotFoundErrorMessage (line 42) | public void testUserNotFoundErrorMessage() {
FILE: presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/mapper/UserModelDataMapperTest.java
class UserModelDataMapperTest (line 31) | public class UserModelDataMapperTest extends TestCase {
method setUp (line 38) | @Override protected void setUp() throws Exception {
method testTransformUser (line 43) | public void testTransformUser() {
method testTransformUserCollection (line 52) | public void testTransformUserCollection() {
method createFakeUser (line 67) | private User createFakeUser() {
FILE: presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/presenter/UserDetailsPresenterTest.java
class UserDetailsPresenterTest (line 35) | @RunWith(MockitoJUnitRunner.class)
method setUp (line 47) | @Before
method testUserDetailsPresenterInitialize (line 53) | @Test
FILE: presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/presenter/UserListPresenterTest.java
class UserListPresenterTest (line 34) | @RunWith(MockitoJUnitRunner.class)
method setUp (line 44) | @Before
method testUserListPresenterInitialize (line 50) | @Test
FILE: presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/view/activity/UserDetailsActivityTest.java
class UserDetailsActivityTest (line 34) | public class UserDetailsActivityTest extends ActivityInstrumentationTest...
method UserDetailsActivityTest (line 40) | public UserDetailsActivityTest() {
method setUp (line 44) | @Override protected void setUp() throws Exception {
method tearDown (line 50) | @Override protected void tearDown() throws Exception {
method testContainsUserDetailsFragment (line 54) | public void testContainsUserDetailsFragment() {
method testContainsProperTitle (line 60) | public void testContainsProperTitle() {
method testLoadUserHappyCaseViews (line 66) | public void testLoadUserHappyCaseViews() {
method testLoadUserHappyCaseData (line 75) | public void testLoadUserHappyCaseData() {
method createTargetIntent (line 81) | private Intent createTargetIntent() {
FILE: presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/view/activity/UserListActivityTest.java
class UserListActivityTest (line 28) | public class UserListActivityTest extends ActivityInstrumentationTestCas...
method UserListActivityTest (line 32) | public UserListActivityTest() {
method setUp (line 36) | @Override protected void setUp() throws Exception {
method tearDown (line 42) | @Override protected void tearDown() throws Exception {
method testContainsUserListFragment (line 46) | public void testContainsUserListFragment() {
method testContainsProperTitle (line 52) | public void testContainsProperTitle() {
method createTargetIntent (line 58) | private Intent createTargetIntent() {
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/AndroidApplication.java
class AndroidApplication (line 27) | public class AndroidApplication extends Application {
method onCreate (line 31) | @Override public void onCreate() {
method initializeInjector (line 37) | private void initializeInjector() {
method getApplicationComponent (line 43) | public ApplicationComponent getApplicationComponent() {
method initializeLeakDetection (line 47) | private void initializeLeakDetection() {
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/UIThread.java
class UIThread (line 28) | @Singleton
method UIThread (line 31) | @Inject
method getScheduler (line 34) | @Override public Scheduler getScheduler() {
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/exception/ErrorMessageFactory.java
class ErrorMessageFactory (line 26) | public class ErrorMessageFactory {
method ErrorMessageFactory (line 28) | private ErrorMessageFactory() {
method create (line 39) | public static String create(Context context, Exception exception) {
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/HasComponent.java
type HasComponent (line 21) | public interface HasComponent<C> {
method getComponent (line 22) | C getComponent();
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/components/ActivityComponent.java
type ActivityComponent (line 30) | @PerActivity
method activity (line 34) | Activity activity();
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/components/ApplicationComponent.java
type ApplicationComponent (line 30) | @Singleton // Constraints this component to one-per-application or unsco...
method inject (line 33) | void inject(BaseActivity baseActivity);
method context (line 36) | Context context();
method threadExecutor (line 37) | ThreadExecutor threadExecutor();
method postExecutionThread (line 38) | PostExecutionThread postExecutionThread();
method userRepository (line 39) | UserRepository userRepository();
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/components/UserComponent.java
type UserComponent (line 29) | @PerActivity
method inject (line 32) | void inject(UserListFragment userListFragment);
method inject (line 33) | void inject(UserDetailsFragment userDetailsFragment);
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/modules/ActivityModule.java
class ActivityModule (line 26) | @Module
method ActivityModule (line 30) | public ActivityModule(Activity activity) {
method activity (line 37) | @Provides @PerActivity Activity activity() {
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/modules/ApplicationModule.java
class ApplicationModule (line 36) | @Module
method ApplicationModule (line 40) | public ApplicationModule(AndroidApplication application) {
method provideApplicationContext (line 44) | @Provides @Singleton Context provideApplicationContext() {
method provideThreadExecutor (line 48) | @Provides @Singleton ThreadExecutor provideThreadExecutor(JobExecutor ...
method providePostExecutionThread (line 52) | @Provides @Singleton PostExecutionThread providePostExecutionThread(UI...
method provideUserCache (line 56) | @Provides @Singleton UserCache provideUserCache(UserCacheImpl userCach...
method provideUserRepository (line 60) | @Provides @Singleton UserRepository provideUserRepository(UserDataRepo...
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/modules/UserModule.java
class UserModule (line 23) | @Module
method UserModule (line 26) | public UserModule() {}
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mapper/UserModelDataMapper.java
class UserModelDataMapper (line 30) | @PerActivity
method UserModelDataMapper (line 33) | @Inject
method transform (line 42) | public UserModel transform(User user) {
method transform (line 62) | public Collection<UserModel> transform(Collection<User> usersCollectio...
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/model/UserModel.java
class UserModel (line 21) | public class UserModel {
method UserModel (line 25) | public UserModel(int userId) {
method getUserId (line 35) | public int getUserId() {
method getCoverUrl (line 39) | public String getCoverUrl() {
method setCoverUrl (line 43) | public void setCoverUrl(String coverUrl) {
method getFullName (line 47) | public String getFullName() {
method setFullName (line 51) | public void setFullName(String fullName) {
method getEmail (line 55) | public String getEmail() {
method setEmail (line 59) | public void setEmail(String email) {
method getDescription (line 63) | public String getDescription() {
method setDescription (line 67) | public void setDescription(String description) {
method getFollowers (line 71) | public int getFollowers() {
method setFollowers (line 75) | public void setFollowers(int followers) {
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/navigation/Navigator.java
class Navigator (line 28) | @Singleton
method Navigator (line 31) | @Inject
method navigateToUserList (line 41) | public void navigateToUserList(Context context) {
method navigateToUserDetails (line 53) | public void navigateToUserDetails(Context context, int userId) {
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/presenter/Presenter.java
type Presenter (line 21) | public interface Presenter {
method resume (line 26) | void resume();
method pause (line 32) | void pause();
method destroy (line 38) | void destroy();
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/presenter/UserDetailsPresenter.java
class UserDetailsPresenter (line 36) | @PerActivity
method UserDetailsPresenter (line 44) | @Inject
method setView (line 51) | public void setView(@NonNull UserDetailsView view) {
method resume (line 55) | @Override public void resume() {}
method pause (line 57) | @Override public void pause() {}
method destroy (line 59) | @Override public void destroy() {
method initialize (line 68) | public void initialize(int userId) {
method getUserDetails (line 74) | private void getUserDetails(int userId) {
method showViewLoading (line 78) | private void showViewLoading() {
method hideViewLoading (line 82) | private void hideViewLoading() {
method showViewRetry (line 86) | private void showViewRetry() {
method hideViewRetry (line 90) | private void hideViewRetry() {
method showErrorMessage (line 94) | private void showErrorMessage(ErrorBundle errorBundle) {
method showUserDetailsInView (line 100) | private void showUserDetailsInView(User user) {
class UserDetailsObserver (line 105) | private final class UserDetailsObserver extends DefaultObserver<User> {
method onComplete (line 107) | @Override public void onComplete() {
method onError (line 111) | @Override public void onError(Throwable e) {
method onNext (line 117) | @Override public void onNext(User user) {
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/presenter/UserListPresenter.java
class UserListPresenter (line 37) | @PerActivity
method UserListPresenter (line 45) | @Inject
method setView (line 52) | public void setView(@NonNull UserListView view) {
method resume (line 56) | @Override public void resume() {}
method pause (line 58) | @Override public void pause() {}
method destroy (line 60) | @Override public void destroy() {
method initialize (line 68) | public void initialize() {
method loadUserList (line 75) | private void loadUserList() {
method onUserClicked (line 81) | public void onUserClicked(UserModel userModel) {
method showViewLoading (line 85) | private void showViewLoading() {
method hideViewLoading (line 89) | private void hideViewLoading() {
method showViewRetry (line 93) | private void showViewRetry() {
method hideViewRetry (line 97) | private void hideViewRetry() {
method showErrorMessage (line 101) | private void showErrorMessage(ErrorBundle errorBundle) {
method showUsersCollectionInView (line 107) | private void showUsersCollectionInView(Collection<User> usersCollectio...
method getUserList (line 113) | private void getUserList() {
class UserListObserver (line 117) | private final class UserListObserver extends DefaultObserver<List<User...
method onComplete (line 119) | @Override public void onComplete() {
method onError (line 123) | @Override public void onError(Throwable e) {
method onNext (line 129) | @Override public void onNext(List<User> users) {
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/LoadDataView.java
type LoadDataView (line 12) | public interface LoadDataView {
method showLoading (line 16) | void showLoading();
method hideLoading (line 21) | void hideLoading();
method showRetry (line 26) | void showRetry();
method hideRetry (line 31) | void hideRetry();
method showError (line 38) | void showError(String message);
method context (line 43) | Context context();
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/UserDetailsView.java
type UserDetailsView (line 13) | public interface UserDetailsView extends LoadDataView {
method renderUser (line 19) | void renderUser(UserModel user);
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/UserListView.java
type UserListView (line 14) | public interface UserListView extends LoadDataView {
method renderUserList (line 20) | void renderUserList(Collection<UserModel> userModelCollection);
method viewUser (line 27) | void viewUser(UserModel userModel);
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/BaseActivity.java
class BaseActivity (line 16) | public abstract class BaseActivity extends Activity {
method onCreate (line 20) | @Override
method addFragment (line 32) | protected void addFragment(int containerViewId, Fragment fragment) {
method getApplicationComponent (line 43) | protected ApplicationComponent getApplicationComponent() {
method getActivityModule (line 52) | protected ActivityModule getActivityModule() {
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/MainActivity.java
class MainActivity (line 13) | public class MainActivity extends BaseActivity {
method onCreate (line 17) | @Override
method navigateToUserList (line 27) | @OnClick(R.id.btn_LoadData)
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/UserDetailsActivity.java
class UserDetailsActivity (line 21) | public class UserDetailsActivity extends BaseActivity implements HasComp...
method getCallingIntent (line 26) | public static Intent getCallingIntent(Context context, int userId) {
method onCreate (line 35) | @Override protected void onCreate(Bundle savedInstanceState) {
method onSaveInstanceState (line 44) | @Override protected void onSaveInstanceState(Bundle outState) {
method initializeActivity (line 54) | private void initializeActivity(Bundle savedInstanceState) {
method initializeInjector (line 63) | private void initializeInjector() {
method getComponent (line 70) | @Override public UserComponent getComponent() {
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/UserListActivity.java
class UserListActivity (line 22) | public class UserListActivity extends BaseActivity implements HasCompone...
method getCallingIntent (line 25) | public static Intent getCallingIntent(Context context) {
method onCreate (line 31) | @Override protected void onCreate(Bundle savedInstanceState) {
method initializeInjector (line 42) | private void initializeInjector() {
method getComponent (line 49) | @Override public UserComponent getComponent() {
method onUserClicked (line 53) | @Override public void onUserClicked(UserModel userModel) {
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/adapter/UsersAdapter.java
class UsersAdapter (line 25) | public class UsersAdapter extends RecyclerView.Adapter<UsersAdapter.User...
type OnItemClickListener (line 27) | public interface OnItemClickListener {
method onUserItemClicked (line 28) | void onUserItemClicked(UserModel userModel);
method UsersAdapter (line 36) | @Inject
method getItemCount (line 43) | @Override public int getItemCount() {
method onCreateViewHolder (line 47) | @Override public UserViewHolder onCreateViewHolder(ViewGroup parent, i...
method onBindViewHolder (line 52) | @Override public void onBindViewHolder(UserViewHolder holder, final in...
method getItemId (line 64) | @Override public long getItemId(int position) {
method setUsersCollection (line 68) | public void setUsersCollection(Collection<UserModel> usersCollection) {
method setOnItemClickListener (line 74) | public void setOnItemClickListener (OnItemClickListener onItemClickLis...
method validateUsersCollection (line 78) | private void validateUsersCollection(Collection<UserModel> usersCollec...
class UserViewHolder (line 84) | static class UserViewHolder extends RecyclerView.ViewHolder {
method UserViewHolder (line 87) | UserViewHolder(View itemView) {
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/adapter/UsersLayoutManager.java
class UsersLayoutManager (line 13) | public class UsersLayoutManager extends LinearLayoutManager {
method UsersLayoutManager (line 14) | public UsersLayoutManager(Context context) {
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/component/AutoLoadImageView.java
class AutoLoadImageView (line 29) | public class AutoLoadImageView extends ImageView {
method AutoLoadImageView (line 37) | public AutoLoadImageView(Context context) {
method AutoLoadImageView (line 41) | public AutoLoadImageView(Context context, AttributeSet attrs) {
method AutoLoadImageView (line 45) | public AutoLoadImageView(Context context, AttributeSet attrs, int defS...
method onSaveInstanceState (line 49) | @Override protected Parcelable onSaveInstanceState() {
method onRestoreInstanceState (line 57) | @Override protected void onRestoreInstanceState(Parcelable state) {
method setImageUrl (line 74) | public void setImageUrl(final String imageUrl) {
method loadImageFromUrl (line 89) | private void loadImageFromUrl(final String imageUrl) {
method loadBitmap (line 121) | private void loadBitmap(final Bitmap bitmap) {
method loadImagePlaceHolder (line 132) | private void loadImagePlaceHolder() {
method getFromCache (line 149) | private Bitmap getFromCache(String fileName) {
method cacheBitmap (line 163) | private void cacheBitmap(Bitmap bitmap, String fileName) {
method isThereInternetConnection (line 174) | private boolean isThereInternetConnection() {
method getFileNameFromUrl (line 191) | private String getFileNameFromUrl(String imageUrl) {
class ImageDownloader (line 203) | private static class ImageDownloader {
type Callback (line 204) | interface Callback {
method onImageDownloaded (line 205) | void onImageDownloaded(Bitmap bitmap);
method onError (line 207) | void onError();
method ImageDownloader (line 210) | ImageDownloader() {}
method download (line 218) | void download(String imageUrl, Callback callback) {
method reportError (line 236) | private void reportError(Callback callback) {
class DiskCache (line 246) | private static class DiskCache {
method DiskCache (line 252) | DiskCache(File cacheDir) {
method get (line 262) | synchronized Bitmap get(String fileName) {
method put (line 277) | synchronized void put(Bitmap bitmap, String fileName) {
method buildFileFromFilename (line 297) | private File buildFileFromFilename(String fileName) {
class SavedState (line 303) | private static class SavedState extends BaseSavedState {
method SavedState (line 307) | SavedState(Parcelable superState) {
method SavedState (line 311) | private SavedState(Parcel in) {
method writeToParcel (line 317) | @Override
method createFromParcel (line 326) | public SavedState createFromParcel(Parcel in) {
method newArray (line 330) | public SavedState[] newArray(int size) {
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/fragment/BaseFragment.java
class BaseFragment (line 15) | public abstract class BaseFragment extends Fragment {
method showToastMessage (line 21) | protected void showToastMessage(String message) {
method getComponent (line 28) | @SuppressWarnings("unchecked")
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/fragment/UserDetailsFragment.java
class UserDetailsFragment (line 30) | public class UserDetailsFragment extends BaseFragment implements UserDet...
method forUser (line 44) | public static UserDetailsFragment forUser(int userId) {
method UserDetailsFragment (line 52) | public UserDetailsFragment() {
method onCreate (line 56) | @Override public void onCreate(Bundle savedInstanceState) {
method onCreateView (line 61) | @Override public View onCreateView(LayoutInflater inflater, ViewGroup ...
method onViewCreated (line 68) | @Override public void onViewCreated(View view, Bundle savedInstanceSta...
method onResume (line 76) | @Override public void onResume() {
method onPause (line 81) | @Override public void onPause() {
method onDestroyView (line 86) | @Override public void onDestroyView() {
method onDestroy (line 91) | @Override public void onDestroy() {
method renderUser (line 96) | @Override public void renderUser(UserModel user) {
method showLoading (line 106) | @Override public void showLoading() {
method hideLoading (line 111) | @Override public void hideLoading() {
method showRetry (line 116) | @Override public void showRetry() {
method hideRetry (line 120) | @Override public void hideRetry() {
method showError (line 124) | @Override public void showError(String message) {
method context (line 128) | @Override public Context context() {
method loadUserDetails (line 135) | private void loadUserDetails() {
method currentUserId (line 144) | private int currentUserId() {
method onButtonRetryClick (line 150) | @OnClick(R.id.bt_retry)
FILE: presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/fragment/UserListFragment.java
class UserListFragment (line 33) | public class UserListFragment extends BaseFragment implements UserListVi...
type UserListListener (line 38) | public interface UserListListener {
method onUserClicked (line 39) | void onUserClicked(final UserModel userModel);
method UserListFragment (line 52) | public UserListFragment() {
method onAttach (line 56) | @Override public void onAttach(Activity activity) {
method onCreate (line 63) | @Override public void onCreate(Bundle savedInstanceState) {
method onCreateView (line 68) | @Override public View onCreateView(LayoutInflater inflater, ViewGroup ...
method onViewCreated (line 76) | @Override public void onViewCreated(View view, Bundle savedInstanceSta...
method onResume (line 84) | @Override public void onResume() {
method onPause (line 89) | @Override public void onPause() {
method onDestroyView (line 94) | @Override public void onDestroyView() {
method onDestroy (line 100) | @Override public void onDestroy() {
method onDetach (line 105) | @Override public void onDetach() {
method showLoading (line 110) | @Override public void showLoading() {
method hideLoading (line 115) | @Override public void hideLoading() {
method showRetry (line 120) | @Override public void showRetry() {
method hideRetry (line 124) | @Override public void hideRetry() {
method renderUserList (line 128) | @Override public void renderUserList(Collection<UserModel> userModelCo...
method viewUser (line 134) | @Override public void viewUser(UserModel userModel) {
method showError (line 140) | @Override public void showError(String message) {
method context (line 144) | @Override public Context context() {
method setupRecyclerView (line 148) | private void setupRecyclerView() {
method loadUserList (line 157) | private void loadUserList() {
method onButtonRetryClick (line 161) | @OnClick(R.id.bt_retry) void onButtonRetryClick() {
method onUserItemClicked (line 167) | @Override public void onUserItemClicked(UserModel userModel) {
Condensed preview — 116 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (240K chars).
[
{
"path": ".gitignore",
"chars": 424,
"preview": "# Windows thumbnail db\nThumbs.db\n\n# OSX files\n.DS_Store\n\n# built application files\n*.apk\n*.ap_\n\n# files for the dex VM\n*"
},
{
"path": ".travis.yml",
"chars": 348,
"preview": "language: android\njdk: oraclejdk8\n\nandroid:\n components:\n - tools\n - platform-tools\n - tools\n - build-tools"
},
{
"path": "LICENSE",
"chars": 11324,
"preview": "Apache License\n Version 2.0, January 2004\n http://www.apache.org/licens"
},
{
"path": "README.md",
"chars": 3471,
"preview": "Android-CleanArchitecture \n=========================\n\n## New version available written in Kotlin:\n[Architecting Android…"
},
{
"path": "build.gradle",
"chars": 1167,
"preview": "apply from: 'buildsystem/ci.gradle'\napply from: 'buildsystem/dependencies.gradle'\n\nbuildscript {\n repositories {\n jc"
},
{
"path": "buildsystem/ci.gradle",
"chars": 511,
"preview": "def ciServer = 'TRAVIS'\ndef executingOnCI = \"true\".equals(System.getenv(ciServer))\n\n// Since for CI we always do full cl"
},
{
"path": "buildsystem/dependencies.gradle",
"chars": 3582,
"preview": "allprojects {\n repositories {\n jcenter()\n }\n}\n\next {\n //Android\n androidBuildToolsVersion = \"27.0.1\"\n androidMin"
},
{
"path": "data/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "data/build.gradle",
"chars": 1993,
"preview": "buildscript {\n repositories {\n mavenCentral()\n }\n dependencies {\n classpath 'me.tatarka:gradle-retrolambda:3.7."
},
{
"path": "data/proguard-rules.pro",
"chars": 670,
"preview": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /U"
},
{
"path": "data/src/main/AndroidManifest.xml",
"chars": 182,
"preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package=\"com.fernandocejas.android10.sample.dat"
},
{
"path": "data/src/main/java/com/fernandocejas/android10/sample/data/cache/FileManager.java",
"chars": 4735,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCache.java",
"chars": 1629,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCacheImpl.java",
"chars": 6358,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/main/java/com/fernandocejas/android10/sample/data/cache/serializer/Serializer.java",
"chars": 1337,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/main/java/com/fernandocejas/android10/sample/data/entity/UserEntity.java",
"chars": 1676,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/main/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityDataMapper.java",
"chars": 2344,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/main/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityJsonMapper.java",
"chars": 2280,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/main/java/com/fernandocejas/android10/sample/data/exception/NetworkConnectionException.java",
"chars": 982,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/main/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundle.java",
"chars": 1265,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/main/java/com/fernandocejas/android10/sample/data/exception/UserNotFoundException.java",
"chars": 882,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/main/java/com/fernandocejas/android10/sample/data/executor/JobExecutor.java",
"chars": 1753,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/main/java/com/fernandocejas/android10/sample/data/net/ApiConnection.java",
"chars": 2622,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApi.java",
"chars": 1598,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApiImpl.java",
"chars": 4203,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/main/java/com/fernandocejas/android10/sample/data/repository/UserDataRepository.java",
"chars": 2392,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/CloudUserDataStore.java",
"chars": 1813,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/DiskUserDataStore.java",
"chars": 1655,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStore.java",
"chars": 1279,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStoreFactory.java",
"chars": 2214,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/test/java/com/fernandocejas/android10/sample/data/ApplicationStub.java",
"chars": 753,
"preview": "/**\n * Copyright (C) 2015 android10.org Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"L"
},
{
"path": "data/src/test/java/com/fernandocejas/android10/sample/data/ApplicationTestCase.java",
"chars": 1583,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/test/java/com/fernandocejas/android10/sample/data/cache/FileManagerTest.java",
"chars": 1985,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/test/java/com/fernandocejas/android10/sample/data/cache/serializer/SerializerTest.java",
"chars": 2782,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/test/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityDataMapperTest.java",
"chars": 2705,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/test/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityJsonMapperTest.java",
"chars": 3722,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/test/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundleTest.java",
"chars": 1372,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/test/java/com/fernandocejas/android10/sample/data/repository/UserDataRepositoryTest.java",
"chars": 2905,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/CloudUserDataStoreTest.java",
"chars": 2052,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/DiskUserDataStoreTest.java",
"chars": 1751,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStoreFactoryTest.java",
"chars": 2477,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "domain/build.gradle",
"chars": 740,
"preview": "apply plugin: 'java'\n\n//noinspection GroovyUnusedAssignment\nsourceCompatibility = 1.7\n//noinspection GroovyUnusedAssignm"
},
{
"path": "domain/src/main/java/com/fernandocejas/android10/sample/domain/User.java",
"chars": 1722,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "domain/src/main/java/com/fernandocejas/android10/sample/domain/exception/DefaultErrorBundle.java",
"chars": 1218,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "domain/src/main/java/com/fernandocejas/android10/sample/domain/exception/ErrorBundle.java",
"chars": 876,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "domain/src/main/java/com/fernandocejas/android10/sample/domain/executor/PostExecutionThread.java",
"chars": 1064,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "domain/src/main/java/com/fernandocejas/android10/sample/domain/executor/ThreadExecutor.java",
"chars": 1028,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/DefaultObserver.java",
"chars": 1135,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetails.java",
"chars": 1986,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java",
"chars": 1655,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/UseCase.java",
"chars": 3220,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "domain/src/main/java/com/fernandocejas/android10/sample/domain/repository/UserRepository.java",
"chars": 1223,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "domain/src/test/java/com/fernandocejas/android10/sample/domain/UserTest.java",
"chars": 1102,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "domain/src/test/java/com/fernandocejas/android10/sample/domain/exception/DefaultErrorBundleTest.java",
"chars": 1292,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetailsTest.java",
"chars": 2450,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserListTest.java",
"chars": 1957,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/UseCaseTest.java",
"chars": 3387,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "gradle/wrapper/gradle-wrapper.properties",
"chars": 230,
"preview": "#Wed Dec 21 17:11:04 ART 2016\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
},
{
"path": "gradle.properties",
"chars": 145,
"preview": "#Gradle configuration\norg.gradle.daemon=true\norg.gradle.jvmargs=-Dfile.encoding=UTF-8\norg.gradle.parallel=true\norg.gradl"
},
{
"path": "gradlew",
"chars": 5299,
"preview": "#!/usr/bin/env sh\n\n##############################################################################\n##\n## Gradle start up"
},
{
"path": "gradlew.bat",
"chars": 2260,
"preview": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@r"
},
{
"path": "presentation/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "presentation/build.gradle",
"chars": 3032,
"preview": "apply plugin: 'com.android.application'\n//apply plugin: 'com.neenbedankt.android-apt'\n\nandroid {\n def globalConfigurati"
},
{
"path": "presentation/proguard-rules.pro",
"chars": 670,
"preview": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /U"
},
{
"path": "presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/exception/ErrorMessageFactoryTest.java",
"chars": 1954,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/mapper/UserModelDataMapperTest.java",
"chars": 2527,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/presenter/UserDetailsPresenterTest.java",
"chars": 2415,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/presenter/UserListPresenterTest.java",
"chars": 2219,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/view/activity/UserDetailsActivityTest.java",
"chars": 3356,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/view/activity/UserListActivityTest.java",
"chars": 2162,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "presentation/src/main/AndroidManifest.xml",
"chars": 1148,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package="
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/AndroidApplication.java",
"chars": 1732,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/UIThread.java",
"chars": 1232,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/exception/ErrorMessageFactory.java",
"chars": 1848,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/HasComponent.java",
"chars": 860,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/PerActivity.java",
"chars": 1038,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/components/ActivityComponent.java",
"chars": 1400,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/components/ApplicationComponent.java",
"chars": 1650,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/components/UserComponent.java",
"chars": 1598,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/modules/ActivityModule.java",
"chars": 1229,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/modules/ApplicationModule.java",
"chars": 2396,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/modules/UserModule.java",
"chars": 854,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mapper/UserModelDataMapper.java",
"chars": 2509,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/model/UserModel.java",
"chars": 1745,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/navigation/Navigator.java",
"chars": 1825,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/presenter/Presenter.java",
"chars": 1298,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/presenter/UserDetailsPresenter.java",
"chars": 4102,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/presenter/UserListPresenter.java",
"chars": 4202,
"preview": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/LoadDataView.java",
"chars": 888,
"preview": "/**\n * Copyright (C) 2014 android10.org. All rights reserved.\n * @author Fernando Cejas (the android10 coder)\n */\npackag"
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/UserDetailsView.java",
"chars": 594,
"preview": "/**\n * Copyright (C) 2014 android10.org. All rights reserved.\n * @author Fernando Cejas (the android10 coder)\n */\npackag"
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/UserListView.java",
"chars": 852,
"preview": "/**\n * Copyright (C) 2014 android10.org. All rights reserved.\n * @author Fernando Cejas (the android10 coder)\n */\npackag"
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/BaseActivity.java",
"chars": 1972,
"preview": "package com.fernandocejas.android10.sample.presentation.view.activity;\n\nimport android.app.Activity;\nimport android.app."
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/MainActivity.java",
"chars": 778,
"preview": "package com.fernandocejas.android10.sample.presentation.view.activity;\n\nimport android.os.Bundle;\nimport android.widget."
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/UserDetailsActivity.java",
"chars": 2598,
"preview": "/**\n * Copyright (C) 2014 android10.org. All rights reserved.\n *\n * @author Fernando Cejas (the android10 coder)\n */\npac"
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/UserListActivity.java",
"chars": 1938,
"preview": "/**\n * Copyright (C) 2014 android10.org. All rights reserved.\n *\n * @author Fernando Cejas (the android10 coder)\n */\npac"
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/adapter/UsersAdapter.java",
"chars": 2979,
"preview": "/**\n * Copyright (C) 2014 android10.org. All rights reserved.\n * @author Fernando Cejas (the android10 coder)\n */\npackag"
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/adapter/UsersLayoutManager.java",
"chars": 508,
"preview": "/**\n * Copyright (C) 2014 android10.org. All rights reserved.\n * @author Fernando Cejas (the android10 coder)\n */\npackag"
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/component/AutoLoadImageView.java",
"chars": 9853,
"preview": "/**\n * Copyright (C) 2014 android10.org. All rights reserved.\n *\n * @author Fernando Cejas (the android10 coder)\n */\npac"
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/fragment/BaseFragment.java",
"chars": 985,
"preview": "/**\n * Copyright (C) 2014 android10.org. All rights reserved.\n *\n * @author Fernando Cejas (the android10 coder)\n */\npac"
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/fragment/UserDetailsFragment.java",
"chars": 4848,
"preview": "/**\n * Copyright (C) 2014 android10.org. All rights reserved.\n * @author Fernando Cejas (the android10 coder)\n */\npackag"
},
{
"path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/fragment/UserListFragment.java",
"chars": 5171,
"preview": "/**\n * Copyright (C) 2014 android10.org. All rights reserved.\n *\n * @author Fernando Cejas (the android10 coder)\n */\npac"
},
{
"path": "presentation/src/main/res/drawable/selector_item_user.xml",
"chars": 453,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <item and"
},
{
"path": "presentation/src/main/res/layout/activity_layout.xml",
"chars": 322,
"preview": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:layout_width=\"match_parent\"\n a"
},
{
"path": "presentation/src/main/res/layout/activity_main.xml",
"chars": 1109,
"preview": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:tools=\"http://schemas.android.com/too"
},
{
"path": "presentation/src/main/res/layout/fragment_user_details.xml",
"chars": 354,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n an"
},
{
"path": "presentation/src/main/res/layout/fragment_user_list.xml",
"chars": 538,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n an"
},
{
"path": "presentation/src/main/res/layout/row_user.xml",
"chars": 1163,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmln"
},
{
"path": "presentation/src/main/res/layout/view_progress.xml",
"chars": 355,
"preview": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:id=\"@+id/rl_progress\"\n android"
},
{
"path": "presentation/src/main/res/layout/view_retry.xml",
"chars": 376,
"preview": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:id=\"@+id/rl_retry\"\n android:la"
},
{
"path": "presentation/src/main/res/layout/view_user_details.xml",
"chars": 1892,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n andr"
},
{
"path": "presentation/src/main/res/values/dimens.xml",
"chars": 581,
"preview": "<resources>\n <!-- Default screen margins, per the Android Design guidelines. -->\n <dimen name=\"activity_horizontal_mar"
},
{
"path": "presentation/src/main/res/values/strings.xml",
"chars": 978,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <string name=\"app_name\">Android Clean Architecture</string>\n <stri"
},
{
"path": "presentation/src/main/res/values/styles.xml",
"chars": 1577,
"preview": "<resources>\n\n <!-- Base application theme. -->\n <style name=\"AppTheme\" parent=\"android:Theme.Holo.Light.DarkActionBar\""
},
{
"path": "settings.gradle",
"chars": 58,
"preview": "include ':presentation'\ninclude ':domain'\ninclude ':data'\n"
}
]
// ... and 2 more files (download for full content)
About this extraction
This page contains the full source code of the android10/Android-CleanArchitecture GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 116 files (214.4 KB), approximately 53.5k tokens, and a symbol index with 478 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.