├── .github └── FUNDING.yml ├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.txt └── src │ └── main │ ├── AndroidManifest.xml │ ├── app_icon-web.png │ ├── java │ └── com │ │ └── github │ │ └── pedrovgs │ │ └── effectiveandroidui │ │ ├── TvShowsApplication.java │ │ ├── di │ │ ├── ActivityContext.java │ │ ├── ActivityModule.java │ │ └── RootModule.java │ │ ├── domain │ │ ├── GetTvShowById.java │ │ ├── GetTvShowByIdInteractor.java │ │ ├── GetTvShows.java │ │ ├── GetTvShowsInteractor.java │ │ ├── TvShowsModule.java │ │ ├── exception │ │ │ └── TvShowNotFoundException.java │ │ └── tvshow │ │ │ ├── Catalog.java │ │ │ ├── Chapter.java │ │ │ ├── ChapterCollection.java │ │ │ └── TvShow.java │ │ ├── executor │ │ ├── Executor.java │ │ ├── ExecutorModule.java │ │ ├── Interactor.java │ │ ├── MainThread.java │ │ ├── MainThreadImpl.java │ │ └── ThreadExecutor.java │ │ ├── ui │ │ ├── activity │ │ │ ├── BaseActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── Navigator.java │ │ │ └── TvShowActivity.java │ │ ├── fragment │ │ │ ├── BaseFragment.java │ │ │ ├── TvShowCatalogFragment.java │ │ │ ├── TvShowDraggableFragment.java │ │ │ └── TvShowFragment.java │ │ ├── presenter │ │ │ ├── Presenter.java │ │ │ ├── TvShowCatalogPresenter.java │ │ │ ├── TvShowPresenter.java │ │ │ └── TvShowUIModule.java │ │ ├── renderer │ │ │ ├── chapter │ │ │ │ ├── ChapterAdapteeCollection.java │ │ │ │ ├── ChapterRenderer.java │ │ │ │ ├── ChapterRendererAdapter.java │ │ │ │ ├── ChapterRendererAdapterFactory.java │ │ │ │ └── ChapterRendererBuilder.java │ │ │ ├── chapterviewmodel │ │ │ │ ├── ChapterViewModelCollection.java │ │ │ │ ├── ChapterViewModelRenderer.java │ │ │ │ ├── ChapterViewModelRendererAdapter.java │ │ │ │ ├── ChapterViewModelRendererAdapterFactory.java │ │ │ │ └── ChapterViewModelRendererBuilder.java │ │ │ └── tvshow │ │ │ │ ├── TvShowCollection.java │ │ │ │ ├── TvShowRenderer.java │ │ │ │ ├── TvShowRendererAdapterFactory.java │ │ │ │ └── TvShowRendererBuilder.java │ │ └── viewmodel │ │ │ ├── ChapterViewModel.java │ │ │ ├── TvShowViewModel.java │ │ │ └── action │ │ │ ├── ActionCommand.java │ │ │ └── ShowTvShowOnBrowserActionCommand.java │ │ └── util │ │ ├── RandomUtils.java │ │ ├── StringUtils.java │ │ ├── TimeMachine.java │ │ └── ToastUtils.java │ ├── lint.xml │ └── res │ ├── drawable-hdpi │ └── app_icon.png │ ├── drawable-mdpi │ └── app_icon.png │ ├── drawable-xhdpi │ └── app_icon.png │ ├── drawable-xxhdpi │ └── app_icon.png │ ├── drawable │ └── empty_case.png │ ├── layout-sw600dp-land │ └── activity_main.xml │ ├── layout-v10 │ └── activity_main.xml │ ├── layout-v11 │ └── activity_main.xml │ ├── layout │ ├── activity_tv_show.xml │ ├── fragment_draggable_tv_show.xml │ ├── fragment_tv_show.xml │ ├── fragment_tv_shows.xml │ ├── header_tv_show_chapters.xml │ ├── row_chapter.xml │ └── row_tv_show.xml │ ├── values-hdpi │ ├── dimens.xml │ └── integers.xml │ ├── values-mdpi │ ├── dimens.xml │ └── integers.xml │ ├── values-w820dp │ └── integers.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ ├── styles-chapter.xml │ └── styles.xml ├── art ├── screenshot1.png ├── screenshot2.png └── screenshot3.png ├── build.gradle ├── config ├── checkstyle │ └── checkstyle.xml ├── findbugs │ └── findbugs-excludes.xml └── pmd │ └── pmd-ruleset.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [pedrovgs] 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | 15 | # Gradle files 16 | .gradle/ 17 | build/ 18 | 19 | # Local configuration file (sdk path, etc) 20 | local.properties 21 | 22 | # Proguard folder generated by Eclipse 23 | proguard/ 24 | 25 | # IntelliJ files 26 | .idea 27 | *.iml 28 | 29 | # Maven output folder 30 | target 31 | 32 | # Misc 33 | .DS_Store 34 | Thumbs.db 35 | *.swp 36 | *.bak -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | 3 | android: 4 | components: 5 | - build-tools-23.0.1 6 | - android-23 7 | - extra-google-google_play_services 8 | - extra-google-m2repository 9 | - extra-android-m2repository 10 | - extra-android-support 11 | 12 | script: 13 | # Place findbugs after build 14 | ./gradlew checkstyle pmd build findbugs -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Effective Android UI [![Build Status](https://travis-ci.org/pedrovgs/EffectiveAndroidUI.svg?branch=master)](https://travis-ci.org/pedrovgs/EffectiveAndroidUI) [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-EffectiveAndroidUI-brightgreen.svg?style=flat)](https://android-arsenal.com/details/1/1347) 2 | ==================== 3 | 4 | 🔝 Top sponsors 🔝 5 | 6 | [![EmergeLogo](https://www.emergetools.com/images/emergetoolsstandard.png)](https://www.emergetools.com) 7 | 8 | Sample project created to reinforce some of the main concepts described in the tech talk "Effective Android UI". 9 | 10 | In this repository you are going to find some interesting samples like: 11 | 12 | * **MVP** and **MVVM (without data binding engine)** samples. 13 | * How to use **fragments**. 14 | * How to use **Dagger to implement dependency injection**. 15 | * Use **resource qualifiers** to change the layout used in **different screen sizes**. 16 | * Use **resource qualifiers** to change the layout used in **different screen densities**. 17 | * Use **resource qualifiers** to change the layout in **different Android version**. 18 | * How to use **styles and themes**. 19 | * How to **communicate fragments** in the same activity. 20 | * **Butterknife** library usage to **avoid UI duplicated code**. 21 | * Uniform **naming** for Android resources. 22 | * How to use **Navigator** or **ActionCommands** to **implement the navigation** inside the application. 23 | * Use resource custom qualifiers to **split resource files by domain**. 24 | * Different **layout usage**: RelativeLayout, LinearLayout, FrameLayout. 25 | * Usage of **merge, include and view stub**. 26 | * Correct **ListView** implementation with **view recycle** using **Renderers**. 27 | * **Interactor implementation** described in the talk "Software Design Patterns on Android". 28 | * Usage of **Dagger to implement two different scopes: Application scope and Activity scope**. 29 | 30 | Implementation description 31 | -------------------------- 32 | 33 | [EffectiveAndroid UI Video - Spanish][4] 34 | 35 | [EffectiveAndroid UI Slides - English][5] 36 | 37 | Screenshots 38 | ------------ 39 | 40 | ![Demo Screenshot 1][1] 41 | ![Demo Screenshot 2][2] 42 | ![Demo Screenshot 3][3] 43 | 44 | Libraries used on the sample project 45 | ------------------------------------ 46 | 47 | * [Renderers][6] 48 | * [Dagger][7] 49 | * [Butterknife][8] 50 | * [Picasso][9] 51 | * [DraggablePanel][10] 52 | 53 | 54 | Developed By 55 | ------------ 56 | 57 | * Pedro Vicente Gómez Sánchez - 58 | 59 | 60 | Follow me on Twitter 61 | 62 | 63 | Add me to Linkedin 64 | 65 | 66 | 67 | License 68 | ------- 69 | 70 | Copyright 2014 Pedro Vicente Gómez Sánchez 71 | 72 | Licensed under the Apache License, Version 2.0 (the "License"); 73 | you may not use this file except in compliance with the License. 74 | You may obtain a copy of the License at 75 | 76 | http://www.apache.org/licenses/LICENSE-2.0 77 | 78 | Unless required by applicable law or agreed to in writing, software 79 | distributed under the License is distributed on an "AS IS" BASIS, 80 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 81 | See the License for the specific language governing permissions and 82 | limitations under the License. 83 | 84 | [1]: ./art/screenshot1.png 85 | [2]: ./art/screenshot2.png 86 | [3]: ./art/screenshot3.png 87 | [4]: https://www.youtube.com/watch?v=N6yqe88ysNw 88 | [5]: http://www.slideshare.net/PedroVicenteGmezSnch/effective-android-ui-english 89 | [6]: https://github.com/pedrovgs/Renderers 90 | [7]: https://github.com/square/dagger 91 | [8]: https://github.com/JakeWharton/butterknife 92 | [9]: https://github.com/square/picasso 93 | [10]: https://github.com/pedrovgs/DraggablePanel 94 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | mavenCentral() 4 | } 5 | dependencies { 6 | classpath 'com.android.tools.build:gradle:1.3.0' 7 | } 8 | } 9 | apply plugin: 'com.android.application' 10 | apply plugin: 'checkstyle' 11 | apply plugin: 'pmd' 12 | apply plugin: 'findbugs' 13 | apply plugin: "sonar-runner" 14 | 15 | repositories { 16 | mavenCentral() 17 | } 18 | 19 | android { 20 | compileSdkVersion 23 21 | buildToolsVersion "23.0.1" 22 | 23 | defaultConfig { 24 | minSdkVersion 8 25 | targetSdkVersion 23 26 | versionCode 1 27 | versionName "1.0" 28 | } 29 | 30 | lintOptions { 31 | abortOnError = true 32 | lintConfig file("src/main/lint.xml") 33 | } 34 | 35 | packagingOptions { 36 | exclude 'META-INF/services/javax.annotation.processing.Processor' 37 | } 38 | } 39 | 40 | dependencies { 41 | compile fileTree(dir: 'libs', include: ['*.jar']) 42 | compile 'com.android.support:appcompat-v7:23.0.1' 43 | compile 'com.android.support:support-v4:23.0.1' 44 | compile 'javax.inject:javax.inject:1@jar' 45 | provided 'com.squareup.dagger:dagger-compiler:1.2.+' 46 | compile 'com.squareup.dagger:dagger:1.2.+' 47 | compile 'com.jakewharton:butterknife:5.1.+' 48 | compile 'com.github.pedrovgs:renderers:1.1.+' 49 | compile 'com.squareup.picasso:picasso:2.3.+' 50 | compile 'com.github.pedrovgs:draggablepanel:1.+' 51 | } 52 | 53 | sonarRunner { 54 | sonarProperties { 55 | property "sonar.projectKey", "EffectiveAndroidUI" 56 | property "sonar.projectName", "EffectiveAndroidUI" 57 | property "sonar.projectVersion", "1.0" 58 | property "sonar.language", "java" 59 | property "sonar.sources", "src/main/java" 60 | property "sonar.binaries", "build" 61 | } 62 | } 63 | 64 | task checkstyle(type: Checkstyle) { 65 | configFile file('../config/checkstyle/checkstyle.xml') 66 | source 'src/main/java' 67 | include '**/*.java' 68 | exclude '**/gen/**' 69 | 70 | classpath = files() 71 | } 72 | 73 | task pmd(type: Pmd) { 74 | ignoreFailures = false 75 | ruleSetFiles = files("${project.rootDir}/config/pmd/pmd-ruleset.xml") 76 | ruleSets = [] 77 | 78 | source 'src' 79 | include '**/*.java' 80 | exclude '**/gen/**' 81 | 82 | reports { 83 | xml.enabled = false 84 | html.enabled = true 85 | xml { 86 | destination "$project.buildDir/reports/pmd/pmd.xml" 87 | } 88 | html { 89 | destination "$project.buildDir/reports/pmd/pmd.html" 90 | } 91 | } 92 | } 93 | 94 | task findbugs(type: FindBugs) { 95 | ignoreFailures = false 96 | effort = "max" 97 | excludeFilter = new File("${project.rootDir}/config/findbugs/findbugs-excludes.xml") 98 | classes = files("${project.rootDir}/app/build/intermediates/classes") 99 | 100 | source 'src' 101 | include '**/*.java' 102 | exclude '**/gen/**' 103 | 104 | reports { 105 | xml.enabled = false 106 | html.enabled = true 107 | xml { 108 | destination "$project.buildDir/reports/findbugs/findbugs.xml" 109 | } 110 | html { 111 | destination "$project.buildDir/reports/findbugs/findbugs.html" 112 | } 113 | } 114 | 115 | classpath = files() 116 | } -------------------------------------------------------------------------------- /app/proguard-rules.txt: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Applications/Android Studio.app/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the ProGuard 5 | # include property in project.properties. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 18 | 19 | 20 | 21 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 37 | 38 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /app/src/main/app_icon-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrovgs/EffectiveAndroidUI/8544d91c73cf4954892c7abd0151e6f636429860/app/src/main/app_icon-web.png -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/TvShowsApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui; 17 | 18 | import android.app.Application; 19 | import com.github.pedrovgs.effectiveandroidui.di.RootModule; 20 | import dagger.ObjectGraph; 21 | import java.util.List; 22 | 23 | /** 24 | * Android Application extension created to get the control of the application lifecycle. 25 | *

26 | * This project is using Dependency Injection based on Dagger as dependency injector. The 27 | * ObjectGraph field used in this class is the dependency container that is going to provide every 28 | * dependency declared in Dagger modules. Take a look to BaseActivit to see how the Activity scope 29 | * injection works using the plus method implemented here. 30 | * 31 | * @author Pedro Vicente Gómez Sánchez 32 | */ 33 | public class TvShowsApplication extends Application { 34 | 35 | private ObjectGraph objectGraph; 36 | 37 | @Override 38 | public void onCreate() { 39 | super.onCreate(); 40 | initializeDependencyInjector(); 41 | } 42 | 43 | /* 44 | * We could use this code to enable or disable night mode or eve use a auto night mode. 45 | * But to use this feature we have to enable car mode and the UX is not the expected :S 46 | * If you enable car mode the application is going to show a persistent notification! 47 | * 48 | * Use this method inside the onCreate and create a new "values-night" directory with some 49 | * color changes to show how it works. 50 | * 51 | */ 52 | @SuppressWarnings("PMD.UnusedPrivateMethod") 53 | private void initializeUiManager() { 54 | /* 55 | * UiModeManager uiModeManager = (UiModeManager) getSystemService(UI_MODE_SERVICE); 56 | * uiModeManager.enableCarMode(0); 57 | * uiModeManager.setNightMode(UiModeManager.MODE_NIGHT_AUTO); 58 | */ 59 | } 60 | 61 | /** 62 | * Inject every dependency declared in the object with the @Inject annotation if the dependency 63 | * has been already declared in a module and already initialized by Dagger. 64 | * 65 | * @param object to inject. 66 | */ 67 | public void inject(Object object) { 68 | objectGraph.inject(object); 69 | } 70 | 71 | /** 72 | * Extend the dependency container graph will new dependencies provided by the modules passed as 73 | * arguments. 74 | * 75 | * @param modules used to populate the dependency container. 76 | */ 77 | public ObjectGraph plus(List modules) { 78 | if (modules == null) { 79 | throw new IllegalArgumentException( 80 | "You can't plus a null module, review your getModules() implementation"); 81 | } 82 | return objectGraph.plus(modules.toArray()); 83 | } 84 | 85 | private void initializeDependencyInjector() { 86 | objectGraph = ObjectGraph.create(new RootModule(this)); 87 | objectGraph.inject(this); 88 | objectGraph.injectStatics(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/di/ActivityContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.di; 17 | 18 | import java.lang.annotation.Retention; 19 | import javax.inject.Qualifier; 20 | 21 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 22 | 23 | /** 24 | * Annotation created to improve Context injection. This annotation is used with @Inject for 25 | * Context class to return the current Activity context. 26 | * 27 | * This annotation can be replaced with a @Named annotation, but configure before the 28 | * ActivityModule. 29 | * 30 | * @author Pedro Vicente Gómez Sánchez 31 | */ 32 | @Qualifier @Retention(RUNTIME) 33 | public @interface ActivityContext { 34 | } 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/di/ActivityModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.di; 17 | 18 | import android.app.Activity; 19 | import android.content.Context; 20 | import dagger.Module; 21 | import dagger.Provides; 22 | 23 | /** 24 | * Dagger module created to provide some common activity scope depdendencies as @ActivityContext. 25 | * This module is going to be added to the graph generated for every activity while the activity 26 | * creation lifecycle. 27 | * 28 | * @author Pedro Vicente Gómez Sánchez 29 | */ 30 | @Module(library = true) public final class ActivityModule { 31 | 32 | private final Activity activity; 33 | 34 | public ActivityModule(Activity activity) { 35 | this.activity = activity; 36 | } 37 | 38 | @ActivityContext @Provides Context provideActivityContext() { 39 | return activity; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/di/RootModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.di; 17 | 18 | import android.content.Context; 19 | import android.view.LayoutInflater; 20 | import com.github.pedrovgs.effectiveandroidui.TvShowsApplication; 21 | import com.github.pedrovgs.effectiveandroidui.domain.TvShowsModule; 22 | import com.github.pedrovgs.effectiveandroidui.executor.ExecutorModule; 23 | import dagger.Module; 24 | import dagger.Provides; 25 | 26 | /** 27 | * Dagger module created to work as junction of every module with an application scope. 28 | * 29 | * This module provides every application scope dependencies related with the AndroidSDK. 30 | * 31 | * @author Pedro Vicente Gómez Sánchez 32 | */ 33 | 34 | @Module( 35 | includes = { 36 | ExecutorModule.class, TvShowsModule.class, 37 | }, 38 | injects = { 39 | TvShowsApplication.class 40 | }, library = true) 41 | public final class RootModule { 42 | 43 | private final Context context; 44 | 45 | public RootModule(Context context) { 46 | this.context = context; 47 | } 48 | 49 | @Provides Context provideApplicationContext() { 50 | return context; 51 | } 52 | 53 | @Provides LayoutInflater provideLayoutInflater() { 54 | return LayoutInflater.from(context); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/domain/GetTvShowById.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.domain; 17 | 18 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.TvShow; 19 | 20 | /** 21 | * Get a TvShow given a TvShow identifier. Return the result using a Callback. 22 | * 23 | * This interactor can execute onTvShowNotFound Callback method if there is no any tv show that 24 | * matches with the tvShowId passed as parameter. Other possible result is the execution of 25 | * OnConnectionError when there is no internet connection and the client code executes this 26 | * interactor. 27 | * 28 | * @author Pedro Vicente Gómez Sánchez 29 | */ 30 | public interface GetTvShowById { 31 | 32 | interface Callback { 33 | void onTvShowLoaded(final TvShow tvShow); 34 | 35 | void onTvShowNotFound(); 36 | 37 | void onConnectionError(); 38 | } 39 | 40 | void execute(final String tvShowId, final Callback callback); 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/domain/GetTvShowByIdInteractor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.domain; 17 | 18 | import com.github.pedrovgs.effectiveandroidui.domain.exception.TvShowNotFoundException; 19 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.Catalog; 20 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.TvShow; 21 | import com.github.pedrovgs.effectiveandroidui.executor.Executor; 22 | import com.github.pedrovgs.effectiveandroidui.executor.Interactor; 23 | import com.github.pedrovgs.effectiveandroidui.executor.MainThread; 24 | import com.github.pedrovgs.effectiveandroidui.util.RandomUtils; 25 | import com.github.pedrovgs.effectiveandroidui.util.StringUtils; 26 | import javax.inject.Inject; 27 | 28 | /** 29 | * GetTvShowById implementation. This interactor will go out of the UI thread using the 30 | * executor, then will get a TvShow from the Catalog using the TvShow identifier and will return 31 | * the result over the main thread using a callback and MainThread dependency. 32 | * 33 | * This application is a sample about how to work effectively on Android, this interactor will 34 | * return an error randomly. 35 | * 36 | * This interactor also contains a little delay to simulate a internal http request. 37 | * 38 | * @author Pedro Vicente Gómez Sánchez 39 | */ 40 | class GetTvShowByIdInteractor implements Interactor, GetTvShowById { 41 | 42 | private static final int PERCENTAGE_OF_FAILS = 50; 43 | private static final long WAIT_TIME = 1500; 44 | 45 | private final Executor executor; 46 | private final MainThread mainThread; 47 | private final Catalog catalog; 48 | 49 | private String tvShowId; 50 | private Callback callback; 51 | 52 | @Inject GetTvShowByIdInteractor(Executor executor, MainThread mainThread, Catalog catalog) { 53 | this.executor = executor; 54 | this.mainThread = mainThread; 55 | this.catalog = catalog; 56 | } 57 | 58 | @Override public void execute(final String tvShowId, final Callback callback) { 59 | validateArguments(callback, tvShowId); 60 | this.callback = callback; 61 | this.tvShowId = tvShowId; 62 | this.executor.run(this); 63 | } 64 | 65 | @Override public void run() { 66 | waitToDoThisSampleMoreInteresting(); 67 | 68 | if (haveToShowError()) { 69 | notifyConnectionError(); 70 | } else { 71 | searchTvShow(); 72 | } 73 | } 74 | 75 | /** 76 | * To simulate a we are getting the TvShows data from internet we are going to force a 1.5 77 | * seconds 78 | * delay using Thread.sleep. 79 | */ 80 | private void waitToDoThisSampleMoreInteresting() { 81 | try { 82 | Thread.sleep(WAIT_TIME); 83 | } catch (InterruptedException e) { 84 | //Empty 85 | } 86 | } 87 | 88 | private boolean haveToShowError() { 89 | return RandomUtils.percent(PERCENTAGE_OF_FAILS); 90 | } 91 | 92 | private void searchTvShow() { 93 | TvShow tvShow = null; 94 | try { 95 | tvShow = this.catalog.getTvShowById(tvShowId); 96 | } catch (TvShowNotFoundException e) { 97 | notifyTvShowNotFound(); 98 | } 99 | notifyTvShowFound(tvShow); 100 | } 101 | 102 | private void validateArguments(Callback callback, String tvShowId) { 103 | if (StringUtils.isNullOrEmpty(tvShowId)) { 104 | throw new IllegalArgumentException("TvShowId parameter can't be null"); 105 | } 106 | if (callback == null) { 107 | throw new IllegalArgumentException("Callback parameter can't be null"); 108 | } 109 | } 110 | 111 | private void notifyConnectionError() { 112 | mainThread.post(new Runnable() { 113 | @Override public void run() { 114 | callback.onConnectionError(); 115 | } 116 | }); 117 | } 118 | 119 | private void notifyTvShowFound(final TvShow tvShow) { 120 | mainThread.post(new Runnable() { 121 | @Override public void run() { 122 | callback.onTvShowLoaded(tvShow); 123 | } 124 | }); 125 | } 126 | 127 | private void notifyTvShowNotFound() { 128 | mainThread.post(new Runnable() { 129 | @Override public void run() { 130 | callback.onTvShowNotFound(); 131 | } 132 | }); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/domain/GetTvShows.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.domain; 17 | 18 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.TvShow; 19 | import java.util.Collection; 20 | 21 | /** 22 | * Returns every available TvShow in the system. 23 | * 24 | * This interactor will not the result is the execution finish with a onConnectionError when there 25 | * is no internet connection and the client code executes this interactor. 26 | * 27 | * @author Pedro Vicente Gómez Sánchez 28 | */ 29 | public interface GetTvShows { 30 | 31 | interface Callback { 32 | void onTvShowsLoaded(final Collection tvShows); 33 | 34 | void onConnectionError(); 35 | } 36 | 37 | void execute(Callback callback); 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/domain/GetTvShowsInteractor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.domain; 17 | 18 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.Catalog; 19 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.TvShow; 20 | import com.github.pedrovgs.effectiveandroidui.executor.Executor; 21 | import com.github.pedrovgs.effectiveandroidui.executor.Interactor; 22 | import com.github.pedrovgs.effectiveandroidui.executor.MainThread; 23 | import com.github.pedrovgs.effectiveandroidui.util.RandomUtils; 24 | import java.util.Collection; 25 | import javax.inject.Inject; 26 | 27 | /** 28 | * GetTvShows interactor implementation. This interactor will go out of the UI thread using the 29 | * executor, then will get a list of TvShows from the Catalog and will return the result over the 30 | * main thread using a callback and MainThread dependency. 31 | * 32 | * This application is a sample about how to work effectively on Android, this interactor will 33 | * return an error randomly. 34 | * 35 | * This interactor also contains a little delay to simulate a internal http request. 36 | * 37 | * @author Pedro Vicente Gómez Sánchez 38 | */ 39 | class GetTvShowsInteractor implements Interactor, GetTvShows { 40 | 41 | private static final int PERCENTAGE_OF_FAILS = 50; 42 | public static final int WAIT_TIME = 1500; 43 | 44 | private final Catalog catalog; 45 | private final Executor executor; 46 | private final MainThread mainThread; 47 | 48 | private Callback callback; 49 | 50 | @Inject GetTvShowsInteractor(Catalog catalog, Executor executor, MainThread mainThread) { 51 | this.catalog = catalog; 52 | this.executor = executor; 53 | this.mainThread = mainThread; 54 | } 55 | 56 | @Override public void execute(final Callback callback) { 57 | if (callback == null) { 58 | throw new IllegalArgumentException( 59 | "Callback can't be null, the client of this interactor needs to get the response " 60 | + "in the callback"); 61 | } 62 | this.callback = callback; 63 | this.executor.run(this); 64 | } 65 | 66 | @Override public void run() { 67 | waitToDoThisSampleMoreInteresting(); 68 | 69 | if (haveToShowError()) { 70 | notifyError(); 71 | } else { 72 | Collection tvShows = catalog.getTvShows(); 73 | nofityTvShowsLoaded(tvShows); 74 | } 75 | } 76 | 77 | /** 78 | * To simulate a we are getting the TvShows data from internet we are going to force a 1.5 79 | * seconds 80 | * delay using Thread.sleep. 81 | */ 82 | private void waitToDoThisSampleMoreInteresting() { 83 | try { 84 | Thread.sleep(WAIT_TIME); 85 | } catch (InterruptedException e) { 86 | //Empty 87 | } 88 | } 89 | 90 | private boolean haveToShowError() { 91 | return RandomUtils.percent(PERCENTAGE_OF_FAILS); 92 | } 93 | 94 | private void notifyError() { 95 | mainThread.post(new Runnable() { 96 | @Override public void run() { 97 | callback.onConnectionError(); 98 | } 99 | }); 100 | } 101 | 102 | private void nofityTvShowsLoaded(final Collection tvShows) { 103 | mainThread.post(new Runnable() { 104 | @Override public void run() { 105 | callback.onTvShowsLoaded(tvShows); 106 | } 107 | }); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/domain/TvShowsModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.domain; 17 | 18 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.Catalog; 19 | import dagger.Module; 20 | import dagger.Provides; 21 | import javax.inject.Singleton; 22 | 23 | /** 24 | * Dagger module created to provide every domain dependencies as interactors or the main class of 25 | * this application: Catalog. 26 | * 27 | * @author Pedro Vicente Gómez Sánchez 28 | */ 29 | @Module(library = true, complete = false) 30 | public final class TvShowsModule { 31 | 32 | @Provides @Singleton Catalog provideCatalog() { 33 | return new Catalog(); 34 | } 35 | 36 | @Provides GetTvShows provideGetTvShowsInteractor(GetTvShowsInteractor interactor) { 37 | return interactor; 38 | } 39 | 40 | @Provides GetTvShowById provideGetTvShowbyIdInteractor(GetTvShowByIdInteractor interactor) { 41 | return interactor; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/domain/exception/TvShowNotFoundException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.domain.exception; 17 | 18 | /** 19 | * Exception throw by the application when a TvShow search can't return a valid result. 20 | * 21 | * @author Pedro Vicente Gómez Sánchez 22 | */ 23 | public class TvShowNotFoundException extends Exception { 24 | 25 | public TvShowNotFoundException() { 26 | super(); 27 | } 28 | 29 | public TvShowNotFoundException(final String message) { 30 | super(message); 31 | } 32 | 33 | public TvShowNotFoundException(final String message, final Throwable cause) { 34 | super(message, cause); 35 | } 36 | 37 | public TvShowNotFoundException(final Throwable cause) { 38 | super(cause); 39 | } 40 | } 41 | 42 | 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/domain/tvshow/Chapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.domain.tvshow; 17 | 18 | import java.io.Serializable; 19 | 20 | /** 21 | * Contains all the information related with a TvShow chapter. 22 | * 23 | * @author Pedro Vicente Gómez Sánchez 24 | */ 25 | public class Chapter implements Serializable { 26 | private static final long serialVersionUID = 8799656473845972L; 27 | 28 | private final String title; 29 | private final String publishDate; 30 | 31 | public Chapter(String title, String publishDate) { 32 | this.title = title; 33 | this.publishDate = publishDate; 34 | } 35 | 36 | /** 37 | * @return title associated to the EpisodeViewModel. 38 | */ 39 | 40 | public String getTitle() { 41 | return title; 42 | } 43 | 44 | /** 45 | * @return publish date associated to the EpisodeViewModel 46 | */ 47 | public String getPublishDate() { 48 | return publishDate; 49 | } 50 | 51 | @Override 52 | public boolean equals(Object o) { 53 | if (this == o) { 54 | return true; 55 | } 56 | if (!(o instanceof Chapter)) { 57 | return false; 58 | } 59 | 60 | Chapter chapter = (Chapter) o; 61 | 62 | if (!title.equals(chapter.title)) { 63 | return false; 64 | } 65 | 66 | return true; 67 | } 68 | 69 | @Override 70 | public int hashCode() { 71 | return title.hashCode(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/domain/tvshow/ChapterCollection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.domain.tvshow; 17 | 18 | import java.io.Serializable; 19 | import java.util.Collection; 20 | import java.util.Iterator; 21 | import java.util.LinkedHashSet; 22 | import java.util.Set; 23 | 24 | /** 25 | * Set of chapters. Contains all the chapters information for each TvShow. 26 | * 27 | * This class implements Serializable because we need to put this inside a bundle when the activity 28 | * lifecycle is restarted. This Serializable implementation could be replaced with a Parcelable 29 | * implementation if the performance is a problem. This is a sample of how, sometimes, an SDK is 30 | * going to influence our software design. 31 | * 32 | * @author Pedro Vicente Gómez Sánchez. 33 | */ 34 | public class ChapterCollection implements Iterable, Serializable { 35 | 36 | private static final long serialVersionUID = 8799656478677673292L; 37 | 38 | private final Set chapters; 39 | 40 | public ChapterCollection() { 41 | this.chapters = new LinkedHashSet(); 42 | } 43 | 44 | public Collection getChapters() { 45 | return (Collection) ((LinkedHashSet) chapters).clone(); 46 | } 47 | 48 | public void add(Chapter chapter) { 49 | this.chapters.add(chapter); 50 | } 51 | 52 | @Override public Iterator iterator() { 53 | return chapters.iterator(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/domain/tvshow/TvShow.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.domain.tvshow; 17 | 18 | import java.io.Serializable; 19 | 20 | /** 21 | * Contains all the information related with a TvShow. 22 | * 23 | * Title field works as TvShow identifier because for the sample we don't have a unique identifier 24 | * ready to be used for this sample. 25 | * 26 | * @author Pedro Vicente Gómez Sánchez 27 | */ 28 | public class TvShow implements Serializable { 29 | private static final long serialVersionUID = 8799656478674716638L; 30 | 31 | private final String title; 32 | private final String poster; 33 | private final String fanArt; 34 | private final int numberOfSeasons; 35 | private final ChapterCollection episodes; 36 | 37 | public TvShow(String title, String poster, String fanArt, int numberOfSeasons) { 38 | this.title = title; 39 | this.poster = poster; 40 | this.fanArt = fanArt; 41 | this.numberOfSeasons = numberOfSeasons; 42 | this.episodes = new ChapterCollection(); 43 | } 44 | 45 | /** 46 | * Add an episode to the tvShowViewModel. 47 | */ 48 | public void addEpisode(Chapter chapterViewModel) { 49 | episodes.add(chapterViewModel); 50 | } 51 | 52 | /** 53 | * @return the tv show title. 54 | */ 55 | public String getTitle() { 56 | return title; 57 | } 58 | 59 | /** 60 | * @return the tv show poster. 61 | */ 62 | public String getPoster() { 63 | return poster; 64 | } 65 | 66 | /** 67 | * @return the tv show fan art. 68 | */ 69 | public String getFanArt() { 70 | return fanArt; 71 | } 72 | 73 | /** 74 | * @return the tv show number of seasons. 75 | */ 76 | public int getNumberOfSeasons() { 77 | return numberOfSeasons; 78 | } 79 | 80 | /** 81 | * @return the tv show ChapterCollection. 82 | */ 83 | public ChapterCollection getChapters() { 84 | return episodes; 85 | } 86 | 87 | @Override 88 | public boolean equals(Object o) { 89 | if (this == o) { 90 | return true; 91 | } 92 | if (!(o instanceof TvShow)) { 93 | return false; 94 | } 95 | 96 | TvShow tvShow = (TvShow) o; 97 | 98 | if (!title.equals(tvShow.title)) { 99 | return false; 100 | } 101 | 102 | return true; 103 | } 104 | 105 | @Override 106 | public int hashCode() { 107 | return title.hashCode(); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/executor/Executor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.executor; 17 | 18 | /** 19 | * Executor implementation can be based on different frameworks or techniques of asynchronous 20 | * execution, but every implementation will execute the Interactor out of the UI thread. 21 | * 22 | * Use this class to execute an Interactor. 23 | * 24 | * This is just a sample implementation of how a Interactor/Executor environment can be 25 | * implemented. 26 | * Ideally interactors should not know about Executor or MainThread dependency. Interactors client 27 | * code should get a Executor instance to execute interactors. 28 | * 29 | * @author Pedro Vicente Gómez Sánchez 30 | */ 31 | public interface Executor { 32 | 33 | void run(final Interactor interactor); 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/executor/ExecutorModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.executor; 17 | 18 | import dagger.Module; 19 | import dagger.Provides; 20 | import javax.inject.Singleton; 21 | 22 | /** 23 | * Dagger module created to provide every dependency related with our execution service. Main 24 | * dependencies provided by this module are: ThreadExecutor and MainThreadImpl. 25 | * 26 | * @author Pedro Vicente Gómez Sánchez 27 | */ 28 | @Module(library = true) 29 | public final class ExecutorModule { 30 | 31 | @Provides @Singleton Executor provideExecutor(ThreadExecutor threadExecutor) { 32 | return threadExecutor; 33 | } 34 | 35 | @Provides @Singleton MainThread provideMainThread(MainThreadImpl mainThread) { 36 | return mainThread; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/executor/Interactor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.executor; 17 | 18 | /** 19 | * Common interface to every Interactor declared in the application. This interface represents a 20 | * execution unit for different use cases. 21 | * 22 | * By convention every interactor implementation will return the result using a Callback. That 23 | * callback should be executed over the UI thread. 24 | * 25 | * This is a simple Interactor implementation. Other approach to do this could be use a class 26 | * instead of an interface and create a base Interactor class that for every execution will use a 27 | * Request object and a callback implementation. 28 | * 29 | * @author Pedro Vicente Gómez Sánchez 30 | */ 31 | public interface Interactor { 32 | void run(); 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/executor/MainThread.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.executor; 17 | 18 | /** 19 | * UI thread abstraction created to change the execution context from any thread to the UI thread. 20 | * 21 | * @author Pedro Vicente Gómez Sánchez 22 | */ 23 | public interface MainThread { 24 | 25 | void post(final Runnable runnable); 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/executor/MainThreadImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.executor; 17 | 18 | import android.os.Handler; 19 | import android.os.Looper; 20 | import javax.inject.Inject; 21 | 22 | /** 23 | * MainThread implementation based on a Handler instantiated over the main looper obtained from 24 | * Looper class. 25 | * 26 | * @author Pedro Vicente Gómez Sánchez 27 | */ 28 | class MainThreadImpl implements MainThread { 29 | 30 | private Handler handler; 31 | 32 | @Inject MainThreadImpl() { 33 | this.handler = new Handler(Looper.getMainLooper()); 34 | } 35 | 36 | public void post(Runnable runnable) { 37 | handler.post(runnable); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/executor/ThreadExecutor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.executor; 17 | 18 | import java.util.concurrent.BlockingQueue; 19 | import java.util.concurrent.LinkedBlockingQueue; 20 | import java.util.concurrent.ThreadPoolExecutor; 21 | import java.util.concurrent.TimeUnit; 22 | import javax.inject.Inject; 23 | 24 | /** 25 | * Executor implementation based on ThreadPoolExecutor. ThreadPoolExecutorConfig: 26 | * 27 | * Core pool size: 3. 28 | * Max pool size: 5. 29 | * Keep alive time: 120. 30 | * Time unit: seconds. 31 | * Work queue: LinkedBlockingQueue. 32 | * 33 | * @author Pedro Vicente Gómez Sánchez 34 | */ 35 | class ThreadExecutor implements Executor { 36 | 37 | private static final int CORE_POOL_SIZE = 3; 38 | private static final int MAX_POOL_SIZE = 5; 39 | private static final int KEEP_ALIVE_TIME = 120; 40 | private static final TimeUnit TIME_UNIT = TimeUnit.SECONDS; 41 | private static final BlockingQueue WORK_QUEUE = new LinkedBlockingQueue(); 42 | 43 | private ThreadPoolExecutor threadPoolExecutor; 44 | 45 | @Inject ThreadExecutor() { 46 | int corePoolSize = CORE_POOL_SIZE; 47 | int maxPoolSize = MAX_POOL_SIZE; 48 | int keepAliveTime = KEEP_ALIVE_TIME; 49 | TimeUnit timeUnit = TIME_UNIT; 50 | BlockingQueue workQueue = WORK_QUEUE; 51 | threadPoolExecutor = 52 | new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, timeUnit, workQueue); 53 | } 54 | 55 | @Override 56 | public void run(final Interactor interactor) { 57 | if (interactor == null) { 58 | throw new IllegalArgumentException("Interactor to execute can't be null"); 59 | } 60 | threadPoolExecutor.submit(new Runnable() { 61 | @Override public void run() { 62 | interactor.run(); 63 | } 64 | }); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/activity/BaseActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.activity; 17 | 18 | import android.os.Bundle; 19 | import android.support.v7.app.ActionBarActivity; 20 | import butterknife.ButterKnife; 21 | import com.github.pedrovgs.effectiveandroidui.TvShowsApplication; 22 | import com.github.pedrovgs.effectiveandroidui.di.ActivityModule; 23 | import dagger.ObjectGraph; 24 | import java.util.List; 25 | 26 | /** 27 | * Base activity created to be extended by every activity in this application. This class provides 28 | * dependency injection configuration, ButterKnife Android library configuration and some methods 29 | * common to every activity. 30 | * 31 | * @author Pedro Vicente Gómez Sánchez 32 | */ 33 | public abstract class BaseActivity extends ActionBarActivity { 34 | 35 | private ObjectGraph activityScopeGraph; 36 | 37 | @Override protected void onCreate(Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | injectDependencies(); 40 | injectViews(); 41 | } 42 | 43 | /** 44 | * Method used to resolve dependencies provided by Dagger modules. Inject an object to provide 45 | * every @Inject annotation contained. 46 | * 47 | * @param object to inject. 48 | */ 49 | public void inject(Object object) { 50 | activityScopeGraph.inject(object); 51 | } 52 | 53 | /** 54 | * Get a list of Dagger modules with Activity scope needed to this Activity. 55 | * 56 | * @return modules with new dependencies to provide. 57 | */ 58 | protected abstract List getModules(); 59 | 60 | /** 61 | * Create a new Dagger ObjectGraph to add new dependencies using a plus operation and inject the 62 | * declared one in the activity. This new graph will be destroyed once the activity lifecycle 63 | * finish. 64 | * 65 | * This is the key of how to use Activity scope dependency injection. 66 | */ 67 | private void injectDependencies() { 68 | TvShowsApplication tvShowsApplication = (TvShowsApplication) getApplication(); 69 | List activityScopeModules = getModules(); 70 | activityScopeModules.add(new ActivityModule(this)); 71 | activityScopeGraph = tvShowsApplication.plus(activityScopeModules); 72 | inject(this); 73 | } 74 | 75 | /** 76 | * Replace every field annotated with ButterKnife annotations like @InjectView with the proper 77 | * value. 78 | */ 79 | private void injectViews() { 80 | ButterKnife.inject(this); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/activity/MainActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.activity; 17 | 18 | import android.os.Bundle; 19 | 20 | import com.github.pedrovgs.effectiveandroidui.R; 21 | import com.github.pedrovgs.effectiveandroidui.ui.fragment.TvShowDraggableFragment; 22 | import com.github.pedrovgs.effectiveandroidui.ui.fragment.TvShowFragment; 23 | import com.github.pedrovgs.effectiveandroidui.ui.presenter.TvShowUIModule; 24 | 25 | import java.util.LinkedList; 26 | import java.util.List; 27 | 28 | /** 29 | * Core activity of this application. This activity receives the launch intent and works as core of 30 | * the sample application. 31 | */ 32 | public class MainActivity extends BaseActivity { 33 | 34 | private TvShowFragment tvShowFragment; 35 | 36 | @Override 37 | protected void onCreate(Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | setContentView(R.layout.activity_main); 40 | initializeTvShowFragment(); 41 | initializeTvShowDraggableFragment(); 42 | } 43 | 44 | @Override 45 | protected List getModules() { 46 | LinkedList modules = new LinkedList<>(); 47 | modules.add(new TvShowUIModule()); 48 | return modules; 49 | } 50 | 51 | private void initializeTvShowFragment() { 52 | tvShowFragment = (TvShowFragment) getSupportFragmentManager().findFragmentById(R.id.f_tv_show); 53 | } 54 | 55 | private void initializeTvShowDraggableFragment() { 56 | TvShowDraggableFragment tvShowDraggableFragment = 57 | (TvShowDraggableFragment) getSupportFragmentManager().findFragmentById( 58 | R.id.f_tv_show_draggable); 59 | /* 60 | * If both fragments are visible we have to disable saved instance state in draggable 61 | * fragment because there are different fragment configurations in activity_main.xml 62 | * when the device is in portrait or landscape. Review layout- directory to get more 63 | * information. 64 | */ 65 | if (tvShowFragment != null && tvShowDraggableFragment != null) { 66 | tvShowDraggableFragment.disableSaveInstanceState(); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/activity/Navigator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.activity; 17 | 18 | import android.content.Context; 19 | import android.content.Intent; 20 | import android.support.v4.app.Fragment; 21 | import android.support.v4.app.FragmentActivity; 22 | import android.support.v4.app.FragmentManager; 23 | 24 | import com.github.pedrovgs.effectiveandroidui.R; 25 | import com.github.pedrovgs.effectiveandroidui.di.ActivityContext; 26 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.TvShow; 27 | import com.github.pedrovgs.effectiveandroidui.ui.fragment.TvShowDraggableFragment; 28 | import com.github.pedrovgs.effectiveandroidui.ui.fragment.TvShowFragment; 29 | 30 | import javax.inject.Inject; 31 | 32 | /** 33 | * Class created to handle all the navigation between activities. This class knows how to open 34 | * every activity in the application and provides to the client code different methods to start 35 | * activities with the information needed. 36 | *

37 | * You can use other approach based on ActionCommands if you wish. To do that review MVVM 38 | * pattern and ActionCommand interface. 39 | * 40 | * @author Pedro Vicente Gómez Sánchez 41 | */ 42 | public class Navigator { 43 | 44 | private TvShowFragment tvShowFragment; 45 | private TvShowDraggableFragment tvShowDraggableFragment; 46 | 47 | private final Context activityContext; 48 | 49 | @Inject 50 | public Navigator(@ActivityContext Context activityContext) { 51 | this.activityContext = activityContext; 52 | } 53 | 54 | /* 55 | * This method contains the key of the application navigation. If there are no fragments attached 56 | * we will launch TvShowActivity. 57 | * 58 | * If any fragment is visible on injected activity, we will load the TvShow. 59 | * 60 | * Other approach to connect fragments could be based on a Bus event implementation. But this is 61 | * only valid if you only have fragments in your activity. 62 | * 63 | */ 64 | public void openTvShowDetails(TvShow tvShow) { 65 | 66 | if (canInteractWithFragments()) { 67 | showTvShowOnTvShowDraggableFragment(tvShow); 68 | showTvShowOnTvShowFragment(tvShow); 69 | } else { 70 | openTvShowActivity(tvShow.getTitle()); 71 | } 72 | } 73 | 74 | private FragmentManager getFragmentManager() { 75 | return ((FragmentActivity) activityContext).getSupportFragmentManager(); 76 | } 77 | 78 | private boolean canInteractWithFragments() { 79 | tvShowFragment = (TvShowFragment) getFragmentManager().findFragmentById(R.id.f_tv_show); 80 | tvShowDraggableFragment = 81 | (TvShowDraggableFragment) getFragmentManager().findFragmentById(R.id.f_tv_show_draggable); 82 | 83 | return tvShowDraggableFragment != null || tvShowFragment != null; 84 | } 85 | 86 | private void showTvShowOnTvShowDraggableFragment(TvShow tvShow) { 87 | if (isFragmentAvailable(tvShowDraggableFragment)) { 88 | tvShowDraggableFragment.showTvShow(tvShow.getTitle()); 89 | } 90 | } 91 | 92 | private void showTvShowOnTvShowFragment(TvShow tvShow) { 93 | if (isFragmentAvailable(tvShowFragment)) { 94 | tvShowFragment.showTvShow(tvShow.getTitle()); 95 | } 96 | } 97 | 98 | /** 99 | * Check if the fragment is ready to be notified of a new TvShow loaded. 100 | * 101 | * @return true if the Fragment instance is not null and is attached. 102 | */ 103 | private boolean isFragmentAvailable(Fragment fragment) { 104 | return fragment != null && fragment.isAdded(); 105 | } 106 | 107 | /** 108 | * Open TvShowActivity using a tvShowId. 109 | */ 110 | public void openTvShowActivity(final String tvShowId) { 111 | Intent intent = TvShowActivity.getLaunchIntent(activityContext, tvShowId); 112 | startActivity(intent); 113 | } 114 | 115 | private void startActivity(Intent intent) { 116 | activityContext.startActivity(intent); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/activity/TvShowActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.activity; 17 | 18 | import android.content.Context; 19 | import android.content.Intent; 20 | import android.os.Bundle; 21 | import com.github.pedrovgs.effectiveandroidui.R; 22 | import com.github.pedrovgs.effectiveandroidui.ui.fragment.TvShowFragment; 23 | import com.github.pedrovgs.effectiveandroidui.ui.presenter.TvShowUIModule; 24 | import com.github.pedrovgs.effectiveandroidui.util.StringUtils; 25 | import java.util.LinkedList; 26 | import java.util.List; 27 | 28 | /** 29 | * Activity created to show a TvShow using TvShowFragment. This activity is going to be launched 30 | * only in Android 2.X versions. 31 | * 32 | * This activity has to be launched using a tv show id as extra or will throw an exception. 33 | * 34 | * This Activity will implement two good practices: 35 | * 36 | * - Implement a method to create the intent needed to start this activity. 37 | * - This activity will be thrown from Navigator class used in TvShowCatalogPresenter instead of 38 | * start the activity from other activity. 39 | * 40 | * @author Pedro Vicente Gómez Sánchez 41 | */ 42 | public class TvShowActivity extends BaseActivity { 43 | 44 | private static final String EXTRA_TV_SHOW_ID = "extra_tv_show_id"; 45 | 46 | private String tvShowId; 47 | 48 | /** 49 | * Generates the intent neede by the client code to launch this activity. This method is a sample 50 | * of who to avoid duplicate this code by all the application. 51 | */ 52 | public static Intent getLaunchIntent(final Context context, final String tvShowId) { 53 | if (StringUtils.isNullOrEmpty(tvShowId)) { 54 | throwIllegalArgumentException(); 55 | } 56 | Intent intent = new Intent(context, TvShowActivity.class); 57 | return intent.putExtra(EXTRA_TV_SHOW_ID, tvShowId); 58 | } 59 | 60 | @Override protected void onCreate(Bundle savedInstanceState) { 61 | super.onCreate(savedInstanceState); 62 | setContentView(R.layout.activity_tv_show); 63 | mapExtras(); 64 | initializeFragment(); 65 | } 66 | 67 | @Override protected List getModules() { 68 | List modules = new LinkedList(); 69 | modules.add(new TvShowUIModule()); 70 | return modules; 71 | } 72 | 73 | /** 74 | * As TvShowId is mandatory if there is no an extra inside the bundle that launches this activity 75 | * we are going to throw a new IllegalArgumentException. 76 | */ 77 | private void mapExtras() { 78 | Bundle extras = getIntent().getExtras(); 79 | if (extras == null) { 80 | throwIllegalArgumentException(); 81 | } 82 | tvShowId = extras.getString(EXTRA_TV_SHOW_ID); 83 | if (StringUtils.isNullOrEmpty(tvShowId)) { 84 | throwIllegalArgumentException(); 85 | } 86 | } 87 | 88 | /** 89 | * Propagates the tv show identifier to the fragment. 90 | */ 91 | private void initializeFragment() { 92 | TvShowFragment tvShowFragment = 93 | (TvShowFragment) getSupportFragmentManager().findFragmentById(R.id.f_tv_show); 94 | tvShowFragment.showTvShow(tvShowId); 95 | } 96 | 97 | private static void throwIllegalArgumentException() { 98 | throw new IllegalArgumentException( 99 | "TvShowActivity has to be launched using a TvShow identifier as extra"); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/fragment/BaseFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.fragment; 17 | 18 | import android.app.Activity; 19 | import android.os.Bundle; 20 | import android.support.v4.app.Fragment; 21 | import android.view.LayoutInflater; 22 | import android.view.View; 23 | import android.view.ViewGroup; 24 | import butterknife.ButterKnife; 25 | import com.github.pedrovgs.effectiveandroidui.ui.activity.BaseActivity; 26 | 27 | /** 28 | * Base fragment created to be extended by every fragment in this application. This class provides 29 | * dependency injection configuration, ButterKnife Android library configuration and some methods 30 | * common to every fragment. 31 | * 32 | * @author Pedro Vicente Gómez Sánchez 33 | */ 34 | public abstract class BaseFragment extends Fragment { 35 | 36 | @Override public void onAttach(Activity activity) { 37 | super.onAttach(activity); 38 | injectDependencies(); 39 | } 40 | 41 | @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, 42 | Bundle savedInstanceState) { 43 | return inflater.inflate(getFragmentLayout(), container, false); 44 | } 45 | 46 | @Override public void onViewCreated(View view, Bundle savedInstanceState) { 47 | super.onViewCreated(view, savedInstanceState); 48 | injectViews(view); 49 | } 50 | 51 | /** 52 | * Every fragment has to inflate a layout in the onCreateView method. We have added this method to 53 | * avoid duplicate all the inflate code in every fragment. You only have to return the layout to 54 | * inflate in this method when extends BaseFragment. 55 | */ 56 | protected abstract int getFragmentLayout(); 57 | 58 | /** 59 | * Replace every field annotated using @Inject annotation with the provided dependency specified 60 | * inside a Dagger module value. 61 | */ 62 | private void injectDependencies() { 63 | ((BaseActivity) getActivity()).inject(this); 64 | } 65 | 66 | /** 67 | * Replace every field annotated with ButterKnife annotations like @InjectView with the proper 68 | * value. 69 | * 70 | * @param view to extract each widget injected in the fragment. 71 | */ 72 | private void injectViews(final View view) { 73 | ButterKnife.inject(this, view); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/fragment/TvShowCatalogFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.fragment; 17 | 18 | import android.os.Bundle; 19 | import android.view.View; 20 | import android.widget.GridView; 21 | import android.widget.ProgressBar; 22 | import butterknife.InjectView; 23 | import com.github.pedrovgs.effectiveandroidui.R; 24 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.TvShow; 25 | import com.github.pedrovgs.effectiveandroidui.ui.presenter.TvShowCatalogPresenter; 26 | import com.github.pedrovgs.effectiveandroidui.ui.renderer.tvshow.TvShowCollection; 27 | import com.github.pedrovgs.effectiveandroidui.ui.renderer.tvshow.TvShowRendererAdapterFactory; 28 | import com.github.pedrovgs.effectiveandroidui.util.ToastUtils; 29 | import com.pedrogomez.renderers.RendererAdapter; 30 | import java.util.Collection; 31 | import javax.inject.Inject; 32 | 33 | /** 34 | * Fragment created to show a collection of TvShows inside a GridView. 35 | * 36 | * This Fragment uses a Model View Presenter implementation to implement all the presentation 37 | * logic. Review TvShowCatalogPresenter to get more info about the implementation. 38 | * 39 | * This fragment is going to notify to the activity every event that has to go out of this 40 | * fragment. TvShowCatalogFragmentListener is the interface declared by this fragment and 41 | * implemented by the activity that contains this fragment. This is a common implementation used to 42 | * notify user actions to the fragment owner. Other approach could be based on a Bus event 43 | * implementation. 44 | * 45 | * @author Pedro Vicente Gómez Sánchez 46 | */ 47 | public class TvShowCatalogFragment extends BaseFragment implements TvShowCatalogPresenter.View { 48 | 49 | private static final String EXTRA_TV_SHOW_CATALOG = "extra_tv_show_catalog"; 50 | 51 | @Inject TvShowCatalogPresenter tvShowCatalogPresenter; 52 | @Inject TvShowRendererAdapterFactory tvShowRendererAdapterFactory; 53 | 54 | private RendererAdapter adapter; 55 | private TvShowCollection tvShows = new TvShowCollection(); 56 | 57 | @InjectView(R.id.pb_loading) ProgressBar pb_loading; 58 | @InjectView(R.id.gv_tv_shows) GridView gv_tv_shows; 59 | @InjectView(R.id.v_empty_case) View v_empty_case; 60 | 61 | @Override public void onViewCreated(View view, Bundle savedInstanceState) { 62 | super.onViewCreated(view, savedInstanceState); 63 | initializeGridView(); 64 | tvShowCatalogPresenter.setView(this); 65 | tvShowCatalogPresenter.initialize(); 66 | } 67 | 68 | @Override public void onResume() { 69 | super.onResume(); 70 | tvShowCatalogPresenter.resume(); 71 | } 72 | 73 | @Override public void onPause() { 74 | super.onPause(); 75 | tvShowCatalogPresenter.pause(); 76 | } 77 | 78 | /** 79 | * We want to keep the catalog loaded in this fragment even if the user rotates the device. We 80 | * are 81 | * using different configurations for landscape and portrait and we have to use this approach 82 | * instead of onConfigurationChanges. 83 | */ 84 | @Override public void onSaveInstanceState(Bundle outState) { 85 | super.onSaveInstanceState(outState); 86 | outState.putSerializable(EXTRA_TV_SHOW_CATALOG, tvShowCatalogPresenter.getCurrentTvShows()); 87 | } 88 | 89 | @Override public void onViewStateRestored(Bundle savedInstanceState) { 90 | super.onViewStateRestored(savedInstanceState); 91 | if (savedInstanceState != null) { 92 | final TvShowCollection tvShowCollection = 93 | (TvShowCollection) savedInstanceState.getSerializable(EXTRA_TV_SHOW_CATALOG); 94 | updatePresenterWithSavedTvShow(tvShowCollection); 95 | } 96 | } 97 | 98 | @Override public void hideLoading() { 99 | pb_loading.setVisibility(View.GONE); 100 | } 101 | 102 | @Override public void showLoading() { 103 | pb_loading.setVisibility(View.VISIBLE); 104 | } 105 | 106 | @Override public void renderVideos(final Collection tvShows) { 107 | this.tvShows.clear(); 108 | this.tvShows.addAll(tvShows); 109 | refreshAdapter(); 110 | } 111 | 112 | @Override public void updateTitleWithCountOfTvShows(final int counter) { 113 | String actionBarTitle = getString(R.string.app_name_with_chapter_counter, counter); 114 | getActivity().setTitle(actionBarTitle); 115 | } 116 | 117 | @Override public void showConnectionErrorMessage() { 118 | String connectionError = getString(R.string.connection_error_message); 119 | ToastUtils.showShortMessage(connectionError, getActivity()); 120 | } 121 | 122 | @Override public void showEmptyCase() { 123 | v_empty_case.setVisibility(View.VISIBLE); 124 | } 125 | 126 | @Override public void showDefaultTitle() { 127 | getActivity().setTitle(R.string.app_name); 128 | } 129 | 130 | @Override public void showTvShowTitleAsMessage(TvShow tvShow) { 131 | ToastUtils.showShortMessage(tvShow.getTitle(), getActivity()); 132 | } 133 | 134 | @Override public boolean isReady() { 135 | return isAdded(); 136 | } 137 | 138 | @Override public boolean isAlreadyLoaded() { 139 | return adapter.getCount() > 0; 140 | } 141 | 142 | @Override protected int getFragmentLayout() { 143 | return R.layout.fragment_tv_shows; 144 | } 145 | 146 | private void initializeGridView() { 147 | adapter = tvShowRendererAdapterFactory.getTvShowRendererAdapter(tvShows); 148 | gv_tv_shows.setAdapter(adapter); 149 | } 150 | 151 | private void updatePresenterWithSavedTvShow(TvShowCollection tvShowCollection) { 152 | if (tvShowCollection != null) { 153 | tvShowCatalogPresenter.loadCatalog(tvShowCollection); 154 | } 155 | } 156 | 157 | private void refreshAdapter() { 158 | adapter.notifyDataSetChanged(); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/fragment/TvShowDraggableFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.fragment; 17 | 18 | import android.os.Bundle; 19 | import android.view.LayoutInflater; 20 | import android.view.View; 21 | import android.widget.ImageView; 22 | import android.widget.ListView; 23 | import android.widget.ProgressBar; 24 | import android.widget.TextView; 25 | import butterknife.InjectView; 26 | import com.github.pedrovgs.DraggableListener; 27 | import com.github.pedrovgs.DraggableView; 28 | import com.github.pedrovgs.effectiveandroidui.R; 29 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.ChapterCollection; 30 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.TvShow; 31 | import com.github.pedrovgs.effectiveandroidui.ui.presenter.TvShowPresenter; 32 | import com.github.pedrovgs.effectiveandroidui.ui.renderer.chapter.ChapterAdapteeCollection; 33 | import com.github.pedrovgs.effectiveandroidui.ui.renderer.chapter.ChapterRendererAdapter; 34 | import com.github.pedrovgs.effectiveandroidui.ui.renderer.chapter.ChapterRendererAdapterFactory; 35 | import com.github.pedrovgs.effectiveandroidui.util.ToastUtils; 36 | import com.squareup.picasso.Picasso; 37 | import javax.inject.Inject; 38 | 39 | /** 40 | * Fragment created to show a TvShows using DraggablePanel library. 41 | * 42 | * This Fragment uses a Model View Presenter implementation to implement all the presentation 43 | * logic. Review TvShowPresenter to get more info about the implementation. 44 | * 45 | * @author Pedro Vicente Gómez Sánchez 46 | */ 47 | public class TvShowDraggableFragment extends BaseFragment implements TvShowPresenter.View { 48 | 49 | private static final String EXTRA_TV_SHOW = "extra_tv_show"; 50 | 51 | @Inject TvShowPresenter tvShowPresenter; 52 | @Inject ChapterRendererAdapterFactory chapterRendererAdapterFactory; 53 | 54 | private ChapterRendererAdapter adapter; 55 | private ChapterAdapteeCollection chapterAdapteeCollection = new ChapterAdapteeCollection(); 56 | 57 | private boolean useSaveInstanceState = true; 58 | 59 | @InjectView(R.id.draggable_view) DraggableView draggable_view; 60 | @InjectView(R.id.iv_fan_art) ImageView iv_fan_art; 61 | @InjectView(R.id.lv_chapters) ListView lv_chapters; 62 | @InjectView(R.id.pb_loading) ProgressBar pb_loading; 63 | 64 | private TextView header_tv_show_chapters; 65 | 66 | @Override public void onViewCreated(View view, Bundle savedInstanceState) { 67 | super.onViewCreated(view, savedInstanceState); 68 | tvShowPresenter.setView(this); 69 | initializeDraggableView(); 70 | initializeListView(); 71 | } 72 | 73 | public void disableSaveInstanceState() { 74 | useSaveInstanceState = false; 75 | } 76 | 77 | private void initializeListView() { 78 | header_tv_show_chapters = (TextView) LayoutInflater.from(getActivity()) 79 | .inflate(R.layout.header_tv_show_chapters, null); 80 | lv_chapters.addHeaderView(header_tv_show_chapters); 81 | adapter = (ChapterRendererAdapter) chapterRendererAdapterFactory.getChapterRendererAdapter( 82 | chapterAdapteeCollection); 83 | lv_chapters.setAdapter(adapter); 84 | } 85 | 86 | public void showTvShow(final String tvShowId) { 87 | if (isAdded()) { 88 | tvShowPresenter.loadTvShow(tvShowId); 89 | } 90 | } 91 | 92 | @Override public void showLoading() { 93 | pb_loading.setVisibility(View.VISIBLE); 94 | } 95 | 96 | @Override public void showFanArt(final String tvShowFanArtUrl) { 97 | iv_fan_art.setVisibility(View.VISIBLE); 98 | Picasso.with(getActivity()) 99 | .load(tvShowFanArtUrl) 100 | .placeholder(R.color.main_color) 101 | .into(iv_fan_art); 102 | } 103 | 104 | @Override public void showTvShowTitle(final String tvShowTitle) { 105 | String tvShowHeaderTitle = getString(R.string.tv_show_title, tvShowTitle); 106 | header_tv_show_chapters.setText(tvShowHeaderTitle); 107 | } 108 | 109 | @Override public boolean isReady() { 110 | return isAdded(); 111 | } 112 | 113 | @Override public boolean isAlreadyLoaded() { 114 | return adapter.getCount() > 0; 115 | } 116 | 117 | @Override public void showChapters(ChapterCollection chapters) { 118 | chapterAdapteeCollection.clear(); 119 | chapterAdapteeCollection.addAll(chapters.getChapters()); 120 | adapter.notifyDataSetChanged(); 121 | } 122 | 123 | @Override public void hideLoading() { 124 | pb_loading.setVisibility(View.GONE); 125 | } 126 | 127 | @Override public void showTvShowNotFoundMessage() { 128 | String tvShowNotFoundMessage = getString(R.string.tv_show_not_found); 129 | ToastUtils.showShortMessage(tvShowNotFoundMessage, getActivity()); 130 | } 131 | 132 | @Override public void showConnectionErrorMessage() { 133 | String connectionErrorMessage = getString(R.string.connection_error_message); 134 | ToastUtils.showError(connectionErrorMessage, getActivity()); 135 | } 136 | 137 | @Override public void showTvShow() { 138 | draggable_view.setVisibility(View.VISIBLE); 139 | draggable_view.maximize(); 140 | } 141 | 142 | @Override public void onSaveInstanceState(Bundle outState) { 143 | super.onSaveInstanceState(outState); 144 | if (useSaveInstanceState) { 145 | outState.putSerializable(EXTRA_TV_SHOW, tvShowPresenter.getCurrentTvShow()); 146 | } 147 | } 148 | 149 | @Override public void onViewStateRestored(Bundle savedInstanceState) { 150 | super.onViewStateRestored(savedInstanceState); 151 | if (savedInstanceState != null) { 152 | final TvShow tvShow = (TvShow) savedInstanceState.getSerializable(EXTRA_TV_SHOW); 153 | updatePresenterWithSavedTvShow(tvShow); 154 | } 155 | } 156 | 157 | private void updatePresenterWithSavedTvShow(final TvShow tvShow) { 158 | if (tvShow != null) { 159 | draggable_view.post(new Runnable() { 160 | @Override public void run() { 161 | tvShowPresenter.loadTvShow(tvShow); 162 | } 163 | }); 164 | } 165 | } 166 | 167 | @Override protected int getFragmentLayout() { 168 | return R.layout.fragment_draggable_tv_show; 169 | } 170 | 171 | private void initializeDraggableView() { 172 | draggable_view.post(new Runnable() { 173 | @Override 174 | public void run() { 175 | draggable_view.closeToRight(); 176 | } 177 | }); 178 | draggable_view.setDraggableListener(new DraggableListener() { 179 | @Override public void onMaximized() { 180 | //Empty 181 | } 182 | 183 | @Override public void onMinimized() { 184 | //Empty 185 | } 186 | 187 | @Override public void onClosedToLeft() { 188 | tvShowPresenter.tvShowClosed(); 189 | } 190 | 191 | @Override public void onClosedToRight() { 192 | tvShowPresenter.tvShowClosed(); 193 | } 194 | }); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/fragment/TvShowFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.fragment; 17 | 18 | import android.app.Activity; 19 | import android.os.Bundle; 20 | import android.view.LayoutInflater; 21 | import android.view.View; 22 | import android.widget.ImageView; 23 | import android.widget.ListView; 24 | import android.widget.ProgressBar; 25 | import android.widget.TextView; 26 | import butterknife.InjectView; 27 | import butterknife.OnClick; 28 | import com.github.pedrovgs.effectiveandroidui.R; 29 | import com.github.pedrovgs.effectiveandroidui.ui.renderer.chapterviewmodel.ChapterViewModelCollection; 30 | import com.github.pedrovgs.effectiveandroidui.ui.renderer.chapterviewmodel.ChapterViewModelRendererAdapter; 31 | import com.github.pedrovgs.effectiveandroidui.ui.renderer.chapterviewmodel.ChapterViewModelRendererAdapterFactory; 32 | import com.github.pedrovgs.effectiveandroidui.ui.viewmodel.ChapterViewModel; 33 | import com.github.pedrovgs.effectiveandroidui.ui.viewmodel.TvShowViewModel; 34 | import com.github.pedrovgs.effectiveandroidui.ui.viewmodel.action.ActionCommand; 35 | import com.github.pedrovgs.effectiveandroidui.util.ToastUtils; 36 | import com.squareup.picasso.Picasso; 37 | import java.util.List; 38 | import javax.inject.Inject; 39 | 40 | /** 41 | * Fragment created to show a TvShows. This fragment is going to be used in the tablet version. 42 | * 43 | * This Fragment uses a Model View View Model implementation to implement all the presentation 44 | * logic. Review TvShowViewModel to get more info about the implementation. 45 | * 46 | * This fragment contains duplicate code that you can see in TvShowDraggableFragment because I want 47 | * to show two different approaches to work over the Android UI layer with different patterns (MVVM 48 | * and MVP) and I don't want to mix both fragments in a hierarchy to clarify different 49 | * implementations. 50 | * 51 | * @author Pedro Vicente Gómez Sánchez 52 | */ 53 | @SuppressWarnings("ALL") public class TvShowFragment extends BaseFragment 54 | implements TvShowViewModel.Listener { 55 | 56 | @Inject TvShowViewModel tvShowViewModel; 57 | @Inject ChapterViewModelRendererAdapterFactory chapterRendererAdapterFactory; 58 | 59 | private ChapterViewModelRendererAdapter adapter; 60 | private ChapterViewModelCollection chapterAdapteeCollection = new ChapterViewModelCollection(); 61 | 62 | @InjectView(R.id.iv_fan_art) ImageView iv_fan_art; 63 | @InjectView(R.id.lv_chapters) ListView lv_chapters; 64 | @InjectView(R.id.pb_loading) ProgressBar pb_loading; 65 | @InjectView(R.id.v_empty_case) View v_empty_case; 66 | 67 | private TextView header_tv_show_chapters; 68 | 69 | @Override public void onViewCreated(View view, Bundle savedInstanceState) { 70 | super.onViewCreated(view, savedInstanceState); 71 | initializeListView(); 72 | bindViewModel(); 73 | } 74 | 75 | @Override public void onAttach(Activity activity) { 76 | super.onAttach(activity); 77 | tvShowViewModel.setReady(true); 78 | } 79 | 80 | @Override public void onDetach() { 81 | super.onDetach(); 82 | tvShowViewModel.setReady(false); 83 | } 84 | 85 | public void showTvShow(final String tvShowId) { 86 | if (isAdded()) { 87 | tvShowViewModel.loadTvShow(tvShowId); 88 | } 89 | } 90 | 91 | @Override protected int getFragmentLayout() { 92 | return R.layout.fragment_tv_show; 93 | } 94 | 95 | @Override public void onFanArtLoaded(final String fanArt) { 96 | Picasso.with(getActivity()).load(fanArt).placeholder(R.color.main_color).into(iv_fan_art); 97 | } 98 | 99 | @Override public void onTvShowTitleLoaded(final String tvShowTitle) { 100 | String tvShowHeaderTitle = getString(R.string.tv_show_title, tvShowTitle); 101 | header_tv_show_chapters.setText(tvShowHeaderTitle); 102 | } 103 | 104 | @Override public void onChaptersLoaded(List chapters) { 105 | chapterAdapteeCollection.clear(); 106 | chapterAdapteeCollection.addAll(chapters); 107 | adapter.notifyDataSetChanged(); 108 | } 109 | 110 | @Override public void onVisibilityChanged(final boolean visible) { 111 | int visibility = getVisibility(visible); 112 | iv_fan_art.setVisibility(visibility); 113 | lv_chapters.setVisibility(visibility); 114 | } 115 | 116 | @Override public void onLoadVisibilityChanged(final boolean visible) { 117 | pb_loading.setVisibility(getVisibility(visible)); 118 | } 119 | 120 | @Override public void onEmptyCaseVisibilityChanged(final boolean visible) { 121 | v_empty_case.setVisibility(getVisibility(visible)); 122 | } 123 | 124 | @Override public void onTvShowMessageNotFound() { 125 | ToastUtils.showError(getString(R.string.tv_show_not_found), getActivity()); 126 | } 127 | 128 | @Override public void onConnectionErrorMessageNotFound() { 129 | ToastUtils.showError(getString(R.string.connection_error_message), getActivity()); 130 | } 131 | 132 | @OnClick(R.id.iv_fan_art) void onFanArtClicked() { 133 | ActionCommand fanArtClickActionCommand = tvShowViewModel.getTvShowClickedCommand(); 134 | fanArtClickActionCommand.execute(); 135 | } 136 | 137 | private void initializeListView() { 138 | header_tv_show_chapters = (TextView) LayoutInflater.from(getActivity()) 139 | .inflate(R.layout.header_tv_show_chapters, null); 140 | lv_chapters.addHeaderView(header_tv_show_chapters); 141 | adapter = 142 | (ChapterViewModelRendererAdapter) chapterRendererAdapterFactory.getChapterRendererAdapter( 143 | chapterAdapteeCollection); 144 | lv_chapters.setAdapter(adapter); 145 | } 146 | 147 | private void bindViewModel() { 148 | tvShowViewModel.setListener(this); 149 | tvShowViewModel.initialize(); 150 | } 151 | 152 | private int getVisibility(final boolean visible) { 153 | return visible ? View.VISIBLE : View.GONE; 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/presenter/Presenter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.presenter; 17 | 18 | /** 19 | * Abstract presenter to work as base for every presenter created in the application. This 20 | * presenter 21 | * declares some methods to attach the fragment/activity lifecycle. 22 | * 23 | * @author Pedro Vicente Gómez Sánchez 24 | */ 25 | public abstract class Presenter { 26 | 27 | /** 28 | * Called when the presenter is initialized, this method represents the start of the presenter 29 | * lifecycle. 30 | */ 31 | public abstract void initialize(); 32 | 33 | /** 34 | * Called when the presenter is resumed. After the initialization and when the presenter comes 35 | * from a pause state. 36 | */ 37 | public abstract void resume(); 38 | 39 | /** 40 | * Called when the presenter is paused. 41 | */ 42 | public abstract void pause(); 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/presenter/TvShowCatalogPresenter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.presenter; 17 | 18 | import com.github.pedrovgs.effectiveandroidui.domain.GetTvShows; 19 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.TvShow; 20 | import com.github.pedrovgs.effectiveandroidui.ui.activity.Navigator; 21 | import com.github.pedrovgs.effectiveandroidui.ui.renderer.tvshow.TvShowCollection; 22 | import java.util.Collection; 23 | import javax.inject.Inject; 24 | import javax.inject.Singleton; 25 | 26 | /** 27 | * Presenter created to show a list of videos inside a view. 28 | * 29 | * This is a sample of Model View Presenter pattern described in the talk Effective Android UI. 30 | * 31 | * We have attached the presenter to the Fragment lifecycle using method like initialize() resume() 32 | * and pause(). We are going to use this methods to connect our presenter with Android components 33 | * lifecycle. 34 | * 35 | * @author Pedro Vicente Gómez Sánchez 36 | */ 37 | @Singleton 38 | public class TvShowCatalogPresenter extends Presenter { 39 | 40 | private GetTvShows getTvShowsInteractor; 41 | private Navigator navigator; 42 | 43 | private View view; 44 | private TvShowCollection currentTvShowCollection; 45 | 46 | @Inject 47 | public TvShowCatalogPresenter(GetTvShows getTvShowsInteractor, Navigator navigator) { 48 | this.getTvShowsInteractor = getTvShowsInteractor; 49 | this.navigator = navigator; 50 | } 51 | 52 | public void setView(View view) { 53 | if (view == null) { 54 | throw new IllegalArgumentException("You can't set a null view"); 55 | } 56 | this.view = view; 57 | } 58 | 59 | @Override 60 | public void initialize() { 61 | checkViewAlreadySetted(); 62 | loadTvShows(); 63 | } 64 | 65 | @Override 66 | public void resume() { 67 | //Empty 68 | } 69 | 70 | @Override 71 | public void pause() { 72 | //Empty 73 | } 74 | 75 | /** 76 | * Used to force a TvShowCollection load in the presenter. This method is used by 77 | * TvShowCatalogFragment when the fragment lifecycle is restored and there are already loaded tv 78 | * shows. 79 | */ 80 | public void loadCatalog(final TvShowCollection tvShowCollection) { 81 | currentTvShowCollection = tvShowCollection; 82 | showTvShows(tvShowCollection.getAsList()); 83 | } 84 | 85 | public void onTvShowThumbnailClicked(final TvShow tvShow) { 86 | navigator.openTvShowDetails(tvShow); 87 | } 88 | 89 | public void onTvShowClicked(final TvShow tvShow) { 90 | view.showTvShowTitleAsMessage(tvShow); 91 | } 92 | 93 | public TvShowCollection getCurrentTvShows() { 94 | return currentTvShowCollection; 95 | } 96 | 97 | /** 98 | * Use GetTvShows interactor to obtain a collection of videos and render it using the view 99 | * object setted previously. If the interactor returns an error the presenter will show an error 100 | * message and the empty case. In both cases, the progress bar visibility will be hidden. 101 | */ 102 | private void loadTvShows() { 103 | if (view.isReady()) { 104 | view.showLoading(); 105 | } 106 | getTvShowsInteractor.execute(new GetTvShows.Callback() { 107 | @Override public void onTvShowsLoaded(final Collection tvShows) { 108 | currentTvShowCollection = new TvShowCollection(tvShows); 109 | showTvShows(tvShows); 110 | } 111 | 112 | @Override public void onConnectionError() { 113 | notifyConnectionError(); 114 | } 115 | }); 116 | } 117 | 118 | private void notifyConnectionError() { 119 | if (view.isReady() && !view.isAlreadyLoaded()) { 120 | view.hideLoading(); 121 | view.showConnectionErrorMessage(); 122 | view.showEmptyCase(); 123 | view.showDefaultTitle(); 124 | } 125 | } 126 | 127 | private void showTvShows(Collection tvShows) { 128 | if (view.isReady()) { 129 | view.renderVideos(tvShows); 130 | view.hideLoading(); 131 | view.updateTitleWithCountOfTvShows(tvShows.size()); 132 | } 133 | } 134 | 135 | private void checkViewAlreadySetted() { 136 | if (view == null) { 137 | throw new IllegalArgumentException("Remember to set a view for this presenter"); 138 | } 139 | } 140 | 141 | /** 142 | * View interface created to abstract the view 143 | * implementation used in this presenter. 144 | */ 145 | public interface View { 146 | 147 | void hideLoading(); 148 | 149 | void showLoading(); 150 | 151 | void renderVideos(final Collection tvShows); 152 | 153 | void updateTitleWithCountOfTvShows(final int counter); 154 | 155 | void showConnectionErrorMessage(); 156 | 157 | void showEmptyCase(); 158 | 159 | void showDefaultTitle(); 160 | 161 | void showTvShowTitleAsMessage(TvShow tvShow); 162 | 163 | boolean isReady(); 164 | 165 | boolean isAlreadyLoaded(); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/presenter/TvShowPresenter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.presenter; 17 | 18 | import com.github.pedrovgs.effectiveandroidui.domain.GetTvShowById; 19 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.ChapterCollection; 20 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.TvShow; 21 | import javax.inject.Inject; 22 | import javax.inject.Singleton; 23 | 24 | /** 25 | * Presenter created to show a TvShow. 26 | * 27 | * This is a sample of Model View Presenter pattern described in the talk Effective Android UI. 28 | * 29 | * This presenter is not attached to the fragment lifecycle because we don't have to show any 30 | * TvShow 31 | * until the TvShow be selected by the user or updated by the fragment lifecycle while the restore 32 | * instance state process. 33 | * 34 | * @author Pedro Vicente Gómez Sánchez 35 | */ 36 | 37 | @Singleton 38 | public class TvShowPresenter extends Presenter { 39 | 40 | private final GetTvShowById getTvShowById; 41 | private TvShow currentTvShow; 42 | 43 | @Inject 44 | public TvShowPresenter(GetTvShowById getTvShowById) { 45 | this.getTvShowById = getTvShowById; 46 | } 47 | 48 | private View view; 49 | 50 | @Override public void initialize() { 51 | //Empty 52 | } 53 | 54 | @Override public void resume() { 55 | //Empty 56 | } 57 | 58 | @Override public void pause() { 59 | //Empty 60 | } 61 | 62 | public void setView(View view) { 63 | this.view = view; 64 | } 65 | 66 | public TvShow getCurrentTvShow() { 67 | return currentTvShow; 68 | } 69 | 70 | public void tvShowClosed() { 71 | currentTvShow = null; 72 | } 73 | 74 | public void loadTvShow(final TvShow tvShow) { 75 | showTvShow(tvShow); 76 | } 77 | 78 | public void loadTvShow(final String tvShowId) { 79 | view.showLoading(); 80 | getTvShowById.execute(tvShowId, new GetTvShowById.Callback() { 81 | @Override public void onTvShowLoaded(TvShow tvShow) { 82 | showTvShow(tvShow); 83 | } 84 | 85 | @Override public void onTvShowNotFound() { 86 | showTvShowNotFound(); 87 | } 88 | 89 | @Override public void onConnectionError() { 90 | showConnectionError(); 91 | } 92 | }); 93 | } 94 | 95 | private void showConnectionError() { 96 | if (view.isReady() && !view.isAlreadyLoaded()) { 97 | currentTvShow = null; 98 | view.hideLoading(); 99 | view.showConnectionErrorMessage(); 100 | } 101 | } 102 | 103 | private void showTvShowNotFound() { 104 | if (view.isReady()) { 105 | currentTvShow = null; 106 | view.hideLoading(); 107 | view.showTvShowNotFoundMessage(); 108 | } 109 | } 110 | 111 | private void showTvShow(TvShow tvShow) { 112 | if (view.isReady()) { 113 | currentTvShow = tvShow; 114 | view.showFanArt(tvShow.getFanArt()); 115 | view.showTvShowTitle(tvShow.getTitle().toUpperCase()); 116 | view.showChapters(tvShow.getChapters()); 117 | view.hideLoading(); 118 | view.showTvShow(); 119 | } 120 | } 121 | 122 | /** 123 | * View interface created to abstract the view implementation used in this presenter. 124 | */ 125 | public interface View { 126 | 127 | void showLoading(); 128 | 129 | void showFanArt(final String tvShowFanArtUrl); 130 | 131 | void showChapters(final ChapterCollection episodes); 132 | 133 | void hideLoading(); 134 | 135 | void showTvShowNotFoundMessage(); 136 | 137 | void showConnectionErrorMessage(); 138 | 139 | void showTvShow(); 140 | 141 | void showTvShowTitle(String tvShowTitle); 142 | 143 | boolean isReady(); 144 | 145 | boolean isAlreadyLoaded(); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/presenter/TvShowUIModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.presenter; 17 | 18 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.Chapter; 19 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.TvShow; 20 | import com.github.pedrovgs.effectiveandroidui.ui.activity.MainActivity; 21 | import com.github.pedrovgs.effectiveandroidui.ui.activity.TvShowActivity; 22 | import com.github.pedrovgs.effectiveandroidui.ui.fragment.TvShowCatalogFragment; 23 | import com.github.pedrovgs.effectiveandroidui.ui.fragment.TvShowDraggableFragment; 24 | import com.github.pedrovgs.effectiveandroidui.ui.fragment.TvShowFragment; 25 | import com.github.pedrovgs.effectiveandroidui.ui.renderer.chapter.ChapterRenderer; 26 | import com.github.pedrovgs.effectiveandroidui.ui.renderer.chapter.ChapterRendererBuilder; 27 | import com.github.pedrovgs.effectiveandroidui.ui.renderer.chapterviewmodel.ChapterViewModelRenderer; 28 | import com.github.pedrovgs.effectiveandroidui.ui.renderer.chapterviewmodel.ChapterViewModelRendererBuilder; 29 | import com.github.pedrovgs.effectiveandroidui.ui.renderer.tvshow.TvShowRenderer; 30 | import com.github.pedrovgs.effectiveandroidui.ui.renderer.tvshow.TvShowRendererBuilder; 31 | import com.github.pedrovgs.effectiveandroidui.ui.viewmodel.ChapterViewModel; 32 | import com.pedrogomez.renderers.Renderer; 33 | import dagger.Module; 34 | import dagger.Provides; 35 | import java.util.LinkedList; 36 | 37 | /** 38 | * Dagger module created to provide TvShows UI dependencies like RendererBuilders or presenters. 39 | * 40 | * @author Pedro Vicente Gómez Sánchez 41 | */ 42 | @Module(complete = false, 43 | injects = { 44 | MainActivity.class, TvShowCatalogFragment.class, TvShowDraggableFragment.class, 45 | TvShowFragment.class, TvShowActivity.class 46 | }) public final class TvShowUIModule { 47 | 48 | @Provides TvShowRendererBuilder provideTvShowRendererBuilder(TvShowRenderer tvShowRenderer) { 49 | LinkedList> renderers = new LinkedList>(); 50 | renderers.add(tvShowRenderer); 51 | return new TvShowRendererBuilder(renderers); 52 | } 53 | 54 | @Provides ChapterRendererBuilder provideChapterRendererBuilder(ChapterRenderer chapterRenderer) { 55 | LinkedList> renderers = new LinkedList>(); 56 | renderers.add(chapterRenderer); 57 | return new ChapterRendererBuilder(renderers); 58 | } 59 | 60 | @Provides ChapterViewModelRendererBuilder provideChapterRendererBuilder( 61 | ChapterViewModelRenderer chapterRenderer) { 62 | LinkedList> renderers = new LinkedList>(); 63 | renderers.add(chapterRenderer); 64 | return new ChapterViewModelRendererBuilder(renderers); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/renderer/chapter/ChapterAdapteeCollection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.renderer.chapter; 17 | 18 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.Chapter; 19 | import com.pedrogomez.renderers.AdapteeCollection; 20 | import java.util.ArrayList; 21 | import java.util.Collection; 22 | import java.util.List; 23 | 24 | /** 25 | * AdapteeCollection implementation for Chapters. 26 | * 27 | * I've changed the name of this collection form ChapterCollection to ChapterAdapteeCollection 28 | * because there is a name collision. 29 | * 30 | * If you want to lear more about how to use Renderers take a look to this project: 31 | * https://github.com/pedrovgs/Renderers. 32 | * 33 | * @author Pedro Vicente Gómez Sánchez 34 | */ 35 | public class ChapterAdapteeCollection implements AdapteeCollection { 36 | 37 | private final List chapters; 38 | 39 | public ChapterAdapteeCollection() { 40 | this(new ArrayList()); 41 | } 42 | 43 | public ChapterAdapteeCollection(List chapters) { 44 | this.chapters = chapters; 45 | } 46 | 47 | @Override public int size() { 48 | return chapters.size(); 49 | } 50 | 51 | @Override public Chapter get(int index) { 52 | return chapters.get(index); 53 | } 54 | 55 | @Override public void add(Chapter chapter) { 56 | chapters.add(chapter); 57 | } 58 | 59 | @Override public void remove(Chapter chapter) { 60 | chapters.remove(chapter); 61 | } 62 | 63 | @Override public void addAll(Collection chapters) { 64 | this.chapters.addAll(chapters); 65 | } 66 | 67 | @Override public void removeAll(Collection chapters) { 68 | this.chapters.removeAll(chapters); 69 | } 70 | 71 | public void clear() { 72 | chapters.clear(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/renderer/chapter/ChapterRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.renderer.chapter; 17 | 18 | import android.view.LayoutInflater; 19 | import android.view.View; 20 | import android.view.ViewGroup; 21 | import android.widget.TextView; 22 | import butterknife.ButterKnife; 23 | import butterknife.InjectView; 24 | import com.github.pedrovgs.effectiveandroidui.R; 25 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.Chapter; 26 | import com.pedrogomez.renderers.Renderer; 27 | import javax.inject.Inject; 28 | 29 | /** 30 | * Renderer implementation for Chapter objects. 31 | * 32 | * If you want to lear more about how to use Renderers take a look to this project: 33 | * https://github.com/pedrovgs/Renderers 34 | * 35 | * @author Pedro Vicente Gómez Sánchez 36 | */ 37 | public class ChapterRenderer extends Renderer { 38 | 39 | @InjectView(R.id.tv_chapter_number) TextView tv_chapter_number; 40 | @InjectView(R.id.tv_chapter_title) TextView tv_chapter_title; 41 | @InjectView(R.id.tv_chapter_publish_date) TextView tv_chapter_publish_date; 42 | 43 | @Inject public ChapterRenderer() { 44 | } 45 | 46 | private int position; 47 | 48 | public void setPosition(int position) { 49 | this.position = position; 50 | } 51 | 52 | @Override protected void setUpView(View view) { 53 | ButterKnife.inject(this, view); 54 | } 55 | 56 | @Override protected void hookListeners(View view) { 57 | //Empty because we are using ButterKnife to inject views. 58 | } 59 | 60 | @Override protected View inflate(LayoutInflater layoutInflater, ViewGroup viewGroup) { 61 | return layoutInflater.inflate(R.layout.row_chapter, viewGroup, false); 62 | } 63 | 64 | @Override public void render() { 65 | Chapter chapter = getContent(); 66 | renderChapterNumber(); 67 | renderChapterTitle(chapter); 68 | renderChapterPublishDate(chapter); 69 | } 70 | 71 | private void renderChapterNumber() { 72 | tv_chapter_number.setText(String.format("%02d", position + 1)); 73 | } 74 | 75 | private void renderChapterTitle(Chapter episode) { 76 | tv_chapter_title.setText(episode.getTitle()); 77 | } 78 | 79 | private void renderChapterPublishDate(Chapter episode) { 80 | tv_chapter_publish_date.setText(episode.getPublishDate()); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/renderer/chapter/ChapterRendererAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.renderer.chapter; 17 | 18 | import android.view.LayoutInflater; 19 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.Chapter; 20 | import com.pedrogomez.renderers.AdapteeCollection; 21 | import com.pedrogomez.renderers.Renderer; 22 | import com.pedrogomez.renderers.RendererAdapter; 23 | import com.pedrogomez.renderers.RendererBuilder; 24 | 25 | /** 26 | * RendererAdapter extension created to provide position information to every Renderer 27 | * 28 | * If you want to lear more about how to use Renderers take a look to this project: 29 | * https://github.com/pedrovgs/Renderers. 30 | * 31 | * @author Pedro Vicente Gómez Sánchez 32 | */ 33 | public class ChapterRendererAdapter extends RendererAdapter { 34 | 35 | public ChapterRendererAdapter(LayoutInflater layoutInflater, RendererBuilder rendererBuilder, 36 | AdapteeCollection collection) { 37 | super(layoutInflater, rendererBuilder, collection); 38 | } 39 | 40 | @Override protected void updateRendererExtraValues(Chapter content, Renderer renderer, 41 | int position) { 42 | super.updateRendererExtraValues(content, renderer, position); 43 | ((ChapterRenderer) renderer).setPosition(position); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/renderer/chapter/ChapterRendererAdapterFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.renderer.chapter; 17 | 18 | import android.view.LayoutInflater; 19 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.Chapter; 20 | import com.pedrogomez.renderers.RendererAdapter; 21 | import javax.inject.Inject; 22 | 23 | /** 24 | * Factory created to provide RendererAdapter implementations. 25 | * 26 | * If you want to lear more about how to use Renderers take a look to this project: 27 | * https://github.com/pedrovgs/Renderers. 28 | * 29 | * @author Pedro Vicente Gómez Sánchez 30 | */ 31 | public class ChapterRendererAdapterFactory { 32 | 33 | private final ChapterRendererBuilder rendererBuilder; 34 | private final LayoutInflater layoutInflater; 35 | 36 | @Inject 37 | public ChapterRendererAdapterFactory(ChapterRendererBuilder rendererBuilder, 38 | LayoutInflater layoutInflater) { 39 | this.rendererBuilder = rendererBuilder; 40 | this.layoutInflater = layoutInflater; 41 | } 42 | 43 | public RendererAdapter getChapterRendererAdapter( 44 | final ChapterAdapteeCollection chapterCollection) { 45 | return new ChapterRendererAdapter(layoutInflater, rendererBuilder, chapterCollection); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/renderer/chapter/ChapterRendererBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.renderer.chapter; 17 | 18 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.Chapter; 19 | import com.pedrogomez.renderers.Renderer; 20 | import com.pedrogomez.renderers.RendererBuilder; 21 | import java.util.Collection; 22 | 23 | /** 24 | * RendererBuilder extension created to provide Renderer implementations. 25 | * 26 | * If you want to lear more about how to use Renderers take a look to this project: 27 | * https://github.com/pedrovgs/Renderers 28 | * 29 | * @author Pedro Vicente Gómez Sánchez 30 | */ 31 | public class ChapterRendererBuilder extends RendererBuilder { 32 | 33 | public ChapterRendererBuilder(Collection> prototypes) { 34 | super(prototypes); 35 | } 36 | 37 | @Override protected Class getPrototypeClass(Chapter content) { 38 | return ChapterRenderer.class; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/renderer/chapterviewmodel/ChapterViewModelCollection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.renderer.chapterviewmodel; 17 | 18 | import com.github.pedrovgs.effectiveandroidui.ui.viewmodel.ChapterViewModel; 19 | import com.pedrogomez.renderers.AdapteeCollection; 20 | import java.util.ArrayList; 21 | import java.util.Collection; 22 | import java.util.List; 23 | 24 | /** 25 | * AdapteeCollection implementation for ChaptersViewModel. 26 | * 27 | * If you want to lear more about how to use Renderers take a look to this project: 28 | * https://github.com/pedrovgs/Renderers. 29 | * 30 | * @author Pedro Vicente Gómez Sánchez 31 | */ 32 | public class ChapterViewModelCollection implements AdapteeCollection { 33 | 34 | private final List chapters; 35 | 36 | public ChapterViewModelCollection() { 37 | this(new ArrayList()); 38 | } 39 | 40 | public ChapterViewModelCollection(List chapters) { 41 | this.chapters = chapters; 42 | } 43 | 44 | @Override public int size() { 45 | return chapters.size(); 46 | } 47 | 48 | @Override public ChapterViewModel get(int index) { 49 | return chapters.get(index); 50 | } 51 | 52 | @Override public void add(ChapterViewModel chapter) { 53 | chapters.add(chapter); 54 | } 55 | 56 | @Override public void remove(ChapterViewModel chapter) { 57 | chapters.remove(chapter); 58 | } 59 | 60 | @Override public void addAll(Collection chapters) { 61 | this.chapters.addAll(chapters); 62 | } 63 | 64 | @Override public void removeAll(Collection chapters) { 65 | this.chapters.removeAll(chapters); 66 | } 67 | 68 | public void clear() { 69 | chapters.clear(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/renderer/chapterviewmodel/ChapterViewModelRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.renderer.chapterviewmodel; 17 | 18 | import android.content.Context; 19 | import android.view.LayoutInflater; 20 | import android.view.View; 21 | import android.view.ViewGroup; 22 | import android.widget.TextView; 23 | import butterknife.ButterKnife; 24 | import butterknife.InjectView; 25 | import com.github.pedrovgs.effectiveandroidui.R; 26 | import com.github.pedrovgs.effectiveandroidui.ui.viewmodel.ChapterViewModel; 27 | import com.pedrogomez.renderers.Renderer; 28 | import javax.inject.Inject; 29 | 30 | /** 31 | * Renderer implementation for ChapterViewModel objects. 32 | * 33 | * If you want to lear more about how to use Renderers take a look to this project: 34 | * https://github.com/pedrovgs/Renderers. 35 | * 36 | * Review how this renderer register and unregister a view model listener while the recycle process 37 | * in order to update the UI without use an adapter.notifyDataSetChanged(). This is a nice 38 | * implementation not really common in Android applications. It has a little bug...can you find it? 39 | * 40 | * @author Pedro Vicente Gómez Sánchez 41 | */ 42 | public class ChapterViewModelRenderer extends Renderer 43 | implements ChapterViewModel.Listener { 44 | 45 | @InjectView(R.id.tv_chapter_number) TextView tv_chapter_number; 46 | @InjectView(R.id.tv_chapter_title) TextView tv_chapter_title; 47 | @InjectView(R.id.tv_chapter_publish_date) TextView tv_chapter_publish_date; 48 | 49 | private final Context context; 50 | 51 | @Inject 52 | public ChapterViewModelRenderer(Context context) { 53 | this.context = context; 54 | } 55 | 56 | private int position; 57 | 58 | public void setPosition(int position) { 59 | this.position = position; 60 | } 61 | 62 | @Override 63 | protected void setUpView(View view) { 64 | ButterKnife.inject(this, view); 65 | } 66 | 67 | @Override 68 | protected void hookListeners(View view) { 69 | //Empty because we are using ButterKnife to inject views. 70 | } 71 | 72 | @Override 73 | protected View inflate(LayoutInflater layoutInflater, ViewGroup viewGroup) { 74 | return layoutInflater.inflate(R.layout.row_chapter, viewGroup, false); 75 | } 76 | 77 | @Override 78 | public void render() { 79 | ChapterViewModel chapter = getContent(); 80 | renderChapterNumber(); 81 | renderChapterTitle(chapter); 82 | renderChapterPublishDate(chapter); 83 | } 84 | 85 | @Override public void onRecycle(ChapterViewModel content) { 86 | getContent().unregisterListener(); 87 | super.onRecycle(content); 88 | getContent().registerListener(this); 89 | } 90 | 91 | @Override public void onCreate(ChapterViewModel content, LayoutInflater layoutInflater, 92 | ViewGroup parent) { 93 | super.onCreate(content, layoutInflater, parent); 94 | getContent().registerListener(this); 95 | } 96 | 97 | private void renderChapterNumber() { 98 | tv_chapter_number.setText(String.format("%02d", position + 1)); 99 | } 100 | 101 | private void renderChapterTitle(ChapterViewModel chapter) { 102 | tv_chapter_title.setText(chapter.getTitle()); 103 | } 104 | 105 | private void renderChapterPublishDate(ChapterViewModel chapter) { 106 | tv_chapter_publish_date.setText(chapter.getPublishDate()); 107 | } 108 | 109 | /** 110 | * This method is going to be called some seconds after the onCreate onRecycle and will update the 111 | * view without use a notifyDataSetChanged(). 112 | */ 113 | @Override public void onRateChanged(int rate) { 114 | ChapterViewModel chapter = getContent(); 115 | String rateMessage = context.getString(R.string.tv_show_rate, rate); 116 | tv_chapter_title.setText(chapter.getTitle() + " - " + rateMessage); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/renderer/chapterviewmodel/ChapterViewModelRendererAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.renderer.chapterviewmodel; 17 | 18 | import android.view.LayoutInflater; 19 | import com.github.pedrovgs.effectiveandroidui.ui.viewmodel.ChapterViewModel; 20 | import com.pedrogomez.renderers.AdapteeCollection; 21 | import com.pedrogomez.renderers.Renderer; 22 | import com.pedrogomez.renderers.RendererAdapter; 23 | import com.pedrogomez.renderers.RendererBuilder; 24 | 25 | /** 26 | * RendererAdapter extension created to provide position information to each 27 | * Renderer 28 | * 29 | * If you want to lear more about how to use Renderers take a look to this project: 30 | * https://github.com/pedrovgs/Renderers. 31 | * 32 | * @author Pedro Vicente Gómez Sánchez 33 | */ 34 | public class ChapterViewModelRendererAdapter extends RendererAdapter { 35 | 36 | public ChapterViewModelRendererAdapter(LayoutInflater layoutInflater, 37 | RendererBuilder rendererBuilder, AdapteeCollection collection) { 38 | super(layoutInflater, rendererBuilder, collection); 39 | } 40 | 41 | @Override protected void updateRendererExtraValues(ChapterViewModel content, 42 | Renderer renderer, int position) { 43 | super.updateRendererExtraValues(content, renderer, position); 44 | ((ChapterViewModelRenderer) renderer).setPosition(position); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/renderer/chapterviewmodel/ChapterViewModelRendererAdapterFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.renderer.chapterviewmodel; 17 | 18 | import android.view.LayoutInflater; 19 | import com.github.pedrovgs.effectiveandroidui.ui.viewmodel.ChapterViewModel; 20 | import com.pedrogomez.renderers.RendererAdapter; 21 | import javax.inject.Inject; 22 | 23 | /** 24 | * Factory created to provide RendererAdapter implementations. 25 | * 26 | * If you want to lear more about how to use Renderers take a look to this project: 27 | * https://github.com/pedrovgs/Renderers. 28 | * 29 | * @author Pedro Vicente Gómez Sánchez 30 | */ 31 | public class ChapterViewModelRendererAdapterFactory { 32 | 33 | private final ChapterViewModelRendererBuilder rendererBuilder; 34 | private final LayoutInflater layoutInflater; 35 | 36 | @Inject 37 | public ChapterViewModelRendererAdapterFactory(ChapterViewModelRendererBuilder rendererBuilder, 38 | LayoutInflater layoutInflater) { 39 | this.rendererBuilder = rendererBuilder; 40 | this.layoutInflater = layoutInflater; 41 | } 42 | 43 | public RendererAdapter getChapterRendererAdapter( 44 | final ChapterViewModelCollection chapterCollection) { 45 | return new ChapterViewModelRendererAdapter(layoutInflater, rendererBuilder, chapterCollection); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/renderer/chapterviewmodel/ChapterViewModelRendererBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.renderer.chapterviewmodel; 17 | 18 | import com.github.pedrovgs.effectiveandroidui.ui.viewmodel.ChapterViewModel; 19 | import com.pedrogomez.renderers.Renderer; 20 | import com.pedrogomez.renderers.RendererBuilder; 21 | import java.util.Collection; 22 | 23 | /** 24 | * RendererBuilder extension created to provide Renderer implementations. 25 | * 26 | * If you want to lear more about how to use Renderers take a look to this project: 27 | * https://github.com/pedrovgs/Renderers 28 | * 29 | * @author Pedro Vicente Gómez Sánchez 30 | */ 31 | public class ChapterViewModelRendererBuilder extends RendererBuilder { 32 | 33 | public ChapterViewModelRendererBuilder(Collection> prototypes) { 34 | super(prototypes); 35 | } 36 | 37 | @Override protected Class getPrototypeClass(ChapterViewModel content) { 38 | return ChapterViewModelRenderer.class; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/renderer/tvshow/TvShowCollection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.renderer.tvshow; 17 | 18 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.TvShow; 19 | import com.pedrogomez.renderers.AdapteeCollection; 20 | import java.io.Serializable; 21 | import java.util.Collection; 22 | import java.util.LinkedList; 23 | import java.util.List; 24 | 25 | /** 26 | * AdapteeCollection implementation for TvShow. 27 | * 28 | * If you want to lear more about how to use Renderers take a look to this project: 29 | * https://github.com/pedrovgs/Renderers. 30 | * 31 | * @author Pedro Vicente Gómez Sánchez 32 | */ 33 | public class TvShowCollection implements AdapteeCollection, Serializable { 34 | private static final long serialVersionUID = 8799677673292716638L; 35 | 36 | private final List tvShows; 37 | 38 | public TvShowCollection() { 39 | this(new LinkedList()); 40 | } 41 | 42 | public TvShowCollection(Collection tvShows) { 43 | this.tvShows = new LinkedList(); 44 | this.tvShows.addAll(tvShows); 45 | } 46 | 47 | @Override public int size() { 48 | return tvShows.size(); 49 | } 50 | 51 | @Override public TvShow get(int index) { 52 | return tvShows.get(index); 53 | } 54 | 55 | @Override public void add(TvShow tvShow) { 56 | tvShows.add(tvShow); 57 | } 58 | 59 | @Override public void remove(TvShow tvShow) { 60 | tvShows.remove(tvShow); 61 | } 62 | 63 | @Override public void addAll(Collection tvShows) { 64 | this.tvShows.addAll(tvShows); 65 | } 66 | 67 | @Override public void removeAll(Collection tvShows) { 68 | this.tvShows.removeAll(tvShows); 69 | } 70 | 71 | public void clear() { 72 | tvShows.clear(); 73 | } 74 | 75 | public List getAsList() { 76 | return (List) ((LinkedList) tvShows).clone(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/renderer/tvshow/TvShowRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.renderer.tvshow; 17 | 18 | import android.content.Context; 19 | import android.view.LayoutInflater; 20 | import android.view.View; 21 | import android.view.ViewGroup; 22 | import android.widget.ImageView; 23 | import android.widget.TextView; 24 | import butterknife.ButterKnife; 25 | import butterknife.InjectView; 26 | import butterknife.OnClick; 27 | import com.github.pedrovgs.effectiveandroidui.R; 28 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.TvShow; 29 | import com.github.pedrovgs.effectiveandroidui.ui.presenter.TvShowCatalogPresenter; 30 | import com.pedrogomez.renderers.Renderer; 31 | import com.squareup.picasso.Picasso; 32 | import javax.inject.Inject; 33 | 34 | /** 35 | * Renderer implementation for TvShow objects. 36 | * 37 | * If you want to lear more about how to use Renderers take a look to this project: 38 | * https://github.com/pedrovgs/Renderers 39 | * 40 | * @author Pedro Vicente Gómez Sánchez 41 | */ 42 | public class TvShowRenderer extends Renderer { 43 | 44 | private final Context context; 45 | private final TvShowCatalogPresenter tvShowCatalogPresenter; 46 | 47 | @InjectView(R.id.iv_thumbnail) ImageView thumbnailImageView; 48 | @InjectView(R.id.tv_title) TextView titleTextView; 49 | @InjectView(R.id.tv_seasons_counter) TextView seasonsCounterTextView; 50 | 51 | @Inject 52 | public TvShowRenderer(Context context, TvShowCatalogPresenter tvShowCatalogPresenter) { 53 | this.context = context; 54 | this.tvShowCatalogPresenter = tvShowCatalogPresenter; 55 | } 56 | 57 | @Override protected void setUpView(View rootView) { 58 | ButterKnife.inject(this, rootView); 59 | } 60 | 61 | @Override protected void hookListeners(View rootView) { 62 | //Empty because we are using ButterKnife library 63 | } 64 | 65 | @Override protected View inflate(LayoutInflater inflater, ViewGroup parent) { 66 | return inflater.inflate(R.layout.row_tv_show, parent, false); 67 | } 68 | 69 | @Override public void render() { 70 | TvShow tvShow = getContent(); 71 | renderThumbnail(tvShow); 72 | renderTitle(tvShow); 73 | renderSeasonCounter(tvShow); 74 | } 75 | 76 | @OnClick(R.id.iv_thumbnail) void onThumbnailClicked() { 77 | tvShowCatalogPresenter.onTvShowThumbnailClicked(getContent()); 78 | } 79 | 80 | @OnClick(R.id.v_row_container) void onBackgroundClicked() { 81 | tvShowCatalogPresenter.onTvShowClicked(getContent()); 82 | } 83 | 84 | private TvShow renderThumbnail(TvShow tvShow) { 85 | Picasso.with(context).load(tvShow.getPoster()).into(thumbnailImageView); 86 | return tvShow; 87 | } 88 | 89 | private void renderTitle(TvShow tvShow) { 90 | titleTextView.setText(tvShow.getTitle().toUpperCase()); 91 | } 92 | 93 | private void renderSeasonCounter(TvShow tvShow) { 94 | String seasons = context.getString(R.string.seasons_counter, tvShow.getNumberOfSeasons()); 95 | seasonsCounterTextView.setText(seasons); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/renderer/tvshow/TvShowRendererAdapterFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.renderer.tvshow; 17 | 18 | import android.view.LayoutInflater; 19 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.TvShow; 20 | import com.pedrogomez.renderers.RendererAdapter; 21 | import javax.inject.Inject; 22 | 23 | /** 24 | * Factory created to provide RendererAdapter implementations. 25 | * 26 | * If you want to lear more about how to use Renderers take a look to this project: 27 | * https://github.com/pedrovgs/Renderers. 28 | * 29 | * @author Pedro Vicente Gómez Sánchez 30 | */ 31 | public class TvShowRendererAdapterFactory { 32 | 33 | private final TvShowRendererBuilder rendererBuilder; 34 | private final LayoutInflater layoutInflater; 35 | 36 | @Inject 37 | public TvShowRendererAdapterFactory(TvShowRendererBuilder rendererBuilder, 38 | LayoutInflater layoutInflater) { 39 | this.rendererBuilder = rendererBuilder; 40 | this.layoutInflater = layoutInflater; 41 | } 42 | 43 | public RendererAdapter getTvShowRendererAdapter(final TvShowCollection tvShowCollection) { 44 | return new RendererAdapter(layoutInflater, rendererBuilder, tvShowCollection); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/renderer/tvshow/TvShowRendererBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.renderer.tvshow; 17 | 18 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.TvShow; 19 | import com.pedrogomez.renderers.Renderer; 20 | import com.pedrogomez.renderers.RendererBuilder; 21 | import java.util.Collection; 22 | 23 | /** 24 | * RendererBuilder extension created to provide Renderer implementations. 25 | * 26 | * If you want to lear more about how to use Renderers take a look to this project: 27 | * https://github.com/pedrovgs/Renderers 28 | * 29 | * @author Pedro Vicente Gómez Sánchez 30 | */ 31 | public class TvShowRendererBuilder extends RendererBuilder { 32 | 33 | public TvShowRendererBuilder(Collection> prototypes) { 34 | super(prototypes); 35 | } 36 | 37 | @Override protected Class getPrototypeClass(TvShow tvShow) { 38 | return TvShowRenderer.class; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/viewmodel/ChapterViewModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.viewmodel; 17 | 18 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.Chapter; 19 | import com.github.pedrovgs.effectiveandroidui.util.RandomUtils; 20 | import com.github.pedrovgs.effectiveandroidui.util.TimeMachine; 21 | 22 | /** 23 | * ViewModel crated to represent a Chapter from the presentation point of view. 24 | * 25 | * This class could be a interface implementation if the ChapterViewModel has more than one 26 | * implementation. 27 | * 28 | * This view model contains code to show how to implement a system update inside a ListView without 29 | * use a notifyDataSetChanged. 30 | * 31 | * @author Pedro Vicente Gómez Sánchez 32 | */ 33 | public class ChapterViewModel { 34 | 35 | private static final int MAX_RATE_VALUE = 10; 36 | private static final int TWO_SECONDS = 2000; 37 | 38 | private final Chapter chapter; 39 | 40 | private Listener listener = new NullListener(); 41 | 42 | /** 43 | * Runnable implementation used to simulate a system update related with the tv show rate. 44 | */ 45 | private Runnable updateRateRunnable = new Runnable() { 46 | @Override public void run() { 47 | int randomRate = RandomUtils.getRandomValueBelow(MAX_RATE_VALUE); 48 | listener.onRateChanged(randomRate); 49 | } 50 | }; 51 | 52 | public ChapterViewModel(Chapter chapter) { 53 | this.chapter = chapter; 54 | } 55 | 56 | public String getTitle() { 57 | return chapter.getTitle(); 58 | } 59 | 60 | public String getPublishDate() { 61 | return chapter.getPublishDate(); 62 | } 63 | 64 | /** 65 | * In addition to register the listener this method is going to trigger a view model update two 66 | * seconds in the future. This has been implemented to show how to use view model binding 67 | * manually 68 | * implemented to update each row without the full list view. 69 | */ 70 | public void registerListener(final Listener listener) { 71 | this.listener = listener; 72 | TimeMachine.sendMessageToTheFuture(updateRateRunnable, TWO_SECONDS); 73 | } 74 | 75 | /** 76 | * Set NullListener (NullObject pattern implementation) and cancel the update sent to the future 77 | * when the listener was registered. 78 | */ 79 | public void unregisterListener() { 80 | this.listener = new NullListener(); 81 | TimeMachine.cancelMessage(updateRateRunnable); 82 | } 83 | 84 | public interface Listener { 85 | 86 | void onRateChanged(final int rate); 87 | } 88 | 89 | /** 90 | * NullObject pattern implementation to avoid listener field null checks inside this view model. 91 | */ 92 | private static class NullListener implements Listener { 93 | @Override public void onRateChanged(int rate) { 94 | //Empty 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/viewmodel/TvShowViewModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.viewmodel; 17 | 18 | import com.github.pedrovgs.effectiveandroidui.domain.GetTvShowById; 19 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.Chapter; 20 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.ChapterCollection; 21 | import com.github.pedrovgs.effectiveandroidui.domain.tvshow.TvShow; 22 | import com.github.pedrovgs.effectiveandroidui.ui.fragment.TvShowFragment; 23 | import com.github.pedrovgs.effectiveandroidui.ui.viewmodel.action.ActionCommand; 24 | import com.github.pedrovgs.effectiveandroidui.ui.viewmodel.action.ShowTvShowOnBrowserActionCommand; 25 | import java.util.LinkedList; 26 | import java.util.List; 27 | import javax.inject.Inject; 28 | 29 | /** 30 | * ViewModel crated to represent a TvShow from the presentation point of view. 31 | * 32 | * This class could be a interface implementation if the TvShowViewModel has more than one 33 | * implementation. 34 | * 35 | * @author Pedro Vicente Gómez Sánchez 36 | */ 37 | public class TvShowViewModel { 38 | 39 | private final GetTvShowById getTvShowById; 40 | private final ShowTvShowOnBrowserActionCommand showTvShowOnBrowserActionCommand; 41 | 42 | private Listener listener; 43 | private boolean isReady; 44 | 45 | @Inject 46 | public TvShowViewModel(GetTvShowById getTvShowById, 47 | ShowTvShowOnBrowserActionCommand showTvShowOnBrowserActionCommand) { 48 | this.getTvShowById = getTvShowById; 49 | this.showTvShowOnBrowserActionCommand = showTvShowOnBrowserActionCommand; 50 | } 51 | 52 | public void initialize() { 53 | listener.onEmptyCaseVisibilityChanged(true); 54 | } 55 | 56 | public void loadTvShow(final String tvShowId) { 57 | listener.onLoadVisibilityChanged(true); 58 | listener.onEmptyCaseVisibilityChanged(false); 59 | getTvShowById.execute(tvShowId, new GetTvShowById.Callback() { 60 | @Override public void onTvShowLoaded(TvShow tvShow) { 61 | notifyTvShowLoaded(tvShow); 62 | } 63 | 64 | @Override public void onTvShowNotFound() { 65 | notifyTvShowNotFound(); 66 | } 67 | 68 | @Override public void onConnectionError() { 69 | notifyConnectionError(); 70 | } 71 | }); 72 | } 73 | 74 | public ActionCommand getTvShowClickedCommand() { 75 | return showTvShowOnBrowserActionCommand; 76 | } 77 | 78 | public void setListener(TvShowFragment listener) { 79 | this.listener = listener; 80 | } 81 | 82 | public Listener getListener() { 83 | return listener; 84 | } 85 | 86 | public void setReady(boolean isReady) { 87 | this.isReady = isReady; 88 | } 89 | 90 | private void notifyConnectionError() { 91 | if (isReady) { 92 | listener.onLoadVisibilityChanged(false); 93 | listener.onVisibilityChanged(false); 94 | listener.onEmptyCaseVisibilityChanged(true); 95 | listener.onConnectionErrorMessageNotFound(); 96 | } 97 | } 98 | 99 | private void notifyTvShowNotFound() { 100 | if (isReady) { 101 | listener.onLoadVisibilityChanged(false); 102 | listener.onVisibilityChanged(false); 103 | listener.onEmptyCaseVisibilityChanged(true); 104 | listener.onTvShowMessageNotFound(); 105 | } 106 | } 107 | 108 | private void notifyTvShowLoaded(TvShow tvShow) { 109 | showTvShowOnBrowserActionCommand.setTvShowUrl(tvShow.getPoster()); 110 | if (isReady) { 111 | listener.onFanArtLoaded(tvShow.getFanArt()); 112 | listener.onTvShowTitleLoaded(tvShow.getTitle()); 113 | listener.onChaptersLoaded(getChaptersViewModel(tvShow.getChapters())); 114 | listener.onVisibilityChanged(true); 115 | listener.onLoadVisibilityChanged(false); 116 | listener.onEmptyCaseVisibilityChanged(false); 117 | } 118 | } 119 | 120 | private List getChaptersViewModel(ChapterCollection chapterCollection) { 121 | List chapterViewModels = new LinkedList(); 122 | for (Chapter chapter : chapterCollection) { 123 | chapterViewModels.add(new ChapterViewModel(chapter)); 124 | } 125 | return chapterViewModels; 126 | } 127 | 128 | /** 129 | * Interface created to work as ViewModel listener. 130 | * Every change in the view model will be 131 | * notified to Listener implementation. 132 | */ 133 | public interface Listener { 134 | 135 | void onFanArtLoaded(final String fanArt); 136 | 137 | void onTvShowTitleLoaded(final String tvShowTitle); 138 | 139 | void onChaptersLoaded(final List chapters); 140 | 141 | void onVisibilityChanged(final boolean visible); 142 | 143 | void onLoadVisibilityChanged(final boolean visible); 144 | 145 | void onEmptyCaseVisibilityChanged(final boolean visible); 146 | 147 | void onTvShowMessageNotFound(); 148 | 149 | void onConnectionErrorMessageNotFound(); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/viewmodel/action/ActionCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.viewmodel.action; 17 | 18 | /** 19 | * Interface created to represent ActionCommands associated to MVVM implementation. View models 20 | * returns ActionCommands to perform some UI operations. 21 | * 22 | * In a MVVM classic implementation every action performed by the user should finish executing an 23 | * ActionCommand implementation. 24 | * 25 | * @author Pedro Vicente Gómez Sánchez 26 | */ 27 | public interface ActionCommand { 28 | void execute(); 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/ui/viewmodel/action/ShowTvShowOnBrowserActionCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.ui.viewmodel.action; 17 | 18 | import android.content.Context; 19 | import android.content.Intent; 20 | import android.net.Uri; 21 | import com.github.pedrovgs.effectiveandroidui.di.ActivityContext; 22 | import javax.inject.Inject; 23 | 24 | /** 25 | * ActionCommand implementation created to show the TvShow title when a TvShow is clicked. 26 | * 27 | * @author Pedro Vicente Gómez Sánchez 28 | */ 29 | public class ShowTvShowOnBrowserActionCommand implements ActionCommand { 30 | 31 | private final Context context; 32 | 33 | private String tvShowUrl; 34 | 35 | @Inject 36 | public ShowTvShowOnBrowserActionCommand(@ActivityContext Context context) { 37 | this.context = context; 38 | } 39 | 40 | public void setTvShowUrl(String tvShowUrl) { 41 | this.tvShowUrl = tvShowUrl; 42 | } 43 | 44 | @Override public void execute() { 45 | if (tvShowUrl != null) { 46 | Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(tvShowUrl)); 47 | context.startActivity(browserIntent); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/util/RandomUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.util; 17 | 18 | import java.util.Random; 19 | 20 | /** 21 | * Some utility methods related with the Random class. 22 | * 23 | * @author Pedro Vicente Gómez Sánchez 24 | */ 25 | public class RandomUtils { 26 | 27 | private static final Random RANDOM = new Random(); 28 | private static final int CENT_PERCENT = 101; 29 | 30 | private RandomUtils() { 31 | //Empty 32 | } 33 | 34 | /** 35 | * Return true of false using a random value generated and the percentage passed as parameter. 36 | * 37 | * @param percentage to evaluate. 38 | * @return true fifty percent of the times it's executed if the percentage parameter is 50. 39 | */ 40 | public static boolean percent(final int percentage) { 41 | return (RANDOM.nextInt(CENT_PERCENT) < percentage); 42 | } 43 | 44 | /** 45 | * Returns a random integer between 0 and the maxValue argument, included maxValue. 46 | */ 47 | public static int getRandomValueBelow(final int maxValue) { 48 | return RANDOM.nextInt(maxValue + 1); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/util/StringUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.util; 17 | 18 | /** 19 | * Some utility methods related with the String class. 20 | * 21 | * @author Pedro Vicente Gómez Sánchez 22 | */ 23 | public class StringUtils { 24 | 25 | private static final String EMPTY_STRING = ""; 26 | 27 | private StringUtils() { 28 | //Empty 29 | } 30 | 31 | public static boolean isNullOrEmpty(final String string) { 32 | return string == null || EMPTY_STRING.equals(string); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/util/TimeMachine.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.util; 17 | 18 | import android.os.Handler; 19 | 20 | /** 21 | * Utility class created to send messages to the future using an Android Handler. 22 | * 23 | * @author Pedro Vicente Gómez Sánchez 24 | */ 25 | public class TimeMachine { 26 | 27 | private static Handler handler = new Handler(); 28 | 29 | private TimeMachine() { 30 | //Empty 31 | } 32 | 33 | /** 34 | * Execute a Runnable implementation some milliseconds in the future. 35 | * 36 | * @param runnable to execute. 37 | * @param delay in miliseconds. 38 | */ 39 | public static void sendMessageToTheFuture(final Runnable runnable, final int delay) { 40 | handler.postDelayed(runnable, delay); 41 | } 42 | 43 | /** 44 | * Cancel a message already sent to the future. 45 | */ 46 | public static void cancelMessage(Runnable runnable) { 47 | handler.removeCallbacks(runnable); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pedrovgs/effectiveandroidui/util/ToastUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pedro Vicente Gómez Sánchez. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pedrovgs.effectiveandroidui.util; 17 | 18 | import android.content.Context; 19 | import android.widget.Toast; 20 | 21 | /** 22 | * Some utility methods related with the Toast class. 23 | * 24 | * @author Pedro Vicente Gómez Sánchez 25 | */ 26 | public class ToastUtils { 27 | 28 | private ToastUtils() { 29 | //Empty 30 | } 31 | 32 | public static void showError(final String message, final Context context) { 33 | getToast(message, context).show(); 34 | } 35 | 36 | public static void showShortMessage(String message, Context context) { 37 | getToast(message, context, Toast.LENGTH_SHORT).show(); 38 | } 39 | 40 | private static Toast getToast(String message, Context context) { 41 | return getToast(message, context, Toast.LENGTH_LONG); 42 | } 43 | 44 | private static Toast getToast(String message, Context context, int length) { 45 | return Toast.makeText(context, message, length); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/lint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrovgs/EffectiveAndroidUI/8544d91c73cf4954892c7abd0151e6f636429860/app/src/main/res/drawable-hdpi/app_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrovgs/EffectiveAndroidUI/8544d91c73cf4954892c7abd0151e6f636429860/app/src/main/res/drawable-mdpi/app_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrovgs/EffectiveAndroidUI/8544d91c73cf4954892c7abd0151e6f636429860/app/src/main/res/drawable-xhdpi/app_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrovgs/EffectiveAndroidUI/8544d91c73cf4954892c7abd0151e6f636429860/app/src/main/res/drawable-xxhdpi/app_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/empty_case.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrovgs/EffectiveAndroidUI/8544d91c73cf4954892c7abd0151e6f636429860/app/src/main/res/drawable/empty_case.png -------------------------------------------------------------------------------- /app/src/main/res/layout-sw600dp-land/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 20 | 21 | 22 | 23 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/layout-v10/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout-v11/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_tv_show.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_draggable_tv_show.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | 15 | 16 | 26 | 27 | 28 | 29 | 33 | 34 | 35 | 36 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_tv_show.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_tv_shows.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 21 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/header_tv_show_chapters.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/row_chapter.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/layout/row_tv_show.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 15 | 16 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/values-hdpi/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 200dip 5 | 185dip 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/values-hdpi/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2 4 | -------------------------------------------------------------------------------- /app/src/main/res/values-mdpi/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 100dip 5 | 120dip 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/values-mdpi/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 1 4 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 3 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #FFDDDDDD 5 | #FFFFFFFF 6 | #FF727A7C 7 | #FF57585A 8 | #FFF0F0F0 9 | #FF8B999B 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10dip 5 | 5dip 6 | 38sp 7 | 20sp 8 | 1dip 9 | 200dip 10 | 185dip 11 | 2 12 | 2 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | EffectiveAndroidUI 5 | EffectiveAndroidUI - %1$d Tv Shows 6 | Ups! Something went wrong :( 7 | %1$d seasons 8 | TV Show not found exception ;) 9 | %1$s - SEASON 1 10 | Rate %1$d 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles-chapter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 14 | 15 | 20 | 21 | 28 | 29 | 36 | 37 | 42 | 43 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 16 | 17 | 20 | 21 | 25 | 26 | 32 | 33 | 38 | 39 | 46 | 47 | 53 | 54 | 62 | 63 | 73 | 74 | 79 | 80 | 84 | 85 | 90 | 91 | 97 | 98 | 102 | 103 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /art/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrovgs/EffectiveAndroidUI/8544d91c73cf4954892c7abd0151e6f636429860/art/screenshot1.png -------------------------------------------------------------------------------- /art/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrovgs/EffectiveAndroidUI/8544d91c73cf4954892c7abd0151e6f636429860/art/screenshot2.png -------------------------------------------------------------------------------- /art/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrovgs/EffectiveAndroidUI/8544d91c73cf4954892c7abd0151e6f636429860/art/screenshot3.png -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | mavenCentral() 6 | mavenLocal() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:1.0.1' 10 | } 11 | } 12 | 13 | allprojects { 14 | repositories { 15 | // Work around https://code.google.com/p/android/issues/detail?id=69270. 16 | def androidHome = System.getenv("ANDROID_HOME") 17 | maven { 18 | url "$androidHome/extras/android/m2repository/" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /config/checkstyle/checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /config/findbugs/findbugs-excludes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /config/pmd/pmd-ruleset.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 3 8 | 9 | 10 | 3 11 | 12 | 13 | 3 14 | 15 | 16 | 3 17 | 18 | 19 | 3 20 | 21 | 22 | 3 23 | 24 | 25 | 3 26 | 27 | 28 | 3 29 | 30 | 31 | 3 32 | 33 | 34 | 2 35 | 36 | 37 | 4 38 | 39 | 40 | 3 41 | 42 | 43 | 3 44 | 45 | 46 | 3 47 | 48 | 49 | 4 50 | 51 | 52 | 3 53 | 54 | 55 | 2 56 | 57 | 58 | 3 59 | 60 | 61 | 3 62 | 63 | 64 | 3 65 | 66 | 67 | 3 68 | 69 | 70 | 4 71 | 72 | 73 | 2 74 | 75 | 76 | 4 77 | 78 | 79 | 3 80 | 81 | 82 | 3 83 | 84 | 85 | 3 86 | 87 | 88 | 2 89 | 90 | 91 | 4 92 | 93 | 94 | 2 95 | 96 | 97 | 3 98 | 99 | 100 | 5 101 | 102 | 103 | 3 104 | 105 | 106 | 3 107 | 108 | 109 | 2 110 | 111 | 112 | 3 113 | 114 | 115 | 1 116 | 117 | 118 | 1 119 | 120 | 121 | 3 122 | 123 | 124 | 3 125 | 126 | 127 | 2 128 | 129 | 130 | 3 131 | 132 | 133 | 3 134 | 135 | 136 | 4 137 | 138 | 139 | 4 140 | 141 | 142 | 3 143 | 144 | 145 | 3 146 | 147 | 148 | 3 149 | 150 | 151 | 3 152 | 153 | 154 | 3 155 | 156 | 157 | 3 158 | 159 | 160 | 3 161 | 162 | 163 | 3 164 | 165 | 166 | 167 | 168 | 169 | 3 170 | 171 | 172 | 3 173 | 174 | 175 | 3 176 | 177 | 178 | 3 179 | 180 | 181 | 182 | 183 | 184 | 4 185 | 186 | 187 | 3 188 | 189 | 190 | 3 191 | 192 | 193 | 4 194 | 195 | 196 | 3 197 | 198 | 199 | 3 200 | 201 | 202 | 3 203 | 204 | 205 | 3 206 | 207 | 208 | 3 209 | 210 | 211 | 3 212 | 213 | 214 | 3 215 | 216 | 217 | 3 218 | 219 | 220 | 3 221 | 222 | 223 | 3 224 | 225 | 226 | 3 227 | 228 | 229 | 230 | 231 | 232 | 3 233 | 234 | 235 | 3 236 | 237 | 238 | 3 239 | 240 | 241 | 3 242 | 243 | 244 | 3 245 | 246 | 247 | 4 248 | 249 | 250 | 3 251 | 252 | 253 | 3 254 | 255 | 256 | 3 257 | 258 | 259 | 4 260 | 261 | 262 | 3 263 | 264 | 265 | 3 266 | 267 | 268 | 3 269 | 270 | 271 | 3 272 | 273 | 274 | 3 275 | 276 | 277 | 278 | 279 | 280 | 3 281 | 282 | 283 | 284 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Settings specified in this file will override any Gradle settings 5 | # configured through the IDE. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrovgs/EffectiveAndroidUI/8544d91c73cf4954892c7abd0151e6f636429860/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Jan 18 17:45:50 CET 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------