├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── README.md ├── app ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── vincentbrison │ │ └── app │ │ └── quality │ │ ├── AbstractTestMainActivity.java │ │ ├── espresso │ │ └── TestMainActivityWithEspresso.java │ │ └── robotium │ │ └── TestMainActivityWithRobotium.java │ ├── approximationPi │ ├── java │ │ └── vb │ │ │ └── android │ │ │ └── app │ │ │ └── quality │ │ │ ├── PiCalculator.java │ │ │ └── dagger │ │ │ └── DataModule.java │ └── res │ │ └── values │ │ ├── colors.xml │ │ └── strings.xml │ ├── daggerMockedPi │ ├── java │ │ └── vb │ │ │ └── android │ │ │ └── app │ │ │ └── quality │ │ │ ├── PiCalculator.java │ │ │ └── dagger │ │ │ └── DataModule.java │ └── res │ │ └── values │ │ ├── colors.xml │ │ └── strings.xml │ ├── exactPi │ ├── java │ │ └── vb │ │ │ └── android │ │ │ └── app │ │ │ └── quality │ │ │ ├── PiCalculator.java │ │ │ └── dagger │ │ │ └── DataModule.java │ └── res │ │ └── values │ │ ├── colors.xml │ │ └── strings.xml │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── vb │ │ │ └── android │ │ │ └── app │ │ │ └── quality │ │ │ ├── InjectorHelper.java │ │ │ ├── app │ │ │ └── QualityApplication.java │ │ │ ├── dagger │ │ │ ├── AppComponent.java │ │ │ └── AppModule.java │ │ │ ├── pi │ │ │ ├── PiGenerator.java │ │ │ └── PiTask.java │ │ │ ├── rest │ │ │ ├── ApiInterface.java │ │ │ └── ResponseRank.java │ │ │ └── ui │ │ │ └── MainActivity.java │ └── res │ │ ├── drawable-hdpi │ │ └── ic_launcher.png │ │ ├── drawable-mdpi │ │ └── ic_launcher.png │ │ ├── drawable-xhdpi │ │ └── ic_launcher.png │ │ ├── drawable-xxhdpi │ │ └── ic_launcher.png │ │ ├── layout │ │ └── activity_main.xml │ │ └── values │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ ├── mockWebServerPi │ ├── AndroidManifest.xml │ ├── assets │ │ └── stubs │ │ │ └── rank_ok.json │ ├── java │ │ └── vb │ │ │ └── android │ │ │ └── app │ │ │ └── quality │ │ │ ├── AssetsHelper.java │ │ │ ├── PiCalculator.java │ │ │ └── dagger │ │ │ └── DataModule.java │ └── res │ │ └── values │ │ ├── colors.xml │ │ └── strings.xml │ └── test │ └── java │ └── vb │ └── android │ └── app │ └── quality │ └── PiCalculatorTest.java ├── build.gradle ├── config ├── quality.gradle └── quality │ ├── checkstyle │ ├── checkstyle.xml │ └── suppressions.xml │ ├── findbugs │ └── findbugs-filter.xml │ ├── lint │ └── lint.xml │ └── pmd │ └── pmd-ruleset.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Gradle files 2 | .gradle/ 3 | build/ 4 | 5 | # Local configuration file (sdk path, etc) 6 | local.properties 7 | 8 | # IntelliJ project files 9 | *.iml 10 | .idea/ 11 | 12 | # Android Studio captures folder 13 | captures/ 14 | 15 | # Misc 16 | .DS_Store 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | 3 | jdk: 4 | - oraclejdk8 5 | 6 | before_cache: 7 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 8 | 9 | cache: 10 | directories: 11 | - $HOME/.gradle/caches/ 12 | - $HOME/.gradle/wrapper/ 13 | 14 | android: 15 | components: 16 | - tools 17 | - build-tools-25.0.2 18 | - android-25 19 | - platform-tools 20 | - extra-google-m2repository 21 | - extra-android-m2repository 22 | - extra-android-support 23 | 24 | # Specify at least one system image, 25 | # if you need to run emulator(s) during your tests 26 | # - sys-img-armeabi-v7a-android-25 27 | 28 | #before_install: 29 | # - echo no | android create avd --force -n test -t android-25 --abi armeabi-v7a 30 | # - emulator -avd test -no-skin -no-audio -no-window & 31 | 32 | #before_script: 33 | # - android-wait-for-emulator 34 | # - adb shell input keyevent 82 & 35 | 36 | script: 37 | - ./gradlew check 38 | 39 | # Disable for now, since Travis Android emulators are not reliable. 40 | #- ./gradlew connectedAndroidTestMockWebServerPiDebug --stacktrace 41 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 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 | # vb-android-app-quality [![Build Status][1]][2] 2 | 3 | Sample Android project using Gradle, with basic quality tools set up. 4 | 5 | It can serve as a clean base for any Gradle Android project, and illustrates my articles about 6 | Gradle on my [website](http://vincentbrison.com). 7 | 8 | The sample app computes Pi using various algorithms, and talks to a REST API. 9 | 10 | ## Libraries 11 | 12 | The following libraries are used in the project: 13 | 14 | - ButterKnife 15 | - Dagger 16 | - Retrofit 17 | - RxAndroid 18 | 19 | ## Quality 20 | 21 | The base configuration to run quality test can be found in the `/config` directory. 22 | The followings tools are used: 23 | 24 | - Checkstyle 25 | - Findbugs 26 | - PMD 27 | - Lint 28 | - Infer 29 | 30 | To run these quality tools and get reports, you need to execute the following Gradle command: 31 | 32 | ```bash 33 | ./gradlew check 34 | ``` 35 | 36 | By default, reports will be generated in `app/build/reports`. 37 | 38 | See [quality.gradle](config/quality.gradle) for configuration options of each tool. 39 | You can for example set: 40 | 41 | - The output format (xml or html) 42 | - Abort the task when a failure is found 43 | 44 | Refer to the [Gradle documentation][3] to configure those plugins. 45 | 46 | ## Flavors 47 | 48 | The project itself show various uses of the flavor system. Flavors are defined in [build.gradle](app/build.gradle). 49 | The following four flavors are defined: 50 | 51 | #### approximationPi 52 | PI is computed using a approximation. 53 | 54 | #### daggerMockedPi 55 | PI is computed using a mocked algorithm. REST communication is mocked through Dagger. 56 | 57 | #### exactPi 58 | PI is computed using an exact algorithm. 59 | 60 | #### mockWebServerPi 61 | PI is computed using a mocked algorithm. REST communication is mocked through MockWebServer. 62 | 63 | You can define several properties to your flavors. One very useful is `applicationId` which let you 64 | change the application ID of your application. This can be used to generate different flavors of 65 | your application and install both of them on the same device (free and paid versions, dev, validation and release versions...). 66 | 67 | You can also put specific sources and resources with flavors. As it is in this project, just create 68 | a directory under the src folder, name it with the name of your flavor, and put inside java codes and resources. 69 | 70 | ## Testing 71 | 72 | Work in progress... 73 | 74 | ## To go further 75 | 76 | This project is based on these two other projects, which are awesome. Consider take a look at them: 77 | 78 | - [Quality-Tools-for-Android][4] 79 | - [volley-examples][5] 80 | 81 | # License 82 | 83 | Copyright 2015 Vincent Brison. 84 | 85 | Licensed under the Apache License, Version 2.0 (the "License"); 86 | you may not use this file except in compliance with the License. 87 | You may obtain a copy of the License at 88 | 89 | http://www.apache.org/licenses/LICENSE-2.0 90 | 91 | Unless required by applicable law or agreed to in writing, software 92 | distributed under the License is distributed on an "AS IS" BASIS, 93 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 94 | See the License for the specific language governing permissions and 95 | limitations under the License. 96 | 97 | [1]: https://travis-ci.org/vincentbrison/vb-android-app-quality.svg?branch=master 98 | [2]: https://travis-ci.org/vincentbrison/vb-android-app-quality 99 | [3]: http://www.gradle.org/docs/current/userguide/userguide.html 100 | [4]: https://github.com/stephanenicolas/Quality-Tools-for-Android 101 | [5]: https://github.com/marcoRS/volley-examples 102 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Vincent Brison. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | apply plugin: 'com.android.application' 18 | apply from: '../config/quality.gradle' 19 | apply plugin: 'spoon' 20 | 21 | android { 22 | compileSdkVersion 25 23 | buildToolsVersion '25.0.2' 24 | defaultConfig { 25 | minSdkVersion 14 26 | targetSdkVersion 25 27 | versionCode 1 28 | versionName "1.0" 29 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 30 | } 31 | 32 | buildTypes { 33 | release { 34 | minifyEnabled false 35 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 36 | } 37 | } 38 | 39 | packagingOptions { 40 | exclude 'LICENSE.txt' 41 | exclude 'META-INF/LICENSE.txt' 42 | exclude 'META-INF/NOTICE.txt' 43 | exclude 'META-INF/ASL2.0' 44 | exclude 'META-INF/LICENSE' 45 | exclude 'META-INF/NOTICE' 46 | exclude 'META-INF/rxjava.properties' 47 | } 48 | 49 | productFlavors { 50 | // PI is computed using a approximation. 51 | approximationPi { 52 | applicationId 'vb.android.app.quality.approximationPi' 53 | } 54 | 55 | // PI is computed using a mocked algorithm. REST communication is mocked through Dagger. 56 | daggerMockedPi { 57 | applicationId 'vb.android.app.quality.daggerMockedPi' 58 | } 59 | 60 | // PI is computed using an exact algorithm. 61 | exactPi { 62 | applicationId 'vb.android.app.quality.exactPi' 63 | } 64 | 65 | // PI is computed using a mocked algorithm. REST communication is mocked through MockWebServer. 66 | mockWebServerPi { 67 | applicationId 'vb.android.app.quality.mockWebServerPi' 68 | } 69 | } 70 | } 71 | 72 | dependencies { 73 | compile 'com.jakewharton:butterknife:8.5.1' 74 | annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1' 75 | 76 | compile 'com.squareup.okhttp3:okhttp:3.6.0' 77 | compile 'com.squareup.okhttp3:logging-interceptor:3.6.0' 78 | compile 'com.squareup.retrofit2:retrofit:2.2.0' 79 | compile 'com.squareup.retrofit2:converter-gson:2.2.0' 80 | compile 'com.squareup.retrofit2:adapter-rxjava:2.2.0' 81 | compile 'com.google.code.gson:gson:2.8.0' 82 | compile 'io.reactivex.rxjava2:rxandroid:2.0.1' 83 | 84 | // Dagger 2 85 | compile 'com.google.dagger:dagger:2.9' 86 | annotationProcessor 'com.google.dagger:dagger-compiler:2.9' 87 | provided 'org.glassfish:javax.annotation:10.0-b28' 88 | 89 | testCompile 'junit:junit:4.12' 90 | 91 | androidTestCompile('com.android.support.test:runner:0.5') { 92 | exclude group: 'com.android.support', module: 'support-annotations' 93 | } 94 | androidTestCompile('com.android.support.test:rules:0.5') { 95 | exclude group: 'com.android.support', module: 'support-annotations' 96 | } 97 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2') { 98 | exclude group: 'com.android.support', module: 'support-annotations' 99 | } 100 | androidTestCompile('com.android.support.test.espresso:espresso-intents:2.2.2') { 101 | exclude group: 'com.android.support', module: 'support-annotations' 102 | } 103 | 104 | androidTestCompile 'com.squareup.okhttp3:mockwebserver:3.6.0' // For creating a mockable web server. 105 | androidTestCompile 'com.jayway.android.robotium:robotium-solo:5.6.3' // for UI tests 106 | androidTestCompile 'com.squareup.spoon:spoon-client:1.6.4' 107 | } 108 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in ${sdk.dir}/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the ProGuard 5 | # include property in project.properties. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/vincentbrison/app/quality/AbstractTestMainActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Vincent Brison. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.vincentbrison.app.quality; 18 | 19 | import android.support.test.espresso.intent.rule.IntentsTestRule; 20 | import android.support.test.runner.AndroidJUnit4; 21 | 22 | import com.squareup.spoon.Spoon; 23 | 24 | import org.junit.After; 25 | import org.junit.Before; 26 | import org.junit.Rule; 27 | import org.junit.Test; 28 | import org.junit.runner.RunWith; 29 | 30 | import java.io.IOException; 31 | 32 | import okhttp3.mockwebserver.MockResponse; 33 | import okhttp3.mockwebserver.MockWebServer; 34 | import vb.android.app.quality.AssetsHelper; 35 | import vb.android.app.quality.R; 36 | import vb.android.app.quality.ui.MainActivity; 37 | 38 | import static junit.framework.Assert.assertTrue; 39 | 40 | /** 41 | * Test class which describe functional tests for the main activity. 42 | */ 43 | @RunWith(AndroidJUnit4.class) 44 | public abstract class AbstractTestMainActivity { 45 | 46 | protected MockWebServer mMockWebServer; 47 | 48 | protected abstract void userAskPIComputation(); 49 | 50 | protected abstract void userAskSendPIOnlineForRank(); 51 | 52 | protected abstract void userAskShare(); 53 | 54 | protected abstract boolean checkPIComputationWentOK(); 55 | 56 | protected abstract boolean checkSendPIWentOK(); 57 | 58 | protected abstract boolean checkSendPIWentWrong(); 59 | 60 | protected abstract boolean checkShareWentOK(); 61 | 62 | @Rule 63 | public IntentsTestRule mActivityRule = new IntentsTestRule<>( 64 | MainActivity.class); 65 | 66 | @Before 67 | public void setUp() throws Exception { 68 | mMockWebServer = new MockWebServer(); 69 | try { 70 | mMockWebServer.start(Integer.parseInt(mActivityRule.getActivity().getString(R.string.port))); 71 | } catch (IOException e) { 72 | throw e; 73 | } 74 | } 75 | 76 | @After 77 | public void tearDown() throws Exception { 78 | mMockWebServer.shutdown(); 79 | } 80 | 81 | @Test 82 | public void testThatDefaultBehaviorIsWorking() throws Exception { 83 | mMockWebServer.enqueue(new MockResponse().setBody(AssetsHelper.getStringFromAsset("stubs/rank_ok.json"))); 84 | userAskPIComputation(); 85 | assertTrue("After a Pi computation, user is able to send its result.", checkPIComputationWentOK()); 86 | Spoon.screenshot(mActivityRule.getActivity(), "checkPIComputationWentOK"); 87 | userAskSendPIOnlineForRank(); 88 | assertTrue("After ranking his result online, the user should be able to share his rank.", checkSendPIWentOK()); 89 | Spoon.screenshot(mActivityRule.getActivity(), "checkSendPIWentOK"); 90 | userAskShare(); 91 | assertTrue("After asking for share, the user should be able to choose how he wants to share", checkShareWentOK()); 92 | Spoon.screenshot(mActivityRule.getActivity(), "checkShareWentOK"); 93 | } 94 | 95 | @Test 96 | public void testThatServerIssueDisplayToast() { 97 | mMockWebServer.enqueue(new MockResponse().setResponseCode(500)); 98 | userAskPIComputation(); 99 | assertTrue("After a Pi computation, user is able to send its result.", checkPIComputationWentOK()); 100 | Spoon.screenshot(mActivityRule.getActivity(), "checkPIComputationWentOK"); 101 | userAskSendPIOnlineForRank(); 102 | assertTrue("A Toast message should be displayed.", checkSendPIWentWrong()); 103 | Spoon.screenshot(mActivityRule.getActivity(), "checkSendPIWentWrong"); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/vincentbrison/app/quality/espresso/TestMainActivityWithEspresso.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Vincent Brison. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.vincentbrison.app.quality.espresso; 18 | 19 | import android.app.Instrumentation; 20 | import android.content.Intent; 21 | import android.support.test.espresso.intent.Intents; 22 | import android.support.test.espresso.intent.matcher.IntentMatchers; 23 | 24 | import com.vincentbrison.app.quality.AbstractTestMainActivity; 25 | 26 | import vb.android.app.quality.R; 27 | 28 | import static android.support.test.espresso.Espresso.onView; 29 | import static android.support.test.espresso.action.ViewActions.click; 30 | import static android.support.test.espresso.action.ViewActions.typeText; 31 | import static android.support.test.espresso.assertion.ViewAssertions.matches; 32 | import static android.support.test.espresso.intent.Intents.intended; 33 | import static android.support.test.espresso.intent.matcher.IntentMatchers.hasAction; 34 | import static android.support.test.espresso.matcher.RootMatchers.withDecorView; 35 | import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; 36 | import static android.support.test.espresso.matcher.ViewMatchers.isEnabled; 37 | import static android.support.test.espresso.matcher.ViewMatchers.withId; 38 | import static android.support.test.espresso.matcher.ViewMatchers.withText; 39 | import static org.hamcrest.Matchers.not; 40 | import static org.hamcrest.core.AllOf.allOf; 41 | 42 | /** 43 | * Class to test instrumentation testing with the help of Espresso. 44 | */ 45 | public class TestMainActivityWithEspresso extends AbstractTestMainActivity { 46 | 47 | @Override 48 | protected void userAskPIComputation() { 49 | onView(withId(R.id.editTextDigits)).perform(typeText("5")); 50 | onView(withId(R.id.buttonCompute)).perform(click()); 51 | } 52 | 53 | @Override 54 | protected void userAskSendPIOnlineForRank() { 55 | onView(withId(R.id.buttonSendPi)).perform(click()); 56 | } 57 | 58 | @Override 59 | protected void userAskShare() { 60 | onView(withId(R.id.buttonShareResult)).perform(click()); 61 | } 62 | 63 | @Override 64 | protected boolean checkPIComputationWentOK() { 65 | onView(withId(R.id.buttonSendPi)).check(matches(isEnabled())); 66 | return true; 67 | } 68 | 69 | @Override 70 | protected boolean checkSendPIWentOK() { 71 | onView(withId(R.id.buttonShareResult)).check(matches(isEnabled())); 72 | return true; 73 | } 74 | 75 | @Override 76 | protected boolean checkSendPIWentWrong() { 77 | onView(withText(R.string.network_issue)).inRoot(withDecorView( 78 | not((mActivityRule.getActivity().getWindow().getDecorView())))).check(matches(isDisplayed())); 79 | return true; 80 | } 81 | 82 | @Override 83 | protected boolean checkShareWentOK() { 84 | intended(allOf( 85 | hasAction(Intent.ACTION_SEND), 86 | IntentMatchers.hasExtra(Intent.EXTRA_SUBJECT, mActivityRule.getActivity().getString(R.string.share_title)))); 87 | return true; 88 | } 89 | 90 | @Override 91 | public void testThatDefaultBehaviorIsWorking() throws Exception { 92 | Instrumentation.ActivityResult dummyResult = new Instrumentation.ActivityResult(0, null); 93 | Intents.intending(allOf( 94 | hasAction(Intent.ACTION_SEND), 95 | IntentMatchers.hasExtra(Intent.EXTRA_SUBJECT, mActivityRule.getActivity().getString(R.string.share_title)))) 96 | .respondWith(dummyResult); 97 | super.testThatDefaultBehaviorIsWorking(); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/vincentbrison/app/quality/robotium/TestMainActivityWithRobotium.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Vincent Brison. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.vincentbrison.app.quality.robotium; 18 | 19 | import android.support.test.InstrumentationRegistry; 20 | import android.widget.Button; 21 | import android.widget.EditText; 22 | 23 | import com.robotium.solo.Condition; 24 | import com.robotium.solo.Solo; 25 | import com.vincentbrison.app.quality.AbstractTestMainActivity; 26 | 27 | import vb.android.app.quality.R; 28 | import vb.android.app.quality.ui.MainActivity; 29 | 30 | /** 31 | * Class to test instrumentation testing with the help of robotium. 32 | */ 33 | public class TestMainActivityWithRobotium extends AbstractTestMainActivity { 34 | 35 | protected Solo mSolo; 36 | protected EditText mEditText; 37 | protected Button mButtonSendPI; 38 | protected Button mButtonCompute; 39 | protected Button mButtonShare; 40 | 41 | @Override 42 | public void setUp() throws Exception { 43 | super.setUp(); 44 | mSolo = new Solo(InstrumentationRegistry.getInstrumentation(), mActivityRule.getActivity()); 45 | mEditText = (EditText) mSolo.getView(R.id.editTextDigits); 46 | mButtonSendPI = (Button) mSolo.getView(R.id.buttonSendPi); 47 | mButtonCompute = (Button) mSolo.getView(R.id.buttonCompute); 48 | mButtonShare = (Button) mSolo.getView(R.id.buttonShareResult); 49 | } 50 | 51 | @Override 52 | public void tearDown() throws Exception { 53 | mSolo.finishOpenedActivities(); 54 | super.tearDown(); 55 | } 56 | 57 | @Override 58 | protected void userAskPIComputation() { 59 | mSolo.enterText(mEditText, "5"); 60 | mSolo.clickOnView(mButtonCompute); 61 | } 62 | 63 | @Override 64 | protected void userAskSendPIOnlineForRank() { 65 | mSolo.clickOnView(mButtonSendPI); 66 | } 67 | 68 | @Override 69 | protected void userAskShare() { 70 | mSolo.clickOnView(mButtonShare); 71 | } 72 | 73 | @Override 74 | protected boolean checkPIComputationWentOK() { 75 | mSolo.waitForCondition(new Condition() { 76 | @Override 77 | public boolean isSatisfied() { 78 | return mButtonSendPI.isEnabled(); 79 | } 80 | }, 5000); 81 | return mButtonSendPI.isEnabled(); 82 | } 83 | 84 | @Override 85 | protected boolean checkSendPIWentOK() { 86 | mSolo.waitForCondition(new Condition() { 87 | @Override 88 | public boolean isSatisfied() { 89 | return mButtonShare.isEnabled(); 90 | } 91 | }, 5000); 92 | return mButtonShare.isEnabled(); 93 | } 94 | 95 | @Override 96 | protected boolean checkSendPIWentWrong() { 97 | mSolo.waitForText(mSolo.getString(R.string.network_issue)); 98 | return mSolo.searchText(mSolo.getString(R.string.network_issue)); 99 | } 100 | 101 | @Override 102 | protected boolean checkShareWentOK() { 103 | // Cannot check that intent is deliver since its go outside of the application. 104 | // Return fake result true. 105 | return true; 106 | } 107 | 108 | @Override 109 | public void testThatDefaultBehaviorIsWorking() throws Exception { 110 | mSolo.waitForActivity(MainActivity.class); 111 | super.testThatDefaultBehaviorIsWorking(); 112 | } 113 | 114 | @Override 115 | public void testThatServerIssueDisplayToast() { 116 | mSolo.waitForActivity(MainActivity.class); 117 | super.testThatServerIssueDisplayToast(); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /app/src/approximationPi/java/vb/android/app/quality/PiCalculator.java: -------------------------------------------------------------------------------- 1 | package vb.android.app.quality; 2 | 3 | import vb.android.app.quality.pi.PiGenerator; 4 | 5 | /** 6 | * Slow way to compute PI based on iterations. 7 | */ 8 | public final class PiCalculator implements PiGenerator { 9 | @Override 10 | public CalculationMethod getCalculationMethod() { 11 | return CalculationMethod.ITERATIONS; 12 | } 13 | 14 | @Override 15 | public double calcPiDigits(int max) { 16 | double sum = 0.0; // final sum 17 | double term; // term without sign 18 | double sign = 1.0; // sign on each term 19 | for (int k = 0; k < max; k++) { 20 | term = 1.0 / (2.0 * k + 1.0); 21 | sum = sum + sign * term; 22 | sign = -sign; 23 | } 24 | return sum * 4; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/approximationPi/java/vb/android/app/quality/dagger/DataModule.java: -------------------------------------------------------------------------------- 1 | package vb.android.app.quality.dagger; 2 | 3 | import android.content.Context; 4 | 5 | import javax.inject.Singleton; 6 | 7 | import dagger.Module; 8 | import dagger.Provides; 9 | import okhttp3.OkHttpClient; 10 | import okhttp3.logging.HttpLoggingInterceptor; 11 | import retrofit2.Retrofit; 12 | import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; 13 | import retrofit2.converter.gson.GsonConverterFactory; 14 | import vb.android.app.quality.R; 15 | import vb.android.app.quality.rest.ApiInterface; 16 | 17 | /** 18 | * Module which expose data providers implementation used by the app. 19 | */ 20 | @Module(includes = { 21 | AppModule.class 22 | }) 23 | public final class DataModule { 24 | /** 25 | * Return the OkHttpClient implementation used by this app. 26 | * 27 | * @return the OkHttpClient implementation used by this app. 28 | */ 29 | @Provides 30 | @Singleton 31 | public OkHttpClient provideOkHttpClient() { 32 | HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); 33 | interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); 34 | return new OkHttpClient.Builder().addInterceptor(interceptor).build(); 35 | } 36 | 37 | /** 38 | * Return the REST API implementation used by this app. 39 | * 40 | * @param context is used to resolve string. 41 | * @param client the OkHttpClient to use. 42 | * @return the REST API implementation used by this app. 43 | */ 44 | @Provides 45 | @Singleton 46 | public ApiInterface provideApi(Context context, OkHttpClient client) { 47 | return new Retrofit.Builder() 48 | .baseUrl(context.getString(R.string.url)) 49 | .addConverterFactory(GsonConverterFactory.create()) 50 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 51 | .client(client) 52 | .build() 53 | .create(ApiInterface.class); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/approximationPi/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | #0066cc 20 | 21 | -------------------------------------------------------------------------------- /app/src/approximationPi/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | Enter the number of iteration to approximate Pi : 20 | iteration(s) 21 | approximation 22 | http://echo.jsontest.com 23 | 24 | -------------------------------------------------------------------------------- /app/src/daggerMockedPi/java/vb/android/app/quality/PiCalculator.java: -------------------------------------------------------------------------------- 1 | package vb.android.app.quality; 2 | 3 | import vb.android.app.quality.pi.PiGenerator; 4 | 5 | /** 6 | * Mock class to return static value of PI. 7 | */ 8 | public final class PiCalculator implements PiGenerator { 9 | @Override 10 | public CalculationMethod getCalculationMethod() { 11 | return CalculationMethod.STATIC; 12 | } 13 | 14 | @Override 15 | public double calcPiDigits(int max) { 16 | return Math.PI; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/daggerMockedPi/java/vb/android/app/quality/dagger/DataModule.java: -------------------------------------------------------------------------------- 1 | package vb.android.app.quality.dagger; 2 | 3 | import javax.inject.Singleton; 4 | 5 | import dagger.Module; 6 | import dagger.Provides; 7 | import io.reactivex.Observable; 8 | import retrofit2.http.Path; 9 | import vb.android.app.quality.rest.ApiInterface; 10 | import vb.android.app.quality.rest.ResponseRank; 11 | 12 | /** 13 | * Module which expose data providers implementation used by the app. 14 | */ 15 | @Module 16 | public final class DataModule { 17 | /** 18 | * Return the REST API implementation used by this app. 19 | * 20 | * @return the REST API implementation used by this app. 21 | */ 22 | @Provides 23 | @Singleton 24 | public ApiInterface provideApi() { 25 | return new MockApiInterface(); 26 | } 27 | 28 | private static class MockApiInterface implements ApiInterface { 29 | @Override 30 | public Observable getRank( 31 | @Path("algo") String algo, @Path("time") double time, @Path("max") double max) { 32 | ResponseRank rank = new ResponseRank(); 33 | rank.setRank(2); 34 | return Observable.just(rank); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/daggerMockedPi/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | #000000 20 | 21 | -------------------------------------------------------------------------------- /app/src/daggerMockedPi/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | Enter the number of digits to compute for Pi (mocked) : 20 | digit(s) 21 | mock 22 | 23 | -------------------------------------------------------------------------------- /app/src/exactPi/java/vb/android/app/quality/PiCalculator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Vincent Brison. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package vb.android.app.quality; 18 | 19 | import java.math.BigInteger; 20 | 21 | import vb.android.app.quality.pi.PiGenerator; 22 | 23 | /** 24 | * Slow way to compute PI based on digits. 25 | */ 26 | public final class PiCalculator implements PiGenerator { 27 | private static final BigInteger TWO = BigInteger.valueOf(2); 28 | private static final BigInteger THREE = BigInteger.valueOf(3); 29 | private static final BigInteger FOUR = BigInteger.valueOf(4); 30 | private static final BigInteger SEVEN = BigInteger.valueOf(7); 31 | 32 | @Override 33 | public CalculationMethod getCalculationMethod() { 34 | return CalculationMethod.DIGITS; 35 | } 36 | 37 | @Override 38 | public double calcPiDigits(int max) { 39 | BigInteger q = BigInteger.ONE; 40 | BigInteger r = BigInteger.ZERO; 41 | BigInteger t = BigInteger.ONE; 42 | BigInteger k = BigInteger.ONE; 43 | BigInteger n = BigInteger.valueOf(3); 44 | BigInteger l = BigInteger.valueOf(3); 45 | BigInteger nn; 46 | BigInteger nr; 47 | double pi = 0; 48 | boolean first = true; 49 | int digits = 0; 50 | BigInteger divider = BigInteger.ONE; 51 | while (digits < max) { 52 | if (FOUR.multiply(q).add(r).subtract(t).compareTo(n.multiply(t)) == -1) { 53 | pi += n.doubleValue() / divider.doubleValue(); 54 | divider = divider.multiply(BigInteger.TEN); 55 | if (first) { 56 | first = false; 57 | } 58 | nr = BigInteger.TEN.multiply(r.subtract(n.multiply(t))); 59 | n = BigInteger.TEN.multiply(THREE.multiply(q).add(r)).divide(t).subtract(BigInteger.TEN.multiply(n)); 60 | q = q.multiply(BigInteger.TEN); 61 | r = nr; 62 | digits++; 63 | } else { 64 | nr = TWO.multiply(q).add(r).multiply(l); 65 | nn = q.multiply((SEVEN.multiply(k))).add(TWO).add(r.multiply(l)).divide(t.multiply(l)); 66 | q = q.multiply(k); 67 | t = t.multiply(l); 68 | l = l.add(TWO); 69 | k = k.add(BigInteger.ONE); 70 | n = nn; 71 | r = nr; 72 | } 73 | } 74 | return pi; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/src/exactPi/java/vb/android/app/quality/dagger/DataModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Vincent Brison. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package vb.android.app.quality.dagger; 18 | 19 | import android.content.Context; 20 | 21 | import javax.inject.Singleton; 22 | 23 | import dagger.Module; 24 | import dagger.Provides; 25 | import okhttp3.OkHttpClient; 26 | import okhttp3.logging.HttpLoggingInterceptor; 27 | import retrofit2.Retrofit; 28 | import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; 29 | import retrofit2.converter.gson.GsonConverterFactory; 30 | import vb.android.app.quality.R; 31 | import vb.android.app.quality.rest.ApiInterface; 32 | 33 | /** 34 | * Module which expose data providers implementation used by the app. 35 | */ 36 | @Module(includes = { 37 | AppModule.class 38 | }) 39 | public final class DataModule { 40 | /** 41 | * Return the OkHttpClient implementation used by this app. 42 | * 43 | * @return the OkHttpClient implementation used by this app. 44 | */ 45 | @Provides 46 | @Singleton 47 | public OkHttpClient provideOkHttpClient() { 48 | HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); 49 | interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); 50 | return new OkHttpClient.Builder().addInterceptor(interceptor).build(); 51 | } 52 | 53 | /** 54 | * Return the REST API implementation used by this app. 55 | * 56 | * @param context is used to resolve string. 57 | * @param client the OkHttpClient to use. 58 | * @return the REST API implementation used by this app. 59 | */ 60 | @Provides 61 | @Singleton 62 | public ApiInterface provideApi(Context context, OkHttpClient client) { 63 | return new Retrofit.Builder() 64 | .baseUrl(context.getString(R.string.url)) 65 | .addConverterFactory(GsonConverterFactory.create()) 66 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 67 | .client(client) 68 | .build() 69 | .create(ApiInterface.class); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/src/exactPi/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | #FF0000 20 | 21 | -------------------------------------------------------------------------------- /app/src/exactPi/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | Enter the number of digits to compute for Pi : 20 | digit(s) 21 | exact 22 | http://echo.jsontest.com 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 22 | 23 | 24 | 25 | 26 | 34 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/java/vb/android/app/quality/InjectorHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Vincent Brison. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package vb.android.app.quality; 18 | 19 | import vb.android.app.quality.app.QualityApplication; 20 | import vb.android.app.quality.dagger.AppComponent; 21 | import vb.android.app.quality.dagger.AppModule; 22 | import vb.android.app.quality.dagger.DaggerAppComponent; 23 | 24 | /** 25 | * Helper to do dagger injection. 26 | */ 27 | public final class InjectorHelper { 28 | private static AppComponent sApplicationComponent; 29 | 30 | private InjectorHelper() { 31 | } 32 | 33 | /** 34 | * Init the dagger graph. 35 | * 36 | * @param application is the context of the application. 37 | */ 38 | public static void initializeApplicationComponent(QualityApplication application) { 39 | sApplicationComponent = DaggerAppComponent.builder().appModule(new AppModule(application)).build(); 40 | } 41 | 42 | /** 43 | * Return the app component to do injection. 44 | * 45 | * @return the app component to do injection. 46 | */ 47 | public static AppComponent getApplicationComponent() { 48 | return sApplicationComponent; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/vb/android/app/quality/app/QualityApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Vincent Brison. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package vb.android.app.quality.app; 18 | 19 | import android.app.Application; 20 | import android.os.StrictMode; 21 | 22 | import vb.android.app.quality.BuildConfig; 23 | import vb.android.app.quality.InjectorHelper; 24 | 25 | /** 26 | * Subclass of app to access easily to the application context and have a callback when the app is created. 27 | */ 28 | public class QualityApplication extends Application { 29 | 30 | @Override 31 | public void onCreate() { 32 | super.onCreate(); 33 | InjectorHelper.initializeApplicationComponent(this); 34 | 35 | if (BuildConfig.DEBUG) { 36 | StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() 37 | .detectAll() 38 | .penaltyLog() 39 | .build()); 40 | StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() 41 | .detectAll() 42 | .penaltyLog() 43 | .penaltyDeath() 44 | .build()); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/vb/android/app/quality/dagger/AppComponent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Vincent Brison. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package vb.android.app.quality.dagger; 18 | 19 | import android.content.Context; 20 | 21 | import javax.inject.Singleton; 22 | 23 | import dagger.Component; 24 | import vb.android.app.quality.rest.ApiInterface; 25 | import vb.android.app.quality.ui.MainActivity; 26 | 27 | /** 28 | * Component (dagger) with activity scope. 29 | */ 30 | @Singleton 31 | @Component(modules = { 32 | DataModule.class, AppModule.class 33 | }) 34 | public interface AppComponent { 35 | /** 36 | * Return the REST API used by the app. 37 | * 38 | * @return the REST API used by the app. 39 | */ 40 | ApiInterface apiInterface(); 41 | 42 | /** 43 | * Return the application context. 44 | * 45 | * @return the application context. 46 | */ 47 | Context context(); 48 | 49 | /** 50 | * Inject the objects exposed by this component. 51 | * 52 | * @param mainActivity is the activity to inject. 53 | */ 54 | void inject(MainActivity mainActivity); 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/vb/android/app/quality/dagger/AppModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Vincent Brison. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package vb.android.app.quality.dagger; 18 | 19 | import android.content.Context; 20 | 21 | import javax.inject.Singleton; 22 | 23 | import dagger.Module; 24 | import dagger.Provides; 25 | import vb.android.app.quality.app.QualityApplication; 26 | 27 | /** 28 | * Module which expose Android application related objects. 29 | */ 30 | @Module 31 | public class AppModule { 32 | 33 | private final Context mAppContext; 34 | 35 | /** 36 | * This module will hold the application context. 37 | * 38 | * @param appContext is the context of the application. 39 | */ 40 | public AppModule(QualityApplication appContext) { 41 | mAppContext = appContext; 42 | } 43 | 44 | @Provides 45 | @Singleton 46 | public Context getApplicationContext() { 47 | return mAppContext; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/vb/android/app/quality/pi/PiGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Vincent Brison. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package vb.android.app.quality.pi; 18 | 19 | /** 20 | * Interface for generating Pi 21 | */ 22 | public interface PiGenerator { 23 | enum CalculationMethod { 24 | /** 25 | * Algorithm based on number of digits 26 | */ 27 | DIGITS, 28 | /** 29 | * Algorithm based on number of iterations 30 | */ 31 | ITERATIONS, 32 | /** 33 | * No calculation, return static Math.PI 34 | */ 35 | STATIC, 36 | } 37 | 38 | /** 39 | * Returns the method of calculation of this generator. 40 | * 41 | * @return the method of calculation of this generator 42 | */ 43 | CalculationMethod getCalculationMethod(); 44 | 45 | /** 46 | * Compute Pi. 47 | * 48 | * @param max is used to parameter the algorithm used for computation (max iteration, max digits...). 49 | * @return the value of Pi. 50 | */ 51 | double calcPiDigits(int max); 52 | } 53 | -------------------------------------------------------------------------------- /app/src/main/java/vb/android/app/quality/pi/PiTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Vincent Brison. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package vb.android.app.quality.pi; 18 | 19 | import android.os.AsyncTask; 20 | 21 | import java.lang.ref.WeakReference; 22 | 23 | import vb.android.app.quality.PiCalculator; 24 | 25 | /** 26 | * AsyncTask to compute PI. 27 | */ 28 | public class PiTask extends AsyncTask { 29 | 30 | private int mMax; 31 | private WeakReference mCallback; 32 | 33 | /** 34 | * Configure the computation task. 35 | * 36 | * @param max is a limitation give to the computation. 37 | * @param callback will be trigger when computation end. 38 | */ 39 | public PiTask(int max, PiTaskCallback callback) { 40 | mMax = max; 41 | mCallback = new WeakReference<>(callback); 42 | } 43 | 44 | @Override 45 | protected Double doInBackground(Void... params) { 46 | return new PiCalculator().calcPiDigits(mMax); 47 | } 48 | 49 | @Override 50 | protected void onPostExecute(Double aDouble) { 51 | super.onPostExecute(aDouble); 52 | if (mCallback.get() != null) { 53 | mCallback.get().onPiComputed(aDouble); 54 | } 55 | } 56 | 57 | /** 58 | * Callback interface used when PI is computed. 59 | */ 60 | public interface PiTaskCallback { 61 | /** 62 | * Call when PI is computed. 63 | * 64 | * @param pi is the value of pi which was computed. 65 | */ 66 | void onPiComputed(double pi); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/vb/android/app/quality/rest/ApiInterface.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Vincent Brison. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package vb.android.app.quality.rest; 18 | 19 | import io.reactivex.Observable; 20 | import retrofit2.http.GET; 21 | import retrofit2.http.Path; 22 | 23 | /** 24 | * Describe the REST API with which the app is communicating 25 | */ 26 | public interface ApiInterface { 27 | /** 28 | * Return the rank of the given performance. 29 | * 30 | * @param algo is the algorithm used to do computation. 31 | * @param time is the time spent during computation. 32 | * @param max is the limit used to limit the computation. 33 | * @return the rank of the given performance. 34 | */ 35 | @GET("/algo/{algo}/time/{time}/max/{max}") 36 | Observable getRank(@Path("algo") String algo, 37 | @Path("time") double time, 38 | @Path("max") double max); 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/vb/android/app/quality/rest/ResponseRank.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Vincent Brison. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package vb.android.app.quality.rest; 18 | 19 | import com.google.gson.annotations.SerializedName; 20 | 21 | /** 22 | * Created by Vincent Brison on 14/06/2015. 23 | * Describe the result of ranking. 24 | */ 25 | public class ResponseRank { 26 | 27 | @SerializedName("rank") 28 | private int mRank; 29 | 30 | public int getRank() { 31 | return mRank; 32 | } 33 | 34 | public void setRank(int rank) { 35 | mRank = rank; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/vb/android/app/quality/ui/MainActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Vincent Brison. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package vb.android.app.quality.ui; 18 | 19 | import android.app.Activity; 20 | import android.content.Intent; 21 | import android.os.Bundle; 22 | import android.text.TextUtils; 23 | import android.view.View; 24 | import android.view.Window; 25 | import android.widget.Button; 26 | import android.widget.EditText; 27 | import android.widget.TextView; 28 | import android.widget.Toast; 29 | 30 | import javax.inject.Inject; 31 | 32 | import butterknife.BindView; 33 | import butterknife.ButterKnife; 34 | import io.reactivex.Observer; 35 | import io.reactivex.android.schedulers.AndroidSchedulers; 36 | import io.reactivex.disposables.Disposable; 37 | import io.reactivex.schedulers.Schedulers; 38 | import vb.android.app.quality.InjectorHelper; 39 | import vb.android.app.quality.R; 40 | import vb.android.app.quality.pi.PiTask; 41 | import vb.android.app.quality.rest.ApiInterface; 42 | import vb.android.app.quality.rest.ResponseRank; 43 | 44 | /** 45 | * Created by Vincent Brison. 46 | */ 47 | public class MainActivity extends Activity implements PiTask.PiTaskCallback, Observer { 48 | 49 | private enum State { 50 | IDLE, 51 | IS_COMPUTING, 52 | IS_PI_COMPUTED, 53 | IS_SENDING, 54 | IS_RANK_READY, 55 | } 56 | 57 | protected State mState = State.IDLE; 58 | 59 | @BindView(R.id.textViewName) 60 | protected TextView mTextViewName; 61 | 62 | @BindView(R.id.textViewValue) 63 | protected TextView mTextViewValue; 64 | 65 | @BindView(R.id.buttonCompute) 66 | protected Button mButtonCompute; 67 | 68 | @BindView(R.id.editTextDigits) 69 | protected EditText mEditTextDigits; 70 | 71 | @BindView(R.id.buttonSendPi) 72 | protected Button mButtonSendPi; 73 | 74 | @BindView(R.id.buttonShareResult) 75 | protected Button mButtonShare; 76 | 77 | @BindView(R.id.textviewRank) 78 | protected TextView mTextViewRank; 79 | 80 | @Inject 81 | protected ApiInterface mApi; 82 | 83 | protected int mMax; 84 | protected long mStartTime; 85 | protected long mTime; 86 | protected ResponseRank mResponseRank; 87 | 88 | @Override 89 | protected void onCreate(Bundle savedInstanceState) { 90 | super.onCreate(savedInstanceState); 91 | 92 | requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); 93 | setContentView(R.layout.activity_main); 94 | ButterKnife.bind(this); 95 | InjectorHelper.getApplicationComponent().inject(this); 96 | mButtonSendPi.setEnabled(false); 97 | mButtonShare.setEnabled(false); 98 | mButtonCompute.setOnClickListener(new View.OnClickListener() { 99 | @Override 100 | public void onClick(View v) { 101 | if (!mState.equals(State.IS_COMPUTING) || mState.equals(State.IS_SENDING)) { 102 | if (TextUtils.isEmpty(mEditTextDigits.getText().toString())) { 103 | Toast.makeText(MainActivity.this, 104 | R.string.enter_number_of_digits, Toast.LENGTH_SHORT).show(); 105 | } else { 106 | setState(State.IS_COMPUTING); 107 | int digits = Integer.parseInt(mEditTextDigits.getText().toString()); 108 | PiTask task = new PiTask(digits, MainActivity.this); 109 | mStartTime = System.currentTimeMillis(); 110 | mMax = digits; 111 | task.execute(); 112 | } 113 | } 114 | } 115 | }); 116 | 117 | mButtonSendPi.setOnClickListener(new View.OnClickListener() { 118 | @Override 119 | public void onClick(View v) { 120 | if (!mState.equals(State.IS_COMPUTING) || mState.equals(State.IS_SENDING)) { 121 | setState(State.IS_SENDING); 122 | mApi.getRank(getString(R.string.algo), mTime, mMax) 123 | .subscribeOn(Schedulers.io()) 124 | .observeOn(AndroidSchedulers.mainThread()) 125 | .subscribe(MainActivity.this); 126 | } 127 | } 128 | }); 129 | 130 | mButtonShare.setOnClickListener(new View.OnClickListener() { 131 | @Override 132 | public void onClick(View v) { 133 | if (mState.equals(State.IS_RANK_READY)) { 134 | Intent intentShare = new Intent(Intent.ACTION_SEND); 135 | intentShare.setType("text/plain"); 136 | intentShare.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.share_title)); 137 | intentShare.putExtra( 138 | Intent.EXTRA_TEXT, "My rank is " + mResponseRank.getRank() + " on Pi computing bench."); 139 | startActivity(intentShare); 140 | } 141 | } 142 | }); 143 | } 144 | 145 | @Override 146 | public void onPiComputed(double pi) { 147 | mTime = System.currentTimeMillis() - mStartTime; 148 | String value = "Pi = " + pi + "(computed in " + mTime 149 | + " ms, for " + mMax + " " + getString(R.string.max_desc) + ")"; 150 | mTextViewValue.setText(value); 151 | setState(State.IS_PI_COMPUTED); 152 | } 153 | 154 | private void setState(State state) { 155 | mState = state; 156 | if (state.equals(State.IS_PI_COMPUTED)) { 157 | mEditTextDigits.setEnabled(true); 158 | mButtonCompute.setEnabled(true); 159 | mButtonSendPi.setEnabled(true); 160 | mButtonShare.setEnabled(false); 161 | setProgressBarIndeterminateVisibility(false); 162 | } else if (state.equals(State.IS_COMPUTING)) { 163 | mEditTextDigits.setEnabled(false); 164 | mButtonCompute.setEnabled(false); 165 | mButtonSendPi.setEnabled(false); 166 | mButtonShare.setEnabled(false); 167 | setProgressBarIndeterminateVisibility(true); 168 | } else if (state.equals(State.IS_SENDING)) { 169 | mEditTextDigits.setEnabled(false); 170 | mButtonCompute.setEnabled(false); 171 | mButtonSendPi.setEnabled(false); 172 | mButtonShare.setEnabled(false); 173 | setProgressBarIndeterminateVisibility(true); 174 | } else if (state.equals(State.IS_RANK_READY)) { 175 | mEditTextDigits.setEnabled(true); 176 | mButtonCompute.setEnabled(true); 177 | mButtonSendPi.setEnabled(false); 178 | mButtonShare.setEnabled(true); 179 | setProgressBarIndeterminateVisibility(false); 180 | } 181 | } 182 | 183 | @Override 184 | public void onComplete() { 185 | setProgressBarIndeterminateVisibility(false); 186 | } 187 | 188 | @Override 189 | public void onError(Throwable e) { 190 | Toast.makeText(this, getString(R.string.network_issue), Toast.LENGTH_SHORT).show(); 191 | setState(State.IS_PI_COMPUTED); 192 | } 193 | 194 | @Override 195 | public void onSubscribe(Disposable disposable) { 196 | } 197 | 198 | @Override 199 | public void onNext(ResponseRank responseRank) { 200 | setState(State.IS_RANK_READY); 201 | mResponseRank = responseRank; 202 | mTextViewRank.setText(getString(R.string.your_rank_is, responseRank.getRank())); 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentbrison/vb-android-app-quality/6d9a7a48d737352c02bf4e614813b3f085b45f0d/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentbrison/vb-android-app-quality/6d9a7a48d737352c02bf4e614813b3f085b45f0d/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentbrison/vb-android-app-quality/6d9a7a48d737352c02bf4e614813b3f085b45f0d/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentbrison/vb-android-app-quality/6d9a7a48d737352c02bf4e614813b3f085b45f0d/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 28 | 29 | 37 | 38 | 43 | 44 |