├── .gitignore ├── LICENSE.txt ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── twistedeqations │ │ └── rxmvp │ │ └── sample │ │ ├── activities │ │ ├── home │ │ │ ├── MainActivity.java │ │ │ ├── dagger │ │ │ │ ├── HomeComponent.java │ │ │ │ ├── HomeModule.java │ │ │ │ └── HomeScope.java │ │ │ └── mvp │ │ │ │ ├── HomeModel.java │ │ │ │ ├── HomePresenter.java │ │ │ │ └── HomeView.java │ │ └── repos │ │ │ ├── ReposActivity.java │ │ │ ├── dagger │ │ │ ├── ReopsModule.java │ │ │ ├── ReposComponent.java │ │ │ └── ReposScope.java │ │ │ └── mvp │ │ │ ├── ReopsModel.java │ │ │ ├── ReposPresenter.java │ │ │ └── view │ │ │ ├── ReposAdapter.java │ │ │ ├── ReposListItem.java │ │ │ └── ReposView.java │ │ ├── app │ │ ├── GithubApplication.java │ │ ├── dagger │ │ │ ├── AppComponent.java │ │ │ ├── AppScope.java │ │ │ └── module │ │ │ │ ├── AppModule.java │ │ │ │ ├── GsonModule.java │ │ │ │ └── NetworkModule.java │ │ └── network │ │ │ ├── GithubNetwork.java │ │ │ └── model │ │ │ ├── GitHubRepo.java │ │ │ ├── GithubOwner.java │ │ │ └── GithubUser.java │ │ └── ext │ │ ├── Constants.java │ │ ├── DateTimeAdapter.java │ │ └── GithubAdapterFactory.java │ └── res │ ├── layout │ ├── activity_main.xml │ ├── activity_repos.xml │ └── list_item_repo.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | ############ 2 | # IntelliJ # 3 | ############ 4 | 5 | *.iml 6 | *.ipr 7 | *.iws 8 | .idea/ 9 | out/ 10 | 11 | ########## 12 | # Gradle # 13 | ########## 14 | 15 | .gradle/ 16 | build 17 | !build.gradle 18 | !settings.gradle 19 | 20 | ################## 21 | # Android Studio # 22 | ################## 23 | 24 | local.properties 25 | *Test Results*.html 26 | captures* 27 | 28 | ########## 29 | # Mac OS # 30 | ########## 31 | 32 | .DS_Store 33 | 34 | ########################## 35 | # Linux temporary files # 36 | ########################## 37 | 38 | *~ 39 | 40 | 41 | ############################### 42 | # Android studio config files # 43 | ############################### 44 | 45 | !.idea/codeStyleSettings.xml 46 | !.idea/copyright/profiles_settings.xml -------------------------------------------------------------------------------- /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 | # Android RxMxP Tutorial 2 | Project code from the rxmvp video tutorails 3 | 4 | The code for a upcoming tutorial series on the RxMVP pattern. Will be avaible on my youtube channel [here](https://www.youtube.com/channel/UC5fT02o8H1NnGfX7j1CmP6Q) 5 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'com.neenbedankt.android-apt' 3 | apply plugin: 'me.tatarka.retrolambda' 4 | 5 | android { 6 | compileSdkVersion 25 7 | buildToolsVersion "25.0.2" 8 | 9 | defaultConfig { 10 | applicationId "com.twistedeqations.rxmvp.sample" 11 | minSdkVersion 21 12 | targetSdkVersion 25 13 | versionCode 1 14 | versionName "1.0" 15 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 16 | } 17 | 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | 25 | compileOptions { 26 | sourceCompatibility JavaVersion.VERSION_1_8 27 | targetCompatibility JavaVersion.VERSION_1_8 28 | } 29 | 30 | packagingOptions { 31 | exclude 'META-INF/LICENSE.txt' 32 | exclude 'META-INF/NOTICE.txt' 33 | } 34 | } 35 | 36 | dependencies { 37 | 38 | retrolambdaConfig 'net.orfjackal.retrolambda:retrolambda:2.4.0' 39 | 40 | //AutoValue 41 | provided "com.google.auto.value:auto-value:1.2" 42 | provided 'com.ryanharter.auto.value:auto-value-gson:0.4.5' 43 | compile 'com.ryanharter.auto.value:auto-value-parcel-adapter:0.2.5' 44 | apt "com.google.auto.value:auto-value:1.2" 45 | apt 'com.ryanharter.auto.value:auto-value-gson:0.4.5' 46 | apt 'com.ryanharter.auto.value:auto-value-parcel:0.2.5' 47 | 48 | //Dagger 2 49 | compile "com.google.dagger:dagger:2.8" 50 | apt "com.google.dagger:dagger-compiler:2.8" 51 | 52 | //Network 53 | compile 'com.squareup.picasso:picasso:2.5.2' 54 | compile 'com.squareup.retrofit2:retrofit:2.1.0' 55 | compile 'com.squareup.retrofit2:converter-gson:2.1.0' 56 | compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0' 57 | compile 'com.squareup.okhttp3:okhttp:3.5.0' 58 | compile 'com.squareup.okhttp3:logging-interceptor:3.5.0' 59 | compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0' 60 | 61 | //Gson 62 | compile 'com.google.code.gson:gson:2.8.0' 63 | compile ('com.fatboyindustrial.gson-jodatime-serialisers:gson-jodatime-serialisers:1.5.0') { 64 | exclude group: "joda-time", module: "joda-time" 65 | } 66 | 67 | //Butterknife 68 | compile 'com.jakewharton:butterknife:8.0.1' 69 | apt 'com.jakewharton:butterknife-compiler:8.0.1' 70 | 71 | //rx java 72 | compile 'io.reactivex:rxjava:1.2.5' 73 | compile 'io.reactivex:rxandroid:1.2.1' 74 | compile "com.twistedequations.rx:rx-savestate:2.0.2" 75 | compile 'com.jakewharton.rxbinding:rxbinding:1.0.0' 76 | compile 'com.jakewharton.rxbinding:rxbinding-support-v4:1.0.0' 77 | compile 'com.jakewharton.rxbinding:rxbinding-appcompat-v7:1.0.0' 78 | 79 | //Android 80 | compile 'com.android.support:support-annotations:25.1.0' 81 | compile 'com.android.support:appcompat-v7:25.1.0' 82 | 83 | //Joda time 84 | compile 'net.danlew:android.joda:2.9.5.1' 85 | 86 | //debugging 87 | compile 'com.jakewharton.timber:timber:4.5.1' 88 | 89 | //Testing 90 | testCompile 'com.google.truth:truth:0.28' 91 | testCompile 'org.mockito:mockito-core:1.10.19' 92 | testCompile 'junit:junit:4.12' 93 | } 94 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/patrickd/Desktop/development/android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -dontwarn java.lang.invoke.* -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/activities/home/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.activities.home; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import com.twistedeqations.rxmvp.sample.activities.home.dagger.DaggerHomeComponent; 6 | import com.twistedeqations.rxmvp.sample.activities.home.dagger.HomeModule; 7 | import com.twistedeqations.rxmvp.sample.activities.home.mvp.HomePresenter; 8 | import com.twistedeqations.rxmvp.sample.activities.home.mvp.HomeView; 9 | import com.twistedeqations.rxmvp.sample.app.GithubApplication; 10 | import javax.inject.Inject; 11 | 12 | public class MainActivity extends AppCompatActivity { 13 | 14 | @Inject 15 | HomeView view; 16 | 17 | @Inject 18 | HomePresenter presenter; 19 | 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | 24 | DaggerHomeComponent.builder() 25 | .appComponent(GithubApplication.get(this).component()) 26 | .homeModule(new HomeModule(this)) 27 | .build().inject(this); 28 | 29 | setContentView(view); 30 | presenter.onCreate(); 31 | } 32 | 33 | @Override 34 | protected void onDestroy() { 35 | super.onDestroy(); 36 | presenter.onDestroy(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/activities/home/dagger/HomeComponent.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.activities.home.dagger; 2 | 3 | import android.content.Context; 4 | import com.twistedeqations.rxmvp.sample.activities.home.MainActivity; 5 | import com.twistedeqations.rxmvp.sample.app.dagger.AppComponent; 6 | import com.twistedeqations.rxmvp.sample.app.dagger.module.AppModule; 7 | import dagger.Component; 8 | 9 | @HomeScope 10 | @Component(modules = { HomeModule.class }, dependencies = AppComponent.class) 11 | public interface HomeComponent { 12 | 13 | void inject(MainActivity mainActivity); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/activities/home/dagger/HomeModule.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.activities.home.dagger; 2 | 3 | import android.app.Activity; 4 | import com.twistedeqations.rxmvp.sample.activities.home.mvp.HomeModel; 5 | import com.twistedeqations.rxmvp.sample.activities.home.mvp.HomePresenter; 6 | import com.twistedeqations.rxmvp.sample.activities.home.mvp.HomeView; 7 | import com.twistedeqations.rxmvp.sample.app.network.GithubNetwork; 8 | import dagger.Module; 9 | import dagger.Provides; 10 | 11 | @Module 12 | public class HomeModule { 13 | 14 | private final Activity activity; 15 | 16 | public HomeModule(Activity activity) { 17 | this.activity = activity; 18 | } 19 | 20 | @Provides 21 | @HomeScope 22 | public HomeView view() { 23 | return new HomeView(activity); 24 | } 25 | @Provides 26 | @HomeScope 27 | public HomeModel model(GithubNetwork githubNetwork) { 28 | return new HomeModel(activity, githubNetwork); 29 | } 30 | 31 | @Provides 32 | @HomeScope 33 | public HomePresenter homePresenter(HomeView homeView, HomeModel model) { 34 | return new HomePresenter(homeView, model); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/activities/home/dagger/HomeScope.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.activities.home.dagger; 2 | 3 | import javax.inject.Scope; 4 | 5 | @Scope 6 | public @interface HomeScope { 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/activities/home/mvp/HomeModel.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.activities.home.mvp; 2 | 3 | import android.app.Activity; 4 | import com.twistedeqations.rxmvp.sample.activities.repos.ReposActivity; 5 | import com.twistedeqations.rxmvp.sample.app.network.GithubNetwork; 6 | import com.twistedeqations.rxmvp.sample.app.network.model.GitHubRepo; 7 | import com.twistedequations.rxstate.RxSaveState; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import rx.Observable; 11 | 12 | public class HomeModel { 13 | 14 | public static final String REPOLIST_STATE_KEY = "REPOLIST_STATE_KEY"; 15 | private final Activity activity; 16 | private final GithubNetwork githubNetwork; 17 | 18 | public HomeModel(Activity activity, GithubNetwork githubNetwork) { 19 | this.activity = activity; 20 | this.githubNetwork = githubNetwork; 21 | } 22 | 23 | public Observable> getUserReops(String username) { 24 | return githubNetwork.getReposForUser(username); 25 | } 26 | 27 | public void saveRepoListState(List repoList) { 28 | RxSaveState.updateSaveState(activity, bundle -> { 29 | bundle.putParcelableArrayList(REPOLIST_STATE_KEY, new ArrayList<>(repoList)); 30 | }); 31 | } 32 | 33 | public Observable> getReposFromSaveState() { 34 | return RxSaveState.getSavedState(activity) 35 | .map(bundle -> bundle.getParcelableArrayList(REPOLIST_STATE_KEY)); 36 | } 37 | 38 | public void startRepoActivity(List repoList) { 39 | ReposActivity.start(activity, repoList); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/activities/home/mvp/HomePresenter.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.activities.home.mvp; 2 | 3 | import rx.Subscription; 4 | import rx.android.schedulers.AndroidSchedulers; 5 | import rx.schedulers.Schedulers; 6 | import rx.subscriptions.CompositeSubscription; 7 | 8 | @SuppressWarnings("Convert2MethodRef") 9 | public class HomePresenter { 10 | 11 | private final HomeView view; 12 | private final HomeModel model; 13 | private final CompositeSubscription compositeSubscription = new CompositeSubscription(); 14 | 15 | public HomePresenter(HomeView view, HomeModel model) { 16 | this.view = view; 17 | this.model = model; 18 | } 19 | 20 | public void onCreate() { 21 | compositeSubscription.add(observeLookupButton()); 22 | compositeSubscription.add(loadSavedState()); 23 | } 24 | 25 | public void onDestroy() { 26 | compositeSubscription.clear(); 27 | } 28 | 29 | private Subscription loadSavedState() { 30 | return model.getReposFromSaveState() 31 | .subscribe(gitHubRepoList -> view.setMessage("Look up button clicked - " + gitHubRepoList)); 32 | } 33 | 34 | private Subscription observeLookupButton() { 35 | return view.observeButton() 36 | .doOnNext(__ -> view.showLoading(true)) 37 | .map(__ -> view.getUsernameEdit()) 38 | .observeOn(Schedulers.io()) 39 | .switchMap(username -> model.getUserReops(username)) 40 | .observeOn(AndroidSchedulers.mainThread()) 41 | .doOnNext(gitHubRepoList -> model.saveRepoListState(gitHubRepoList)) 42 | .doOnEach(__ -> view.showLoading(false)) 43 | .retry() 44 | .subscribe(gitHubRepoList -> { 45 | model.startRepoActivity(gitHubRepoList); 46 | }); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/activities/home/mvp/HomeView.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.activities.home.mvp; 2 | 3 | 4 | import android.annotation.SuppressLint; 5 | import android.app.Activity; 6 | import android.app.ProgressDialog; 7 | import android.support.v7.widget.Toolbar; 8 | import android.widget.Button; 9 | import android.widget.EditText; 10 | import android.widget.FrameLayout; 11 | import android.widget.Toast; 12 | import butterknife.BindView; 13 | import butterknife.ButterKnife; 14 | import com.jakewharton.rxbinding.view.RxView; 15 | import com.twistedeqations.rxmvp.sample.R; 16 | import rx.Observable; 17 | 18 | @SuppressLint("ViewConstructor") 19 | public class HomeView extends FrameLayout { 20 | 21 | @BindView(R.id.edit_username) 22 | EditText usernameEditText; 23 | 24 | @BindView(R.id.btn_username_lookup) 25 | Button usernameLookUpBtn; 26 | 27 | @BindView(R.id.main_toolbar) 28 | Toolbar toolbar; 29 | 30 | private final ProgressDialog progressDialog = new ProgressDialog(getContext()); 31 | 32 | public HomeView(Activity activity) { 33 | super(activity); 34 | 35 | inflate(getContext(), R.layout.activity_main, this); 36 | 37 | progressDialog.setMessage("Looking up user"); 38 | ButterKnife.bind(this); 39 | } 40 | 41 | public void setMessage(String message) { 42 | Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show(); 43 | } 44 | 45 | public Observable observeButton() { 46 | return RxView.clicks(usernameLookUpBtn); 47 | } 48 | 49 | public String getUsernameEdit() { 50 | return usernameEditText.getText().toString(); 51 | } 52 | 53 | public void showLoading(boolean loading) { 54 | if(loading) { 55 | progressDialog.show(); 56 | } else { 57 | progressDialog.dismiss(); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/activities/repos/ReposActivity.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.activities.repos; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.support.v7.app.AppCompatActivity; 7 | import com.twistedeqations.rxmvp.sample.activities.repos.dagger.DaggerReposComponent; 8 | import com.twistedeqations.rxmvp.sample.activities.repos.dagger.ReopsModule; 9 | import com.twistedeqations.rxmvp.sample.activities.repos.mvp.ReposPresenter; 10 | import com.twistedeqations.rxmvp.sample.activities.repos.mvp.view.ReposView; 11 | import com.twistedeqations.rxmvp.sample.app.GithubApplication; 12 | import com.twistedeqations.rxmvp.sample.app.network.model.GitHubRepo; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import javax.inject.Inject; 16 | 17 | public class ReposActivity extends AppCompatActivity { 18 | 19 | public static final String INTENT_DATA_REPOS = "repoList"; 20 | 21 | public static void start(Context context, List repoList) { 22 | Intent intent = new Intent(context, ReposActivity.class); 23 | intent.putExtra(INTENT_DATA_REPOS, new ArrayList<>(repoList)); 24 | context.startActivity(intent); 25 | } 26 | 27 | @Inject 28 | ReposView view; 29 | 30 | @Inject 31 | ReposPresenter presenter; 32 | 33 | @Override 34 | protected void onCreate(Bundle savedInstanceState) { 35 | super.onCreate(savedInstanceState); 36 | 37 | DaggerReposComponent.builder() 38 | .appComponent(GithubApplication.get(this).component()) 39 | .reopsModule(new ReopsModule(this)) 40 | .build().inject(this); 41 | 42 | setContentView(view); 43 | presenter.onCreate(); 44 | } 45 | 46 | @Override 47 | protected void onDestroy() { 48 | super.onDestroy(); 49 | presenter.onDestroy(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/activities/repos/dagger/ReopsModule.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.activities.repos.dagger; 2 | 3 | import android.app.Activity; 4 | import com.twistedeqations.rxmvp.sample.activities.repos.mvp.ReopsModel; 5 | import com.twistedeqations.rxmvp.sample.activities.repos.mvp.ReposPresenter; 6 | import com.twistedeqations.rxmvp.sample.activities.repos.mvp.view.ReposView; 7 | import com.twistedeqations.rxmvp.sample.app.network.GithubNetwork; 8 | import dagger.Module; 9 | import dagger.Provides; 10 | 11 | @Module 12 | public class ReopsModule { 13 | 14 | private final Activity activity; 15 | 16 | public ReopsModule(Activity activity) { 17 | this.activity = activity; 18 | } 19 | 20 | @Provides 21 | @ReposScope 22 | public ReposView view() { 23 | return new ReposView(activity); 24 | } 25 | @Provides 26 | @ReposScope 27 | public ReopsModel model() { 28 | return new ReopsModel(activity); 29 | } 30 | 31 | @Provides 32 | @ReposScope 33 | public ReposPresenter homePresenter(ReposView homeView, ReopsModel model) { 34 | return new ReposPresenter(homeView, model); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/activities/repos/dagger/ReposComponent.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.activities.repos.dagger; 2 | 3 | import com.twistedeqations.rxmvp.sample.activities.repos.ReposActivity; 4 | import com.twistedeqations.rxmvp.sample.app.dagger.AppComponent; 5 | import dagger.Component; 6 | 7 | @ReposScope 8 | @Component(modules = { ReopsModule.class }, dependencies = AppComponent.class) 9 | public interface ReposComponent { 10 | 11 | void inject(ReposActivity mainActivity); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/activities/repos/dagger/ReposScope.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.activities.repos.dagger; 2 | 3 | import javax.inject.Scope; 4 | 5 | @Scope 6 | public @interface ReposScope { 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/activities/repos/mvp/ReopsModel.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.activities.repos.mvp; 2 | 3 | import android.app.Activity; 4 | import com.twistedeqations.rxmvp.sample.activities.repos.ReposActivity; 5 | import com.twistedeqations.rxmvp.sample.app.network.GithubNetwork; 6 | import com.twistedeqations.rxmvp.sample.app.network.model.GitHubRepo; 7 | import com.twistedequations.rxstate.RxSaveState; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import rx.Observable; 11 | 12 | public class ReopsModel { 13 | 14 | private final Activity activity; 15 | 16 | public ReopsModel(Activity activity) { 17 | this.activity = activity; 18 | } 19 | 20 | public List gitHubReposIntent() { 21 | return activity.getIntent().getParcelableArrayListExtra(ReposActivity.INTENT_DATA_REPOS); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/activities/repos/mvp/ReposPresenter.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.activities.repos.mvp; 2 | 3 | import com.twistedeqations.rxmvp.sample.activities.repos.mvp.view.ReposView; 4 | import rx.Subscription; 5 | import rx.android.schedulers.AndroidSchedulers; 6 | import rx.schedulers.Schedulers; 7 | import rx.subscriptions.CompositeSubscription; 8 | 9 | @SuppressWarnings("Convert2MethodRef") 10 | public class ReposPresenter { 11 | 12 | private final ReposView view; 13 | private final ReopsModel model; 14 | private final CompositeSubscription compositeSubscription = new CompositeSubscription(); 15 | 16 | public ReposPresenter(ReposView view, ReopsModel model) { 17 | this.view = view; 18 | this.model = model; 19 | } 20 | 21 | public void onCreate() { 22 | setIntentData(); 23 | } 24 | 25 | public void onDestroy() { 26 | compositeSubscription.clear(); 27 | } 28 | 29 | private void setIntentData() { 30 | view.setData(model.gitHubReposIntent()); 31 | } 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/activities/repos/mvp/view/ReposAdapter.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.activities.repos.mvp.view; 2 | 3 | import android.view.View; 4 | import android.view.ViewGroup; 5 | import android.widget.BaseAdapter; 6 | import com.twistedeqations.rxmvp.sample.app.network.model.GitHubRepo; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class ReposAdapter extends BaseAdapter { 11 | 12 | private final List repoList = new ArrayList<>(0); 13 | 14 | @Override 15 | public int getCount() { 16 | return repoList.size(); 17 | } 18 | 19 | @Override 20 | public GitHubRepo getItem(int position) { 21 | return repoList.get(position); 22 | } 23 | 24 | @Override 25 | public long getItemId(int position) { 26 | return repoList.get(position).id().hashCode(); 27 | } 28 | 29 | @Override 30 | public View getView(int position, View convertView, ViewGroup parent) { 31 | ReposListItem listItem; 32 | 33 | if (convertView == null) { 34 | listItem = new ReposListItem(parent.getContext()); 35 | } else { 36 | listItem = ReposListItem.class.cast(convertView); 37 | } 38 | listItem.setRepo(repoList.get(position)); 39 | return listItem; 40 | } 41 | 42 | public void swapData(List repoList) { 43 | this.repoList.clear(); 44 | if (repoList != null && !repoList.isEmpty()) { 45 | this.repoList.addAll(repoList); 46 | } 47 | notifyDataSetChanged(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/activities/repos/mvp/view/ReposListItem.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.activities.repos.mvp.view; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.Toolbar; 5 | import android.widget.FrameLayout; 6 | import android.widget.TextView; 7 | import butterknife.BindView; 8 | import butterknife.ButterKnife; 9 | import com.twistedeqations.rxmvp.sample.R; 10 | import com.twistedeqations.rxmvp.sample.app.network.model.GitHubRepo; 11 | 12 | public class ReposListItem extends FrameLayout { 13 | 14 | @BindView(R.id.repo_name) 15 | TextView nameTextView; 16 | 17 | @BindView(R.id.repo_desc) 18 | TextView descTextView; 19 | 20 | public ReposListItem(Context context) { 21 | super(context); 22 | 23 | inflate(getContext(), R.layout.list_item_repo, this); 24 | ButterKnife.bind(this); 25 | } 26 | 27 | public void setRepo(GitHubRepo gitHubRepo) { 28 | nameTextView.setText(gitHubRepo.name()); 29 | descTextView.setText(gitHubRepo.description()); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/activities/repos/mvp/view/ReposView.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.activities.repos.mvp.view; 2 | 3 | 4 | import android.annotation.SuppressLint; 5 | import android.app.Activity; 6 | import android.app.ProgressDialog; 7 | import android.support.v7.widget.Toolbar; 8 | import android.widget.Button; 9 | import android.widget.EditText; 10 | import android.widget.FrameLayout; 11 | import android.widget.ListView; 12 | import android.widget.Toast; 13 | import butterknife.BindView; 14 | import butterknife.ButterKnife; 15 | import com.jakewharton.rxbinding.view.RxView; 16 | import com.twistedeqations.rxmvp.sample.R; 17 | import com.twistedeqations.rxmvp.sample.app.network.model.GitHubRepo; 18 | import java.util.List; 19 | import rx.Observable; 20 | 21 | @SuppressLint("ViewConstructor") 22 | public class ReposView extends FrameLayout { 23 | 24 | @BindView(R.id.repos_list) 25 | ListView listView; 26 | 27 | @BindView(R.id.repos_toolbar) 28 | Toolbar toolbar; 29 | 30 | private final ReposAdapter reposAdapter = new ReposAdapter(); 31 | 32 | public ReposView(Activity activity) { 33 | super(activity); 34 | 35 | inflate(getContext(), R.layout.activity_repos, this); 36 | ButterKnife.bind(this); 37 | 38 | listView.setAdapter(reposAdapter); 39 | } 40 | 41 | public void setMessage(String message) { 42 | Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show(); 43 | } 44 | 45 | public void setData(List gitHubRepoList) { 46 | reposAdapter.swapData(gitHubRepoList); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/app/GithubApplication.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.app; 2 | 3 | import android.app.Activity; 4 | import android.app.Application; 5 | import android.app.Service; 6 | import com.twistedeqations.rxmvp.sample.BuildConfig; 7 | import com.twistedeqations.rxmvp.sample.app.dagger.AppComponent; 8 | import com.twistedeqations.rxmvp.sample.app.dagger.DaggerAppComponent; 9 | import com.twistedeqations.rxmvp.sample.app.dagger.module.AppModule; 10 | import com.twistedeqations.rxmvp.sample.ext.Constants; 11 | import net.danlew.android.joda.JodaTimeAndroid; 12 | import timber.log.Timber; 13 | 14 | public class GithubApplication extends Application { 15 | 16 | public static GithubApplication get(Activity activity) { 17 | return (GithubApplication) activity.getApplication(); 18 | } 19 | 20 | public static GithubApplication get(Service service) { 21 | return (GithubApplication) service.getApplication(); 22 | } 23 | 24 | private AppComponent appComponent; 25 | 26 | @Override 27 | public void onCreate() { 28 | super.onCreate(); 29 | 30 | if(BuildConfig.DEBUG) { 31 | Timber.plant(new Timber.DebugTree(){ 32 | @Override 33 | protected void log(int priority, String tag, String message, Throwable t) { 34 | super.log(priority, Constants.LOGTAG, message, t); 35 | } 36 | }); 37 | } 38 | 39 | JodaTimeAndroid.init(this); 40 | 41 | appComponent = DaggerAppComponent.builder() 42 | .appModule(new AppModule(this)) 43 | .build(); 44 | } 45 | 46 | public AppComponent component() { 47 | return appComponent; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/app/dagger/AppComponent.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.app.dagger; 2 | 3 | import android.content.Context; 4 | import com.squareup.picasso.Picasso; 5 | import com.twistedeqations.rxmvp.sample.app.dagger.module.AppModule; 6 | import com.twistedeqations.rxmvp.sample.app.dagger.module.GsonModule; 7 | import com.twistedeqations.rxmvp.sample.app.dagger.module.NetworkModule; 8 | import com.twistedeqations.rxmvp.sample.app.network.GithubNetwork; 9 | import dagger.Component; 10 | import okhttp3.OkHttpClient; 11 | 12 | @AppScope 13 | @Component(modules = { AppModule.class , NetworkModule.class, GsonModule.class}) 14 | public interface AppComponent { 15 | 16 | Context context(); 17 | 18 | OkHttpClient okhttpClient(); 19 | 20 | GithubNetwork githubNetwork(); 21 | 22 | Picasso picasso(); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/app/dagger/AppScope.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.app.dagger; 2 | 3 | public @interface AppScope { 4 | } 5 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/app/dagger/module/AppModule.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.app.dagger.module; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import com.twistedeqations.rxmvp.sample.app.dagger.AppScope; 6 | import dagger.Module; 7 | import dagger.Provides; 8 | 9 | @Module 10 | public class AppModule { 11 | 12 | private final Context context; 13 | 14 | public AppModule(Application application) { 15 | this.context = application.getApplicationContext(); 16 | } 17 | 18 | @AppScope 19 | @Provides 20 | public Context context() { 21 | return context; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/app/dagger/module/GsonModule.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.app.dagger.module; 2 | 3 | import com.fatboyindustrial.gsonjodatime.Converters; 4 | import com.google.gson.Gson; 5 | import com.google.gson.GsonBuilder; 6 | import com.twistedeqations.rxmvp.sample.app.dagger.AppScope; 7 | import com.twistedeqations.rxmvp.sample.ext.GithubAdapterFactory; 8 | import dagger.Module; 9 | import dagger.Provides; 10 | 11 | @Module 12 | public class GsonModule { 13 | 14 | @AppScope 15 | @Provides 16 | public Gson context() { 17 | return Converters.registerAll(new GsonBuilder()) 18 | .registerTypeAdapterFactory(GithubAdapterFactory.create()) 19 | .create(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/app/dagger/module/NetworkModule.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.app.dagger.module; 2 | 3 | import android.content.Context; 4 | import com.google.gson.Gson; 5 | import com.jakewharton.picasso.OkHttp3Downloader; 6 | import com.squareup.picasso.Picasso; 7 | import com.twistedeqations.rxmvp.sample.app.dagger.AppScope; 8 | import com.twistedeqations.rxmvp.sample.app.network.GithubNetwork; 9 | import dagger.Module; 10 | import dagger.Provides; 11 | import java.io.File; 12 | import okhttp3.Cache; 13 | import okhttp3.OkHttpClient; 14 | import okhttp3.logging.HttpLoggingInterceptor; 15 | import retrofit2.Retrofit; 16 | import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; 17 | import retrofit2.converter.gson.GsonConverterFactory; 18 | import timber.log.Timber; 19 | 20 | import com.twistedeqations.rxmvp.sample.ext.Constants; 21 | 22 | @Module 23 | public class NetworkModule { 24 | 25 | @AppScope 26 | @Provides 27 | public Cache cache(Context context) { 28 | return new Cache(new File(context.getCacheDir(), Constants.HTTP_CACHE_DIR), 29 | Constants.HTTP_CACHE_SIZE); 30 | } 31 | 32 | @AppScope 33 | @Provides 34 | public OkHttpClient okHttpClient(HttpLoggingInterceptor loggingInterceptor, Cache cache) { 35 | return new OkHttpClient.Builder() 36 | .addInterceptor(loggingInterceptor) 37 | .cache(cache) 38 | .build(); 39 | } 40 | 41 | @AppScope 42 | @Provides 43 | public HttpLoggingInterceptor httpLoggingInterceptor() { 44 | HttpLoggingInterceptor logging = new HttpLoggingInterceptor( 45 | message -> Timber.d(message)); 46 | return logging.setLevel(HttpLoggingInterceptor.Level.BASIC); 47 | } 48 | 49 | @AppScope 50 | @Provides 51 | public Retrofit retrofit(OkHttpClient okHttpClient, Gson gson) { 52 | return new Retrofit.Builder() 53 | .client(okHttpClient) 54 | .baseUrl("https://api.github.com/") 55 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 56 | .addConverterFactory(GsonConverterFactory.create(gson)) 57 | .build(); 58 | } 59 | 60 | @AppScope 61 | @Provides 62 | public GithubNetwork githubNetwork(Retrofit retrofit) { 63 | return retrofit.create(GithubNetwork.class); 64 | } 65 | 66 | @AppScope 67 | @Provides 68 | public Picasso picasso(Context context, OkHttpClient okHttpClient) { 69 | return new Picasso.Builder(context) 70 | .downloader(new OkHttp3Downloader(okHttpClient)) 71 | .build(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/app/network/GithubNetwork.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.app.network; 2 | 3 | import com.twistedeqations.rxmvp.sample.app.network.model.GitHubRepo; 4 | import com.twistedeqations.rxmvp.sample.app.network.model.GithubUser; 5 | import java.util.List; 6 | import retrofit2.http.GET; 7 | import retrofit2.http.Path; 8 | import rx.Observable; 9 | 10 | public interface GithubNetwork { 11 | 12 | @GET("users/{user}/repos") 13 | Observable> getReposForUser(@Path("user") String user); 14 | 15 | @GET("repos/{user}/{repo}") 16 | Observable getRepo(@Path("user") String username, @Path("repo") String repo); 17 | 18 | @GET("user/{user}") 19 | Observable getUser(@Path("user") String username); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/app/network/model/GitHubRepo.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.app.network.model; 2 | 3 | import android.os.Parcelable; 4 | import android.support.annotation.Nullable; 5 | import com.google.auto.value.AutoValue; 6 | import com.google.gson.Gson; 7 | import com.google.gson.TypeAdapter; 8 | import com.google.gson.annotations.SerializedName; 9 | import com.ryanharter.auto.value.parcel.ParcelAdapter; 10 | import com.twistedeqations.rxmvp.sample.ext.DateTimeAdapter; 11 | import org.joda.time.DateTime; 12 | 13 | @AutoValue 14 | public abstract class GitHubRepo implements Parcelable { 15 | 16 | public static TypeAdapter typeAdapter(Gson gson) { 17 | return new AutoValue_GitHubRepo.GsonTypeAdapter(gson); 18 | } 19 | 20 | public abstract String id(); 21 | 22 | public abstract String name(); 23 | 24 | @SerializedName("full_name") 25 | public abstract String fullName(); 26 | 27 | @SerializedName("owner") 28 | public abstract GithubOwner owner(); 29 | 30 | @SerializedName("private") 31 | public abstract boolean isPrivate(); 32 | 33 | @SerializedName("html_url") 34 | public abstract String htmlUrl(); 35 | 36 | @Nullable 37 | @SerializedName("description") 38 | public abstract String description(); 39 | 40 | @SerializedName("fork") 41 | public abstract boolean fork(); 42 | 43 | @SerializedName("url") 44 | public abstract String url(); 45 | 46 | @SerializedName("created_at") 47 | @ParcelAdapter(DateTimeAdapter.class) 48 | public abstract DateTime created(); 49 | 50 | @SerializedName("updated_at") 51 | @ParcelAdapter(DateTimeAdapter.class) 52 | public abstract DateTime updated(); 53 | 54 | @SerializedName("pushed_at") 55 | @ParcelAdapter(DateTimeAdapter.class) 56 | public abstract DateTime pushedAt(); 57 | 58 | @SerializedName("git_url") 59 | public abstract String gitUrl(); 60 | 61 | @SerializedName("ssh_url") 62 | public abstract String sshUrl(); 63 | 64 | @SerializedName("size") 65 | public abstract int size(); 66 | 67 | @SerializedName("watchers_count") 68 | public abstract int watchersCount(); 69 | 70 | @SerializedName("forks_count") 71 | public abstract int forksCount(); 72 | 73 | @Nullable 74 | @SerializedName("language") 75 | public abstract String language(); 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/app/network/model/GithubOwner.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.app.network.model; 2 | 3 | import android.os.Parcelable; 4 | import com.google.auto.value.AutoValue; 5 | import com.google.gson.Gson; 6 | import com.google.gson.TypeAdapter; 7 | import com.google.gson.annotations.SerializedName; 8 | 9 | @AutoValue 10 | public abstract class GithubOwner implements Parcelable { 11 | 12 | public static TypeAdapter typeAdapter(Gson gson) { 13 | return new AutoValue_GithubOwner.GsonTypeAdapter(gson); 14 | } 15 | 16 | @SerializedName("login") 17 | public abstract String login(); 18 | 19 | @SerializedName("id") 20 | public abstract String id(); 21 | 22 | @SerializedName("avatar_url") 23 | public abstract String avatarUrl(); 24 | 25 | @SerializedName("url") 26 | public abstract String url(); 27 | 28 | @SerializedName("html_url") 29 | public abstract String htmlUrl(); 30 | 31 | @SerializedName("repos_url") 32 | public abstract String reposUrl(); 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/app/network/model/GithubUser.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.app.network.model; 2 | 3 | import android.os.Parcelable; 4 | import com.google.auto.value.AutoValue; 5 | import com.google.gson.Gson; 6 | import com.google.gson.TypeAdapter; 7 | import com.google.gson.annotations.SerializedName; 8 | import com.ryanharter.auto.value.parcel.ParcelAdapter; 9 | import com.twistedeqations.rxmvp.sample.ext.DateTimeAdapter; 10 | import org.joda.time.DateTime; 11 | 12 | @AutoValue 13 | public abstract class GithubUser implements Parcelable { 14 | 15 | public static TypeAdapter typeAdapter(Gson gson) { 16 | return new AutoValue_GithubUser.GsonTypeAdapter(gson); 17 | } 18 | 19 | @SerializedName("login") 20 | public abstract String login(); 21 | 22 | @SerializedName("id") 23 | public abstract String id(); 24 | 25 | @SerializedName("avatar_url") 26 | public abstract String avatarUrl(); 27 | 28 | @SerializedName("url") 29 | public abstract String url(); 30 | 31 | @SerializedName("html_url") 32 | public abstract String htmlUrl(); 33 | 34 | @SerializedName("name") 35 | public abstract String name(); 36 | 37 | @SerializedName("bio") 38 | public abstract String bio(); 39 | 40 | @SerializedName("followers") 41 | public abstract int followers(); 42 | 43 | @SerializedName("created_at") 44 | @ParcelAdapter(DateTimeAdapter.class) 45 | public abstract DateTime created(); 46 | 47 | @SerializedName("updated_at") 48 | @ParcelAdapter(DateTimeAdapter.class) 49 | public abstract DateTime updated(); 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/ext/Constants.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.ext; 2 | 3 | public final class Constants { 4 | 5 | private Constants() { 6 | } 7 | 8 | public static final String HTTP_CACHE_DIR = "okhttp_cache"; 9 | public static final String LOGTAG = "RxMVP"; 10 | public static final int HTTP_CACHE_SIZE = 10 * 1024 * 1024; //10MB 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/ext/DateTimeAdapter.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.ext; 2 | 3 | import android.os.Parcel; 4 | import com.ryanharter.auto.value.parcel.TypeAdapter; 5 | import org.joda.time.DateTime; 6 | import org.joda.time.format.DateTimeFormatter; 7 | import org.joda.time.format.ISODateTimeFormat; 8 | 9 | public class DateTimeAdapter implements TypeAdapter { 10 | 11 | private static final DateTimeFormatter formatter = ISODateTimeFormat.dateTime(); 12 | 13 | @Override 14 | public DateTime fromParcel(Parcel in) { 15 | return formatter.parseDateTime(in.readString()); 16 | } 17 | 18 | @Override 19 | public void toParcel(DateTime value, Parcel dest) { 20 | dest.writeString(formatter.print(value)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/twistedeqations/rxmvp/sample/ext/GithubAdapterFactory.java: -------------------------------------------------------------------------------- 1 | package com.twistedeqations.rxmvp.sample.ext; 2 | 3 | import com.google.gson.TypeAdapterFactory; 4 | import com.ryanharter.auto.value.gson.GsonTypeAdapterFactory; 5 | 6 | @GsonTypeAdapterFactory 7 | public abstract class GithubAdapterFactory implements TypeAdapterFactory { 8 | 9 | // Static factory method to access the package 10 | // private generated implementation 11 | public static TypeAdapterFactory create() { 12 | return new AutoValueGson_GithubAdapterFactory(); 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 17 | 18 | 25 | 26 |