[
  {
    "path": ".gitignore",
    "content": "# 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*.dex\n  \n# Java class files\n*.class\n  \n# generated files\nbin/\ngen/\nbuild/\n  \n# Local configuration file (sdk path, etc)\nlocal.properties\n  \n# Eclipse project files\n.classpath\n.project\n  \n# Android Studio\n.idea\n.gradle\n/*/local.properties\n/*/out\n/*/*/build\nbuild\n/*/*/production\n*.iml\n*.iws\n*.ipr\n*~\n*.swp\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: android\njdk: oraclejdk8\n\nandroid:\n  components:\n    - tools\n    - platform-tools\n    - tools\n    - build-tools-27.0.1\n    - android-26\n    - extra-google-m2repository\n    - extra-android-m2repository\n\nlicenses:\n    - 'android-sdk-preview-license-.+'\n    - 'android-sdk-license-.+'\n    - 'google-gdk-license-.+'\n\nscript:\n  ./gradlew build\n"
  },
  {
    "path": "LICENSE",
    "content": "Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright {yyyy} {name of copyright owner}\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "Android-CleanArchitecture \n=========================\n\n## New version available written in Kotlin:\n[Architecting Android… Reloaded](https://fernandocejas.com/2018/05/07/architecting-android-reloaded/)\n\nIntroduction\n-----------------\nThis 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. \n\n[Architecting Android…The clean way?](http://fernandocejas.com/2014/09/03/architecting-android-the-clean-way/)\n\n[Architecting Android…The evolution](http://fernandocejas.com/2015/07/18/architecting-android-the-evolution/)\n\n[Tasting Dagger 2 on Android](http://fernandocejas.com/2015/04/11/tasting-dagger-2-on-android/)\n\n[Clean Architecture…Dynamic Parameters in Use Cases](http://fernandocejas.com/2016/12/24/clean-architecture-dynamic-parameters-in-use-cases/)\n\n[Demo video of this sample](http://youtu.be/XSjV4sG3ni0)\n\nClean architecture\n-----------------\n![http://fernandocejas.com/2015/07/18/architecting-android-the-evolution/](https://github.com/android10/Sample-Data/blob/master/Android-CleanArchitecture/clean_architecture.png)\n\nArchitectural approach\n-----------------\n![http://fernandocejas.com/2015/07/18/architecting-android-the-evolution/](https://github.com/android10/Sample-Data/blob/master/Android-CleanArchitecture/clean_architecture_layers.png)\n\nArchitectural reactive approach\n-----------------\n![http://fernandocejas.com/2015/07/18/architecting-android-the-evolution/](https://github.com/android10/Sample-Data/blob/master/Android-CleanArchitecture/clean_architecture_layers_details.png)\n\nLocal Development\n-----------------\n\nHere are some useful Gradle/adb commands for executing this example:\n\n * `./gradlew clean build` - Build the entire example and execute unit and integration tests plus lint check.\n * `./gradlew installDebug` - Install the debug apk on the current connected device.\n * `./gradlew runUnitTests` - Execute domain and data layer tests (both unit and integration).\n * `./gradlew runAcceptanceTests` - Execute espresso and instrumentation acceptance tests.\n \nDiscussions\n-----------------\n\nRefer to the issues section: https://github.com/android10/Android-CleanArchitecture/issues\n \n\nCode style\n-----------\n\nHere you can download and install the java codestyle.\nhttps://github.com/android10/java-code-styles\n\n\nLicense\n--------\n\n    Copyright 2018 Fernando Cejas\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n\n\n![http://www.fernandocejas.com](https://github.com/android10/Sample-Data/blob/master/android10/android10_logo_big.png)\n\n[![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Android--CleanArchitecture-brightgreen.svg?style=flat)](https://android-arsenal.com/details/3/909)\n\n<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>\n"
  },
  {
    "path": "build.gradle",
    "content": "apply from: 'buildsystem/ci.gradle'\napply from: 'buildsystem/dependencies.gradle'\n\nbuildscript {\n  repositories {\n    jcenter()\n    mavenCentral()\n    google()\n  }\n  dependencies {\n    classpath 'com.android.tools.build:gradle:3.0.1'\n//    classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'\n  }\n}\n\nallprojects {\n  ext {\n    androidApplicationId = 'com.fernanependocejas.android10.sample.presentation'\n    androidVersionCode = 1\n    androidVersionName = \"1.0\"\n    testInstrumentationRunner = \"android.support.test.runner.AndroidJUnitRunner\"\n    testApplicationId = 'com.fernandocejas.android10.sample.presentation.test'\n  }\n}\n\ntask runDomainUnitTests(dependsOn: [':domain:test']) {\n  description 'Run unit tests for the domain layer.'\n}\n\ntask runDataUnitTests(dependsOn: [':data:cleanTestDebugUnitTest', ':data:testDebugUnitTest']) {\n  description 'Run unit tests for the data layer.'\n}\n\ntask runUnitTests(dependsOn: ['runDomainUnitTests', 'runDataUnitTests']) {\n  description 'Run unit tests for both domain and data layers.'\n}\n\ntask runAcceptanceTests(dependsOn: [':presentation:connectedAndroidTest']) {\n  description 'Run application acceptance tests.'\n}\n\n"
  },
  {
    "path": "buildsystem/ci.gradle",
    "content": "def ciServer = 'TRAVIS'\ndef executingOnCI = \"true\".equals(System.getenv(ciServer))\n\n// Since for CI we always do full clean builds, we don't want to pre-dex\n// See http://tools.android.com/tech-docs/new-build-system/tips\nsubprojects {\n  project.plugins.whenPluginAdded { plugin ->\n    if ('com.android.build.gradle.AppPlugin'.equals(plugin.class.name) ||\n        'com.android.build.gradle.LibraryPlugin'.equals(plugin.class.name)) {\n      project.android.dexOptions.preDexLibraries = !executingOnCI\n    }\n  }\n}\n"
  },
  {
    "path": "buildsystem/dependencies.gradle",
    "content": "allprojects {\n  repositories {\n    jcenter()\n  }\n}\n\next {\n  //Android\n  androidBuildToolsVersion = \"27.0.1\"\n  androidMinSdkVersion = 15\n  androidTargetSdkVersion = 26\n  androidCompileSdkVersion = 26\n\n  //Libraries\n  daggerVersion = '2.8'\n  butterKnifeVersion = '7.0.1'\n  recyclerViewVersion = '25.4.0'\n  rxJavaVersion = '2.0.2'\n  rxAndroidVersion = '2.0.1'\n  javaxAnnotationVersion = '1.0'\n  javaxInjectVersion = '1'\n  gsonVersion = '2.3'\n  okHttpVersion = '2.5.0'\n  androidAnnotationsVersion = '25.4.0'\n  arrowVersion = '1.0.0'\n\n  //Testing\n  robolectricVersion = '3.1.1'\n  jUnitVersion = '4.12'\n  assertJVersion = '1.7.1'\n  mockitoVersion = '1.9.5'\n  dexmakerVersion = '1.0'\n  espressoVersion = '3.0.1'\n  testingSupportLibVersion = '0.1'\n\n  //Development\n  leakCanaryVersion = '1.3.1'\n\n  presentationDependencies = [\n      daggerCompiler:     \"com.google.dagger:dagger-compiler:${daggerVersion}\",\n      dagger:             \"com.google.dagger:dagger:${daggerVersion}\",\n      butterKnife:        \"com.jakewharton:butterknife:${butterKnifeVersion}\",\n      recyclerView:       \"com.android.support:recyclerview-v7:${recyclerViewVersion}\",\n      rxJava:             \"io.reactivex.rxjava2:rxjava:${rxJavaVersion}\",\n      rxAndroid:          \"io.reactivex.rxjava2:rxandroid:${rxAndroidVersion}\",\n      javaxAnnotation:    \"javax.annotation:jsr250-api:${javaxAnnotationVersion}\"\n  ]\n\n  presentationTestDependencies = [\n      mockito:            \"org.mockito:mockito-core:${mockitoVersion}\",\n      dexmaker:           \"com.google.dexmaker:dexmaker:${dexmakerVersion}\",\n      dexmakerMockito:    \"com.google.dexmaker:dexmaker-mockito:${dexmakerVersion}\",\n      espresso:           \"com.android.support.test.espresso:espresso-core:${espressoVersion}\",\n      testingSupportLib:  \"com.android.support.test:testing-support-lib:${testingSupportLibVersion}\",\n  ]\n\n  domainDependencies = [\n      javaxAnnotation:    \"javax.annotation:jsr250-api:${javaxAnnotationVersion}\",\n      javaxInject:        \"javax.inject:javax.inject:${javaxInjectVersion}\",\n      rxJava:             \"io.reactivex.rxjava2:rxjava:${rxJavaVersion}\",\n      arrow:              \"com.fernandocejas:arrow:${arrowVersion}\"\n  ]\n\n  domainTestDependencies = [\n      junit:              \"junit:junit:${jUnitVersion}\",\n      mockito:            \"org.mockito:mockito-core:${mockitoVersion}\",\n      assertj:            \"org.assertj:assertj-core:${assertJVersion}\"\n  ]\n\n  dataDependencies = [\n      daggerCompiler:     \"com.google.dagger:dagger-compiler:${daggerVersion}\",\n      dagger:             \"com.google.dagger:dagger:${daggerVersion}\",\n      okHttp:             \"com.squareup.okhttp:okhttp:${okHttpVersion}\",\n      gson:               \"com.google.code.gson:gson:${gsonVersion}\",\n      rxJava:             \"io.reactivex.rxjava2:rxjava:${rxJavaVersion}\",\n      rxAndroid:          \"io.reactivex.rxjava2:rxandroid:${rxAndroidVersion}\",\n      javaxAnnotation:    \"javax.annotation:jsr250-api:${javaxAnnotationVersion}\",\n      javaxInject:        \"javax.inject:javax.inject:${javaxInjectVersion}\",\n      androidAnnotations: \"com.android.support:support-annotations:${androidAnnotationsVersion}\"\n  ]\n\n  dataTestDependencies = [\n      junit:              \"junit:junit:${jUnitVersion}\",\n      assertj:            \"org.assertj:assertj-core:${assertJVersion}\",\n      mockito:            \"org.mockito:mockito-core:${mockitoVersion}\",\n      robolectric:        \"org.robolectric:robolectric:${robolectricVersion}\",\n  ]\n\n  developmentDependencies = [\n      leakCanary: \"com.squareup.leakcanary:leakcanary-android:${leakCanaryVersion}\",\n  ]\n}\n"
  },
  {
    "path": "data/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "data/build.gradle",
    "content": "buildscript {\n  repositories {\n    mavenCentral()\n  }\n  dependencies {\n    classpath 'me.tatarka:gradle-retrolambda:3.7.0'\n  }\n}\n\napply plugin: 'com.android.library'\n//apply plugin: 'com.neenbedankt.android-apt'\napply plugin: 'me.tatarka.retrolambda'\n\nandroid {\n  defaultPublishConfig \"debug\"\n\n  def globalConfiguration = rootProject.extensions.getByName(\"ext\")\n\n  compileSdkVersion globalConfiguration.getAt(\"androidCompileSdkVersion\")\n  buildToolsVersion globalConfiguration.getAt(\"androidBuildToolsVersion\")\n\n  defaultConfig {\n    minSdkVersion globalConfiguration.getAt(\"androidMinSdkVersion\")\n    targetSdkVersion globalConfiguration.getAt(\"androidTargetSdkVersion\")\n    versionCode globalConfiguration.getAt(\"androidVersionCode\")\n  }\n\n  compileOptions {\n    sourceCompatibility JavaVersion.VERSION_1_8\n    targetCompatibility JavaVersion.VERSION_1_8\n  }\n\n  packagingOptions {\n    exclude 'LICENSE.txt'\n    exclude 'META-INF/DEPENDENCIES'\n    exclude 'META-INF/ASL2.0'\n    exclude 'META-INF/NOTICE'\n    exclude 'META-INF/LICENSE'\n  }\n\n  lintOptions {\n    quiet true\n    abortOnError false\n    ignoreWarnings true\n    disable 'InvalidPackage'  // Some libraries have issues with this\n    disable 'OldTargetApi'    // Due to Robolectric that modifies the manifest when running tests\n  }\n}\n\ndependencies {\n  def dataDependencies = rootProject.ext.dataDependencies\n  def testDependencies = rootProject.ext.dataTestDependencies\n\n  implementation project(':domain')\n  compileOnly dataDependencies.javaxAnnotation\n  implementation dataDependencies.javaxInject\n  implementation dataDependencies.okHttp\n  implementation dataDependencies.gson\n  implementation dataDependencies.rxJava\n  implementation dataDependencies.rxAndroid\n  implementation dataDependencies.androidAnnotations\n\n  testImplementation testDependencies.junit\n  testImplementation testDependencies.assertj\n  testImplementation testDependencies.mockito\n  testImplementation testDependencies.robolectric\n}\n\nrepositories {\n  google()\n}"
  },
  {
    "path": "data/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Users/fcejas/Software/SDKs/android-sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "data/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.fernandocejas.android10.sample.data\">\n\n  <application android:allowBackup=\"true\" />\n</manifest>\n"
  },
  {
    "path": "data/src/main/java/com/fernandocejas/android10/sample/data/cache/FileManager.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.cache;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport javax.inject.Inject;\nimport javax.inject.Singleton;\n\n/**\n * Helper class to do operations on regular files/directories.\n */\n@Singleton\npublic class FileManager {\n\n  @Inject\n  FileManager() {}\n\n  /**\n   * Writes a file to Disk.\n   * This is an I/O operation and this method executes in the main thread, so it is recommended to\n   * perform this operation using another thread.\n   *\n   * @param file The file to write to Disk.\n   */\n  void writeToFile(File file, String fileContent) {\n    if (!file.exists()) {\n      try {\n        final FileWriter writer = new FileWriter(file);\n        writer.write(fileContent);\n        writer.close();\n      } catch (IOException e) {\n        e.printStackTrace();\n      }\n    }\n  }\n\n  /**\n   * Reads a content from a file.\n   * This is an I/O operation and this method executes in the main thread, so it is recommended to\n   * perform the operation using another thread.\n   *\n   * @param file The file to read from.\n   * @return A string with the content of the file.\n   */\n  String readFileContent(File file) {\n    final StringBuilder fileContentBuilder = new StringBuilder();\n    if (file.exists()) {\n      String stringLine;\n      try {\n        final FileReader fileReader = new FileReader(file);\n        final BufferedReader bufferedReader = new BufferedReader(fileReader);\n        while ((stringLine = bufferedReader.readLine()) != null) {\n          fileContentBuilder.append(stringLine).append(\"\\n\");\n        }\n        bufferedReader.close();\n        fileReader.close();\n      } catch (IOException e) {\n        e.printStackTrace();\n      }\n    }\n    return fileContentBuilder.toString();\n  }\n\n  /**\n   * Returns a boolean indicating whether this file can be found on the underlying file system.\n   *\n   * @param file The file to check existence.\n   * @return true if this file exists, false otherwise.\n   */\n  boolean exists(File file) {\n    return file.exists();\n  }\n\n  /**\n   * Warning: Deletes the content of a directory.\n   * This is an I/O operation and this method executes in the main thread, so it is recommended to\n   * perform the operation using another thread.\n   *\n   * @param directory The directory which its content will be deleted.\n   */\n  boolean clearDirectory(File directory) {\n    boolean result = false;\n    if (directory.exists()) {\n      for (File file : directory.listFiles()) {\n        result = file.delete();\n      }\n    }\n    return result;\n  }\n\n  /**\n   * Write a value to a user preferences file.\n   *\n   * @param context {@link android.content.Context} to retrieve android user preferences.\n   * @param preferenceFileName A file name reprensenting where data will be written to.\n   * @param key A string for the key that will be used to retrieve the value in the future.\n   * @param value A long representing the value to be inserted.\n   */\n  void writeToPreferences(Context context, String preferenceFileName, String key,\n      long value) {\n\n    final SharedPreferences sharedPreferences = context.getSharedPreferences(preferenceFileName,\n        Context.MODE_PRIVATE);\n    final SharedPreferences.Editor editor = sharedPreferences.edit();\n    editor.putLong(key, value);\n    editor.apply();\n  }\n\n  /**\n   * Get a value from a user preferences file.\n   *\n   * @param context {@link android.content.Context} to retrieve android user preferences.\n   * @param preferenceFileName A file name representing where data will be get from.\n   * @param key A key that will be used to retrieve the value from the preference file.\n   * @return A long representing the value retrieved from the preferences file.\n   */\n  long getFromPreferences(Context context, String preferenceFileName, String key) {\n    final SharedPreferences sharedPreferences = context.getSharedPreferences(preferenceFileName,\n        Context.MODE_PRIVATE);\n    return sharedPreferences.getLong(key, 0);\n  }\n}\n"
  },
  {
    "path": "data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCache.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.cache;\n\nimport com.fernandocejas.android10.sample.data.entity.UserEntity;\nimport io.reactivex.Observable;\n\n/**\n * An interface representing a user Cache.\n */\npublic interface UserCache {\n  /**\n   * Gets an {@link Observable} which will emit a {@link UserEntity}.\n   *\n   * @param userId The user id to retrieve data.\n   */\n  Observable<UserEntity> get(final int userId);\n\n  /**\n   * Puts and element into the cache.\n   *\n   * @param userEntity Element to insert in the cache.\n   */\n  void put(UserEntity userEntity);\n\n  /**\n   * Checks if an element (User) exists in the cache.\n   *\n   * @param userId The id used to look for inside the cache.\n   * @return true if the element is cached, otherwise false.\n   */\n  boolean isCached(final int userId);\n\n  /**\n   * Checks if the cache is expired.\n   *\n   * @return true, the cache is expired, otherwise false.\n   */\n  boolean isExpired();\n\n  /**\n   * Evict all elements of the cache.\n   */\n  void evictAll();\n}\n"
  },
  {
    "path": "data/src/main/java/com/fernandocejas/android10/sample/data/cache/UserCacheImpl.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.cache;\n\nimport android.content.Context;\nimport com.fernandocejas.android10.sample.data.cache.serializer.Serializer;\nimport com.fernandocejas.android10.sample.data.entity.UserEntity;\nimport com.fernandocejas.android10.sample.data.exception.UserNotFoundException;\nimport com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;\nimport io.reactivex.Observable;\nimport java.io.File;\nimport javax.inject.Inject;\nimport javax.inject.Singleton;\n\n/**\n * {@link UserCache} implementation.\n */\n@Singleton\npublic class UserCacheImpl implements UserCache {\n\n  private static final String SETTINGS_FILE_NAME = \"com.fernandocejas.android10.SETTINGS\";\n  private static final String SETTINGS_KEY_LAST_CACHE_UPDATE = \"last_cache_update\";\n\n  private static final String DEFAULT_FILE_NAME = \"user_\";\n  private static final long EXPIRATION_TIME = 60 * 10 * 1000;\n\n  private final Context context;\n  private final File cacheDir;\n  private final Serializer serializer;\n  private final FileManager fileManager;\n  private final ThreadExecutor threadExecutor;\n\n  /**\n   * Constructor of the class {@link UserCacheImpl}.\n   *\n   * @param context A\n   * @param serializer {@link Serializer} for object serialization.\n   * @param fileManager {@link FileManager} for saving serialized objects to the file system.\n   */\n  @Inject UserCacheImpl(Context context, Serializer serializer,\n      FileManager fileManager, ThreadExecutor executor) {\n    if (context == null || serializer == null || fileManager == null || executor == null) {\n      throw new IllegalArgumentException(\"Invalid null parameter\");\n    }\n    this.context = context.getApplicationContext();\n    this.cacheDir = this.context.getCacheDir();\n    this.serializer = serializer;\n    this.fileManager = fileManager;\n    this.threadExecutor = executor;\n  }\n\n  @Override public Observable<UserEntity> get(final int userId) {\n    return Observable.create(emitter -> {\n      final File userEntityFile = UserCacheImpl.this.buildFile(userId);\n      final String fileContent = UserCacheImpl.this.fileManager.readFileContent(userEntityFile);\n      final UserEntity userEntity =\n          UserCacheImpl.this.serializer.deserialize(fileContent, UserEntity.class);\n\n      if (userEntity != null) {\n        emitter.onNext(userEntity);\n        emitter.onComplete();\n      } else {\n        emitter.onError(new UserNotFoundException());\n      }\n    });\n  }\n\n  @Override public void put(UserEntity userEntity) {\n    if (userEntity != null) {\n      final File userEntityFile = this.buildFile(userEntity.getUserId());\n      if (!isCached(userEntity.getUserId())) {\n        final String jsonString = this.serializer.serialize(userEntity, UserEntity.class);\n        this.executeAsynchronously(new CacheWriter(this.fileManager, userEntityFile, jsonString));\n        setLastCacheUpdateTimeMillis();\n      }\n    }\n  }\n\n  @Override public boolean isCached(int userId) {\n    final File userEntityFile = this.buildFile(userId);\n    return this.fileManager.exists(userEntityFile);\n  }\n\n  @Override public boolean isExpired() {\n    long currentTime = System.currentTimeMillis();\n    long lastUpdateTime = this.getLastCacheUpdateTimeMillis();\n\n    boolean expired = ((currentTime - lastUpdateTime) > EXPIRATION_TIME);\n\n    if (expired) {\n      this.evictAll();\n    }\n\n    return expired;\n  }\n\n  @Override public void evictAll() {\n    this.executeAsynchronously(new CacheEvictor(this.fileManager, this.cacheDir));\n  }\n\n  /**\n   * Build a file, used to be inserted in the disk cache.\n   *\n   * @param userId The id user to build the file.\n   * @return A valid file.\n   */\n  private File buildFile(int userId) {\n    final StringBuilder fileNameBuilder = new StringBuilder();\n    fileNameBuilder.append(this.cacheDir.getPath());\n    fileNameBuilder.append(File.separator);\n    fileNameBuilder.append(DEFAULT_FILE_NAME);\n    fileNameBuilder.append(userId);\n\n    return new File(fileNameBuilder.toString());\n  }\n\n  /**\n   * Set in millis, the last time the cache was accessed.\n   */\n  private void setLastCacheUpdateTimeMillis() {\n    final long currentMillis = System.currentTimeMillis();\n    this.fileManager.writeToPreferences(this.context, SETTINGS_FILE_NAME,\n        SETTINGS_KEY_LAST_CACHE_UPDATE, currentMillis);\n  }\n\n  /**\n   * Get in millis, the last time the cache was accessed.\n   */\n  private long getLastCacheUpdateTimeMillis() {\n    return this.fileManager.getFromPreferences(this.context, SETTINGS_FILE_NAME,\n        SETTINGS_KEY_LAST_CACHE_UPDATE);\n  }\n\n  /**\n   * Executes a {@link Runnable} in another Thread.\n   *\n   * @param runnable {@link Runnable} to execute\n   */\n  private void executeAsynchronously(Runnable runnable) {\n    this.threadExecutor.execute(runnable);\n  }\n\n  /**\n   * {@link Runnable} class for writing to disk.\n   */\n  private static class CacheWriter implements Runnable {\n    private final FileManager fileManager;\n    private final File fileToWrite;\n    private final String fileContent;\n\n    CacheWriter(FileManager fileManager, File fileToWrite, String fileContent) {\n      this.fileManager = fileManager;\n      this.fileToWrite = fileToWrite;\n      this.fileContent = fileContent;\n    }\n\n    @Override public void run() {\n      this.fileManager.writeToFile(fileToWrite, fileContent);\n    }\n  }\n\n  /**\n   * {@link Runnable} class for evicting all the cached files\n   */\n  private static class CacheEvictor implements Runnable {\n    private final FileManager fileManager;\n    private final File cacheDir;\n\n    CacheEvictor(FileManager fileManager, File cacheDir) {\n      this.fileManager = fileManager;\n      this.cacheDir = cacheDir;\n    }\n\n    @Override public void run() {\n      this.fileManager.clearDirectory(this.cacheDir);\n    }\n  }\n}\n"
  },
  {
    "path": "data/src/main/java/com/fernandocejas/android10/sample/data/cache/serializer/Serializer.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.cache.serializer;\n\nimport com.google.gson.Gson;\nimport javax.inject.Inject;\nimport javax.inject.Singleton;\n\n/**\n * Json Serializer/Deserializer.\n */\n@Singleton\npublic class Serializer {\n\n  private final Gson gson = new Gson();\n\n  @Inject Serializer() {}\n\n  /**\n   * Serialize an object to Json.\n   *\n   * @param object to serialize.\n   */\n  public String serialize(Object object, Class clazz) {\n    return gson.toJson(object, clazz);\n  }\n\n  /**\n   * Deserialize a json representation of an object.\n   *\n   * @param string A json string to deserialize.\n   */\n  public <T> T deserialize(String string, Class<T> clazz) {\n    return gson.fromJson(string, clazz);\n  }\n}\n"
  },
  {
    "path": "data/src/main/java/com/fernandocejas/android10/sample/data/entity/UserEntity.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.entity;\n\nimport com.google.gson.annotations.SerializedName;\n\n/**\n * User Entity used in the data layer.\n */\npublic class UserEntity {\n\n  @SerializedName(\"id\")\n  private int userId;\n\n  @SerializedName(\"cover_url\")\n  private String coverUrl;\n\n  @SerializedName(\"full_name\")\n  private String fullname;\n\n  @SerializedName(\"description\")\n  private String description;\n\n  @SerializedName(\"followers\")\n  private int followers;\n\n  @SerializedName(\"email\")\n  private String email;\n\n  public UserEntity() {\n    //empty\n  }\n\n  public int getUserId() {\n    return userId;\n  }\n\n  public void setUserId(int userId) {\n    this.userId = userId;\n  }\n\n  public String getCoverUrl() {\n    return coverUrl;\n  }\n\n  public String getFullname() {\n    return fullname;\n  }\n\n  public void setFullname(String fullname) {\n    this.fullname = fullname;\n  }\n\n  public String getDescription() {\n    return description;\n  }\n\n  public int getFollowers() {\n    return followers;\n  }\n\n  public String getEmail() {\n    return email;\n  }\n}\n"
  },
  {
    "path": "data/src/main/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityDataMapper.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.entity.mapper;\n\nimport com.fernandocejas.android10.sample.data.entity.UserEntity;\nimport com.fernandocejas.android10.sample.domain.User;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport javax.inject.Inject;\nimport javax.inject.Singleton;\n\n/**\n * Mapper class used to transform {@link UserEntity} (in the data layer) to {@link User} in the\n * domain layer.\n */\n@Singleton\npublic class UserEntityDataMapper {\n\n  @Inject\n  UserEntityDataMapper() {}\n\n  /**\n   * Transform a {@link UserEntity} into an {@link User}.\n   *\n   * @param userEntity Object to be transformed.\n   * @return {@link User} if valid {@link UserEntity} otherwise null.\n   */\n  public User transform(UserEntity userEntity) {\n    User user = null;\n    if (userEntity != null) {\n      user = new User(userEntity.getUserId());\n      user.setCoverUrl(userEntity.getCoverUrl());\n      user.setFullName(userEntity.getFullname());\n      user.setDescription(userEntity.getDescription());\n      user.setFollowers(userEntity.getFollowers());\n      user.setEmail(userEntity.getEmail());\n    }\n    return user;\n  }\n\n  /**\n   * Transform a List of {@link UserEntity} into a Collection of {@link User}.\n   *\n   * @param userEntityCollection Object Collection to be transformed.\n   * @return {@link User} if valid {@link UserEntity} otherwise null.\n   */\n  public List<User> transform(Collection<UserEntity> userEntityCollection) {\n    final List<User> userList = new ArrayList<>(20);\n    for (UserEntity userEntity : userEntityCollection) {\n      final User user = transform(userEntity);\n      if (user != null) {\n        userList.add(user);\n      }\n    }\n    return userList;\n  }\n}\n"
  },
  {
    "path": "data/src/main/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityJsonMapper.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.entity.mapper;\n\nimport com.fernandocejas.android10.sample.data.entity.UserEntity;\nimport com.google.gson.Gson;\nimport com.google.gson.JsonSyntaxException;\nimport com.google.gson.reflect.TypeToken;\nimport java.lang.reflect.Type;\nimport java.util.List;\nimport javax.inject.Inject;\n\n/**\n * Class used to transform from Strings representing json to valid objects.\n */\npublic class UserEntityJsonMapper {\n\n  private final Gson gson;\n\n  @Inject\n  public UserEntityJsonMapper() {\n    this.gson = new Gson();\n  }\n\n  /**\n   * Transform from valid json string to {@link UserEntity}.\n   *\n   * @param userJsonResponse A json representing a user profile.\n   * @return {@link UserEntity}.\n   * @throws com.google.gson.JsonSyntaxException if the json string is not a valid json structure.\n   */\n  public UserEntity transformUserEntity(String userJsonResponse) throws JsonSyntaxException {\n    final Type userEntityType = new TypeToken<UserEntity>() {}.getType();\n    return this.gson.fromJson(userJsonResponse, userEntityType);\n  }\n\n  /**\n   * Transform from valid json string to List of {@link UserEntity}.\n   *\n   * @param userListJsonResponse A json representing a collection of users.\n   * @return List of {@link UserEntity}.\n   * @throws com.google.gson.JsonSyntaxException if the json string is not a valid json structure.\n   */\n  public List<UserEntity> transformUserEntityCollection(String userListJsonResponse)\n      throws JsonSyntaxException {\n    final Type listOfUserEntityType = new TypeToken<List<UserEntity>>() {}.getType();\n    return this.gson.fromJson(userListJsonResponse, listOfUserEntityType);\n  }\n}\n"
  },
  {
    "path": "data/src/main/java/com/fernandocejas/android10/sample/data/exception/NetworkConnectionException.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.exception;\n\n/**\n * Exception throw by the application when a there is a network connection exception.\n */\npublic class NetworkConnectionException extends Exception {\n\n  public NetworkConnectionException() {\n    super();\n  }\n\n  public NetworkConnectionException(final Throwable cause) {\n    super(cause);\n  }\n}\n"
  },
  {
    "path": "data/src/main/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundle.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.exception;\n\nimport com.fernandocejas.android10.sample.domain.exception.ErrorBundle;\n\n/**\n * Wrapper around Exceptions used to manage errors in the repository.\n */\nclass RepositoryErrorBundle implements ErrorBundle {\n\n  private final Exception exception;\n\n  RepositoryErrorBundle(Exception exception) {\n    this.exception = exception;\n  }\n\n  @Override\n  public Exception getException() {\n    return exception;\n  }\n\n  @Override\n  public String getErrorMessage() {\n    String message = \"\";\n    if (this.exception != null) {\n      message = this.exception.getMessage();\n    }\n    return message;\n  }\n}\n"
  },
  {
    "path": "data/src/main/java/com/fernandocejas/android10/sample/data/exception/UserNotFoundException.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.exception;\n\n/**\n * Exception throw by the application when a User search can't return a valid result.\n */\npublic class UserNotFoundException extends Exception {\n  public UserNotFoundException() {\n    super();\n  }\n}\n"
  },
  {
    "path": "data/src/main/java/com/fernandocejas/android10/sample/data/executor/JobExecutor.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.executor;\n\nimport android.support.annotation.NonNull;\nimport com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport javax.inject.Inject;\nimport javax.inject.Singleton;\n\n/**\n * Decorated {@link java.util.concurrent.ThreadPoolExecutor}\n */\n@Singleton\npublic class JobExecutor implements ThreadExecutor {\n  private final ThreadPoolExecutor threadPoolExecutor;\n\n  @Inject\n  JobExecutor() {\n    this.threadPoolExecutor = new ThreadPoolExecutor(3, 5, 10, TimeUnit.SECONDS,\n        new LinkedBlockingQueue<>(), new JobThreadFactory());\n  }\n\n  @Override public void execute(@NonNull Runnable runnable) {\n    this.threadPoolExecutor.execute(runnable);\n  }\n\n  private static class JobThreadFactory implements ThreadFactory {\n    private int counter = 0;\n\n    @Override public Thread newThread(@NonNull Runnable runnable) {\n      return new Thread(runnable, \"android_\" + counter++);\n    }\n  }\n}\n"
  },
  {
    "path": "data/src/main/java/com/fernandocejas/android10/sample/data/net/ApiConnection.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.net;\n\nimport android.support.annotation.Nullable;\nimport com.squareup.okhttp.OkHttpClient;\nimport com.squareup.okhttp.Request;\nimport java.io.IOException;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Api Connection class used to retrieve data from the cloud.\n * Implements {@link java.util.concurrent.Callable} so when executed asynchronously can\n * return a value.\n */\nclass ApiConnection implements Callable<String> {\n\n  private static final String CONTENT_TYPE_LABEL = \"Content-Type\";\n  private static final String CONTENT_TYPE_VALUE_JSON = \"application/json; charset=utf-8\";\n\n  private URL url;\n  private String response;\n\n  private ApiConnection(String url) throws MalformedURLException {\n    this.url = new URL(url);\n  }\n\n  static ApiConnection createGET(String url) throws MalformedURLException {\n    return new ApiConnection(url);\n  }\n\n  /**\n   * Do a request to an api synchronously.\n   * It should not be executed in the main thread of the application.\n   *\n   * @return A string response\n   */\n  @Nullable\n  String requestSyncCall() {\n    connectToApi();\n    return response;\n  }\n\n  private void connectToApi() {\n    OkHttpClient okHttpClient = this.createClient();\n    final Request request = new Request.Builder()\n        .url(this.url)\n        .addHeader(CONTENT_TYPE_LABEL, CONTENT_TYPE_VALUE_JSON)\n        .get()\n        .build();\n\n    try {\n      this.response = okHttpClient.newCall(request).execute().body().string();\n    } catch (IOException e) {\n      e.printStackTrace();\n    }\n  }\n\n  private OkHttpClient createClient() {\n    final OkHttpClient okHttpClient = new OkHttpClient();\n    okHttpClient.setReadTimeout(10000, TimeUnit.MILLISECONDS);\n    okHttpClient.setConnectTimeout(15000, TimeUnit.MILLISECONDS);\n\n    return okHttpClient;\n  }\n\n  @Override public String call() throws Exception {\n    return requestSyncCall();\n  }\n}\n"
  },
  {
    "path": "data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApi.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.net;\n\nimport com.fernandocejas.android10.sample.data.entity.UserEntity;\nimport io.reactivex.Observable;\nimport java.util.List;\n\n/**\n * RestApi for retrieving data from the network.\n */\npublic interface RestApi {\n  String API_BASE_URL =\n      \"https://raw.githubusercontent.com/android10/Sample-Data/master/Android-CleanArchitecture/\";\n\n  /** Api url for getting all users */\n  String API_URL_GET_USER_LIST = API_BASE_URL + \"users.json\";\n  /** Api url for getting a user profile: Remember to concatenate id + 'json' */\n  String API_URL_GET_USER_DETAILS = API_BASE_URL + \"user_\";\n\n  /**\n   * Retrieves an {@link Observable} which will emit a List of {@link UserEntity}.\n   */\n  Observable<List<UserEntity>> userEntityList();\n\n  /**\n   * Retrieves an {@link Observable} which will emit a {@link UserEntity}.\n   *\n   * @param userId The user id used to get user data.\n   */\n  Observable<UserEntity> userEntityById(final int userId);\n}\n"
  },
  {
    "path": "data/src/main/java/com/fernandocejas/android10/sample/data/net/RestApiImpl.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.net;\n\nimport android.content.Context;\nimport android.net.ConnectivityManager;\nimport android.net.NetworkInfo;\nimport com.fernandocejas.android10.sample.data.entity.UserEntity;\nimport com.fernandocejas.android10.sample.data.entity.mapper.UserEntityJsonMapper;\nimport com.fernandocejas.android10.sample.data.exception.NetworkConnectionException;\nimport io.reactivex.Observable;\nimport java.net.MalformedURLException;\nimport java.util.List;\n\n/**\n * {@link RestApi} implementation for retrieving data from the network.\n */\npublic class RestApiImpl implements RestApi {\n\n  private final Context context;\n  private final UserEntityJsonMapper userEntityJsonMapper;\n\n  /**\n   * Constructor of the class\n   *\n   * @param context {@link android.content.Context}.\n   * @param userEntityJsonMapper {@link UserEntityJsonMapper}.\n   */\n  public RestApiImpl(Context context, UserEntityJsonMapper userEntityJsonMapper) {\n    if (context == null || userEntityJsonMapper == null) {\n      throw new IllegalArgumentException(\"The constructor parameters cannot be null!!!\");\n    }\n    this.context = context.getApplicationContext();\n    this.userEntityJsonMapper = userEntityJsonMapper;\n  }\n\n  @Override public Observable<List<UserEntity>> userEntityList() {\n    return Observable.create(emitter -> {\n      if (isThereInternetConnection()) {\n        try {\n          String responseUserEntities = getUserEntitiesFromApi();\n          if (responseUserEntities != null) {\n            emitter.onNext(userEntityJsonMapper.transformUserEntityCollection(\n                responseUserEntities));\n            emitter.onComplete();\n          } else {\n            emitter.onError(new NetworkConnectionException());\n          }\n        } catch (Exception e) {\n          emitter.onError(new NetworkConnectionException(e.getCause()));\n        }\n      } else {\n        emitter.onError(new NetworkConnectionException());\n      }\n    });\n  }\n\n  @Override public Observable<UserEntity> userEntityById(final int userId) {\n    return Observable.create(emitter -> {\n      if (isThereInternetConnection()) {\n        try {\n          String responseUserDetails = getUserDetailsFromApi(userId);\n          if (responseUserDetails != null) {\n            emitter.onNext(userEntityJsonMapper.transformUserEntity(responseUserDetails));\n            emitter.onComplete();\n          } else {\n            emitter.onError(new NetworkConnectionException());\n          }\n        } catch (Exception e) {\n          emitter.onError(new NetworkConnectionException(e.getCause()));\n        }\n      } else {\n        emitter.onError(new NetworkConnectionException());\n      }\n    });\n  }\n\n  private String getUserEntitiesFromApi() throws MalformedURLException {\n    return ApiConnection.createGET(API_URL_GET_USER_LIST).requestSyncCall();\n  }\n\n  private String getUserDetailsFromApi(int userId) throws MalformedURLException {\n    String apiUrl = API_URL_GET_USER_DETAILS + userId + \".json\";\n    return ApiConnection.createGET(apiUrl).requestSyncCall();\n  }\n\n  /**\n   * Checks if the device has any active internet connection.\n   *\n   * @return true device with internet connection, otherwise false.\n   */\n  private boolean isThereInternetConnection() {\n    boolean isConnected;\n\n    ConnectivityManager connectivityManager =\n        (ConnectivityManager) this.context.getSystemService(Context.CONNECTIVITY_SERVICE);\n    NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();\n    isConnected = (networkInfo != null && networkInfo.isConnectedOrConnecting());\n\n    return isConnected;\n  }\n}\n"
  },
  {
    "path": "data/src/main/java/com/fernandocejas/android10/sample/data/repository/UserDataRepository.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.repository;\n\nimport com.fernandocejas.android10.sample.data.entity.mapper.UserEntityDataMapper;\nimport com.fernandocejas.android10.sample.data.repository.datasource.UserDataStore;\nimport com.fernandocejas.android10.sample.data.repository.datasource.UserDataStoreFactory;\nimport com.fernandocejas.android10.sample.domain.User;\nimport com.fernandocejas.android10.sample.domain.repository.UserRepository;\nimport io.reactivex.Observable;\nimport java.util.List;\nimport javax.inject.Inject;\nimport javax.inject.Singleton;\n\n/**\n * {@link UserRepository} for retrieving user data.\n */\n@Singleton\npublic class UserDataRepository implements UserRepository {\n\n  private final UserDataStoreFactory userDataStoreFactory;\n  private final UserEntityDataMapper userEntityDataMapper;\n\n  /**\n   * Constructs a {@link UserRepository}.\n   *\n   * @param dataStoreFactory A factory to construct different data source implementations.\n   * @param userEntityDataMapper {@link UserEntityDataMapper}.\n   */\n  @Inject\n  UserDataRepository(UserDataStoreFactory dataStoreFactory,\n      UserEntityDataMapper userEntityDataMapper) {\n    this.userDataStoreFactory = dataStoreFactory;\n    this.userEntityDataMapper = userEntityDataMapper;\n  }\n\n  @Override public Observable<List<User>> users() {\n    //we always get all users from the cloud\n    final UserDataStore userDataStore = this.userDataStoreFactory.createCloudDataStore();\n    return userDataStore.userEntityList().map(this.userEntityDataMapper::transform);\n  }\n\n  @Override public Observable<User> user(int userId) {\n    final UserDataStore userDataStore = this.userDataStoreFactory.create(userId);\n    return userDataStore.userEntityDetails(userId).map(this.userEntityDataMapper::transform);\n  }\n}\n"
  },
  {
    "path": "data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/CloudUserDataStore.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.repository.datasource;\n\nimport com.fernandocejas.android10.sample.data.cache.UserCache;\nimport com.fernandocejas.android10.sample.data.entity.UserEntity;\nimport com.fernandocejas.android10.sample.data.net.RestApi;\nimport io.reactivex.Observable;\nimport java.util.List;\n\n/**\n * {@link UserDataStore} implementation based on connections to the api (Cloud).\n */\nclass CloudUserDataStore implements UserDataStore {\n\n  private final RestApi restApi;\n  private final UserCache userCache;\n\n  /**\n   * Construct a {@link UserDataStore} based on connections to the api (Cloud).\n   *\n   * @param restApi The {@link RestApi} implementation to use.\n   * @param userCache A {@link UserCache} to cache data retrieved from the api.\n   */\n  CloudUserDataStore(RestApi restApi, UserCache userCache) {\n    this.restApi = restApi;\n    this.userCache = userCache;\n  }\n\n  @Override public Observable<List<UserEntity>> userEntityList() {\n    return this.restApi.userEntityList();\n  }\n\n  @Override public Observable<UserEntity> userEntityDetails(final int userId) {\n    return this.restApi.userEntityById(userId).doOnNext(CloudUserDataStore.this.userCache::put);\n  }\n}\n"
  },
  {
    "path": "data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/DiskUserDataStore.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.repository.datasource;\n\nimport com.fernandocejas.android10.sample.data.cache.UserCache;\nimport com.fernandocejas.android10.sample.data.entity.UserEntity;\nimport io.reactivex.Observable;\nimport java.util.List;\n\n/**\n * {@link UserDataStore} implementation based on file system data store.\n */\nclass DiskUserDataStore implements UserDataStore {\n\n  private final UserCache userCache;\n\n  /**\n   * Construct a {@link UserDataStore} based file system data store.\n   *\n   * @param userCache A {@link UserCache} to cache data retrieved from the api.\n   */\n  DiskUserDataStore(UserCache userCache) {\n    this.userCache = userCache;\n  }\n\n  @Override public Observable<List<UserEntity>> userEntityList() {\n    //TODO: implement simple cache for storing/retrieving collections of users.\n    throw new UnsupportedOperationException(\"Operation is not available!!!\");\n  }\n\n  @Override public Observable<UserEntity> userEntityDetails(final int userId) {\n     return this.userCache.get(userId);\n  }\n}\n"
  },
  {
    "path": "data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStore.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.repository.datasource;\n\nimport com.fernandocejas.android10.sample.data.entity.UserEntity;\nimport io.reactivex.Observable;\nimport java.util.List;\n\n/**\n * Interface that represents a data store from where data is retrieved.\n */\npublic interface UserDataStore {\n  /**\n   * Get an {@link Observable} which will emit a List of {@link UserEntity}.\n   */\n  Observable<List<UserEntity>> userEntityList();\n\n  /**\n   * Get an {@link Observable} which will emit a {@link UserEntity} by its id.\n   *\n   * @param userId The id to retrieve user data.\n   */\n  Observable<UserEntity> userEntityDetails(final int userId);\n}\n"
  },
  {
    "path": "data/src/main/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStoreFactory.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.repository.datasource;\n\nimport android.content.Context;\nimport android.support.annotation.NonNull;\nimport com.fernandocejas.android10.sample.data.cache.UserCache;\nimport com.fernandocejas.android10.sample.data.entity.mapper.UserEntityJsonMapper;\nimport com.fernandocejas.android10.sample.data.net.RestApi;\nimport com.fernandocejas.android10.sample.data.net.RestApiImpl;\nimport javax.inject.Inject;\nimport javax.inject.Singleton;\n\n/**\n * Factory that creates different implementations of {@link UserDataStore}.\n */\n@Singleton\npublic class UserDataStoreFactory {\n\n  private final Context context;\n  private final UserCache userCache;\n\n  @Inject\n  UserDataStoreFactory(@NonNull Context context, @NonNull UserCache userCache) {\n    this.context = context.getApplicationContext();\n    this.userCache = userCache;\n  }\n\n  /**\n   * Create {@link UserDataStore} from a user id.\n   */\n  public UserDataStore create(int userId) {\n    UserDataStore userDataStore;\n\n    if (!this.userCache.isExpired() && this.userCache.isCached(userId)) {\n      userDataStore = new DiskUserDataStore(this.userCache);\n    } else {\n      userDataStore = createCloudDataStore();\n    }\n\n    return userDataStore;\n  }\n\n  /**\n   * Create {@link UserDataStore} to retrieve data from the Cloud.\n   */\n  public UserDataStore createCloudDataStore() {\n    final UserEntityJsonMapper userEntityJsonMapper = new UserEntityJsonMapper();\n    final RestApi restApi = new RestApiImpl(this.context, userEntityJsonMapper);\n\n    return new CloudUserDataStore(restApi, this.userCache);\n  }\n}\n"
  },
  {
    "path": "data/src/test/java/com/fernandocejas/android10/sample/data/ApplicationStub.java",
    "content": "/**\n * Copyright (C) 2015 android10.org Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data;\n\nimport android.app.Application;\n\npublic class ApplicationStub extends Application {}\n"
  },
  {
    "path": "data/src/test/java/com/fernandocejas/android10/sample/data/ApplicationTestCase.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data;\n\nimport android.content.Context;\nimport java.io.File;\nimport org.junit.Rule;\nimport org.junit.rules.TestRule;\nimport org.junit.runner.RunWith;\nimport org.mockito.MockitoAnnotations;\nimport org.robolectric.RobolectricTestRunner;\nimport org.robolectric.RuntimeEnvironment;\nimport org.robolectric.annotation.Config;\n\n/**\n * Base class for Robolectric data layer tests.\n * Inherit from this class to create a test.\n */\n@RunWith(RobolectricTestRunner.class)\n@Config(constants = BuildConfig.class, application = ApplicationStub.class, sdk = 21)\npublic abstract class ApplicationTestCase {\n\n  @Rule public TestRule injectMocksRule = (base, description) -> {\n    MockitoAnnotations.initMocks(ApplicationTestCase.this);\n    return base;\n  };\n\n  public static Context context() {\n    return RuntimeEnvironment.application;\n  }\n\n  public static File cacheDir() {\n    return RuntimeEnvironment.application.getCacheDir();\n  }\n}\n"
  },
  {
    "path": "data/src/test/java/com/fernandocejas/android10/sample/data/cache/FileManagerTest.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.cache;\n\nimport com.fernandocejas.android10.sample.data.ApplicationTestCase;\nimport java.io.File;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport static org.hamcrest.CoreMatchers.equalTo;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamcrest.MatcherAssert.assertThat;\n\npublic class FileManagerTest extends ApplicationTestCase {\n\n  private FileManager fileManager;\n\n  @Before\n  public void setUp() {\n    fileManager = new FileManager();\n  }\n\n  @After\n  public void tearDown() {\n    if (cacheDir() != null) {\n      fileManager.clearDirectory(cacheDir());\n    }\n  }\n\n  @Test\n  public void testWriteToFile() {\n    File fileToWrite = createDummyFile();\n    String fileContent = \"content\";\n\n    fileManager.writeToFile(fileToWrite, fileContent);\n\n    assertThat(fileToWrite.exists(), is(true));\n  }\n\n  @Test\n  public void testFileContent() {\n    File fileToWrite = createDummyFile();\n    String fileContent = \"content\\n\";\n\n    fileManager.writeToFile(fileToWrite, fileContent);\n    String expectedContent = fileManager.readFileContent(fileToWrite);\n\n    assertThat(expectedContent, is(equalTo(fileContent)));\n  }\n\n  private File createDummyFile() {\n    String dummyFilePath = cacheDir().getPath() + File.separator + \"dummyFile\";\n    return new File(dummyFilePath);\n  }\n}\n"
  },
  {
    "path": "data/src/test/java/com/fernandocejas/android10/sample/data/cache/serializer/SerializerTest.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.cache.serializer;\n\nimport com.fernandocejas.android10.sample.data.entity.UserEntity;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.runners.MockitoJUnitRunner;\n\nimport static org.hamcrest.CoreMatchers.equalTo;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.junit.Assert.assertThat;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class SerializerTest {\n\n  private static final String JSON_RESPONSE = \"{\\n\"\n      + \"    \\\"id\\\": 1,\\n\"\n      + \"    \\\"cover_url\\\": \\\"http://www.android10.org/myapi/cover_1.jpg\\\",\\n\"\n      + \"    \\\"full_name\\\": \\\"Simon Hill\\\",\\n\"\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\"\n      + \"    \\\"followers\\\": 7484,\\n\"\n      + \"    \\\"email\\\": \\\"jcooper@babbleset.edu\\\"\\n\"\n      + \"}\";\n\n  private Serializer serializer;\n\n  @Before\n  public void setUp() {\n    serializer = new Serializer();\n  }\n\n  @Test\n  public void testSerializeHappyCase() {\n    final UserEntity userEntityOne = serializer.deserialize(JSON_RESPONSE, UserEntity.class);\n    final String jsonString = serializer.serialize(userEntityOne, UserEntity.class);\n    final UserEntity userEntityTwo = serializer.deserialize(jsonString, UserEntity.class);\n\n    assertThat(userEntityOne.getUserId(), is(userEntityTwo.getUserId()));\n    assertThat(userEntityOne.getFullname(), is(equalTo(userEntityTwo.getFullname())));\n    assertThat(userEntityOne.getFollowers(), is(userEntityTwo.getFollowers()));\n  }\n\n  @Test\n  public void testDesearializeHappyCase() {\n    final UserEntity userEntity = serializer.deserialize(JSON_RESPONSE, UserEntity.class);\n\n    assertThat(userEntity.getUserId(), is(1));\n    assertThat(userEntity.getFullname(), is(\"Simon Hill\"));\n    assertThat(userEntity.getFollowers(), is(7484));\n  }\n}\n"
  },
  {
    "path": "data/src/test/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityDataMapperTest.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.entity.mapper;\n\nimport com.fernandocejas.android10.sample.data.entity.UserEntity;\nimport com.fernandocejas.android10.sample.domain.User;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.runners.MockitoJUnitRunner;\n\nimport static org.hamcrest.CoreMatchers.instanceOf;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamcrest.MatcherAssert.assertThat;\nimport static org.mockito.Mockito.mock;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class UserEntityDataMapperTest {\n\n  private static final int FAKE_USER_ID = 123;\n  private static final String FAKE_FULLNAME = \"Tony Stark\";\n\n  private UserEntityDataMapper userEntityDataMapper;\n\n  @Before\n  public void setUp() throws Exception {\n    userEntityDataMapper = new UserEntityDataMapper();\n  }\n\n  @Test\n  public void testTransformUserEntity() {\n    UserEntity userEntity = createFakeUserEntity();\n    User user = userEntityDataMapper.transform(userEntity);\n\n    assertThat(user, is(instanceOf(User.class)));\n    assertThat(user.getUserId(), is(FAKE_USER_ID));\n    assertThat(user.getFullName(), is(FAKE_FULLNAME));\n  }\n\n  @Test\n  public void testTransformUserEntityCollection() {\n    UserEntity mockUserEntityOne = mock(UserEntity.class);\n    UserEntity mockUserEntityTwo = mock(UserEntity.class);\n\n    List<UserEntity> userEntityList = new ArrayList<UserEntity>(5);\n    userEntityList.add(mockUserEntityOne);\n    userEntityList.add(mockUserEntityTwo);\n\n    Collection<User> userCollection = userEntityDataMapper.transform(userEntityList);\n\n    assertThat(userCollection.toArray()[0], is(instanceOf(User.class)));\n    assertThat(userCollection.toArray()[1], is(instanceOf(User.class)));\n    assertThat(userCollection.size(), is(2));\n  }\n\n  private UserEntity createFakeUserEntity() {\n    UserEntity userEntity = new UserEntity();\n    userEntity.setUserId(FAKE_USER_ID);\n    userEntity.setFullname(FAKE_FULLNAME);\n\n    return userEntity;\n  }\n}\n"
  },
  {
    "path": "data/src/test/java/com/fernandocejas/android10/sample/data/entity/mapper/UserEntityJsonMapperTest.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.entity.mapper;\n\nimport com.fernandocejas.android10.sample.data.entity.UserEntity;\nimport com.google.gson.JsonSyntaxException;\nimport java.util.Collection;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.runners.MockitoJUnitRunner;\n\nimport static org.hamcrest.CoreMatchers.equalTo;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamcrest.MatcherAssert.assertThat;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class UserEntityJsonMapperTest {\n\n  private static final String JSON_RESPONSE_USER_DETAILS = \"{\\n\"\n      + \"    \\\"id\\\": 1,\\n\"\n      + \"    \\\"cover_url\\\": \\\"http://www.android10.org/myapi/cover_1.jpg\\\",\\n\"\n      + \"    \\\"full_name\\\": \\\"Simon Hill\\\",\\n\"\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\"\n      + \"    \\\"followers\\\": 7484,\\n\"\n      + \"    \\\"email\\\": \\\"jcooper@babbleset.edu\\\"\\n\"\n      + \"}\";\n\n  private static final String JSON_RESPONSE_USER_COLLECTION = \"[{\\n\"\n      + \"    \\\"id\\\": 1,\\n\"\n      + \"    \\\"full_name\\\": \\\"Simon Hill\\\",\\n\"\n      + \"    \\\"followers\\\": 7484\\n\"\n      + \"}, {\\n\"\n      + \"    \\\"id\\\": 12,\\n\"\n      + \"    \\\"full_name\\\": \\\"Pedro Garcia\\\",\\n\"\n      + \"    \\\"followers\\\": 1381\\n\"\n      + \"}]\";\n\n  private UserEntityJsonMapper userEntityJsonMapper;\n\n  @Rule\n  public ExpectedException expectedException = ExpectedException.none();\n\n  @Before\n  public void setUp() {\n    userEntityJsonMapper = new UserEntityJsonMapper();\n  }\n\n  @Test\n  public void testTransformUserEntityHappyCase() {\n    UserEntity userEntity = userEntityJsonMapper.transformUserEntity(JSON_RESPONSE_USER_DETAILS);\n\n    assertThat(userEntity.getUserId(), is(1));\n    assertThat(userEntity.getFullname(), is(equalTo(\"Simon Hill\")));\n    assertThat(userEntity.getEmail(), is(equalTo(\"jcooper@babbleset.edu\")));\n  }\n\n  @Test\n  public void testTransformUserEntityCollectionHappyCase() {\n    Collection<UserEntity> userEntityCollection =\n        userEntityJsonMapper.transformUserEntityCollection(\n            JSON_RESPONSE_USER_COLLECTION);\n\n    assertThat(((UserEntity) userEntityCollection.toArray()[0]).getUserId(), is(1));\n    assertThat(((UserEntity) userEntityCollection.toArray()[1]).getUserId(), is(12));\n    assertThat(userEntityCollection.size(), is(2));\n  }\n\n  @Test\n  public void testTransformUserEntityNotValidResponse() {\n    expectedException.expect(JsonSyntaxException.class);\n    userEntityJsonMapper.transformUserEntity(\"ironman\");\n  }\n\n  @Test\n  public void testTransformUserEntityCollectionNotValidResponse() {\n    expectedException.expect(JsonSyntaxException.class);\n    userEntityJsonMapper.transformUserEntityCollection(\"Tony Stark\");\n  }\n}\n"
  },
  {
    "path": "data/src/test/java/com/fernandocejas/android10/sample/data/exception/RepositoryErrorBundleTest.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.exception;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.runners.MockitoJUnitRunner;\n\nimport static org.mockito.Mockito.verify;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class RepositoryErrorBundleTest {\n\n  private RepositoryErrorBundle repositoryErrorBundle;\n\n  @Mock private Exception mockException;\n\n  @Before\n  public void setUp() {\n    repositoryErrorBundle = new RepositoryErrorBundle(mockException);\n  }\n\n  @Test\n  @SuppressWarnings(\"ThrowableResultOfMethodCallIgnored\")\n  public void testGetErrorMessageInteraction() {\n    repositoryErrorBundle.getErrorMessage();\n\n    verify(mockException).getMessage();\n  }\n}\n"
  },
  {
    "path": "data/src/test/java/com/fernandocejas/android10/sample/data/repository/UserDataRepositoryTest.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.repository;\n\nimport com.fernandocejas.android10.sample.data.entity.UserEntity;\nimport com.fernandocejas.android10.sample.data.entity.mapper.UserEntityDataMapper;\nimport com.fernandocejas.android10.sample.data.repository.datasource.UserDataStore;\nimport com.fernandocejas.android10.sample.data.repository.datasource.UserDataStoreFactory;\nimport com.fernandocejas.android10.sample.domain.User;\nimport io.reactivex.Observable;\nimport java.util.ArrayList;\nimport java.util.List;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.runners.MockitoJUnitRunner;\n\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Matchers.anyInt;\nimport static org.mockito.Mockito.verify;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class UserDataRepositoryTest {\n\n  private static final int FAKE_USER_ID = 123;\n\n  private UserDataRepository userDataRepository;\n\n  @Mock private UserDataStoreFactory mockUserDataStoreFactory;\n  @Mock private UserEntityDataMapper mockUserEntityDataMapper;\n  @Mock private UserDataStore mockUserDataStore;\n  @Mock private UserEntity mockUserEntity;\n  @Mock private User mockUser;\n\n  @Before\n  public void setUp() {\n    userDataRepository = new UserDataRepository(mockUserDataStoreFactory, mockUserEntityDataMapper);\n    given(mockUserDataStoreFactory.create(anyInt())).willReturn(mockUserDataStore);\n    given(mockUserDataStoreFactory.createCloudDataStore()).willReturn(mockUserDataStore);\n  }\n\n  @Test\n  public void testGetUsersHappyCase() {\n    List<UserEntity> usersList = new ArrayList<>();\n    usersList.add(new UserEntity());\n    given(mockUserDataStore.userEntityList()).willReturn(Observable.just(usersList));\n\n    userDataRepository.users();\n\n    verify(mockUserDataStoreFactory).createCloudDataStore();\n    verify(mockUserDataStore).userEntityList();\n  }\n\n  @Test\n  public void testGetUserHappyCase() {\n    UserEntity userEntity = new UserEntity();\n    given(mockUserDataStore.userEntityDetails(FAKE_USER_ID)).willReturn(Observable.just(userEntity));\n    userDataRepository.user(FAKE_USER_ID);\n\n    verify(mockUserDataStoreFactory).create(FAKE_USER_ID);\n    verify(mockUserDataStore).userEntityDetails(FAKE_USER_ID);\n  }\n}\n"
  },
  {
    "path": "data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/CloudUserDataStoreTest.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.repository.datasource;\n\nimport com.fernandocejas.android10.sample.data.cache.UserCache;\nimport com.fernandocejas.android10.sample.data.entity.UserEntity;\nimport com.fernandocejas.android10.sample.data.net.RestApi;\nimport io.reactivex.Observable;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.runners.MockitoJUnitRunner;\n\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class CloudUserDataStoreTest {\n\n  private static final int FAKE_USER_ID = 765;\n\n  private CloudUserDataStore cloudUserDataStore;\n\n  @Mock private RestApi mockRestApi;\n  @Mock private UserCache mockUserCache;\n\n  @Before\n  public void setUp() {\n    cloudUserDataStore = new CloudUserDataStore(mockRestApi, mockUserCache);\n  }\n\n  @Test\n  public void testGetUserEntityListFromApi() {\n    cloudUserDataStore.userEntityList();\n    verify(mockRestApi).userEntityList();\n  }\n\n  @Test\n  public void testGetUserEntityDetailsFromApi() {\n    UserEntity fakeUserEntity = new UserEntity();\n    Observable<UserEntity> fakeObservable = Observable.just(fakeUserEntity);\n    given(mockRestApi.userEntityById(FAKE_USER_ID)).willReturn(fakeObservable);\n\n    cloudUserDataStore.userEntityDetails(FAKE_USER_ID);\n\n    verify(mockRestApi).userEntityById(FAKE_USER_ID);\n  }\n}\n"
  },
  {
    "path": "data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/DiskUserDataStoreTest.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.repository.datasource;\n\nimport com.fernandocejas.android10.sample.data.cache.UserCache;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.runners.MockitoJUnitRunner;\n\nimport static org.mockito.Mockito.verify;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class DiskUserDataStoreTest {\n\n  private static final int FAKE_USER_ID = 11;\n\n  private DiskUserDataStore diskUserDataStore;\n\n  @Mock private UserCache mockUserCache;\n\n  @Rule public ExpectedException expectedException = ExpectedException.none();\n\n  @Before\n  public void setUp() {\n    diskUserDataStore = new DiskUserDataStore(mockUserCache);\n  }\n\n  @Test\n  public void testGetUserEntityListUnsupported() {\n    expectedException.expect(UnsupportedOperationException.class);\n    diskUserDataStore.userEntityList();\n  }\n\n  @Test\n  public void testGetUserEntityDetailesFromCache() {\n    diskUserDataStore.userEntityDetails(FAKE_USER_ID);\n    verify(mockUserCache).get(FAKE_USER_ID);\n  }\n}\n"
  },
  {
    "path": "data/src/test/java/com/fernandocejas/android10/sample/data/repository/datasource/UserDataStoreFactoryTest.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.data.repository.datasource;\n\nimport com.fernandocejas.android10.sample.data.ApplicationTestCase;\nimport com.fernandocejas.android10.sample.data.cache.UserCache;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.robolectric.RuntimeEnvironment;\n\nimport static org.hamcrest.CoreMatchers.instanceOf;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamcrest.CoreMatchers.notNullValue;\nimport static org.hamcrest.MatcherAssert.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\n\npublic class UserDataStoreFactoryTest extends ApplicationTestCase {\n\n  private static final int FAKE_USER_ID = 11;\n\n  private UserDataStoreFactory userDataStoreFactory;\n\n  @Mock private UserCache mockUserCache;\n\n  @Before\n  public void setUp() {\n    userDataStoreFactory = new UserDataStoreFactory(RuntimeEnvironment.application, mockUserCache);\n  }\n\n  @Test\n  public void testCreateDiskDataStore() {\n    given(mockUserCache.isCached(FAKE_USER_ID)).willReturn(true);\n    given(mockUserCache.isExpired()).willReturn(false);\n\n    UserDataStore userDataStore = userDataStoreFactory.create(FAKE_USER_ID);\n\n    assertThat(userDataStore, is(notNullValue()));\n    assertThat(userDataStore, is(instanceOf(DiskUserDataStore.class)));\n\n    verify(mockUserCache).isCached(FAKE_USER_ID);\n    verify(mockUserCache).isExpired();\n  }\n\n  @Test\n  public void testCreateCloudDataStore() {\n    given(mockUserCache.isExpired()).willReturn(true);\n    given(mockUserCache.isCached(FAKE_USER_ID)).willReturn(false);\n\n    UserDataStore userDataStore = userDataStoreFactory.create(FAKE_USER_ID);\n\n    assertThat(userDataStore, is(notNullValue()));\n    assertThat(userDataStore, is(instanceOf(CloudUserDataStore.class)));\n\n    verify(mockUserCache).isExpired();\n  }\n}\n"
  },
  {
    "path": "domain/build.gradle",
    "content": "apply plugin: 'java'\n\n//noinspection GroovyUnusedAssignment\nsourceCompatibility = 1.7\n//noinspection GroovyUnusedAssignment\ntargetCompatibility = 1.7\n\nconfigurations {\n  provided\n}\n\nsourceSets {\n  main {\n    compileClasspath += configurations.provided\n  }\n}\n\ndependencies {\n  def domainDependencies = rootProject.ext.domainDependencies\n  def domainTestDependencies = rootProject.ext.domainTestDependencies\n\n  compileOnly domainDependencies.javaxAnnotation\n\n  implementation domainDependencies.javaxInject\n  implementation domainDependencies.rxJava\n  compile domainDependencies.arrow\n\n  testImplementation domainTestDependencies.junit\n  testImplementation domainTestDependencies.mockito\n  testImplementation domainTestDependencies.assertj\n}\n"
  },
  {
    "path": "domain/src/main/java/com/fernandocejas/android10/sample/domain/User.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.domain;\n\n/**\n * Class that represents a User in the domain layer.\n */\npublic class User {\n\n  private final int userId;\n\n  public User(int userId) {\n    this.userId = userId;\n  }\n\n  private String coverUrl;\n  private String fullName;\n  private String email;\n  private String description;\n  private int followers;\n\n  public int getUserId() {\n    return userId;\n  }\n\n  public String getCoverUrl() {\n    return coverUrl;\n  }\n\n  public void setCoverUrl(String coverUrl) {\n    this.coverUrl = coverUrl;\n  }\n\n  public String getFullName() {\n    return fullName;\n  }\n\n  public void setFullName(String fullName) {\n    this.fullName = fullName;\n  }\n\n  public String getEmail() {\n    return email;\n  }\n\n  public void setEmail(String email) {\n    this.email = email;\n  }\n\n  public String getDescription() {\n    return description;\n  }\n\n  public void setDescription(String description) {\n    this.description = description;\n  }\n\n  public int getFollowers() {\n    return followers;\n  }\n\n  public void setFollowers(int followers) {\n    this.followers = followers;\n  }\n}\n"
  },
  {
    "path": "domain/src/main/java/com/fernandocejas/android10/sample/domain/exception/DefaultErrorBundle.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.domain.exception;\n\n/**\n *  Wrapper around Exceptions used to manage default errors.\n */\npublic class DefaultErrorBundle implements ErrorBundle {\n\n  private static final String DEFAULT_ERROR_MSG = \"Unknown error\";\n\n  private final Exception exception;\n\n  public DefaultErrorBundle(Exception exception) {\n    this.exception = exception;\n  }\n\n  @Override\n  public Exception getException() {\n    return exception;\n  }\n\n  @Override\n  public String getErrorMessage() {\n    return (exception != null) ? this.exception.getMessage() : DEFAULT_ERROR_MSG;\n  }\n}\n"
  },
  {
    "path": "domain/src/main/java/com/fernandocejas/android10/sample/domain/exception/ErrorBundle.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.domain.exception;\n\n/**\n * Interface to represent a wrapper around an {@link java.lang.Exception} to manage errors.\n */\npublic interface ErrorBundle {\n  Exception getException();\n\n  String getErrorMessage();\n}\n"
  },
  {
    "path": "domain/src/main/java/com/fernandocejas/android10/sample/domain/executor/PostExecutionThread.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.domain.executor;\n\nimport io.reactivex.Scheduler;\n\n/**\n * Thread abstraction created to change the execution context from any thread to any other thread.\n * Useful to encapsulate a UI Thread for example, since some job will be done in background, an\n * implementation of this interface will change context and update the UI.\n */\npublic interface PostExecutionThread {\n  Scheduler getScheduler();\n}\n"
  },
  {
    "path": "domain/src/main/java/com/fernandocejas/android10/sample/domain/executor/ThreadExecutor.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.domain.executor;\n\nimport java.util.concurrent.Executor;\n\n/**\n * Executor implementation can be based on different frameworks or techniques of asynchronous\n * execution, but every implementation will execute the\n * {@link com.fernandocejas.android10.sample.domain.interactor.UseCase} out of the UI thread.\n */\npublic interface ThreadExecutor extends Executor {}\n"
  },
  {
    "path": "domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/DefaultObserver.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.domain.interactor;\n\nimport io.reactivex.observers.DisposableObserver;\n\n/**\n * Default {@link DisposableObserver} base class to be used whenever you want default error handling.\n */\npublic class DefaultObserver<T> extends DisposableObserver<T> {\n  @Override public void onNext(T t) {\n    // no-op by default.\n  }\n\n  @Override public void onComplete() {\n    // no-op by default.\n  }\n\n  @Override public void onError(Throwable exception) {\n    // no-op by default.\n  }\n}\n"
  },
  {
    "path": "domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetails.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.domain.interactor;\n\nimport com.fernandocejas.android10.sample.domain.User;\nimport com.fernandocejas.android10.sample.domain.executor.PostExecutionThread;\nimport com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;\nimport com.fernandocejas.android10.sample.domain.repository.UserRepository;\nimport com.fernandocejas.arrow.checks.Preconditions;\nimport io.reactivex.Observable;\nimport javax.inject.Inject;\n\n/**\n * This class is an implementation of {@link UseCase} that represents a use case for\n * retrieving data related to an specific {@link User}.\n */\npublic class GetUserDetails extends UseCase<User, GetUserDetails.Params> {\n\n  private final UserRepository userRepository;\n\n  @Inject\n  GetUserDetails(UserRepository userRepository, ThreadExecutor threadExecutor,\n      PostExecutionThread postExecutionThread) {\n    super(threadExecutor, postExecutionThread);\n    this.userRepository = userRepository;\n  }\n\n  @Override Observable<User> buildUseCaseObservable(Params params) {\n    Preconditions.checkNotNull(params);\n    return this.userRepository.user(params.userId);\n  }\n\n  public static final class Params {\n\n    private final int userId;\n\n    private Params(int userId) {\n      this.userId = userId;\n    }\n\n    public static Params forUser(int userId) {\n      return new Params(userId);\n    }\n  }\n}\n"
  },
  {
    "path": "domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.domain.interactor;\n\nimport com.fernandocejas.android10.sample.domain.User;\nimport com.fernandocejas.android10.sample.domain.executor.PostExecutionThread;\nimport com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;\nimport com.fernandocejas.android10.sample.domain.repository.UserRepository;\nimport io.reactivex.Observable;\nimport java.util.List;\nimport javax.inject.Inject;\n\n/**\n * This class is an implementation of {@link UseCase} that represents a use case for\n * retrieving a collection of all {@link User}.\n */\npublic class GetUserList extends UseCase<List<User>, Void> {\n\n  private final UserRepository userRepository;\n\n  @Inject\n  GetUserList(UserRepository userRepository, ThreadExecutor threadExecutor,\n      PostExecutionThread postExecutionThread) {\n    super(threadExecutor, postExecutionThread);\n    this.userRepository = userRepository;\n  }\n\n  @Override Observable<List<User>> buildUseCaseObservable(Void unused) {\n    return this.userRepository.users();\n  }\n}\n"
  },
  {
    "path": "domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/UseCase.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.domain.interactor;\n\nimport com.fernandocejas.android10.sample.domain.executor.PostExecutionThread;\nimport com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;\nimport com.fernandocejas.arrow.checks.Preconditions;\nimport io.reactivex.Observable;\nimport io.reactivex.disposables.CompositeDisposable;\nimport io.reactivex.disposables.Disposable;\nimport io.reactivex.observers.DisposableObserver;\nimport io.reactivex.schedulers.Schedulers;\n\n/**\n * Abstract class for a Use Case (Interactor in terms of Clean Architecture).\n * This interface represents a execution unit for different use cases (this means any use case\n * in the application should implement this contract).\n *\n * By convention each UseCase implementation will return the result using a {@link DisposableObserver}\n * that will execute its job in a background thread and will post the result in the UI thread.\n */\npublic abstract class UseCase<T, Params> {\n\n  private final ThreadExecutor threadExecutor;\n  private final PostExecutionThread postExecutionThread;\n  private final CompositeDisposable disposables;\n\n  UseCase(ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) {\n    this.threadExecutor = threadExecutor;\n    this.postExecutionThread = postExecutionThread;\n    this.disposables = new CompositeDisposable();\n  }\n\n  /**\n   * Builds an {@link Observable} which will be used when executing the current {@link UseCase}.\n   */\n  abstract Observable<T> buildUseCaseObservable(Params params);\n\n  /**\n   * Executes the current use case.\n   *\n   * @param observer {@link DisposableObserver} which will be listening to the observable build\n   * by {@link #buildUseCaseObservable(Params)} ()} method.\n   * @param params Parameters (Optional) used to build/execute this use case.\n   */\n  public void execute(DisposableObserver<T> observer, Params params) {\n    Preconditions.checkNotNull(observer);\n    final Observable<T> observable = this.buildUseCaseObservable(params)\n        .subscribeOn(Schedulers.from(threadExecutor))\n        .observeOn(postExecutionThread.getScheduler());\n    addDisposable(observable.subscribeWith(observer));\n  }\n\n  /**\n   * Dispose from current {@link CompositeDisposable}.\n   */\n  public void dispose() {\n    if (!disposables.isDisposed()) {\n      disposables.dispose();\n    }\n  }\n\n  /**\n   * Dispose from current {@link CompositeDisposable}.\n   */\n  private void addDisposable(Disposable disposable) {\n    Preconditions.checkNotNull(disposable);\n    Preconditions.checkNotNull(disposables);\n    disposables.add(disposable);\n  }\n}\n"
  },
  {
    "path": "domain/src/main/java/com/fernandocejas/android10/sample/domain/repository/UserRepository.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.domain.repository;\n\nimport com.fernandocejas.android10.sample.domain.User;\nimport io.reactivex.Observable;\nimport java.util.List;\n\n/**\n * Interface that represents a Repository for getting {@link User} related data.\n */\npublic interface UserRepository {\n  /**\n   * Get an {@link Observable} which will emit a List of {@link User}.\n   */\n  Observable<List<User>> users();\n\n  /**\n   * Get an {@link Observable} which will emit a {@link User}.\n   *\n   * @param userId The user id used to retrieve user data.\n   */\n  Observable<User> user(final int userId);\n}\n"
  },
  {
    "path": "domain/src/test/java/com/fernandocejas/android10/sample/domain/UserTest.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.domain;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class UserTest {\n\n  private static final int FAKE_USER_ID = 8;\n\n  private User user;\n\n  @Before\n  public void setUp() {\n    user = new User(FAKE_USER_ID);\n  }\n\n  @Test\n  public void testUserConstructorHappyCase() {\n    final int userId = user.getUserId();\n\n    assertThat(userId).isEqualTo(FAKE_USER_ID);\n  }\n}\n"
  },
  {
    "path": "domain/src/test/java/com/fernandocejas/android10/sample/domain/exception/DefaultErrorBundleTest.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.domain.exception;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.runners.MockitoJUnitRunner;\n\nimport static org.mockito.Mockito.verify;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class DefaultErrorBundleTest {\n  private DefaultErrorBundle defaultErrorBundle;\n\n  @Mock private Exception mockException;\n\n  @Before\n  public void setUp() {\n    defaultErrorBundle = new DefaultErrorBundle(mockException);\n  }\n\n  @Test\n  public void testGetErrorMessageInteraction() {\n    defaultErrorBundle.getErrorMessage();\n\n    verify(mockException).getMessage();\n  }\n}\n"
  },
  {
    "path": "domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserDetailsTest.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.domain.interactor;\n\nimport com.fernandocejas.android10.sample.domain.executor.PostExecutionThread;\nimport com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;\nimport com.fernandocejas.android10.sample.domain.interactor.GetUserDetails.Params;\nimport com.fernandocejas.android10.sample.domain.repository.UserRepository;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.runners.MockitoJUnitRunner;\n\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\nimport static org.mockito.Mockito.verifyZeroInteractions;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class GetUserDetailsTest {\n\n  private static final int USER_ID = 123;\n\n  private GetUserDetails getUserDetails;\n\n  @Mock private UserRepository mockUserRepository;\n  @Mock private ThreadExecutor mockThreadExecutor;\n  @Mock private PostExecutionThread mockPostExecutionThread;\n\n  @Rule public ExpectedException expectedException = ExpectedException.none();\n\n  @Before\n  public void setUp() {\n    getUserDetails = new GetUserDetails(mockUserRepository, mockThreadExecutor,\n        mockPostExecutionThread);\n  }\n\n  @Test\n  public void testGetUserDetailsUseCaseObservableHappyCase() {\n    getUserDetails.buildUseCaseObservable(Params.forUser(USER_ID));\n\n    verify(mockUserRepository).user(USER_ID);\n    verifyNoMoreInteractions(mockUserRepository);\n    verifyZeroInteractions(mockPostExecutionThread);\n    verifyZeroInteractions(mockThreadExecutor);\n  }\n\n  @Test\n  public void testShouldFailWhenNoOrEmptyParameters() {\n    expectedException.expect(NullPointerException.class);\n    getUserDetails.buildUseCaseObservable(null);\n  }\n}\n"
  },
  {
    "path": "domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/GetUserListTest.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.domain.interactor;\n\nimport com.fernandocejas.android10.sample.domain.executor.PostExecutionThread;\nimport com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;\nimport com.fernandocejas.android10.sample.domain.repository.UserRepository;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.runners.MockitoJUnitRunner;\n\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\nimport static org.mockito.Mockito.verifyZeroInteractions;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class GetUserListTest {\n\n  private GetUserList getUserList;\n\n  @Mock private ThreadExecutor mockThreadExecutor;\n  @Mock private PostExecutionThread mockPostExecutionThread;\n  @Mock private UserRepository mockUserRepository;\n\n  @Before\n  public void setUp() {\n    getUserList = new GetUserList(mockUserRepository, mockThreadExecutor,\n        mockPostExecutionThread);\n  }\n\n  @Test\n  public void testGetUserListUseCaseObservableHappyCase() {\n    getUserList.buildUseCaseObservable(null);\n\n    verify(mockUserRepository).users();\n    verifyNoMoreInteractions(mockUserRepository);\n    verifyZeroInteractions(mockThreadExecutor);\n    verifyZeroInteractions(mockPostExecutionThread);\n  }\n}\n"
  },
  {
    "path": "domain/src/test/java/com/fernandocejas/android10/sample/domain/interactor/UseCaseTest.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.domain.interactor;\n\nimport com.fernandocejas.android10.sample.domain.executor.PostExecutionThread;\nimport com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;\nimport io.reactivex.Observable;\nimport io.reactivex.observers.DisposableObserver;\nimport io.reactivex.schedulers.TestScheduler;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.runners.MockitoJUnitRunner;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class UseCaseTest {\n\n  private UseCaseTestClass useCase;\n\n  private TestDisposableObserver<Object> testObserver;\n\n  @Mock private ThreadExecutor mockThreadExecutor;\n  @Mock private PostExecutionThread mockPostExecutionThread;\n\n  @Rule public ExpectedException expectedException = ExpectedException.none();\n\n  @Before\n  public void setUp() {\n    this.useCase = new UseCaseTestClass(mockThreadExecutor, mockPostExecutionThread);\n    this.testObserver = new TestDisposableObserver<>();\n    given(mockPostExecutionThread.getScheduler()).willReturn(new TestScheduler());\n  }\n\n  @Test\n  public void testBuildUseCaseObservableReturnCorrectResult() {\n    useCase.execute(testObserver, Params.EMPTY);\n\n    assertThat(testObserver.valuesCount).isZero();\n  }\n\n  @Test\n  public void testSubscriptionWhenExecutingUseCase() {\n    useCase.execute(testObserver, Params.EMPTY);\n    useCase.dispose();\n\n    assertThat(testObserver.isDisposed()).isTrue();\n  }\n\n  @Test\n  public void testShouldFailWhenExecuteWithNullObserver() {\n    expectedException.expect(NullPointerException.class);\n    useCase.execute(null, Params.EMPTY);\n  }\n\n  private static class UseCaseTestClass extends UseCase<Object, Params> {\n\n    UseCaseTestClass(ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) {\n      super(threadExecutor, postExecutionThread);\n    }\n\n    @Override Observable<Object> buildUseCaseObservable(Params params) {\n      return Observable.empty();\n    }\n\n    @Override\n    public void execute(DisposableObserver<Object> observer, Params params) {\n      super.execute(observer, params);\n    }\n  }\n\n  private static class TestDisposableObserver<T> extends DisposableObserver<T> {\n    private int valuesCount = 0;\n\n    @Override public void onNext(T value) {\n      valuesCount++;\n    }\n\n    @Override public void onError(Throwable e) {\n      // no-op by default.\n    }\n\n    @Override public void onComplete() {\n      // no-op by default.\n    }\n  }\n\n  private static class Params {\n    private static Params EMPTY = new Params();\n    private Params() {}\n  }\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Wed Dec 21 17:11:04 ART 2016\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-4.1-all.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "#Gradle configuration\norg.gradle.daemon=true\norg.gradle.jvmargs=-Dfile.encoding=UTF-8\norg.gradle.parallel=true\norg.gradle.configureondemand=true\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave ( ) {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=$(save \"$@\")\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\n# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong\nif [ \"$(uname)\" = \"Darwin\" ] && [ \"$HOME\" = \"$PWD\" ]; then\n  cd \"$(dirname \"$0\")\"\nfi\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windows variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "presentation/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "presentation/build.gradle",
    "content": "apply plugin: 'com.android.application'\n//apply plugin: 'com.neenbedankt.android-apt'\n\nandroid {\n  def globalConfiguration = rootProject.extensions.getByName(\"ext\")\n\n  compileSdkVersion globalConfiguration.getAt(\"androidCompileSdkVersion\")\n  buildToolsVersion globalConfiguration.getAt(\"androidBuildToolsVersion\")\n\n  defaultConfig {\n    minSdkVersion globalConfiguration.getAt(\"androidMinSdkVersion\")\n    targetSdkVersion globalConfiguration.getAt(\"androidTargetSdkVersion\")\n\n    applicationId globalConfiguration.getAt(\"androidApplicationId\")\n    versionCode globalConfiguration.getAt(\"androidVersionCode\")\n    versionName globalConfiguration.getAt(\"androidVersionName\")\n    testInstrumentationRunner globalConfiguration.getAt(\"testInstrumentationRunner\")\n    testApplicationId globalConfiguration.getAt(\"testApplicationId\")\n  }\n\n  compileOptions {\n    sourceCompatibility JavaVersion.VERSION_1_7\n    targetCompatibility JavaVersion.VERSION_1_7\n  }\n\n  packagingOptions {\n    exclude 'LICENSE.txt'\n    exclude 'META-INF/DEPENDENCIES'\n    exclude 'META-INF/ASL2.0'\n    exclude 'META-INF/NOTICE'\n    exclude 'META-INF/LICENSE'\n  }\n\n  lintOptions {\n    quiet true\n    abortOnError false\n    ignoreWarnings true\n    disable 'InvalidPackage'            //Some libraries have issues with this.\n    disable 'OldTargetApi'              //Lint gives this warning but SDK 20 would be Android L Beta.\n    disable 'IconDensities'             //For testing purpose. This is safe to remove.\n    disable 'IconMissingDensityFolder'  //For testing purpose. This is safe to remove.\n  }\n\n  signingConfigs {\n    debug {\n      storeFile file('../buildsystem/debug.keystore')\n      storePassword 'android'\n      keyAlias 'androiddebugkey'\n      keyPassword 'android'\n    }\n  }\n\n  buildTypes {\n    debug {\n      signingConfig signingConfigs.debug\n    }\n  }\n}\n\ndependencies {\n  def presentationDependencies = rootProject.ext.presentationDependencies\n  def presentationTestDependencies = rootProject.ext.presentationTestDependencies\n  def developmentDependencies = rootProject.ext.developmentDependencies\n\n  implementation project(':domain')\n  implementation project(':data')\n\n  annotationProcessor presentationDependencies.daggerCompiler\n  implementation presentationDependencies.dagger\n  implementation presentationDependencies.butterKnife\n  annotationProcessor presentationDependencies.butterKnife\n  implementation presentationDependencies.recyclerView\n  implementation presentationDependencies.rxJava\n  implementation presentationDependencies.rxAndroid\n  compileOnly presentationDependencies.javaxAnnotation\n\n  androidTestImplementation presentationTestDependencies.mockito\n  androidTestImplementation presentationTestDependencies.dexmaker\n  androidTestImplementation presentationTestDependencies.dexmakerMockito\n  androidTestImplementation presentationTestDependencies.espresso\n  androidTestImplementation presentationTestDependencies.testingSupportLib\n\n  //Development\n  implementation developmentDependencies.leakCanary\n}\n\nrepositories {\n  google()\n}"
  },
  {
    "path": "presentation/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Users/fcejas/Software/SDKs/android-sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/exception/ErrorMessageFactoryTest.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.test.exception;\n\nimport android.test.AndroidTestCase;\nimport com.fernandocejas.android10.sample.data.exception.NetworkConnectionException;\nimport com.fernandocejas.android10.sample.data.exception.UserNotFoundException;\nimport com.fernandocejas.android10.sample.presentation.R;\nimport com.fernandocejas.android10.sample.presentation.exception.ErrorMessageFactory;\n\nimport static org.hamcrest.CoreMatchers.equalTo;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamcrest.MatcherAssert.assertThat;\n\npublic class ErrorMessageFactoryTest extends AndroidTestCase {\n\n  @Override protected void setUp() throws Exception {\n    super.setUp();\n  }\n\n  public void testNetworkConnectionErrorMessage() {\n    String expectedMessage = getContext().getString(R.string.exception_message_no_connection);\n    String actualMessage = ErrorMessageFactory.create(getContext(),\n        new NetworkConnectionException());\n\n    assertThat(actualMessage, is(equalTo(expectedMessage)));\n  }\n\n  public void testUserNotFoundErrorMessage() {\n    String expectedMessage = getContext().getString(R.string.exception_message_user_not_found);\n    String actualMessage = ErrorMessageFactory.create(getContext(), new UserNotFoundException());\n\n    assertThat(actualMessage, is(equalTo(expectedMessage)));\n  }\n}\n"
  },
  {
    "path": "presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/mapper/UserModelDataMapperTest.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.test.mapper;\n\nimport com.fernandocejas.android10.sample.domain.User;\nimport com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper;\nimport com.fernandocejas.android10.sample.presentation.model.UserModel;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport junit.framework.TestCase;\n\nimport static org.hamcrest.CoreMatchers.instanceOf;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamcrest.MatcherAssert.assertThat;\nimport static org.mockito.Mockito.mock;\n\npublic class UserModelDataMapperTest extends TestCase {\n\n  private static final int FAKE_USER_ID = 123;\n  private static final String FAKE_FULL_NAME = \"Tony Stark\";\n\n  private UserModelDataMapper userModelDataMapper;\n\n  @Override protected void setUp() throws Exception {\n    super.setUp();\n    userModelDataMapper = new UserModelDataMapper();\n  }\n\n  public void testTransformUser() {\n    User user = createFakeUser();\n    UserModel userModel = userModelDataMapper.transform(user);\n\n    assertThat(userModel, is(instanceOf(UserModel.class)));\n    assertThat(userModel.getUserId(), is(FAKE_USER_ID));\n    assertThat(userModel.getFullName(), is(FAKE_FULL_NAME));\n  }\n\n  public void testTransformUserCollection() {\n    User mockUserOne = mock(User.class);\n    User mockUserTwo = mock(User.class);\n\n    List<User> userList = new ArrayList<User>(5);\n    userList.add(mockUserOne);\n    userList.add(mockUserTwo);\n\n    Collection<UserModel> userModelList = userModelDataMapper.transform(userList);\n\n    assertThat(userModelList.toArray()[0], is(instanceOf(UserModel.class)));\n    assertThat(userModelList.toArray()[1], is(instanceOf(UserModel.class)));\n    assertThat(userModelList.size(), is(2));\n  }\n\n  private User createFakeUser() {\n    User user = new User(FAKE_USER_ID);\n    user.setFullName(FAKE_FULL_NAME);\n\n    return user;\n  }\n}\n"
  },
  {
    "path": "presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/presenter/UserDetailsPresenterTest.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.test.presenter;\n\nimport android.content.Context;\nimport com.fernandocejas.android10.sample.domain.interactor.GetUserDetails;\nimport com.fernandocejas.android10.sample.domain.interactor.GetUserDetails.Params;\nimport com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper;\nimport com.fernandocejas.android10.sample.presentation.presenter.UserDetailsPresenter;\nimport com.fernandocejas.android10.sample.presentation.view.UserDetailsView;\nimport io.reactivex.observers.DisposableObserver;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.runners.MockitoJUnitRunner;\n\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Matchers.any;\nimport static org.mockito.Mockito.verify;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class UserDetailsPresenterTest {\n\n  private static final int USER_ID = 1;\n\n  private UserDetailsPresenter userDetailsPresenter;\n\n  @Mock private Context mockContext;\n  @Mock private UserDetailsView mockUserDetailsView;\n  @Mock private GetUserDetails mockGetUserDetails;\n  @Mock private UserModelDataMapper mockUserModelDataMapper;\n\n  @Before\n  public void setUp() {\n    userDetailsPresenter = new UserDetailsPresenter(mockGetUserDetails, mockUserModelDataMapper);\n    userDetailsPresenter.setView(mockUserDetailsView);\n  }\n\n  @Test\n  @SuppressWarnings(\"unchecked\")\n  public void testUserDetailsPresenterInitialize() {\n    given(mockUserDetailsView.context()).willReturn(mockContext);\n\n    userDetailsPresenter.initialize(USER_ID);\n\n    verify(mockUserDetailsView).hideRetry();\n    verify(mockUserDetailsView).showLoading();\n    verify(mockGetUserDetails).execute(any(DisposableObserver.class), any(Params.class));\n  }\n}\n"
  },
  {
    "path": "presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/presenter/UserListPresenterTest.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.test.presenter;\n\nimport android.content.Context;\nimport com.fernandocejas.android10.sample.domain.interactor.GetUserList;\nimport com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper;\nimport com.fernandocejas.android10.sample.presentation.presenter.UserListPresenter;\nimport com.fernandocejas.android10.sample.presentation.view.UserListView;\nimport io.reactivex.observers.DisposableObserver;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.runners.MockitoJUnitRunner;\n\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Matchers.any;\nimport static org.mockito.Mockito.verify;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class UserListPresenterTest {\n\n  private UserListPresenter userListPresenter;\n\n  @Mock private Context mockContext;\n  @Mock private UserListView mockUserListView;\n  @Mock private GetUserList mockGetUserList;\n  @Mock private UserModelDataMapper mockUserModelDataMapper;\n\n  @Before\n  public void setUp() {\n    userListPresenter = new UserListPresenter(mockGetUserList, mockUserModelDataMapper);\n    userListPresenter.setView(mockUserListView);\n  }\n\n  @Test\n  @SuppressWarnings(\"unchecked\")\n  public void testUserListPresenterInitialize() {\n    given(mockUserListView.context()).willReturn(mockContext);\n\n    userListPresenter.initialize();\n\n    verify(mockUserListView).hideRetry();\n    verify(mockUserListView).showLoading();\n    verify(mockGetUserList).execute(any(DisposableObserver.class), any(Void.class));\n  }\n}\n"
  },
  {
    "path": "presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/view/activity/UserDetailsActivityTest.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.test.view.activity;\n\nimport android.app.Fragment;\nimport android.content.Intent;\nimport android.test.ActivityInstrumentationTestCase2;\nimport com.fernandocejas.android10.sample.presentation.R;\nimport com.fernandocejas.android10.sample.presentation.view.activity.UserDetailsActivity;\n\nimport static android.support.test.espresso.Espresso.onView;\nimport static android.support.test.espresso.assertion.ViewAssertions.matches;\nimport static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;\nimport static android.support.test.espresso.matcher.ViewMatchers.withId;\nimport static android.support.test.espresso.matcher.ViewMatchers.withText;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamcrest.CoreMatchers.notNullValue;\nimport static org.hamcrest.MatcherAssert.assertThat;\nimport static org.hamcrest.Matchers.not;\n\npublic class UserDetailsActivityTest extends ActivityInstrumentationTestCase2<UserDetailsActivity> {\n\n  private static final int FAKE_USER_ID = 10;\n\n  private UserDetailsActivity userDetailsActivity;\n\n  public UserDetailsActivityTest() {\n    super(UserDetailsActivity.class);\n  }\n\n  @Override protected void setUp() throws Exception {\n    super.setUp();\n    this.setActivityIntent(createTargetIntent());\n    this.userDetailsActivity = getActivity();\n  }\n\n  @Override protected void tearDown() throws Exception {\n    super.tearDown();\n  }\n\n  public void testContainsUserDetailsFragment() {\n    Fragment userDetailsFragment =\n        userDetailsActivity.getFragmentManager().findFragmentById(R.id.fragmentContainer);\n    assertThat(userDetailsFragment, is(notNullValue()));\n  }\n\n  public void testContainsProperTitle() {\n    String actualTitle = this.userDetailsActivity.getTitle().toString().trim();\n\n    assertThat(actualTitle, is(\"User Details\"));\n  }\n\n  public void testLoadUserHappyCaseViews() {\n    onView(withId(R.id.rl_retry)).check(matches(not(isDisplayed())));\n    onView(withId(R.id.rl_progress)).check(matches(not(isDisplayed())));\n\n    onView(withId(R.id.tv_fullname)).check(matches(isDisplayed()));\n    onView(withId(R.id.tv_email)).check(matches(isDisplayed()));\n    onView(withId(R.id.tv_description)).check(matches(isDisplayed()));\n  }\n\n  public void testLoadUserHappyCaseData() {\n    onView(withId(R.id.tv_fullname)).check(matches(withText(\"John Sanchez\")));\n    onView(withId(R.id.tv_email)).check(matches(withText(\"dmedina@katz.edu\")));\n    onView(withId(R.id.tv_followers)).check(matches(withText(\"4523\")));\n  }\n\n  private Intent createTargetIntent() {\n    Intent intentLaunchActivity =\n        UserDetailsActivity.getCallingIntent(getInstrumentation().getTargetContext(), FAKE_USER_ID);\n\n    return intentLaunchActivity;\n  }\n}\n"
  },
  {
    "path": "presentation/src/androidTest/java/com/fernandocejas/android10/sample/test/view/activity/UserListActivityTest.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.test.view.activity;\n\nimport android.app.Fragment;\nimport android.content.Intent;\nimport android.test.ActivityInstrumentationTestCase2;\nimport com.fernandocejas.android10.sample.presentation.R;\nimport com.fernandocejas.android10.sample.presentation.view.activity.UserListActivity;\n\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamcrest.CoreMatchers.notNullValue;\nimport static org.hamcrest.MatcherAssert.assertThat;\n\npublic class UserListActivityTest extends ActivityInstrumentationTestCase2<UserListActivity> {\n\n  private UserListActivity userListActivity;\n\n  public UserListActivityTest() {\n    super(UserListActivity.class);\n  }\n\n  @Override protected void setUp() throws Exception {\n    super.setUp();\n    this.setActivityIntent(createTargetIntent());\n    userListActivity = getActivity();\n  }\n\n  @Override protected void tearDown() throws Exception {\n    super.tearDown();\n  }\n\n  public void testContainsUserListFragment() {\n    Fragment userListFragment =\n        userListActivity.getFragmentManager().findFragmentById(R.id.fragmentContainer);\n    assertThat(userListFragment, is(notNullValue()));\n  }\n\n  public void testContainsProperTitle() {\n    String actualTitle = this.userListActivity.getTitle().toString().trim();\n\n    assertThat(actualTitle, is(\"Users List\"));\n  }\n\n  private Intent createTargetIntent() {\n    Intent intentLaunchActivity =\n        UserListActivity.getCallingIntent(getInstrumentation().getTargetContext());\n\n    return intentLaunchActivity;\n  }\n}\n"
  },
  {
    "path": "presentation/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.fernandocejas.android10.sample.presentation\">\n\n  <uses-permission android:name=\"android.permission.INTERNET\" />\n  <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n\n  <application\n      android:allowBackup=\"true\"\n      android:name=\".AndroidApplication\"\n      android:icon=\"@drawable/ic_launcher\"\n      android:label=\"@string/app_name\"\n      android:theme=\"@style/AppTheme\">\n\n    <activity\n        android:name=\".view.activity.MainActivity\"\n        android:label=\"@string/app_name\">\n      <intent-filter>\n        <action android:name=\"android.intent.action.MAIN\" />\n        <category android:name=\"android.intent.category.LAUNCHER\" />\n      </intent-filter>\n    </activity>\n\n    <activity\n        android:name=\".view.activity.UserListActivity\"\n        android:label=\"@string/activity_title_user_list\">\n    </activity>\n\n    <activity\n        android:name=\".view.activity.UserDetailsActivity\"\n        android:label=\"@string/activity_title_user_details\">\n    </activity>\n  </application>\n\n</manifest>\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/AndroidApplication.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.presentation;\n\nimport android.app.Application;\nimport com.fernandocejas.android10.sample.presentation.internal.di.components.ApplicationComponent;\nimport com.fernandocejas.android10.sample.presentation.internal.di.components.DaggerApplicationComponent;\nimport com.fernandocejas.android10.sample.presentation.internal.di.modules.ApplicationModule;\nimport com.squareup.leakcanary.LeakCanary;\n\n/**\n * Android Main Application\n */\npublic class AndroidApplication extends Application {\n\n  private ApplicationComponent applicationComponent;\n\n  @Override public void onCreate() {\n    super.onCreate();\n    this.initializeInjector();\n    this.initializeLeakDetection();\n  }\n\n  private void initializeInjector() {\n    this.applicationComponent = DaggerApplicationComponent.builder()\n        .applicationModule(new ApplicationModule(this))\n        .build();\n  }\n\n  public ApplicationComponent getApplicationComponent() {\n    return this.applicationComponent;\n  }\n\n  private void initializeLeakDetection() {\n    if (BuildConfig.DEBUG) {\n      LeakCanary.install(this);\n    }\n  }\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/UIThread.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.presentation;\n\nimport com.fernandocejas.android10.sample.domain.executor.PostExecutionThread;\nimport io.reactivex.Scheduler;\nimport io.reactivex.android.schedulers.AndroidSchedulers;\nimport javax.inject.Inject;\nimport javax.inject.Singleton;\n\n/**\n * MainThread (UI Thread) implementation based on a {@link Scheduler}\n * which will execute actions on the Android UI thread\n */\n@Singleton\npublic class UIThread implements PostExecutionThread {\n\n  @Inject\n  UIThread() {}\n\n  @Override public Scheduler getScheduler() {\n    return AndroidSchedulers.mainThread();\n  }\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/exception/ErrorMessageFactory.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.presentation.exception;\n\nimport android.content.Context;\nimport com.fernandocejas.android10.sample.data.exception.NetworkConnectionException;\nimport com.fernandocejas.android10.sample.data.exception.UserNotFoundException;\nimport com.fernandocejas.android10.sample.presentation.R;\n\n/**\n * Factory used to create error messages from an Exception as a condition.\n */\npublic class ErrorMessageFactory {\n\n  private ErrorMessageFactory() {\n    //empty\n  }\n\n  /**\n   * Creates a String representing an error message.\n   *\n   * @param context Context needed to retrieve string resources.\n   * @param exception An exception used as a condition to retrieve the correct error message.\n   * @return {@link String} an error message.\n   */\n  public static String create(Context context, Exception exception) {\n    String message = context.getString(R.string.exception_message_generic);\n\n    if (exception instanceof NetworkConnectionException) {\n      message = context.getString(R.string.exception_message_no_connection);\n    } else if (exception instanceof UserNotFoundException) {\n      message = context.getString(R.string.exception_message_user_not_found);\n    }\n\n    return message;\n  }\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/HasComponent.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.presentation.internal.di;\n\n/**\n * Interface representing a contract for clients that contains a component for dependency injection.\n */\npublic interface HasComponent<C> {\n  C getComponent();\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/PerActivity.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.presentation.internal.di;\n\nimport java.lang.annotation.Retention;\nimport javax.inject.Scope;\n\nimport static java.lang.annotation.RetentionPolicy.RUNTIME;\n\n/**\n * A scoping annotation to permit objects whose lifetime should\n * conform to the life of the activity to be memorized in the\n * correct component.\n */\n@Scope\n@Retention(RUNTIME)\npublic @interface PerActivity {}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/components/ActivityComponent.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.presentation.internal.di.components;\n\nimport android.app.Activity;\nimport com.fernandocejas.android10.sample.presentation.internal.di.PerActivity;\nimport com.fernandocejas.android10.sample.presentation.internal.di.modules.ActivityModule;\nimport dagger.Component;\n\n/**\n * A base component upon which fragment's components may depend.\n * Activity-level components should extend this component.\n *\n * Subtypes of ActivityComponent should be decorated with annotation:\n * {@link com.fernandocejas.android10.sample.presentation.internal.di.PerActivity}\n */\n@PerActivity\n@Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class)\ninterface ActivityComponent {\n  //Exposed to sub-graphs.\n  Activity activity();\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/components/ApplicationComponent.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.presentation.internal.di.components;\n\nimport android.content.Context;\nimport com.fernandocejas.android10.sample.domain.executor.PostExecutionThread;\nimport com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;\nimport com.fernandocejas.android10.sample.domain.repository.UserRepository;\nimport com.fernandocejas.android10.sample.presentation.internal.di.modules.ApplicationModule;\nimport com.fernandocejas.android10.sample.presentation.view.activity.BaseActivity;\nimport dagger.Component;\nimport javax.inject.Singleton;\n\n/**\n * A component whose lifetime is the life of the application.\n */\n@Singleton // Constraints this component to one-per-application or unscoped bindings.\n@Component(modules = ApplicationModule.class)\npublic interface ApplicationComponent {\n  void inject(BaseActivity baseActivity);\n\n  //Exposed to sub-graphs.\n  Context context();\n  ThreadExecutor threadExecutor();\n  PostExecutionThread postExecutionThread();\n  UserRepository userRepository();\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/components/UserComponent.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.presentation.internal.di.components;\n\nimport com.fernandocejas.android10.sample.presentation.internal.di.PerActivity;\nimport com.fernandocejas.android10.sample.presentation.internal.di.modules.ActivityModule;\nimport com.fernandocejas.android10.sample.presentation.internal.di.modules.UserModule;\nimport com.fernandocejas.android10.sample.presentation.view.fragment.UserDetailsFragment;\nimport com.fernandocejas.android10.sample.presentation.view.fragment.UserListFragment;\nimport dagger.Component;\n\n/**\n * A scope {@link com.fernandocejas.android10.sample.presentation.internal.di.PerActivity} component.\n * Injects user specific Fragments.\n */\n@PerActivity\n@Component(dependencies = ApplicationComponent.class, modules = {ActivityModule.class, UserModule.class})\npublic interface UserComponent extends ActivityComponent {\n  void inject(UserListFragment userListFragment);\n  void inject(UserDetailsFragment userDetailsFragment);\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/modules/ActivityModule.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.presentation.internal.di.modules;\n\nimport android.app.Activity;\nimport com.fernandocejas.android10.sample.presentation.internal.di.PerActivity;\nimport dagger.Module;\nimport dagger.Provides;\n\n/**\n * A module to wrap the Activity state and expose it to the graph.\n */\n@Module\npublic class ActivityModule {\n  private final Activity activity;\n\n  public ActivityModule(Activity activity) {\n    this.activity = activity;\n  }\n\n  /**\n  * Expose the activity to dependents in the graph.\n  */\n  @Provides @PerActivity Activity activity() {\n    return this.activity;\n  }\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/modules/ApplicationModule.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.presentation.internal.di.modules;\n\nimport android.content.Context;\nimport com.fernandocejas.android10.sample.data.cache.UserCache;\nimport com.fernandocejas.android10.sample.data.cache.UserCacheImpl;\nimport com.fernandocejas.android10.sample.data.executor.JobExecutor;\nimport com.fernandocejas.android10.sample.data.repository.UserDataRepository;\nimport com.fernandocejas.android10.sample.domain.executor.PostExecutionThread;\nimport com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;\nimport com.fernandocejas.android10.sample.domain.repository.UserRepository;\nimport com.fernandocejas.android10.sample.presentation.AndroidApplication;\nimport com.fernandocejas.android10.sample.presentation.UIThread;\nimport com.fernandocejas.android10.sample.presentation.navigation.Navigator;\nimport dagger.Module;\nimport dagger.Provides;\nimport javax.inject.Singleton;\n\n/**\n * Dagger module that provides objects which will live during the application lifecycle.\n */\n@Module\npublic class ApplicationModule {\n  private final AndroidApplication application;\n\n  public ApplicationModule(AndroidApplication application) {\n    this.application = application;\n  }\n\n  @Provides @Singleton Context provideApplicationContext() {\n    return this.application;\n  }\n\n  @Provides @Singleton ThreadExecutor provideThreadExecutor(JobExecutor jobExecutor) {\n    return jobExecutor;\n  }\n\n  @Provides @Singleton PostExecutionThread providePostExecutionThread(UIThread uiThread) {\n    return uiThread;\n  }\n\n  @Provides @Singleton UserCache provideUserCache(UserCacheImpl userCache) {\n    return userCache;\n  }\n\n  @Provides @Singleton UserRepository provideUserRepository(UserDataRepository userDataRepository) {\n    return userDataRepository;\n  }\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/internal/di/modules/UserModule.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.presentation.internal.di.modules;\n\nimport dagger.Module;\n\n/**\n * Dagger module that provides user related collaborators.\n */\n@Module\npublic class UserModule {\n\n  public UserModule() {}\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/mapper/UserModelDataMapper.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.presentation.mapper;\n\nimport com.fernandocejas.android10.sample.domain.User;\nimport com.fernandocejas.android10.sample.presentation.internal.di.PerActivity;\nimport com.fernandocejas.android10.sample.presentation.model.UserModel;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport javax.inject.Inject;\n\n/**\n * Mapper class used to transform {@link User} (in the domain layer) to {@link UserModel} in the\n * presentation layer.\n */\n@PerActivity\npublic class UserModelDataMapper {\n\n  @Inject\n  public UserModelDataMapper() {}\n\n  /**\n   * Transform a {@link User} into an {@link UserModel}.\n   *\n   * @param user Object to be transformed.\n   * @return {@link UserModel}.\n   */\n  public UserModel transform(User user) {\n    if (user == null) {\n      throw new IllegalArgumentException(\"Cannot transform a null value\");\n    }\n    final UserModel userModel = new UserModel(user.getUserId());\n    userModel.setCoverUrl(user.getCoverUrl());\n    userModel.setFullName(user.getFullName());\n    userModel.setEmail(user.getEmail());\n    userModel.setDescription(user.getDescription());\n    userModel.setFollowers(user.getFollowers());\n\n    return userModel;\n  }\n\n  /**\n   * Transform a Collection of {@link User} into a Collection of {@link UserModel}.\n   *\n   * @param usersCollection Objects to be transformed.\n   * @return List of {@link UserModel}.\n   */\n  public Collection<UserModel> transform(Collection<User> usersCollection) {\n    Collection<UserModel> userModelsCollection;\n\n    if (usersCollection != null && !usersCollection.isEmpty()) {\n      userModelsCollection = new ArrayList<>();\n      for (User user : usersCollection) {\n        userModelsCollection.add(transform(user));\n      }\n    } else {\n      userModelsCollection = Collections.emptyList();\n    }\n\n    return userModelsCollection;\n  }\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/model/UserModel.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.presentation.model;\n\n/**\n * Class that represents a user in the presentation layer.\n */\npublic class UserModel {\n\n  private final int userId;\n\n  public UserModel(int userId) {\n    this.userId = userId;\n  }\n\n  private String coverUrl;\n  private String fullName;\n  private String email;\n  private String description;\n  private int followers;\n\n  public int getUserId() {\n    return userId;\n  }\n\n  public String getCoverUrl() {\n    return coverUrl;\n  }\n\n  public void setCoverUrl(String coverUrl) {\n    this.coverUrl = coverUrl;\n  }\n\n  public String getFullName() {\n    return fullName;\n  }\n\n  public void setFullName(String fullName) {\n    this.fullName = fullName;\n  }\n\n  public String getEmail() {\n    return email;\n  }\n\n  public void setEmail(String email) {\n    this.email = email;\n  }\n\n  public String getDescription() {\n    return description;\n  }\n\n  public void setDescription(String description) {\n    this.description = description;\n  }\n\n  public int getFollowers() {\n    return followers;\n  }\n\n  public void setFollowers(int followers) {\n    this.followers = followers;\n  }\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/navigation/Navigator.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.presentation.navigation;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport com.fernandocejas.android10.sample.presentation.view.activity.UserDetailsActivity;\nimport com.fernandocejas.android10.sample.presentation.view.activity.UserListActivity;\nimport javax.inject.Inject;\nimport javax.inject.Singleton;\n\n/**\n * Class used to navigate through the application.\n */\n@Singleton\npublic class Navigator {\n\n  @Inject\n  public Navigator() {\n    //empty\n  }\n\n  /**\n   * Goes to the user list screen.\n   *\n   * @param context A Context needed to open the destiny activity.\n   */\n  public void navigateToUserList(Context context) {\n    if (context != null) {\n      Intent intentToLaunch = UserListActivity.getCallingIntent(context);\n      context.startActivity(intentToLaunch);\n    }\n  }\n\n  /**\n   * Goes to the user details screen.\n   *\n   * @param context A Context needed to open the destiny activity.\n   */\n  public void navigateToUserDetails(Context context, int userId) {\n    if (context != null) {\n      Intent intentToLaunch = UserDetailsActivity.getCallingIntent(context, userId);\n      context.startActivity(intentToLaunch);\n    }\n  }\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/presenter/Presenter.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.presentation.presenter;\n\n/**\n * Interface representing a Presenter in a model view presenter (MVP) pattern.\n */\npublic interface Presenter {\n  /**\n   * Method that control the lifecycle of the view. It should be called in the view's\n   * (Activity or Fragment) onResume() method.\n   */\n  void resume();\n\n  /**\n   * Method that control the lifecycle of the view. It should be called in the view's\n   * (Activity or Fragment) onPause() method.\n   */\n  void pause();\n\n  /**\n   * Method that control the lifecycle of the view. It should be called in the view's\n   * (Activity or Fragment) onDestroy() method.\n   */\n  void destroy();\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/presenter/UserDetailsPresenter.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.presentation.presenter;\n\nimport android.support.annotation.NonNull;\nimport com.fernandocejas.android10.sample.domain.User;\nimport com.fernandocejas.android10.sample.domain.exception.DefaultErrorBundle;\nimport com.fernandocejas.android10.sample.domain.exception.ErrorBundle;\nimport com.fernandocejas.android10.sample.domain.interactor.DefaultObserver;\nimport com.fernandocejas.android10.sample.domain.interactor.GetUserDetails;\nimport com.fernandocejas.android10.sample.domain.interactor.GetUserDetails.Params;\nimport com.fernandocejas.android10.sample.presentation.exception.ErrorMessageFactory;\nimport com.fernandocejas.android10.sample.presentation.internal.di.PerActivity;\nimport com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper;\nimport com.fernandocejas.android10.sample.presentation.model.UserModel;\nimport com.fernandocejas.android10.sample.presentation.view.UserDetailsView;\nimport javax.inject.Inject;\n\n/**\n * {@link Presenter} that controls communication between views and models of the presentation\n * layer.\n */\n@PerActivity\npublic class UserDetailsPresenter implements Presenter {\n\n  private UserDetailsView viewDetailsView;\n\n  private final GetUserDetails getUserDetailsUseCase;\n  private final UserModelDataMapper userModelDataMapper;\n\n  @Inject\n  public UserDetailsPresenter(GetUserDetails getUserDetailsUseCase,\n      UserModelDataMapper userModelDataMapper) {\n    this.getUserDetailsUseCase = getUserDetailsUseCase;\n    this.userModelDataMapper = userModelDataMapper;\n  }\n\n  public void setView(@NonNull UserDetailsView view) {\n    this.viewDetailsView = view;\n  }\n\n  @Override public void resume() {}\n\n  @Override public void pause() {}\n\n  @Override public void destroy() {\n    this.getUserDetailsUseCase.dispose();\n    this.viewDetailsView = null;\n  }\n\n  /**\n   * Initializes the presenter by showing/hiding proper views\n   * and retrieving user details.\n   */\n  public void initialize(int userId) {\n    this.hideViewRetry();\n    this.showViewLoading();\n    this.getUserDetails(userId);\n  }\n\n  private void getUserDetails(int userId) {\n    this.getUserDetailsUseCase.execute(new UserDetailsObserver(), Params.forUser(userId));\n  }\n\n  private void showViewLoading() {\n    this.viewDetailsView.showLoading();\n  }\n\n  private void hideViewLoading() {\n    this.viewDetailsView.hideLoading();\n  }\n\n  private void showViewRetry() {\n    this.viewDetailsView.showRetry();\n  }\n\n  private void hideViewRetry() {\n    this.viewDetailsView.hideRetry();\n  }\n\n  private void showErrorMessage(ErrorBundle errorBundle) {\n    String errorMessage = ErrorMessageFactory.create(this.viewDetailsView.context(),\n        errorBundle.getException());\n    this.viewDetailsView.showError(errorMessage);\n  }\n\n  private void showUserDetailsInView(User user) {\n    final UserModel userModel = this.userModelDataMapper.transform(user);\n    this.viewDetailsView.renderUser(userModel);\n  }\n\n  private final class UserDetailsObserver extends DefaultObserver<User> {\n\n    @Override public void onComplete() {\n      UserDetailsPresenter.this.hideViewLoading();\n    }\n\n    @Override public void onError(Throwable e) {\n      UserDetailsPresenter.this.hideViewLoading();\n      UserDetailsPresenter.this.showErrorMessage(new DefaultErrorBundle((Exception) e));\n      UserDetailsPresenter.this.showViewRetry();\n    }\n\n    @Override public void onNext(User user) {\n      UserDetailsPresenter.this.showUserDetailsInView(user);\n    }\n  }\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/presenter/UserListPresenter.java",
    "content": "/**\n * Copyright (C) 2015 Fernando Cejas Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.fernandocejas.android10.sample.presentation.presenter;\n\nimport android.support.annotation.NonNull;\nimport com.fernandocejas.android10.sample.domain.User;\nimport com.fernandocejas.android10.sample.domain.exception.DefaultErrorBundle;\nimport com.fernandocejas.android10.sample.domain.exception.ErrorBundle;\nimport com.fernandocejas.android10.sample.domain.interactor.DefaultObserver;\nimport com.fernandocejas.android10.sample.domain.interactor.GetUserList;\nimport com.fernandocejas.android10.sample.presentation.exception.ErrorMessageFactory;\nimport com.fernandocejas.android10.sample.presentation.internal.di.PerActivity;\nimport com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper;\nimport com.fernandocejas.android10.sample.presentation.model.UserModel;\nimport com.fernandocejas.android10.sample.presentation.view.UserListView;\nimport java.util.Collection;\nimport java.util.List;\nimport javax.inject.Inject;\n\n/**\n * {@link Presenter} that controls communication between views and models of the presentation\n * layer.\n */\n@PerActivity\npublic class UserListPresenter implements Presenter {\n\n  private UserListView viewListView;\n\n  private final GetUserList getUserListUseCase;\n  private final UserModelDataMapper userModelDataMapper;\n\n  @Inject\n  public UserListPresenter(GetUserList getUserListUserCase,\n      UserModelDataMapper userModelDataMapper) {\n    this.getUserListUseCase = getUserListUserCase;\n    this.userModelDataMapper = userModelDataMapper;\n  }\n\n  public void setView(@NonNull UserListView view) {\n    this.viewListView = view;\n  }\n\n  @Override public void resume() {}\n\n  @Override public void pause() {}\n\n  @Override public void destroy() {\n    this.getUserListUseCase.dispose();\n    this.viewListView = null;\n  }\n\n  /**\n   * Initializes the presenter by start retrieving the user list.\n   */\n  public void initialize() {\n    this.loadUserList();\n  }\n\n  /**\n   * Loads all users.\n   */\n  private void loadUserList() {\n    this.hideViewRetry();\n    this.showViewLoading();\n    this.getUserList();\n  }\n\n  public void onUserClicked(UserModel userModel) {\n    this.viewListView.viewUser(userModel);\n  }\n\n  private void showViewLoading() {\n    this.viewListView.showLoading();\n  }\n\n  private void hideViewLoading() {\n    this.viewListView.hideLoading();\n  }\n\n  private void showViewRetry() {\n    this.viewListView.showRetry();\n  }\n\n  private void hideViewRetry() {\n    this.viewListView.hideRetry();\n  }\n\n  private void showErrorMessage(ErrorBundle errorBundle) {\n    String errorMessage = ErrorMessageFactory.create(this.viewListView.context(),\n        errorBundle.getException());\n    this.viewListView.showError(errorMessage);\n  }\n\n  private void showUsersCollectionInView(Collection<User> usersCollection) {\n    final Collection<UserModel> userModelsCollection =\n        this.userModelDataMapper.transform(usersCollection);\n    this.viewListView.renderUserList(userModelsCollection);\n  }\n\n  private void getUserList() {\n    this.getUserListUseCase.execute(new UserListObserver(), null);\n  }\n\n  private final class UserListObserver extends DefaultObserver<List<User>> {\n\n    @Override public void onComplete() {\n      UserListPresenter.this.hideViewLoading();\n    }\n\n    @Override public void onError(Throwable e) {\n      UserListPresenter.this.hideViewLoading();\n      UserListPresenter.this.showErrorMessage(new DefaultErrorBundle((Exception) e));\n      UserListPresenter.this.showViewRetry();\n    }\n\n    @Override public void onNext(List<User> users) {\n      UserListPresenter.this.showUsersCollectionInView(users);\n    }\n  }\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/LoadDataView.java",
    "content": "/**\n * Copyright (C) 2014 android10.org. All rights reserved.\n * @author Fernando Cejas (the android10 coder)\n */\npackage com.fernandocejas.android10.sample.presentation.view;\n\nimport android.content.Context;\n\n/**\n * Interface representing a View that will use to load data.\n */\npublic interface LoadDataView {\n  /**\n   * Show a view with a progress bar indicating a loading process.\n   */\n  void showLoading();\n\n  /**\n   * Hide a loading view.\n   */\n  void hideLoading();\n\n  /**\n   * Show a retry view in case of an error when retrieving data.\n   */\n  void showRetry();\n\n  /**\n   * Hide a retry view shown if there was an error when retrieving data.\n   */\n  void hideRetry();\n\n  /**\n   * Show an error message\n   *\n   * @param message A string representing an error.\n   */\n  void showError(String message);\n\n  /**\n   * Get a {@link android.content.Context}.\n   */\n  Context context();\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/UserDetailsView.java",
    "content": "/**\n * Copyright (C) 2014 android10.org. All rights reserved.\n * @author Fernando Cejas (the android10 coder)\n */\npackage com.fernandocejas.android10.sample.presentation.view;\n\nimport com.fernandocejas.android10.sample.presentation.model.UserModel;\n\n/**\n * Interface representing a View in a model view presenter (MVP) pattern.\n * In this case is used as a view representing a user profile.\n */\npublic interface UserDetailsView extends LoadDataView {\n  /**\n   * Render a user in the UI.\n   *\n   * @param user The {@link UserModel} that will be shown.\n   */\n  void renderUser(UserModel user);\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/UserListView.java",
    "content": "/**\n * Copyright (C) 2014 android10.org. All rights reserved.\n * @author Fernando Cejas (the android10 coder)\n */\npackage com.fernandocejas.android10.sample.presentation.view;\n\nimport com.fernandocejas.android10.sample.presentation.model.UserModel;\nimport java.util.Collection;\n\n/**\n * Interface representing a View in a model view presenter (MVP) pattern.\n * In this case is used as a view representing a list of {@link UserModel}.\n */\npublic interface UserListView extends LoadDataView {\n  /**\n   * Render a user list in the UI.\n   *\n   * @param userModelCollection The collection of {@link UserModel} that will be shown.\n   */\n  void renderUserList(Collection<UserModel> userModelCollection);\n\n  /**\n   * View a {@link UserModel} profile/details.\n   *\n   * @param userModel The user that will be shown.\n   */\n  void viewUser(UserModel userModel);\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/BaseActivity.java",
    "content": "package com.fernandocejas.android10.sample.presentation.view.activity;\n\nimport android.app.Activity;\nimport android.app.Fragment;\nimport android.app.FragmentTransaction;\nimport android.os.Bundle;\nimport com.fernandocejas.android10.sample.presentation.AndroidApplication;\nimport com.fernandocejas.android10.sample.presentation.internal.di.components.ApplicationComponent;\nimport com.fernandocejas.android10.sample.presentation.internal.di.modules.ActivityModule;\nimport com.fernandocejas.android10.sample.presentation.navigation.Navigator;\nimport javax.inject.Inject;\n\n/**\n * Base {@link android.app.Activity} class for every Activity in this application.\n */\npublic abstract class BaseActivity extends Activity {\n\n  @Inject Navigator navigator;\n\n  @Override\n  protected void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    this.getApplicationComponent().inject(this);\n  }\n\n  /**\n   * Adds a {@link Fragment} to this activity's layout.\n   *\n   * @param containerViewId The container view to where add the fragment.\n   * @param fragment The fragment to be added.\n   */\n  protected void addFragment(int containerViewId, Fragment fragment) {\n    final FragmentTransaction fragmentTransaction = this.getFragmentManager().beginTransaction();\n    fragmentTransaction.add(containerViewId, fragment);\n    fragmentTransaction.commit();\n  }\n\n  /**\n   * Get the Main Application component for dependency injection.\n   *\n   * @return {@link com.fernandocejas.android10.sample.presentation.internal.di.components.ApplicationComponent}\n   */\n  protected ApplicationComponent getApplicationComponent() {\n    return ((AndroidApplication) getApplication()).getApplicationComponent();\n  }\n\n  /**\n   * Get an Activity module for dependency injection.\n   *\n   * @return {@link com.fernandocejas.android10.sample.presentation.internal.di.modules.ActivityModule}\n   */\n  protected ActivityModule getActivityModule() {\n    return new ActivityModule(this);\n  }\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/MainActivity.java",
    "content": "package com.fernandocejas.android10.sample.presentation.view.activity;\n\nimport android.os.Bundle;\nimport android.widget.Button;\nimport butterknife.Bind;\nimport butterknife.ButterKnife;\nimport butterknife.OnClick;\nimport com.fernandocejas.android10.sample.presentation.R;\n\n/**\n * Main application screen. This is the app entry point.\n */\npublic class MainActivity extends BaseActivity {\n\n  @Bind(R.id.btn_LoadData) Button btn_LoadData;\n\n  @Override\n  protected void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    setContentView(R.layout.activity_main);\n    ButterKnife.bind(this);\n  }\n\n  /**\n   * Goes to the user list screen.\n   */\n  @OnClick(R.id.btn_LoadData)\n  void navigateToUserList() {\n    this.navigator.navigateToUserList(this);\n  }\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/UserDetailsActivity.java",
    "content": "/**\n * Copyright (C) 2014 android10.org. All rights reserved.\n *\n * @author Fernando Cejas (the android10 coder)\n */\npackage com.fernandocejas.android10.sample.presentation.view.activity;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.view.Window;\nimport com.fernandocejas.android10.sample.presentation.R;\nimport com.fernandocejas.android10.sample.presentation.internal.di.HasComponent;\nimport com.fernandocejas.android10.sample.presentation.internal.di.components.DaggerUserComponent;\nimport com.fernandocejas.android10.sample.presentation.internal.di.components.UserComponent;\nimport com.fernandocejas.android10.sample.presentation.view.fragment.UserDetailsFragment;\n\n/**\n * Activity that shows details of a certain user.\n */\npublic class UserDetailsActivity extends BaseActivity implements HasComponent<UserComponent> {\n\n  private static final String INTENT_EXTRA_PARAM_USER_ID = \"org.android10.INTENT_PARAM_USER_ID\";\n  private static final String INSTANCE_STATE_PARAM_USER_ID = \"org.android10.STATE_PARAM_USER_ID\";\n\n  public static Intent getCallingIntent(Context context, int userId) {\n    Intent callingIntent = new Intent(context, UserDetailsActivity.class);\n    callingIntent.putExtra(INTENT_EXTRA_PARAM_USER_ID, userId);\n    return callingIntent;\n  }\n\n  private int userId;\n  private UserComponent userComponent;\n\n  @Override protected void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);\n    setContentView(R.layout.activity_layout);\n\n    this.initializeActivity(savedInstanceState);\n    this.initializeInjector();\n  }\n\n  @Override protected void onSaveInstanceState(Bundle outState) {\n    if (outState != null) {\n      outState.putInt(INSTANCE_STATE_PARAM_USER_ID, this.userId);\n    }\n    super.onSaveInstanceState(outState);\n  }\n\n  /**\n   * Initializes this activity.\n   */\n  private void initializeActivity(Bundle savedInstanceState) {\n    if (savedInstanceState == null) {\n      this.userId = getIntent().getIntExtra(INTENT_EXTRA_PARAM_USER_ID, -1);\n      addFragment(R.id.fragmentContainer, UserDetailsFragment.forUser(userId));\n    } else {\n      this.userId = savedInstanceState.getInt(INSTANCE_STATE_PARAM_USER_ID);\n    }\n  }\n\n  private void initializeInjector() {\n    this.userComponent = DaggerUserComponent.builder()\n        .applicationComponent(getApplicationComponent())\n        .activityModule(getActivityModule())\n        .build();\n  }\n\n  @Override public UserComponent getComponent() {\n    return userComponent;\n  }\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/activity/UserListActivity.java",
    "content": "/**\n * Copyright (C) 2014 android10.org. All rights reserved.\n *\n * @author Fernando Cejas (the android10 coder)\n */\npackage com.fernandocejas.android10.sample.presentation.view.activity;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.view.Window;\nimport com.fernandocejas.android10.sample.presentation.R;\nimport com.fernandocejas.android10.sample.presentation.internal.di.HasComponent;\nimport com.fernandocejas.android10.sample.presentation.internal.di.components.DaggerUserComponent;\nimport com.fernandocejas.android10.sample.presentation.internal.di.components.UserComponent;\nimport com.fernandocejas.android10.sample.presentation.model.UserModel;\nimport com.fernandocejas.android10.sample.presentation.view.fragment.UserListFragment;\n\n/**\n * Activity that shows a list of Users.\n */\npublic class UserListActivity extends BaseActivity implements HasComponent<UserComponent>,\n    UserListFragment.UserListListener {\n\n  public static Intent getCallingIntent(Context context) {\n    return new Intent(context, UserListActivity.class);\n  }\n\n  private UserComponent userComponent;\n\n  @Override protected void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);\n    setContentView(R.layout.activity_layout);\n\n    this.initializeInjector();\n    if (savedInstanceState == null) {\n      addFragment(R.id.fragmentContainer, new UserListFragment());\n    }\n  }\n\n  private void initializeInjector() {\n    this.userComponent = DaggerUserComponent.builder()\n        .applicationComponent(getApplicationComponent())\n        .activityModule(getActivityModule())\n        .build();\n  }\n\n  @Override public UserComponent getComponent() {\n    return userComponent;\n  }\n\n  @Override public void onUserClicked(UserModel userModel) {\n    this.navigator.navigateToUserDetails(this, userModel.getUserId());\n  }\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/adapter/UsersAdapter.java",
    "content": "/**\n * Copyright (C) 2014 android10.org. All rights reserved.\n * @author Fernando Cejas (the android10 coder)\n */\npackage com.fernandocejas.android10.sample.presentation.view.adapter;\n\nimport android.content.Context;\nimport android.support.v7.widget.RecyclerView;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\nimport butterknife.Bind;\nimport butterknife.ButterKnife;\nimport com.fernandocejas.android10.sample.presentation.R;\nimport com.fernandocejas.android10.sample.presentation.model.UserModel;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport javax.inject.Inject;\n\n/**\n * Adaptar that manages a collection of {@link UserModel}.\n */\npublic class UsersAdapter extends RecyclerView.Adapter<UsersAdapter.UserViewHolder> {\n\n  public interface OnItemClickListener {\n    void onUserItemClicked(UserModel userModel);\n  }\n\n  private List<UserModel> usersCollection;\n  private final LayoutInflater layoutInflater;\n\n  private OnItemClickListener onItemClickListener;\n\n  @Inject\n  UsersAdapter(Context context) {\n    this.layoutInflater =\n        (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);\n    this.usersCollection = Collections.emptyList();\n  }\n\n  @Override public int getItemCount() {\n    return (this.usersCollection != null) ? this.usersCollection.size() : 0;\n  }\n\n  @Override public UserViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n    final View view = this.layoutInflater.inflate(R.layout.row_user, parent, false);\n    return new UserViewHolder(view);\n  }\n\n  @Override public void onBindViewHolder(UserViewHolder holder, final int position) {\n    final UserModel userModel = this.usersCollection.get(position);\n    holder.textViewTitle.setText(userModel.getFullName());\n    holder.itemView.setOnClickListener(new View.OnClickListener() {\n      @Override public void onClick(View v) {\n        if (UsersAdapter.this.onItemClickListener != null) {\n          UsersAdapter.this.onItemClickListener.onUserItemClicked(userModel);\n        }\n      }\n    });\n  }\n\n  @Override public long getItemId(int position) {\n    return position;\n  }\n\n  public void setUsersCollection(Collection<UserModel> usersCollection) {\n    this.validateUsersCollection(usersCollection);\n    this.usersCollection = (List<UserModel>) usersCollection;\n    this.notifyDataSetChanged();\n  }\n\n  public void setOnItemClickListener (OnItemClickListener onItemClickListener) {\n    this.onItemClickListener = onItemClickListener;\n  }\n\n  private void validateUsersCollection(Collection<UserModel> usersCollection) {\n    if (usersCollection == null) {\n      throw new IllegalArgumentException(\"The list cannot be null\");\n    }\n  }\n\n  static class UserViewHolder extends RecyclerView.ViewHolder {\n    @Bind(R.id.title) TextView textViewTitle;\n\n    UserViewHolder(View itemView) {\n      super(itemView);\n      ButterKnife.bind(this, itemView);\n    }\n  }\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/adapter/UsersLayoutManager.java",
    "content": "/**\n * Copyright (C) 2014 android10.org. All rights reserved.\n * @author Fernando Cejas (the android10 coder)\n */\npackage com.fernandocejas.android10.sample.presentation.view.adapter;\n\nimport android.content.Context;\nimport android.support.v7.widget.LinearLayoutManager;\n\n/**\n * Layout manager to position items inside a {@link android.support.v7.widget.RecyclerView}.\n */\npublic class UsersLayoutManager extends LinearLayoutManager {\n  public UsersLayoutManager(Context context) {\n    super(context);\n  }\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/component/AutoLoadImageView.java",
    "content": "/**\n * Copyright (C) 2014 android10.org. All rights reserved.\n *\n * @author Fernando Cejas (the android10 coder)\n */\npackage com.fernandocejas.android10.sample.presentation.view.component;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.net.ConnectivityManager;\nimport android.net.NetworkInfo;\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.widget.ImageView;\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.net.URL;\nimport java.net.URLConnection;\n\n/**\n * Simple implementation of {@link android.widget.ImageView} with extended features like setting an\n * image from an url and an internal file cache using the application cache directory.\n */\npublic class AutoLoadImageView extends ImageView {\n\n  private static final String BASE_IMAGE_NAME_CACHED = \"image_\";\n\n  private String imageUrl = null;\n  private int imagePlaceHolderResId = -1;\n  private DiskCache cache = new DiskCache(getContext().getCacheDir());\n\n  public AutoLoadImageView(Context context) {\n    super(context);\n  }\n\n  public AutoLoadImageView(Context context, AttributeSet attrs) {\n    super(context, attrs);\n  }\n\n  public AutoLoadImageView(Context context, AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n\n  @Override protected Parcelable onSaveInstanceState() {\n    Parcelable superState = super.onSaveInstanceState();\n    SavedState savedState = new SavedState(superState);\n    savedState.imagePlaceHolderResId = this.imagePlaceHolderResId;\n    savedState.imageUrl = this.imageUrl;\n    return savedState;\n  }\n\n  @Override protected void onRestoreInstanceState(Parcelable state) {\n    if(!(state instanceof SavedState)) {\n      super.onRestoreInstanceState(state);\n      return;\n    }\n    SavedState savedState = (SavedState)state;\n    super.onRestoreInstanceState(savedState.getSuperState());\n    this.imagePlaceHolderResId = savedState.imagePlaceHolderResId;\n    this.imageUrl = savedState.imageUrl;\n    this.setImageUrl(this.imageUrl);\n  }\n\n  /**\n   * Set an image from a remote url.\n   *\n   * @param imageUrl The url of the resource to load.\n   */\n  public void setImageUrl(final String imageUrl) {\n    this.imageUrl = imageUrl;\n    AutoLoadImageView.this.loadImagePlaceHolder();\n    if (this.imageUrl != null) {\n      this.loadImageFromUrl(this.imageUrl);\n    } else {\n      this.loadImagePlaceHolder();\n    }\n  }\n\n  /**\n   * Loads and image from the internet (and cache it) or from the internal cache.\n   *\n   * @param imageUrl The remote image url to load.\n   */\n  private void loadImageFromUrl(final String imageUrl) {\n    new Thread() {\n      @Override public void run() {\n        final Bitmap bitmap = AutoLoadImageView.this.getFromCache(getFileNameFromUrl(imageUrl));\n        if (bitmap != null) {\n          AutoLoadImageView.this.loadBitmap(bitmap);\n        } else {\n          if (isThereInternetConnection()) {\n            final ImageDownloader imageDownloader = new ImageDownloader();\n            imageDownloader.download(imageUrl, new ImageDownloader.Callback() {\n              @Override public void onImageDownloaded(Bitmap bitmap) {\n                AutoLoadImageView.this.cacheBitmap(bitmap, getFileNameFromUrl(imageUrl));\n                AutoLoadImageView.this.loadBitmap(bitmap);\n              }\n\n              @Override public void onError() {\n                AutoLoadImageView.this.loadImagePlaceHolder();\n              }\n            });\n          } else {\n            AutoLoadImageView.this.loadImagePlaceHolder();\n          }\n        }\n      }\n    }.start();\n  }\n\n  /**\n   * Run the operation of loading a bitmap on the UI thread.\n   *\n   * @param bitmap The image to load.\n   */\n  private void loadBitmap(final Bitmap bitmap) {\n    ((Activity) getContext()).runOnUiThread(new Runnable() {\n      @Override public void run() {\n        AutoLoadImageView.this.setImageBitmap(bitmap);\n      }\n    });\n  }\n\n  /**\n   * Loads the image place holder if any has been assigned.\n   */\n  private void loadImagePlaceHolder() {\n    if (this.imagePlaceHolderResId != -1) {\n      ((Activity) getContext()).runOnUiThread(new Runnable() {\n        @Override public void run() {\n          AutoLoadImageView.this.setImageResource(\n              AutoLoadImageView.this.imagePlaceHolderResId);\n        }\n      });\n    }\n  }\n\n  /**\n   * Get a {@link android.graphics.Bitmap} from the internal cache or null if it does not exist.\n   *\n   * @param fileName The name of the file to look for in the cache.\n   * @return A valid cached bitmap, otherwise null.\n   */\n  private Bitmap getFromCache(String fileName) {\n    Bitmap bitmap = null;\n    if (this.cache != null) {\n      bitmap = this.cache.get(fileName);\n    }\n    return bitmap;\n  }\n\n  /**\n   * Cache an image using the internal cache.\n   *\n   * @param bitmap The bitmap to cache.\n   * @param fileName The file name used for caching the bitmap.\n   */\n  private void cacheBitmap(Bitmap bitmap, String fileName) {\n    if (this.cache != null) {\n      this.cache.put(bitmap, fileName);\n    }\n  }\n\n  /**\n   * Checks if the device has any active internet connection.\n   *\n   * @return true device with internet connection, otherwise false.\n   */\n  private boolean isThereInternetConnection() {\n    boolean isConnected;\n\n    final ConnectivityManager connectivityManager =\n        (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);\n    final NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();\n    isConnected = (networkInfo != null && networkInfo.isConnectedOrConnecting());\n\n    return isConnected;\n  }\n\n  /**\n   * Creates a file name from an image url\n   *\n   * @param imageUrl The image url used to build the file name.\n   * @return An String representing a unique file name.\n   */\n  private String getFileNameFromUrl(String imageUrl) {\n    //we could generate an unique MD5/SHA-1 here\n    String hash = String.valueOf(imageUrl.hashCode());\n    if (hash.startsWith(\"-\")) {\n      hash = hash.substring(1);\n    }\n    return BASE_IMAGE_NAME_CACHED + hash;\n  }\n\n  /**\n   * Class used to download images from the internet\n   */\n  private static class ImageDownloader {\n    interface Callback {\n      void onImageDownloaded(Bitmap bitmap);\n\n      void onError();\n    }\n\n    ImageDownloader() {}\n\n    /**\n     * Download an image from an url.\n     *\n     * @param imageUrl The url of the image to download.\n     * @param callback A callback used to be reported when the task is finished.\n     */\n    void download(String imageUrl, Callback callback) {\n      try {\n        final URLConnection conn = new URL(imageUrl).openConnection();\n        conn.connect();\n        final Bitmap bitmap = BitmapFactory.decodeStream(conn.getInputStream());\n        if (callback != null) {\n          callback.onImageDownloaded(bitmap);\n        }\n      } catch (IOException e) {\n        reportError(callback);\n      }\n    }\n\n    /**\n     * Report an error to the caller\n     *\n     * @param callback Caller implementing {@link Callback}\n     */\n    private void reportError(Callback callback) {\n      if (callback != null) {\n        callback.onError();\n      }\n    }\n  }\n\n  /**\n   * A simple disk cache implementation\n   */\n  private static class DiskCache {\n\n    private static final String TAG = \"DiskCache\";\n\n    private final File cacheDir;\n\n    DiskCache(File cacheDir) {\n      this.cacheDir = cacheDir;\n    }\n\n    /**\n     * Get an element from the cache.\n     *\n     * @param fileName The name of the file to look for.\n     * @return A valid element, otherwise false.\n     */\n    synchronized Bitmap get(String fileName) {\n      Bitmap bitmap = null;\n      File file = buildFileFromFilename(fileName);\n      if (file.exists()) {\n        bitmap = BitmapFactory.decodeFile(file.getPath());\n      }\n      return bitmap;\n    }\n\n    /**\n     * Cache an element.\n     *\n     * @param bitmap The bitmap to be put in the cache.\n     * @param fileName A string representing the name of the file to be cached.\n     */\n    synchronized void put(Bitmap bitmap, String fileName) {\n      final File file = buildFileFromFilename(fileName);\n      if (!file.exists()) {\n        try {\n          final FileOutputStream fileOutputStream = new FileOutputStream(file);\n          bitmap.compress(Bitmap.CompressFormat.PNG, 90, fileOutputStream);\n          fileOutputStream.flush();\n          fileOutputStream.close();\n        } catch (IOException e) {\n          Log.e(TAG, e.getMessage());\n        }\n      }\n    }\n\n    /**\n     * Creates a file name from an image url\n     *\n     * @param fileName The image url used to build the file name.\n     * @return A {@link java.io.File} representing a unique element.\n     */\n    private File buildFileFromFilename(String fileName) {\n      String fullPath = this.cacheDir.getPath() + File.separator + fileName;\n      return new File(fullPath);\n    }\n  }\n\n  private static class SavedState extends BaseSavedState {\n    int imagePlaceHolderResId;\n    String imageUrl;\n\n    SavedState(Parcelable superState) {\n      super(superState);\n    }\n\n    private SavedState(Parcel in) {\n      super(in);\n      this.imagePlaceHolderResId = in.readInt();\n      this.imageUrl = in.readString();\n    }\n\n    @Override\n    public void writeToParcel(Parcel out, int flags) {\n      super.writeToParcel(out, flags);\n      out.writeInt(this.imagePlaceHolderResId);\n      out.writeString(this.imageUrl);\n    }\n\n    public static final Parcelable.Creator<SavedState> CREATOR =\n        new Parcelable.Creator<SavedState>() {\n          public SavedState createFromParcel(Parcel in) {\n            return new SavedState(in);\n          }\n\n          public SavedState[] newArray(int size) {\n            return new SavedState[size];\n          }\n        };\n  }\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/fragment/BaseFragment.java",
    "content": "/**\n * Copyright (C) 2014 android10.org. All rights reserved.\n *\n * @author Fernando Cejas (the android10 coder)\n */\npackage com.fernandocejas.android10.sample.presentation.view.fragment;\n\nimport android.app.Fragment;\nimport android.widget.Toast;\nimport com.fernandocejas.android10.sample.presentation.internal.di.HasComponent;\n\n/**\n * Base {@link android.app.Fragment} class for every fragment in this application.\n */\npublic abstract class BaseFragment extends Fragment {\n  /**\n   * Shows a {@link android.widget.Toast} message.\n   *\n   * @param message An string representing a message to be shown.\n   */\n  protected void showToastMessage(String message) {\n    Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show();\n  }\n\n  /**\n   * Gets a component for dependency injection by its type.\n   */\n  @SuppressWarnings(\"unchecked\")\n  protected <C> C getComponent(Class<C> componentType) {\n    return componentType.cast(((HasComponent<C>) getActivity()).getComponent());\n  }\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/fragment/UserDetailsFragment.java",
    "content": "/**\n * Copyright (C) 2014 android10.org. All rights reserved.\n * @author Fernando Cejas (the android10 coder)\n */\npackage com.fernandocejas.android10.sample.presentation.view.fragment;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Button;\nimport android.widget.RelativeLayout;\nimport android.widget.TextView;\nimport butterknife.Bind;\nimport butterknife.ButterKnife;\nimport butterknife.OnClick;\nimport com.fernandocejas.android10.sample.presentation.R;\nimport com.fernandocejas.android10.sample.presentation.internal.di.components.UserComponent;\nimport com.fernandocejas.android10.sample.presentation.model.UserModel;\nimport com.fernandocejas.android10.sample.presentation.presenter.UserDetailsPresenter;\nimport com.fernandocejas.android10.sample.presentation.view.UserDetailsView;\nimport com.fernandocejas.android10.sample.presentation.view.component.AutoLoadImageView;\nimport com.fernandocejas.arrow.checks.Preconditions;\nimport javax.inject.Inject;\n\n/**\n * Fragment that shows details of a certain user.\n */\npublic class UserDetailsFragment extends BaseFragment implements UserDetailsView {\n  private static final String PARAM_USER_ID = \"param_user_id\";\n\n  @Inject UserDetailsPresenter userDetailsPresenter;\n\n  @Bind(R.id.iv_cover) AutoLoadImageView iv_cover;\n  @Bind(R.id.tv_fullname) TextView tv_fullname;\n  @Bind(R.id.tv_email) TextView tv_email;\n  @Bind(R.id.tv_followers) TextView tv_followers;\n  @Bind(R.id.tv_description) TextView tv_description;\n  @Bind(R.id.rl_progress) RelativeLayout rl_progress;\n  @Bind(R.id.rl_retry) RelativeLayout rl_retry;\n  @Bind(R.id.bt_retry) Button bt_retry;\n\n  public static UserDetailsFragment forUser(int userId) {\n    final UserDetailsFragment userDetailsFragment = new UserDetailsFragment();\n    final Bundle arguments = new Bundle();\n    arguments.putInt(PARAM_USER_ID, userId);\n    userDetailsFragment.setArguments(arguments);\n    return userDetailsFragment;\n  }\n\n  public UserDetailsFragment() {\n    setRetainInstance(true);\n  }\n\n  @Override public void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    this.getComponent(UserComponent.class).inject(this);\n  }\n\n  @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,\n      Bundle savedInstanceState) {\n    final View fragmentView = inflater.inflate(R.layout.fragment_user_details, container, false);\n    ButterKnife.bind(this, fragmentView);\n    return fragmentView;\n  }\n\n  @Override public void onViewCreated(View view, Bundle savedInstanceState) {\n    super.onViewCreated(view, savedInstanceState);\n    this.userDetailsPresenter.setView(this);\n    if (savedInstanceState == null) {\n      this.loadUserDetails();\n    }\n  }\n\n  @Override public void onResume() {\n    super.onResume();\n    this.userDetailsPresenter.resume();\n  }\n\n  @Override public void onPause() {\n    super.onPause();\n    this.userDetailsPresenter.pause();\n  }\n\n  @Override public void onDestroyView() {\n    super.onDestroyView();\n    ButterKnife.unbind(this);\n  }\n\n  @Override public void onDestroy() {\n    super.onDestroy();\n    this.userDetailsPresenter.destroy();\n  }\n\n  @Override public void renderUser(UserModel user) {\n    if (user != null) {\n      this.iv_cover.setImageUrl(user.getCoverUrl());\n      this.tv_fullname.setText(user.getFullName());\n      this.tv_email.setText(user.getEmail());\n      this.tv_followers.setText(String.valueOf(user.getFollowers()));\n      this.tv_description.setText(user.getDescription());\n    }\n  }\n\n  @Override public void showLoading() {\n    this.rl_progress.setVisibility(View.VISIBLE);\n    this.getActivity().setProgressBarIndeterminateVisibility(true);\n  }\n\n  @Override public void hideLoading() {\n    this.rl_progress.setVisibility(View.GONE);\n    this.getActivity().setProgressBarIndeterminateVisibility(false);\n  }\n\n  @Override public void showRetry() {\n    this.rl_retry.setVisibility(View.VISIBLE);\n  }\n\n  @Override public void hideRetry() {\n    this.rl_retry.setVisibility(View.GONE);\n  }\n\n  @Override public void showError(String message) {\n    this.showToastMessage(message);\n  }\n\n  @Override public Context context() {\n    return getActivity().getApplicationContext();\n  }\n\n  /**\n   * Load user details.\n   */\n  private void loadUserDetails() {\n    if (this.userDetailsPresenter != null) {\n      this.userDetailsPresenter.initialize(currentUserId());\n    }\n  }\n\n  /**\n   * Get current user id from fragments arguments.\n   */\n  private int currentUserId() {\n    final Bundle arguments = getArguments();\n    Preconditions.checkNotNull(arguments, \"Fragment arguments cannot be null\");\n    return arguments.getInt(PARAM_USER_ID);\n  }\n\n  @OnClick(R.id.bt_retry)\n  void onButtonRetryClick() {\n    UserDetailsFragment.this.loadUserDetails();\n  }\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/fernandocejas/android10/sample/presentation/view/fragment/UserListFragment.java",
    "content": "/**\n * Copyright (C) 2014 android10.org. All rights reserved.\n *\n * @author Fernando Cejas (the android10 coder)\n */\npackage com.fernandocejas.android10.sample.presentation.view.fragment;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.support.v7.widget.RecyclerView;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Button;\nimport android.widget.RelativeLayout;\nimport butterknife.Bind;\nimport butterknife.ButterKnife;\nimport butterknife.OnClick;\nimport com.fernandocejas.android10.sample.presentation.R;\nimport com.fernandocejas.android10.sample.presentation.internal.di.components.UserComponent;\nimport com.fernandocejas.android10.sample.presentation.model.UserModel;\nimport com.fernandocejas.android10.sample.presentation.presenter.UserListPresenter;\nimport com.fernandocejas.android10.sample.presentation.view.UserListView;\nimport com.fernandocejas.android10.sample.presentation.view.adapter.UsersAdapter;\nimport com.fernandocejas.android10.sample.presentation.view.adapter.UsersLayoutManager;\nimport java.util.Collection;\nimport javax.inject.Inject;\n\n/**\n * Fragment that shows a list of Users.\n */\npublic class UserListFragment extends BaseFragment implements UserListView {\n\n  /**\n   * Interface for listening user list events.\n   */\n  public interface UserListListener {\n    void onUserClicked(final UserModel userModel);\n  }\n\n  @Inject UserListPresenter userListPresenter;\n  @Inject UsersAdapter usersAdapter;\n\n  @Bind(R.id.rv_users) RecyclerView rv_users;\n  @Bind(R.id.rl_progress) RelativeLayout rl_progress;\n  @Bind(R.id.rl_retry) RelativeLayout rl_retry;\n  @Bind(R.id.bt_retry) Button bt_retry;\n\n  private UserListListener userListListener;\n\n  public UserListFragment() {\n    setRetainInstance(true);\n  }\n\n  @Override public void onAttach(Activity activity) {\n    super.onAttach(activity);\n    if (activity instanceof UserListListener) {\n      this.userListListener = (UserListListener) activity;\n    }\n  }\n\n  @Override public void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    this.getComponent(UserComponent.class).inject(this);\n  }\n\n  @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,\n      Bundle savedInstanceState) {\n    final View fragmentView = inflater.inflate(R.layout.fragment_user_list, container, false);\n    ButterKnife.bind(this, fragmentView);\n    setupRecyclerView();\n    return fragmentView;\n  }\n\n  @Override public void onViewCreated(View view, Bundle savedInstanceState) {\n    super.onViewCreated(view, savedInstanceState);\n    this.userListPresenter.setView(this);\n    if (savedInstanceState == null) {\n      this.loadUserList();\n    }\n  }\n\n  @Override public void onResume() {\n    super.onResume();\n    this.userListPresenter.resume();\n  }\n\n  @Override public void onPause() {\n    super.onPause();\n    this.userListPresenter.pause();\n  }\n\n  @Override public void onDestroyView() {\n    super.onDestroyView();\n    rv_users.setAdapter(null);\n    ButterKnife.unbind(this);\n  }\n\n  @Override public void onDestroy() {\n    super.onDestroy();\n    this.userListPresenter.destroy();\n  }\n\n  @Override public void onDetach() {\n    super.onDetach();\n    this.userListListener = null;\n  }\n\n  @Override public void showLoading() {\n    this.rl_progress.setVisibility(View.VISIBLE);\n    this.getActivity().setProgressBarIndeterminateVisibility(true);\n  }\n\n  @Override public void hideLoading() {\n    this.rl_progress.setVisibility(View.GONE);\n    this.getActivity().setProgressBarIndeterminateVisibility(false);\n  }\n\n  @Override public void showRetry() {\n    this.rl_retry.setVisibility(View.VISIBLE);\n  }\n\n  @Override public void hideRetry() {\n    this.rl_retry.setVisibility(View.GONE);\n  }\n\n  @Override public void renderUserList(Collection<UserModel> userModelCollection) {\n    if (userModelCollection != null) {\n      this.usersAdapter.setUsersCollection(userModelCollection);\n    }\n  }\n\n  @Override public void viewUser(UserModel userModel) {\n    if (this.userListListener != null) {\n      this.userListListener.onUserClicked(userModel);\n    }\n  }\n\n  @Override public void showError(String message) {\n    this.showToastMessage(message);\n  }\n\n  @Override public Context context() {\n    return this.getActivity().getApplicationContext();\n  }\n\n  private void setupRecyclerView() {\n    this.usersAdapter.setOnItemClickListener(onItemClickListener);\n    this.rv_users.setLayoutManager(new UsersLayoutManager(context()));\n    this.rv_users.setAdapter(usersAdapter);\n  }\n\n  /**\n   * Loads all users.\n   */\n  private void loadUserList() {\n    this.userListPresenter.initialize();\n  }\n\n  @OnClick(R.id.bt_retry) void onButtonRetryClick() {\n    UserListFragment.this.loadUserList();\n  }\n\n  private UsersAdapter.OnItemClickListener onItemClickListener =\n      new UsersAdapter.OnItemClickListener() {\n        @Override public void onUserItemClicked(UserModel userModel) {\n          if (UserListFragment.this.userListPresenter != null && userModel != null) {\n            UserListFragment.this.userListPresenter.onUserClicked(userModel);\n          }\n        }\n      };\n}\n"
  },
  {
    "path": "presentation/src/main/res/drawable/selector_item_user.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <item android:drawable=\"@android:color/holo_blue_light\" android:state_focused=\"true\" />\n  <item android:drawable=\"@android:color/holo_blue_light\" android:state_pressed=\"true\" />\n  <item android:drawable=\"@android:color/holo_blue_light\" android:state_selected=\"true\" />\n  <item android:drawable=\"@android:color/background_light\" />\n</selector>"
  },
  {
    "path": "presentation/src/main/res/layout/activity_layout.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n  <FrameLayout\n      android:id=\"@+id/fragmentContainer\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\" />\n\n</RelativeLayout>\n"
  },
  {
    "path": "presentation/src/main/res/layout/activity_main.xml",
    "content": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:orientation=\"vertical\"\n    tools:context=\".MainActivity\"\n    >\n\n  <TextView\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:text=\"@string/url\"\n      android:gravity=\"center\"\n      />\n\n  <ImageView\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:src=\"@drawable/logo\"\n      android:contentDescription=\"@string/string_content_description\"\n      />\n\n  <Button\n      android:id=\"@+id/btn_LoadData\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:text=\"@string/btn_text_load_data\"\n      />\n\n</LinearLayout>\n"
  },
  {
    "path": "presentation/src/main/res/layout/fragment_user_details.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n  <include layout=\"@layout/view_user_details\" />\n\n  <include layout=\"@layout/view_progress\" />\n\n  <include layout=\"@layout/view_retry\" />\n\n</RelativeLayout>\n"
  },
  {
    "path": "presentation/src/main/res/layout/fragment_user_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    >\n\n  <android.support.v7.widget.RecyclerView\n      android:id=\"@+id/rv_users\"\n      android:scrollbars=\"vertical\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      />\n\n  <include\n      layout=\"@layout/view_progress\"\n      />\n\n  <include\n      layout=\"@layout/view_retry\"\n      />\n\n</RelativeLayout>"
  },
  {
    "path": "presentation/src/main/res/layout/row_user.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:ignore=\"UseCompoundDrawables\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"horizontal\"\n    android:padding=\"@dimen/lv_row_padding\"\n    android:background=\"@drawable/selector_item_user\"\n    >\n\n  <ImageView\n      android:id=\"@+id/avatar\"\n      android:layout_width=\"@dimen/lv_row_icon_width\"\n      android:layout_height=\"@dimen/lv_row_icon_width\"\n      android:src=\"@drawable/ic_launcher\"\n      android:paddingLeft=\"@dimen/lv_row_icon_padding\"\n      android:paddingRight=\"@dimen/lv_row_icon_padding\"\n      android:contentDescription=\"@string/string_content_description\"\n      />\n\n  <TextView\n      android:id=\"@+id/title\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:textSize=\"@dimen/lv_row_text_size\"\n      android:textColor=\"@android:color/black\"\n      android:gravity=\"center_vertical\"\n      android:singleLine=\"true\"\n      android:ellipsize=\"end\"\n      />\n\n</LinearLayout>\n"
  },
  {
    "path": "presentation/src/main/res/layout/view_progress.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/rl_progress\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:visibility=\"gone\"\n    >\n\n  <ProgressBar\n      style=\"@style/AppTheme.ProgressBar\"\n      android:layout_centerInParent=\"true\"\n      />\n\n</RelativeLayout>\n"
  },
  {
    "path": "presentation/src/main/res/layout/view_retry.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/rl_retry\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:visibility=\"gone\"\n    >\n\n  <Button\n      android:id=\"@+id/bt_retry\"\n      style=\"@style/AppTheme.Button\"\n      android:text=\"@string/btn_text_retry\"\n      />\n\n</RelativeLayout>\n"
  },
  {
    "path": "presentation/src/main/res/layout/view_user_details.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    >\n\n  <com.fernandocejas.android10.sample.presentation.view.component.AutoLoadImageView\n      android:id=\"@+id/iv_cover\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"@dimen/iv_cover_height\"\n      android:background=\"@android:color/darker_gray\"\n      />\n\n  <TextView\n      android:id=\"@+id/tv_fullname\"\n      style=\"@style/AppTheme.TextViewHeader\"\n      android:freezesText=\"true\"\n      />\n\n  <LinearLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:layout_marginLeft=\"@dimen/activity_horizontal_margin\"\n      android:layout_marginRight=\"@dimen/activity_horizontal_margin\"\n      android:orientation=\"vertical\"\n      >\n    <TextView\n        style=\"@style/AppTheme.TextViewTitle\"\n        android:text=\"@string/view_text_email\"\n        />\n    <TextView\n        android:id=\"@+id/tv_email\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:freezesText=\"true\"\n        />\n    <TextView\n        style=\"@style/AppTheme.TextViewTitle\"\n        android:text=\"@string/view_text_followers\"\n        />\n    <TextView\n        android:id=\"@+id/tv_followers\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:freezesText=\"true\"\n        />\n\n    <TextView\n        style=\"@style/AppTheme.TextViewTitle\"\n        android:text=\"@string/view_text_description\"\n        />\n    <TextView\n        android:id=\"@+id/tv_description\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:freezesText=\"true\"\n        />\n  </LinearLayout>\n\n</LinearLayout>\n"
  },
  {
    "path": "presentation/src/main/res/values/dimens.xml",
    "content": "<resources>\n  <!-- Default screen margins, per the Android Design guidelines. -->\n  <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n  <dimen name=\"activity_vertical_margin\">16dp</dimen>\n\n  <!--User Header stuff-->\n  <dimen name=\"header_text_size\">26sp</dimen>\n  <dimen name=\"header_margin\">5dp</dimen>\n  <dimen name=\"iv_cover_height\">140dp</dimen>\n\n  <!--Listview rows stuff-->\n  <dimen name=\"lv_row_padding\">5dp</dimen>\n  <dimen name=\"lv_row_text_size\">20sp</dimen>\n  <dimen name=\"lv_row_icon_width\">50dp</dimen>\n  <dimen name=\"lv_row_icon_padding\">5dp</dimen>\n</resources>\n"
  },
  {
    "path": "presentation/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n  <string name=\"app_name\">Android Clean Architecture</string>\n  <string name=\"url\">http://www.android10.org/</string>\n\n  <string name=\"activity_title_user_list\">Users List</string>\n  <string name=\"activity_title_user_details\">User Details</string>\n\n  <string name=\"btn_text_load_data\">Load Sample Data</string>\n  <string name=\"btn_text_retry\">Retry</string>\n\n  <string name=\"view_text_email\">Email:</string>\n  <string name=\"view_text_followers\">Followers:</string>\n  <string name=\"view_text_description\">Description:</string>\n\n  <string name=\"string_content_description\">Non accessible view</string>\n\n  //Exception messages\n  <string name=\"exception_message_generic\">There was an application error</string>\n  <string name=\"exception_message_no_connection\">There is no internet connection</string>\n  <string name=\"exception_message_user_not_found\">Cannot retrieve user data. Check your internet connection</string>\n</resources>\n"
  },
  {
    "path": "presentation/src/main/res/values/styles.xml",
    "content": "<resources>\n\n  <!-- Base application theme. -->\n  <style name=\"AppTheme\" parent=\"android:Theme.Holo.Light.DarkActionBar\">\n  </style>\n\n  <style name=\"AppTheme.ProgressBar\">\n    <item name=\"android:layout_width\">wrap_content</item>\n    <item name=\"android:layout_height\">wrap_content</item>\n    <item name=\"android:layout_centerInParent\">true</item>\n    <item name=\"android:layout_gravity\">center</item>\n    <item name=\"android:visibility\">visible</item>\n  </style>\n\n  <style name=\"AppTheme.Button\">\n    <item name=\"android:layout_width\">wrap_content</item>\n    <item name=\"android:layout_height\">wrap_content</item>\n    <item name=\"android:layout_centerVertical\">true</item>\n    <item name=\"android:layout_centerHorizontal\">true</item>\n    <item name=\"android:layout_gravity\">center</item>\n  </style>\n\n  <style name=\"AppTheme.TextViewHeader\">\n    <item name=\"android:layout_width\">match_parent</item>\n    <item name=\"android:layout_height\">wrap_content</item>\n    <item name=\"android:layout_marginBottom\">@dimen/header_margin</item>\n    <item name=\"android:textSize\">@dimen/header_text_size</item>\n    <item name=\"android:textStyle\">bold</item>\n    <item name=\"android:gravity\">center</item>\n    <item name=\"android:background\">@android:color/background_light</item>\n  </style>\n\n  <style name=\"AppTheme.TextViewTitle\">\n    <item name=\"android:layout_width\">match_parent</item>\n    <item name=\"android:layout_height\">wrap_content</item>\n    <item name=\"android:layout_marginTop\">@dimen/header_margin</item>\n    <item name=\"android:textStyle\">bold</item>\n  </style>\n\n</resources>\n"
  },
  {
    "path": "settings.gradle",
    "content": "include ':presentation'\ninclude ':domain'\ninclude ':data'\n"
  }
]