├── .editorconfig ├── .github ├── FUNDING.yml └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── RELEASING.md ├── app-kotlin ├── .gitignore ├── build.gradle ├── detekt.yml ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── kotlin │ └── com │ │ └── github │ │ └── pwittchen │ │ └── reactivenetwork │ │ └── kotlinapp │ │ └── MainActivity.kt │ └── res │ ├── layout │ └── activity_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── github │ │ └── pwittchen │ │ └── reactivenetwork │ │ └── app │ │ └── MainActivity.java │ └── res │ ├── layout │ └── activity_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── codecov.yml ├── 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 ├── jetbrains_logo.png ├── library ├── .gitignore ├── build.gradle ├── gradle.properties ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── com │ │ └── github │ │ └── pwittchen │ │ └── reactivenetwork │ │ └── library │ │ └── rx2 │ │ ├── Connectivity.java │ │ ├── ConnectivityPredicate.java │ │ ├── Preconditions.java │ │ ├── ReactiveNetwork.java │ │ ├── internet │ │ └── observing │ │ │ ├── InternetObservingSettings.java │ │ │ ├── InternetObservingStrategy.java │ │ │ ├── error │ │ │ ├── DefaultErrorHandler.java │ │ │ └── ErrorHandler.java │ │ │ └── strategy │ │ │ ├── SocketInternetObservingStrategy.java │ │ │ └── WalledGardenInternetObservingStrategy.java │ │ └── network │ │ └── observing │ │ ├── NetworkObservingStrategy.java │ │ └── strategy │ │ ├── LollipopNetworkObservingStrategy.java │ │ ├── MarshmallowNetworkObservingStrategy.java │ │ └── PreLollipopNetworkObservingStrategy.java │ └── test │ ├── java │ └── com │ │ └── github │ │ └── pwittchen │ │ └── reactivenetwork │ │ └── library │ │ └── rx2 │ │ ├── ConnectivityTest.java │ │ ├── PreconditionsTest.java │ │ ├── ReactiveNetworkTest.java │ │ ├── internet │ │ └── observing │ │ │ ├── InternetObservingSettingsTest.java │ │ │ ├── error │ │ │ └── DefaultErrorHandlerTest.java │ │ │ └── strategy │ │ │ ├── SocketInternetObservingStrategyTest.java │ │ │ └── WalledGardenInternetObservingStrategyTest.java │ │ └── network │ │ └── observing │ │ ├── NetworkObservingStrategyTest.java │ │ └── strategy │ │ ├── LollipopNetworkObservingStrategyTest.java │ │ ├── MarshmallowNetworkObservingStrategyTest.java │ │ └── PreLollipopNetworkObservingStrategyTest.java │ └── resources │ └── robolectric.properties ├── maven_push.gradle ├── release.sh ├── settings.gradle ├── update_docs.sh └── update_javadocs.sh /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{kt,kts}] 2 | # possible values: number (e.g. 2), "unset" (makes ktlint ignore indentation completely) 3 | indent_size=2 4 | # possible values: number (e.g. 2), "unset" 5 | continuation_indent_size=2 6 | # true (recommended) / false 7 | insert_final_newline=unset 8 | # possible values: number (e.g. 120) (package name, imports & comments are ignored), "off" 9 | # it's automatically set to 100 on `ktlint --android ...` (per Android Kotlin Style Guide) 10 | max_line_length=off -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [pwittchen] 2 | custom: ['https://paypal.me/pwittchen'] 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | or create a failing unit test (recommended) 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Screenshots** 23 | If applicable, add screenshots to help explain your problem. 24 | 25 | **Smartphone (please complete the following information):** 26 | - Device: [e.g. Google Pixel 2] 27 | - OS: [e.g. Android 9] 28 | - Library Version: [e.g. 3.0.0] 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.gradle 2 | /local.properties 3 | /.idea 4 | /.DS_Store 5 | /build 6 | *.iml 7 | .gradletasknamecach 8 | *.exec 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | 3 | android: 4 | components: 5 | - tools 6 | - platform-tools 7 | - build-tools-28 8 | - android-28 9 | - extra-android-support 10 | - extra-android-m2repository 11 | licenses: 12 | - android-sdk-license-5be876d5 13 | - android-sdk-license-c81a61d9 14 | - 'android-sdk-preview-license-.+' 15 | - 'android-sdk-license-.+' 16 | - 'google-gdk-license-.+' 17 | 18 | jdk: oraclejdk8 19 | 20 | before_install: 21 | - yes | sdkmanager "platforms;android-27" 22 | 23 | install: 24 | - true 25 | 26 | after_success: 27 | - bash <(curl -s https://codecov.io/bash) 28 | 29 | script: 30 | - ./gradlew clean build test jacocoTestReport check 31 | 32 | cache: 33 | directories: 34 | - $HOME/.m2 35 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at piotr@wittchen.biz.pl. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing guidelines 2 | ======================= 3 | 4 | Looking for help? 5 | ----------------- 6 | 7 | You have the following options: 8 | - Check out documentation in `README.md` file and read it carefully. It covers almost everything what is important. 9 | - Browse JavaDoc at http://pwittchen.github.io/ReactiveNetwork/ 10 | - [Ask the question on StackOverlow](http://stackoverflow.com/questions/ask?tags=reactivenetwork). 11 | - Provide detailed information about your problem and environment and then ask the question in the new GitHub issue here. 12 | 13 | Found a bug? 14 | ------------ 15 | 16 | Provide detailed steps to reproduce and make sure this bug is not related to your custom project or environment. 17 | Ideally, create Pull Request with failing unit test (or more tests). 18 | 19 | Want a few feature or improvement? 20 | ---------------------------------- 21 | 22 | This is tiny library, so I would avoid overcomplicating it, but if you think a new feature 23 | would be useful and make this project better, then create a new issue. 24 | After that, we can discuss it and work on a Pull Request. 25 | 26 | Want to create a Pull Request? 27 | ------------------------------ 28 | 29 | Before creating new Pull Request, please create a new issue and discuss the problem. 30 | If we agree that PR will be reasonable solution, then fork repository, create a separate branch 31 | and work on a feature or bug-fix on this branch. When you're done, make sure that project passes 32 | static code analysis verification with `./gradlew check` command. Moreover, format your code according to 33 | [SquareAndroid](https://github.com/square/java-code-styles) Java Code Styles. 34 | When you performed more commits than one, squash them into one within a single PR (you can use http://rebaseandsqua.sh/ website). 35 | Make sure that your commit message is descriptive enough. If not, then use `git commit --amend` command and change it. 36 | -------------------------------------------------------------------------------- /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 | 203 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | This PR introduces the following update: 2 | 3 | [briefly describe your update, e.g. closes specified issue, fixes a bug, adds new unit test, etc.] 4 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | Releasing Guidelines 2 | ==================== 3 | 4 | In order to release new version of the library, we need to perform the following operations: 5 | - create new release issue on GitHub 6 | - prepare release notes and put them to the issue 7 | - update javaDocs by running `./update_javadocs.sh` script 8 | - verify, commit and push changes to `gh-pages` branch 9 | - checkout to the appropriate branch (`RxJava1.x` or `RxJava2.x`) 10 | - bump library version (`VERSION_NAME` and `VERSION_CODE`) in `gradle.properties` file 11 | - commit and push the changes 12 | - run command: `./release.sh` 13 | - wait for the Maven Sync (up to 48 hours) 14 | - when sync is done, checkout to the `RxJava2.x` branch 15 | - update `CHANGELOG.md` file with new release version, current date and release notes 16 | - update website with docs by running `./update_docs.sh` script and push the changes 17 | - create new tagged GitHub release with name the same as `VERSION_NAME` from `gradle.properties` and release notes 18 | -------------------------------------------------------------------------------- /app-kotlin/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app-kotlin/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | apply plugin: 'io.gitlab.arturbosch.detekt' 5 | 6 | android { 7 | compileSdkVersion rootProject.ext.compileSdkVersion 8 | buildToolsVersion rootProject.ext.buildToolsVersion 9 | 10 | defaultConfig { 11 | multiDexEnabled true 12 | applicationId "com.github.pwittchen.reactivenetwork.kotlinapp" 13 | minSdkVersion rootProject.ext.minSdkVersionApps 14 | targetSdkVersion rootProject.ext.compileSdkVersion 15 | versionCode 1 16 | versionName "1.0" 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled true 22 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 23 | } 24 | 25 | debug { 26 | minifyEnabled false 27 | } 28 | } 29 | 30 | sourceSets { 31 | main.java.srcDirs += 'src/main/kotlin' 32 | } 33 | 34 | lintOptions { 35 | abortOnError false 36 | } 37 | } 38 | 39 | detekt { 40 | version = rootProject.ext.detektVersion 41 | profile("main") { 42 | input = "$projectDir" 43 | config = "$projectDir/detekt.yml" 44 | filters = ".*test.*,.*/resources/.*,.*/tmp/.*" 45 | } 46 | } 47 | 48 | dependencies { 49 | implementation project(':library') 50 | implementation deps.kotlinstdlib 51 | implementation deps.annotation 52 | implementation deps.appcompat 53 | } 54 | 55 | buildscript { 56 | repositories { 57 | mavenCentral() 58 | google() 59 | maven { 60 | url 'https://plugins.gradle.org/m2/' 61 | } 62 | } 63 | 64 | dependencies { 65 | classpath deps.kotlingradleplugin 66 | classpath deps.detekt 67 | } 68 | } -------------------------------------------------------------------------------- /app-kotlin/detekt.yml: -------------------------------------------------------------------------------- 1 | autoCorrect: true 2 | failFast: false 3 | 4 | test-pattern: # Configure exclusions for test sources 5 | active: true 6 | patterns: # Test file regexes 7 | - '.*/test/.*' 8 | - '.*Test.kt' 9 | - '.*Spec.kt' 10 | exclude-rule-sets: 11 | - 'comments' 12 | exclude-rules: 13 | - 'NamingRules' 14 | - 'WildcardImport' 15 | - 'MagicNumber' 16 | - 'MaxLineLength' 17 | - 'LateinitUsage' 18 | - 'StringLiteralDuplication' 19 | - 'SpreadOperator' 20 | - 'TooManyFunctions' 21 | 22 | build: 23 | warningThreshold: 5 24 | failThreshold: 10 25 | weights: 26 | complexity: 2 27 | formatting: 1 28 | LongParameterList: 1 29 | comments: 1 30 | 31 | processors: 32 | active: true 33 | exclude: 34 | # - 'FunctionCountProcessor' 35 | # - 'PropertyCountProcessor' 36 | # - 'ClassCountProcessor' 37 | # - 'PackageCountProcessor' 38 | # - 'KtFileCountProcessor' 39 | 40 | console-reports: 41 | active: true 42 | exclude: 43 | # - 'ProjectStatisticsReport' 44 | # - 'ComplexityReport' 45 | # - 'NotificationReport' 46 | # - 'FindingsReport' 47 | # - 'BuildFailureReport' 48 | 49 | output-reports: 50 | active: true 51 | exclude: 52 | # - 'PlainOutputReport' 53 | # - 'XmlOutputReport' 54 | 55 | comments: 56 | active: true 57 | CommentOverPrivateFunction: 58 | active: false 59 | CommentOverPrivateProperty: 60 | active: false 61 | UndocumentedPublicClass: 62 | active: false 63 | searchInNestedClass: true 64 | searchInInnerClass: true 65 | searchInInnerObject: true 66 | searchInInnerInterface: true 67 | UndocumentedPublicFunction: 68 | active: false 69 | 70 | complexity: 71 | active: true 72 | LongParameterList: 73 | active: true 74 | threshold: 5 75 | LongMethod: 76 | active: true 77 | threshold: 20 78 | LargeClass: 79 | active: true 80 | threshold: 150 81 | ComplexInterface: 82 | active: false 83 | threshold: 10 84 | includeStaticDeclarations: false 85 | ComplexMethod: 86 | active: true 87 | threshold: 10 88 | StringLiteralDuplication: 89 | active: false 90 | threshold: 2 91 | ignoreAnnotation: true 92 | excludeStringsWithLessThan5Characters: true 93 | ignoreStringsRegex: '$^' 94 | MethodOverloading: 95 | active: false 96 | threshold: 5 97 | NestedBlockDepth: 98 | active: true 99 | threshold: 3 100 | TooManyFunctions: 101 | active: true 102 | thresholdInFiles: 10 103 | thresholdInClasses: 10 104 | thresholdInInterfaces: 10 105 | thresholdInObjects: 10 106 | thresholdInEnums: 10 107 | ComplexCondition: 108 | active: true 109 | threshold: 3 110 | LabeledExpression: 111 | active: false 112 | 113 | empty-blocks: 114 | active: true 115 | EmptyCatchBlock: 116 | active: true 117 | EmptyClassBlock: 118 | active: true 119 | EmptyDefaultConstructor: 120 | active: true 121 | EmptyDoWhileBlock: 122 | active: true 123 | EmptyElseBlock: 124 | active: true 125 | EmptyFinallyBlock: 126 | active: true 127 | EmptyForBlock: 128 | active: true 129 | EmptyFunctionBlock: 130 | active: true 131 | EmptyIfBlock: 132 | active: true 133 | EmptyInitBlock: 134 | active: true 135 | EmptyKtFile: 136 | active: true 137 | EmptySecondaryConstructor: 138 | active: true 139 | EmptyWhenBlock: 140 | active: true 141 | EmptyWhileBlock: 142 | active: true 143 | 144 | exceptions: 145 | active: true 146 | TooGenericExceptionCaught: 147 | active: true 148 | exceptions: 149 | - ArrayIndexOutOfBoundsException 150 | - Error 151 | - Exception 152 | - IllegalMonitorStateException 153 | - NullPointerException 154 | - IndexOutOfBoundsException 155 | - RuntimeException 156 | - Throwable 157 | ExceptionRaisedInUnexpectedLocation: 158 | active: false 159 | methodNames: 'toString,hashCode,equals,finalize' 160 | TooGenericExceptionThrown: 161 | active: true 162 | exceptions: 163 | - Error 164 | - Exception 165 | - NullPointerException 166 | - Throwable 167 | - RuntimeException 168 | NotImplementedDeclaration: 169 | active: false 170 | PrintStackTrace: 171 | active: false 172 | InstanceOfCheckForException: 173 | active: false 174 | ThrowingExceptionsWithoutMessageOrCause: 175 | active: false 176 | exceptions: 'IllegalArgumentException,IllegalStateException,IOException' 177 | ReturnFromFinally: 178 | active: false 179 | ThrowingExceptionFromFinally: 180 | active: false 181 | ThrowingExceptionInMain: 182 | active: false 183 | RethrowCaughtException: 184 | active: false 185 | ThrowingNewInstanceOfSameException: 186 | active: false 187 | SwallowedException: 188 | active: false 189 | 190 | performance: 191 | active: true 192 | ForEachOnRange: 193 | active: true 194 | SpreadOperator: 195 | active: true 196 | UnnecessaryTemporaryInstantiation: 197 | active: true 198 | 199 | potential-bugs: 200 | active: true 201 | DuplicateCaseInWhenExpression: 202 | active: true 203 | EqualsAlwaysReturnsTrueOrFalse: 204 | active: false 205 | EqualsWithHashCodeExist: 206 | active: true 207 | IteratorNotThrowingNoSuchElementException: 208 | active: false 209 | IteratorHasNextCallsNextMethod: 210 | active: false 211 | UselessPostfixExpression: 212 | active: false 213 | InvalidLoopCondition: 214 | active: false 215 | WrongEqualsTypeParameter: 216 | active: false 217 | ExplicitGarbageCollectionCall: 218 | active: true 219 | LateinitUsage: 220 | active: false 221 | excludeAnnotatedProperties: "" 222 | ignoreOnClassesPattern: "" 223 | UnconditionalJumpStatementInLoop: 224 | active: false 225 | UnreachableCode: 226 | active: true 227 | UnsafeCallOnNullableType: 228 | active: false 229 | UnsafeCast: 230 | active: false 231 | 232 | style: 233 | active: true 234 | CollapsibleIfStatements: 235 | active: false 236 | ReturnCount: 237 | active: true 238 | max: 2 239 | excludedFunctions: "equals" 240 | ThrowsCount: 241 | active: true 242 | max: 2 243 | NewLineAtEndOfFile: 244 | active: true 245 | WildcardImport: 246 | active: true 247 | excludeImports: 'java.util.*,kotlinx.android.synthetic.*' 248 | MaxLineLength: 249 | active: true 250 | maxLineLength: 120 251 | excludePackageStatements: false 252 | excludeImportStatements: false 253 | EqualsNullCall: 254 | active: false 255 | ForbiddenComment: 256 | active: true 257 | values: 'TODO:,FIXME:,STOPSHIP:' 258 | ForbiddenImport: 259 | active: false 260 | imports: '' 261 | FunctionOnlyReturningConstant: 262 | active: false 263 | ignoreOverridableFunction: true 264 | excludedFunctions: 'describeContents' 265 | SpacingBetweenPackageAndImports: 266 | active: false 267 | LoopWithTooManyJumpStatements: 268 | active: false 269 | maxJumpCount: 1 270 | MemberNameEqualsClassName: 271 | active: false 272 | ignoreOverriddenFunction: true 273 | VariableNaming: 274 | active: true 275 | variablePattern: '[a-z][A-Za-z0-9]*' 276 | privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' 277 | VariableMinLength: 278 | active: false 279 | minimumVariableNameLength: 1 280 | VariableMaxLength: 281 | active: false 282 | maximumVariableNameLength: 64 283 | TopLevelPropertyNaming: 284 | active: true 285 | constantPattern: '[A-Z][_A-Z0-9]*' 286 | propertyPattern: '[a-z][A-Za-z\d]*' 287 | privatePropertyPattern: '(_)?[a-z][A-Za-z0-9]*' 288 | ObjectPropertyNaming: 289 | active: true 290 | propertyPattern: '[A-Za-z][_A-Za-z0-9]*' 291 | PackageNaming: 292 | active: true 293 | packagePattern: '^[a-z]+(\.[a-z][a-z0-9]*)*$' 294 | ClassNaming: 295 | active: true 296 | classPattern: '[A-Z$][a-zA-Z0-9$]*' 297 | EnumNaming: 298 | active: true 299 | enumEntryPattern: '^[A-Z$][a-zA-Z_$]*$' 300 | FunctionNaming: 301 | active: true 302 | functionPattern: '^([a-z$][a-zA-Z$0-9]*)|(`.*`)$' 303 | FunctionMaxLength: 304 | active: false 305 | maximumFunctionNameLength: 30 306 | FunctionMinLength: 307 | active: false 308 | minimumFunctionNameLength: 3 309 | ForbiddenClassName: 310 | active: false 311 | forbiddenName: '' 312 | SafeCast: 313 | active: true 314 | UnnecessaryAbstractClass: 315 | active: false 316 | UnnecessaryParentheses: 317 | active: false 318 | UnnecessaryInheritance: 319 | active: false 320 | UtilityClassWithPublicConstructor: 321 | active: false 322 | OptionalAbstractKeyword: 323 | active: true 324 | OptionalWhenBraces: 325 | active: false 326 | OptionalReturnKeyword: 327 | active: false 328 | OptionalUnit: 329 | active: false 330 | ProtectedMemberInFinalClass: 331 | active: false 332 | SerialVersionUIDInSerializableClass: 333 | active: false 334 | MagicNumber: 335 | active: true 336 | ignoreNumbers: '-1,0,1,2' 337 | ignoreHashCodeFunction: false 338 | ignorePropertyDeclaration: false 339 | ignoreConstantDeclaration: true 340 | ignoreCompanionObjectPropertyDeclaration: true 341 | ignoreAnnotation: false 342 | ignoreNamedArgument: true 343 | ignoreEnums: false 344 | ModifierOrder: 345 | active: true 346 | DataClassContainsFunctions: 347 | active: false 348 | conversionFunctionPrefix: 'to' 349 | UseDataClass: 350 | active: false 351 | UnusedImports: 352 | active: false 353 | ExpressionBodySyntax: 354 | active: false 355 | NestedClassesVisibility: 356 | active: false 357 | RedundantVisibilityModifierRule: 358 | active: false 359 | -------------------------------------------------------------------------------- /app-kotlin/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 /home/piotr/Development/android/android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -dontwarn com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork 19 | -dontwarn io.reactivex.functions.Function 20 | -dontwarn rx.internal.util.** 21 | -dontwarn sun.misc.Unsafe -------------------------------------------------------------------------------- /app-kotlin/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app-kotlin/src/main/kotlin/com/github/pwittchen/reactivenetwork/kotlinapp/MainActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Piotr Wittchen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pwittchen.reactivenetwork.kotlinapp 17 | 18 | import android.app.Activity 19 | import android.os.Bundle 20 | import android.util.Log 21 | import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork 22 | import io.reactivex.android.schedulers.AndroidSchedulers 23 | import io.reactivex.disposables.Disposable 24 | import io.reactivex.schedulers.Schedulers 25 | import kotlinx.android.synthetic.main.activity_main.connectivity_status 26 | import kotlinx.android.synthetic.main.activity_main.internet_status 27 | 28 | class MainActivity : Activity() { 29 | private var connectivityDisposable: Disposable? = null 30 | private var internetDisposable: Disposable? = null 31 | 32 | companion object { 33 | private val TAG = "ReactiveNetwork" 34 | } 35 | 36 | override fun onCreate(savedInstanceState: Bundle?) { 37 | super.onCreate(savedInstanceState) 38 | setContentView(R.layout.activity_main) 39 | } 40 | 41 | override fun onResume() { 42 | super.onResume() 43 | 44 | connectivityDisposable = ReactiveNetwork.observeNetworkConnectivity(applicationContext) 45 | .subscribeOn(Schedulers.io()) 46 | .observeOn(AndroidSchedulers.mainThread()) 47 | .subscribe { connectivity -> 48 | Log.d(TAG, connectivity.toString()) 49 | val state = connectivity.state() 50 | val name = connectivity.typeName() 51 | connectivity_status.text = String.format("state: %s, typeName: %s", state, name) 52 | } 53 | 54 | internetDisposable = ReactiveNetwork.observeInternetConnectivity() 55 | .subscribeOn(Schedulers.io()) 56 | .observeOn(AndroidSchedulers.mainThread()) 57 | .subscribe { isConnectedToInternet -> 58 | internet_status.text = isConnectedToInternet.toString() 59 | } 60 | } 61 | 62 | override fun onPause() { 63 | super.onPause() 64 | safelyDispose(connectivityDisposable) 65 | safelyDispose(internetDisposable) 66 | } 67 | 68 | private fun safelyDispose(disposable: Disposable?) { 69 | if (disposable != null && !disposable.isDisposed) { 70 | disposable.dispose() 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /app-kotlin/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 22 | 23 | 33 | 34 | 45 | 46 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /app-kotlin/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwittchen/ReactiveNetwork/ddfde340e4c1f3acb6bfe10b8c3804dd63ae121b/app-kotlin/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app-kotlin/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwittchen/ReactiveNetwork/ddfde340e4c1f3acb6bfe10b8c3804dd63ae121b/app-kotlin/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app-kotlin/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwittchen/ReactiveNetwork/ddfde340e4c1f3acb6bfe10b8c3804dd63ae121b/app-kotlin/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app-kotlin/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwittchen/ReactiveNetwork/ddfde340e4c1f3acb6bfe10b8c3804dd63ae121b/app-kotlin/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app-kotlin/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwittchen/ReactiveNetwork/ddfde340e4c1f3acb6bfe10b8c3804dd63ae121b/app-kotlin/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app-kotlin/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app-kotlin/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app-kotlin/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 16dp 3 | 16dp 4 | 10dp 5 | 6 | -------------------------------------------------------------------------------- /app-kotlin/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ReactiveNetwork 3 | Waiting for connectivity status… 4 | Waiting for Internet status… 5 | Connectivity status: 6 | Internet status: 7 | 8 | -------------------------------------------------------------------------------- /app-kotlin/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion rootProject.ext.compileSdkVersion 5 | buildToolsVersion rootProject.ext.buildToolsVersion 6 | 7 | defaultConfig { 8 | multiDexEnabled true 9 | applicationId "com.github.pwittchen.reactivenetwork.app" 10 | minSdkVersion rootProject.ext.minSdkVersionApps 11 | targetSdkVersion rootProject.ext.compileSdkVersion 12 | versionCode 1 13 | versionName "1.0" 14 | } 15 | 16 | buildTypes { 17 | release { 18 | minifyEnabled true 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | 22 | debug { 23 | minifyEnabled false 24 | } 25 | } 26 | 27 | lintOptions { 28 | abortOnError false 29 | } 30 | 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | } 36 | 37 | dependencies { 38 | implementation project(':library') 39 | implementation deps.appcompat 40 | } 41 | 42 | buildscript { 43 | repositories { 44 | mavenCentral() 45 | google() 46 | maven { 47 | url 'https://plugins.gradle.org/m2/' 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /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 /home/piotr/Android/Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -dontwarn com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork 19 | -dontwarn io.reactivex.functions.Function 20 | -dontwarn rx.internal.util.** 21 | -dontwarn sun.misc.Unsafe 22 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/pwittchen/reactivenetwork/app/MainActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Piotr Wittchen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pwittchen.reactivenetwork.app; 17 | 18 | import android.app.Activity; 19 | import android.net.NetworkInfo; 20 | import android.os.Bundle; 21 | import android.util.Log; 22 | import android.widget.TextView; 23 | import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork; 24 | import io.reactivex.android.schedulers.AndroidSchedulers; 25 | import io.reactivex.disposables.Disposable; 26 | import io.reactivex.schedulers.Schedulers; 27 | 28 | public class MainActivity extends Activity { 29 | private static final String TAG = "ReactiveNetwork"; 30 | private TextView tvConnectivityStatus; 31 | private TextView tvInternetStatus; 32 | private Disposable networkDisposable; 33 | private Disposable internetDisposable; 34 | 35 | @Override protected void onCreate(Bundle savedInstanceState) { 36 | super.onCreate(savedInstanceState); 37 | setContentView(R.layout.activity_main); 38 | tvConnectivityStatus = (TextView) findViewById(R.id.connectivity_status); 39 | tvInternetStatus = (TextView) findViewById(R.id.internet_status); 40 | } 41 | 42 | @Override protected void onResume() { 43 | super.onResume(); 44 | 45 | networkDisposable = ReactiveNetwork.observeNetworkConnectivity(getApplicationContext()) 46 | .subscribeOn(Schedulers.io()) 47 | .observeOn(AndroidSchedulers.mainThread()) 48 | .subscribe(connectivity -> { 49 | Log.d(TAG, connectivity.toString()); 50 | final NetworkInfo.State state = connectivity.state(); 51 | final String name = connectivity.typeName(); 52 | tvConnectivityStatus.setText(String.format("state: %s, typeName: %s", state, name)); 53 | }); 54 | 55 | internetDisposable = ReactiveNetwork.observeInternetConnectivity() 56 | .subscribeOn(Schedulers.io()) 57 | .observeOn(AndroidSchedulers.mainThread()) 58 | .subscribe(isConnected -> tvInternetStatus.setText(isConnected.toString())); 59 | } 60 | 61 | @Override protected void onPause() { 62 | super.onPause(); 63 | safelyDispose(networkDisposable, internetDisposable); 64 | } 65 | 66 | private void safelyDispose(Disposable... disposables) { 67 | for (Disposable subscription : disposables) { 68 | if (subscription != null && !subscription.isDisposed()) { 69 | subscription.dispose(); 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 22 | 23 | 33 | 34 | 45 | 46 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwittchen/ReactiveNetwork/ddfde340e4c1f3acb6bfe10b8c3804dd63ae121b/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwittchen/ReactiveNetwork/ddfde340e4c1f3acb6bfe10b8c3804dd63ae121b/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwittchen/ReactiveNetwork/ddfde340e4c1f3acb6bfe10b8c3804dd63ae121b/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwittchen/ReactiveNetwork/ddfde340e4c1f3acb6bfe10b8c3804dd63ae121b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 16dp 3 | 16dp 4 | 10dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ReactiveNetwork 3 | Waiting for connectivity status… 4 | Waiting for Internet status… 5 | Connectivity status: 6 | Internet status: 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | apply plugin: 'io.codearte.nexus-staging' 4 | 5 | ext { 6 | minSdkVersion = 14 7 | minSdkVersionApps = 23 8 | compileSdkVersion = 29 9 | buildToolsVersion = '28.0.3' 10 | gradleVersion = '4.6' 11 | kotlinVersion = '1.4.0' 12 | detektVersion = '1.0.0.RC6-1' 13 | } 14 | 15 | ext.deps = [rxjava2 : 'io.reactivex.rxjava2:rxjava:2.2.21', 16 | rxandroid2 : 'io.reactivex.rxjava2:rxandroid:2.1.1', 17 | annotation : 'androidx.annotation:annotation:1.1.0', 18 | appcompat : 'androidx.appcompat:appcompat:1.2.0', 19 | junit : 'junit:junit:4.13', 20 | truth : 'com.google.truth:truth:1.0.1', 21 | robolectric : 'org.robolectric:robolectric:4.9', 22 | mockitocore : 'org.mockito:mockito-core:3.5.2', 23 | nullaway : 'com.uber.nullaway:nullaway:0.8.0', 24 | errorprone : 'com.google.errorprone:error_prone_core:2.3.4', 25 | nopen : 'com.jakewharton.nopen:nopen-annotations:1.0.1', 26 | nopenchecker : 'com.jakewharton.nopen:nopen-checker:1.0.1', 27 | kotlinstdlib : "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion", 28 | kotlingradleplugin: "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion", 29 | detekt : "gradle.plugin.io.gitlab.arturbosch.detekt:detekt-gradle-plugin:$detektVersion"] 30 | 31 | buildscript { 32 | repositories { 33 | google() 34 | mavenCentral() 35 | maven { 36 | url 'https://plugins.gradle.org/m2/' 37 | } 38 | } 39 | dependencies { 40 | classpath 'com.android.tools.build:gradle:3.6.4' 41 | classpath('com.hiya:jacoco-android:0.2') { 42 | exclude group: 'org.codehaus.groovy', module: 'groovy-all' 43 | } 44 | 45 | classpath 'net.ltgt.gradle:gradle-errorprone-plugin:0.0.13' 46 | classpath 'net.ltgt.gradle:gradle-apt-plugin:0.21' 47 | classpath "io.codearte.gradle.nexus:gradle-nexus-staging-plugin:0.22.0" 48 | // NOTE: Do not place your application dependencies here; they belong 49 | // in the individual module build.gradle files 50 | } 51 | } 52 | 53 | allprojects { 54 | repositories { 55 | google() 56 | mavenCentral() 57 | maven { 58 | url 'https://plugins.gradle.org/m2/' 59 | } 60 | } 61 | } 62 | 63 | def getRepositoryUsername() { 64 | return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : "" 65 | } 66 | 67 | def getRepositoryPassword() { 68 | return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : "" 69 | } 70 | 71 | nexusStaging { 72 | packageGroup = GROUP //optional if packageGroup == project.getGroup() 73 | stagingProfileId = "9add401d06ecc9" //when not defined will be got from server using "packageGroup" 74 | username = getRepositoryUsername() 75 | password = getRepositoryPassword() 76 | } -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | target: 80% 6 | threshold: 10% 7 | 8 | patch: 9 | default: 10 | target: 80% 11 | threshold: 10% 12 | 13 | changes: no 14 | -------------------------------------------------------------------------------- /config/quality.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'checkstyle' 2 | apply plugin: 'pmd' 3 | 4 | // Add checkstyle, pmd and lint to the check task. 5 | check.dependsOn 'checkstyle', 'pmd', 'lint' 6 | 7 | checkstyle { 8 | toolVersion = "6.0" 9 | } 10 | 11 | task checkstyle(type: Checkstyle) { 12 | configFile file("${project.rootDir}/config/quality/checkstyle/checkstyle.xml") 13 | configProperties.checkstyleSuppressionsPath = file("${project.rootDir}/config/quality/checkstyle/suppressions.xml").absolutePath 14 | source 'src' 15 | include '**/*.java' 16 | exclude '**/gen/**' 17 | classpath = files() 18 | } 19 | 20 | task pmd(type: Pmd) { 21 | ignoreFailures = false 22 | ruleSetFiles = files("${project.rootDir}/config/quality/pmd/pmd-ruleset.xml") 23 | ruleSets = [] 24 | 25 | source 'src' 26 | include '**/*.java' 27 | exclude '**/gen/**' 28 | 29 | reports { 30 | xml.enabled = false 31 | html.enabled = true 32 | xml { 33 | destination file("$project.buildDir/reports/pmd/pmd.xml") 34 | } 35 | html { 36 | destination file("$project.buildDir/reports/pmd/pmd.html") 37 | } 38 | } 39 | } 40 | 41 | android { 42 | lintOptions { 43 | abortOnError false 44 | xmlReport false 45 | htmlReport true 46 | lintConfig file("${project.rootDir}/config/quality/lint/lint.xml") 47 | htmlOutput file("$project.buildDir/reports/lint/lint-result.html") 48 | xmlOutput file("$project.buildDir/reports/lint/lint-result.xml") 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /config/quality/checkstyle/checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 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 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /config/quality/checkstyle/suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /config/quality/findbugs/findbugs-filter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /config/quality/lint/lint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /config/quality/pmd/pmd-ruleset.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | Custom ruleset for 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 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | VERSION_NAME=3.0.8 2 | VERSION_CODE=39 3 | GROUP=com.github.pwittchen 4 | 5 | POM_DESCRIPTION=Android library listening network connection state and Internet connectivity with RxJava Observables 6 | POM_URL=https://github.com/pwittchen/ReactiveNetwork 7 | POM_SCM_URL=https://github.com/pwittchen/ReactiveNetwork 8 | POM_SCM_CONNECTION=scm:git@github.com:pwittchen/ReactiveNetwork.git 9 | POM_SCM_DEV_CONNECTION=scm:git@github.com:pwittchen/ReactiveNetwork.git 10 | POM_LICENCE_NAME=The Apache Software License, Version 2.0 11 | POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt 12 | POM_LICENCE_DIST=repo 13 | POM_DEVELOPER_ID=pwittchen 14 | POM_DEVELOPER_NAME=Piotr Wittchen 15 | 16 | org.gradle.daemon=true 17 | org.gradle.jvmargs=-XX:MaxPermSize=1024m -XX:+CMSClassUnloadingEnabled -XX:+HeapDumpOnOutOfMemoryError -Xmx4096m -XX:MaxHeapSize=4048 -Xms1024m 18 | 19 | android.useAndroidX=true 20 | android.enableJetifier=true 21 | android.jetifier.blacklist=bcprov-jdk15on 22 | android.enableR8.fullMode=false 23 | android.enableUnitTestBinaryResources=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwittchen/ReactiveNetwork/ddfde340e4c1f3acb6bfe10b8c3804dd63ae121b/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin or MSYS, switch paths to Windows format before running java 129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=`expr $i + 1` 158 | done 159 | case $i in 160 | 0) set -- ;; 161 | 1) set -- "$args0" ;; 162 | 2) set -- "$args0" "$args1" ;; 163 | 3) set -- "$args0" "$args1" "$args2" ;; 164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=`save "$@"` 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | exec "$JAVACMD" "$@" 184 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto init 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto init 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :init 68 | @rem Get command-line arguments, handling Windows variants 69 | 70 | if not "%OS%" == "Windows_NT" goto win9xME_args 71 | 72 | :win9xME_args 73 | @rem Slurp the command line arguments. 74 | set CMD_LINE_ARGS= 75 | set _SKIP=2 76 | 77 | :win9xME_args_slurp 78 | if "x%~1" == "x" goto execute 79 | 80 | set CMD_LINE_ARGS=%* 81 | 82 | :execute 83 | @rem Setup the command line 84 | 85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 86 | 87 | @rem Execute Gradle 88 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 89 | 90 | :end 91 | @rem End local scope for the variables with windows NT shell 92 | if "%ERRORLEVEL%"=="0" goto mainEnd 93 | 94 | :fail 95 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 96 | rem the _cmd.exe /c_ return code! 97 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 98 | exit /b 1 99 | 100 | :mainEnd 101 | if "%OS%"=="Windows_NT" endlocal 102 | 103 | :omega 104 | -------------------------------------------------------------------------------- /jetbrains_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwittchen/ReactiveNetwork/ddfde340e4c1f3acb6bfe10b8c3804dd63ae121b/jetbrains_logo.png -------------------------------------------------------------------------------- /library/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'jacoco' 3 | apply plugin: 'com.hiya.jacoco-android' 4 | apply plugin: 'net.ltgt.errorprone' 5 | apply plugin: 'net.ltgt.apt' 6 | apply from: '../config/quality.gradle' 7 | apply from: '../maven_push.gradle' 8 | 9 | jacoco { 10 | toolVersion = "0.8.4" 11 | } 12 | 13 | android { 14 | compileSdkVersion rootProject.ext.compileSdkVersion 15 | buildToolsVersion rootProject.ext.buildToolsVersion 16 | 17 | defaultConfig { 18 | multiDexEnabled true 19 | minSdkVersion rootProject.ext.minSdkVersion 20 | targetSdkVersion rootProject.ext.compileSdkVersion 21 | versionCode 1 22 | versionName "1.0" 23 | } 24 | 25 | buildTypes { 26 | release { 27 | minifyEnabled false 28 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 29 | } 30 | 31 | debug { 32 | minifyEnabled false 33 | testCoverageEnabled true 34 | } 35 | } 36 | 37 | compileOptions { 38 | sourceCompatibility JavaVersion.VERSION_1_8 39 | targetCompatibility JavaVersion.VERSION_1_8 40 | } 41 | 42 | packagingOptions { 43 | exclude 'LICENSE.txt' 44 | exclude 'META-INF/LICENSE.txt' 45 | } 46 | 47 | testOptions { 48 | unitTests.all { 49 | jacoco { 50 | includeNoLocationClasses = true 51 | } 52 | systemProperty 'robolectric.dependency.repo.url', 'https://repo1.maven.org/maven2' 53 | } 54 | } 55 | 56 | testOptions.unitTests.includeAndroidResources = true 57 | 58 | jacocoAndroidUnitTestReport { 59 | csv.enabled false 60 | html.enabled true 61 | xml.enabled true 62 | } 63 | 64 | dependencies { 65 | api deps.rxjava2 66 | api deps.rxandroid2 67 | implementation deps.annotation 68 | 69 | testImplementation project(path: ':library') 70 | testImplementation deps.junit 71 | testImplementation deps.truth 72 | testImplementation deps.robolectric 73 | testImplementation deps.mockitocore 74 | 75 | annotationProcessor deps.nullaway 76 | 77 | compileOnly deps.nopen 78 | 79 | errorprone deps.errorprone 80 | errorprone deps.nopenchecker 81 | } 82 | 83 | compileOptions { 84 | sourceCompatibility JavaVersion.VERSION_1_8 85 | targetCompatibility JavaVersion.VERSION_1_8 86 | } 87 | 88 | tasks.withType(JavaCompile) { 89 | if (!name.toLowerCase().contains("test")) { 90 | options.compilerArgs += 91 | ["-Xep:NullAway:ERROR", "-XepOpt:NullAway:AnnotatedPackages=com.github.pwittchen.reactivenetwork"] 92 | } 93 | } 94 | 95 | tasks.withType(Test) { 96 | jacoco.includeNoLocationClasses = true 97 | jacoco.excludes = ['jdk.internal.*'] 98 | } 99 | } -------------------------------------------------------------------------------- /library/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=reactivenetwork 2 | POM_ARTIFACT_ID=reactivenetwork-rx2 3 | POM_PACKAGING=aar -------------------------------------------------------------------------------- /library/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 /home/piotr/Android/Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /library/src/main/java/com/github/pwittchen/reactivenetwork/library/rx2/Connectivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Piotr Wittchen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pwittchen.reactivenetwork.library.rx2; 17 | 18 | import android.content.Context; 19 | import android.net.ConnectivityManager; 20 | import android.net.NetworkInfo; 21 | import android.os.Build; 22 | import androidx.annotation.NonNull; 23 | import androidx.annotation.RequiresApi; 24 | 25 | /** 26 | * Connectivity class represents current connectivity status. It wraps NetworkInfo object. 27 | */ 28 | @RequiresApi(api = Build.VERSION_CODES.CUPCAKE) 29 | public final class Connectivity { 30 | static final int UNKNOWN_TYPE = -1; 31 | static final int UNKNOWN_SUB_TYPE = -1; 32 | private NetworkInfo.State state; // NOPMD 33 | private NetworkInfo.DetailedState detailedState; // NOPMD 34 | private int type; // NOPMD 35 | private int subType; // NOPMD 36 | private boolean available; // NOPMD 37 | private boolean failover; // NOPMD 38 | private boolean roaming; // NOPMD 39 | private String typeName; // NOPMD 40 | private String subTypeName; // NOPMD 41 | private String reason; // NOPMD 42 | private String extraInfo; // NOPMD 43 | 44 | public static Connectivity create() { 45 | return builder().build(); 46 | } 47 | 48 | public static Connectivity create(@NonNull Context context) { 49 | Preconditions.checkNotNull(context, "context == null"); 50 | return create(context, getConnectivityManager(context)); 51 | } 52 | 53 | private static ConnectivityManager getConnectivityManager(Context context) { 54 | final String service = Context.CONNECTIVITY_SERVICE; 55 | return (ConnectivityManager) context.getSystemService(service); 56 | } 57 | 58 | protected static Connectivity create(@NonNull Context context, ConnectivityManager manager) { 59 | Preconditions.checkNotNull(context, "context == null"); 60 | 61 | if (manager == null) { 62 | return create(); 63 | } 64 | 65 | final NetworkInfo networkInfo = manager.getActiveNetworkInfo(); 66 | return (networkInfo == null) ? create() : create(networkInfo); 67 | } 68 | 69 | private static Connectivity create(NetworkInfo networkInfo) { 70 | return new Builder() 71 | .state(networkInfo.getState()) 72 | .detailedState(networkInfo.getDetailedState()) 73 | .type(networkInfo.getType()) 74 | .subType(networkInfo.getSubtype()) 75 | .available(networkInfo.isAvailable()) 76 | .failover(networkInfo.isFailover()) 77 | .roaming(networkInfo.isRoaming()) 78 | .typeName(networkInfo.getTypeName()) 79 | .subTypeName(networkInfo.getSubtypeName()) 80 | .reason(networkInfo.getReason()) 81 | .extraInfo(networkInfo.getExtraInfo()) 82 | .build(); 83 | } 84 | 85 | private Connectivity(Builder builder) { 86 | state = builder.state; 87 | detailedState = builder.detailedState; 88 | type = builder.type; 89 | subType = builder.subType; 90 | available = builder.available; 91 | failover = builder.failover; 92 | roaming = builder.roaming; 93 | typeName = builder.typeName; 94 | subTypeName = builder.subTypeName; 95 | reason = builder.reason; 96 | extraInfo = builder.extraInfo; 97 | } 98 | 99 | private Connectivity() { 100 | this(builder()); 101 | } 102 | 103 | private static Builder builder() { 104 | return new Connectivity.Builder(); 105 | } 106 | 107 | public NetworkInfo.State state() { 108 | return state; 109 | } 110 | 111 | public static Builder state(NetworkInfo.State state) { 112 | return builder().state(state); 113 | } 114 | 115 | public NetworkInfo.DetailedState detailedState() { 116 | return detailedState; 117 | } 118 | 119 | public static Builder state(NetworkInfo.DetailedState detailedState) { 120 | return builder().detailedState(detailedState); 121 | } 122 | 123 | public int type() { 124 | return type; 125 | } 126 | 127 | public static Builder type(int type) { 128 | return builder().type(type); 129 | } 130 | 131 | public int subType() { 132 | return subType; 133 | } 134 | 135 | public static Builder subType(int subType) { 136 | return builder().subType(subType); 137 | } 138 | 139 | public boolean available() { 140 | return available; 141 | } 142 | 143 | public static Builder available(boolean available) { 144 | return builder().available(available); 145 | } 146 | 147 | public boolean failover() { 148 | return failover; 149 | } 150 | 151 | public static Builder failover(boolean failover) { 152 | return builder().failover(failover); 153 | } 154 | 155 | public boolean roaming() { 156 | return roaming; 157 | } 158 | 159 | public static Builder roaming(boolean roaming) { 160 | return builder().roaming(roaming); 161 | } 162 | 163 | public String typeName() { 164 | return typeName; 165 | } 166 | 167 | public static Builder typeName(String typeName) { 168 | return builder().typeName(typeName); 169 | } 170 | 171 | public String subTypeName() { 172 | return subTypeName; 173 | } 174 | 175 | public static Builder subTypeName(String subTypeName) { 176 | return builder().subTypeName(subTypeName); 177 | } 178 | 179 | public String reason() { 180 | return reason; 181 | } 182 | 183 | public static Builder reason(String reason) { 184 | return builder().reason(reason); 185 | } 186 | 187 | public String extraInfo() { 188 | return extraInfo; 189 | } 190 | 191 | public static Builder extraInfo(String extraInfo) { 192 | return builder().extraInfo(extraInfo); 193 | } 194 | 195 | @Override public boolean equals(Object o) { 196 | if (this == o) { 197 | return true; 198 | } 199 | if (o == null || getClass() != o.getClass()) { 200 | return false; 201 | } 202 | 203 | Connectivity that = (Connectivity) o; 204 | 205 | if (type != that.type) { 206 | return false; 207 | } 208 | if (subType != that.subType) { 209 | return false; 210 | } 211 | if (available != that.available) { 212 | return false; 213 | } 214 | if (failover != that.failover) { 215 | return false; 216 | } 217 | if (roaming != that.roaming) { 218 | return false; 219 | } 220 | if (state != that.state) { 221 | return false; 222 | } 223 | if (detailedState != that.detailedState) { 224 | return false; 225 | } 226 | if (!typeName.equals(that.typeName)) { 227 | return false; 228 | } 229 | if (subTypeName != null ? !subTypeName.equals(that.subTypeName) : that.subTypeName != null) { 230 | return false; 231 | } 232 | if (reason != null ? !reason.equals(that.reason) : that.reason != null) { 233 | return false; 234 | } 235 | 236 | return extraInfo != null ? extraInfo.equals(that.extraInfo) : that.extraInfo == null; 237 | } 238 | 239 | @Override public int hashCode() { 240 | int result = state.hashCode(); 241 | result = 31 * result + (detailedState != null ? detailedState.hashCode() : 0); 242 | result = 31 * result + type; 243 | result = 31 * result + subType; 244 | result = 31 * result + (available ? 1 : 0); 245 | result = 31 * result + (failover ? 1 : 0); 246 | result = 31 * result + (roaming ? 1 : 0); 247 | result = 31 * result + typeName.hashCode(); 248 | result = 31 * result + (subTypeName != null ? subTypeName.hashCode() : 0); 249 | result = 31 * result + (reason != null ? reason.hashCode() : 0); 250 | result = 31 * result + (extraInfo != null ? extraInfo.hashCode() : 0); 251 | return result; 252 | } 253 | 254 | @Override public String toString() { 255 | return "Connectivity{" 256 | + "state=" 257 | + state 258 | + ", detailedState=" 259 | + detailedState 260 | + ", type=" 261 | + type 262 | + ", subType=" 263 | + subType 264 | + ", available=" 265 | + available 266 | + ", failover=" 267 | + failover 268 | + ", roaming=" 269 | + roaming 270 | + ", typeName='" 271 | + typeName 272 | + '\'' 273 | + ", subTypeName='" 274 | + subTypeName 275 | + '\'' 276 | + ", reason='" 277 | + reason 278 | + '\'' 279 | + ", extraInfo='" 280 | + extraInfo 281 | + '\'' 282 | + '}'; 283 | } 284 | 285 | public final static class Builder { 286 | 287 | // disabling PMD for builder class attributes 288 | // because we want to have the same method names as names of the attributes for builder 289 | 290 | private NetworkInfo.State state = NetworkInfo.State.DISCONNECTED; // NOPMD 291 | private NetworkInfo.DetailedState detailedState = NetworkInfo.DetailedState.IDLE; // NOPMD 292 | private int type = UNKNOWN_TYPE; // NOPMD 293 | private int subType = UNKNOWN_SUB_TYPE; // NOPMD 294 | private boolean available = false; // NOPMD 295 | private boolean failover = false; // NOPMD 296 | private boolean roaming = false; // NOPMD 297 | private String typeName = "NONE"; // NOPMD 298 | private String subTypeName = "NONE"; // NOPMD 299 | private String reason = ""; // NOPMD 300 | private String extraInfo = ""; // NOPMD 301 | 302 | public Builder state(NetworkInfo.State state) { 303 | this.state = state; 304 | return this; 305 | } 306 | 307 | public Builder detailedState(NetworkInfo.DetailedState detailedState) { 308 | this.detailedState = detailedState; 309 | return this; 310 | } 311 | 312 | public Builder type(int type) { 313 | this.type = type; 314 | return this; 315 | } 316 | 317 | public Builder subType(int subType) { 318 | this.subType = subType; 319 | return this; 320 | } 321 | 322 | public Builder available(boolean available) { 323 | this.available = available; 324 | return this; 325 | } 326 | 327 | public Builder failover(boolean failover) { 328 | this.failover = failover; 329 | return this; 330 | } 331 | 332 | public Builder roaming(boolean roaming) { 333 | this.roaming = roaming; 334 | return this; 335 | } 336 | 337 | public Builder typeName(String name) { 338 | this.typeName = name; 339 | return this; 340 | } 341 | 342 | public Builder subTypeName(String subTypeName) { 343 | this.subTypeName = subTypeName; 344 | return this; 345 | } 346 | 347 | public Builder reason(String reason) { 348 | this.reason = reason; 349 | return this; 350 | } 351 | 352 | public Builder extraInfo(String extraInfo) { 353 | this.extraInfo = extraInfo; 354 | return this; 355 | } 356 | 357 | public Connectivity build() { 358 | return new Connectivity(this); 359 | } 360 | } 361 | } 362 | -------------------------------------------------------------------------------- /library/src/main/java/com/github/pwittchen/reactivenetwork/library/rx2/ConnectivityPredicate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Piotr Wittchen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pwittchen.reactivenetwork.library.rx2; 17 | 18 | import android.net.NetworkInfo; 19 | 20 | import io.reactivex.annotations.NonNull; 21 | import io.reactivex.functions.Predicate; 22 | 23 | /** 24 | * ConnectivityPredicate is a class containing predefined methods, which can be used for filtering 25 | * reactive streams of network connectivity 26 | */ 27 | public final class ConnectivityPredicate { 28 | 29 | private ConnectivityPredicate() { 30 | } 31 | 32 | /** 33 | * Filter, which returns true if at least one given state occurred 34 | * 35 | * @param states NetworkInfo.State, which can have one or more states 36 | * @return true if at least one given state occurred 37 | */ 38 | public static Predicate hasState(final NetworkInfo.State... states) { 39 | return new Predicate() { 40 | @Override public boolean test(@NonNull Connectivity connectivity) throws Exception { 41 | for (NetworkInfo.State state : states) { 42 | if (connectivity.state() == state) { 43 | return true; 44 | } 45 | } 46 | return false; 47 | } 48 | }; 49 | } 50 | 51 | /** 52 | * Filter, which returns true if at least one given type occurred 53 | * 54 | * @param types int, which can have one or more types 55 | * @return true if at least one given type occurred 56 | */ 57 | public static Predicate hasType(final int... types) { 58 | final int[] extendedTypes = appendUnknownNetworkTypeToTypes(types); 59 | return new Predicate() { 60 | @Override public boolean test(@NonNull Connectivity connectivity) throws Exception { 61 | for (int type : extendedTypes) { 62 | if (connectivity.type() == type) { 63 | return true; 64 | } 65 | } 66 | return false; 67 | } 68 | }; 69 | } 70 | 71 | /** 72 | * Returns network types from the input with additional unknown type, 73 | * what helps during connections filtering when device 74 | * is being disconnected from a specific network 75 | * 76 | * @param types of the network as an array of ints 77 | * @return types of the network with unknown type as an array of ints 78 | */ 79 | protected static int[] appendUnknownNetworkTypeToTypes(int[] types) { 80 | int i = 0; 81 | final int[] extendedTypes = new int[types.length + 1]; 82 | for (int type : types) { 83 | extendedTypes[i] = type; 84 | i++; 85 | } 86 | extendedTypes[i] = Connectivity.UNKNOWN_TYPE; 87 | return extendedTypes; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /library/src/main/java/com/github/pwittchen/reactivenetwork/library/rx2/Preconditions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Piotr Wittchen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pwittchen.reactivenetwork.library.rx2; 17 | 18 | import android.os.Build; 19 | 20 | public final class Preconditions { 21 | /** 22 | * Validation method, which checks if an object is null 23 | * 24 | * @param object to verify 25 | * @param message to be thrown in exception 26 | */ 27 | public static void checkNotNull(Object object, String message) { 28 | if (object == null) { 29 | throw new IllegalArgumentException(message); 30 | } 31 | } 32 | 33 | /** 34 | * Validation method, which checks if a string is null or empty 35 | * 36 | * @param string to verify 37 | * @param message to be thrown in exception 38 | */ 39 | public static void checkNotNullOrEmpty(String string, String message) { 40 | if (string == null || string.isEmpty()) { 41 | throw new IllegalArgumentException(message); 42 | } 43 | } 44 | 45 | /** 46 | * Validation method, which checks is an integer number is positive 47 | * 48 | * @param number integer to verify 49 | * @param message to be thrown in exception 50 | */ 51 | public static void checkGreaterOrEqualToZero(int number, String message) { 52 | if (number < 0) { 53 | throw new IllegalArgumentException(message); 54 | } 55 | } 56 | 57 | /** 58 | * Validation method, which checks is an integer number is non-zero or positive 59 | * 60 | * @param number integer to verify 61 | * @param message to be thrown in exception 62 | */ 63 | public static void checkGreaterThanZero(int number, String message) { 64 | if (number <= 0) { 65 | throw new IllegalArgumentException(message); 66 | } 67 | } 68 | 69 | /** 70 | * Validation method, which checks if current Android version is at least Lollipop (API 21) or 71 | * higher 72 | * 73 | * @return boolean true if current Android version is Lollipop or higher 74 | */ 75 | public static boolean isAtLeastAndroidLollipop() { 76 | return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; 77 | } 78 | 79 | /** 80 | * Validation method, which checks if current Android version is at least Marshmallow (API 23) or 81 | * higher 82 | * 83 | * @return boolean true if current Android version is Marshmallow or higher 84 | */ 85 | public static boolean isAtLeastAndroidMarshmallow() { 86 | return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; 87 | } 88 | } 89 | 90 | 91 | -------------------------------------------------------------------------------- /library/src/main/java/com/github/pwittchen/reactivenetwork/library/rx2/ReactiveNetwork.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Piotr Wittchen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pwittchen.reactivenetwork.library.rx2; 17 | 18 | import android.Manifest; 19 | import android.content.Context; 20 | import androidx.annotation.RequiresPermission; 21 | import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings; 22 | import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingStrategy; 23 | import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.error.ErrorHandler; 24 | import com.github.pwittchen.reactivenetwork.library.rx2.network.observing.NetworkObservingStrategy; 25 | import com.github.pwittchen.reactivenetwork.library.rx2.network.observing.strategy.LollipopNetworkObservingStrategy; 26 | import com.github.pwittchen.reactivenetwork.library.rx2.network.observing.strategy.MarshmallowNetworkObservingStrategy; 27 | import com.github.pwittchen.reactivenetwork.library.rx2.network.observing.strategy.PreLollipopNetworkObservingStrategy; 28 | import com.jakewharton.nopen.annotation.Open; 29 | import io.reactivex.Observable; 30 | import io.reactivex.Single; 31 | 32 | /** 33 | * ReactiveNetwork is an Android library 34 | * listening network connection state and change of the WiFi signal strength 35 | * with RxJava Observables. It can be easily used with RxAndroid. 36 | */ 37 | @Open public class ReactiveNetwork { 38 | public final static String LOG_TAG = "ReactiveNetwork"; 39 | 40 | protected ReactiveNetwork() { 41 | } 42 | 43 | /** 44 | * Creates a new instance of the ReactiveNetwork class 45 | * 46 | * @return ReactiveNetwork object 47 | */ 48 | public static ReactiveNetwork create() { 49 | return new ReactiveNetwork(); 50 | } 51 | 52 | /** 53 | * Observes network connectivity. Information about network state, type and typeName are contained 54 | * in 55 | * observed Connectivity object. 56 | * 57 | * @param context Context of the activity or an application 58 | * @return RxJava Observable with Connectivity class containing information about network state, 59 | * type and typeName 60 | */ 61 | @RequiresPermission(Manifest.permission.ACCESS_NETWORK_STATE) 62 | public static Observable observeNetworkConnectivity(final Context context) { 63 | final NetworkObservingStrategy strategy; 64 | 65 | if (Preconditions.isAtLeastAndroidMarshmallow()) { 66 | strategy = new MarshmallowNetworkObservingStrategy(); 67 | } else if (Preconditions.isAtLeastAndroidLollipop()) { 68 | strategy = new LollipopNetworkObservingStrategy(); 69 | } else { 70 | strategy = new PreLollipopNetworkObservingStrategy(); 71 | } 72 | 73 | return observeNetworkConnectivity(context, strategy); 74 | } 75 | 76 | /** 77 | * Observes network connectivity. Information about network state, type and typeName are contained 78 | * in observed Connectivity object. Moreover, allows you to define NetworkObservingStrategy. 79 | * 80 | * @param context Context of the activity or an application 81 | * @param strategy NetworkObserving strategy to be applied - you can use one of the existing 82 | * strategies {@link PreLollipopNetworkObservingStrategy}, 83 | * {@link LollipopNetworkObservingStrategy} or create your own custom strategy 84 | * @return RxJava Observable with Connectivity class containing information about network state, 85 | * type and typeName 86 | */ 87 | @RequiresPermission(Manifest.permission.ACCESS_NETWORK_STATE) 88 | public static Observable observeNetworkConnectivity(final Context context, 89 | final NetworkObservingStrategy strategy) { 90 | Preconditions.checkNotNull(context, "context == null"); 91 | Preconditions.checkNotNull(strategy, "strategy == null"); 92 | return strategy.observeNetworkConnectivity(context); 93 | } 94 | 95 | /** 96 | * Observes connectivity with the Internet with default settings. It pings remote host 97 | * (www.google.com) at port 80 every 2 seconds with 2 seconds of timeout. This operation is used 98 | * for determining if device is connected to the Internet or not. Please note that this method is 99 | * less efficient than {@link #observeNetworkConnectivity(Context)} method and consumes data 100 | * transfer, but it gives you actual information if device is connected to the Internet or not. 101 | * 102 | * @return RxJava Observable with Boolean - true, when we have an access to the Internet 103 | * and false if not 104 | */ 105 | @RequiresPermission(Manifest.permission.INTERNET) 106 | public static Observable observeInternetConnectivity() { 107 | InternetObservingSettings settings = InternetObservingSettings.create(); 108 | return observeInternetConnectivity(settings.strategy(), settings.initialInterval(), 109 | settings.interval(), settings.host(), settings.port(), 110 | settings.timeout(), settings.httpResponse(), settings.errorHandler()); 111 | } 112 | 113 | /** 114 | * Observes connectivity with the Internet in a given time interval. 115 | * 116 | * @param settings Internet Observing Settings created via Builder pattern 117 | * @return RxJava Observable with Boolean - true, when we have connection with host and false if 118 | * not 119 | */ 120 | @RequiresPermission(Manifest.permission.INTERNET) 121 | public static Observable observeInternetConnectivity( 122 | InternetObservingSettings settings) { 123 | return observeInternetConnectivity(settings.strategy(), settings.initialInterval(), 124 | settings.interval(), settings.host(), settings.port(), 125 | settings.timeout(), settings.httpResponse(), settings.errorHandler()); 126 | } 127 | 128 | /** 129 | * Observes connectivity with the Internet in a given time interval. 130 | * 131 | * @param strategy for observing Internet connectivity 132 | * @param initialIntervalInMs in milliseconds determining the delay of the first connectivity 133 | * check 134 | * @param intervalInMs in milliseconds determining how often we want to check connectivity 135 | * @param host for checking Internet connectivity 136 | * @param port for checking Internet connectivity 137 | * @param timeoutInMs for pinging remote host in milliseconds 138 | * @param httpResponse expected HTTP response code indicating that connection is established 139 | * @param errorHandler for handling errors during connectivity check 140 | * @return RxJava Observable with Boolean - true, when we have connection with host and false if 141 | * not 142 | */ 143 | @RequiresPermission(Manifest.permission.INTERNET) 144 | protected static Observable observeInternetConnectivity( 145 | final InternetObservingStrategy strategy, final int initialIntervalInMs, 146 | final int intervalInMs, final String host, final int port, final int timeoutInMs, 147 | final int httpResponse, final ErrorHandler errorHandler) { 148 | checkStrategyIsNotNull(strategy); 149 | return strategy.observeInternetConnectivity(initialIntervalInMs, intervalInMs, host, port, 150 | timeoutInMs, httpResponse, errorHandler); 151 | } 152 | 153 | /** 154 | * Checks connectivity with the Internet. This operation is performed only once. 155 | * 156 | * @return RxJava Single with Boolean - true, when we have an access to the Internet 157 | * and false if not 158 | */ 159 | @RequiresPermission(Manifest.permission.INTERNET) 160 | public static Single checkInternetConnectivity() { 161 | InternetObservingSettings settings = InternetObservingSettings.create(); 162 | return checkInternetConnectivity(settings.strategy(), settings.host(), settings.port(), 163 | settings.timeout(), settings.httpResponse(), settings.errorHandler()); 164 | } 165 | 166 | /** 167 | * Checks connectivity with the Internet. This operation is performed only once. 168 | * 169 | * @param settings Internet Observing Settings created via Builder pattern 170 | * @return RxJava Single with Boolean - true, when we have connection with host and false if 171 | * not 172 | */ 173 | @RequiresPermission(Manifest.permission.INTERNET) 174 | public static Single checkInternetConnectivity(InternetObservingSettings settings) { 175 | return checkInternetConnectivity(settings.strategy(), settings.host(), settings.port(), 176 | settings.timeout(), settings.httpResponse(), settings.errorHandler()); 177 | } 178 | 179 | /** 180 | * Checks connectivity with the Internet. This operation is performed only once. 181 | * 182 | * @param strategy for observing Internet connectivity 183 | * @param host for checking Internet connectivity 184 | * @param port for checking Internet connectivity 185 | * @param timeoutInMs for pinging remote host in milliseconds 186 | * @param httpResponse expected HTTP response code indicating that connection is established 187 | * @param errorHandler for handling errors during connectivity check 188 | * @return RxJava Single with Boolean - true, when we have connection with host and false if 189 | * not 190 | */ 191 | @RequiresPermission(Manifest.permission.INTERNET) 192 | protected static Single checkInternetConnectivity( 193 | final InternetObservingStrategy strategy, 194 | final String host, final int port, final int timeoutInMs, final int httpResponse, 195 | final ErrorHandler errorHandler) { 196 | checkStrategyIsNotNull(strategy); 197 | return strategy.checkInternetConnectivity(host, port, timeoutInMs, httpResponse, errorHandler); 198 | } 199 | 200 | private static void checkStrategyIsNotNull(InternetObservingStrategy strategy) { 201 | Preconditions.checkNotNull(strategy, "strategy == null"); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /library/src/main/java/com/github/pwittchen/reactivenetwork/library/rx2/internet/observing/InternetObservingSettings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 Piotr Wittchen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pwittchen.reactivenetwork.library.rx2.internet.observing; 17 | 18 | import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.error.DefaultErrorHandler; 19 | import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.error.ErrorHandler; 20 | import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.strategy.WalledGardenInternetObservingStrategy; 21 | import java.net.HttpURLConnection; 22 | 23 | /** 24 | * Contains state of internet connectivity settings. 25 | * We should use its Builder for creating new settings 26 | */ 27 | @SuppressWarnings("PMD") // I want to have the same method names as variable names on purpose 28 | public final class InternetObservingSettings { 29 | private final int initialInterval; 30 | private final int interval; 31 | private final String host; 32 | private final int port; 33 | private final int timeout; 34 | private final int httpResponse; 35 | private final ErrorHandler errorHandler; 36 | private final InternetObservingStrategy strategy; 37 | 38 | private InternetObservingSettings(int initialInterval, int interval, String host, int port, 39 | int timeout, int httpResponse, ErrorHandler errorHandler, 40 | InternetObservingStrategy strategy) { 41 | this.initialInterval = initialInterval; 42 | this.interval = interval; 43 | this.host = host; 44 | this.port = port; 45 | this.timeout = timeout; 46 | this.httpResponse = httpResponse; 47 | this.errorHandler = errorHandler; 48 | this.strategy = strategy; 49 | } 50 | 51 | /** 52 | * @return settings with default parameters 53 | */ 54 | public static InternetObservingSettings create() { 55 | return new Builder().build(); 56 | } 57 | 58 | private InternetObservingSettings(Builder builder) { 59 | this(builder.initialInterval, builder.interval, builder.host, builder.port, builder.timeout, 60 | builder.httpResponse, builder.errorHandler, builder.strategy); 61 | } 62 | 63 | private InternetObservingSettings() { 64 | this(builder()); 65 | } 66 | 67 | /** 68 | * Creates builder object 69 | * @return Builder 70 | */ 71 | public static Builder builder() { 72 | return new Builder(); 73 | } 74 | 75 | /** 76 | * @return initial ping interval in milliseconds 77 | */ 78 | public int initialInterval() { 79 | return initialInterval; 80 | } 81 | 82 | /** 83 | * @return ping interval in milliseconds 84 | */ 85 | public int interval() { 86 | return interval; 87 | } 88 | 89 | /** 90 | * @return ping host 91 | */ 92 | public String host() { 93 | return host; 94 | } 95 | 96 | /** 97 | * @return ping port 98 | */ 99 | public int port() { 100 | return port; 101 | } 102 | 103 | /** 104 | * @return ping timeout in milliseconds 105 | */ 106 | public int timeout() { 107 | return timeout; 108 | } 109 | 110 | public int httpResponse() { 111 | return httpResponse; 112 | } 113 | 114 | /** 115 | * @return error handler for pings and connections 116 | */ 117 | public ErrorHandler errorHandler() { 118 | return errorHandler; 119 | } 120 | 121 | /** 122 | * @return internet observing strategy 123 | */ 124 | public InternetObservingStrategy strategy() { 125 | return strategy; 126 | } 127 | 128 | /** 129 | * Settings builder, which contains default parameters 130 | */ 131 | public final static class Builder { 132 | private int initialInterval = 0; 133 | private int interval = 2000; 134 | private String host = "http://clients3.google.com/generate_204"; 135 | private int port = 80; 136 | private int timeout = 2000; 137 | private int httpResponse = HttpURLConnection.HTTP_NO_CONTENT; 138 | private ErrorHandler errorHandler = new DefaultErrorHandler(); 139 | private InternetObservingStrategy strategy = new WalledGardenInternetObservingStrategy(); 140 | 141 | private Builder() { 142 | } 143 | 144 | /** 145 | * sets initial ping interval in milliseconds 146 | * 147 | * @param initialInterval in milliseconds 148 | * @return Builder 149 | */ 150 | public Builder initialInterval(int initialInterval) { 151 | this.initialInterval = initialInterval; 152 | return this; 153 | } 154 | 155 | /** 156 | * sets ping interval in milliseconds 157 | * 158 | * @param interval in milliseconds 159 | * @return Builder 160 | */ 161 | public Builder interval(int interval) { 162 | this.interval = interval; 163 | return this; 164 | } 165 | 166 | /** 167 | * sets ping host 168 | * 169 | * @return Builder 170 | */ 171 | public Builder host(String host) { 172 | this.host = host; 173 | return this; 174 | } 175 | 176 | /** 177 | * sets ping port 178 | * 179 | * @return Builder 180 | */ 181 | public Builder port(int port) { 182 | this.port = port; 183 | return this; 184 | } 185 | 186 | /** 187 | * sets ping timeout in milliseconds 188 | * 189 | * @param timeout in milliseconds 190 | * @return Builder 191 | */ 192 | public Builder timeout(int timeout) { 193 | this.timeout = timeout; 194 | return this; 195 | } 196 | 197 | /** 198 | * sets HTTP response code indicating that connection is established 199 | * 200 | * @param httpResponse as integer 201 | * @return Builder 202 | */ 203 | public Builder httpResponse(final int httpResponse) { 204 | this.httpResponse = httpResponse; 205 | return this; 206 | } 207 | 208 | /** 209 | * sets error handler for pings and connections 210 | * 211 | * @return Builder 212 | */ 213 | public Builder errorHandler(ErrorHandler errorHandler) { 214 | this.errorHandler = errorHandler; 215 | return this; 216 | } 217 | 218 | /** 219 | * sets internet observing strategy 220 | * 221 | * @param strategy for observing and internet connection 222 | * @return Builder 223 | */ 224 | public Builder strategy(InternetObservingStrategy strategy) { 225 | this.strategy = strategy; 226 | return this; 227 | } 228 | 229 | public InternetObservingSettings build() { 230 | return new InternetObservingSettings(this); 231 | } 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /library/src/main/java/com/github/pwittchen/reactivenetwork/library/rx2/internet/observing/InternetObservingStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Piotr Wittchen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pwittchen.reactivenetwork.library.rx2.internet.observing; 17 | 18 | import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.error.ErrorHandler; 19 | import io.reactivex.Observable; 20 | import io.reactivex.Single; 21 | 22 | /** 23 | * Internet observing strategy allows to implement different strategies for monitoring connectivity 24 | * with the Internet. 25 | */ 26 | public interface InternetObservingStrategy { 27 | /** 28 | * Observes connectivity with the Internet by opening socket connection with remote host in a 29 | * given interval infinitely 30 | * 31 | * @param initialIntervalInMs in milliseconds determining the delay of the first connectivity 32 | * check 33 | * @param intervalInMs in milliseconds determining how often we want to check connectivity 34 | * @param host for checking Internet connectivity 35 | * @param port for checking Internet connectivity 36 | * @param timeoutInMs for pinging remote host in milliseconds 37 | * @param errorHandler for handling errors while checking connectivity 38 | * @return RxJava Observable with Boolean - true, when we have connection with host and false if 39 | * not 40 | */ 41 | Observable observeInternetConnectivity(final int initialIntervalInMs, 42 | final int intervalInMs, final String host, final int port, final int timeoutInMs, 43 | final int httpResponse, final ErrorHandler errorHandler); 44 | 45 | /** 46 | * Observes connectivity with the Internet by opening socket connection with remote host once 47 | * 48 | * @param host for checking Internet connectivity 49 | * @param port for checking Internet connectivity 50 | * @param timeoutInMs for pinging remote host in milliseconds 51 | * @param errorHandler for handling errors while checking connectivity 52 | * @return RxJava Single with Boolean - true, when we have connection with host and false if 53 | * not 54 | */ 55 | Single checkInternetConnectivity(final String host, final int port, 56 | final int timeoutInMs, final int httpResponse, final ErrorHandler errorHandler); 57 | 58 | /** 59 | * Gets default remote ping host for a given Internet Observing Strategy 60 | * 61 | * @return String with a ping host used in the current strategy 62 | */ 63 | String getDefaultPingHost(); 64 | } 65 | -------------------------------------------------------------------------------- /library/src/main/java/com/github/pwittchen/reactivenetwork/library/rx2/internet/observing/error/DefaultErrorHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Piotr Wittchen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.error; 17 | 18 | import android.util.Log; 19 | import com.jakewharton.nopen.annotation.Open; 20 | 21 | import static com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork.LOG_TAG; 22 | 23 | @Open public class DefaultErrorHandler implements ErrorHandler { 24 | @Override public void handleError(final Exception exception, final String message) { 25 | Log.e(LOG_TAG, message, exception); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /library/src/main/java/com/github/pwittchen/reactivenetwork/library/rx2/internet/observing/error/ErrorHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Piotr Wittchen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.error; 17 | 18 | public interface ErrorHandler { 19 | void handleError(final Exception exception, final String message); 20 | } 21 | -------------------------------------------------------------------------------- /library/src/main/java/com/github/pwittchen/reactivenetwork/library/rx2/internet/observing/strategy/SocketInternetObservingStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Piotr Wittchen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.strategy; 17 | 18 | import com.github.pwittchen.reactivenetwork.library.rx2.Preconditions; 19 | import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingStrategy; 20 | import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.error.ErrorHandler; 21 | import com.jakewharton.nopen.annotation.Open; 22 | import io.reactivex.Observable; 23 | import io.reactivex.Single; 24 | import io.reactivex.SingleEmitter; 25 | import io.reactivex.SingleOnSubscribe; 26 | import io.reactivex.annotations.NonNull; 27 | import io.reactivex.functions.Function; 28 | import io.reactivex.schedulers.Schedulers; 29 | import java.io.IOException; 30 | import java.net.InetSocketAddress; 31 | import java.net.Socket; 32 | import java.util.concurrent.TimeUnit; 33 | 34 | /** 35 | * Socket strategy for monitoring connectivity with the Internet. 36 | * It monitors Internet connectivity via opening socket connection with the remote host. 37 | */ 38 | @Open public class SocketInternetObservingStrategy implements InternetObservingStrategy { 39 | private static final String EMPTY_STRING = ""; 40 | private static final String DEFAULT_HOST = "www.google.com"; 41 | private static final String HTTP_PROTOCOL = "http://"; 42 | private static final String HTTPS_PROTOCOL = "https://"; 43 | 44 | @Override public String getDefaultPingHost() { 45 | return DEFAULT_HOST; 46 | } 47 | 48 | @Override public Observable observeInternetConnectivity(final int initialIntervalInMs, 49 | final int intervalInMs, final String host, final int port, final int timeoutInMs, 50 | final int httpResponse, final ErrorHandler errorHandler) { 51 | Preconditions.checkGreaterOrEqualToZero(initialIntervalInMs, 52 | "initialIntervalInMs is not a positive number"); 53 | Preconditions.checkGreaterThanZero(intervalInMs, "intervalInMs is not a positive number"); 54 | checkGeneralPreconditions(host, port, timeoutInMs, errorHandler); 55 | 56 | final String adjustedHost = adjustHost(host); 57 | 58 | return Observable.interval(initialIntervalInMs, intervalInMs, TimeUnit.MILLISECONDS, 59 | Schedulers.io()).map(new Function() { 60 | @Override public Boolean apply(@NonNull Long tick) throws Exception { 61 | return isConnected(adjustedHost, port, timeoutInMs, errorHandler); 62 | } 63 | }).distinctUntilChanged(); 64 | } 65 | 66 | @Override public Single checkInternetConnectivity(final String host, final int port, 67 | final int timeoutInMs, final int httpResponse, final ErrorHandler errorHandler) { 68 | checkGeneralPreconditions(host, port, timeoutInMs, errorHandler); 69 | 70 | return Single.create(new SingleOnSubscribe() { 71 | @Override public void subscribe(@NonNull SingleEmitter emitter) throws Exception { 72 | emitter.onSuccess(isConnected(host, port, timeoutInMs, errorHandler)); 73 | } 74 | }); 75 | } 76 | 77 | /** 78 | * adjusts host to needs of SocketInternetObservingStrategy 79 | * 80 | * @return transformed host 81 | */ 82 | protected String adjustHost(final String host) { 83 | if (host.startsWith(HTTP_PROTOCOL)) { 84 | return host.replace(HTTP_PROTOCOL, EMPTY_STRING); 85 | } else if (host.startsWith(HTTPS_PROTOCOL)) { 86 | return host.replace(HTTPS_PROTOCOL, EMPTY_STRING); 87 | } 88 | return host; 89 | } 90 | 91 | private void checkGeneralPreconditions(String host, int port, int timeoutInMs, 92 | ErrorHandler errorHandler) { 93 | Preconditions.checkNotNullOrEmpty(host, "host is null or empty"); 94 | Preconditions.checkGreaterThanZero(port, "port is not a positive number"); 95 | Preconditions.checkGreaterThanZero(timeoutInMs, "timeoutInMs is not a positive number"); 96 | Preconditions.checkNotNull(errorHandler, "errorHandler is null"); 97 | } 98 | 99 | /** 100 | * checks if device is connected to given host at given port 101 | * 102 | * @param host to connect 103 | * @param port to connect 104 | * @param timeoutInMs connection timeout 105 | * @param errorHandler error handler for socket connection 106 | * @return boolean true if connected and false if not 107 | */ 108 | protected boolean isConnected(final String host, final int port, final int timeoutInMs, 109 | final ErrorHandler errorHandler) { 110 | final Socket socket = new Socket(); 111 | return isConnected(socket, host, port, timeoutInMs, errorHandler); 112 | } 113 | 114 | /** 115 | * checks if device is connected to given host at given port 116 | * 117 | * @param socket to connect 118 | * @param host to connect 119 | * @param port to connect 120 | * @param timeoutInMs connection timeout 121 | * @param errorHandler error handler for socket connection 122 | * @return boolean true if connected and false if not 123 | */ 124 | protected boolean isConnected(final Socket socket, final String host, final int port, 125 | final int timeoutInMs, final ErrorHandler errorHandler) { 126 | boolean isConnected; 127 | try { 128 | socket.connect(new InetSocketAddress(host, port), timeoutInMs); 129 | isConnected = socket.isConnected(); 130 | } catch (IOException e) { 131 | isConnected = Boolean.FALSE; 132 | } finally { 133 | try { 134 | socket.close(); 135 | } catch (IOException exception) { 136 | errorHandler.handleError(exception, "Could not close the socket"); 137 | } 138 | } 139 | return isConnected; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /library/src/main/java/com/github/pwittchen/reactivenetwork/library/rx2/internet/observing/strategy/WalledGardenInternetObservingStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Piotr Wittchen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.strategy; 17 | 18 | import com.github.pwittchen.reactivenetwork.library.rx2.Preconditions; 19 | import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingStrategy; 20 | import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.error.ErrorHandler; 21 | import com.jakewharton.nopen.annotation.Open; 22 | import io.reactivex.Observable; 23 | import io.reactivex.Single; 24 | import io.reactivex.SingleEmitter; 25 | import io.reactivex.SingleOnSubscribe; 26 | import io.reactivex.annotations.NonNull; 27 | import io.reactivex.functions.Function; 28 | import io.reactivex.schedulers.Schedulers; 29 | import java.io.IOException; 30 | import java.net.HttpURLConnection; 31 | import java.net.URL; 32 | import java.util.concurrent.TimeUnit; 33 | import javax.net.ssl.HttpsURLConnection; 34 | 35 | /** 36 | * Walled Garden Strategy for monitoring connectivity with the Internet. 37 | * This strategy handle use case of the countries behind Great Firewall (e.g. China), 38 | * which does not has access to several websites like Google. It such case, different HTTP responses 39 | * are generated. Instead HTTP 200 (OK), we got HTTP 204 (NO CONTENT), but it still can tell us 40 | * if a device is connected to the Internet or not. 41 | */ 42 | @Open public class WalledGardenInternetObservingStrategy implements InternetObservingStrategy { 43 | private static final String DEFAULT_HOST = "http://clients3.google.com/generate_204"; 44 | private static final String HTTP_PROTOCOL = "http://"; 45 | private static final String HTTPS_PROTOCOL = "https://"; 46 | 47 | @Override public String getDefaultPingHost() { 48 | return DEFAULT_HOST; 49 | } 50 | 51 | @Override public Observable observeInternetConnectivity(final int initialIntervalInMs, 52 | final int intervalInMs, final String host, final int port, final int timeoutInMs, 53 | final int httpResponse, 54 | final ErrorHandler errorHandler) { 55 | 56 | Preconditions.checkGreaterOrEqualToZero(initialIntervalInMs, 57 | "initialIntervalInMs is not a positive number"); 58 | Preconditions.checkGreaterThanZero(intervalInMs, "intervalInMs is not a positive number"); 59 | checkGeneralPreconditions(host, port, timeoutInMs, httpResponse, errorHandler); 60 | 61 | final String adjustedHost = adjustHost(host); 62 | 63 | return Observable.interval(initialIntervalInMs, intervalInMs, TimeUnit.MILLISECONDS, 64 | Schedulers.io()).map(new Function() { 65 | @Override public Boolean apply(@NonNull Long tick) { 66 | return isConnected(adjustedHost, port, timeoutInMs, httpResponse, errorHandler); 67 | } 68 | }).distinctUntilChanged(); 69 | } 70 | 71 | @Override public Single checkInternetConnectivity(final String host, final int port, 72 | final int timeoutInMs, final int httpResponse, final ErrorHandler errorHandler) { 73 | checkGeneralPreconditions(host, port, timeoutInMs, httpResponse, errorHandler); 74 | 75 | return Single.create(new SingleOnSubscribe() { 76 | @Override public void subscribe(@NonNull SingleEmitter emitter) { 77 | emitter.onSuccess(isConnected(host, port, timeoutInMs, httpResponse, errorHandler)); 78 | } 79 | }); 80 | } 81 | 82 | protected String adjustHost(final String host) { 83 | if (!host.startsWith(HTTP_PROTOCOL) && !host.startsWith(HTTPS_PROTOCOL)) { 84 | return HTTPS_PROTOCOL.concat(host); 85 | } 86 | 87 | return host; 88 | } 89 | 90 | private void checkGeneralPreconditions(final String host, final int port, final int timeoutInMs, 91 | final int httpResponse, final ErrorHandler errorHandler) { 92 | Preconditions.checkNotNullOrEmpty(host, "host is null or empty"); 93 | Preconditions.checkGreaterThanZero(port, "port is not a positive number"); 94 | Preconditions.checkGreaterThanZero(timeoutInMs, "timeoutInMs is not a positive number"); 95 | Preconditions.checkNotNull(errorHandler, "errorHandler is null"); 96 | Preconditions.checkNotNull(httpResponse, "httpResponse is null"); 97 | Preconditions.checkGreaterThanZero(httpResponse, "httpResponse is not a positive number"); 98 | } 99 | 100 | protected Boolean isConnected(final String host, final int port, final int timeoutInMs, 101 | final int httpResponse, final ErrorHandler errorHandler) { 102 | HttpURLConnection urlConnection = null; 103 | try { 104 | if (host.startsWith(HTTPS_PROTOCOL)) { 105 | urlConnection = createHttpsUrlConnection(host, port, timeoutInMs); 106 | } else { 107 | urlConnection = createHttpUrlConnection(host, port, timeoutInMs); 108 | } 109 | return urlConnection.getResponseCode() == httpResponse; 110 | } catch (IOException e) { 111 | errorHandler.handleError(e, "Could not establish connection with WalledGardenStrategy"); 112 | return Boolean.FALSE; 113 | } finally { 114 | if (urlConnection != null) { 115 | urlConnection.disconnect(); 116 | } 117 | } 118 | } 119 | 120 | protected HttpURLConnection createHttpUrlConnection(final String host, final int port, 121 | final int timeoutInMs) throws IOException { 122 | URL initialUrl = new URL(host); 123 | URL url = new URL(initialUrl.getProtocol(), initialUrl.getHost(), port, initialUrl.getFile()); 124 | HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); 125 | urlConnection.setConnectTimeout(timeoutInMs); 126 | urlConnection.setReadTimeout(timeoutInMs); 127 | urlConnection.setInstanceFollowRedirects(false); 128 | urlConnection.setUseCaches(false); 129 | return urlConnection; 130 | } 131 | 132 | protected HttpsURLConnection createHttpsUrlConnection(final String host, final int port, 133 | final int timeoutInMs) throws IOException { 134 | URL initialUrl = new URL(host); 135 | URL url = new URL(initialUrl.getProtocol(), initialUrl.getHost(), port, initialUrl.getFile()); 136 | HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection(); 137 | urlConnection.setConnectTimeout(timeoutInMs); 138 | urlConnection.setReadTimeout(timeoutInMs); 139 | urlConnection.setInstanceFollowRedirects(false); 140 | urlConnection.setUseCaches(false); 141 | return urlConnection; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /library/src/main/java/com/github/pwittchen/reactivenetwork/library/rx2/network/observing/NetworkObservingStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Piotr Wittchen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pwittchen.reactivenetwork.library.rx2.network.observing; 17 | 18 | import android.content.Context; 19 | import com.github.pwittchen.reactivenetwork.library.rx2.Connectivity; 20 | import io.reactivex.Observable; 21 | 22 | /** 23 | * Network observing strategy allows to implement different strategies for monitoring network 24 | * connectivity change. Network monitoring API may differ depending of specific Android version. 25 | */ 26 | public interface NetworkObservingStrategy { 27 | /** 28 | * Observes network connectivity 29 | * 30 | * @param context of the Activity or an Application 31 | * @return Observable representing stream of the network connectivity 32 | */ 33 | Observable observeNetworkConnectivity(final Context context); 34 | 35 | /** 36 | * Handles errors, which occurred during observing network connectivity 37 | * 38 | * @param message to be processed 39 | * @param exception which was thrown 40 | */ 41 | void onError(final String message, final Exception exception); 42 | } 43 | -------------------------------------------------------------------------------- /library/src/main/java/com/github/pwittchen/reactivenetwork/library/rx2/network/observing/strategy/LollipopNetworkObservingStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Piotr Wittchen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pwittchen.reactivenetwork.library.rx2.network.observing.strategy; 17 | 18 | import android.annotation.TargetApi; 19 | import android.content.Context; 20 | import android.net.ConnectivityManager; 21 | import android.net.ConnectivityManager.NetworkCallback; 22 | import android.net.Network; 23 | import android.net.NetworkRequest; 24 | import android.util.Log; 25 | import com.github.pwittchen.reactivenetwork.library.rx2.Connectivity; 26 | import com.github.pwittchen.reactivenetwork.library.rx2.network.observing.NetworkObservingStrategy; 27 | import com.jakewharton.nopen.annotation.Open; 28 | import io.reactivex.Observable; 29 | import io.reactivex.ObservableEmitter; 30 | import io.reactivex.ObservableOnSubscribe; 31 | import io.reactivex.functions.Action; 32 | 33 | import static com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork.LOG_TAG; 34 | 35 | /** 36 | * Network observing strategy for devices with Android Lollipop (API 21) or higher. 37 | * Uses Network Callback API. 38 | */ 39 | @Open @TargetApi(21) public class LollipopNetworkObservingStrategy 40 | implements NetworkObservingStrategy { 41 | @SuppressWarnings("NullAway") // it has to be initialized in the Observable due to Context 42 | private NetworkCallback networkCallback; 43 | 44 | @Override public Observable observeNetworkConnectivity(final Context context) { 45 | final String service = Context.CONNECTIVITY_SERVICE; 46 | final ConnectivityManager manager = (ConnectivityManager) context.getSystemService(service); 47 | 48 | return Observable.create(new ObservableOnSubscribe() { 49 | @Override public void subscribe(ObservableEmitter subscriber) throws Exception { 50 | networkCallback = createNetworkCallback(subscriber, context); 51 | final NetworkRequest networkRequest = new NetworkRequest.Builder().build(); 52 | manager.registerNetworkCallback(networkRequest, networkCallback); 53 | } 54 | }).doOnDispose(new Action() { 55 | @Override public void run() { 56 | tryToUnregisterCallback(manager); 57 | } 58 | }).startWith(Connectivity.create(context)).distinctUntilChanged(); 59 | } 60 | 61 | private void tryToUnregisterCallback(final ConnectivityManager manager) { 62 | try { 63 | manager.unregisterNetworkCallback(networkCallback); 64 | } catch (Exception exception) { 65 | onError("could not unregister network callback", exception); 66 | } 67 | } 68 | 69 | @Override public void onError(final String message, final Exception exception) { 70 | Log.e(LOG_TAG, message, exception); 71 | } 72 | 73 | private NetworkCallback createNetworkCallback(final ObservableEmitter subscriber, 74 | final Context context) { 75 | return new ConnectivityManager.NetworkCallback() { 76 | @Override public void onAvailable(Network network) { 77 | subscriber.onNext(Connectivity.create(context)); 78 | } 79 | 80 | @Override public void onLost(Network network) { 81 | subscriber.onNext(Connectivity.create(context)); 82 | } 83 | }; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /library/src/main/java/com/github/pwittchen/reactivenetwork/library/rx2/network/observing/strategy/MarshmallowNetworkObservingStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Piotr Wittchen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pwittchen.reactivenetwork.library.rx2.network.observing.strategy; 17 | 18 | import android.annotation.TargetApi; 19 | import android.content.BroadcastReceiver; 20 | import android.content.Context; 21 | import android.content.Intent; 22 | import android.content.IntentFilter; 23 | import android.net.ConnectivityManager; 24 | import android.net.Network; 25 | import android.net.NetworkCapabilities; 26 | import android.net.NetworkInfo; 27 | import android.net.NetworkRequest; 28 | import android.os.PowerManager; 29 | import android.util.Log; 30 | import androidx.annotation.NonNull; 31 | import com.github.pwittchen.reactivenetwork.library.rx2.Connectivity; 32 | import com.github.pwittchen.reactivenetwork.library.rx2.network.observing.NetworkObservingStrategy; 33 | import com.jakewharton.nopen.annotation.Open; 34 | import io.reactivex.BackpressureStrategy; 35 | import io.reactivex.Flowable; 36 | import io.reactivex.Observable; 37 | import io.reactivex.functions.Action; 38 | import io.reactivex.functions.Consumer; 39 | import io.reactivex.functions.Function; 40 | import io.reactivex.subjects.PublishSubject; 41 | import io.reactivex.subjects.Subject; 42 | import org.reactivestreams.Publisher; 43 | 44 | import static com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork.LOG_TAG; 45 | 46 | /** 47 | * Network observing strategy for devices with Android Marshmallow (API 23) or higher. 48 | * Uses Network Callback API and handles Doze mode. 49 | */ 50 | @Open @TargetApi(23) public class MarshmallowNetworkObservingStrategy 51 | implements NetworkObservingStrategy { 52 | protected static final String ERROR_MSG_NETWORK_CALLBACK = 53 | "could not unregister network callback"; 54 | protected static final String ERROR_MSG_RECEIVER = "could not unregister receiver"; 55 | 56 | @SuppressWarnings("NullAway") // it has to be initialized in the Observable due to Context 57 | private ConnectivityManager.NetworkCallback networkCallback; 58 | private final Subject connectivitySubject; 59 | private final BroadcastReceiver idleReceiver; 60 | private Connectivity lastConnectivity = Connectivity.create(); 61 | 62 | @SuppressWarnings("NullAway") // networkCallback cannot be initialized here 63 | public MarshmallowNetworkObservingStrategy() { 64 | this.idleReceiver = createIdleBroadcastReceiver(); 65 | this.connectivitySubject = PublishSubject.create().toSerialized(); 66 | } 67 | 68 | @Override public Observable observeNetworkConnectivity(final Context context) { 69 | final String service = Context.CONNECTIVITY_SERVICE; 70 | final ConnectivityManager manager = (ConnectivityManager) context.getSystemService(service); 71 | networkCallback = createNetworkCallback(context); 72 | 73 | registerIdleReceiver(context); 74 | 75 | final NetworkRequest request = 76 | new NetworkRequest.Builder().addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 77 | .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) 78 | .build(); 79 | 80 | manager.registerNetworkCallback(request, networkCallback); 81 | 82 | return connectivitySubject.toFlowable(BackpressureStrategy.LATEST).doOnCancel(new Action() { 83 | @Override public void run() { 84 | tryToUnregisterCallback(manager); 85 | tryToUnregisterReceiver(context); 86 | } 87 | }).doAfterNext(new Consumer() { 88 | @Override 89 | public void accept(final Connectivity connectivity) { 90 | lastConnectivity = connectivity; 91 | } 92 | }).flatMap(new Function>() { 93 | @Override 94 | public Publisher apply(final Connectivity connectivity) { 95 | return propagateAnyConnectedState(lastConnectivity, connectivity); 96 | } 97 | }).startWith(Connectivity.create(context)).distinctUntilChanged().toObservable(); 98 | } 99 | 100 | protected Publisher propagateAnyConnectedState(final Connectivity last, 101 | final Connectivity current) { 102 | final boolean typeChanged = last.type() != current.type(); 103 | final boolean wasConnected = last.state() == NetworkInfo.State.CONNECTED; 104 | final boolean isDisconnected = current.state() == NetworkInfo.State.DISCONNECTED; 105 | final boolean isNotIdle = current.detailedState() != NetworkInfo.DetailedState.IDLE; 106 | 107 | if (typeChanged && wasConnected && isDisconnected && isNotIdle) { 108 | return Flowable.fromArray(current, last); 109 | } else { 110 | return Flowable.fromArray(current); 111 | } 112 | } 113 | 114 | protected void registerIdleReceiver(final Context context) { 115 | final IntentFilter filter = new IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); 116 | context.registerReceiver(idleReceiver, filter); 117 | } 118 | 119 | @NonNull protected BroadcastReceiver createIdleBroadcastReceiver() { 120 | return new BroadcastReceiver() { 121 | @Override public void onReceive(final Context context, final Intent intent) { 122 | if (isIdleMode(context)) { 123 | onNext(Connectivity.create()); 124 | } else { 125 | onNext(Connectivity.create(context)); 126 | } 127 | } 128 | }; 129 | } 130 | 131 | protected boolean isIdleMode(final Context context) { 132 | final String packageName = context.getPackageName(); 133 | final PowerManager manager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 134 | boolean isIgnoringOptimizations = manager.isIgnoringBatteryOptimizations(packageName); 135 | return manager.isDeviceIdleMode() && !isIgnoringOptimizations; 136 | } 137 | 138 | protected void tryToUnregisterCallback(final ConnectivityManager manager) { 139 | try { 140 | manager.unregisterNetworkCallback(networkCallback); 141 | } catch (Exception exception) { 142 | onError(ERROR_MSG_NETWORK_CALLBACK, exception); 143 | } 144 | } 145 | 146 | protected void tryToUnregisterReceiver(Context context) { 147 | try { 148 | context.unregisterReceiver(idleReceiver); 149 | } catch (Exception exception) { 150 | onError(ERROR_MSG_RECEIVER, exception); 151 | } 152 | } 153 | 154 | @Override public void onError(final String message, final Exception exception) { 155 | Log.e(LOG_TAG, message, exception); 156 | } 157 | 158 | protected ConnectivityManager.NetworkCallback createNetworkCallback(final Context context) { 159 | return new ConnectivityManager.NetworkCallback() { 160 | @Override public void onAvailable(Network network) { 161 | onNext(Connectivity.create(context)); 162 | } 163 | 164 | @Override public void onLost(Network network) { 165 | onNext(Connectivity.create(context)); 166 | } 167 | }; 168 | } 169 | 170 | protected void onNext(Connectivity connectivity) { 171 | connectivitySubject.onNext(connectivity); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /library/src/main/java/com/github/pwittchen/reactivenetwork/library/rx2/network/observing/strategy/PreLollipopNetworkObservingStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Piotr Wittchen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pwittchen.reactivenetwork.library.rx2.network.observing.strategy; 17 | 18 | import android.content.BroadcastReceiver; 19 | import android.content.Context; 20 | import android.content.Intent; 21 | import android.content.IntentFilter; 22 | import android.net.ConnectivityManager; 23 | import android.os.Looper; 24 | import android.util.Log; 25 | import com.github.pwittchen.reactivenetwork.library.rx2.Connectivity; 26 | import com.github.pwittchen.reactivenetwork.library.rx2.network.observing.NetworkObservingStrategy; 27 | import com.jakewharton.nopen.annotation.Open; 28 | import io.reactivex.Observable; 29 | import io.reactivex.ObservableEmitter; 30 | import io.reactivex.ObservableOnSubscribe; 31 | import io.reactivex.Scheduler; 32 | import io.reactivex.android.schedulers.AndroidSchedulers; 33 | import io.reactivex.disposables.Disposable; 34 | import io.reactivex.disposables.Disposables; 35 | import io.reactivex.functions.Action; 36 | 37 | import static com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork.LOG_TAG; 38 | 39 | /** 40 | * Network observing strategy for Android devices before Lollipop (API 20 or lower). 41 | * Uses Broadcast Receiver. 42 | */ 43 | @Open public class PreLollipopNetworkObservingStrategy implements NetworkObservingStrategy { 44 | 45 | @Override public Observable observeNetworkConnectivity(final Context context) { 46 | final IntentFilter filter = new IntentFilter(); 47 | filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); 48 | 49 | return Observable.create(new ObservableOnSubscribe() { 50 | @Override public void subscribe(final ObservableEmitter emitter) 51 | throws Exception { 52 | final BroadcastReceiver receiver = new BroadcastReceiver() { 53 | @Override public void onReceive(Context context, Intent intent) { 54 | emitter.onNext(Connectivity.create(context)); 55 | } 56 | }; 57 | 58 | context.registerReceiver(receiver, filter); 59 | 60 | Disposable disposable = disposeInUiThread(new Action() { 61 | @Override public void run() { 62 | tryToUnregisterReceiver(context, receiver); 63 | } 64 | }); 65 | emitter.setDisposable(disposable); 66 | } 67 | }).defaultIfEmpty(Connectivity.create()); 68 | } 69 | 70 | protected void tryToUnregisterReceiver(final Context context, final BroadcastReceiver receiver) { 71 | try { 72 | context.unregisterReceiver(receiver); 73 | } catch (Exception exception) { 74 | onError("receiver was already unregistered", exception); 75 | } 76 | } 77 | 78 | @Override public void onError(final String message, final Exception exception) { 79 | Log.e(LOG_TAG, message, exception); 80 | } 81 | 82 | private Disposable disposeInUiThread(final Action action) { 83 | return Disposables.fromAction(new Action() { 84 | @Override public void run() throws Exception { 85 | if (Looper.getMainLooper() == Looper.myLooper()) { 86 | action.run(); 87 | } else { 88 | final Scheduler.Worker inner = AndroidSchedulers.mainThread().createWorker(); 89 | inner.schedule(new Runnable() { 90 | @Override public void run() { 91 | try { 92 | action.run(); 93 | } catch (Exception e) { 94 | onError("Could not unregister receiver in UI Thread", e); 95 | } 96 | inner.dispose(); 97 | } 98 | }); 99 | } 100 | } 101 | }); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /library/src/test/java/com/github/pwittchen/reactivenetwork/library/rx2/ConnectivityTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Piotr Wittchen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pwittchen.reactivenetwork.library.rx2; 17 | 18 | import android.content.Context; 19 | import android.net.ConnectivityManager; 20 | import android.net.NetworkInfo; 21 | import io.reactivex.functions.Predicate; 22 | import org.junit.Test; 23 | import org.junit.runner.RunWith; 24 | import org.robolectric.RobolectricTestRunner; 25 | import org.robolectric.RuntimeEnvironment; 26 | 27 | import static com.google.common.truth.Truth.assertThat; 28 | 29 | @RunWith(RobolectricTestRunner.class) 30 | @SuppressWarnings("NullAway") public class ConnectivityTest { 31 | private static final String TYPE_NAME_WIFI = "WIFI"; 32 | private static final String TYPE_NAME_MOBILE = "MOBILE"; 33 | private static final String TYPE_NAME_NONE = "NONE"; 34 | 35 | @Test public void shouldCreateConnectivity() { 36 | // when 37 | Connectivity connectivity = Connectivity.create(); 38 | 39 | // then 40 | assertThat(connectivity).isNotNull(); 41 | assertThat(connectivity.state()).isEqualTo(NetworkInfo.State.DISCONNECTED); 42 | assertThat(connectivity.detailedState()).isEqualTo(NetworkInfo.DetailedState.IDLE); 43 | assertThat(connectivity.type()).isEqualTo(Connectivity.UNKNOWN_TYPE); 44 | assertThat(connectivity.subType()).isEqualTo(Connectivity.UNKNOWN_SUB_TYPE); 45 | assertThat(connectivity.available()).isFalse(); 46 | assertThat(connectivity.failover()).isFalse(); 47 | assertThat(connectivity.roaming()).isFalse(); 48 | assertThat(connectivity.typeName()).isEqualTo(TYPE_NAME_NONE); 49 | assertThat(connectivity.subTypeName()).isEqualTo(TYPE_NAME_NONE); 50 | assertThat(connectivity.reason()).isEmpty(); 51 | assertThat(connectivity.extraInfo()).isEmpty(); 52 | } 53 | 54 | @Test public void stateShouldBeEqualToGivenValue() throws Exception { 55 | // given 56 | final Connectivity connectivity = Connectivity.state(NetworkInfo.State.CONNECTED) 57 | .type(ConnectivityManager.TYPE_WIFI) 58 | .typeName(TYPE_NAME_WIFI) 59 | .build(); 60 | 61 | // when 62 | final Predicate equalTo = ConnectivityPredicate.hasState(connectivity.state()); 63 | final Boolean shouldBeEqualToGivenStatus = equalTo.test(connectivity); 64 | 65 | // then 66 | assertThat(shouldBeEqualToGivenStatus).isTrue(); 67 | } 68 | 69 | @Test public void stateShouldBeEqualToOneOfGivenMultipleValues() throws Exception { 70 | // given 71 | final Connectivity connectivity = Connectivity.state(NetworkInfo.State.CONNECTING) 72 | .type(ConnectivityManager.TYPE_WIFI) 73 | .typeName(TYPE_NAME_WIFI) 74 | .build(); 75 | 76 | //noinspection CStyleArrayDeclaration 77 | final NetworkInfo.State states[] = 78 | { NetworkInfo.State.CONNECTED, NetworkInfo.State.CONNECTING }; 79 | 80 | // when 81 | final Predicate equalTo = ConnectivityPredicate.hasState(states); 82 | final Boolean shouldBeEqualToGivenStatus = equalTo.test(connectivity); 83 | 84 | // then 85 | assertThat(shouldBeEqualToGivenStatus).isTrue(); 86 | } 87 | 88 | @Test public void stateShouldNotBeEqualToGivenValue() throws Exception { 89 | // given 90 | final Connectivity connectivity = 91 | Connectivity.state(NetworkInfo.State.DISCONNECTED) 92 | .type(ConnectivityManager.TYPE_WIFI) 93 | .typeName(TYPE_NAME_WIFI) 94 | .build(); 95 | 96 | // when 97 | final Predicate equalTo = 98 | ConnectivityPredicate.hasState(NetworkInfo.State.CONNECTED); 99 | final Boolean shouldBeEqualToGivenStatus = equalTo.test(connectivity); 100 | 101 | // then 102 | assertThat(shouldBeEqualToGivenStatus).isFalse(); 103 | } 104 | 105 | @Test public void typeShouldBeEqualToGivenValue() throws Exception { 106 | // given 107 | final Connectivity connectivity = Connectivity.state(NetworkInfo.State.CONNECTED) 108 | .type(ConnectivityManager.TYPE_WIFI) 109 | .typeName(TYPE_NAME_WIFI) 110 | .build(); 111 | 112 | // note that unknown type is added initially by the ConnectivityPredicate#hasType method 113 | //noinspection CStyleArrayDeclaration 114 | final int givenTypes[] = { connectivity.type(), Connectivity.UNKNOWN_TYPE }; 115 | 116 | // when 117 | final Predicate equalTo = ConnectivityPredicate.hasType(givenTypes); 118 | final Boolean shouldBeEqualToGivenStatus = equalTo.test(connectivity); 119 | 120 | // then 121 | assertThat(shouldBeEqualToGivenStatus).isTrue(); 122 | } 123 | 124 | @Test public void typeShouldBeEqualToOneOfGivenMultipleValues() throws Exception { 125 | // given 126 | final Connectivity connectivity = Connectivity.state(NetworkInfo.State.CONNECTING) 127 | .type(ConnectivityManager.TYPE_MOBILE) 128 | .typeName(TYPE_NAME_MOBILE) 129 | .build(); 130 | 131 | // note that unknown type is added initially by the ConnectivityPredicate#hasType method 132 | //noinspection CStyleArrayDeclaration 133 | final int givenTypes[] = { 134 | ConnectivityManager.TYPE_WIFI, ConnectivityManager.TYPE_MOBILE, Connectivity.UNKNOWN_TYPE 135 | }; 136 | 137 | // when 138 | final Predicate equalTo = ConnectivityPredicate.hasType(givenTypes); 139 | final Boolean shouldBeEqualToGivenStatus = equalTo.test(connectivity); 140 | 141 | // then 142 | assertThat(shouldBeEqualToGivenStatus).isTrue(); 143 | } 144 | 145 | @Test public void typeShouldNotBeEqualToGivenValue() throws Exception { 146 | // given 147 | final Connectivity connectivity = Connectivity.state(NetworkInfo.State.CONNECTED) 148 | .type(ConnectivityManager.TYPE_WIFI) 149 | .typeName(TYPE_NAME_WIFI) 150 | .build(); 151 | 152 | // note that unknown type is added initially by the ConnectivityPredicate#hasType method 153 | //noinspection CStyleArrayDeclaration 154 | final int givenTypes[] = { ConnectivityManager.TYPE_MOBILE, Connectivity.UNKNOWN_TYPE }; 155 | 156 | // when 157 | final Predicate equalTo = ConnectivityPredicate.hasType(givenTypes); 158 | final Boolean shouldBeEqualToGivenStatus = equalTo.test(connectivity); 159 | 160 | // then 161 | assertThat(shouldBeEqualToGivenStatus).isFalse(); 162 | } 163 | 164 | @Test(expected = IllegalArgumentException.class) 165 | public void createShouldThrowAnExceptionWhenContextIsNull() { 166 | // given 167 | final Context context = null; 168 | 169 | // when 170 | //noinspection ConstantConditions 171 | Connectivity.create(context); 172 | 173 | // then 174 | // an exception is thrown 175 | } 176 | 177 | @Test public void shouldReturnProperToStringValue() { 178 | // given 179 | final String expectedToString = "Connectivity{" 180 | + "state=DISCONNECTED, " 181 | + "detailedState=IDLE, " 182 | + "type=-1, " 183 | + "subType=-1, " 184 | + "available=false, " 185 | + "failover=false, " 186 | + "roaming=false, " 187 | + "typeName='NONE', " 188 | + "subTypeName='NONE', " 189 | + "reason='', " 190 | + "extraInfo=''}"; 191 | 192 | // when 193 | Connectivity connectivity = Connectivity.create(); 194 | 195 | // then 196 | assertThat(connectivity.toString()).isEqualTo(expectedToString); 197 | } 198 | 199 | @Test public void theSameConnectivityObjectsShouldBeEqual() { 200 | // given 201 | final Connectivity connectivityOne = Connectivity.create(); 202 | final Connectivity connectivityTwo = Connectivity.create(); 203 | 204 | // when 205 | boolean objectsAreEqual = connectivityOne.equals(connectivityTwo); 206 | 207 | // then 208 | assertThat(objectsAreEqual).isTrue(); 209 | } 210 | 211 | @Test public void twoDefaultObjectsShouldBeInTheSameBucket() { 212 | // given 213 | final Connectivity connectivityOne = Connectivity.create(); 214 | final Connectivity connectivityTwo = Connectivity.create(); 215 | 216 | // when 217 | boolean hashCodesAreEqual = connectivityOne.hashCode() == connectivityTwo.hashCode(); 218 | 219 | // then 220 | assertThat(hashCodesAreEqual).isTrue(); 221 | } 222 | 223 | @Test public void shouldAppendUnknownTypeWhileFilteringNetworkTypesInsidePredicate() { 224 | // given 225 | int[] types = { ConnectivityManager.TYPE_MOBILE, ConnectivityManager.TYPE_WIFI }; 226 | int[] expectedOutputTypes = { 227 | ConnectivityManager.TYPE_MOBILE, ConnectivityManager.TYPE_WIFI, Connectivity.UNKNOWN_TYPE 228 | }; 229 | 230 | // when 231 | int[] outputTypes = ConnectivityPredicate.appendUnknownNetworkTypeToTypes(types); 232 | 233 | // then 234 | assertThat(outputTypes).isEqualTo(expectedOutputTypes); 235 | } 236 | 237 | @Test 238 | public void shouldAppendUnknownTypeWhileFilteringNetworkTypesInsidePredicateForEmptyArray() { 239 | // given 240 | int[] types = {}; 241 | int[] expectedOutputTypes = { Connectivity.UNKNOWN_TYPE }; 242 | 243 | // when 244 | int[] outputTypes = ConnectivityPredicate.appendUnknownNetworkTypeToTypes(types); 245 | 246 | // then 247 | assertThat(outputTypes).isEqualTo(expectedOutputTypes); 248 | } 249 | 250 | @Test public void shouldCreateConnectivityWithBuilder() { 251 | // given 252 | NetworkInfo.State state = NetworkInfo.State.CONNECTED; 253 | NetworkInfo.DetailedState detailedState = NetworkInfo.DetailedState.CONNECTED; 254 | int type = ConnectivityManager.TYPE_WIFI; 255 | int subType = ConnectivityManager.TYPE_WIMAX; 256 | String typeName = TYPE_NAME_WIFI; 257 | String subTypeName = "test subType"; 258 | String reason = "no reason"; 259 | String extraInfo = "extra info"; 260 | 261 | // when 262 | Connectivity connectivity = Connectivity.state(state) 263 | .detailedState(detailedState) 264 | .type(type) 265 | .subType(subType) 266 | .available(true) 267 | .failover(false) 268 | .roaming(true) 269 | .typeName(typeName) 270 | .subTypeName(subTypeName) 271 | .reason(reason) 272 | .extraInfo(extraInfo) 273 | .build(); 274 | 275 | // then 276 | assertThat(connectivity.state()).isEqualTo(state); 277 | assertThat(connectivity.detailedState()).isEqualTo(detailedState); 278 | assertThat(connectivity.type()).isEqualTo(type); 279 | assertThat(connectivity.subType()).isEqualTo(subType); 280 | assertThat(connectivity.available()).isTrue(); 281 | assertThat(connectivity.failover()).isFalse(); 282 | assertThat(connectivity.roaming()).isTrue(); 283 | assertThat(connectivity.typeName()).isEqualTo(typeName); 284 | assertThat(connectivity.subTypeName()).isEqualTo(subTypeName); 285 | assertThat(connectivity.reason()).isEqualTo(reason); 286 | assertThat(connectivity.extraInfo()).isEqualTo(extraInfo); 287 | } 288 | 289 | @Test public void connectivityShouldNotBeEqualToAnotherOne() { 290 | // given 291 | Connectivity connectivityOne = Connectivity.state(NetworkInfo.State.CONNECTED) 292 | .detailedState(NetworkInfo.DetailedState.CONNECTED) 293 | .type(ConnectivityManager.TYPE_WIFI) 294 | .subType(1) 295 | .available(true) 296 | .failover(true) 297 | .roaming(true) 298 | .typeName(TYPE_NAME_WIFI) 299 | .subTypeName("subtypeOne") 300 | .reason("reasonOne") 301 | .extraInfo("extraInfoOne") 302 | .build(); 303 | 304 | Connectivity connectivityTwo = Connectivity.state(NetworkInfo.State.DISCONNECTED) 305 | .detailedState(NetworkInfo.DetailedState.DISCONNECTED) 306 | .type(ConnectivityManager.TYPE_MOBILE) 307 | .subType(2) 308 | .available(false) 309 | .failover(false) 310 | .roaming(false) 311 | .typeName(TYPE_NAME_MOBILE) 312 | .subTypeName("subtypeTwo") 313 | .reason("reasonTwo") 314 | .extraInfo("extraInfoTwo") 315 | .build(); 316 | 317 | // when 318 | final boolean isAnotherConnectivityTheSame = connectivityOne.equals(connectivityTwo); 319 | 320 | // then 321 | assertThat(isAnotherConnectivityTheSame).isFalse(); 322 | } 323 | 324 | @Test public void shouldCreateDefaultConnectivityWhenConnectivityManagerIsNull() { 325 | // given 326 | final Context context = RuntimeEnvironment.getApplication().getApplicationContext(); 327 | final ConnectivityManager connectivityManager = null; 328 | 329 | // when 330 | @SuppressWarnings("ConstantConditions") 331 | Connectivity connectivity = Connectivity.create(context, connectivityManager); 332 | 333 | // then 334 | assertThat(connectivity.type()).isEqualTo(Connectivity.UNKNOWN_TYPE); 335 | assertThat(connectivity.subType()).isEqualTo(Connectivity.UNKNOWN_SUB_TYPE); 336 | assertThat(connectivity.state()).isEqualTo(NetworkInfo.State.DISCONNECTED); 337 | assertThat(connectivity.detailedState()).isEqualTo(NetworkInfo.DetailedState.IDLE); 338 | assertThat(connectivity.available()).isFalse(); 339 | assertThat(connectivity.failover()).isFalse(); 340 | assertThat(connectivity.roaming()).isFalse(); 341 | assertThat(connectivity.typeName()).isEqualTo(TYPE_NAME_NONE); 342 | assertThat(connectivity.subTypeName()).isEqualTo(TYPE_NAME_NONE); 343 | assertThat(connectivity.reason()).isEmpty(); 344 | assertThat(connectivity.extraInfo()).isEmpty(); 345 | } 346 | } 347 | -------------------------------------------------------------------------------- /library/src/test/java/com/github/pwittchen/reactivenetwork/library/rx2/PreconditionsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Piotr Wittchen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pwittchen.reactivenetwork.library.rx2; 17 | 18 | import org.junit.Test; 19 | import org.junit.runner.RunWith; 20 | import org.robolectric.RobolectricTestRunner; 21 | import org.robolectric.annotation.Config; 22 | 23 | import static com.google.common.truth.Truth.assertThat; 24 | 25 | @RunWith(RobolectricTestRunner.class) 26 | @SuppressWarnings("NullAway") public class PreconditionsTest { 27 | 28 | private static final String MSG_STRING_IS_NULL = "String is null"; 29 | private static final String MSG_VALUE_IS_NOT_GREATER_THAN_ZERO = "value is not greater than zero"; 30 | 31 | @Test @Config(sdk = 21) public void shouldBeAtLeastAndroidLollipop() { 32 | boolean isAtLeastAndroidLollipop = Preconditions.isAtLeastAndroidLollipop(); 33 | assertThat(isAtLeastAndroidLollipop).isTrue(); 34 | } 35 | 36 | @Test @Config(sdk = 22) public void shouldBeAtLeastAndroidLollipopForHigherApi() { 37 | boolean isAtLeastAndroidLollipop = Preconditions.isAtLeastAndroidLollipop(); 38 | assertThat(isAtLeastAndroidLollipop).isTrue(); 39 | } 40 | 41 | @Test @Config(sdk = 22) public void shouldNotBeAtLeastAndroidMarshmallowForLowerApi() { 42 | boolean isAtLeastAndroidMarshmallow = Preconditions.isAtLeastAndroidMarshmallow(); 43 | assertThat(isAtLeastAndroidMarshmallow).isFalse(); 44 | } 45 | 46 | @Test @Config(sdk = 23) public void shouldBeAtLeastAndroidMarshmallow() { 47 | boolean isAtLeastAndroidMarshmallow = Preconditions.isAtLeastAndroidMarshmallow(); 48 | assertThat(isAtLeastAndroidMarshmallow).isTrue(); 49 | } 50 | 51 | @Test(expected = IllegalArgumentException.class) 52 | public void shouldThrowAnExceptionWhenStringIsNull() { 53 | Preconditions.checkNotNullOrEmpty(null, MSG_STRING_IS_NULL); 54 | } 55 | 56 | @Test(expected = IllegalArgumentException.class) 57 | public void shouldThrowAnExceptionWhenStringIsEmpty() { 58 | Preconditions.checkNotNullOrEmpty("", MSG_STRING_IS_NULL); 59 | } 60 | 61 | @Test 62 | public void shouldNotThrowAnythingWhenStringIsNotEmpty() { 63 | Preconditions.checkNotNullOrEmpty("notEmpty", MSG_STRING_IS_NULL); 64 | } 65 | 66 | @Test(expected = IllegalArgumentException.class) 67 | public void shouldThrowAnExceptionWhenValueIsZero() { 68 | Preconditions.checkGreaterThanZero(0, MSG_VALUE_IS_NOT_GREATER_THAN_ZERO); 69 | } 70 | 71 | @Test(expected = IllegalArgumentException.class) 72 | public void shouldThrowAnExceptionWhenValueLowerThanZero() { 73 | Preconditions.checkGreaterThanZero(-1, MSG_VALUE_IS_NOT_GREATER_THAN_ZERO); 74 | } 75 | 76 | @Test 77 | public void shouldNotThrowAnythingWhenValueIsGreaterThanZero() { 78 | Preconditions.checkGreaterThanZero(1, MSG_VALUE_IS_NOT_GREATER_THAN_ZERO); 79 | } 80 | 81 | @Test(expected = IllegalArgumentException.class) 82 | public void shouldThrowAnExceptionWhenValueLowerThanZeroForGreaterOrEqualCheck() { 83 | Preconditions.checkGreaterOrEqualToZero(-1, MSG_VALUE_IS_NOT_GREATER_THAN_ZERO); 84 | } 85 | 86 | @Test 87 | public void shouldNotThrowAnythingWhenValueIsGreaterThanZeroForGreaterOrEqualCheck() { 88 | Preconditions.checkGreaterOrEqualToZero(1, MSG_VALUE_IS_NOT_GREATER_THAN_ZERO); 89 | } 90 | 91 | @Test 92 | public void shouldNotThrowAnythingWhenValueIsEqualToZero() { 93 | Preconditions.checkGreaterOrEqualToZero(0, MSG_VALUE_IS_NOT_GREATER_THAN_ZERO); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /library/src/test/java/com/github/pwittchen/reactivenetwork/library/rx2/ReactiveNetworkTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Piotr Wittchen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pwittchen.reactivenetwork.library.rx2; 17 | 18 | import android.app.Application; 19 | import android.content.Context; 20 | import android.net.NetworkInfo; 21 | import androidx.annotation.NonNull; 22 | import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings; 23 | import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingStrategy; 24 | import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.error.DefaultErrorHandler; 25 | import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.error.ErrorHandler; 26 | import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.strategy.SocketInternetObservingStrategy; 27 | import com.github.pwittchen.reactivenetwork.library.rx2.network.observing.NetworkObservingStrategy; 28 | import com.github.pwittchen.reactivenetwork.library.rx2.network.observing.strategy.LollipopNetworkObservingStrategy; 29 | import io.reactivex.Observable; 30 | import io.reactivex.Single; 31 | import java.lang.reflect.Method; 32 | import org.junit.Test; 33 | import org.junit.runner.RunWith; 34 | import org.robolectric.RobolectricTestRunner; 35 | import org.robolectric.RuntimeEnvironment; 36 | import org.robolectric.annotation.Config; 37 | import org.robolectric.shadows.ShadowConnectivityManager; 38 | 39 | import static com.google.common.truth.Truth.assertThat; 40 | 41 | @RunWith(RobolectricTestRunner.class) 42 | @Config(shadows = ShadowConnectivityManager.class) 43 | @SuppressWarnings("NullAway") public class ReactiveNetworkTest { 44 | 45 | private static final String TEST_VALID_HOST = "www.test.com"; 46 | private static final int TEST_VALID_PORT = 80; 47 | private static final int TEST_VALID_TIMEOUT = 1000; 48 | private static final int TEST_VALID_INTERVAL = 1000; 49 | private static final int TEST_VALID_INITIAL_INTERVAL = 1000; 50 | private static final int TEST_VALID_HTTP_RESPONSE = 204; 51 | 52 | @Test public void testReactiveNetworkObjectShouldNotBeNull() { 53 | // given 54 | ReactiveNetwork reactiveNetwork; 55 | 56 | // when 57 | reactiveNetwork = ReactiveNetwork.create(); 58 | 59 | // then 60 | assertThat(reactiveNetwork).isNotNull(); 61 | } 62 | 63 | @Test public void observeNetworkConnectivityShouldNotBeNull() { 64 | // given 65 | networkConnectivityObservableShouldNotBeNull(); 66 | } 67 | 68 | @Test @Config(sdk = 23) public void observeNetworkConnectivityShouldNotBeNullForMarshmallow() { 69 | // given 70 | networkConnectivityObservableShouldNotBeNull(); 71 | } 72 | 73 | @Test @Config(sdk = 21) public void observeNetworkConnectivityShouldNotBeNullForLollipop() { 74 | networkConnectivityObservableShouldNotBeNull(); 75 | } 76 | 77 | private void networkConnectivityObservableShouldNotBeNull() { 78 | // given 79 | final Application context = RuntimeEnvironment.getApplication(); 80 | //final ConnectivityManager connectivityManagerMock = (ConnectivityManager) context 81 | // .getSystemService(Context.CONNECTIVITY_SERVICE); 82 | // 83 | //shadowOf(connectivityManagerMock); 84 | 85 | // when 86 | Observable observable; 87 | observable = ReactiveNetwork.observeNetworkConnectivity(context); 88 | 89 | // then 90 | assertThat(observable).isNotNull(); 91 | } 92 | 93 | @Test public void observeNetworkConnectivityWithStrategyShouldNotBeNull() { 94 | // given 95 | final Application context = RuntimeEnvironment.getApplication(); 96 | 97 | NetworkObservingStrategy strategy = new LollipopNetworkObservingStrategy(); 98 | 99 | // when 100 | Observable observable; 101 | observable = ReactiveNetwork.observeNetworkConnectivity(context, strategy); 102 | 103 | // then 104 | assertThat(observable).isNotNull(); 105 | } 106 | 107 | @Test public void observeInternetConnectivityDefaultShouldNotBeNull() { 108 | // given 109 | Observable observable; 110 | 111 | // when 112 | observable = ReactiveNetwork.observeInternetConnectivity(); 113 | 114 | // then 115 | assertThat(observable).isNotNull(); 116 | } 117 | 118 | @Test public void observeNetworkConnectivityShouldBeConnectedOnStartWhenNetworkIsAvailable() { 119 | // given 120 | final Application context = RuntimeEnvironment.getApplication(); 121 | 122 | // when 123 | Connectivity connectivity = ReactiveNetwork.observeNetworkConnectivity(context).blockingFirst(); 124 | 125 | // then 126 | assertThat(connectivity.state()).isEqualTo(NetworkInfo.State.CONNECTED); 127 | } 128 | 129 | @Test(expected = IllegalArgumentException.class) 130 | public void observeNetworkConnectivityShouldThrowAnExceptionForNullContext() { 131 | // given 132 | final Context context = null; 133 | final NetworkObservingStrategy strategy = new LollipopNetworkObservingStrategy(); 134 | 135 | // when 136 | //noinspection ConstantConditions 137 | ReactiveNetwork.observeNetworkConnectivity(context, strategy); 138 | 139 | // then an exception is thrown 140 | } 141 | 142 | @Test(expected = IllegalArgumentException.class) 143 | public void observeNetworkConnectivityShouldThrowAnExceptionForNullStrategy() { 144 | // given 145 | final Context context = RuntimeEnvironment.application; 146 | final NetworkObservingStrategy strategy = null; 147 | 148 | // when 149 | ReactiveNetwork.observeNetworkConnectivity(context, strategy); 150 | 151 | // then an exception is thrown 152 | } 153 | 154 | @Test(expected = IllegalArgumentException.class) 155 | public void observeInternetConnectivityShouldThrowAnExceptionWhenStrategyIsNull() { 156 | // given 157 | final InternetObservingStrategy strategy = null; 158 | final ErrorHandler errorHandler = new DefaultErrorHandler(); 159 | 160 | // when 161 | //noinspection ConstantConditions 162 | ReactiveNetwork.observeInternetConnectivity(strategy, TEST_VALID_INITIAL_INTERVAL, 163 | TEST_VALID_INTERVAL, TEST_VALID_HOST, TEST_VALID_PORT, TEST_VALID_TIMEOUT, 164 | TEST_VALID_HTTP_RESPONSE, errorHandler); 165 | 166 | // then an exception is thrown 167 | } 168 | 169 | @Test public void observeInternetConnectivityShouldNotThrowAnExceptionWhenStrategyIsNotNull() { 170 | // given 171 | final InternetObservingStrategy strategy = new SocketInternetObservingStrategy(); 172 | final ErrorHandler errorHandler = new DefaultErrorHandler(); 173 | 174 | // when 175 | final Observable observable = 176 | ReactiveNetwork.observeInternetConnectivity(strategy, TEST_VALID_INITIAL_INTERVAL, 177 | TEST_VALID_INTERVAL, TEST_VALID_HOST, TEST_VALID_PORT, TEST_VALID_TIMEOUT, 178 | TEST_VALID_HTTP_RESPONSE, errorHandler); 179 | 180 | // then 181 | assertThat(observable).isNotNull(); 182 | } 183 | 184 | @Test(expected = IllegalArgumentException.class) 185 | public void checkInternetConnectivityShouldThrowAnExceptionWhenStrategyIsNull() { 186 | // given 187 | final ErrorHandler errorHandler = new DefaultErrorHandler(); 188 | 189 | // when 190 | ReactiveNetwork.checkInternetConnectivity(null, TEST_VALID_HOST, TEST_VALID_PORT, 191 | TEST_VALID_TIMEOUT, TEST_VALID_HTTP_RESPONSE, errorHandler); 192 | 193 | // then an exception is thrown 194 | } 195 | 196 | @Test public void checkInternetConnectivityShouldNotThrowAnExceptionWhenStrategyIsNotNull() { 197 | // given 198 | final InternetObservingStrategy strategy = new SocketInternetObservingStrategy(); 199 | final ErrorHandler errorHandler = new DefaultErrorHandler(); 200 | 201 | // when 202 | final Single single = 203 | ReactiveNetwork.checkInternetConnectivity(strategy, TEST_VALID_HOST, TEST_VALID_PORT, 204 | TEST_VALID_TIMEOUT, TEST_VALID_HTTP_RESPONSE, errorHandler); 205 | 206 | // then 207 | assertThat(single).isNotNull(); 208 | } 209 | 210 | @Test 211 | public void shouldObserveInternetConnectivityWithCustomSettings() { 212 | // given 213 | final int initialInterval = 1; 214 | final int interval = 2; 215 | final String host = "www.test.com"; 216 | int port = 90; 217 | int timeout = 3; 218 | ErrorHandler testErrorHandler = createTestErrorHandler(); 219 | InternetObservingStrategy strategy = createTestInternetObservingStrategy(); 220 | 221 | // when 222 | InternetObservingSettings settings = InternetObservingSettings.builder() 223 | .initialInterval(initialInterval) 224 | .interval(interval) 225 | .host(host) 226 | .port(port) 227 | .timeout(timeout) 228 | .errorHandler(testErrorHandler) 229 | .strategy(strategy) 230 | .build(); 231 | 232 | // then 233 | Observable observable = ReactiveNetwork.observeInternetConnectivity(settings); 234 | assertThat(observable).isNotNull(); 235 | } 236 | 237 | @Test 238 | public void shouldCheckInternetConnectivityWithCustomSettings() { 239 | // given 240 | final int initialInterval = 1; 241 | final int interval = 2; 242 | final String host = "www.test.com"; 243 | int port = 90; 244 | int timeout = 3; 245 | int httpResponse = 200; 246 | ErrorHandler testErrorHandler = createTestErrorHandler(); 247 | InternetObservingStrategy strategy = createTestInternetObservingStrategy(); 248 | 249 | // when 250 | InternetObservingSettings settings = InternetObservingSettings.builder() 251 | .initialInterval(initialInterval) 252 | .interval(interval) 253 | .host(host) 254 | .port(port) 255 | .timeout(timeout) 256 | .httpResponse(httpResponse) 257 | .errorHandler(testErrorHandler) 258 | .strategy(strategy) 259 | .build(); 260 | 261 | // then 262 | Single single = ReactiveNetwork.checkInternetConnectivity(settings); 263 | assertThat(single).isNotNull(); 264 | } 265 | 266 | @NonNull private InternetObservingStrategy createTestInternetObservingStrategy() { 267 | return new InternetObservingStrategy() { 268 | @Override public Observable observeInternetConnectivity(int initialIntervalInMs, 269 | int intervalInMs, String host, int port, int timeoutInMs, int httpResponse, 270 | ErrorHandler errorHandler) { 271 | return Observable.empty(); 272 | } 273 | 274 | @Override public Single checkInternetConnectivity(String host, int port, 275 | int timeoutInMs, int httpResponse, ErrorHandler errorHandler) { 276 | return Single.fromCallable(() -> true); 277 | } 278 | 279 | @Override public String getDefaultPingHost() { 280 | return null; 281 | } 282 | }; 283 | } 284 | 285 | @NonNull private ErrorHandler createTestErrorHandler() { 286 | return (exception, message) -> { 287 | }; 288 | } 289 | 290 | @Test 291 | public void shouldHaveJustSevenMethodsInPublicApi() { 292 | // given 293 | Class clazz = ReactiveNetwork.create().getClass(); 294 | final int predefinedNumberOfMethods = 9; 295 | final int publicMethodsInApi = 7; // this number can be increased only in reasonable case 296 | 297 | // when 298 | Method[] methods = clazz.getMethods(); 299 | 300 | // then 301 | assertThat(methods.length).isEqualTo(predefinedNumberOfMethods + publicMethodsInApi); 302 | } 303 | } 304 | -------------------------------------------------------------------------------- /library/src/test/java/com/github/pwittchen/reactivenetwork/library/rx2/internet/observing/InternetObservingSettingsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 Piotr Wittchen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pwittchen.reactivenetwork.library.rx2.internet.observing; 17 | 18 | import androidx.annotation.NonNull; 19 | import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.error.DefaultErrorHandler; 20 | import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.error.ErrorHandler; 21 | import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.strategy.SocketInternetObservingStrategy; 22 | import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.strategy.WalledGardenInternetObservingStrategy; 23 | import org.junit.Test; 24 | import org.junit.runner.RunWith; 25 | import org.robolectric.RobolectricTestRunner; 26 | 27 | import static com.google.common.truth.Truth.assertThat; 28 | 29 | @RunWith(RobolectricTestRunner.class) 30 | public class InternetObservingSettingsTest { 31 | 32 | @Test 33 | public void shouldCreateSettings() { 34 | // when 35 | InternetObservingSettings settings = InternetObservingSettings.create(); 36 | 37 | // then 38 | assertThat(settings).isNotNull(); 39 | } 40 | 41 | @Test 42 | public void shouldBuildSettingsWithDefaultValues() { 43 | // when 44 | InternetObservingSettings settings = InternetObservingSettings.create(); 45 | 46 | // then 47 | assertThat(settings.initialInterval()).isEqualTo(0); 48 | assertThat(settings.interval()).isEqualTo(2000); 49 | assertThat(settings.host()).isEqualTo("http://clients3.google.com/generate_204"); 50 | assertThat(settings.port()).isEqualTo(80); 51 | assertThat(settings.timeout()).isEqualTo(2000); 52 | assertThat(settings.httpResponse()).isEqualTo(204); 53 | assertThat(settings.errorHandler()).isInstanceOf(DefaultErrorHandler.class); 54 | assertThat(settings.strategy()).isInstanceOf(WalledGardenInternetObservingStrategy.class); 55 | } 56 | 57 | @Test 58 | public void shouldBuildSettings() { 59 | // given 60 | final int initialInterval = 1; 61 | final int interval = 2; 62 | final String host = "www.test.com"; 63 | int port = 90; 64 | int timeout = 3; 65 | int httpResponse = 200; 66 | ErrorHandler testErrorHandler = createTestErrorHandler(); 67 | SocketInternetObservingStrategy strategy = new SocketInternetObservingStrategy(); 68 | 69 | // when 70 | InternetObservingSettings settings = InternetObservingSettings.builder() 71 | .initialInterval(initialInterval) 72 | .interval(interval) 73 | .host(host) 74 | .port(port) 75 | .timeout(timeout) 76 | .httpResponse(httpResponse) 77 | .errorHandler(testErrorHandler) 78 | .strategy(strategy) 79 | .build(); 80 | 81 | // then 82 | assertThat(settings.initialInterval()).isEqualTo(initialInterval); 83 | assertThat(settings.interval()).isEqualTo(interval); 84 | assertThat(settings.host()).isEqualTo(host); 85 | assertThat(settings.port()).isEqualTo(port); 86 | assertThat(settings.timeout()).isEqualTo(timeout); 87 | assertThat(settings.httpResponse()).isEqualTo(httpResponse); 88 | assertThat(settings.errorHandler()).isNotNull(); 89 | assertThat(settings.errorHandler()).isNotInstanceOf(DefaultErrorHandler.class); 90 | assertThat(settings.strategy()).isInstanceOf(SocketInternetObservingStrategy.class); 91 | } 92 | 93 | @NonNull private ErrorHandler createTestErrorHandler() { 94 | return (exception, message) -> { }; 95 | } 96 | } -------------------------------------------------------------------------------- /library/src/test/java/com/github/pwittchen/reactivenetwork/library/rx2/internet/observing/error/DefaultErrorHandlerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Piotr Wittchen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.error; 17 | 18 | import org.junit.Rule; 19 | import org.junit.Test; 20 | import org.junit.runner.RunWith; 21 | import org.mockito.Spy; 22 | import org.mockito.junit.MockitoJUnit; 23 | import org.mockito.junit.MockitoRule; 24 | import org.robolectric.RobolectricTestRunner; 25 | 26 | import static org.mockito.Mockito.times; 27 | import static org.mockito.Mockito.verify; 28 | 29 | @RunWith(RobolectricTestRunner.class) 30 | @SuppressWarnings("NullAway") public class DefaultErrorHandlerTest { 31 | 32 | @Rule public MockitoRule rule = MockitoJUnit.rule(); 33 | @Spy private DefaultErrorHandler handler = new DefaultErrorHandler(); 34 | 35 | @Test public void shouldHandleErrorDuringClosingSocket() { 36 | // given 37 | final String errorMsg = "Could not close the socket"; 38 | final Exception exception = new Exception(errorMsg); 39 | 40 | // when 41 | handler.handleError(exception, errorMsg); 42 | 43 | // then 44 | verify(handler, times(1)).handleError(exception, errorMsg); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /library/src/test/java/com/github/pwittchen/reactivenetwork/library/rx2/internet/observing/strategy/SocketInternetObservingStrategyTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Piotr Wittchen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.strategy; 17 | 18 | import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.error.ErrorHandler; 19 | import io.reactivex.Observable; 20 | import io.reactivex.Single; 21 | import java.io.IOException; 22 | import java.net.InetSocketAddress; 23 | import java.net.Socket; 24 | import org.junit.Rule; 25 | import org.junit.Test; 26 | import org.junit.runner.RunWith; 27 | import org.mockito.Mock; 28 | import org.mockito.Spy; 29 | import org.mockito.junit.MockitoJUnit; 30 | import org.mockito.junit.MockitoRule; 31 | import org.robolectric.RobolectricTestRunner; 32 | 33 | import static com.google.common.truth.Truth.assertThat; 34 | import static org.mockito.Mockito.doThrow; 35 | import static org.mockito.Mockito.times; 36 | import static org.mockito.Mockito.verify; 37 | import static org.mockito.Mockito.when; 38 | 39 | @RunWith(RobolectricTestRunner.class) 40 | @SuppressWarnings({ "PMD", "NullAway" }) public class SocketInternetObservingStrategyTest { 41 | 42 | private static final int INITIAL_INTERVAL_IN_MS = 0; 43 | private static final int INTERVAL_IN_MS = 2000; 44 | private static final int PORT = 80; 45 | private static final int TIMEOUT_IN_MS = 30; 46 | private static final int HTTP_RESPONSE = 204; 47 | private static final String HOST_WITH_HTTP = "http://www.website.com"; 48 | private static final String HOST_WITHOUT_HTTP = "www.website.com"; 49 | 50 | @Rule public MockitoRule rule = MockitoJUnit.rule(); 51 | @Spy private SocketInternetObservingStrategy strategy; 52 | @Mock private ErrorHandler errorHandler; 53 | @Mock private Socket socket; 54 | 55 | private String getHost() { 56 | return strategy.getDefaultPingHost(); 57 | } 58 | 59 | @Test public void shouldBeConnectedToTheInternet() { 60 | // given 61 | when(strategy.isConnected(getHost(), PORT, TIMEOUT_IN_MS, errorHandler)).thenReturn(true); 62 | 63 | // when 64 | final Observable observable = 65 | strategy.observeInternetConnectivity(INITIAL_INTERVAL_IN_MS, INTERVAL_IN_MS, getHost(), 66 | PORT, TIMEOUT_IN_MS, HTTP_RESPONSE, errorHandler); 67 | 68 | boolean isConnected = observable.blockingFirst(); 69 | 70 | // then 71 | assertThat(isConnected).isTrue(); 72 | } 73 | 74 | @Test public void shouldNotBeConnectedToTheInternet() { 75 | // given 76 | when(strategy.isConnected(getHost(), PORT, TIMEOUT_IN_MS, errorHandler)).thenReturn(false); 77 | 78 | // when 79 | final Observable observable = 80 | strategy.observeInternetConnectivity(INITIAL_INTERVAL_IN_MS, INTERVAL_IN_MS, getHost(), 81 | PORT, TIMEOUT_IN_MS, HTTP_RESPONSE, errorHandler); 82 | 83 | boolean isConnected = observable.blockingFirst(); 84 | 85 | // then 86 | assertThat(isConnected).isFalse(); 87 | } 88 | 89 | @Test public void shouldNotBeConnectedToTheInternetWhenSocketThrowsAnExceptionOnConnect() 90 | throws IOException { 91 | // given 92 | final InetSocketAddress address = new InetSocketAddress(getHost(), PORT); 93 | doThrow(new IOException()).when(socket).connect(address, TIMEOUT_IN_MS); 94 | 95 | // when 96 | final boolean isConnected = 97 | strategy.isConnected(socket, getHost(), PORT, TIMEOUT_IN_MS, errorHandler); 98 | 99 | // then 100 | assertThat(isConnected).isFalse(); 101 | } 102 | 103 | @Test public void shouldHandleAnExceptionThrownDuringClosingTheSocket() throws IOException { 104 | // given 105 | final String errorMsg = "Could not close the socket"; 106 | final IOException givenException = new IOException(errorMsg); 107 | doThrow(givenException).when(socket).close(); 108 | 109 | // when 110 | strategy.isConnected(socket, getHost(), PORT, TIMEOUT_IN_MS, errorHandler); 111 | 112 | // then 113 | verify(errorHandler, times(1)).handleError(givenException, errorMsg); 114 | } 115 | 116 | @Test public void shouldBeConnectedToTheInternetViaSingle() { 117 | // given 118 | when(strategy.isConnected(getHost(), PORT, TIMEOUT_IN_MS, errorHandler)).thenReturn(true); 119 | 120 | // when 121 | final Single observable = 122 | strategy.checkInternetConnectivity(getHost(), PORT, TIMEOUT_IN_MS, HTTP_RESPONSE, 123 | errorHandler); 124 | 125 | boolean isConnected = observable.blockingGet(); 126 | 127 | // then 128 | assertThat(isConnected).isTrue(); 129 | } 130 | 131 | @Test public void shouldNotBeConnectedToTheInternetViaSingle() { 132 | // given 133 | when(strategy.isConnected(getHost(), PORT, TIMEOUT_IN_MS, errorHandler)).thenReturn(false); 134 | 135 | // when 136 | final Single observable = 137 | strategy.checkInternetConnectivity(getHost(), PORT, TIMEOUT_IN_MS, HTTP_RESPONSE, 138 | errorHandler); 139 | 140 | boolean isConnected = observable.blockingGet(); 141 | 142 | // then 143 | assertThat(isConnected).isFalse(); 144 | } 145 | 146 | @Test public void shouldNotTransformHost() { 147 | // when 148 | String transformedHost = strategy.adjustHost(HOST_WITHOUT_HTTP); 149 | 150 | // then 151 | assertThat(transformedHost).isEqualTo(HOST_WITHOUT_HTTP); 152 | } 153 | 154 | @Test public void shouldRemoveHttpProtocolFromHost() { 155 | // when 156 | String transformedHost = strategy.adjustHost(HOST_WITH_HTTP); 157 | 158 | // then 159 | assertThat(transformedHost).isEqualTo(HOST_WITHOUT_HTTP); 160 | } 161 | 162 | @Test public void shouldRemoveHttpsProtocolFromHost() { 163 | // when 164 | String transformedHost = strategy.adjustHost(HOST_WITH_HTTP); 165 | 166 | // then 167 | assertThat(transformedHost).isEqualTo(HOST_WITHOUT_HTTP); 168 | } 169 | 170 | @Test @SuppressWarnings("CheckReturnValue") 171 | public void shouldAdjustHostDuringCheckingConnectivity() { 172 | // given 173 | final String host = getHost(); 174 | when(strategy.isConnected(host, PORT, TIMEOUT_IN_MS, errorHandler)).thenReturn(true); 175 | 176 | // when 177 | strategy.observeInternetConnectivity(INITIAL_INTERVAL_IN_MS, INTERVAL_IN_MS, host, PORT, 178 | TIMEOUT_IN_MS, HTTP_RESPONSE, errorHandler).blockingFirst(); 179 | 180 | // then 181 | verify(strategy).adjustHost(host); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /library/src/test/java/com/github/pwittchen/reactivenetwork/library/rx2/internet/observing/strategy/WalledGardenInternetObservingStrategyTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Piotr Wittchen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.strategy; 17 | 18 | import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.error.ErrorHandler; 19 | import io.reactivex.Observable; 20 | import io.reactivex.Single; 21 | import java.io.IOException; 22 | import java.net.HttpURLConnection; 23 | import org.junit.Rule; 24 | import org.junit.Test; 25 | import org.junit.runner.RunWith; 26 | import org.mockito.Mock; 27 | import org.mockito.Spy; 28 | import org.mockito.junit.MockitoJUnit; 29 | import org.mockito.junit.MockitoRule; 30 | import org.robolectric.RobolectricTestRunner; 31 | import org.robolectric.annotation.Config; 32 | 33 | import static com.google.common.truth.Truth.assertThat; 34 | import static org.mockito.Mockito.verify; 35 | import static org.mockito.Mockito.when; 36 | 37 | @Config(manifest = Config.NONE) 38 | @RunWith(RobolectricTestRunner.class) 39 | @SuppressWarnings("NullAway") public class WalledGardenInternetObservingStrategyTest { 40 | 41 | private static final int INITIAL_INTERVAL_IN_MS = 0; 42 | private static final int INTERVAL_IN_MS = 2000; 43 | private static final int PORT = 80; 44 | private static final int TIMEOUT_IN_MS = 30; 45 | private static final int HTTP_RESPONSE = 204; 46 | private static final String HOST_WITH_HTTP = "http://www.website.com"; 47 | private static final String HOST_WITH_HTTPS = "https://www.website.com"; 48 | private static final String HOST_WITHOUT_HTTPS = "www.website.com"; 49 | 50 | @Rule public MockitoRule rule = MockitoJUnit.rule(); 51 | @Mock private ErrorHandler errorHandler; 52 | @Spy private WalledGardenInternetObservingStrategy strategy; 53 | 54 | private String getHost() { 55 | return strategy.getDefaultPingHost(); 56 | } 57 | 58 | @Test public void shouldBeConnectedToTheInternet() { 59 | // given 60 | final ErrorHandler errorHandlerStub = createErrorHandlerStub(); 61 | when(strategy.isConnected(getHost(), PORT, TIMEOUT_IN_MS, HTTP_RESPONSE, 62 | errorHandlerStub)).thenReturn(true); 63 | 64 | // when 65 | final Observable observable = 66 | strategy.observeInternetConnectivity(INITIAL_INTERVAL_IN_MS, INTERVAL_IN_MS, getHost(), 67 | PORT, TIMEOUT_IN_MS, HTTP_RESPONSE, errorHandlerStub); 68 | 69 | boolean isConnected = observable.blockingFirst(); 70 | 71 | // then 72 | assertThat(isConnected).isTrue(); 73 | } 74 | 75 | @Test public void shouldNotBeConnectedToTheInternet() { 76 | // given 77 | final ErrorHandler errorHandlerStub = createErrorHandlerStub(); 78 | when(strategy.isConnected(getHost(), PORT, TIMEOUT_IN_MS, HTTP_RESPONSE, 79 | errorHandlerStub)).thenReturn(false); 80 | 81 | // when 82 | final Observable observable = 83 | strategy.observeInternetConnectivity(INITIAL_INTERVAL_IN_MS, INTERVAL_IN_MS, getHost(), 84 | PORT, TIMEOUT_IN_MS, HTTP_RESPONSE, errorHandlerStub); 85 | 86 | boolean isConnected = observable.blockingFirst(); 87 | 88 | // then 89 | assertThat(isConnected).isFalse(); 90 | } 91 | 92 | @Test public void shouldBeConnectedToTheInternetViaSingle() { 93 | // given 94 | final ErrorHandler errorHandlerStub = createErrorHandlerStub(); 95 | when(strategy.isConnected(getHost(), PORT, TIMEOUT_IN_MS, HTTP_RESPONSE, 96 | errorHandlerStub)).thenReturn(true); 97 | 98 | // when 99 | final Single observable = 100 | strategy.checkInternetConnectivity(getHost(), PORT, TIMEOUT_IN_MS, HTTP_RESPONSE, 101 | errorHandlerStub); 102 | 103 | boolean isConnected = observable.blockingGet(); 104 | 105 | // then 106 | assertThat(isConnected).isTrue(); 107 | } 108 | 109 | @Test public void shouldNotBeConnectedToTheInternetViaSingle() { 110 | // given 111 | final ErrorHandler errorHandlerStub = createErrorHandlerStub(); 112 | when(strategy.isConnected(getHost(), PORT, TIMEOUT_IN_MS, HTTP_RESPONSE, 113 | errorHandlerStub)).thenReturn(false); 114 | 115 | // when 116 | final Single observable = 117 | strategy.checkInternetConnectivity(getHost(), PORT, TIMEOUT_IN_MS, HTTP_RESPONSE, 118 | errorHandlerStub); 119 | 120 | boolean isConnected = observable.blockingGet(); 121 | 122 | // then 123 | assertThat(isConnected).isFalse(); 124 | } 125 | 126 | @Test public void shouldCreateHttpUrlConnection() throws IOException { 127 | // given 128 | final String parsedDefaultHost = "clients3.google.com"; 129 | 130 | // when 131 | HttpURLConnection connection = strategy.createHttpUrlConnection(getHost(), PORT, TIMEOUT_IN_MS); 132 | 133 | // then 134 | assertThat(connection).isNotNull(); 135 | assertThat(connection.getURL().getHost()).isEqualTo(parsedDefaultHost); 136 | assertThat(connection.getURL().getPort()).isEqualTo(PORT); 137 | assertThat(connection.getConnectTimeout()).isEqualTo(TIMEOUT_IN_MS); 138 | assertThat(connection.getReadTimeout()).isEqualTo(TIMEOUT_IN_MS); 139 | assertThat(connection.getInstanceFollowRedirects()).isFalse(); 140 | assertThat(connection.getUseCaches()).isFalse(); 141 | } 142 | 143 | @Test public void shouldHandleAnExceptionWhileCreatingHttpUrlConnection() throws IOException { 144 | // given 145 | final String errorMsg = "Could not establish connection with WalledGardenStrategy"; 146 | final IOException givenException = new IOException(errorMsg); 147 | when(strategy.createHttpUrlConnection(HOST_WITH_HTTP, PORT, TIMEOUT_IN_MS)).thenThrow( 148 | givenException); 149 | 150 | // when 151 | strategy.isConnected(HOST_WITH_HTTP, PORT, TIMEOUT_IN_MS, HTTP_RESPONSE, errorHandler); 152 | 153 | // then 154 | verify(errorHandler).handleError(givenException, errorMsg); 155 | } 156 | 157 | @Test public void shouldCreateHttpsUrlConnection() throws IOException { 158 | // given 159 | final String parsedDefaultHost = "clients3.google.com"; 160 | 161 | // when 162 | HttpURLConnection connection = 163 | strategy.createHttpsUrlConnection("https://clients3.google.com", PORT, TIMEOUT_IN_MS); 164 | 165 | // then 166 | assertThat(connection).isNotNull(); 167 | assertThat(connection.getURL().getHost()).isEqualTo(parsedDefaultHost); 168 | assertThat(connection.getURL().getPort()).isEqualTo(PORT); 169 | assertThat(connection.getConnectTimeout()).isEqualTo(TIMEOUT_IN_MS); 170 | assertThat(connection.getReadTimeout()).isEqualTo(TIMEOUT_IN_MS); 171 | assertThat(connection.getInstanceFollowRedirects()).isFalse(); 172 | assertThat(connection.getUseCaches()).isFalse(); 173 | } 174 | 175 | @Test public void shouldHandleAnExceptionWhileCreatingHttpsUrlConnection() throws IOException { 176 | // given 177 | final String errorMsg = "Could not establish connection with WalledGardenStrategy"; 178 | final IOException givenException = new IOException(errorMsg); 179 | final String host = "https://clients3.google.com"; 180 | when(strategy.createHttpsUrlConnection(host, PORT, TIMEOUT_IN_MS)).thenThrow( 181 | givenException); 182 | 183 | // when 184 | strategy.isConnected(host, PORT, TIMEOUT_IN_MS, HTTP_RESPONSE, errorHandler); 185 | 186 | // then 187 | verify(errorHandler).handleError(givenException, errorMsg); 188 | } 189 | 190 | @Test public void shouldNotTransformHttpHost() { 191 | // when 192 | String transformedHost = strategy.adjustHost(HOST_WITH_HTTPS); 193 | 194 | // then 195 | assertThat(transformedHost).isEqualTo(HOST_WITH_HTTPS); 196 | } 197 | 198 | @Test public void shouldNotTransformHttpsHost() { 199 | // when 200 | String transformedHost = strategy.adjustHost(HOST_WITH_HTTPS); 201 | 202 | // then 203 | assertThat(transformedHost).isEqualTo(HOST_WITH_HTTPS); 204 | } 205 | 206 | @Test public void shouldAddHttpsProtocolToHost() { 207 | // when 208 | String transformedHost = strategy.adjustHost(HOST_WITHOUT_HTTPS); 209 | 210 | // then 211 | assertThat(transformedHost).isEqualTo(HOST_WITH_HTTPS); 212 | } 213 | 214 | @Test @SuppressWarnings("CheckReturnValue") 215 | public void shouldAdjustHostWhileCheckingConnectivity() { 216 | // given 217 | final ErrorHandler errorHandlerStub = createErrorHandlerStub(); 218 | final String host = getHost(); 219 | when(strategy.isConnected(host, PORT, TIMEOUT_IN_MS, HTTP_RESPONSE, 220 | errorHandlerStub)).thenReturn( 221 | true); 222 | 223 | // when 224 | strategy.observeInternetConnectivity(INITIAL_INTERVAL_IN_MS, INTERVAL_IN_MS, host, PORT, 225 | TIMEOUT_IN_MS, HTTP_RESPONSE, errorHandlerStub).blockingFirst(); 226 | 227 | // then 228 | verify(strategy).adjustHost(host); 229 | } 230 | 231 | private ErrorHandler createErrorHandlerStub() { 232 | return new ErrorHandler() { 233 | @Override public void handleError(Exception exception, String message) { 234 | } 235 | }; 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /library/src/test/java/com/github/pwittchen/reactivenetwork/library/rx2/network/observing/NetworkObservingStrategyTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Piotr Wittchen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pwittchen.reactivenetwork.library.rx2.network.observing; 17 | 18 | import android.annotation.SuppressLint; 19 | import android.content.Context; 20 | import android.net.NetworkInfo; 21 | import com.github.pwittchen.reactivenetwork.library.rx2.Connectivity; 22 | import com.github.pwittchen.reactivenetwork.library.rx2.network.observing.strategy.LollipopNetworkObservingStrategy; 23 | import com.github.pwittchen.reactivenetwork.library.rx2.network.observing.strategy.PreLollipopNetworkObservingStrategy; 24 | import io.reactivex.disposables.Disposable; 25 | import org.junit.Test; 26 | import org.junit.runner.RunWith; 27 | import org.robolectric.RobolectricTestRunner; 28 | import org.robolectric.RuntimeEnvironment; 29 | import static com.google.common.truth.Truth.assertThat; 30 | 31 | @RunWith(RobolectricTestRunner.class) 32 | @SuppressWarnings("NullAway") public class NetworkObservingStrategyTest { 33 | 34 | @Test public void lollipopObserveNetworkConnectivityShouldBeConnectedWhenNetworkIsAvailable() { 35 | // given 36 | final NetworkObservingStrategy strategy = new LollipopNetworkObservingStrategy(); 37 | 38 | // when 39 | assertThatIsConnected(strategy); 40 | } 41 | 42 | @Test public void preLollipopObserveNetworkConnectivityShouldBeConnectedWhenNetworkIsAvailable() { 43 | // given 44 | final NetworkObservingStrategy strategy = new PreLollipopNetworkObservingStrategy(); 45 | 46 | // when 47 | assertThatIsConnected(strategy); 48 | } 49 | 50 | @SuppressLint("CheckResult") 51 | private void assertThatIsConnected(NetworkObservingStrategy strategy) { 52 | // given 53 | final Context context = RuntimeEnvironment.getApplication().getApplicationContext(); 54 | 55 | //when 56 | Disposable disposable = strategy.observeNetworkConnectivity(context) 57 | .subscribe( 58 | (@SuppressLint("CheckResult") Connectivity connectivity) -> { 59 | // then 60 | assertThat(connectivity.state()).isEqualTo(NetworkInfo.State.CONNECTED); 61 | }); 62 | 63 | disposable.dispose(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /library/src/test/java/com/github/pwittchen/reactivenetwork/library/rx2/network/observing/strategy/LollipopNetworkObservingStrategyTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Piotr Wittchen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pwittchen.reactivenetwork.library.rx2.network.observing.strategy; 17 | 18 | import android.app.Application; 19 | import android.content.Context; 20 | import android.net.NetworkInfo; 21 | import com.github.pwittchen.reactivenetwork.library.rx2.Connectivity; 22 | import com.github.pwittchen.reactivenetwork.library.rx2.network.observing.NetworkObservingStrategy; 23 | import io.reactivex.Observable; 24 | import io.reactivex.observers.TestObserver; 25 | import org.junit.Rule; 26 | import org.junit.Test; 27 | import org.junit.runner.RunWith; 28 | import org.mockito.Spy; 29 | import org.mockito.junit.MockitoJUnit; 30 | import org.mockito.junit.MockitoRule; 31 | import org.robolectric.RobolectricTestRunner; 32 | import org.robolectric.RuntimeEnvironment; 33 | 34 | import static com.google.common.truth.Truth.assertThat; 35 | import static org.mockito.Mockito.times; 36 | import static org.mockito.Mockito.verify; 37 | 38 | @RunWith(RobolectricTestRunner.class) 39 | @SuppressWarnings("NullAway") public class LollipopNetworkObservingStrategyTest { 40 | 41 | @Rule public MockitoRule rule = MockitoJUnit.rule(); 42 | @Spy private NetworkObservingStrategy strategy = new LollipopNetworkObservingStrategy(); 43 | 44 | @Test public void shouldObserveConnectivity() { 45 | // given 46 | final NetworkObservingStrategy strategy = new LollipopNetworkObservingStrategy(); 47 | final Context context = RuntimeEnvironment.getApplication().getApplicationContext(); 48 | 49 | // when 50 | Connectivity connectivity = strategy.observeNetworkConnectivity(context).blockingFirst(); 51 | 52 | // then 53 | assertThat(connectivity.state()).isEqualTo(NetworkInfo.State.CONNECTED); 54 | } 55 | 56 | @Test public void shouldStopObservingConnectivity() { 57 | // given 58 | final NetworkObservingStrategy strategy = new LollipopNetworkObservingStrategy(); 59 | final Application context = RuntimeEnvironment.getApplication(); 60 | final Observable observable = strategy.observeNetworkConnectivity(context); 61 | final TestObserver observer = new TestObserver<>(); 62 | 63 | // when 64 | observable.subscribe(observer); 65 | observer.dispose(); 66 | 67 | // then 68 | assertThat(observer.isDisposed()).isTrue(); 69 | } 70 | 71 | @Test public void shouldCallOnError() { 72 | // given 73 | final String message = "error message"; 74 | final Exception exception = new Exception(); 75 | 76 | // when 77 | strategy.onError(message, exception); 78 | 79 | // then 80 | verify(strategy, times(1)).onError(message, exception); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /library/src/test/java/com/github/pwittchen/reactivenetwork/library/rx2/network/observing/strategy/PreLollipopNetworkObservingStrategyTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Piotr Wittchen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.pwittchen.reactivenetwork.library.rx2.network.observing.strategy; 17 | 18 | import android.annotation.SuppressLint; 19 | import android.app.Application; 20 | import android.content.BroadcastReceiver; 21 | import android.content.Context; 22 | import android.net.NetworkInfo; 23 | import com.github.pwittchen.reactivenetwork.library.rx2.Connectivity; 24 | import com.github.pwittchen.reactivenetwork.library.rx2.network.observing.NetworkObservingStrategy; 25 | import io.reactivex.Observable; 26 | import io.reactivex.observers.TestObserver; 27 | import org.junit.Rule; 28 | import org.junit.Test; 29 | import org.junit.runner.RunWith; 30 | import org.mockito.Mock; 31 | import org.mockito.Spy; 32 | import org.mockito.junit.MockitoJUnit; 33 | import org.mockito.junit.MockitoRule; 34 | import org.robolectric.RobolectricTestRunner; 35 | import org.robolectric.RuntimeEnvironment; 36 | import static com.google.common.truth.Truth.assertThat; 37 | import static org.mockito.ArgumentMatchers.any; 38 | import static org.mockito.ArgumentMatchers.eq; 39 | import static org.mockito.Mockito.spy; 40 | import static org.mockito.Mockito.times; 41 | import static org.mockito.Mockito.verify; 42 | 43 | // We are suppressing PMD here because we want static imports in unit tests 44 | @RunWith(RobolectricTestRunner.class) 45 | @SuppressWarnings({ "PMD", "NullAway" }) public class PreLollipopNetworkObservingStrategyTest { 46 | 47 | @Rule public MockitoRule rule = MockitoJUnit.rule(); 48 | @Spy private PreLollipopNetworkObservingStrategy strategy = 49 | new PreLollipopNetworkObservingStrategy(); 50 | @Mock private BroadcastReceiver broadcastReceiver; 51 | 52 | @SuppressLint("CheckResult") @Test @SuppressWarnings("CheckReturnValue") 53 | public void shouldObserveConnectivity() { 54 | // given 55 | final NetworkObservingStrategy strategy = new PreLollipopNetworkObservingStrategy(); 56 | final Context context = RuntimeEnvironment.application.getApplicationContext(); 57 | 58 | // when 59 | strategy.observeNetworkConnectivity(context).subscribe(connectivity -> { 60 | // then 61 | assertThat(connectivity.state()).isEqualTo(NetworkInfo.State.CONNECTED); 62 | }); 63 | } 64 | 65 | @Test public void shouldStopObservingConnectivity() { 66 | // given 67 | final NetworkObservingStrategy strategy = new PreLollipopNetworkObservingStrategy(); 68 | final Context context = RuntimeEnvironment.application.getApplicationContext(); 69 | final Observable observable = strategy.observeNetworkConnectivity(context); 70 | final TestObserver observer = new TestObserver<>(); 71 | 72 | // when 73 | observable.subscribe(observer); 74 | observer.dispose(); 75 | 76 | // then 77 | assertThat(observer.isDisposed()).isTrue(); 78 | } 79 | 80 | @Test public void shouldCallOnError() { 81 | // given 82 | final String message = "error message"; 83 | final Exception exception = new Exception(); 84 | 85 | // when 86 | strategy.onError(message, exception); 87 | 88 | // then 89 | verify(strategy, times(1)).onError(message, exception); 90 | } 91 | 92 | @Test public void shouldTryToUnregisterReceiver() { 93 | // given 94 | final PreLollipopNetworkObservingStrategy strategy = new PreLollipopNetworkObservingStrategy(); 95 | final Application context = spy(RuntimeEnvironment.getApplication()); 96 | 97 | // when 98 | strategy.tryToUnregisterReceiver(context, broadcastReceiver); 99 | 100 | // then 101 | verify(context).unregisterReceiver(broadcastReceiver); 102 | } 103 | 104 | @Test public void shouldTryToUnregisterReceiverAfterDispose() { 105 | // given 106 | final Context context = RuntimeEnvironment.getApplication().getApplicationContext(); 107 | final TestObserver observer = new TestObserver<>(); 108 | 109 | // when 110 | strategy.observeNetworkConnectivity(context).subscribe(observer); 111 | observer.dispose(); 112 | 113 | // then 114 | verify(strategy).tryToUnregisterReceiver(eq(context), any(BroadcastReceiver.class)); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /library/src/test/resources/robolectric.properties: -------------------------------------------------------------------------------- 1 | sdk=23 -------------------------------------------------------------------------------- /maven_push.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Chris Banes 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: 'maven' 18 | apply plugin: 'signing' 19 | 20 | def isReleaseBuild() { 21 | return VERSION_NAME.contains("SNAPSHOT") == false 22 | } 23 | 24 | def getReleaseRepositoryUrl() { 25 | return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL 26 | : "https://oss.sonatype.org/service/local/staging/deploy/maven2/" 27 | } 28 | 29 | def getSnapshotRepositoryUrl() { 30 | return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL 31 | : "https://oss.sonatype.org/content/repositories/snapshots/" 32 | } 33 | 34 | def getRepositoryUsername() { 35 | return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : "" 36 | } 37 | 38 | def getRepositoryPassword() { 39 | return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : "" 40 | } 41 | 42 | afterEvaluate { project -> 43 | uploadArchives { 44 | repositories { 45 | mavenDeployer { 46 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } 47 | 48 | pom.groupId = GROUP 49 | pom.artifactId = POM_ARTIFACT_ID 50 | pom.version = VERSION_NAME 51 | 52 | repository(url: getReleaseRepositoryUrl()) { 53 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) 54 | } 55 | snapshotRepository(url: getSnapshotRepositoryUrl()) { 56 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) 57 | } 58 | 59 | pom.project { 60 | name POM_NAME 61 | packaging POM_PACKAGING 62 | description POM_DESCRIPTION 63 | url POM_URL 64 | 65 | scm { 66 | url POM_SCM_URL 67 | connection POM_SCM_CONNECTION 68 | developerConnection POM_SCM_DEV_CONNECTION 69 | } 70 | 71 | licenses { 72 | license { 73 | name POM_LICENCE_NAME 74 | url POM_LICENCE_URL 75 | distribution POM_LICENCE_DIST 76 | } 77 | } 78 | 79 | developers { 80 | developer { 81 | id POM_DEVELOPER_ID 82 | name POM_DEVELOPER_NAME 83 | } 84 | } 85 | } 86 | } 87 | } 88 | } 89 | 90 | signing { 91 | required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } 92 | sign configurations.archives 93 | } 94 | 95 | task androidJavadocs(type: Javadoc) { 96 | source = android.sourceSets.main.java.srcDirs 97 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 98 | failOnError = false 99 | } 100 | 101 | task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { 102 | classifier = 'javadoc' 103 | from androidJavadocs.destinationDir 104 | } 105 | 106 | task androidSourcesJar(type: Jar) { 107 | classifier = 'sources' 108 | from android.sourceSets.main.java.sourceFiles 109 | } 110 | 111 | artifacts { 112 | archives androidSourcesJar 113 | archives androidJavadocsJar 114 | } 115 | } -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | ./gradlew clean build test check uploadArchives closeAndReleaseRepository 3 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':library', ':app-kotlin' 2 | -------------------------------------------------------------------------------- /update_docs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | git checkout gh-pages 3 | git show RxJava2.x:README.md >docs/RxJava2.x/README.md 4 | git add -A 5 | git commit -m "updating docs" 6 | echo "docs updated, now you can push your changes" 7 | -------------------------------------------------------------------------------- /update_javadocs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # update javadocs for RxJava2.x 4 | git checkout RxJava2.x 5 | ./gradlew clean androidJavadocs 6 | git checkout gh-pages 7 | rm -rf javadoc/RxJava2.x/* 8 | cp -avr library/build/docs/javadoc/* ./javadoc/RxJava2.x 9 | git add -A 10 | git commit -m "updating JavaDoc for RxJava2.x" 11 | rm -rf library/build/docs 12 | echo "javadocs for RxJava2.x updated" 13 | 14 | echo "javadocs for both RxJava1.x and RxJava2.x updated - now you can push your changes" 15 | --------------------------------------------------------------------------------