├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── config.gradle ├── config └── quality │ ├── checkstyle │ └── checkstyle.xml │ ├── findbugs │ └── exclude-filter.xml │ ├── pmd │ └── ruleset.xml │ └── quality.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── proguards ├── proguard-glide-okhttp3.pro ├── proguard-glide.pro ├── proguard-rx-bus.pro ├── proguard-rx-java.pro ├── proguard-square-leakcanary.pro ├── proguard-square-okhttp.pro ├── proguard-square-okio.pro ├── proguard-square-retrofit2.pro └── retrolambda.pro ├── settings.gradle └── tiger ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src ├── androidTest └── java │ └── com │ └── hwangjr │ └── mvp │ └── ApplicationTest.java ├── main ├── AndroidManifest.xml ├── java │ └── com │ │ └── hwangjr │ │ ├── mvp │ │ ├── MVPApplication.java │ │ ├── base │ │ │ ├── BaseActivity.java │ │ │ ├── BaseFragment.java │ │ │ ├── presenter │ │ │ │ ├── ActivityPresenter.java │ │ │ │ ├── FragmentPresenter.java │ │ │ │ └── Presenter.java │ │ │ ├── recycler │ │ │ │ ├── PullToRefreshRecyclerFragmentPresenter.java │ │ │ │ ├── PullToRefreshRecyclerFragmentView.java │ │ │ │ ├── RecyclerAdapter.java │ │ │ │ ├── RecyclerSectionAdapter.java │ │ │ │ ├── RecyclerViewHolder.java │ │ │ │ └── RecyclerWithFooterAdapter.java │ │ │ └── view │ │ │ │ ├── DataView.java │ │ │ │ ├── MVPView.java │ │ │ │ ├── activity │ │ │ │ ├── ActivityDataView.java │ │ │ │ └── ActivityView.java │ │ │ │ ├── fragment │ │ │ │ └── FragmentView.java │ │ │ │ └── pulltorefresh │ │ │ │ ├── PullToRefreshActivityView.java │ │ │ │ └── PullToRefreshFragmentView.java │ │ ├── injection │ │ │ ├── components │ │ │ │ ├── ActivityComponent.java │ │ │ │ ├── AppComponent.java │ │ │ │ └── FragmentComponent.java │ │ │ ├── modules │ │ │ │ ├── ActivityModule.java │ │ │ │ ├── AppModule.java │ │ │ │ └── FragmentModule.java │ │ │ └── scopes │ │ │ │ ├── PerActivity.java │ │ │ │ └── PerFragment.java │ │ ├── utils │ │ │ ├── ActivityAssistant.java │ │ │ ├── DateUtils.java │ │ │ ├── DensityUtils.java │ │ │ ├── LoggerUtils.java │ │ │ ├── SDCardUtils.java │ │ │ └── StatusBarUtils.java │ │ └── widget │ │ │ ├── DividerItemDecoration.java │ │ │ └── SnackbarWrapper.java │ │ └── tiger │ │ ├── AppApplication.java │ │ └── ui │ │ ├── MainActivity.java │ │ └── MainActivityPresenter.java └── res │ ├── anim │ ├── slide_in_bottom.xml │ ├── slide_in_right.xml │ ├── slide_out_bottom.xml │ └── slide_out_right.xml │ ├── layout │ ├── activity_main.xml │ ├── mvp_layout_coordinator.xml │ └── mvp_more_progress.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 │ ├── arrays.xml │ ├── colors.xml │ ├── dimens.xml │ ├── ids.xml │ ├── strings.xml │ └── styles.xml └── test └── java └── com └── hwangjr └── mvp └── ExampleUnitTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # Intellij 36 | *.iml 37 | .idea/workspace.xml 38 | .idea/ 39 | 40 | # Keystore files 41 | *.jks 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tiger-Pro For Android 2 | 3 | Sample Android app that i use at Tiger Application as a reference for new Android projects. It demonstrates the architecture, tools that i use when developing for the Android platform. 4 | 5 | (Nowadays, i have used this pattern in at least 3 projects, it turns out to speed up the android development and make a cool application.) 6 | 7 | Libraries and tools included: 8 | 9 | - Support libraries 10 | - RecyclerViews 11 | - [RxJava](https://github.com/ReactiveX/RxJava) and [RxAndroid](https://github.com/ReactiveX/RxAndroid) 12 | - [RxBus](https://github.com/AndroidKnife/RxBus) 13 | - [Retrofit 2](http://square.github.io/retrofit/) 14 | - [OKHttp](https://github.com/square/okhttp) 15 | - [Dagger 2](http://google.github.io/dagger/) 16 | - [Butterknife](https://github.com/JakeWharton/butterknife) 17 | - [Timber](https://github.com/JakeWharton/timber) 18 | - [Glide](https://github.com/bumptech/glide) 19 | - Functional tests with [Espresso](https://google.github.io/android-testing-support-library/docs/espresso/index.html) 20 | - [Robolectric](http://robolectric.org/) 21 | - [Mockito](http://mockito.org/) 22 | - [Checkstyle](http://checkstyle.sourceforge.net/), [PMD](https://pmd.github.io/) and [Findbugs](http://findbugs.sourceforge.net/) for code analysis 23 | 24 | ## Requirements 25 | 26 | - JDK 1.8 27 | - [Android SDK](http://developer.android.com/sdk/index.html). 28 | - Android N [(API 24) ](http://developer.android.com/tools/revisions/platforms.html). 29 | - Latest Android SDK Tools and build tools. 30 | 31 | ## Architecture 32 | 33 | This project follows Android architecture guidelines that are based on MVP. For more details, you can checkout [android-architecture](https://github.com/googlesamples/android-architecture) 34 | 35 | ### How to implement a new screen following MVP 36 | 37 | Imagine you have to implement a sign in screen (Include an activity only). 38 | 39 | 1. Create a new package under `ui` called `signin` 40 | 2. Create an new class called `SignInActivityView` that extends `ActivityView`. 41 | 3. Create a `SignInActivityViewPresenter` class that extends `ActivityPresenter` 42 | 4. Implement the methods in `SignInActivityView` and `SignInActivityViewPresenter`, add functions that your Activity requires to perform the necessary actions, e.g. `signIn(String email)`. Once the sign in action finishes you should call `view.showSignInSuccessful()` in presenter. 43 | 5. Create a `SignInActivityViewPresenterTest`and write unit tests for `signIn(email)`. 44 | 6. Fragments also provide the same workflow. 45 | 46 | **From above, you should have `SignInActivityView` and `SignInActivityViewPresenter` in your `ui.signin` package to define your MVP view.** 47 | 48 | ## Code Quality 49 | 50 | This project include code analysis tools and unit test tools, **but still working on unit test and functional test for this project.** 51 | 52 | ### Code Analysis tools 53 | 54 | The following code analysis tools are set up on this project: 55 | 56 | * [PMD](https://pmd.github.io/): It finds common programming flaws like unused variables, empty catch blocks, unnecessary object creation, and so forth. See [this project's PMD ruleset](config/quality/pmd/ruleset.xml). 57 | 58 | ``` 59 | ./gradlew pmd 60 | ``` 61 | 62 | * [Findbugs](http://findbugs.sourceforge.net/): This tool uses static analysis to find bugs in Java code. Unlike PMD, it uses compiled Java bytecode instead of source code. 63 | 64 | ``` 65 | ./gradlew findbugs 66 | ``` 67 | 68 | * [Checkstyle](http://checkstyle.sourceforge.net/): It ensures that the code style follows your team development guidelines. See [checkstyle config file](config/quality/checkstyle/checkstyle.xml). 69 | 70 | ``` 71 | ./gradlew checkstyle 72 | ``` 73 | 74 | ### The check task 75 | 76 | To ensure that your code is valid and stable use check: 77 | 78 | ``` 79 | ./gradlew check 80 | ``` 81 | 82 | This will run checkstyle -> findbugs -> PMD -> Android Lint -> Unit Test. All Done! 83 | 84 | ## New project setup 85 | 86 | To quickly start a new project, you can: 87 | 88 | 1. Keep the `mvp` package and rename the `tiger` package to your application. 89 | 2. Modify package in `build.gradle` and `AndroidManifest.xml`. 90 | 3. Add sign config to project. 91 | 4. Add proguard files to project, if it's the library proguard file, you should put it in `proguards` folder. 92 | 5. If you want to enable jni support, checkout `build.gradle` for more details. 93 | 6. All Done! Enjoy your development. 94 | 95 | ## TODO List 96 | 97 | 1. Unit Test For Project 98 | 2. Project Template for Android Studio IDE 99 | 3. Usage Examples For New Project 100 | 101 | ## FAQ 102 | **Q:** How to do pull requests?
103 | **A:** Ensure good code quality and consistent formatting. 104 | 105 | ## License 106 | 107 | ``` 108 | Copyright 2016 HwangJR, Inc. 109 | 110 | Licensed under the Apache License, Version 2.0 (the "License"); 111 | you may not use this file except in compliance with the License. 112 | You may obtain a copy of the License at 113 | 114 | http://www.apache.org/licenses/LICENSE-2.0 115 | 116 | Unless required by applicable law or agreed to in writing, software 117 | distributed under the License is distributed on an "AS IS" BASIS, 118 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 119 | See the License for the specific language governing permissions and 120 | limitations under the License. 121 | ``` -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | apply from: "config.gradle" 3 | 4 | buildscript { 5 | repositories { 6 | jcenter() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:2.2.2' 10 | classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' 11 | classpath 'me.tatarka:gradle-retrolambda:3.2.5' 12 | classpath 'me.tatarka.retrolambda.projectlombok:lombok.ast:0.2.3.a2' 13 | 14 | // NOTE: Do not place your application dependencies here; they belong 15 | // in the individual module build.gradle files 16 | } 17 | // Exclude the lombok version that the android plugin depends on. 18 | configurations.classpath.exclude group: 'com.android.tools.external.lombok' 19 | } 20 | 21 | allprojects { 22 | repositories { 23 | jcenter() 24 | } 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /config.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | android = [ 3 | compileSdkVersion: 24, 4 | buildToolsVersion: "24.0.3", 5 | minSdkVersion : 15, 6 | targetSdkVersion : 24, 7 | ] 8 | deps = [ 9 | // Android 10 | supportannotations : 'com.android.support:support-annotations:24.2.0', 11 | appcompat : 'com.android.support:appcompat-v7:24.2.0', 12 | recyclerview : 'com.android.support:recyclerview-v7:24.2.0', 13 | design : 'com.android.support:design:24.2.0', 14 | 15 | // RxJava 16 | rxjava : 'io.reactivex:rxjava:1.2.0', 17 | rxandroid : 'io.reactivex:rxandroid:1.2.1', 18 | 19 | // ButterKnife 20 | butterknife : 'com.jakewharton:butterknife:8.4.0', 21 | butterknifecompiler : 'com.jakewharton:butterknife-compiler:8.4.0', 22 | 23 | // Dagger2 24 | dagger : 'com.google.dagger:dagger:2.7', 25 | daggercompiler : 'com.google.dagger:dagger-compiler:2.7', 26 | // javaxannotation : 'javax.annotation:jsr250-api:1.0', 27 | 28 | // Retrofit 29 | retrofit : 'com.squareup.retrofit2:retrofit:2.1.0', 30 | gsonconvert : 'com.squareup.retrofit2:converter-gson:2.1.0', 31 | rxjavaadapter : 'com.squareup.retrofit2:adapter-rxjava:2.1.0', 32 | 33 | // OKHttp 34 | okhttp : 'com.squareup.okhttp3:okhttp:3.4.1', 35 | okhttpmockwebserver : 'com.squareup.okhttp3:mockwebserver:3.4.1', 36 | okhttploginterceptor : 'com.squareup.okhttp3:logging-interceptor:3.4.1', 37 | 38 | // Gson 39 | gson : 'com.google.code.gson:gson:2.7', 40 | 41 | // Glide 42 | glide : 'com.github.bumptech.glide:glide:3.7.0', 43 | glideokhttpintegration: 'com.github.bumptech.glide:okhttp3-integration:1.4.0@aar', 44 | 45 | // Timber 46 | timber : 'com.jakewharton.timber:timber:4.3.1', 47 | 48 | // Pull to refresh 49 | ultraptr : 'in.srain.cube:ultra-ptr:1.0.11', 50 | 51 | // Tab Layout 52 | flycotablayout : 'com.flyco.tablayout:FlycoTabLayout_Lib:2.0.6@aar', 53 | 54 | // RxBus 55 | rxbus : 'com.hwangjr.rxbus:rxbus:1.0.5', 56 | 57 | // Leak Canary 58 | leakcanary : 'com.squareup.leakcanary:leakcanary-android:1.5', 59 | leakcanarynoop : 'com.squareup.leakcanary:leakcanary-android-no-op:1.5', 60 | 61 | // Unit test dependencies 62 | junit : 'junit:junit:4.12', 63 | robolectric : 'org.robolectric:robolectric:3.1.1', 64 | mockito : 'org.mockito:mockito-core:1.+', 65 | hamcrestcore : 'org.hamcrest:hamcrest-core:1.3', 66 | hamcrestlibrary : 'org.hamcrest:hamcrest-library:1.3', 67 | hamcrestintegration : 'org.hamcrest:hamcrest-integration:1.3', 68 | 69 | // Integration test dependencies 70 | testrunner : 'com.android.support.test:runner:0.5', 71 | testrules : 'com.android.support.test:rules:0.5', 72 | espresso : 'com.android.support.test.espresso:espresso-core:2.2.2', 73 | dexmaker : 'com.crittercism.dexmaker:dexmaker:1.4', 74 | dexmakerdx : 'com.crittercism.dexmaker:dexmaker-dx:1.4', 75 | dexmakermockito : 'com.crittercism.dexmaker:dexmaker-mockito:1.4', 76 | spoon : 'com.squareup.spoon:spoon-client:1.3.1' 77 | ] 78 | } -------------------------------------------------------------------------------- /config/quality/checkstyle/checkstyle.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 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 | 54 | 55 | 56 | 57 | 58 | 60 | 61 | 62 | 63 | 65 | 66 | 67 | 68 | 70 | 71 | 72 | 73 | 74 | 76 | 77 | 78 | 79 | 81 | 82 | 83 | 84 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 114 | 116 | 118 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 144 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | -------------------------------------------------------------------------------- /config/quality/findbugs/exclude-filter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /config/quality/pmd/ruleset.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | Custom ruleset for ribot Android application 8 | 9 | .*/R.java 10 | .*/gen/.* 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 | -------------------------------------------------------------------------------- /config/quality/quality.gradle: -------------------------------------------------------------------------------- 1 | /** 2 | * Set up Checkstyle, Findbugs and PMD to perform extensive code analysis. 3 | * 4 | * Gradle tasks added: 5 | * - checkstyle 6 | * - findbugs 7 | * - pmd 8 | * 9 | * The three tasks above are added as dependencies of the check task so running check will 10 | * run all of them. 11 | */ 12 | 13 | apply plugin: 'checkstyle' 14 | apply plugin: 'findbugs' 15 | apply plugin: 'pmd' 16 | 17 | dependencies { 18 | checkstyle 'com.puppycrawl.tools:checkstyle:6.5' 19 | } 20 | 21 | def qualityConfigDir = "$project.rootDir/config/quality"; 22 | def reportsDir = "$project.buildDir/reports" 23 | 24 | check.dependsOn 'checkstyle', 'findbugs', 'pmd' 25 | 26 | task checkstyle(type: Checkstyle, group: 'Verification', description: 'Runs code style checks') { 27 | configFile file("$qualityConfigDir/checkstyle/checkstyle.xml") 28 | source 'src' 29 | include '**/*.java' 30 | 31 | reports { 32 | xml.enabled = true 33 | xml { 34 | destination "$reportsDir/checkstyle/checkstyle.xml" 35 | } 36 | } 37 | 38 | classpath = files() 39 | } 40 | 41 | task findbugs(type: FindBugs, 42 | group: 'Verification', 43 | description: 'Inspect java bytecode for bugs', 44 | dependsOn: ['compileDebugSources', 'compileReleaseSources']) { 45 | 46 | ignoreFailures = false 47 | effort = "max" 48 | reportLevel = "high" 49 | excludeFilter = new File("$qualityConfigDir/findbugs/exclude-filter.xml") 50 | classes = files("$project.rootDir/tiger/build/intermediates/classes") 51 | 52 | source 'src' 53 | include '**/*.java' 54 | exclude '**/gen/**' 55 | 56 | reports { 57 | xml.enabled = false 58 | html.enabled = true 59 | xml { 60 | destination "$reportsDir/findbugs/findbugs.xml" 61 | } 62 | html { 63 | destination "$reportsDir/findbugs/findbugs.html" 64 | } 65 | } 66 | 67 | classpath = files() 68 | } 69 | 70 | 71 | task pmd(type: Pmd, group: 'Verification', description: 'Inspect sourcecode for bugs') { 72 | ruleSetFiles = files("$qualityConfigDir/pmd/ruleset.xml") 73 | ignoreFailures = false 74 | ruleSets = [] 75 | 76 | source 'src' 77 | include '**/*.java' 78 | exclude '**/gen/**' 79 | 80 | reports { 81 | xml.enabled = true 82 | html.enabled = true 83 | xml { 84 | destination "$reportsDir/pmd/pmd.xml" 85 | } 86 | html { 87 | destination "$reportsDir/pmd/pmd.html" 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 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/AndroidKnife/Tiger-Pro/cc4477b2842d895b8d31dad5060f7740fc967d40/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Sep 18 11:42:25 HKT 2016 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.14.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 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /proguards/proguard-glide-okhttp3.pro: -------------------------------------------------------------------------------- 1 | # Glide Integration specific rules # 2 | # [Integration Libraries · bumptech/glide Wiki](https://github.com/bumptech/glide/wiki/Integration-Libraries) 3 | 4 | -keep class com.bumptech.glide.integration.okhttp3.OkHttpGlideModule -------------------------------------------------------------------------------- /proguards/proguard-glide.pro: -------------------------------------------------------------------------------- 1 | # Glide specific rules # 2 | # https://github.com/bumptech/glide 3 | 4 | -keep public class * implements com.bumptech.glide.module.GlideModule 5 | -keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** { 6 | **[] $VALUES; 7 | public *; 8 | } 9 | 10 | -------------------------------------------------------------------------------- /proguards/proguard-rx-bus.pro: -------------------------------------------------------------------------------- 1 | # RxBus 1.0.3 2 | 3 | -keepattributes *Annotation* 4 | -keepclassmembers class ** { 5 | @com.hwangjr.rxbus.annotation.Subscribe public *; 6 | @com.hwangjr.rxbus.annotation.Produce public *; 7 | } -------------------------------------------------------------------------------- /proguards/proguard-rx-java.pro: -------------------------------------------------------------------------------- 1 | # RxJava 1.0.15 2 | 3 | -dontwarn sun.misc.** 4 | -keep class rx.schedulers.Schedulers { 5 | public static ; 6 | } 7 | -keep class rx.schedulers.ImmediateScheduler { 8 | public ; 9 | } 10 | -keep class rx.schedulers.TestScheduler { 11 | public ; 12 | } 13 | -keep class rx.schedulers.Schedulers { 14 | public static ** test(); 15 | } 16 | -keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* { 17 | long producerIndex; 18 | long consumerIndex; 19 | } 20 | -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef { 21 | rx.internal.util.atomic.LinkedQueueNode producerNode; 22 | } 23 | -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef { 24 | rx.internal.util.atomic.LinkedQueueNode consumerNode; 25 | } -------------------------------------------------------------------------------- /proguards/proguard-square-leakcanary.pro: -------------------------------------------------------------------------------- 1 | # LeakCanary specific rules # 2 | # https://github.com/square/leakcanary 3 | 4 | -keep class org.eclipse.mat.** { *; } 5 | -keep class com.squareup.leakcanary.** { *; } 6 | -keep class com.squareup.haha.** { *; } 7 | -keep class sun.misc.** { *; } 8 | -keep class sun.nio.** { *; } 9 | -dontwarn com.squareup.leakcanary.** -------------------------------------------------------------------------------- /proguards/proguard-square-okhttp.pro: -------------------------------------------------------------------------------- 1 | # OkHttp 2 | -keepattributes Signature 3 | -keepattributes *Annotation* 4 | -keep class com.squareup.okhttp.** { *; } 5 | -keep interface com.squareup.okhttp.** { *; } 6 | -dontwarn com.squareup.okhttp.** -------------------------------------------------------------------------------- /proguards/proguard-square-okio.pro: -------------------------------------------------------------------------------- 1 | # Okio 2 | -keep class sun.misc.Unsafe { *; } 3 | -dontwarn java.nio.file.* 4 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement 5 | -dontwarn okio.** -------------------------------------------------------------------------------- /proguards/proguard-square-retrofit2.pro: -------------------------------------------------------------------------------- 1 | -dontwarn retrofit2.** 2 | -keep class retrofit2.** { *; } 3 | -keepattributes Signature 4 | -keepattributes Exceptions -------------------------------------------------------------------------------- /proguards/retrolambda.pro: -------------------------------------------------------------------------------- 1 | -dontwarn java.lang.invoke.* -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':tiger' 2 | -------------------------------------------------------------------------------- /tiger/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /tiger/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'com.neenbedankt.android-apt' 3 | apply plugin: 'me.tatarka.retrolambda' 4 | apply from: '../config/quality/quality.gradle' 5 | 6 | dependencies { 7 | compile fileTree(dir: 'libs', include: ['*.jar']) 8 | 9 | // Support 10 | compile deps.appcompat 11 | compile deps.recyclerview 12 | compile deps.design 13 | compile deps.supportannotations 14 | // RxJava 15 | compile deps.rxjava 16 | compile deps.rxandroid 17 | // RxBus 18 | compile(deps.rxbus) { 19 | exclude group: 'com.hwangjr.utils', module: 'timber' 20 | } 21 | // ButterKnife 22 | compile deps.butterknife 23 | apt deps.butterknifecompiler 24 | // Dagger 25 | compile deps.dagger 26 | apt deps.daggercompiler 27 | // Retrofit 28 | compile deps.retrofit 29 | compile deps.gsonconvert 30 | compile deps.rxjavaadapter 31 | // OKHttp 32 | compile deps.okhttp 33 | compile deps.okhttploginterceptor 34 | testCompile deps.okhttpmockwebserver 35 | // Gson 36 | compile deps.gson 37 | // Glide 38 | compile deps.glide 39 | compile deps.glideokhttpintegration 40 | // Leak Canary 41 | releaseCompile deps.leakcanarynoop 42 | debugCompile deps.leakcanary 43 | testCompile deps.leakcanarynoop 44 | // Timber 45 | compile deps.timber 46 | // Pull to refresh 47 | compile deps.ultraptr 48 | // Tab Layout 49 | compile deps.flycotablayout 50 | 51 | // Unit tests dependencies 52 | testCompile deps.junit 53 | testCompile deps.robolectric 54 | testCompile deps.mockito 55 | testCompile deps.hamcrestcore 56 | testCompile deps.hamcrestlibrary 57 | testCompile deps.hamcrestintegration 58 | 59 | // Integration 60 | androidTestCompile deps.junit 61 | androidTestCompile deps.testrunner 62 | androidTestCompile deps.testrules 63 | androidTestCompile deps.espresso 64 | androidTestCompile deps.mockito 65 | androidTestCompile deps.dexmaker 66 | androidTestCompile deps.dexmakerdx 67 | androidTestCompile deps.dexmakermockito 68 | androidTestCompile deps.spoon 69 | // Resolve conflicts between main and test APK: 70 | androidTestCompile deps.supportannotations 71 | androidTestCompile deps.appcompat 72 | androidTestCompile deps.recyclerview 73 | androidTestCompile deps.design 74 | } 75 | 76 | android { 77 | compileSdkVersion rootProject.ext.android.compileSdkVersion 78 | buildToolsVersion rootProject.ext.android.buildToolsVersion 79 | 80 | defaultConfig { 81 | applicationId "com.hwangjr.tiger" 82 | minSdkVersion rootProject.ext.android.minSdkVersion 83 | targetSdkVersion rootProject.ext.android.targetSdkVersion 84 | versionCode 1 85 | versionName "1.0" 86 | 87 | multiDexEnabled true 88 | } 89 | compileOptions { 90 | sourceCompatibility JavaVersion.VERSION_1_8 91 | targetCompatibility JavaVersion.VERSION_1_8 92 | } 93 | buildTypes { 94 | debug { 95 | versionNameSuffix "-debug" 96 | minifyEnabled false 97 | zipAlignEnabled false 98 | shrinkResources false 99 | } 100 | release { 101 | minifyEnabled true 102 | zipAlignEnabled true 103 | shrinkResources true 104 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 105 | proguardFiles fileTree(dir: "${projectDir.parent}/proguards", include: ['*.pro']).asList().toArray() 106 | } 107 | } 108 | lintOptions { 109 | textReport true 110 | textOutput 'stdout' 111 | fatal 'UnusedResources' 112 | } 113 | sourceSets { 114 | main { 115 | // disable automatic ndk-build call, which ignore our Android.mk 116 | jni.srcDirs = [] 117 | jniLibs.srcDirs = ['libs', 'src/main/libs'] 118 | } 119 | } 120 | } -------------------------------------------------------------------------------- /tiger/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 /usr/local/android/android-sdk-linux/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 | -keepclassmembers class * implements java.io.Serializable { 19 | static final long serialVersionUID; 20 | private static final java.io.ObjectStreamField[] serialPersistentFields; 21 | !static !transient ; 22 | !private ; 23 | !private ; 24 | private void writeObject(java.io.ObjectOutputStream); 25 | private void readObject(java.io.ObjectInputStream); 26 | java.lang.Object writeReplace(); 27 | java.lang.Object readResolve(); 28 | } 29 | 30 | -keepclassmembernames class co.sihe.tigerlottery.entity.order.SportOrderCalculator { 31 | public ; 32 | } 33 | 34 | -keepattributes JavascriptInterface 35 | -keepclassmembers class * { 36 | @android.webkit.JavascriptInterface ; 37 | } -------------------------------------------------------------------------------- /tiger/src/androidTest/java/com/hwangjr/mvp/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /tiger/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/MVPApplication.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.os.StrictMode; 6 | import android.util.Log; 7 | 8 | import com.hwangjr.mvp.injection.components.AppComponent; 9 | import com.hwangjr.mvp.injection.components.DaggerAppComponent; 10 | import com.hwangjr.mvp.injection.modules.AppModule; 11 | import com.hwangjr.mvp.utils.LoggerUtils; 12 | import com.hwangjr.tiger.BuildConfig; 13 | import com.squareup.leakcanary.LeakCanary; 14 | import com.squareup.leakcanary.RefWatcher; 15 | 16 | import timber.log.Timber; 17 | 18 | public class MVPApplication extends Application { 19 | private static MVPApplication sInstance; 20 | private AppComponent appComponent; 21 | 22 | private RefWatcher refWatcher; 23 | 24 | @Override 25 | public void onCreate() { 26 | super.onCreate(); 27 | setInstance(this); 28 | setupComponent(); 29 | 30 | refWatcher = LeakCanary.install(this); 31 | if (BuildConfig.DEBUG) { 32 | Timber.plant(new Timber.DebugTree()); 33 | 34 | StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() 35 | .detectAll() 36 | .penaltyLog() 37 | .build()); 38 | StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() 39 | .detectAll() 40 | .penaltyLog() 41 | .penaltyDeathOnNetwork() 42 | .build()); 43 | } else { 44 | Timber.plant(new CrashReportTree()); 45 | } 46 | } 47 | 48 | private static void setInstance(MVPApplication application) { 49 | MVPApplication.sInstance = application; 50 | } 51 | 52 | private void setupComponent() { 53 | appComponent = DaggerAppComponent.builder() 54 | .appModule(new AppModule(this)) 55 | .build(); 56 | } 57 | 58 | @Override 59 | public void onLowMemory() { 60 | android.os.Process.killProcess(android.os.Process.myPid()); 61 | super.onLowMemory(); 62 | } 63 | 64 | public static MVPApplication getInstance() { 65 | return sInstance; 66 | } 67 | 68 | public static AppComponent getAppComponent() { 69 | return sInstance.appComponent; 70 | } 71 | 72 | public static RefWatcher getRefWatcher(Context context) { 73 | MVPApplication application = (MVPApplication) context.getApplicationContext(); 74 | return application.refWatcher; 75 | } 76 | 77 | /** 78 | * A tree which logs important information for crash reporting. 79 | */ 80 | private static class CrashReportTree extends Timber.DebugTree { 81 | @Override 82 | protected void log(int priority, String tag, String message, Throwable t) { 83 | if (priority == Log.ERROR && t != null) { 84 | String crashLog = Log.getStackTraceString(t); 85 | StringBuilder sb = new StringBuilder(message + "\n"); 86 | sb.append(crashLog); 87 | LoggerUtils.writeCrashLog(sb.toString()); 88 | super.log(priority, tag, sb.toString(), t); 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/base/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.base; 2 | 3 | import android.content.Context; 4 | import android.os.Build; 5 | import android.os.Bundle; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.View; 8 | import android.view.ViewStub; 9 | import android.view.WindowManager; 10 | import android.view.inputmethod.InputMethodManager; 11 | 12 | import com.hwangjr.mvp.MVPApplication; 13 | import com.hwangjr.mvp.injection.components.ActivityComponent; 14 | import com.hwangjr.mvp.injection.components.DaggerActivityComponent; 15 | import com.hwangjr.mvp.injection.modules.ActivityModule; 16 | import com.hwangjr.mvp.utils.ActivityAssistant; 17 | import com.hwangjr.mvp.utils.StatusBarUtils; 18 | import com.hwangjr.mvp.widget.SnackbarWrapper; 19 | import com.hwangjr.tiger.R; 20 | import com.squareup.leakcanary.RefWatcher; 21 | 22 | import java.lang.reflect.Field; 23 | 24 | import butterknife.ButterKnife; 25 | import butterknife.Unbinder; 26 | 27 | public abstract class BaseActivity extends AppCompatActivity { 28 | 29 | private ActivityComponent activityComponent; 30 | private Unbinder unbinder; 31 | 32 | private ViewStub tipsViewStub; 33 | private View tipsView; 34 | 35 | @Override 36 | protected void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | setContentView(getLayoutId()); 39 | unbinder = ButterKnife.bind(this); 40 | translucentStatus(); 41 | if (activityComponent == null) { 42 | activityComponent = DaggerActivityComponent.builder() 43 | .appComponent(MVPApplication.getAppComponent()) 44 | .activityModule(new ActivityModule(this)).build(); 45 | } 46 | } 47 | 48 | protected ActivityComponent getActivityComponent() { 49 | return activityComponent; 50 | } 51 | 52 | protected abstract int getLayoutId(); 53 | 54 | @Override 55 | protected void onDestroy() { 56 | unbinder.unbind(); 57 | super.onDestroy(); 58 | fixInputMethodManagerLeak(this); 59 | RefWatcher refWatcher = MVPApplication.getRefWatcher(this); 60 | refWatcher.watch(this); 61 | } 62 | 63 | private void translucentStatus() { 64 | StatusBarUtils.setColor(this, getResources().getColor(R.color.colorPrimaryDark)); 65 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT 66 | && getWindow().getAttributes().softInputMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) { 67 | ActivityAssistant.assistActivity(this); 68 | } 69 | } 70 | 71 | private void fixInputMethodManagerLeak(Context destContext) { 72 | if (destContext != null) { 73 | InputMethodManager imm = (InputMethodManager) destContext.getSystemService( 74 | INPUT_METHOD_SERVICE); 75 | if (imm != null) { 76 | String[] arr = new String[]{"mCurRootView", "mServedView", "mNextServedView"}; 77 | Field f = null; 78 | Object objGet = null; 79 | for (int i = 0; i < arr.length; i++) { 80 | String param = arr[i]; 81 | try { 82 | f = imm.getClass() 83 | .getDeclaredField(param); 84 | if (!f.isAccessible()) { 85 | f.setAccessible(true); 86 | } 87 | objGet = f.get(imm); 88 | if (objGet != null && objGet instanceof View) { 89 | View vGet = (View) objGet; 90 | if (vGet.getContext() == destContext) { 91 | f.set(imm, null); 92 | } else { 93 | break; 94 | } 95 | } 96 | } catch (Throwable t) { 97 | t.printStackTrace(); 98 | } 99 | } 100 | } 101 | } 102 | } 103 | 104 | public void initTipsView() { 105 | if (getTipsStubViewId() > 0) { 106 | tipsViewStub = (ViewStub) findViewById(getTipsStubViewId()); 107 | if (getTipsRootViewLayoutId() > 0) { 108 | tipsViewStub.setLayoutResource(getTipsRootViewLayoutId()); 109 | tipsView = tipsViewStub.inflate(); 110 | } 111 | } 112 | } 113 | 114 | public View getTipsView() { 115 | if (tipsViewStub == null) { 116 | initTipsView(); 117 | } 118 | return tipsView; 119 | } 120 | 121 | public int getTipsStubViewId() { 122 | return R.id.mvp_tips; 123 | } 124 | 125 | public int getTipsRootViewLayoutId() { 126 | return R.layout.mvp_layout_coordinator; 127 | } 128 | 129 | public void showTips(String msg) { 130 | if (tipsViewStub == null) { 131 | initTipsView(); 132 | } 133 | if (tipsView != null) { 134 | SnackbarWrapper.wrapper(tipsView, msg).show(); 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/base/BaseFragment.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.base; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v4.app.Fragment; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.view.ViewStub; 10 | 11 | import com.hwangjr.mvp.MVPApplication; 12 | import com.hwangjr.mvp.injection.components.ActivityComponent; 13 | import com.hwangjr.mvp.injection.components.DaggerFragmentComponent; 14 | import com.hwangjr.mvp.injection.components.FragmentComponent; 15 | import com.hwangjr.mvp.injection.modules.FragmentModule; 16 | import com.hwangjr.mvp.widget.SnackbarWrapper; 17 | import com.hwangjr.tiger.R; 18 | import com.squareup.leakcanary.RefWatcher; 19 | 20 | import butterknife.ButterKnife; 21 | import butterknife.Unbinder; 22 | 23 | public abstract class BaseFragment extends Fragment { 24 | 25 | protected View mainView; 26 | private Unbinder unbinder; 27 | private boolean isVisibleToUser; 28 | private boolean isViewCreated = false; 29 | private boolean isPageRunning; 30 | 31 | private ActivityComponent activityComponent; 32 | private FragmentComponent fragmentComponent; 33 | 34 | private ViewStub tipsViewStub; 35 | private View tipsView; 36 | 37 | @Override 38 | public void onCreate(@Nullable Bundle savedInstanceState) { 39 | super.onCreate(savedInstanceState); 40 | } 41 | 42 | // Should call after activity created 43 | @Deprecated 44 | protected ActivityComponent getActivityComponent() { 45 | try { 46 | if (activityComponent == null) { 47 | activityComponent = ((BaseActivity) getActivity()).getActivityComponent(); 48 | } 49 | } catch (Exception e) { 50 | e.printStackTrace(); 51 | } 52 | return activityComponent; 53 | } 54 | 55 | protected FragmentComponent getFragmentComponent() { 56 | try { 57 | if (fragmentComponent == null) { 58 | fragmentComponent = DaggerFragmentComponent.builder() 59 | .appComponent(MVPApplication.getAppComponent()) 60 | .fragmentModule(new FragmentModule(this)).build(); 61 | } 62 | } catch (Exception e) { 63 | e.printStackTrace(); 64 | } 65 | return fragmentComponent; 66 | } 67 | 68 | protected abstract int getLayoutId(); 69 | 70 | protected abstract void initView(); 71 | 72 | protected void initData(@Nullable Bundle arguments) { 73 | } 74 | 75 | @Nullable 76 | @Override 77 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 78 | if (mainView == null) { 79 | mainView = inflater.inflate(getLayoutId(), container, false); 80 | unbinder = ButterKnife.bind(this, mainView); 81 | initView(); 82 | isViewCreated = true; 83 | initData(getArguments()); 84 | } 85 | return mainView; 86 | } 87 | 88 | @Override 89 | public void onResume() { 90 | super.onResume(); 91 | isPageRunning = true; 92 | if (getUserVisibleHint()) { 93 | onUserVisible(true); 94 | } 95 | } 96 | 97 | @Override 98 | public void onPause() { 99 | onUserVisible(false); 100 | isPageRunning = false; 101 | super.onPause(); 102 | } 103 | 104 | @Override 105 | public void onDestroy() { 106 | if (unbinder != null) { 107 | unbinder.unbind(); 108 | } 109 | Fragment fragment = getTargetFragment(); 110 | if (fragment != null && fragment instanceof BaseFragment) { 111 | BaseFragment targetFragment = (BaseFragment) fragment; 112 | targetFragment.onFragmentResult(getTargetRequestCode()); 113 | } 114 | if (mainView != null && mainView.getParent() != null) { 115 | ((ViewGroup) mainView.getParent()).removeView(mainView); 116 | } 117 | super.onDestroy(); 118 | RefWatcher refWatcher = MVPApplication.getRefWatcher(getActivity()); 119 | refWatcher.watch(this); 120 | } 121 | 122 | protected void onFragmentResult(int requestCode) { 123 | } 124 | 125 | protected boolean isVisibleToUser() { 126 | return isVisibleToUser; 127 | } 128 | 129 | protected boolean isViewCreated() { 130 | return isViewCreated; 131 | } 132 | 133 | protected boolean isPageRunning() { 134 | return isPageRunning; 135 | } 136 | 137 | @Override 138 | public void setUserVisibleHint(boolean isVisibleToUser) { 139 | super.setUserVisibleHint(isVisibleToUser); 140 | this.isVisibleToUser = isVisibleToUser; 141 | onUserVisible(isVisibleToUser); 142 | } 143 | 144 | protected void onUserVisible(boolean isVisibleToUser) { 145 | } 146 | 147 | protected View getTipsViewFromActivity() { 148 | return ((BaseActivity) getActivity()).getTipsView(); 149 | } 150 | 151 | protected void initTipsView() { 152 | tipsView = getTipsViewFromActivity(); 153 | if (tipsView == null) { 154 | if (getTipsStubViewId() > 0) { 155 | tipsViewStub = (ViewStub) mainView.findViewById(getTipsStubViewId()); 156 | if (getTipsRootViewLayoutId() > 0) { 157 | tipsViewStub.setLayoutResource(getTipsRootViewLayoutId()); 158 | tipsView = tipsViewStub.inflate(); 159 | } 160 | } 161 | } 162 | } 163 | 164 | public int getTipsStubViewId() { 165 | return R.id.mvp_tips; 166 | } 167 | 168 | public int getTipsRootViewLayoutId() { 169 | return R.layout.mvp_layout_coordinator; 170 | } 171 | 172 | public void showTips(String msg) { 173 | if (tipsViewStub == null) { 174 | initTipsView(); 175 | } 176 | if (tipsView != null) { 177 | SnackbarWrapper.wrapper(tipsView, msg).show(); 178 | } 179 | } 180 | } -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/base/presenter/ActivityPresenter.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.base.presenter; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | 6 | import com.hwangjr.mvp.base.view.MVPView; 7 | 8 | import rx.Subscription; 9 | import rx.subscriptions.CompositeSubscription; 10 | 11 | public abstract class ActivityPresenter implements Presenter { 12 | 13 | protected V view; 14 | protected CompositeSubscription subscriptions = new CompositeSubscription(); 15 | 16 | public void addSubscription(Subscription subscription) { 17 | subscriptions.add(subscription); 18 | } 19 | 20 | public void unsubscribe() { 21 | subscriptions.unsubscribe(); 22 | } 23 | 24 | public boolean isViewAttached() { 25 | return this.view != null; 26 | } 27 | 28 | @Override 29 | public void attach(V view) { 30 | this.view = view; 31 | } 32 | 33 | @Override 34 | public void onCreate(@Nullable Bundle savedInstanceState) { 35 | } 36 | 37 | @Override 38 | public void onResume() { 39 | } 40 | 41 | @Override 42 | public void onPause() { 43 | } 44 | 45 | @Override 46 | public void onDestroy() { 47 | unsubscribe(); 48 | this.view = null; 49 | } 50 | 51 | @Override 52 | public void onSaveInstanceState(Bundle outState) { 53 | } 54 | 55 | @Override 56 | public void onRestoreInstanceState(Bundle savedInstanceState) { 57 | } 58 | } -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/base/presenter/FragmentPresenter.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.base.presenter; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | 6 | import com.hwangjr.mvp.base.view.MVPView; 7 | 8 | import rx.Subscription; 9 | import rx.subscriptions.CompositeSubscription; 10 | 11 | public abstract class FragmentPresenter implements Presenter { 12 | 13 | protected V view; 14 | protected CompositeSubscription subscriptions = new CompositeSubscription(); 15 | 16 | public void addSubscription(Subscription subscription) { 17 | subscriptions.add(subscription); 18 | } 19 | 20 | public void unsubscribe() { 21 | subscriptions.unsubscribe(); 22 | } 23 | 24 | public boolean isViewAttached() { 25 | return this.view != null; 26 | } 27 | 28 | @Override 29 | public void attach(V view) { 30 | this.view = view; 31 | } 32 | 33 | @Override 34 | public void onCreate(@Nullable Bundle savedInstanceState) { 35 | } 36 | 37 | public void initData(@Nullable Bundle arguments) { 38 | } 39 | 40 | public void onActivityCreated(@Nullable Bundle savedInstanceState) { 41 | } 42 | 43 | @Override 44 | public void onResume() { 45 | } 46 | 47 | @Override 48 | public void onPause() { 49 | } 50 | 51 | public void onDestroyView() { 52 | } 53 | 54 | @Override 55 | public void onDestroy() { 56 | unsubscribe(); 57 | this.view = null; 58 | } 59 | 60 | @Override 61 | public void onSaveInstanceState(Bundle outState) { 62 | } 63 | 64 | @Override 65 | public void onRestoreInstanceState(Bundle savedInstanceState) { 66 | } 67 | } -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/base/presenter/Presenter.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.base.presenter; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | 6 | import com.hwangjr.mvp.base.view.MVPView; 7 | 8 | public interface Presenter { 9 | void attach(V view); 10 | 11 | void onCreate(@Nullable Bundle savedInstanceState); 12 | 13 | void onResume(); 14 | 15 | void onPause(); 16 | 17 | void onDestroy(); 18 | 19 | void onSaveInstanceState(Bundle outState); 20 | 21 | void onRestoreInstanceState(Bundle savedInstanceState); 22 | } -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/base/recycler/PullToRefreshRecyclerFragmentPresenter.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.base.recycler; 2 | 3 | import com.hwangjr.mvp.base.presenter.FragmentPresenter; 4 | 5 | public abstract class PullToRefreshRecyclerFragmentPresenter 6 | extends FragmentPresenter { 7 | 8 | protected boolean hasMoreData = true; 9 | 10 | public boolean hasMoreData() { 11 | return hasMoreData; 12 | } 13 | 14 | public abstract void loadData(); 15 | 16 | public void reloadData() { 17 | loadData(); 18 | } 19 | 20 | public abstract void loadMore(int totalItemCount); 21 | } -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/base/recycler/PullToRefreshRecyclerFragmentView.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.base.recycler; 2 | 3 | import android.graphics.Rect; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.v7.widget.DefaultItemAnimator; 7 | import android.support.v7.widget.GridLayoutManager; 8 | import android.support.v7.widget.LinearLayoutManager; 9 | import android.support.v7.widget.RecyclerView; 10 | import android.support.v7.widget.StaggeredGridLayoutManager; 11 | import android.view.View; 12 | import android.view.ViewStub; 13 | 14 | import com.hwangjr.mvp.base.view.DataView; 15 | import com.hwangjr.mvp.base.view.pulltorefresh.PullToRefreshFragmentView; 16 | import com.hwangjr.mvp.widget.DividerItemDecoration; 17 | import com.hwangjr.tiger.R; 18 | 19 | import in.srain.cube.views.ptr.PtrFrameLayout; 20 | 21 | public abstract class PullToRefreshRecyclerFragmentView

22 | extends PullToRefreshFragmentView

implements DataView { 23 | private static final int ITEM_LEFT_TO_LOAD_MORE = 10; 24 | 25 | public static final int LAYOUT_MANAGER_TYPE_LINEAR = 0; 26 | public static final int LAYOUT_MANAGER_TYPE_GRID = 1; 27 | public static final int LAYOUT_MANAGER_TYPE_STAGGERED_GRID = 2; 28 | 29 | private RecyclerView recyclerView; 30 | private int[] lastScrollPositions; 31 | private int layoutManagerType = LAYOUT_MANAGER_TYPE_LINEAR; 32 | 33 | private ViewStub moreProgress; 34 | private View moreProgressView; 35 | private boolean isLoadingMore; 36 | private int moreProgressId; 37 | 38 | protected RecyclerView.OnScrollListener internalOnScrollListener; 39 | protected RecyclerView.OnScrollListener externalOnScrollListener; 40 | 41 | protected int getRecyclerViewID() { 42 | return R.id.recycler_view; 43 | } 44 | 45 | protected int getMoreProgressViewID() { 46 | return R.layout.mvp_more_progress; 47 | } 48 | 49 | public RecyclerView getRecyclerView() { 50 | return recyclerView; 51 | } 52 | 53 | @Override 54 | protected int getPullMode() { 55 | return PULL_MODE_REFRESH_LOADMORE; 56 | } 57 | 58 | @Override 59 | protected void initView() { 60 | super.initView(); 61 | if (recyclerView == null) { 62 | if (getPullMode() == PULL_MODE_REFRESH_LOADMORE) { 63 | this.moreProgressId = getMoreProgressViewID(); 64 | moreProgress = (ViewStub) mainView.findViewById(R.id.more_progress); 65 | moreProgress.setLayoutResource(moreProgressId); 66 | if (moreProgressId != 0) { 67 | moreProgressView = moreProgress.inflate(); 68 | } 69 | hideMoreProgress(); 70 | } 71 | 72 | this.initRecyclerView(mainView); 73 | if (recyclerView != null) { 74 | recyclerView.setLayoutManager(getLayoutManager()); 75 | } 76 | this.customizeRecyclerView(); 77 | this.setAdapter(getAdapter()); 78 | } 79 | } 80 | 81 | @Override 82 | protected void initData(@Nullable Bundle arguments) { 83 | super.initData(arguments); 84 | loadData(); 85 | } 86 | 87 | @Override 88 | public void loadData() { 89 | presenter.loadData(); 90 | } 91 | 92 | @Override 93 | public void reloadData() { 94 | presenter.reloadData(); 95 | } 96 | 97 | private void initRecyclerView(View view) { 98 | View recyclerView = view.findViewById(getRecyclerViewID()); 99 | if (recyclerView instanceof RecyclerView) { 100 | this.recyclerView = (RecyclerView) recyclerView; 101 | } else { 102 | throw new IllegalArgumentException(getClass().getSimpleName() + " can't work without a RecyclerView!"); 103 | } 104 | 105 | if (this.recyclerView != null) { 106 | internalOnScrollListener = new RecyclerView.OnScrollListener() { 107 | @Override 108 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) { 109 | super.onScrolled(recyclerView, dx, dy); 110 | 111 | if (getPullMode() == PULL_MODE_REFRESH_LOADMORE) { 112 | processOnMore(); 113 | } 114 | if (externalOnScrollListener != null) { 115 | externalOnScrollListener.onScrolled(recyclerView, dx, dy); 116 | } 117 | } 118 | 119 | @Override 120 | public void onScrollStateChanged(RecyclerView recyclerView, int newState) { 121 | super.onScrollStateChanged(recyclerView, newState); 122 | if (externalOnScrollListener != null) { 123 | externalOnScrollListener.onScrollStateChanged(recyclerView, newState); 124 | } 125 | } 126 | }; 127 | this.recyclerView.addOnScrollListener(internalOnScrollListener); 128 | this.recyclerView.setItemAnimator(new DefaultItemAnimator()); 129 | } 130 | } 131 | 132 | protected RecyclerView.ItemDecoration getItemDecoration() { 133 | int space = (int) getContext().getResources().getDimension(R.dimen.padding_small); 134 | return new DefaultSpaceItemDecoration(space); 135 | } 136 | 137 | protected RecyclerView.ItemDecoration getItemDividerDecoration(int direction) { 138 | return new DividerItemDecoration(getActivity(), direction); 139 | } 140 | 141 | private boolean haveMore() { 142 | return presenter.hasMoreData(); 143 | } 144 | 145 | private void processOnMore() { 146 | RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); 147 | int lastVisibleItemPosition = getLastVisibleItemPosition(layoutManager); 148 | int visibleItemCount = layoutManager.getChildCount(); 149 | int totalItemCount = layoutManager.getItemCount(); 150 | 151 | if (((totalItemCount - lastVisibleItemPosition) <= ITEM_LEFT_TO_LOAD_MORE 152 | || (totalItemCount - lastVisibleItemPosition) == 0 && totalItemCount > visibleItemCount) 153 | && !isLoadingMore) { 154 | if (haveMore()) { 155 | showMoreProgress(); 156 | setLoadingMore(true); 157 | loadMore(totalItemCount); 158 | } else { 159 | hideMoreProgress(); 160 | setLoadingMore(false); 161 | } 162 | } 163 | } 164 | 165 | @Override 166 | public void onRefreshBegin(PtrFrameLayout frame) { 167 | reloadData(); 168 | } 169 | 170 | private int getLastVisibleItemPosition(RecyclerView.LayoutManager layoutManager) { 171 | int lastVisibleItemPosition = -1; 172 | if (layoutManager instanceof LinearLayoutManager) { 173 | layoutManagerType = LAYOUT_MANAGER_TYPE_LINEAR; 174 | } else if (layoutManager instanceof GridLayoutManager) { 175 | layoutManagerType = LAYOUT_MANAGER_TYPE_GRID; 176 | } else if (layoutManager instanceof StaggeredGridLayoutManager) { 177 | layoutManagerType = LAYOUT_MANAGER_TYPE_STAGGERED_GRID; 178 | } else { 179 | throw new RuntimeException( 180 | "Unsupported LayoutManager used. Valid ones are LinearLayoutManager, GridLayoutManager and StaggeredGridLayoutManager"); 181 | } 182 | 183 | switch (layoutManagerType) { 184 | case LAYOUT_MANAGER_TYPE_GRID: 185 | lastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition(); 186 | break; 187 | case LAYOUT_MANAGER_TYPE_STAGGERED_GRID: 188 | StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager; 189 | if (lastScrollPositions == null) { 190 | lastScrollPositions = new int[staggeredGridLayoutManager.getSpanCount()]; 191 | } 192 | 193 | staggeredGridLayoutManager.findLastVisibleItemPositions(lastScrollPositions); 194 | lastVisibleItemPosition = findMax(lastScrollPositions); 195 | break; 196 | case LAYOUT_MANAGER_TYPE_LINEAR: 197 | default: 198 | lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition(); 199 | break; 200 | } 201 | return lastVisibleItemPosition; 202 | } 203 | 204 | private int findMax(int[] lastPositions) { 205 | int max = Integer.MIN_VALUE; 206 | for (int value : lastPositions) { 207 | if (value > max) { 208 | max = value; 209 | } 210 | } 211 | return max; 212 | } 213 | 214 | private void setAdapterInternal(RecyclerView.Adapter adapter, boolean compatibleWithPrevious, 215 | boolean removeAndRecycleExistingViews) { 216 | if (recyclerView != null) { 217 | if (compatibleWithPrevious) { 218 | recyclerView.swapAdapter(adapter, removeAndRecycleExistingViews); 219 | } else { 220 | recyclerView.setAdapter(adapter); 221 | } 222 | 223 | if (adapter != null && getPullMode() == PULL_MODE_REFRESH_LOADMORE) { 224 | adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { 225 | @Override 226 | public void onItemRangeChanged(int positionStart, int itemCount) { 227 | super.onItemRangeChanged(positionStart, itemCount); 228 | onUpdated(); 229 | } 230 | 231 | @Override 232 | public void onItemRangeInserted(int positionStart, int itemCount) { 233 | super.onItemRangeInserted(positionStart, itemCount); 234 | onUpdated(); 235 | } 236 | 237 | @Override 238 | public void onItemRangeRemoved(int positionStart, int itemCount) { 239 | super.onItemRangeRemoved(positionStart, itemCount); 240 | onUpdated(); 241 | } 242 | 243 | @Override 244 | public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { 245 | super.onItemRangeMoved(fromPosition, toPosition, itemCount); 246 | onUpdated(); 247 | } 248 | 249 | @Override 250 | public void onChanged() { 251 | super.onChanged(); 252 | onUpdated(); 253 | } 254 | 255 | private void onUpdated() { 256 | hideMoreProgress(); 257 | isLoadingMore = false; 258 | } 259 | }); 260 | } 261 | } 262 | } 263 | 264 | protected void customizeRecyclerView() { 265 | } 266 | 267 | protected abstract RecyclerView.LayoutManager getLayoutManager(); 268 | 269 | protected abstract RecyclerAdapter getAdapter(); 270 | 271 | protected void loadMore(int totalItemCount) { 272 | presenter.loadMore(totalItemCount); 273 | } 274 | 275 | public void setAdapter(RecyclerView.Adapter adapter) { 276 | setAdapterInternal(adapter, false, true); 277 | } 278 | 279 | public void swapAdapter(RecyclerView.Adapter adapter, boolean removeAndRecycleExistingViews) { 280 | setAdapterInternal(adapter, true, removeAndRecycleExistingViews); 281 | } 282 | 283 | public void setOnScrollListener(RecyclerView.OnScrollListener listener) { 284 | externalOnScrollListener = listener; 285 | } 286 | 287 | public boolean isLoadingMore() { 288 | return isLoadingMore; 289 | } 290 | 291 | public void setLoadingMore(boolean isLoadingMore) { 292 | this.isLoadingMore = isLoadingMore; 293 | } 294 | 295 | public View getMoreProgressView() { 296 | return moreProgressView; 297 | } 298 | 299 | public void showMoreProgress() { 300 | if (moreProgress != null) { 301 | moreProgress.setVisibility(View.VISIBLE); 302 | } 303 | } 304 | 305 | public void hideMoreProgress() { 306 | if (moreProgress != null) { 307 | moreProgress.setVisibility(View.GONE); 308 | } 309 | } 310 | 311 | public void showLoadMoreFinished() { 312 | hideMoreProgress(); 313 | isLoadingMore = false; 314 | } 315 | 316 | private static class DefaultSpaceItemDecoration extends RecyclerView.ItemDecoration { 317 | private int space; 318 | 319 | public DefaultSpaceItemDecoration(int space) { 320 | this.space = space; 321 | } 322 | 323 | @Override 324 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 325 | outRect.left = space; 326 | outRect.right = space; 327 | outRect.bottom = space; 328 | if (parent.getChildAdapterPosition(view) == 0) { 329 | outRect.top = space; 330 | } 331 | } 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/base/recycler/RecyclerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.base.recycler; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | public abstract class RecyclerAdapter extends RecyclerView.Adapter { 13 | 14 | public static final int VIEW_TYPE_HEADER = -1; 15 | private Context context; 16 | private List data; 17 | private RecyclerViewHolder headerView; 18 | protected boolean isScrolling; 19 | private OnItemClickListener listener; 20 | 21 | public RecyclerAdapter(RecyclerView recyclerView) { 22 | this(recyclerView, null); 23 | } 24 | 25 | public RecyclerAdapter(RecyclerView recyclerView, List data) { 26 | this.context = recyclerView.getContext(); 27 | this.data = (data == null ? new ArrayList<>() : data); 28 | 29 | recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { 30 | @Override 31 | public void onScrollStateChanged(RecyclerView recyclerView, int newState) { 32 | super.onScrollStateChanged(recyclerView, newState); 33 | isScrolling = (newState == RecyclerView.SCROLL_STATE_DRAGGING || newState == RecyclerView.SCROLL_STATE_SETTLING); 34 | } 35 | }); 36 | } 37 | 38 | public List getData() { 39 | return data; 40 | } 41 | 42 | private boolean isHeader(int position) { 43 | return (hasHeader() && position == 0); 44 | } 45 | 46 | protected boolean hasHeader() { 47 | return getHeaderViewHolder() != null; 48 | } 49 | 50 | @Override 51 | public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 52 | RecyclerViewHolder viewHolder; 53 | if (viewType != VIEW_TYPE_HEADER) { 54 | View itemView = LayoutInflater.from(context) 55 | .inflate(getItemLayoutID(viewType), parent, false); 56 | viewHolder = createItemViewHolder(itemView, viewType); 57 | } else { 58 | viewHolder = getHeaderViewHolder(); 59 | } 60 | return viewHolder; 61 | } 62 | 63 | @Override 64 | public int getItemCount() { 65 | if (hasHeader()) { 66 | return data.size() + 1; 67 | } 68 | return data.size(); 69 | } 70 | 71 | @Override 72 | public int getItemViewType(int position) { 73 | if (hasHeader()) { 74 | return isHeader(position) ? VIEW_TYPE_HEADER : getViewType(position - 1); 75 | } else { 76 | return getViewType(position); 77 | } 78 | } 79 | 80 | @Override 81 | public void onBindViewHolder(RecyclerViewHolder holder, int position) { 82 | if (hasHeader()) { 83 | if (!isHeader(position)) { 84 | int index = position - 1; 85 | onBindItemViewHolder((V) holder, position, index, isScrolling); 86 | ((V) holder).itemView.setOnClickListener(getOnClickListener(index)); 87 | } 88 | } else { 89 | onBindItemViewHolder((V) holder, position, position, isScrolling); 90 | ((V) holder).itemView.setOnClickListener(getOnClickListener(position)); 91 | } 92 | } 93 | 94 | public void setHeaderView(RecyclerViewHolder headerView) { 95 | this.headerView = headerView; 96 | } 97 | 98 | protected H getHeaderViewHolder() { 99 | return (H) headerView; 100 | } 101 | 102 | protected abstract int getItemLayoutID(int viewType); 103 | 104 | protected abstract V createItemViewHolder(View itemView, int viewType); 105 | 106 | protected abstract int getViewType(int position); 107 | 108 | protected abstract void onBindItemViewHolder(V holder, int position, int index, 109 | boolean isScrolling); 110 | 111 | public void add(D elem) { 112 | data.add(elem); 113 | notifyItemInserted(data.size() - 1); 114 | } 115 | 116 | public void addAll(List elem) { 117 | final int positionStart = data.size(); 118 | data.addAll(elem); 119 | notifyItemRangeInserted(positionStart, elem.size()); 120 | } 121 | 122 | public void set(D oldElem, D newElem) { 123 | set(data.indexOf(oldElem), newElem); 124 | } 125 | 126 | public void set(int index, D elem) { 127 | if (index >= data.size()) { 128 | return; 129 | } 130 | 131 | data.set(index, elem); 132 | notifyItemChanged(index); 133 | } 134 | 135 | public void remove(D elem) { 136 | remove(data.indexOf(elem)); 137 | } 138 | 139 | public void remove(int index) { 140 | if (index < data.size()) { 141 | data.remove(index); 142 | notifyItemRemoved(index); 143 | } 144 | } 145 | 146 | public void replaceAll(List elem) { 147 | data.clear(); 148 | data.addAll(elem); 149 | notifyDataSetChanged(); 150 | } 151 | 152 | public void setOnItemClickListener(OnItemClickListener l) { 153 | listener = l; 154 | } 155 | 156 | public View.OnClickListener getOnClickListener(final int position) { 157 | return view -> { 158 | if (listener != null && view != null && getData().size() > position) { 159 | listener.onItemClick(view, getData().get(position), position); 160 | } 161 | }; 162 | } 163 | 164 | public interface OnItemClickListener { 165 | void onItemClick(View view, D data, int position); 166 | } 167 | } -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/base/recycler/RecyclerSectionAdapter.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.base.recycler; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.view.ViewGroup; 5 | 6 | import java.util.Collection; 7 | import java.util.List; 8 | 9 | public abstract class RecyclerSectionAdapter extends RecyclerAdapter { 10 | protected static final int VIEW_TYPE_SECTION = -2; 11 | 12 | public RecyclerSectionAdapter(RecyclerView recyclerView) { 13 | super(recyclerView); 14 | } 15 | 16 | public RecyclerSectionAdapter(RecyclerView recyclerView, List data) { 17 | super(recyclerView, data); 18 | } 19 | 20 | protected abstract Collection getSections(); 21 | 22 | protected abstract RecyclerViewHolder onCreateSectionViewHolder(ViewGroup parent); 23 | 24 | protected abstract void onBindSectionViewHolder(RecyclerViewHolder sectionView, int sectionIndex); 25 | 26 | protected boolean checkSectionForPosition(int position) { 27 | int headerIndex = hasHeader() ? 1 : 0; 28 | Collection sections = getSections(); 29 | if (sections != null && sections.size() > 0) { 30 | int index = 0; 31 | for (Integer value : sections) { 32 | value = value + index + headerIndex; 33 | if (value == position) { 34 | return true; 35 | } 36 | index++; 37 | } 38 | } 39 | return false; 40 | } 41 | 42 | private int getLastSectionIndex(int position) { 43 | int headerIndex = hasHeader() ? 1 : 0; 44 | Collection sections = getSections(); 45 | if (sections != null && sections.size() > 0) { 46 | int index = 0; 47 | for (Integer value : sections) { 48 | value = value + index + headerIndex; 49 | if (value >= position) { 50 | return index; 51 | } 52 | index++; 53 | } 54 | return index--; 55 | } 56 | return 0; 57 | } 58 | 59 | @Override 60 | public final int getItemCount() { 61 | int sectionCount = getSections() != null ? getSections().size() : 0; 62 | return super.getItemCount() + sectionCount; 63 | } 64 | 65 | @Override 66 | public int getItemViewType(int position) { 67 | if (checkSectionForPosition(position)) { 68 | return VIEW_TYPE_SECTION; 69 | } else { 70 | position = position - getLastSectionIndex(position); 71 | return super.getItemViewType(position); 72 | } 73 | } 74 | 75 | @Override 76 | public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 77 | if (viewType == VIEW_TYPE_SECTION) { 78 | return onCreateSectionViewHolder(parent); 79 | } else { 80 | return super.onCreateViewHolder(parent, viewType); 81 | } 82 | } 83 | 84 | @Override 85 | public void onBindViewHolder(RecyclerViewHolder holder, int position) { 86 | if (checkSectionForPosition(position)) { 87 | onBindSectionViewHolder(holder, getLastSectionIndex(position)); 88 | } else { 89 | position = position - getLastSectionIndex(position); 90 | super.onBindViewHolder(holder, position); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/base/recycler/RecyclerViewHolder.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.base.recycler; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.view.View; 5 | 6 | import butterknife.ButterKnife; 7 | 8 | public abstract class RecyclerViewHolder extends RecyclerView.ViewHolder { 9 | 10 | public RecyclerViewHolder(View itemView) { 11 | super(itemView); 12 | ButterKnife.bind(this, itemView); 13 | } 14 | 15 | protected abstract void bindView(D data); 16 | } -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/base/recycler/RecyclerWithFooterAdapter.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.base.recycler; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.view.ViewGroup; 5 | 6 | import java.util.List; 7 | 8 | public abstract class RecyclerWithFooterAdapter extends RecyclerAdapter { 9 | 10 | public static final int VIEW_TYPE_FOOTER = -2; 11 | 12 | private RecyclerViewHolder footerView; 13 | 14 | public RecyclerWithFooterAdapter(RecyclerView recyclerView) { 15 | this(recyclerView, null); 16 | } 17 | 18 | public RecyclerWithFooterAdapter(RecyclerView recyclerView, List data) { 19 | super(recyclerView, data); 20 | } 21 | 22 | private boolean isFooter(int position) { 23 | return (hasFooter() && position == (getItemCount() - 1)); 24 | } 25 | 26 | protected boolean hasFooter() { 27 | return getFooterViewHolder() != null; 28 | } 29 | 30 | @Override 31 | public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 32 | if (viewType == VIEW_TYPE_FOOTER) { 33 | return getFooterViewHolder(); 34 | } else { 35 | return super.onCreateViewHolder(parent, viewType); 36 | } 37 | } 38 | 39 | @Override 40 | public int getItemCount() { 41 | if (hasFooter()) { 42 | return super.getItemCount() + 1; 43 | } 44 | return super.getItemCount(); 45 | } 46 | 47 | @Override 48 | public int getItemViewType(int position) { 49 | return hasFooter() && isFooter(position) ? VIEW_TYPE_FOOTER : super.getItemViewType(position); 50 | } 51 | 52 | @Override 53 | public void onBindViewHolder(RecyclerViewHolder holder, int position) { 54 | if (hasFooter()) { 55 | if (!isFooter(position)) { 56 | super.onBindViewHolder(holder, position); 57 | } 58 | } else { 59 | super.onBindViewHolder(holder, position); 60 | } 61 | } 62 | 63 | public void setFooterView(RecyclerViewHolder footerView) { 64 | this.footerView = footerView; 65 | } 66 | 67 | protected F getFooterViewHolder() { 68 | return (F) footerView; 69 | } 70 | } -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/base/view/DataView.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.base.view; 2 | 3 | public interface DataView { 4 | void loadData(); 5 | 6 | void reloadData(); 7 | } -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/base/view/MVPView.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.base.view; 2 | 3 | import com.hwangjr.mvp.base.presenter.Presenter; 4 | 5 | public interface MVPView

{ 6 | void setPresenter(P presenter); 7 | 8 | boolean isActive(); 9 | } -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/base/view/activity/ActivityDataView.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.base.view.activity; 2 | 3 | import com.hwangjr.mvp.base.presenter.ActivityPresenter; 4 | import com.hwangjr.mvp.base.view.DataView; 5 | 6 | public abstract class ActivityDataView

extends ActivityView

implements DataView { 7 | 8 | @Override 9 | public void onResume() { 10 | super.onResume(); 11 | loadData(); 12 | } 13 | 14 | @Override 15 | public void reloadData() { 16 | loadData(); 17 | } 18 | } -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/base/view/activity/ActivityView.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.base.view.activity; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.hwangjr.mvp.base.BaseActivity; 6 | import com.hwangjr.mvp.base.presenter.ActivityPresenter; 7 | import com.hwangjr.mvp.base.view.MVPView; 8 | 9 | import javax.inject.Inject; 10 | 11 | public abstract class ActivityView

extends BaseActivity implements MVPView

{ 12 | 13 | @Inject 14 | protected P presenter; 15 | 16 | public void setPresenter(P presenter) { 17 | this.presenter = presenter; 18 | } 19 | 20 | public boolean isActive() { 21 | return !isFinishing(); 22 | } 23 | 24 | protected abstract void injectComponent(); 25 | 26 | @Override 27 | protected void onCreate(Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | injectComponent(); 30 | presenter.attach(this); 31 | presenter.onCreate(savedInstanceState); 32 | } 33 | 34 | @Override 35 | public void onResume() { 36 | super.onResume(); 37 | presenter.onResume(); 38 | } 39 | 40 | @Override 41 | public void onPause() { 42 | presenter.onPause(); 43 | super.onPause(); 44 | } 45 | 46 | @Override 47 | public void onDestroy() { 48 | presenter.onDestroy(); 49 | super.onDestroy(); 50 | } 51 | 52 | @Override 53 | protected void onSaveInstanceState(Bundle outState) { 54 | super.onSaveInstanceState(outState); 55 | presenter.onSaveInstanceState(outState); 56 | } 57 | 58 | @Override 59 | protected void onRestoreInstanceState(Bundle savedInstanceState) { 60 | super.onRestoreInstanceState(savedInstanceState); 61 | presenter.onSaveInstanceState(savedInstanceState); 62 | } 63 | } -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/base/view/fragment/FragmentView.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.base.view.fragment; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | 7 | import com.hwangjr.mvp.base.BaseFragment; 8 | import com.hwangjr.mvp.base.presenter.FragmentPresenter; 9 | import com.hwangjr.mvp.base.view.MVPView; 10 | 11 | import javax.inject.Inject; 12 | 13 | public abstract class FragmentView

extends BaseFragment implements MVPView

{ 14 | 15 | @Inject 16 | protected P presenter; 17 | 18 | public void setPresenter(P presenter) { 19 | this.presenter = presenter; 20 | } 21 | 22 | public boolean isActive() { 23 | return isAdded(); 24 | } 25 | 26 | protected abstract void injectComponent(); 27 | 28 | @Override 29 | public void onAttach(Context context) { 30 | super.onAttach(context); 31 | injectComponent(); 32 | presenter.attach(this); 33 | } 34 | 35 | @Override 36 | public void onCreate(@Nullable Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | presenter.onSaveInstanceState(savedInstanceState); 39 | presenter.onCreate(savedInstanceState); 40 | } 41 | 42 | @Override 43 | protected void initData(@Nullable Bundle arguments) { 44 | super.initData(arguments); 45 | presenter.initData(arguments); 46 | } 47 | 48 | @Override 49 | public void onActivityCreated(@Nullable Bundle savedInstanceState) { 50 | super.onActivityCreated(savedInstanceState); 51 | presenter.onActivityCreated(savedInstanceState); 52 | } 53 | 54 | @Override 55 | public void onResume() { 56 | super.onResume(); 57 | presenter.onResume(); 58 | } 59 | 60 | @Override 61 | public void onPause() { 62 | presenter.onPause(); 63 | super.onPause(); 64 | } 65 | 66 | @Override 67 | public void onDestroyView() { 68 | presenter.onDestroyView(); 69 | super.onDestroyView(); 70 | } 71 | 72 | @Override 73 | public void onDestroy() { 74 | presenter.onDestroy(); 75 | super.onDestroy(); 76 | } 77 | 78 | @Override 79 | public void onSaveInstanceState(Bundle outState) { 80 | super.onSaveInstanceState(outState); 81 | presenter.onSaveInstanceState(outState); 82 | } 83 | } -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/base/view/pulltorefresh/PullToRefreshActivityView.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.base.view.pulltorefresh; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.view.ViewCompat; 5 | import android.view.View; 6 | import android.widget.AbsListView; 7 | 8 | import com.hwangjr.mvp.base.presenter.ActivityPresenter; 9 | import com.hwangjr.mvp.base.view.activity.ActivityView; 10 | import com.hwangjr.mvp.utils.DensityUtils; 11 | import com.hwangjr.tiger.R; 12 | 13 | import in.srain.cube.views.ptr.PtrFrameLayout; 14 | import in.srain.cube.views.ptr.PtrHandler; 15 | import in.srain.cube.views.ptr.header.MaterialHeader; 16 | 17 | public abstract class PullToRefreshActivityView

extends ActivityView

implements PtrHandler { 18 | 19 | public static final long REFRESH_TIME_INTERVAL = 2000 * 1000L; 20 | 21 | public static final int PULL_MODE_REFRESH = 0; 22 | public static final int PULL_MODE_REFRESH_LOADMORE = 1; 23 | public static final int PULL_MODE_DISABLED = 2; 24 | 25 | protected PtrFrameLayout ptrFrameLayout; 26 | private long lastLoadDataTime = System.currentTimeMillis(); 27 | 28 | public long getLastLoadDataTime() { 29 | return lastLoadDataTime; 30 | } 31 | 32 | public void setLastLoadDataTime(long lastLoadDataTime) { 33 | this.lastLoadDataTime = lastLoadDataTime; 34 | } 35 | 36 | protected int getPtrFrameLayoutId() { 37 | return R.id.ptr_frame; 38 | } 39 | 40 | protected PtrFrameLayout getPtrFrameLayout() { 41 | return ptrFrameLayout; 42 | } 43 | 44 | @Override 45 | protected void onCreate(Bundle savedInstanceState) { 46 | super.onCreate(savedInstanceState); 47 | initView(); 48 | } 49 | 50 | protected void initView() { 51 | if (getPullMode() != PULL_MODE_DISABLED && ptrFrameLayout == null && getPtrFrameLayoutId() != 0) { 52 | ptrFrameLayout = (PtrFrameLayout) findViewById(getPtrFrameLayoutId()); 53 | 54 | final MaterialHeader header = new MaterialHeader(this); 55 | int[] colors = getResources().getIntArray(R.array.material_colors); 56 | header.setColorSchemeColors(colors); 57 | header.setLayoutParams(new PtrFrameLayout.LayoutParams(-1, -2)); 58 | header.setPadding(0, DensityUtils.dip2px(this, 15), 0, 59 | DensityUtils.dip2px(this, 10)); 60 | header.setPtrFrameLayout(ptrFrameLayout); 61 | 62 | ptrFrameLayout.setLoadingMinTime(1000); 63 | ptrFrameLayout.setDurationToCloseHeader(1500); 64 | ptrFrameLayout.setHeaderView(header); 65 | ptrFrameLayout.addPtrUIHandler(header); 66 | ptrFrameLayout.setPullToRefresh(false); 67 | ptrFrameLayout.autoRefresh(false); 68 | ptrFrameLayout.setPtrHandler(this); 69 | } 70 | } 71 | 72 | @Override 73 | public void onResume() { 74 | super.onResume(); 75 | if (System.currentTimeMillis() - getLastLoadDataTime() > REFRESH_TIME_INTERVAL) { 76 | if (getPullMode() != PULL_MODE_DISABLED && ptrFrameLayout != null) { 77 | setLastLoadDataTime(System.currentTimeMillis()); 78 | ptrFrameLayout.autoRefresh(); 79 | } 80 | } 81 | } 82 | 83 | @Override 84 | public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { 85 | return !canChildScrollUp(content); 86 | } 87 | 88 | protected int getPullMode() { 89 | return PULL_MODE_REFRESH; 90 | } 91 | 92 | public void showRefreshComplete() { 93 | getPtrFrameLayout().refreshComplete(); 94 | } 95 | 96 | public static boolean canChildScrollUp(View content) { 97 | if (android.os.Build.VERSION.SDK_INT < 14) { 98 | if (content instanceof AbsListView) { 99 | final AbsListView absListView = (AbsListView) content; 100 | return absListView.getChildCount() > 0 101 | && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0).getTop() < absListView.getPaddingTop()); 102 | } else { 103 | return ViewCompat.canScrollVertically(content, -1) || content.getScrollY() > 0; 104 | } 105 | } else { 106 | return ViewCompat.canScrollVertically(content, -1); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/base/view/pulltorefresh/PullToRefreshFragmentView.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.base.view.pulltorefresh; 2 | 3 | import android.support.v4.view.ViewCompat; 4 | import android.view.View; 5 | import android.widget.AbsListView; 6 | 7 | import com.hwangjr.mvp.base.presenter.FragmentPresenter; 8 | import com.hwangjr.mvp.base.view.fragment.FragmentView; 9 | import com.hwangjr.mvp.utils.DensityUtils; 10 | import com.hwangjr.tiger.R; 11 | 12 | import in.srain.cube.views.ptr.PtrFrameLayout; 13 | import in.srain.cube.views.ptr.PtrHandler; 14 | import in.srain.cube.views.ptr.header.MaterialHeader; 15 | 16 | public abstract class PullToRefreshFragmentView

extends FragmentView

implements PtrHandler { 17 | 18 | public static final long REFRESH_TIME_INTERVAL = 2000 * 1000L; 19 | 20 | public static final int PULL_MODE_REFRESH = 0; 21 | public static final int PULL_MODE_REFRESH_LOADMORE = 1; 22 | public static final int PULL_MODE_DISABLED = 2; 23 | 24 | protected PtrFrameLayout ptrFrameLayout; 25 | private long lastLoadDataTime = System.currentTimeMillis(); 26 | 27 | public long getLastLoadDataTime() { 28 | return lastLoadDataTime; 29 | } 30 | 31 | public void setLastLoadDataTime(long lastLoadDataTime) { 32 | this.lastLoadDataTime = lastLoadDataTime; 33 | } 34 | 35 | protected int getPtrFrameLayoutId() { 36 | return R.id.ptr_frame; 37 | } 38 | 39 | protected PtrFrameLayout getPtrFrameLayout() { 40 | return ptrFrameLayout; 41 | } 42 | 43 | @Override 44 | protected void initView() { 45 | if (getPullMode() != PULL_MODE_DISABLED && ptrFrameLayout == null && getPtrFrameLayoutId() != 0) { 46 | ptrFrameLayout = (PtrFrameLayout) mainView.findViewById(getPtrFrameLayoutId()); 47 | 48 | final MaterialHeader header = new MaterialHeader(getContext()); 49 | int[] colors = getResources().getIntArray(R.array.material_colors); 50 | header.setColorSchemeColors(colors); 51 | header.setLayoutParams(new PtrFrameLayout.LayoutParams(-1, -2)); 52 | header.setPadding(0, DensityUtils.dip2px(getContext(), 15), 0, 53 | DensityUtils.dip2px(getContext(), 10)); 54 | header.setPtrFrameLayout(ptrFrameLayout); 55 | 56 | ptrFrameLayout.setLoadingMinTime(1000); 57 | ptrFrameLayout.setDurationToCloseHeader(1500); 58 | ptrFrameLayout.setHeaderView(header); 59 | ptrFrameLayout.addPtrUIHandler(header); 60 | ptrFrameLayout.setPullToRefresh(false); 61 | ptrFrameLayout.autoRefresh(false); 62 | ptrFrameLayout.setPtrHandler(this); 63 | } 64 | } 65 | 66 | @Override 67 | protected void onUserVisible(boolean isVisibleToUser) { 68 | super.onUserVisible(isVisibleToUser); 69 | if (isVisibleToUser) { 70 | if (System.currentTimeMillis() - getLastLoadDataTime() > REFRESH_TIME_INTERVAL) { 71 | if (getPullMode() != PULL_MODE_DISABLED && ptrFrameLayout != null) { 72 | setLastLoadDataTime(System.currentTimeMillis()); 73 | ptrFrameLayout.autoRefresh(); 74 | } 75 | } 76 | } 77 | } 78 | 79 | @Override 80 | public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { 81 | return !canChildScrollUp(content); 82 | } 83 | 84 | @Override 85 | public void onDestroyView() { 86 | if (getPtrFrameLayout() != null) { 87 | getPtrFrameLayout().refreshComplete(); 88 | } 89 | super.onDestroyView(); 90 | } 91 | 92 | protected int getPullMode() { 93 | return PULL_MODE_REFRESH; 94 | } 95 | 96 | public static boolean canChildScrollUp(View content) { 97 | if (android.os.Build.VERSION.SDK_INT < 14) { 98 | if (content instanceof AbsListView) { 99 | final AbsListView absListView = (AbsListView) content; 100 | return absListView.getChildCount() > 0 101 | && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0).getTop() < absListView.getPaddingTop()); 102 | } else { 103 | return ViewCompat.canScrollVertically(content, -1) || content.getScrollY() > 0; 104 | } 105 | } else { 106 | return ViewCompat.canScrollVertically(content, -1); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/injection/components/ActivityComponent.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.injection.components; 2 | 3 | import android.app.Activity; 4 | 5 | import com.hwangjr.mvp.injection.modules.ActivityModule; 6 | import com.hwangjr.mvp.injection.scopes.PerActivity; 7 | import com.hwangjr.tiger.ui.MainActivity; 8 | 9 | import dagger.Component; 10 | 11 | @PerActivity 12 | @Component(dependencies = AppComponent.class, modules = {ActivityModule.class}) 13 | public interface ActivityComponent { 14 | Activity getActivity(); 15 | 16 | void inject(MainActivity activity); 17 | } -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/injection/components/AppComponent.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.injection.components; 2 | 3 | import android.app.Application; 4 | 5 | import com.hwangjr.mvp.injection.modules.AppModule; 6 | 7 | import javax.inject.Singleton; 8 | 9 | import dagger.Component; 10 | 11 | @Singleton 12 | @Component(modules = {AppModule.class}) 13 | public interface AppComponent { 14 | 15 | Application getApplication(); 16 | } 17 | -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/injection/components/FragmentComponent.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.injection.components; 2 | 3 | import android.support.v4.app.Fragment; 4 | 5 | import com.hwangjr.mvp.injection.modules.FragmentModule; 6 | import com.hwangjr.mvp.injection.scopes.PerFragment; 7 | 8 | import dagger.Component; 9 | 10 | @PerFragment 11 | @Component(dependencies = AppComponent.class, modules = {FragmentModule.class}) 12 | public interface FragmentComponent { 13 | Fragment getFragment(); 14 | } -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/injection/modules/ActivityModule.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.injection.modules; 2 | 3 | import android.app.Activity; 4 | 5 | import com.hwangjr.mvp.injection.scopes.PerActivity; 6 | 7 | import dagger.Module; 8 | import dagger.Provides; 9 | 10 | @Module 11 | public class ActivityModule { 12 | 13 | private final Activity activity; 14 | 15 | public ActivityModule(Activity activity) { 16 | this.activity = activity; 17 | } 18 | 19 | @PerActivity 20 | @Provides 21 | public Activity provideActivity() { 22 | return activity; 23 | } 24 | } -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/injection/modules/AppModule.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.injection.modules; 2 | 3 | import android.app.Application; 4 | 5 | import javax.inject.Singleton; 6 | 7 | import dagger.Module; 8 | import dagger.Provides; 9 | 10 | @Module 11 | public class AppModule { 12 | private Application application; 13 | 14 | public AppModule(Application application) { 15 | this.application = application; 16 | } 17 | 18 | @Provides 19 | @Singleton 20 | public Application provideApplication() { 21 | return application; 22 | } 23 | } -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/injection/modules/FragmentModule.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.injection.modules; 2 | 3 | import android.support.v4.app.Fragment; 4 | 5 | import com.hwangjr.mvp.injection.scopes.PerFragment; 6 | 7 | import dagger.Module; 8 | import dagger.Provides; 9 | 10 | @Module 11 | public class FragmentModule { 12 | 13 | private final Fragment fragment; 14 | 15 | public FragmentModule(Fragment fragment) { 16 | this.fragment = fragment; 17 | } 18 | 19 | @PerFragment 20 | @Provides 21 | public Fragment provideFragment() { 22 | return fragment; 23 | } 24 | } -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/injection/scopes/PerActivity.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.injection.scopes; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | import javax.inject.Scope; 7 | 8 | @Scope 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface PerActivity { 11 | } -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/injection/scopes/PerFragment.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.injection.scopes; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | import javax.inject.Scope; 7 | 8 | @Scope 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface PerFragment { 11 | } -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/utils/ActivityAssistant.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.utils; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.graphics.Rect; 6 | import android.view.View; 7 | import android.widget.FrameLayout; 8 | 9 | public final class ActivityAssistant { 10 | // For more information, see 11 | // https://code.google.com/p/android/issues/detail?id=5497 12 | // To use this class, simply invoke assistActivity() on an Activity that already has its content view set. 13 | 14 | private View mChildOfContent; 15 | private int mUsableHeightPrevious; 16 | private FrameLayout.LayoutParams frameLayoutParams; 17 | private int mWindowHeight; 18 | 19 | public static void assistActivity(Activity activity) { 20 | new ActivityAssistant(activity); 21 | } 22 | 23 | private ActivityAssistant(final Activity activity) { 24 | final FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content); 25 | mWindowHeight = activity.getWindowManager().getDefaultDisplay().getHeight(); 26 | mChildOfContent = content.getChildAt(0); 27 | mChildOfContent.getViewTreeObserver() 28 | .addOnGlobalLayoutListener(() -> possiblyResizeChildOfContent(activity)); 29 | frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams(); 30 | } 31 | 32 | private void possiblyResizeChildOfContent(Activity activity) { 33 | int usableHeightNow = computeUsableHeight(); 34 | if (usableHeightNow != mUsableHeightPrevious) { 35 | int usableHeightSansKeyboard = mWindowHeight; 36 | int heightDifference = usableHeightSansKeyboard - usableHeightNow; 37 | if (heightDifference > (usableHeightSansKeyboard / 4)) { 38 | frameLayoutParams.height = usableHeightSansKeyboard - heightDifference 39 | + getStatusBarHeight(activity); 40 | } else { 41 | frameLayoutParams.height = usableHeightSansKeyboard; 42 | } 43 | mChildOfContent.requestLayout(); 44 | mUsableHeightPrevious = usableHeightNow; 45 | } 46 | } 47 | 48 | private int computeUsableHeight() { 49 | Rect r = new Rect(); 50 | mChildOfContent.getWindowVisibleDisplayFrame(r); 51 | return (r.bottom - r.top); 52 | } 53 | 54 | public int getStatusBarHeight(Context context) { 55 | int result = 0; 56 | int resourceId = context.getResources() 57 | .getIdentifier("status_bar_height", "dimen", "android"); 58 | if (resourceId > 0) { 59 | result = context.getResources() 60 | .getDimensionPixelSize(resourceId); 61 | } 62 | return result; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/utils/DateUtils.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.utils; 2 | 3 | import android.text.format.DateFormat; 4 | 5 | import java.util.Date; 6 | 7 | public final class DateUtils { 8 | 9 | public static final String SDF_YYYYMMDD = "yyyy-MM-dd"; 10 | public static final String SDF_YMDHHMMSS = "yyyy-MM-dd HH:mm:ss"; 11 | 12 | public static String format(String dateFormat, Date date) { 13 | return String.valueOf(DateFormat.format(dateFormat, date)); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/utils/DensityUtils.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.utils; 2 | 3 | import android.content.Context; 4 | import android.util.DisplayMetrics; 5 | 6 | public final class DensityUtils { 7 | 8 | public static int dip2px(Context context, float dpValue) { 9 | final float scale = getDensity(context); 10 | return (int) (dpValue * scale + 0.5f); 11 | } 12 | 13 | public static int px2dip(Context context, float pxValue) { 14 | final float scale = getDensity(context); 15 | return (int) (pxValue / scale + 0.5f); 16 | } 17 | 18 | public static float getDensity(Context context) { 19 | return context.getResources() 20 | .getDisplayMetrics().density; 21 | } 22 | 23 | public static int sp2px(Context context, float spValue) { 24 | final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; 25 | return (int) (spValue * fontScale + 0.5f); 26 | } 27 | 28 | public static String getDisplayPixels(Context context) { 29 | DisplayMetrics dm = context.getResources().getDisplayMetrics(); 30 | int screenWidth = dm.widthPixels; 31 | int screenHeight = dm.heightPixels; 32 | 33 | String displayPixels = screenWidth + "x" + screenHeight; 34 | 35 | return displayPixels; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/utils/LoggerUtils.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.utils; 2 | 3 | import android.os.Environment; 4 | 5 | import com.hwangjr.mvp.MVPApplication; 6 | 7 | import java.io.File; 8 | import java.io.FileOutputStream; 9 | import java.io.OutputStreamWriter; 10 | import java.io.PrintWriter; 11 | import java.io.Writer; 12 | import java.util.Date; 13 | 14 | public final class LoggerUtils { 15 | private static final String CRASH_FILE_NAME = "Crash.log"; 16 | private static final String DEBUG_FILE_NAME = "Debug.log"; 17 | private static final String LEAK_FILE_NAME = "Leak.log"; 18 | 19 | private static void writeLogToFile(String fileName, String logContent) { 20 | if (SDCardUtils.hasSDCard()) { 21 | File root = new File(new File(Environment.getExternalStorageDirectory(), MVPApplication.getInstance().getPackageName()), "log"); 22 | if (!root.exists()) { 23 | root.mkdirs(); 24 | } 25 | 26 | Date nowtime = new Date(); 27 | String nowtimeFormat = DateUtils.format(DateUtils.SDF_YYYYMMDD, nowtime); 28 | String logFileName = nowtimeFormat + fileName; 29 | 30 | PrintWriter printWriter = null; 31 | try { 32 | File file = new File(root, logFileName); 33 | Writer writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8"); 34 | printWriter = new PrintWriter(writer); 35 | printWriter.println("================================================="); 36 | printWriter.println(DateUtils.format(DateUtils.SDF_YMDHHMMSS, nowtime)); 37 | printWriter.println(logContent); 38 | printWriter.close(); 39 | } catch (Exception e) { 40 | e.printStackTrace(); 41 | } finally { 42 | if (printWriter != null) { 43 | try { 44 | printWriter.close(); 45 | } catch (Exception e) { 46 | e.printStackTrace(); 47 | } 48 | } 49 | } 50 | } 51 | } 52 | 53 | public static void writeCrashLog(String crashLog) { 54 | writeLogToFile(CRASH_FILE_NAME, crashLog); 55 | } 56 | 57 | public static void writeDebugLog(String debugLog) { 58 | writeLogToFile(DEBUG_FILE_NAME, debugLog); 59 | } 60 | 61 | public static void writeLeakLog(String leakLog) { 62 | writeLogToFile(LEAK_FILE_NAME, leakLog); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/utils/SDCardUtils.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.utils; 2 | 3 | import android.os.Environment; 4 | 5 | public final class SDCardUtils { 6 | public static boolean hasSDCard() { 7 | return Environment.getExternalStorageState().equals( 8 | Environment.MEDIA_MOUNTED); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/utils/StatusBarUtils.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.utils; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.Activity; 5 | import android.content.Context; 6 | import android.graphics.Color; 7 | import android.os.Build; 8 | import android.support.v4.widget.DrawerLayout; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.view.WindowManager; 12 | import android.widget.LinearLayout; 13 | 14 | public final class StatusBarUtils { 15 | public static final int DEFAULT_STATUS_BAR_ALPHA = 112; 16 | 17 | public static void setColor(Activity activity, int color) { 18 | setColor(activity, color, DEFAULT_STATUS_BAR_ALPHA); 19 | } 20 | 21 | public static void setColor(Activity activity, int color, int statusBarAlpha) { 22 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 23 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 24 | activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 25 | activity.getWindow().setStatusBarColor(calculateStatusColor(color, statusBarAlpha)); 26 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 27 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 28 | View statusView = createStatusBarView(activity, color, statusBarAlpha); 29 | ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView(); 30 | decorView.addView(statusView); 31 | setRootView(activity); 32 | } 33 | } 34 | 35 | public static void setColorNoTranslucent(Activity activity, int color) { 36 | setColor(activity, color, 0); 37 | } 38 | 39 | public static void setColorDiff(Activity activity, int color) { 40 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { 41 | return; 42 | } 43 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 44 | View statusView = createStatusBarView(activity, color); 45 | ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView(); 46 | decorView.addView(statusView); 47 | setRootView(activity); 48 | } 49 | 50 | public static void setTranslucent(Activity activity) { 51 | setTranslucent(activity, DEFAULT_STATUS_BAR_ALPHA); 52 | } 53 | 54 | public static void setTranslucent(Activity activity, int statusBarAlpha) { 55 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { 56 | return; 57 | } 58 | setTransparent(activity); 59 | addTranslucentView(activity, statusBarAlpha); 60 | } 61 | 62 | public static void setTransparent(Activity activity) { 63 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { 64 | return; 65 | } 66 | transparentStatusBar(activity); 67 | setRootView(activity); 68 | } 69 | 70 | public static void setTranslucentDiff(Activity activity) { 71 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 72 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 73 | setRootView(activity); 74 | } 75 | } 76 | 77 | public static void setColorForDrawerLayout(Activity activity, DrawerLayout drawerLayout, int color) { 78 | setColorForDrawerLayout(activity, drawerLayout, color, DEFAULT_STATUS_BAR_ALPHA); 79 | } 80 | 81 | public static void setColorNoTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout, int color) { 82 | setColorForDrawerLayout(activity, drawerLayout, color, 0); 83 | } 84 | 85 | public static void setColorForDrawerLayout(Activity activity, DrawerLayout drawerLayout, int color, int statusBarAlpha) { 86 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { 87 | return; 88 | } 89 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 90 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 91 | activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 92 | activity.getWindow().setStatusBarColor(Color.TRANSPARENT); 93 | } else { 94 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 95 | } 96 | View statusBarView = createStatusBarView(activity, color); 97 | ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0); 98 | contentLayout.addView(statusBarView, 0); 99 | if (!(contentLayout instanceof LinearLayout) && contentLayout.getChildAt(1) != null) { 100 | contentLayout.getChildAt(1).setPadding(0, getStatusBarHeight(activity), 0, 0); 101 | } 102 | ViewGroup drawer = (ViewGroup) drawerLayout.getChildAt(1); 103 | drawerLayout.setFitsSystemWindows(false); 104 | contentLayout.setFitsSystemWindows(false); 105 | contentLayout.setClipToPadding(true); 106 | drawer.setFitsSystemWindows(false); 107 | 108 | addTranslucentView(activity, statusBarAlpha); 109 | } 110 | 111 | public static void setColorForDrawerLayoutDiff(Activity activity, DrawerLayout drawerLayout, int color) { 112 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 113 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 114 | View statusBarView = createStatusBarView(activity, color); 115 | ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0); 116 | contentLayout.addView(statusBarView, 0); 117 | if (!(contentLayout instanceof LinearLayout) && contentLayout.getChildAt(1) != null) { 118 | contentLayout.getChildAt(1).setPadding(0, getStatusBarHeight(activity), 0, 0); 119 | } 120 | ViewGroup drawer = (ViewGroup) drawerLayout.getChildAt(1); 121 | drawerLayout.setFitsSystemWindows(false); 122 | contentLayout.setFitsSystemWindows(false); 123 | contentLayout.setClipToPadding(true); 124 | drawer.setFitsSystemWindows(false); 125 | } 126 | } 127 | 128 | public static void setTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout) { 129 | setTranslucentForDrawerLayout(activity, drawerLayout, DEFAULT_STATUS_BAR_ALPHA); 130 | } 131 | 132 | public static void setTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout, int statusBarAlpha) { 133 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { 134 | return; 135 | } 136 | setTransparentForDrawerLayout(activity, drawerLayout); 137 | addTranslucentView(activity, statusBarAlpha); 138 | } 139 | 140 | public static void setTransparentForDrawerLayout(Activity activity, DrawerLayout drawerLayout) { 141 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { 142 | return; 143 | } 144 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 145 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 146 | activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 147 | activity.getWindow().setStatusBarColor(Color.TRANSPARENT); 148 | } else { 149 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 150 | } 151 | 152 | ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0); 153 | if (!(contentLayout instanceof LinearLayout) && contentLayout.getChildAt(1) != null) { 154 | contentLayout.getChildAt(1).setPadding(0, getStatusBarHeight(activity), 0, 0); 155 | } 156 | 157 | ViewGroup drawer = (ViewGroup) drawerLayout.getChildAt(1); 158 | drawerLayout.setFitsSystemWindows(false); 159 | contentLayout.setFitsSystemWindows(false); 160 | contentLayout.setClipToPadding(true); 161 | drawer.setFitsSystemWindows(false); 162 | } 163 | 164 | public static void setTranslucentForDrawerLayoutDiff(Activity activity, DrawerLayout drawerLayout) { 165 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 166 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 167 | ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0); 168 | contentLayout.setFitsSystemWindows(true); 169 | contentLayout.setClipToPadding(true); 170 | ViewGroup vg = (ViewGroup) drawerLayout.getChildAt(1); 171 | vg.setFitsSystemWindows(false); 172 | drawerLayout.setFitsSystemWindows(false); 173 | } 174 | } 175 | 176 | private static void addTranslucentView(Activity activity, int statusBarAlpha) { 177 | ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content); 178 | if (contentView.getChildCount() > 1) { 179 | contentView.removeViewAt(1); 180 | } 181 | contentView.addView(createTranslucentStatusBarView(activity, statusBarAlpha)); 182 | } 183 | 184 | private static View createStatusBarView(Activity activity, int color) { 185 | View statusBarView = new View(activity); 186 | LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 187 | getStatusBarHeight(activity)); 188 | statusBarView.setLayoutParams(params); 189 | statusBarView.setBackgroundColor(color); 190 | return statusBarView; 191 | } 192 | 193 | private static View createStatusBarView(Activity activity, int color, int alpha) { 194 | View statusBarView = new View(activity); 195 | LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 196 | getStatusBarHeight(activity)); 197 | statusBarView.setLayoutParams(params); 198 | statusBarView.setBackgroundColor(calculateStatusColor(color, alpha)); 199 | return statusBarView; 200 | } 201 | 202 | private static void setRootView(Activity activity) { 203 | ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0); 204 | rootView.setFitsSystemWindows(true); 205 | rootView.setClipToPadding(true); 206 | } 207 | 208 | @TargetApi(Build.VERSION_CODES.KITKAT) 209 | private static void transparentStatusBar(Activity activity) { 210 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 211 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 212 | activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 213 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); 214 | activity.getWindow().setStatusBarColor(Color.TRANSPARENT); 215 | } else { 216 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 217 | } 218 | } 219 | 220 | private static View createTranslucentStatusBarView(Activity activity, int alpha) { 221 | View statusBarView = new View(activity); 222 | LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 223 | getStatusBarHeight(activity)); 224 | statusBarView.setLayoutParams(params); 225 | statusBarView.setBackgroundColor(Color.argb(alpha, 0, 0, 0)); 226 | return statusBarView; 227 | } 228 | 229 | private static int getStatusBarHeight(Context context) { 230 | int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android"); 231 | return context.getResources().getDimensionPixelSize(resourceId); 232 | } 233 | 234 | private static int calculateStatusColor(int color, int alpha) { 235 | float a = 1 - alpha / 255f; 236 | int red = color >> 16 & 0xff; 237 | int green = color >> 8 & 0xff; 238 | int blue = color & 0xff; 239 | red = (int) (red * a + 0.5); 240 | green = (int) (green * a + 0.5); 241 | blue = (int) (blue * a + 0.5); 242 | return 0xff << 24 | red << 16 | green << 8 | blue; 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/widget/DividerItemDecoration.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.widget; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Canvas; 6 | import android.graphics.Rect; 7 | import android.graphics.drawable.Drawable; 8 | import android.support.v7.widget.LinearLayoutManager; 9 | import android.support.v7.widget.RecyclerView; 10 | import android.view.View; 11 | 12 | public class DividerItemDecoration extends RecyclerView.ItemDecoration { 13 | private static final int[] ATTRS = new int[]{ 14 | android.R.attr.listDivider 15 | }; 16 | public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL; 17 | public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL; 18 | private Drawable mDivider; 19 | private int mOrientation; 20 | private int mItemSize = 20; 21 | 22 | public DividerItemDecoration(Context context, int orientation) { 23 | final TypedArray a = context.obtainStyledAttributes(ATTRS); 24 | mDivider = a.getDrawable(0); 25 | a.recycle(); 26 | setOrientation(orientation); 27 | } 28 | 29 | public DividerItemDecoration(Context context, int orientation, int size) { 30 | final TypedArray a = context.obtainStyledAttributes(ATTRS); 31 | mItemSize = size; 32 | mDivider = a.getDrawable(0); 33 | a.recycle(); 34 | setOrientation(orientation); 35 | } 36 | 37 | public void setOrientation(int orientation) { 38 | if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) { 39 | throw new IllegalArgumentException("invalid orientation"); 40 | } 41 | mOrientation = orientation; 42 | } 43 | 44 | @Override 45 | public void onDraw(Canvas c, RecyclerView parent) { 46 | 47 | if (mOrientation == VERTICAL_LIST) { 48 | drawVertical(c, parent); 49 | } else { 50 | drawHorizontal(c, parent); 51 | } 52 | 53 | } 54 | 55 | 56 | public void drawVertical(Canvas c, RecyclerView parent) { 57 | final int left = parent.getPaddingLeft(); 58 | final int right = parent.getWidth() - parent.getPaddingRight(); 59 | 60 | final int childCount = parent.getChildCount(); 61 | for (int i = 0; i < childCount; i++) { 62 | final View child = parent.getChildAt(i); 63 | android.support.v7.widget.RecyclerView v = new android.support.v7.widget.RecyclerView(parent.getContext()); 64 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child 65 | .getLayoutParams(); 66 | final int top = child.getBottom() + params.bottomMargin; 67 | final int bottom = top + mDivider.getIntrinsicHeight(); 68 | mDivider.setBounds(left, top, right, bottom); 69 | mDivider.draw(c); 70 | } 71 | } 72 | 73 | public void drawHorizontal(Canvas c, RecyclerView parent) { 74 | final int top = parent.getPaddingTop(); 75 | final int bottom = parent.getHeight() - parent.getPaddingBottom(); 76 | 77 | final int childCount = parent.getChildCount(); 78 | for (int i = 0; i < childCount; i++) { 79 | final View child = parent.getChildAt(i); 80 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child 81 | .getLayoutParams(); 82 | final int left = child.getRight() + params.rightMargin; 83 | final int right = left + mDivider.getIntrinsicHeight(); 84 | mDivider.setBounds(left, top, right, bottom); 85 | mDivider.draw(c); 86 | } 87 | } 88 | 89 | @Override 90 | public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { 91 | if (mOrientation == VERTICAL_LIST) { 92 | outRect.set(0, 0, 0, mItemSize); 93 | } else { 94 | outRect.set(0, 0, mItemSize, 0); 95 | } 96 | } 97 | } 98 | 99 | -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/mvp/widget/SnackbarWrapper.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp.widget; 2 | 3 | import android.support.design.widget.Snackbar; 4 | import android.view.View; 5 | import android.widget.TextView; 6 | 7 | public class SnackbarWrapper { 8 | public static Snackbar wrapper(View rootView, String msg) { 9 | Snackbar snackbar = Snackbar.make(rootView, msg, Snackbar.LENGTH_LONG); 10 | View snackView = snackbar.getView(); 11 | snackView.setBackgroundColor(0xFF5B5B5B); 12 | TextView textView = (TextView) snackView.findViewById(android.support.design.R.id.snackbar_text); 13 | textView.setTextColor(0xFFFF9224); 14 | return snackbar; 15 | } 16 | } -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/tiger/AppApplication.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.tiger; 2 | 3 | import com.hwangjr.mvp.MVPApplication; 4 | 5 | public class AppApplication extends MVPApplication { 6 | } 7 | -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/tiger/ui/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.tiger.ui; 2 | 3 | import com.hwangjr.mvp.base.view.activity.ActivityView; 4 | import com.hwangjr.tiger.R; 5 | 6 | public class MainActivity extends ActivityView { 7 | @Override 8 | protected void injectComponent() { 9 | getActivityComponent().inject(this); 10 | } 11 | 12 | @Override 13 | protected int getLayoutId() { 14 | return R.layout.activity_main; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tiger/src/main/java/com/hwangjr/tiger/ui/MainActivityPresenter.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.tiger.ui; 2 | 3 | import com.hwangjr.mvp.base.presenter.ActivityPresenter; 4 | import com.hwangjr.mvp.injection.scopes.PerActivity; 5 | 6 | import javax.inject.Inject; 7 | 8 | @PerActivity 9 | public class MainActivityPresenter extends ActivityPresenter { 10 | @Inject 11 | public MainActivityPresenter() { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tiger/src/main/res/anim/slide_in_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /tiger/src/main/res/anim/slide_in_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | -------------------------------------------------------------------------------- /tiger/src/main/res/anim/slide_out_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /tiger/src/main/res/anim/slide_out_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | -------------------------------------------------------------------------------- /tiger/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /tiger/src/main/res/layout/mvp_layout_coordinator.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tiger/src/main/res/layout/mvp_more_progress.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tiger/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidKnife/Tiger-Pro/cc4477b2842d895b8d31dad5060f7740fc967d40/tiger/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /tiger/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidKnife/Tiger-Pro/cc4477b2842d895b8d31dad5060f7740fc967d40/tiger/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /tiger/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidKnife/Tiger-Pro/cc4477b2842d895b8d31dad5060f7740fc967d40/tiger/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /tiger/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidKnife/Tiger-Pro/cc4477b2842d895b8d31dad5060f7740fc967d40/tiger/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /tiger/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidKnife/Tiger-Pro/cc4477b2842d895b8d31dad5060f7740fc967d40/tiger/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /tiger/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @color/red 5 | @color/blue 6 | @color/orange 7 | @color/green 8 | 9 | -------------------------------------------------------------------------------- /tiger/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFE52F2E 4 | #FFE52F2E 5 | #FFFFFFFF 6 | 7 | #ffffff 8 | #db3c38 9 | #4cb14e 10 | #ffa836 11 | #007bff 12 | #ff000000 13 | #ff808080 14 | 15 | #000000 16 | -------------------------------------------------------------------------------- /tiger/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 32dp 4 | 16dp 5 | 8dp 6 | 6dp 7 | 3dp 8 | 9 | 18sp 10 | 15sp 11 | 12sp 12 | 13 | -------------------------------------------------------------------------------- /tiger/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /tiger/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | MVP Demo 4 | -------------------------------------------------------------------------------- /tiger/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 15 | 16 | 19 | 20 | 26 | 27 | 30 | 31 | 35 | 36 | 40 | 41 | 44 | 45 | 49 | 50 | 54 | 55 | 64 | -------------------------------------------------------------------------------- /tiger/src/test/java/com/hwangjr/mvp/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.mvp; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test 12 | public void addition_isCorrect() throws Exception { 13 | assertEquals(4, 2 + 2); 14 | } 15 | } --------------------------------------------------------------------------------