diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml new file mode 100644 index 000000000..405a2b306 --- /dev/null +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -0,0 +1,10 @@ +name: "Validate Gradle Wrapper" +on: [push, pull_request] + +jobs: + validation: + name: "Validation" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: gradle/wrapper-validation-action@v1 diff --git a/.travis.yml b/.travis.yml index 6f48cc940..ddc10848e 100755 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,16 @@ language: android -jdk: openjdk7 +sudo: false +dist: trusty +jdk: openjdk8 android: components: - - build-tools-19.1.0 - - extra-android-support + - platform-tools + - tools + - build-tools-28.0.3 + - android-28 - extra-android-m2repository - - android-19 + - extra-google-m2repository licenses: - '.+' script: - # Sonatype bypass - - echo "nexusUsername=dummy" >> library/gradle.properties - - echo "nexusPassword=dummy" >> library/gradle.properties - ./gradlew clean assemble check diff --git a/CHANGELOG.md b/CHANGELOG.md index 0566956b0..72c4b65f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,63 @@ # CHANGELOG -## 1.4.7 (future version) +## 1.4.11 + + - fix SNI issue on lower android device with Conscrypt + +## 1.4.10 + + - Fixed IP/name resolution errors #998 + - Fixed SNI compatibility + - Upgraded library HttpClient 4.5.8 from 4.3.6 + +## 1.4.9 (released 19. 9. 2015) + +Complete list of commits included is here [https://github.com/loopj/android-async-http/commits/1.4.9](https://github.com/loopj/android-async-http/commits/1.4.9) +List of closed issues is here [https://github.com/loopj/android-async-http/issues?milestone=8&state=closed](https://github.com/loopj/android-async-http/issues?milestone=8&state=closed) + + - **IMPORTANT**, We've switched library from using `org.apache.http` to use `cz.msebera.android.httpclient`, you have to update all your code + - Library is from now on using upstream version of HttpClient libraries, provided by repackaging project https://github.com/smarek/httpclient-android/ + - Achieved API23 Compatibility, see #830 for more info + - Added HeadSample into sample application, to verify Head request works as it should + - FileAsyncHttpResponseHandler now has constructor with `usePoolThread` param, which causes callbacks to be fired from ThreadPool instead of main looper + +## 1.4.8 (released 17. 7. 2015) + +Complete list of commits included is here [https://github.com/loopj/android-async-http/commits/1.4.8](https://github.com/loopj/android-async-http/commits/1.4.8) +List of closed issues is here [https://github.com/loopj/android-async-http/issues?milestone=7&state=closed](https://github.com/loopj/android-async-http/issues?milestone=7&state=closed) + + - New constructor for BinaryHttpResponseHandler which takes Looper as argument (thanks to @ScottFrank) + - SaxAsyncHttpResponseHandler can be now provided with custom charset, instead of just using default one + - Library LogCat tags now use shorter form (forced through Lint checks), appendix "ResponseHandler" shortened to "RH" + - Updated documentation on `RequestHandle.cancel(boolean)` and returning correct response according to handle state + - SaxAsyncHttpResponseHandler onFailure(int, Header[], byte[], Throwable) used wrong fallback to onSuccess(int, Header[], T), fixed to onFailure(int, Header[], T), where T extends SAX DefaultHandler + - Regression fix on onProgress(int,int) documentation + - Sample application now can be built with LeakCanary, use i.e. `gradle :sample:installWithLeakCanaryDebug` to use it + - Updated RequestParams documentation on handling arrays, sets and maps, along with new RequestParamsDebug sample + - Added BlackholeHttpResponseHandler implementation, which discards all response contents and silents all various log messages (see #416) + - Added LogInterface, it's default implementation and interface option to disable/enable logging library-wide and set logging verbosity + - Added option to TAG RequestHandle and cancel all requests matching specified TAG through `AsyncHttpClient.cancelRequestsByTAG(Object TAG)` + - Removed deprecated `getTimeout()` replaced by `getConnectTimeout()` and `getResponseTimeout()` respectively + - Removed deprecated `clearBasicAuth()` replaced by `clearCredentialsProvider()` + +## 1.4.7 (released 9. 5. 2015) + +Complete list of commits included is here [https://github.com/loopj/android-async-http/commits/1.4.7](https://github.com/loopj/android-async-http/commits/1.4.7) +List of closed issues is here [https://github.com/loopj/android-async-http/issues?milestone=6&state=closed](https://github.com/loopj/android-async-http/issues?milestone=6&state=closed) - Fixed crash when canceling through RequestHandle from UI Thread (NetworkOnMainThreadException) + - Fixed URL encoding feature, that was breaking whole URL, not just path and query parts + - FileAsyncHttpResponseHandler now checks that target file path is available or can be created + - DataAsyncHttpResponseHandler was sending cancel notification instead of progress notification, fixed + - Added support for HTTP PATCH requests + - Fixed Assert exception when mkdirs in FileAsyncHttpResponseHandler tries to create dirs that already exists + - Provided option to easily override ClientConnectionManager provision in AsyncHttpClient + - Changed onProgress from (int,int) to (long,long) for dealing with large transfers + - Renamed typo of `preemtive` to `preemptive` (preemptive basic auth) + - Added option to put File array in RequestParams + - RequestParams now support forcing Content-Type into `multipart/form-data` even if there are no files/streams to be multiparted + - Gradle added support for installing to local maven repository, through `gradle installArchives` task + - Added support for Json RFC5179 in JsonHttpResponseHandler ## 1.4.6 (released 7. 9. 2014) @@ -96,7 +151,7 @@ List of closed issues is here [https://github.com/loopj/android-async-http/issue - Is returned for each call (`.post(...)`, `.get(...)`, `.head(...)`, `.put(...)`, etc..) - Added BaseJsonHttpResponseHandler to simplify integration with Jackson JSON, Gson and other JSON parsing libraries - Added Sample application to demonstrate functions and usage - - Using [http://httpbin.org/](http://httpbin.org/) to test methods + - Using [https://httpbin.org/](https://httpbin.org/) to test methods - Enforcing INTERNET permission - Support for Gradle buildscript - Support for Travis CI (Continuous Integration) testing diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bc55a3b23..fa99f5847 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,7 +22,7 @@ Don't be discouraged if your pull request is rejected. This is not a deadline an ISSUES --------- -![Read the ISSUES?](http://i.imgur.com/LPWyLe7.jpg "Read the ISSUES?") +![Read the ISSUES?](https://i.imgur.com/LPWyLe7.jpg "Read the ISSUES?") The issues system is the place to report bugs and not for submitting patches or new functionality. As helpful as we would like to be, we cannot replace the developer and we certainly do not see what you're seeing. So when you come to report an issue, follow these simple rules: diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/NOTICE.txt b/NOTICE.txt new file mode 100644 index 000000000..792ce018f --- /dev/null +++ b/NOTICE.txt @@ -0,0 +1,9 @@ +Android Async Http Client library +Copyright (c) 2011-2015 James Smith +https://loopj.com + +Copyright (c) 2015-2019 Marek Sebera +htts://msebera.cz + +This product includes software developed by +The Apache Software Foundation (https://www.apache.org/). diff --git a/PUBLISHING.md b/PUBLISHING.md new file mode 100644 index 000000000..9ac180688 --- /dev/null +++ b/PUBLISHING.md @@ -0,0 +1,48 @@ +# Publish to oss.sonatype.org staging repository + +``` +gradle clean +# edit build.gradle to update allprojects.version +# edit gradle.properties to update VERSION_NAME and VERSION_CODE +# edit CHANGELOG.md and add changes for published version +# edit sample/src/main/AndroidManifest.xml and update both versionCode and versionName attributes +# edit README.md and update paths, latest version, repository links and sample codes +gradle check +# fix all possible errors and warnings before publishing +cd library +# publishing only library, so following tasks are run in "library" sub-folder +gradle generateJavadocJar +# this will create javadoc archive check the contents via following cmd (use different name and/or path if needed) +# jar -tf ./library/build/libs/android-async-http-null-Release-1.4.11-javadoc.jar +gradle publish +``` + +# Publish to maven central + +*For Nexus Repository Manager 2.14+* + + - Login into https://oss.sonatype.org/ + - Navigation, choose Build Promotion > Staging Repositories + - Explore if repo was automatically created and if contents do match expectations + - Select repository and use "Close" action, to run pre-publishing checks + - Wait a bit + - Refresh the panel with repositories + - Select repository and use "Release" action, if not available, there are issues, that need to be fixed before publishing + +# In GIT + + +**example code using 1.4.11 as released version** +``` +git tag 1.4.11 +git push origin --tags +``` + +# Github + +in *releases* https://github.com/android-async-http/android-async-http/releases + + - Create new release from appropriate tag (see GIT above) + - Describe in similar terms as in CHANGELOG.md what is being done + - Upload JAR (library, sources and javadoc) and AAR (library) along with the release + - Publish by saving form diff --git a/README.md b/README.md index de4656d3f..1ff6c9f85 100755 --- a/README.md +++ b/README.md @@ -1,22 +1,22 @@ Asynchronous Http Client for Android ==================================== -[![Build Status](https://travis-ci.org/loopj/android-async-http.png?branch=master)](https://travis-ci.org/loopj/android-async-http) +[![Build Status](https://travis-ci.org/android-async-http/android-async-http.png?branch=master)](https://travis-ci.org/android-async-http/android-async-http) -An asynchronous, callback-based Http client for Android built on top of Apache's [HttpClient](http://hc.apache.org/httpcomponents-client-ga/) libraries. +An asynchronous, callback-based Http client for Android built on top of Apache's [HttpClient](https://hc.apache.org/httpcomponents-client-ga/) libraries. Changelog --------- -See what is new in version 1.4.6 released on 7th Sep 2014 +See what is new in version 1.4.11 released on 29th June 2020 -https://github.com/loopj/android-async-http/blob/1.4.6/CHANGELOG.md +https://github.com/android-async-http/android-async-http/blob/1.4.11/CHANGELOG.md Javadoc ------- -Latest Javadoc for 1.4.6 release are available here (also included in Maven repository): +Latest Javadoc for 1.4.11 release are available here (also included in Maven repository): -http://loopj.com/android-async-http/doc/ +https://android-async-http.github.io/android-async-http/doc/ Features -------- @@ -30,12 +30,13 @@ Features - Automatic **gzip** response decoding support for super-fast requests - Optional built-in response parsing into **JSON** (JsonHttpResponseHandler) - Optional **persistent cookie store**, saves cookies into your app's SharedPreferences +- Support sni with Conscrypt on older android device ([wiki](https://github.com/android-async-http/android-async-http/wiki/Support-SNI-on-lower-android-device)) Examples -------- For inspiration and testing on device we've provided Sample Application. -See individual samples [here on Github](https://github.com/loopj/android-async-http/tree/1.4.6/sample/src/main/java/com/loopj/android/http/sample) +See individual samples [here on Github](https://github.com/android-async-http/android-async-http/tree/1.4.11/sample/src/main/java/com/loopj/android/http/sample) To run Sample application, simply clone the repository and run this command, to install it on connected device ```java @@ -48,34 +49,51 @@ You can now integrate this library in your project via Maven. There are availabl **releases, maven central** -http://central.maven.org/maven2/com/loopj/android/android-async-http/ +https://repo1.maven.org/maven2/com/loopj/android/android-async-http/ ``` -Maven URL: http://repo1.maven.org/maven2/ +Maven URL: https://repo1.maven.org/maven2/ GroupId: com.loopj.android ArtifactId: android-async-http -Version: 1.4.6 +Version: 1.4.11 Packaging: JAR or AAR ``` -Gradle: `com.loopj.android:android-async-http:1.4.6` +Gradle +```groovy +repositories { + mavenCentral() +} + +dependencies { + implementation 'com.loopj.android:android-async-http:1.4.11' +} +``` **development snapshots** +snapshot might not be published yet https://oss.sonatype.org/content/repositories/snapshots/com/loopj/android/android-async-http/ ``` Maven URL: https://oss.sonatype.org/content/repositories/snapshots/ GroupId: com.loopj.android ArtifactId: android-async-http -Version: 1.4.7-SNAPSHOT +Version: 1.4.12-SNAPSHOT Packaging: JAR or AAR ``` -Gradle: `com.loopj.android:android-async-http:1.4.7-SNAPSHOT` +Gradle +```groovy +repositories { + maven { + url 'https://oss.sonatype.org/content/repositories/snapshots/' + } +} +dependencies { + implementation 'com.loopj.android:android-async-http:1.4.11-SNAPSHOT' +} +``` Documentation, Features and Examples ------------------------------------ Full details and documentation can be found on the project page here: -http://loopj.com/android-async-http/ - - -[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/loopj/android-async-http/trend.png)](https://bitdeli.com/free "Bitdeli Badge") +https://android-async-http.github.io/android-async-http/ diff --git a/build.gradle b/build.gradle index 6dcaf5c10..2b8e1368e 100755 --- a/build.gradle +++ b/build.gradle @@ -1,10 +1,16 @@ buildscript { repositories { - mavenCentral() + jcenter() + google() + maven { url "https://oss.sonatype.org/content/repositories/snapshots" } + maven { url "https://plugins.gradle.org/m2/" } } dependencies { - classpath 'com.android.tools.build:gradle:0.12.2' + classpath 'com.android.tools.build:gradle:4.0.0' + classpath 'com.vanniktech:gradle-android-javadoc-plugin:0.4.0-SNAPSHOT' + classpath 'digital.wup:android-maven-publish:3.6.2' + classpath "gradle.plugin.com.dorongold.plugins:task-tree:1.4" } } @@ -14,16 +20,21 @@ def isReleaseBuild() { allprojects { group = 'com.loopj.android' - version = '1.4.7-SNAPSHOT' + version = '1.4.11' repositories { + google() + jcenter() mavenCentral() } tasks.withType(JavaCompile) { options.encoding = "UTF-8" options.compilerArgs << "-Xlint:unchecked" + options.compilerArgs << "-Xlint:deprecation" } } apply plugin: 'android-reporting' +apply plugin: 'com.vanniktech.android.javadoc' +apply plugin: 'com.dorongold.task-tree' diff --git a/gradle.properties b/gradle.properties index df1bf1ff3..c9086631a 100755 --- a/gradle.properties +++ b/gradle.properties @@ -1,14 +1,14 @@ -VERSION_NAME=1.4.7-SNAPSHOT -VERSION_CODE=147 +VERSION_NAME=1.4.11 +VERSION_CODE=1411 GROUP=com.loopj.android POM_DESCRIPTION=An Asynchronous HTTP Library for Android -POM_URL=loopj.com/android-async-http/ -POM_SCM_URL=https://github.com/loopj/android-async-http -POM_SCM_CONNECTION=scm:git@github.com:loopj/android-async-http.git -POM_SCM_DEV_CONNECTION=scm:git@github.com:loopj/android-async-http.git +POM_URL=https://android-async-http.github.io/android-async-http/ +POM_SCM_URL=https://github.com/android-async-http/android-async-http +POM_SCM_CONNECTION=scm:git@github.com:android-async-http/android-async-http.git +POM_SCM_DEV_CONNECTION=scm:git@github.com:android-async-http/android-async-http.git POM_LICENCE_NAME=The Apache Software License, Version 2.0 -POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt +POM_LICENCE_URL=https://www.apache.org/licenses/LICENSE-2.0.txt POM_LICENCE_DIST=repo POM_DEVELOPER_ID=jamessmith diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 0087cd3b1..30d399d8d 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 796a51118..7bfd9f1e4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Jul 02 18:01:59 CEST 2014 +#Sun Jun 28 22:59:06 PDT 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-1.12-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip diff --git a/library/build.gradle b/library/build.gradle index f68a9d92e..d63952f0b 100755 --- a/library/build.gradle +++ b/library/build.gradle @@ -1,12 +1,14 @@ apply plugin: 'com.android.library' +apply plugin: 'digital.wup.android-maven-publish' +apply plugin: 'signing' android { - compileSdkVersion 19 - buildToolsVersion '19.1' + compileSdkVersion 28 defaultConfig { - minSdkVersion 3 - targetSdkVersion 19 + minSdkVersion 9 + targetSdkVersion 28 + consumerProguardFiles 'proguard.txt' } lintOptions { @@ -16,19 +18,92 @@ android { showAll true disable 'OldTargetApi' } +} - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_6 - targetCompatibility JavaVersion.VERSION_1_6 - } +dependencies { + api 'cz.msebera.android:httpclient:4.5.8' + compileOnly 'org.conscrypt:conscrypt-android:2.4.0' } -android.libraryVariants.all { variant -> - def name = variant.buildType.name - def task = project.tasks.create "jar${name.capitalize()}", Jar - task.dependsOn variant.javaCompile - task.from variant.javaCompile.destinationDir - artifacts.add('archives', task); +project.afterEvaluate { project -> + + android.libraryVariants.all { variant -> + def name = variant.buildType.name + def task = project.tasks.create "jar${name.capitalize()}", Jar + task.dependsOn variant.javaCompileProvider.get() + task.from variant.javaCompileProvider.get().destinationDir + } + + task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + archiveClassifier = 'sources' + } + + task javadocJar(type: Jar, dependsOn: tasks.findAll { task -> task.name.contains('Javadoc') }) { + archiveClassifier = 'javadoc' + from 'build/docs/javadoc/release/' + } + + publishing { + publications { + maven(MavenPublication) { + artifactId = POM_ARTIFACT_ID + artifact javadocJar + artifact sourcesJar + artifact jarRelease + from components.android + + pom { + name = POM_NAME + description = POM_DESCRIPTION + packaging = POM_PACKAGING + url = POM_URL + + scm { + connection = POM_SCM_CONNECTION + developerConnection = POM_SCM_DEV_CONNECTION + url = POM_SCM_URL + } + + licenses { + license { + name = POM_LICENCE_NAME + url = POM_LICENCE_URL + distribution = POM_LICENCE_DIST + } + } + + developers { + developer { + id = 'mareksebera' + name = 'Marek Sebera' + } + } + } + + pom.name = POM_NAME + pom.description = POM_DESCRIPTION + pom.url = POM_URL + pom.packaging = POM_PACKAGING + } + } + repositories { + maven { + def releaseUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/" + def snapshotUrl = "https://oss.sonatype.org/content/repositories/snapshots/" + url = version.endsWith('SNAPSHOT') ? snapshotUrl : releaseUrl + credentials { + def NexusUsername = project.hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : '' + def NexusPassword = project.hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : '' + username NexusUsername + password NexusPassword + } + } + } + } + + signing { + sign publishing.publications.maven + } } -apply from: '../maven_push.gradle' diff --git a/library/proguard.txt b/library/proguard.txt new file mode 100644 index 000000000..e3ab81252 --- /dev/null +++ b/library/proguard.txt @@ -0,0 +1,2 @@ +-keep class cz.msebera.android.httpclient.** { *; } +-keep class com.loopj.android.http.** { *; } diff --git a/library/src/main/AndroidManifest.xml b/library/src/main/AndroidManifest.xml index 58b8a4f53..7af3e5711 100755 --- a/library/src/main/AndroidManifest.xml +++ b/library/src/main/AndroidManifest.xml @@ -1,11 +1,7 @@ + package="com.loopj.android.http"> - - - + diff --git a/library/src/main/java/com/loopj/android/http/AsyncHttpClient.java b/library/src/main/java/com/loopj/android/http/AsyncHttpClient.java index dbfa5c79e..f38c4fe10 100755 --- a/library/src/main/java/com/loopj/android/http/AsyncHttpClient.java +++ b/library/src/main/java/com/loopj/android/http/AsyncHttpClient.java @@ -1,14 +1,13 @@ +package com.loopj.android.http; + /* Android Asynchronous Http Client Copyright (c) 2011 James Smith - http://loopj.com - + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,67 +15,17 @@ limitations under the License. */ -package com.loopj.android.http; - import android.content.Context; import android.os.Looper; -import android.util.Log; - -import org.apache.http.Header; -import org.apache.http.HeaderElement; -import org.apache.http.HttpEntity; -import org.apache.http.HttpException; -import org.apache.http.HttpHost; -import org.apache.http.HttpRequest; -import org.apache.http.HttpRequestInterceptor; -import org.apache.http.HttpResponse; -import org.apache.http.HttpResponseInterceptor; -import org.apache.http.HttpVersion; -import org.apache.http.auth.AuthScope; -import org.apache.http.auth.AuthState; -import org.apache.http.auth.Credentials; -import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.CookieStore; -import org.apache.http.client.CredentialsProvider; -import org.apache.http.client.HttpClient; -import org.apache.http.client.RedirectHandler; -import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpHead; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.client.params.ClientPNames; -import org.apache.http.client.protocol.ClientContext; -import org.apache.http.conn.params.ConnManagerParams; -import org.apache.http.conn.params.ConnPerRouteBean; -import org.apache.http.conn.params.ConnRoutePNames; -import org.apache.http.conn.scheme.PlainSocketFactory; -import org.apache.http.conn.scheme.Scheme; -import org.apache.http.conn.scheme.SchemeRegistry; -import org.apache.http.conn.ssl.SSLSocketFactory; -import org.apache.http.entity.HttpEntityWrapper; -import org.apache.http.impl.auth.BasicScheme; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.HttpConnectionParams; -import org.apache.http.params.HttpParams; -import org.apache.http.params.HttpProtocolParams; -import org.apache.http.protocol.BasicHttpContext; -import org.apache.http.protocol.ExecutionContext; -import org.apache.http.protocol.HttpContext; -import org.apache.http.protocol.SyncBasicHttpContext; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PushbackInputStream; -import java.io.UnsupportedEncodingException; import java.lang.reflect.Field; import java.net.URI; -import java.net.URLEncoder; +import java.net.URL; +import java.net.URLDecoder; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; @@ -88,6 +37,55 @@ import java.util.concurrent.Executors; import java.util.zip.GZIPInputStream; +import cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.HeaderElement; +import cz.msebera.android.httpclient.HttpEntity; +import cz.msebera.android.httpclient.HttpException; +import cz.msebera.android.httpclient.HttpHost; +import cz.msebera.android.httpclient.HttpRequest; +import cz.msebera.android.httpclient.HttpRequestInterceptor; +import cz.msebera.android.httpclient.HttpResponse; +import cz.msebera.android.httpclient.HttpResponseInterceptor; +import cz.msebera.android.httpclient.HttpVersion; +import cz.msebera.android.httpclient.auth.AuthSchemeRegistry; +import cz.msebera.android.httpclient.auth.AuthScope; +import cz.msebera.android.httpclient.auth.AuthState; +import cz.msebera.android.httpclient.auth.Credentials; +import cz.msebera.android.httpclient.auth.UsernamePasswordCredentials; +import cz.msebera.android.httpclient.client.CookieStore; +import cz.msebera.android.httpclient.client.CredentialsProvider; +import cz.msebera.android.httpclient.client.HttpClient; +import cz.msebera.android.httpclient.client.RedirectHandler; +import cz.msebera.android.httpclient.client.methods.HttpEntityEnclosingRequestBase; +import cz.msebera.android.httpclient.client.methods.HttpHead; +import cz.msebera.android.httpclient.client.methods.HttpOptions; +import cz.msebera.android.httpclient.client.methods.HttpPatch; +import cz.msebera.android.httpclient.client.methods.HttpPost; +import cz.msebera.android.httpclient.client.methods.HttpPut; +import cz.msebera.android.httpclient.client.methods.HttpUriRequest; +import cz.msebera.android.httpclient.client.params.ClientPNames; +import cz.msebera.android.httpclient.client.protocol.ClientContext; +import cz.msebera.android.httpclient.conn.ClientConnectionManager; +import cz.msebera.android.httpclient.conn.params.ConnManagerParams; +import cz.msebera.android.httpclient.conn.params.ConnPerRouteBean; +import cz.msebera.android.httpclient.conn.params.ConnRoutePNames; +import cz.msebera.android.httpclient.conn.scheme.PlainSocketFactory; +import cz.msebera.android.httpclient.conn.scheme.Scheme; +import cz.msebera.android.httpclient.conn.scheme.SchemeRegistry; +import cz.msebera.android.httpclient.conn.ssl.SSLSocketFactory; +import cz.msebera.android.httpclient.entity.HttpEntityWrapper; +import cz.msebera.android.httpclient.impl.auth.BasicScheme; +import cz.msebera.android.httpclient.impl.client.DefaultHttpClient; +import cz.msebera.android.httpclient.impl.conn.tsccm.ThreadSafeClientConnManager; +import cz.msebera.android.httpclient.params.BasicHttpParams; +import cz.msebera.android.httpclient.params.HttpConnectionParams; +import cz.msebera.android.httpclient.params.HttpParams; +import cz.msebera.android.httpclient.params.HttpProtocolParams; +import cz.msebera.android.httpclient.protocol.BasicHttpContext; +import cz.msebera.android.httpclient.protocol.ExecutionContext; +import cz.msebera.android.httpclient.protocol.HttpContext; +import cz.msebera.android.httpclient.protocol.SyncBasicHttpContext; + /** * The AsyncHttpClient can be used to make asynchronous GET, POST, PUT and DELETE HTTP requests in @@ -96,7 +94,7 @@ * ResponseHandlerInterface} instance.

 

For example:

 

*
  * AsyncHttpClient client = new AsyncHttpClient();
- * client.get("http://www.google.com/", new AsyncHttpResponseHandler() {
+ * client.get("https://www.google.com/", new AsyncHttpResponseHandler() {
  *     @Override
  *     public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
  *          System.out.println(response);
@@ -130,16 +128,15 @@ public class AsyncHttpClient {
     public static final int DEFAULT_MAX_RETRIES = 5;
     public static final int DEFAULT_RETRY_SLEEP_TIME_MILLIS = 1500;
     public static final int DEFAULT_SOCKET_BUFFER_SIZE = 8192;
-
-    private int maxConnections = DEFAULT_MAX_CONNECTIONS;
-    private int connectTimeout = DEFAULT_SOCKET_TIMEOUT;
-    private int responseTimeout = DEFAULT_SOCKET_TIMEOUT;
-
+    public static LogInterface log = new LogHandler();
     private final DefaultHttpClient httpClient;
     private final HttpContext httpContext;
-    private ExecutorService threadPool;
     private final Map> requestMap;
     private final Map clientHeaderMap;
+    private int maxConnections = DEFAULT_MAX_CONNECTIONS;
+    private int connectTimeout = DEFAULT_SOCKET_TIMEOUT;
+    private int responseTimeout = DEFAULT_SOCKET_TIMEOUT;
+    private ExecutorService threadPool;
     private boolean isUrlEncodingEnabled = true;
 
     /**
@@ -179,44 +176,6 @@ public AsyncHttpClient(boolean fixNoHttpResponseException, int httpPort, int htt
         this(getDefaultSchemeRegistry(fixNoHttpResponseException, httpPort, httpsPort));
     }
 
-    /**
-     * Returns default instance of SchemeRegistry
-     *
-     * @param fixNoHttpResponseException Whether to fix issue or not, by omitting SSL verification
-     * @param httpPort                   HTTP port to be used, must be greater than 0
-     * @param httpsPort                  HTTPS port to be used, must be greater than 0
-     */
-    private static SchemeRegistry getDefaultSchemeRegistry(boolean fixNoHttpResponseException, int httpPort, int httpsPort) {
-        if (fixNoHttpResponseException) {
-            Log.d(LOG_TAG, "Beware! Using the fix is insecure, as it doesn't verify SSL certificates.");
-        }
-
-        if (httpPort < 1) {
-            httpPort = 80;
-            Log.d(LOG_TAG, "Invalid HTTP port number specified, defaulting to 80");
-        }
-
-        if (httpsPort < 1) {
-            httpsPort = 443;
-            Log.d(LOG_TAG, "Invalid HTTPS port number specified, defaulting to 443");
-        }
-
-        // Fix to SSL flaw in API < ICS
-        // See https://code.google.com/p/android/issues/detail?id=13117
-        SSLSocketFactory sslSocketFactory;
-        if (fixNoHttpResponseException) {
-            sslSocketFactory = MySSLSocketFactory.getFixedSocketFactory();
-        } else {
-            sslSocketFactory = SSLSocketFactory.getSocketFactory();
-        }
-
-        SchemeRegistry schemeRegistry = new SchemeRegistry();
-        schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), httpPort));
-        schemeRegistry.register(new Scheme("https", sslSocketFactory, httpsPort));
-
-        return schemeRegistry;
-    }
-
     /**
      * Creates a new AsyncHttpClient.
      *
@@ -237,7 +196,8 @@ public AsyncHttpClient(SchemeRegistry schemeRegistry) {
 
         HttpProtocolParams.setVersion(httpParams, HttpVersion.HTTP_1_1);
 
-        ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(httpParams, schemeRegistry);
+        ClientConnectionManager cm = createConnectionManager(schemeRegistry, httpParams);
+        Utils.asserts(cm != null, "Custom implementation of #createConnectionManager(SchemeRegistry, BasicHttpParams) returned null");
 
         threadPool = getDefaultThreadPool();
         requestMap = Collections.synchronizedMap(new WeakHashMap>());
@@ -254,7 +214,7 @@ public void process(HttpRequest request, HttpContext context) {
                 for (String header : clientHeaderMap.keySet()) {
                     if (request.containsHeader(header)) {
                         Header overwritten = request.getFirstHeader(header);
-                        Log.d(LOG_TAG,
+                        log.d(LOG_TAG,
                                 String.format("Headers were overwritten! (%s | %s) overwrites (%s | %s)",
                                         header, clientHeaderMap.get(header),
                                         overwritten.getName(), overwritten.getValue())
@@ -290,6 +250,11 @@ public void process(HttpResponse response, HttpContext context) {
         httpClient.addRequestInterceptor(new HttpRequestInterceptor() {
             @Override
             public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
+
+                AuthSchemeRegistry authSchemeRegistry = new AuthSchemeRegistry();
+                authSchemeRegistry.register("Bearer", new BearerAuthSchemeFactory());
+                context.setAttribute(ClientContext.AUTHSCHEME_REGISTRY, authSchemeRegistry);
+
                 AuthState authState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE);
                 CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(
                         ClientContext.CREDS_PROVIDER);
@@ -298,7 +263,10 @@ public void process(final HttpRequest request, final HttpContext context) throws
                 if (authState.getAuthScheme() == null) {
                     AuthScope authScope = new AuthScope(targetHost.getHostName(), targetHost.getPort());
                     Credentials creds = credsProvider.getCredentials(authScope);
-                    if (creds != null) {
+                    if (creds instanceof TokenCredentials) {
+                        authState.setAuthScheme(new BearerAuthSchemeFactory.BearerAuthScheme());
+                        authState.setCredentials(creds);
+                    } else if (creds != null) {
                         authState.setAuthScheme(new BasicScheme());
                         authState.setCredentials(creds);
                     }
@@ -309,6 +277,44 @@ public void process(final HttpRequest request, final HttpContext context) throws
         httpClient.setHttpRequestRetryHandler(new RetryHandler(DEFAULT_MAX_RETRIES, DEFAULT_RETRY_SLEEP_TIME_MILLIS));
     }
 
+    /**
+     * Returns default instance of SchemeRegistry
+     *
+     * @param fixNoHttpResponseException Whether to fix issue or not, by omitting SSL verification
+     * @param httpPort                   HTTP port to be used, must be greater than 0
+     * @param httpsPort                  HTTPS port to be used, must be greater than 0
+     */
+    private static SchemeRegistry getDefaultSchemeRegistry(boolean fixNoHttpResponseException, int httpPort, int httpsPort) {
+        if (fixNoHttpResponseException) {
+            log.d(LOG_TAG, "Beware! Using the fix is insecure, as it doesn't verify SSL certificates.");
+        }
+
+        if (httpPort < 1) {
+            httpPort = 80;
+            log.d(LOG_TAG, "Invalid HTTP port number specified, defaulting to 80");
+        }
+
+        if (httpsPort < 1) {
+            httpsPort = 443;
+            log.d(LOG_TAG, "Invalid HTTPS port number specified, defaulting to 443");
+        }
+
+        // Fix to SSL flaw in API < ICS
+        // See https://code.google.com/p/android/issues/detail?id=13117
+        SSLSocketFactory sslSocketFactory;
+        if (fixNoHttpResponseException) {
+            sslSocketFactory = MySSLSocketFactory.getFixedSocketFactory();
+        } else {
+            sslSocketFactory = SSLSocketFactory.getSocketFactory();
+        }
+
+        SchemeRegistry schemeRegistry = new SchemeRegistry();
+        schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), httpPort));
+        schemeRegistry.register(new Scheme("https", sslSocketFactory, httpsPort));
+
+        return schemeRegistry;
+    }
+
     public static void allowRetryExceptionClass(Class cls) {
         if (cls != null) {
             RetryHandler.addClassToWhitelist(cls);
@@ -321,6 +327,132 @@ public static void blockRetryExceptionClass(Class cls) {
         }
     }
 
+    /**
+     * Will encode url, if not disabled, and adds params on the end of it
+     *
+     * @param url             String with URL, should be valid URL without params
+     * @param params          RequestParams to be appended on the end of URL
+     * @param shouldEncodeUrl whether url should be encoded (replaces spaces with %20)
+     * @return encoded url if requested with params appended if any available
+     */
+    public static String getUrlWithQueryString(boolean shouldEncodeUrl, String url, RequestParams params) {
+        if (url == null)
+            return null;
+
+        if (shouldEncodeUrl) {
+            try {
+                String decodedURL = URLDecoder.decode(url, "UTF-8");
+                URL _url = new URL(decodedURL);
+                URI _uri = new URI(_url.getProtocol(), _url.getUserInfo(), _url.getHost(), _url.getPort(), _url.getPath(), _url.getQuery(), _url.getRef());
+                url = _uri.toASCIIString();
+            } catch (Exception ex) {
+                // Should not really happen, added just for sake of validity
+                log.e(LOG_TAG, "getUrlWithQueryString encoding URL", ex);
+            }
+        }
+
+        if (params != null) {
+            // Construct the query string and trim it, in case it
+            // includes any excessive white spaces.
+            String paramString = params.getParamString().trim();
+
+            // Only add the query string if it isn't empty and it
+            // isn't equal to '?'.
+            if (!paramString.equals("") && !paramString.equals("?")) {
+                url += url.contains("?") ? "&" : "?";
+                url += paramString;
+            }
+        }
+
+        return url;
+    }
+
+    /**
+     * Checks the InputStream if it contains  GZIP compressed data
+     *
+     * @param inputStream InputStream to be checked
+     * @return true or false if the stream contains GZIP compressed data
+     * @throws java.io.IOException if read from inputStream fails
+     */
+    public static boolean isInputStreamGZIPCompressed(final PushbackInputStream inputStream) throws IOException {
+        if (inputStream == null)
+            return false;
+
+        byte[] signature = new byte[2];
+        int count = 0;
+        try {
+            while (count < 2) {
+                int readCount = inputStream.read(signature, count, 2 - count);
+                if (readCount < 0) return false;
+                count = count + readCount;
+            }
+        } finally {
+            inputStream.unread(signature, 0, count);
+        }
+        int streamHeader = ((int) signature[0] & 0xff) | ((signature[1] << 8) & 0xff00);
+        return GZIPInputStream.GZIP_MAGIC == streamHeader;
+    }
+
+    /**
+     * A utility function to close an input stream without raising an exception.
+     *
+     * @param is input stream to close safely
+     */
+    public static void silentCloseInputStream(InputStream is) {
+        try {
+            if (is != null) {
+                is.close();
+            }
+        } catch (IOException e) {
+            log.w(LOG_TAG, "Cannot close input stream", e);
+        }
+    }
+
+    /**
+     * A utility function to close an output stream without raising an exception.
+     *
+     * @param os output stream to close safely
+     */
+    public static void silentCloseOutputStream(OutputStream os) {
+        try {
+            if (os != null) {
+                os.close();
+            }
+        } catch (IOException e) {
+            log.w(LOG_TAG, "Cannot close output stream", e);
+        }
+    }
+
+    /**
+     * This horrible hack is required on Android, due to implementation of BasicManagedEntity, which
+     * doesn't chain call consumeContent on underlying wrapped HttpEntity
+     *
+     * @param entity HttpEntity, may be null
+     */
+    public static void endEntityViaReflection(HttpEntity entity) {
+        if (entity instanceof HttpEntityWrapper) {
+            try {
+                Field f = null;
+                Field[] fields = HttpEntityWrapper.class.getDeclaredFields();
+                for (Field ff : fields) {
+                    if (ff.getName().equals("wrappedEntity")) {
+                        f = ff;
+                        break;
+                    }
+                }
+                if (f != null) {
+                    f.setAccessible(true);
+                    HttpEntity wrapped = (HttpEntity) f.get(entity);
+                    if (wrapped != null) {
+                        wrapped.consumeContent();
+                    }
+                }
+            } catch (Throwable t) {
+                log.e(LOG_TAG, "wrappedEntity consume", t);
+            }
+        }
+    }
+
     /**
      * Get the underlying HttpClient instance. This is useful for setting additional fine-grained
      * settings for requests by accessing the client's ConnectionManager, HttpParams and
@@ -342,6 +474,67 @@ public HttpContext getHttpContext() {
         return this.httpContext;
     }
 
+    /**
+     * Returns logging enabled flag from underlying LogInterface instance
+     * Default setting is logging enabled.
+     *
+     * @return boolean whether is logging across the library currently enabled
+     */
+    public boolean isLoggingEnabled() {
+        return log.isLoggingEnabled();
+    }
+
+    /**
+     * Will set logging enabled flag on underlying LogInterface instance.
+     * Default setting is logging enabled.
+     *
+     * @param loggingEnabled whether the logging should be enabled or not
+     */
+    public void setLoggingEnabled(boolean loggingEnabled) {
+        log.setLoggingEnabled(loggingEnabled);
+    }
+
+    /**
+     * Retrieves current log level from underlying LogInterface instance.
+     * Default setting is VERBOSE log level.
+     *
+     * @return int log level currently in effect
+     */
+    public int getLoggingLevel() {
+        return log.getLoggingLevel();
+    }
+
+    /**
+     * Sets log level to be used across all library default implementation
+     * Default setting is VERBOSE log level.
+     *
+     * @param logLevel int log level, either from LogInterface interface or from {@link android.util.Log}
+     */
+    public void setLoggingLevel(int logLevel) {
+        log.setLoggingLevel(logLevel);
+    }
+
+    /**
+     * Will return current LogInterface used in AsyncHttpClient instance
+     *
+     * @return LogInterface currently used by AsyncHttpClient instance
+     */
+    public LogInterface getLogInterface() {
+        return log;
+    }
+
+    /**
+     * Sets default LogInterface (similar to std Android Log util class) instance,
+     * to be used in AsyncHttpClient instance
+     *
+     * @param logInterfaceInstance LogInterface instance, if null, nothing is done
+     */
+    public void setLogInterface(LogInterface logInterfaceInstance) {
+        if (logInterfaceInstance != null) {
+            log = logInterfaceInstance;
+        }
+    }
+
     /**
      * Sets an optional CookieStore to use when making requests
      *
@@ -352,6 +545,16 @@ public void setCookieStore(CookieStore cookieStore) {
         httpContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);
     }
 
+    /**
+     * Returns the current executor service used. By default, Executors.newCachedThreadPool() is
+     * used.
+     *
+     * @return current executor service used
+     */
+    public ExecutorService getThreadPool() {
+        return threadPool;
+    }
+
     /**
      * Overrides the threadpool implementation used when queuing/pooling requests. By default,
      * Executors.newCachedThreadPool() is used.
@@ -364,22 +567,23 @@ public void setThreadPool(ExecutorService threadPool) {
     }
 
     /**
-     * Returns the current executor service used. By default, Executors.newCachedThreadPool() is
-     * used.
+     * Get the default threading pool to be used for this HTTP client.
      *
-     * @return current executor service used
+     * @return The default threading pool to be used
      */
-    public ExecutorService getThreadPool() {
-        return threadPool;
+    protected ExecutorService getDefaultThreadPool() {
+        return Executors.newCachedThreadPool();
     }
 
     /**
-     * Get the default threading pool to be used for this HTTP client.
+     * Provided so it is easier for developers to provide custom ThreadSafeClientConnManager implementation
      *
-     * @return The default threading pool to be used
+     * @param schemeRegistry SchemeRegistry, usually provided by {@link #getDefaultSchemeRegistry(boolean, int, int)}
+     * @param httpParams     BasicHttpParams
+     * @return ClientConnectionManager instance
      */
-    protected ExecutorService getDefaultThreadPool() {
-        return Executors.newCachedThreadPool();
+    protected ClientConnectionManager createConnectionManager(SchemeRegistry schemeRegistry, BasicHttpParams httpParams) {
+        return new ThreadSafeClientConnManager(httpParams, schemeRegistry);
     }
 
     /**
@@ -429,7 +633,7 @@ public void setRedirectHandler(final RedirectHandler customRedirectHandler) {
 
     /**
      * Sets the User-Agent header to be sent with each request. By default, "Android Asynchronous
-     * Http Client/VERSION (http://loopj.com/android-async-http/)" is used.
+     * Http Client/VERSION (https://github.com/android-async-http/android-async-http/)" is used.
      *
      * @param userAgent the string to use in the User-Agent header.
      */
@@ -437,7 +641,6 @@ public void setUserAgent(String userAgent) {
         HttpProtocolParams.setUserAgent(this.httpClient.getParams(), userAgent);
     }
 
-
     /**
      * Returns current limit of parallel connections
      *
@@ -460,17 +663,6 @@ public void setMaxConnections(int maxConnections) {
         ConnManagerParams.setMaxConnectionsPerRoute(httpParams, new ConnPerRouteBean(this.maxConnections));
     }
 
-    /**
-     * Returns current socket timeout limit (milliseconds). By default, this is
-     * set to 10 seconds.
-     *
-     * @return Socket Timeout limit in milliseconds
-     * @deprecated Use either {@link #getConnectTimeout()} or {@link #getResponseTimeout()}
-     */
-    public int getTimeout() {
-        return connectTimeout;
-    }
-
     /**
      * Set both the connection and socket timeouts. By default, both are set to
      * 10 seconds.
@@ -606,6 +798,31 @@ public void removeHeader(String header) {
         clientHeaderMap.remove(header);
     }
 
+    /**
+     * Sets bearer authentication for the request. Uses AuthScope.ANY. This is the same as
+     * setBearerAuth('token',AuthScope.ANY, false)
+     *
+     * @param token Bearer Token
+     */
+    public void setBearerAuth(String token) {
+        setBearerAuth(token, AuthScope.ANY, false);
+    }
+
+
+    /**
+     * Sets bearer authentication for the request. You should pass in your AuthScope for security. It
+     * should be like this setBearerAuth("token", new AuthScope("host",port,AuthScope.ANY_REALM), false)
+     *
+     * @param token      Bearer Token
+     * @param scope      an AuthScope object
+     * @param preemptive sets authorization in preemptive manner
+     */
+    public void setBearerAuth(String token, AuthScope scope, boolean preemptive) {
+        TokenCredentials credentials = new TokenCredentials(token);
+        setCredentials(scope, credentials);
+        setAuthenticationPreemptive(preemptive);
+    }
+
     /**
      * Sets basic authentication for the request. Uses AuthScope.ANY. This is the same as
      * setBasicAuth('username','password',AuthScope.ANY)
@@ -621,12 +838,12 @@ public void setBasicAuth(String username, String password) {
      * Sets basic authentication for the request. Uses AuthScope.ANY. This is the same as
      * setBasicAuth('username','password',AuthScope.ANY)
      *
-     * @param username  Basic Auth username
-     * @param password  Basic Auth password
-     * @param preemtive sets authorization in preemtive manner
+     * @param username   Basic Auth username
+     * @param password   Basic Auth password
+     * @param preemptive sets authorization in preemptive manner
      */
-    public void setBasicAuth(String username, String password, boolean preemtive) {
-        setBasicAuth(username, password, null, preemtive);
+    public void setBasicAuth(String username, String password, boolean preemptive) {
+        setBasicAuth(username, password, null, preemptive);
     }
 
     /**
@@ -645,48 +862,40 @@ public void setBasicAuth(String username, String password, AuthScope scope) {
      * Sets basic authentication for the request. You should pass in your AuthScope for security. It
      * should be like this setBasicAuth("username","password", new AuthScope("host",port,AuthScope.ANY_REALM))
      *
-     * @param username  Basic Auth username
-     * @param password  Basic Auth password
-     * @param scope     an AuthScope object
-     * @param preemtive sets authorization in preemtive manner
+     * @param username   Basic Auth username
+     * @param password   Basic Auth password
+     * @param scope      an AuthScope object
+     * @param preemptive sets authorization in preemptive manner
      */
-    public void setBasicAuth(String username, String password, AuthScope scope, boolean preemtive) {
+    public void setBasicAuth(String username, String password, AuthScope scope, boolean preemptive) {
         UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password);
         setCredentials(scope, credentials);
-        setAuthenticationPreemptive(preemtive);
+        setAuthenticationPreemptive(preemptive);
     }
 
     public void setCredentials(AuthScope authScope, Credentials credentials) {
         if (credentials == null) {
-            Log.d(LOG_TAG, "Provided credentials are null, not setting");
+            log.d(LOG_TAG, "Provided credentials are null, not setting");
             return;
         }
         this.httpClient.getCredentialsProvider().setCredentials(authScope == null ? AuthScope.ANY : authScope, credentials);
     }
 
     /**
-     * Sets HttpRequestInterceptor which handles authorization in preemtive way, as workaround you
+     * Sets HttpRequestInterceptor which handles authorization in preemptive way, as workaround you
      * can use call `AsyncHttpClient.addHeader("Authorization","Basic base64OfUsernameAndPassword==")`
      *
-     * @param isPreemtive whether the authorization is processed in preemtive way
+     * @param isPreemptive whether the authorization is processed in preemptive way
      */
-    public void setAuthenticationPreemptive(boolean isPreemtive) {
-        if (isPreemtive) {
-            httpClient.addRequestInterceptor(new PreemtiveAuthorizationHttpRequestInterceptor(), 0);
+    public void setAuthenticationPreemptive(boolean isPreemptive) {
+        if (isPreemptive) {
+            httpClient.addRequestInterceptor(new PreemptiveAuthorizationHttpRequestInterceptor(), 0);
         } else {
-            httpClient.removeRequestInterceptorByClass(PreemtiveAuthorizationHttpRequestInterceptor.class);
+            httpClient.removeRequestInterceptorByClass(PreemptiveAuthorizationHttpRequestInterceptor.class);
         }
     }
 
-    /**
-     * Removes previously set basic auth credentials
-     *
-     * @deprecated
-     */
-    @Deprecated
-    public void clearBasicAuth() {
-        clearCredentialsProvider();
-    }
+    // [+] HTTP HEAD
 
     /**
      * Removes previously set auth credentials
@@ -707,49 +916,98 @@ public void clearCredentialsProvider() {
      */
     public void cancelRequests(final Context context, final boolean mayInterruptIfRunning) {
         if (context == null) {
-            Log.e(LOG_TAG, "Passed null Context to cancelRequests");
+            log.e(LOG_TAG, "Passed null Context to cancelRequests");
             return;
         }
-        Runnable r = new Runnable() {
-            @Override
-            public void run() {
-                List requestList = requestMap.get(context);
-                if (requestList != null) {
-                    for (RequestHandle requestHandle : requestList) {
-                        requestHandle.cancel(mayInterruptIfRunning);
-                    }
-                    requestMap.remove(context);
+
+        final List requestList = requestMap.get(context);
+        requestMap.remove(context);
+
+        if (Looper.myLooper() == Looper.getMainLooper()) {
+            Runnable runnable = new Runnable() {
+                @Override
+                public void run() {
+                    cancelRequests(requestList, mayInterruptIfRunning);
+                }
+            };
+            threadPool.submit(runnable);
+        } else {
+            cancelRequests(requestList, mayInterruptIfRunning);
+        }
+    }
+
+    private void cancelRequests(final List requestList, final boolean mayInterruptIfRunning) {
+        if (requestList != null) {
+            for (RequestHandle requestHandle : requestList) {
+                requestHandle.cancel(mayInterruptIfRunning);
+            }
+        }
+    }
+
+    /**
+     * Cancels all pending (or potentially active) requests. 

 

Note: This will + * only affect requests which were created with a non-null android Context. This method is + * intended to be used in the onDestroy method of your android activities to destroy all + * requests which are no longer required. + * + * @param mayInterruptIfRunning specifies if active requests should be cancelled along with + * pending requests. + */ + public void cancelAllRequests(boolean mayInterruptIfRunning) { + for (List requestList : requestMap.values()) { + if (requestList != null) { + for (RequestHandle requestHandle : requestList) { + requestHandle.cancel(mayInterruptIfRunning); } } - }; - if (Looper.myLooper() == Looper.getMainLooper()) { - new Thread(r).start(); - } else { - r.run(); } + requestMap.clear(); } /** - * Cancels all pending (or potentially active) requests.

 

Note: This will - * only affect requests which were created with a non-null android Context. This method is - * intended to be used in the onDestroy method of your android activities to destroy all - * requests which are no longer required. + * Allows you to cancel all requests currently in queue or running, by set TAG, + * if passed TAG is null, will not attempt to cancel any requests, if TAG is null + * on RequestHandle, it cannot be canceled by this call * + * @param TAG TAG to be matched in RequestHandle * @param mayInterruptIfRunning specifies if active requests should be cancelled along with * pending requests. */ - public void cancelAllRequests(boolean mayInterruptIfRunning) { + public void cancelRequestsByTAG(Object TAG, boolean mayInterruptIfRunning) { + if (TAG == null) { + log.d(LOG_TAG, "cancelRequestsByTAG, passed TAG is null, cannot proceed"); + return; + } for (List requestList : requestMap.values()) { if (requestList != null) { for (RequestHandle requestHandle : requestList) { - requestHandle.cancel(mayInterruptIfRunning); + if (TAG.equals(requestHandle.getTag())) + requestHandle.cancel(mayInterruptIfRunning); } } } - requestMap.clear(); } - // [+] HTTP HEAD + // [-] HTTP HEAD + // [+] HTTP OPTIONS + + /** + * Perform a HTTP OPTIONS request, without any parameters. + * + * @param url the URL to send the request to. + * @param responseHandler the response handler instance that should handle the response. + * @return RequestHandle of future request process + */ + public RequestHandle options(String url, ResponseHandlerInterface responseHandler) { + return options(null, url, null, responseHandler); + } + + public RequestHandle options(Context context, String url, RequestParams params, ResponseHandlerInterface responseHandler) { + return sendRequest(httpClient, httpContext, new HttpOptions(getUrlWithQueryString(isUrlEncodingEnabled(), url, params)), null, responseHandler, context); + } + + // [-] HTTP OPTIONS + // [+] HTTP GET /** * Perform a HTTP HEAD request, without any parameters. @@ -818,9 +1076,6 @@ public RequestHandle head(Context context, String url, Header[] headers, Request context); } - // [-] HTTP HEAD - // [+] HTTP GET - /** * Perform a HTTP GET request, without any parameters. * @@ -832,6 +1087,9 @@ public RequestHandle get(String url, ResponseHandlerInterface responseHandler) { return get(null, url, null, responseHandler); } + // [-] HTTP GET + // [+] HTTP POST + /** * Perform a HTTP GET request with parameters. * @@ -888,8 +1146,22 @@ public RequestHandle get(Context context, String url, Header[] headers, RequestP context); } - // [-] HTTP GET - // [+] HTTP POST + /** + * Perform a HTTP GET request and track the Android Context which initiated the request. + * + * @param context the Android Context which initiated the request. + * @param url the URL to send the request to. + * @param entity a raw {@link cz.msebera.android.httpclient.HttpEntity} to send with the request, for + * example, use this to send string/json/xml payloads to a server by + * passing a {@link cz.msebera.android.httpclient.entity.StringEntity}. + * @param contentType the content type of the payload you are sending, for example + * application/json if sending a json payload. + * @param responseHandler the response ha ndler instance that should handle the response. + * @return RequestHandle of future request process + */ + public RequestHandle get(Context context, String url, HttpEntity entity, String contentType, ResponseHandlerInterface responseHandler) { + return sendRequest(httpClient, httpContext, addEntityToRequestBase(new HttpGet(URI.create(url).normalize()), entity), contentType, responseHandler, context); + } /** * Perform a HTTP POST request, without any parameters. @@ -902,6 +1174,9 @@ public RequestHandle post(String url, ResponseHandlerInterface responseHandler) return post(null, url, null, responseHandler); } + // [-] HTTP POST + // [+] HTTP PUT + /** * Perform a HTTP POST request with parameters. * @@ -932,16 +1207,16 @@ public RequestHandle post(Context context, String url, RequestParams params, Res * * @param context the Android Context which initiated the request. * @param url the URL to send the request to. - * @param entity a raw {@link org.apache.http.HttpEntity} to send with the request, for + * @param entity a raw {@link cz.msebera.android.httpclient.HttpEntity} to send with the request, for * example, use this to send string/json/xml payloads to a server by - * passing a {@link org.apache.http.entity.StringEntity}. + * passing a {@link cz.msebera.android.httpclient.entity.StringEntity}. * @param contentType the content type of the payload you are sending, for example * application/json if sending a json payload. * @param responseHandler the response ha ndler instance that should handle the response. * @return RequestHandle of future request process */ public RequestHandle post(Context context, String url, HttpEntity entity, String contentType, ResponseHandlerInterface responseHandler) { - return sendRequest(httpClient, httpContext, addEntityToRequestBase(new HttpPost(URI.create(url).normalize()), entity), contentType, responseHandler, context); + return sendRequest(httpClient, httpContext, addEntityToRequestBase(new HttpPost(getURI(url)), entity), contentType, responseHandler, context); } /** @@ -959,7 +1234,7 @@ public RequestHandle post(Context context, String url, HttpEntity entity, String */ public RequestHandle post(Context context, String url, Header[] headers, RequestParams params, String contentType, ResponseHandlerInterface responseHandler) { - HttpEntityEnclosingRequestBase request = new HttpPost(URI.create(url).normalize()); + HttpEntityEnclosingRequestBase request = new HttpPost(getURI(url)); if (params != null) request.setEntity(paramsToEntity(params, responseHandler)); if (headers != null) request.setHeaders(headers); return sendRequest(httpClient, httpContext, request, contentType, @@ -975,7 +1250,7 @@ public RequestHandle post(Context context, String url, Header[] headers, Request * @param headers set headers only for this request * @param entity a raw {@link HttpEntity} to send with the request, for example, use * this to send string/json/xml payloads to a server by passing a {@link - * org.apache.http.entity.StringEntity}. + * cz.msebera.android.httpclient.entity.StringEntity}. * @param contentType the content type of the payload you are sending, for example * application/json if sending a json payload. * @param responseHandler the response handler instance that should handle the response. @@ -983,14 +1258,11 @@ public RequestHandle post(Context context, String url, Header[] headers, Request */ public RequestHandle post(Context context, String url, Header[] headers, HttpEntity entity, String contentType, ResponseHandlerInterface responseHandler) { - HttpEntityEnclosingRequestBase request = addEntityToRequestBase(new HttpPost(URI.create(url).normalize()), entity); + HttpEntityEnclosingRequestBase request = addEntityToRequestBase(new HttpPost(getURI(url)), entity); if (headers != null) request.setHeaders(headers); return sendRequest(httpClient, httpContext, request, contentType, responseHandler, context); } - // [-] HTTP POST - // [+] HTTP PUT - /** * Perform a HTTP PUT request, without any parameters. * @@ -1035,14 +1307,14 @@ public RequestHandle put(Context context, String url, RequestParams params, Resp * @param url the URL to send the request to. * @param entity a raw {@link HttpEntity} to send with the request, for example, use * this to send string/json/xml payloads to a server by passing a {@link - * org.apache.http.entity.StringEntity}. + * cz.msebera.android.httpclient.entity.StringEntity}. * @param contentType the content type of the payload you are sending, for example * application/json if sending a json payload. * @param responseHandler the response handler instance that should handle the response. * @return RequestHandle of future request process */ public RequestHandle put(Context context, String url, HttpEntity entity, String contentType, ResponseHandlerInterface responseHandler) { - return sendRequest(httpClient, httpContext, addEntityToRequestBase(new HttpPut(URI.create(url).normalize()), entity), contentType, responseHandler, context); + return sendRequest(httpClient, httpContext, addEntityToRequestBase(new HttpPut(getURI(url)), entity), contentType, responseHandler, context); } /** @@ -1054,14 +1326,14 @@ public RequestHandle put(Context context, String url, HttpEntity entity, String * @param headers set one-time headers for this request * @param entity a raw {@link HttpEntity} to send with the request, for example, use * this to send string/json/xml payloads to a server by passing a {@link - * org.apache.http.entity.StringEntity}. + * cz.msebera.android.httpclient.entity.StringEntity}. * @param contentType the content type of the payload you are sending, for example * application/json if sending a json payload. * @param responseHandler the response handler instance that should handle the response. * @return RequestHandle of future request process */ public RequestHandle put(Context context, String url, Header[] headers, HttpEntity entity, String contentType, ResponseHandlerInterface responseHandler) { - HttpEntityEnclosingRequestBase request = addEntityToRequestBase(new HttpPut(URI.create(url).normalize()), entity); + HttpEntityEnclosingRequestBase request = addEntityToRequestBase(new HttpPut(getURI(url)), entity); if (headers != null) request.setHeaders(headers); return sendRequest(httpClient, httpContext, request, contentType, responseHandler, context); } @@ -1069,6 +1341,81 @@ public RequestHandle put(Context context, String url, Header[] headers, HttpEnti // [-] HTTP PUT // [+] HTTP DELETE + /** + * Perform a HTTP + * request, without any parameters. + * + * @param url the URL to send the request to. + * @param responseHandler the response handler instance that should handle the response. + * @return RequestHandle of future request process + */ + public RequestHandle patch(String url, ResponseHandlerInterface responseHandler) { + return patch(null, url, null, responseHandler); + } + + /** + * Perform a HTTP PATCH request with parameters. + * + * @param url the URL to send the request to. + * @param params additional PUT parameters or files to send with the request. + * @param responseHandler the response handler instance that should handle the response. + * @return RequestHandle of future request process + */ + public RequestHandle patch(String url, RequestParams params, ResponseHandlerInterface responseHandler) { + return patch(null, url, params, responseHandler); + } + + /** + * Perform a HTTP PATCH request and track the Android Context which initiated the request. + * + * @param context the Android Context which initiated the request. + * @param url the URL to send the request to. + * @param params additional PUT parameters or files to send with the request. + * @param responseHandler the response handler instance that should handle the response. + * @return RequestHandle of future request process + */ + public RequestHandle patch(Context context, String url, RequestParams params, ResponseHandlerInterface responseHandler) { + return patch(context, url, paramsToEntity(params, responseHandler), null, responseHandler); + } + + /** + * Perform a HTTP PATCH request and track the Android Context which initiated the request. + * + * @param context the Android Context which initiated the request. + * @param url the URL to send the request to. + * @param responseHandler the response handler instance that should handle the response. + * @param entity a raw {@link HttpEntity} to send with the request, for example, use + * this to send string/json/xml payloads to a server by passing a {@link + * cz.msebera.android.httpclient.entity.StringEntity} + * @param contentType the content type of the payload you are sending, for example + * "application/json" if sending a json payload. + * @return RequestHandle of future request process + */ + public RequestHandle patch(Context context, String url, HttpEntity entity, String contentType, ResponseHandlerInterface responseHandler) { + return sendRequest(httpClient, httpContext, addEntityToRequestBase(new HttpPatch(getURI(url)), entity), contentType, responseHandler, context); + } + + /** + * Perform a HTTP PATCH request and track the Android Context which initiated the request. And set + * one-time headers for the request + * + * @param context the Android Context which initiated the request. + * @param url the URL to send the request to. + * @param headers set one-time headers for this request + * @param entity a raw {@link HttpEntity} to send with the request, for example, use + * this to send string/json/xml payloads to a server by passing a {@link + * cz.msebera.android.httpclient.entity.StringEntity}. + * @param contentType the content type of the payload you are sending, for example + * application/json if sending a json payload. + * @param responseHandler the response handler instance that should handle the response. + * @return RequestHandle of future request process + */ + public RequestHandle patch(Context context, String url, Header[] headers, HttpEntity entity, String contentType, ResponseHandlerInterface responseHandler) { + HttpEntityEnclosingRequestBase request = addEntityToRequestBase(new HttpPatch(getURI(url)), entity); + if (headers != null) request.setHeaders(headers); + return sendRequest(httpClient, httpContext, request, contentType, responseHandler, context); + } + /** * Perform a HTTP DELETE request. * @@ -1080,6 +1427,8 @@ public RequestHandle delete(String url, ResponseHandlerInterface responseHandler return delete(null, url, responseHandler); } + // [-] HTTP DELETE + /** * Perform a HTTP DELETE request. * @@ -1089,7 +1438,7 @@ public RequestHandle delete(String url, ResponseHandlerInterface responseHandler * @return RequestHandle of future request process */ public RequestHandle delete(Context context, String url, ResponseHandlerInterface responseHandler) { - final HttpDelete delete = new HttpDelete(URI.create(url).normalize()); + final HttpDelete delete = new HttpDelete(getURI(url)); return sendRequest(httpClient, httpContext, delete, null, responseHandler, context); } @@ -1103,11 +1452,24 @@ public RequestHandle delete(Context context, String url, ResponseHandlerInterfac * @return RequestHandle of future request process */ public RequestHandle delete(Context context, String url, Header[] headers, ResponseHandlerInterface responseHandler) { - final HttpDelete delete = new HttpDelete(URI.create(url).normalize()); + final HttpDelete delete = new HttpDelete(getURI(url)); if (headers != null) delete.setHeaders(headers); return sendRequest(httpClient, httpContext, delete, null, responseHandler, context); } + /** + * Perform a HTTP DELETE request. + * + * @param url the URL to send the request to. + * @param params additional DELETE parameters or files to send with the request. + * @param responseHandler the response handler instance that should handle the response. + * @return RequestHandle of future request process + */ + public RequestHandle delete(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) { + final HttpDelete delete = new HttpDelete(getUrlWithQueryString(isUrlEncodingEnabled, url, params)); + return sendRequest(httpClient, httpContext, delete, null, responseHandler, null); + } + /** * Perform a HTTP DELETE request. * @@ -1124,7 +1486,22 @@ public RequestHandle delete(Context context, String url, Header[] headers, Reque return sendRequest(httpClient, httpContext, httpDelete, null, responseHandler, context); } - // [-] HTTP DELETE + /** + * Perform a HTTP DELETE request and track the Android Context which initiated the request. + * + * @param context the Android Context which initiated the request. + * @param url the URL to send the request to. + * @param entity a raw {@link cz.msebera.android.httpclient.HttpEntity} to send with the request, for + * example, use this to send string/json/xml payloads to a server by + * passing a {@link cz.msebera.android.httpclient.entity.StringEntity}. + * @param contentType the content type of the payload you are sending, for example + * application/json if sending a json payload. + * @param responseHandler the response ha ndler instance that should handle the response. + * @return RequestHandle of future request process + */ + public RequestHandle delete(Context context, String url, HttpEntity entity, String contentType, ResponseHandlerInterface responseHandler) { + return sendRequest(httpClient, httpContext, addEntityToRequestBase(new HttpDelete(URI.create(url).normalize()), entity), contentType, responseHandler, context); + } /** * Instantiate a new asynchronous HTTP request for the passed parameters. @@ -1163,13 +1540,13 @@ protected RequestHandle sendRequest(DefaultHttpClient client, HttpContext httpCo throw new IllegalArgumentException("ResponseHandler must not be null"); } - if (responseHandler.getUseSynchronousMode()) { + if (responseHandler.getUseSynchronousMode() && !responseHandler.getUsePoolThread()) { throw new IllegalArgumentException("Synchronous ResponseHandler used in AsyncHttpClient. You should create your response handler in a looper thread or use SyncHttpClient instead."); } if (contentType != null) { - if (uriRequest instanceof HttpEntityEnclosingRequestBase && ((HttpEntityEnclosingRequestBase) uriRequest).getEntity() != null) { - Log.w(LOG_TAG, "Passed contentType will be ignored because HttpEntity sets content type"); + if (uriRequest instanceof HttpEntityEnclosingRequestBase && ((HttpEntityEnclosingRequestBase) uriRequest).getEntity() != null && uriRequest.containsHeader(HEADER_CONTENT_TYPE)) { + log.w(LOG_TAG, "Passed contentType will be ignored because HttpEntity sets content type"); } else { uriRequest.setHeader(HEADER_CONTENT_TYPE, contentType); } @@ -1183,9 +1560,10 @@ protected RequestHandle sendRequest(DefaultHttpClient client, HttpContext httpCo RequestHandle requestHandle = new RequestHandle(request); if (context != null) { + List requestList; // Add request to request map - List requestList = requestMap.get(context); synchronized (requestMap) { + requestList = requestMap.get(context); if (requestList == null) { requestList = Collections.synchronizedList(new LinkedList()); requestMap.put(context, requestList); @@ -1206,98 +1584,23 @@ protected RequestHandle sendRequest(DefaultHttpClient client, HttpContext httpCo } /** - * Sets state of URL encoding feature, see bug #227, this method allows you to turn off and on - * this auto-magic feature on-demand. - * - * @param enabled desired state of feature - */ - public void setURLEncodingEnabled(boolean enabled) { - this.isUrlEncodingEnabled = enabled; - } - - /** - * Will encode url, if not disabled, and adds params on the end of it - * - * @param url String with URL, should be valid URL without params - * @param params RequestParams to be appended on the end of URL - * @param shouldEncodeUrl whether url should be encoded (replaces spaces with %20) - * @return encoded url if requested with params appended if any available - */ - public static String getUrlWithQueryString(boolean shouldEncodeUrl, String url, RequestParams params) { - if (url == null) - return null; - - if (shouldEncodeUrl) { - try { - url = URLEncoder.encode(url, "UTF-8"); - } catch (UnsupportedEncodingException e) { - // Should not really happen, added just for sake of validity - Log.e(LOG_TAG, "getUrlWithQueryString encoding URL", e); - } - } - - if (params != null) { - // Construct the query string and trim it, in case it - // includes any excessive white spaces. - String paramString = params.getParamString().trim(); - - // Only add the query string if it isn't empty and it - // isn't equal to '?'. - if (!paramString.equals("") && !paramString.equals("?")) { - url += url.contains("?") ? "&" : "?"; - url += paramString; - } - } - - return url; - } - - /** - * Checks the InputStream if it contains GZIP compressed data - * - * @param inputStream InputStream to be checked - * @return true or false if the stream contains GZIP compressed data - * @throws java.io.IOException - */ - public static boolean isInputStreamGZIPCompressed(final PushbackInputStream inputStream) throws IOException { - if (inputStream == null) - return false; - - byte[] signature = new byte[2]; - int readStatus = inputStream.read(signature); - inputStream.unread(signature); - int streamHeader = ((int) signature[0] & 0xff) | ((signature[1] << 8) & 0xff00); - return readStatus == 2 && GZIPInputStream.GZIP_MAGIC == streamHeader; - } - - /** - * A utility function to close an input stream without raising an exception. + * Returns a {@link URI} instance for the specified, absolute URL string. * - * @param is input stream to close safely + * @param url absolute URL string, containing scheme, host and path + * @return URI instance for the URL string */ - public static void silentCloseInputStream(InputStream is) { - try { - if (is != null) { - is.close(); - } - } catch (IOException e) { - Log.w(LOG_TAG, "Cannot close input stream", e); - } + protected URI getURI(String url) { + return URI.create(url).normalize(); } /** - * A utility function to close an output stream without raising an exception. + * Sets state of URL encoding feature, see bug #227, this method allows you to turn off and on + * this auto-magic feature on-demand. * - * @param os output stream to close safely + * @param enabled desired state of feature */ - public static void silentCloseOutputStream(OutputStream os) { - try { - if (os != null) { - os.close(); - } - } catch (IOException e) { - Log.w(LOG_TAG, "Cannot close output stream", e); - } + public void setURLEncodingEnabled(boolean enabled) { + this.isUrlEncodingEnabled = enabled; } /** @@ -1344,49 +1647,19 @@ private HttpEntityEnclosingRequestBase addEntityToRequestBase(HttpEntityEnclosin return requestBase; } - /** - * This horrible hack is required on Android, due to implementation of BasicManagedEntity, which - * doesn't chain call consumeContent on underlying wrapped HttpEntity - * - * @param entity HttpEntity, may be null - */ - public static void endEntityViaReflection(HttpEntity entity) { - if (entity instanceof HttpEntityWrapper) { - try { - Field f = null; - Field[] fields = HttpEntityWrapper.class.getDeclaredFields(); - for (Field ff : fields) { - if (ff.getName().equals("wrappedEntity")) { - f = ff; - break; - } - } - if (f != null) { - f.setAccessible(true); - HttpEntity wrapped = (HttpEntity) f.get(entity); - if (wrapped != null) { - wrapped.consumeContent(); - } - } - } catch (Throwable t) { - Log.e(LOG_TAG, "wrappedEntity consume", t); - } - } - } - /** * Enclosing entity to hold stream of gzip decoded data for accessing HttpEntity contents */ private static class InflatingEntity extends HttpEntityWrapper { - public InflatingEntity(HttpEntity wrapped) { - super(wrapped); - } - InputStream wrappedStream; PushbackInputStream pushbackStream; GZIPInputStream gzippedStream; + public InflatingEntity(HttpEntity wrapped) { + super(wrapped); + } + @Override public InputStream getContent() throws IOException { wrappedStream = wrappedEntity.getContent(); @@ -1412,4 +1685,12 @@ public void consumeContent() throws IOException { super.consumeContent(); } } + + /** + * Call this method if your app target android below 4.4 + * This method enable sni in android below 4.4 + */ + public static void useConscryptSSLProvider(){ + ConscryptSSLProvider.install(); + } } diff --git a/library/src/main/java/com/loopj/android/http/AsyncHttpRequest.java b/library/src/main/java/com/loopj/android/http/AsyncHttpRequest.java index 682a4f7b7..346cde73e 100755 --- a/library/src/main/java/com/loopj/android/http/AsyncHttpRequest.java +++ b/library/src/main/java/com/loopj/android/http/AsyncHttpRequest.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Copyright (c) 2011 James Smith - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -18,17 +18,16 @@ package com.loopj.android.http; -import android.util.Log; - -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpRequestRetryHandler; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.impl.client.AbstractHttpClient; -import org.apache.http.protocol.HttpContext; - import java.io.IOException; import java.net.MalformedURLException; import java.net.UnknownHostException; +import java.util.concurrent.atomic.AtomicBoolean; + +import cz.msebera.android.httpclient.HttpResponse; +import cz.msebera.android.httpclient.client.HttpRequestRetryHandler; +import cz.msebera.android.httpclient.client.methods.HttpUriRequest; +import cz.msebera.android.httpclient.impl.client.AbstractHttpClient; +import cz.msebera.android.httpclient.protocol.HttpContext; /** * Internal class, representing the HttpRequest, done in asynchronous manner @@ -38,24 +37,24 @@ public class AsyncHttpRequest implements Runnable { private final HttpContext context; private final HttpUriRequest request; private final ResponseHandlerInterface responseHandler; + private final AtomicBoolean isCancelled = new AtomicBoolean(); private int executionCount; - private boolean isCancelled; private boolean cancelIsNotified; - private boolean isFinished; + private volatile boolean isFinished; private boolean isRequestPreProcessed; public AsyncHttpRequest(AbstractHttpClient client, HttpContext context, HttpUriRequest request, ResponseHandlerInterface responseHandler) { - this.client = client; - this.context = context; - this.request = request; - this.responseHandler = responseHandler; + this.client = Utils.notNull(client, "client"); + this.context = Utils.notNull(context, "context"); + this.request = Utils.notNull(request, "request"); + this.responseHandler = Utils.notNull(responseHandler, "responseHandler"); } /** * This method is called once by the system when the request is about to be * processed by the system. The library makes sure that a single request * is pre-processed only once. - * + *

 

* Please note: pre-processing does NOT run on the main thread, and thus * any UI activities that you must perform should be properly dispatched to * the app's UI thread. @@ -70,7 +69,7 @@ public void onPreProcessRequest(AsyncHttpRequest request) { * This method is called once by the system when the request has been fully * sent, handled and finished. The library makes sure that a single request * is post-processed only once. - * + *

 

* Please note: post-processing does NOT run on the main thread, and thus * any UI activities that you must perform should be properly dispatched to * the app's UI thread. @@ -97,9 +96,7 @@ public void run() { return; } - if (responseHandler != null) { - responseHandler.sendStartMessage(); - } + responseHandler.sendStartMessage(); if (isCancelled()) { return; @@ -108,10 +105,10 @@ public void run() { try { makeRequestWithRetries(); } catch (IOException e) { - if (!isCancelled() && responseHandler != null) { + if (!isCancelled()) { responseHandler.sendFailureMessage(0, null, null, e); } else { - Log.e("AsyncHttpRequest", "makeRequestWithRetries returned error, but handler is null", e); + AsyncHttpClient.log.e("AsyncHttpRequest", "makeRequestWithRetries returned error", e); } } @@ -119,9 +116,7 @@ public void run() { return; } - if (responseHandler != null) { - responseHandler.sendFinishMessage(); - } + responseHandler.sendFinishMessage(); if (isCancelled()) { return; @@ -150,7 +145,7 @@ private void makeRequest() throws IOException { HttpResponse response = client.execute(request, context); - if (isCancelled() || responseHandler == null) { + if (isCancelled()) { return; } @@ -185,12 +180,12 @@ private void makeRequestWithRetries() throws IOException { // switching between WI-FI and mobile data networks can cause a retry which then results in an UnknownHostException // while the WI-FI is initialising. The retry logic will be invoked here, if this is NOT the first retry // (to assist in genuine cases of unknown host) which seems better than outright failure - cause = new IOException("UnknownHostException exception: " + e.getMessage()); - retry = (executionCount > 0) && retryHandler.retryRequest(cause, ++executionCount, context); + cause = new IOException("UnknownHostException exception: " + e.getMessage(), e); + retry = (executionCount > 0) && retryHandler.retryRequest(e, ++executionCount, context); } catch (NullPointerException e) { // there's a bug in HttpClient 4.0.x that on some occasions causes // DefaultRequestExecutor to throw an NPE, see - // http://code.google.com/p/android/issues/detail?id=5255 + // https://code.google.com/p/android/issues/detail?id=5255 cause = new IOException("NPE in HttpClient: " + e.getMessage()); retry = retryHandler.retryRequest(cause, ++executionCount, context); } catch (IOException e) { @@ -201,14 +196,14 @@ private void makeRequestWithRetries() throws IOException { cause = e; retry = retryHandler.retryRequest(cause, ++executionCount, context); } - if (retry && (responseHandler != null)) { + if (retry) { responseHandler.sendRetryMessage(executionCount); } } } catch (Exception e) { // catch anything else to ensure failure message is propagated - Log.e("AsyncHttpRequest", "Unhandled exception origin cause", e); - cause = new IOException("Unhandled exception: " + e.getMessage()); + AsyncHttpClient.log.e("AsyncHttpRequest", "Unhandled exception origin cause", e); + cause = new IOException("Unhandled exception: " + e.getMessage(), cause); } // cleaned up to throw IOException @@ -216,17 +211,17 @@ private void makeRequestWithRetries() throws IOException { } public boolean isCancelled() { - if (isCancelled) { + boolean cancelled = isCancelled.get(); + if (cancelled) { sendCancelNotification(); } - return isCancelled; + return cancelled; } private synchronized void sendCancelNotification() { - if (!isFinished && isCancelled && !cancelIsNotified) { + if (!isFinished && isCancelled.get() && !cancelIsNotified) { cancelIsNotified = true; - if (responseHandler != null) - responseHandler.sendCancelMessage(); + responseHandler.sendCancelMessage(); } } @@ -235,8 +230,28 @@ public boolean isDone() { } public boolean cancel(boolean mayInterruptIfRunning) { - isCancelled = true; + isCancelled.set(true); request.abort(); return isCancelled(); } + + /** + * Will set Object as TAG to this request, wrapped by WeakReference + * + * @param TAG Object used as TAG to this RequestHandle + * @return this AsyncHttpRequest to allow fluid syntax + */ + public AsyncHttpRequest setRequestTag(Object TAG) { + this.responseHandler.setTag(TAG); + return this; + } + + /** + * Will return TAG of this AsyncHttpRequest + * + * @return Object TAG, can be null, if it's been already garbage collected + */ + public Object getTag() { + return this.responseHandler.getTag(); + } } diff --git a/library/src/main/java/com/loopj/android/http/AsyncHttpResponseHandler.java b/library/src/main/java/com/loopj/android/http/AsyncHttpResponseHandler.java index 6943584af..096020d9e 100755 --- a/library/src/main/java/com/loopj/android/http/AsyncHttpResponseHandler.java +++ b/library/src/main/java/com/loopj/android/http/AsyncHttpResponseHandler.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Copyright (c) 2011 James Smith - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -21,29 +21,30 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.util.Log; - -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.StatusLine; -import org.apache.http.client.HttpResponseException; -import org.apache.http.util.ByteArrayBuffer; import java.io.IOException; import java.io.InputStream; +import java.lang.ref.WeakReference; import java.net.URI; +import java.util.Locale; + +import cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.HttpEntity; +import cz.msebera.android.httpclient.HttpResponse; +import cz.msebera.android.httpclient.StatusLine; +import cz.msebera.android.httpclient.client.HttpResponseException; +import cz.msebera.android.httpclient.util.ByteArrayBuffer; /** * Used to intercept and handle the responses from requests made using {@link AsyncHttpClient}. The - * {@link #onSuccess(int, org.apache.http.Header[], byte[])} method is designed to be anonymously + * {@link #onSuccess(int, cz.msebera.android.httpclient.Header[], byte[])} method is designed to be anonymously * overridden with your own response handling code.

 

Additionally, you can override the - * {@link #onFailure(int, org.apache.http.Header[], byte[], Throwable)}, {@link #onStart()}, {@link - * #onFinish()}, {@link #onRetry(int)} and {@link #onProgress(int, int)} methods as required. + * {@link #onFailure(int, cz.msebera.android.httpclient.Header[], byte[], Throwable)}, {@link #onStart()}, {@link + * #onFinish()}, {@link #onRetry(int)} and {@link #onProgress(long, long)} methods as required. *

 

For example:

 

*
  * AsyncHttpClient client = new AsyncHttpClient();
- * client.get("http://www.google.com/", new AsyncHttpResponseHandler() {
+ * client.get("https://www.google.com/", new AsyncHttpResponseHandler() {
  *     @Override
  *     public void onStart() {
  *         // Initiated the request
@@ -67,7 +68,7 @@
  *     }
  *
  *     @Override
- *     public void onProgress(int bytesWritten, int totalSize) {
+ *     public void onProgress(long bytesWritten, long totalSize) {
  *         // Progress notification
  *     }
  *
@@ -78,10 +79,11 @@
  * });
  * 
*/ +@SuppressWarnings("DesignForExtension") public abstract class AsyncHttpResponseHandler implements ResponseHandlerInterface { - private static final String LOG_TAG = "AsyncHttpResponseHandler"; - + public static final String DEFAULT_CHARSET = "UTF-8"; + public static final String UTF8_BOM = "\uFEFF"; protected static final int SUCCESS_MESSAGE = 0; protected static final int FAILURE_MESSAGE = 1; protected static final int START_MESSAGE = 2; @@ -89,27 +91,77 @@ public abstract class AsyncHttpResponseHandler implements ResponseHandlerInterfa protected static final int PROGRESS_MESSAGE = 4; protected static final int RETRY_MESSAGE = 5; protected static final int CANCEL_MESSAGE = 6; - protected static final int BUFFER_SIZE = 4096; - - public static final String DEFAULT_CHARSET = "UTF-8"; - public static final String UTF8_BOM = "\uFEFF"; + private static final String LOG_TAG = "AsyncHttpRH"; private String responseCharset = DEFAULT_CHARSET; private Handler handler; private boolean useSynchronousMode; + private boolean usePoolThread; private URI requestURI = null; private Header[] requestHeaders = null; private Looper looper = null; + private WeakReference TAG = new WeakReference(null); + + /** + * Creates a new AsyncHttpResponseHandler + */ + public AsyncHttpResponseHandler() { + this(null); + } + + /** + * Creates a new AsyncHttpResponseHandler with a user-supplied looper. If + * the passed looper is null, the looper attached to the current thread will + * be used. + * + * @param looper The looper to work with + */ + public AsyncHttpResponseHandler(Looper looper) { + // Do not use the pool's thread to fire callbacks by default. + this(looper == null ? Looper.myLooper() : looper, false); + } + + /** + * Creates a new AsyncHttpResponseHandler and decide whether the callbacks + * will be fired on current thread's looper or the pool thread's. + * + * @param usePoolThread Whether to use the pool's thread to fire callbacks + */ + public AsyncHttpResponseHandler(boolean usePoolThread) { + this(usePoolThread ? null : Looper.myLooper(), usePoolThread); + } + + private AsyncHttpResponseHandler(Looper looper, boolean usePoolThread) { + if (!usePoolThread) { + Utils.asserts(looper != null, "use looper thread, must call Looper.prepare() first!"); + this.looper = looper; + // Create a handler on current thread to submit tasks + this.handler = new ResponderHandler(this, looper); + } else { + Utils.asserts(looper == null, "use pool thread, looper should be null!"); + // If pool thread is to be used, there's no point in keeping a reference + // to the looper and handler. + this.looper = null; + this.handler = null; + } + + this.usePoolThread = usePoolThread; + } @Override - public URI getRequestURI() { - return this.requestURI; + public Object getTag() { + return this.TAG.get(); } @Override - public Header[] getRequestHeaders() { - return this.requestHeaders; + public void setTag(Object TAG) { + this.TAG = new WeakReference(TAG); + } + + @Override + public URI getRequestURI() { + return this.requestURI; } @Override @@ -118,25 +170,13 @@ public void setRequestURI(URI requestURI) { } @Override - public void setRequestHeaders(Header[] requestHeaders) { - this.requestHeaders = requestHeaders; + public Header[] getRequestHeaders() { + return this.requestHeaders; } - /** - * Avoid leaks by using a non-anonymous handler class. - */ - private static class ResponderHandler extends Handler { - private final AsyncHttpResponseHandler mResponder; - - ResponderHandler(AsyncHttpResponseHandler mResponder, Looper looper) { - super(looper); - this.mResponder = mResponder; - } - - @Override - public void handleMessage(Message msg) { - mResponder.handleMessage(msg); - } + @Override + public void setRequestHeaders(Header[] requestHeaders) { + this.requestHeaders = requestHeaders; } @Override @@ -149,7 +189,7 @@ public void setUseSynchronousMode(boolean sync) { // A looper must be prepared before setting asynchronous mode. if (!sync && looper == null) { sync = true; - Log.w(LOG_TAG, "Current thread has not called Looper.prepare(). Forcing synchronous mode."); + AsyncHttpClient.log.w(LOG_TAG, "Current thread has not called Looper.prepare(). Forcing synchronous mode."); } // If using asynchronous mode. @@ -164,38 +204,35 @@ public void setUseSynchronousMode(boolean sync) { useSynchronousMode = sync; } - /** - * Sets the charset for the response string. If not set, the default is UTF-8. - * - * @param charset to be used for the response string. - * @see Charset - */ - public void setCharset(final String charset) { - this.responseCharset = charset; + @Override + public boolean getUsePoolThread() { + return usePoolThread; } - public String getCharset() { - return this.responseCharset == null ? DEFAULT_CHARSET : this.responseCharset; + @Override + public void setUsePoolThread(boolean pool) { + // If pool thread is to be used, there's no point in keeping a reference + // to the looper and no need for a handler. + if (pool) { + looper = null; + handler = null; + } + + usePoolThread = pool; } - /** - * Creates a new AsyncHttpResponseHandler - */ - public AsyncHttpResponseHandler() { - this(null); + public String getCharset() { + return this.responseCharset == null ? DEFAULT_CHARSET : this.responseCharset; } /** - * Creates a new AsyncHttpResponseHandler with a user-supplied looper. If - * the passed looper is null, the looper attached to the current thread will - * be used. + * Sets the charset for the response string. If not set, the default is UTF-8. * - * @param looper The looper to work with + * @param charset to be used for the response string. + * @see Charset */ - public AsyncHttpResponseHandler(Looper looper) { - this.looper = looper == null ? Looper.myLooper() : looper; - // Use asynchronous mode by default. - setUseSynchronousMode(false); + public void setCharset(final String charset) { + this.responseCharset = charset; } /** @@ -204,8 +241,8 @@ public AsyncHttpResponseHandler(Looper looper) { * @param bytesWritten offset from start of file * @param totalSize total size of file */ - public void onProgress(int bytesWritten, int totalSize) { - Log.v(LOG_TAG, String.format("Progress %d from %d (%2.0f%%)", bytesWritten, totalSize, (totalSize > 0) ? (bytesWritten * 1.0 / totalSize) * 100 : -1)); + public void onProgress(long bytesWritten, long totalSize) { + AsyncHttpClient.log.v(LOG_TAG, String.format(Locale.US, "Progress %d from %d (%2.0f%%)", bytesWritten, totalSize, (totalSize > 0) ? (bytesWritten * 1.0 / totalSize) * 100 : -1)); } /** @@ -258,15 +295,20 @@ public void onPostProcessResponse(ResponseHandlerInterface instance, HttpRespons * @param retryNo number of retry */ public void onRetry(int retryNo) { - Log.d(LOG_TAG, String.format("Request retry no. %d", retryNo)); + AsyncHttpClient.log.d(LOG_TAG, String.format(Locale.US, "Request retry no. %d", retryNo)); } public void onCancel() { - Log.d(LOG_TAG, "Request got cancelled"); + AsyncHttpClient.log.d(LOG_TAG, "Request got cancelled"); + } + + public void onUserException(Throwable error) { + AsyncHttpClient.log.e(LOG_TAG, "User-space exception detected!", error); + throw new RuntimeException(error); } @Override - final public void sendProgressMessage(int bytesWritten, int bytesTotal) { + final public void sendProgressMessage(long bytesWritten, long bytesTotal) { sendMessage(obtainMessage(PROGRESS_MESSAGE, new Object[]{bytesWritten, bytesTotal})); } @@ -304,52 +346,56 @@ final public void sendCancelMessage() { protected void handleMessage(Message message) { Object[] response; - switch (message.what) { - case SUCCESS_MESSAGE: - response = (Object[]) message.obj; - if (response != null && response.length >= 3) { - onSuccess((Integer) response[0], (Header[]) response[1], (byte[]) response[2]); - } else { - Log.e(LOG_TAG, "SUCCESS_MESSAGE didn't got enough params"); - } - break; - case FAILURE_MESSAGE: - response = (Object[]) message.obj; - if (response != null && response.length >= 4) { - onFailure((Integer) response[0], (Header[]) response[1], (byte[]) response[2], (Throwable) response[3]); - } else { - Log.e(LOG_TAG, "FAILURE_MESSAGE didn't got enough params"); - } - break; - case START_MESSAGE: - onStart(); - break; - case FINISH_MESSAGE: - onFinish(); - break; - case PROGRESS_MESSAGE: - response = (Object[]) message.obj; - if (response != null && response.length >= 2) { - try { - onProgress((Integer) response[0], (Integer) response[1]); - } catch (Throwable t) { - Log.e(LOG_TAG, "custom onProgress contains an error", t); + try { + switch (message.what) { + case SUCCESS_MESSAGE: + response = (Object[]) message.obj; + if (response != null && response.length >= 3) { + onSuccess((Integer) response[0], (Header[]) response[1], (byte[]) response[2]); + } else { + AsyncHttpClient.log.e(LOG_TAG, "SUCCESS_MESSAGE didn't got enough params"); } - } else { - Log.e(LOG_TAG, "PROGRESS_MESSAGE didn't got enough params"); - } - break; - case RETRY_MESSAGE: - response = (Object[]) message.obj; - if (response != null && response.length == 1) { - onRetry((Integer) response[0]); - } else { - Log.e(LOG_TAG, "RETRY_MESSAGE didn't get enough params"); - } - break; - case CANCEL_MESSAGE: - onCancel(); - break; + break; + case FAILURE_MESSAGE: + response = (Object[]) message.obj; + if (response != null && response.length >= 4) { + onFailure((Integer) response[0], (Header[]) response[1], (byte[]) response[2], (Throwable) response[3]); + } else { + AsyncHttpClient.log.e(LOG_TAG, "FAILURE_MESSAGE didn't got enough params"); + } + break; + case START_MESSAGE: + onStart(); + break; + case FINISH_MESSAGE: + onFinish(); + break; + case PROGRESS_MESSAGE: + response = (Object[]) message.obj; + if (response != null && response.length >= 2) { + try { + onProgress((Long) response[0], (Long) response[1]); + } catch (Throwable t) { + AsyncHttpClient.log.e(LOG_TAG, "custom onProgress contains an error", t); + } + } else { + AsyncHttpClient.log.e(LOG_TAG, "PROGRESS_MESSAGE didn't got enough params"); + } + break; + case RETRY_MESSAGE: + response = (Object[]) message.obj; + if (response != null && response.length == 1) { + onRetry((Integer) response[0]); + } else { + AsyncHttpClient.log.e(LOG_TAG, "RETRY_MESSAGE didn't get enough params"); + } + break; + case CANCEL_MESSAGE: + onCancel(); + break; + } + } catch (Throwable error) { + onUserException(error); } } @@ -357,7 +403,7 @@ protected void sendMessage(Message msg) { if (getUseSynchronousMode() || handler == null) { handleMessage(msg); } else if (!Thread.currentThread().isInterrupted()) { // do not send messages if request has been cancelled - AssertUtils.asserts(handler != null, "handler should not be null!"); + Utils.asserts(handler != null, "handler should not be null!"); handler.sendMessage(msg); } } @@ -374,7 +420,6 @@ protected void postRunnable(Runnable runnable) { runnable.run(); } else { // Otherwise, run on provided handler - AssertUtils.asserts(handler != null, "handler should not be null!"); handler.post(runnable); } } @@ -430,12 +475,13 @@ byte[] getResponseData(HttpEntity entity) throws IOException { ByteArrayBuffer buffer = new ByteArrayBuffer(buffersize); try { byte[] tmp = new byte[BUFFER_SIZE]; - int l, count = 0; + long count = 0; + int l; // do not send messages if request has been cancelled while ((l = instream.read(tmp)) != -1 && !Thread.currentThread().isInterrupted()) { count += l; buffer.append(tmp, 0, l); - sendProgressMessage(count, (int) (contentLength <= 0 ? 1 : contentLength)); + sendProgressMessage(count, (contentLength <= 0 ? 1 : contentLength)); } } finally { AsyncHttpClient.silentCloseInputStream(instream); @@ -450,4 +496,21 @@ byte[] getResponseData(HttpEntity entity) throws IOException { } return responseBody; } + + /** + * Avoid leaks by using a non-anonymous handler class. + */ + private static class ResponderHandler extends Handler { + private final AsyncHttpResponseHandler mResponder; + + ResponderHandler(AsyncHttpResponseHandler mResponder, Looper looper) { + super(looper); + this.mResponder = mResponder; + } + + @Override + public void handleMessage(Message msg) { + mResponder.handleMessage(msg); + } + } } diff --git a/library/src/main/java/com/loopj/android/http/Base64.java b/library/src/main/java/com/loopj/android/http/Base64.java index cc26d4398..045b46ead 100755 --- a/library/src/main/java/com/loopj/android/http/Base64.java +++ b/library/src/main/java/com/loopj/android/http/Base64.java @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -20,7 +20,7 @@ /** * Utilities for encoding and decoding the Base64 representation of binary data. See RFCs 2045 and 3548. + * href="https://www.ietf.org/rfc/rfc2045.txt">2045 and 3548. */ public class Base64 { /** @@ -61,28 +61,8 @@ public class Base64 { // shared code // -------------------------------------------------------- - /* package */ static abstract class Coder { - public byte[] output; - public int op; - - /** - * Encode/decode another block of input data. this.output is provided by the caller, and - * must be big enough to hold all the coded data. On exit, this.opwill be set to the length - * of the coded data. - * - * @param finish true if this is the final call to process for this object. Will finalize - * the coder state and include any final bytes in the output. - * @return true if the input so far is good; false if some error has been detected in the - * input stream.. - */ - public abstract boolean process(byte[] input, int offset, int len, boolean finish); - - /** - * @return the maximum number of bytes a call to process() could produce for the given - * number of input bytes. This may be an overestimate. - */ - public abstract int maxOutputSize(int len); - } + private Base64() { + } // don't instantiate // -------------------------------------------------------- // decoding @@ -153,6 +133,131 @@ public static byte[] decode(byte[] input, int offset, int len, int flags) { return temp; } + /** + * Base64-encode the given data and return a newly allocated String with the result. + * + * @param input the data to encode + * @param flags controls certain features of the encoded output. Passing {@code DEFAULT} results + * in output that adheres to RFC 2045. + * @return base64 string containing encoded input + */ + public static String encodeToString(byte[] input, int flags) { + try { + return new String(encode(input, flags), "US-ASCII"); + } catch (UnsupportedEncodingException e) { + // US-ASCII is guaranteed to be available. + throw new AssertionError(e); + } + } + + // -------------------------------------------------------- + // encoding + // -------------------------------------------------------- + + /** + * Base64-encode the given data and return a newly allocated String with the result. + * + * @param input the data to encode + * @param offset the position within the input array at which to start + * @param len the number of bytes of input to encode + * @param flags controls certain features of the encoded output. Passing {@code DEFAULT} + * results in output that adheres to RFC 2045. + * @return base64 string containing encoded range of input + */ + public static String encodeToString(byte[] input, int offset, int len, int flags) { + try { + return new String(encode(input, offset, len, flags), "US-ASCII"); + } catch (UnsupportedEncodingException e) { + // US-ASCII is guaranteed to be available. + throw new AssertionError(e); + } + } + + /** + * Base64-encode the given data and return a newly allocated byte[] with the result. + * + * @param input the data to encode + * @param flags controls certain features of the encoded output. Passing {@code DEFAULT} results + * in output that adheres to RFC 2045. + * @return base64 encoded input as bytes + */ + public static byte[] encode(byte[] input, int flags) { + return encode(input, 0, input.length, flags); + } + + /** + * Base64-encode the given data and return a newly allocated byte[] with the result. + * + * @param input the data to encode + * @param offset the position within the input array at which to start + * @param len the number of bytes of input to encode + * @param flags controls certain features of the encoded output. Passing {@code DEFAULT} + * results in output that adheres to RFC 2045. + * @return base64 encoded input as bytes + */ + public static byte[] encode(byte[] input, int offset, int len, int flags) { + Encoder encoder = new Encoder(flags, null); + + // Compute the exact length of the array we will produce. + int output_len = len / 3 * 4; + + // Account for the tail of the data and the padding bytes, if any. + if (encoder.do_padding) { + if (len % 3 > 0) { + output_len += 4; + } + } else { + switch (len % 3) { + case 0: + break; + case 1: + output_len += 2; + break; + case 2: + output_len += 3; + break; + } + } + + // Account for the newlines, if any. + if (encoder.do_newline && len > 0) { + output_len += (((len - 1) / (3 * Encoder.LINE_GROUPS)) + 1) * + (encoder.do_cr ? 2 : 1); + } + + encoder.output = new byte[output_len]; + encoder.process(input, offset, len, true); + + if (BuildConfig.DEBUG && encoder.op != output_len) { + throw new AssertionError(); + } + + return encoder.output; + } + + /* package */ static abstract class Coder { + public byte[] output; + public int op; + + /** + * Encode/decode another block of input data. this.output is provided by the caller, and + * must be big enough to hold all the coded data. On exit, this.opwill be set to the length + * of the coded data. + * + * @param finish true if this is the final call to process for this object. Will finalize + * the coder state and include any final bytes in the output. + * @return true if the input so far is good; false if some error has been detected in the + * input stream.. + */ + public abstract boolean process(byte[] input, int offset, int len, boolean finish); + + /** + * @return the maximum number of bytes a call to process() could produce for the given + * number of input bytes. This may be an overestimate. + */ + public abstract int maxOutputSize(int len); + } + /* package */ static class Decoder extends Coder { /** * Lookup table for turning bytes into their position in the Base64 alphabet. @@ -204,7 +309,7 @@ public static byte[] decode(byte[] input, int offset, int len, int flags) { */ private static final int SKIP = -1; private static final int EQUALS = -2; - + final private int[] alphabet; /** * States 0-3 are reading through the next input tuple. State 4 is having read one '=' and * expecting exactly one more. State 5 is expecting no more data or padding characters in @@ -214,8 +319,6 @@ public static byte[] decode(byte[] input, int offset, int len, int flags) { private int state; // state number (0 to 6) private int value; - final private int[] alphabet; - public Decoder(int flags, byte[] output) { this.output = output; @@ -415,112 +518,10 @@ public boolean process(byte[] input, int offset, int len, boolean finish) { } } - // -------------------------------------------------------- - // encoding - // -------------------------------------------------------- - - /** - * Base64-encode the given data and return a newly allocated String with the result. - * - * @param input the data to encode - * @param flags controls certain features of the encoded output. Passing {@code DEFAULT} results - * in output that adheres to RFC 2045. - * @return base64 string containing encoded input - */ - public static String encodeToString(byte[] input, int flags) { - try { - return new String(encode(input, flags), "US-ASCII"); - } catch (UnsupportedEncodingException e) { - // US-ASCII is guaranteed to be available. - throw new AssertionError(e); - } - } - - /** - * Base64-encode the given data and return a newly allocated String with the result. - * - * @param input the data to encode - * @param offset the position within the input array at which to start - * @param len the number of bytes of input to encode - * @param flags controls certain features of the encoded output. Passing {@code DEFAULT} - * results in output that adheres to RFC 2045. - * @return base64 string containing encoded range of input - */ - public static String encodeToString(byte[] input, int offset, int len, int flags) { - try { - return new String(encode(input, offset, len, flags), "US-ASCII"); - } catch (UnsupportedEncodingException e) { - // US-ASCII is guaranteed to be available. - throw new AssertionError(e); - } - } - - /** - * Base64-encode the given data and return a newly allocated byte[] with the result. - * - * @param input the data to encode - * @param flags controls certain features of the encoded output. Passing {@code DEFAULT} results - * in output that adheres to RFC 2045. - * @return base64 encoded input as bytes - */ - public static byte[] encode(byte[] input, int flags) { - return encode(input, 0, input.length, flags); - } - - /** - * Base64-encode the given data and return a newly allocated byte[] with the result. - * - * @param input the data to encode - * @param offset the position within the input array at which to start - * @param len the number of bytes of input to encode - * @param flags controls certain features of the encoded output. Passing {@code DEFAULT} - * results in output that adheres to RFC 2045. - * @return base64 encoded input as bytes - */ - public static byte[] encode(byte[] input, int offset, int len, int flags) { - Encoder encoder = new Encoder(flags, null); - - // Compute the exact length of the array we will produce. - int output_len = len / 3 * 4; - - // Account for the tail of the data and the padding bytes, if any. - if (encoder.do_padding) { - if (len % 3 > 0) { - output_len += 4; - } - } else { - switch (len % 3) { - case 0: - break; - case 1: - output_len += 2; - break; - case 2: - output_len += 3; - break; - } - } - - // Account for the newlines, if any. - if (encoder.do_newline && len > 0) { - output_len += (((len - 1) / (3 * Encoder.LINE_GROUPS)) + 1) * - (encoder.do_cr ? 2 : 1); - } - - encoder.output = new byte[output_len]; - encoder.process(input, offset, len, true); - - if (BuildConfig.DEBUG && encoder.op != output_len) { - throw new AssertionError(); - } - - return encoder.output; - } - /* package */ static class Encoder extends Coder { /** * Emit a new line every this many output tuples. Corresponds to a 76-character line length - * (the maximum allowable according to RFC + * (the maximum allowable according to RFC * 2045). */ public static final int LINE_GROUPS = 19; @@ -544,15 +545,13 @@ public static byte[] encode(byte[] input, int offset, int len, int flags) { 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_', }; - - final private byte[] tail; - /* package */ int tailLen; - private int count; - final public boolean do_padding; final public boolean do_newline; final public boolean do_cr; + final private byte[] tail; final private byte[] alphabet; + /* package */ int tailLen; + private int count; public Encoder(int flags, byte[] output) { this.output = output; @@ -712,7 +711,4 @@ public boolean process(byte[] input, int offset, int len, boolean finish) { return true; } } - - private Base64() { - } // don't instantiate } diff --git a/library/src/main/java/com/loopj/android/http/Base64DataException.java b/library/src/main/java/com/loopj/android/http/Base64DataException.java index ea2af5428..50127c1f4 100755 --- a/library/src/main/java/com/loopj/android/http/Base64DataException.java +++ b/library/src/main/java/com/loopj/android/http/Base64DataException.java @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, diff --git a/library/src/main/java/com/loopj/android/http/Base64OutputStream.java b/library/src/main/java/com/loopj/android/http/Base64OutputStream.java index 036d4a28c..07fb6f7cd 100755 --- a/library/src/main/java/com/loopj/android/http/Base64OutputStream.java +++ b/library/src/main/java/com/loopj/android/http/Base64OutputStream.java @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -21,14 +21,12 @@ import java.io.OutputStream; public class Base64OutputStream extends FilterOutputStream { + private static final byte[] EMPTY = new byte[0]; private final Base64.Coder coder; private final int flags; - private byte[] buffer = null; private int bpos = 0; - private static byte[] EMPTY = new byte[0]; - /** * Performs Base64 encoding on the data written to the stream, writing the encoded data to * another OutputStream. diff --git a/library/src/main/java/com/loopj/android/http/BaseJsonHttpResponseHandler.java b/library/src/main/java/com/loopj/android/http/BaseJsonHttpResponseHandler.java index dae49c599..69349f1d0 100755 --- a/library/src/main/java/com/loopj/android/http/BaseJsonHttpResponseHandler.java +++ b/library/src/main/java/com/loopj/android/http/BaseJsonHttpResponseHandler.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Copyright (c) 2011 James Smith - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -18,22 +18,20 @@ package com.loopj.android.http; -import android.util.Log; - -import org.apache.http.Header; -import org.apache.http.HttpStatus; +import cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.HttpStatus; /** * Class meant to be used with custom JSON parser (such as GSON or Jackson JSON)

 

* {@link #parseResponse(String, boolean)} should be overriden and must return type of generic param * class, response will be then handled to implementation of abstract methods {@link #onSuccess(int, - * org.apache.http.Header[], String, Object)} or {@link #onFailure(int, org.apache.http.Header[], + * cz.msebera.android.httpclient.Header[], String, Object)} or {@link #onFailure(int, cz.msebera.android.httpclient.Header[], * Throwable, String, Object)}, depending of response HTTP status line (result http code) * * @param Generic type meant to be returned in callback */ public abstract class BaseJsonHttpResponseHandler extends TextHttpResponseHandler { - private static final String LOG_TAG = "BaseJsonHttpResponseHandler"; + private static final String LOG_TAG = "BaseJsonHttpRH"; /** * Creates a new JsonHttpResponseHandler with default charset "UTF-8" @@ -45,7 +43,7 @@ public BaseJsonHttpResponseHandler() { /** * Creates a new JsonHttpResponseHandler with given string encoding * - * @param encoding result string encoding, see Charset + * @param encoding result string encoding, see Charset */ public BaseJsonHttpResponseHandler(String encoding) { super(encoding); @@ -87,7 +85,7 @@ public void run() { } }); } catch (final Throwable t) { - Log.d(LOG_TAG, "parseResponse thrown an problem", t); + AsyncHttpClient.log.d(LOG_TAG, "parseResponse thrown an problem", t); postRunnable(new Runnable() { @Override public void run() { @@ -97,7 +95,7 @@ public void run() { } } }; - if (!getUseSynchronousMode()) { + if (!getUseSynchronousMode() && !getUsePoolThread()) { new Thread(parser).start(); } else { // In synchronous mode everything should be run on one thread @@ -123,7 +121,7 @@ public void run() { } }); } catch (Throwable t) { - Log.d(LOG_TAG, "parseResponse thrown an problem", t); + AsyncHttpClient.log.d(LOG_TAG, "parseResponse thrown an problem", t); postRunnable(new Runnable() { @Override public void run() { @@ -133,7 +131,7 @@ public void run() { } } }; - if (!getUseSynchronousMode()) { + if (!getUseSynchronousMode() && !getUsePoolThread()) { new Thread(parser).start(); } else { // In synchronous mode everything should be run on one thread diff --git a/library/src/main/java/com/loopj/android/http/BearerAuthSchemeFactory.java b/library/src/main/java/com/loopj/android/http/BearerAuthSchemeFactory.java new file mode 100644 index 000000000..3d17ea294 --- /dev/null +++ b/library/src/main/java/com/loopj/android/http/BearerAuthSchemeFactory.java @@ -0,0 +1,89 @@ +/* + Android Asynchronous Http Client + Copyright (c) 2011 James Smith + https://github.com/android-async-http/android-async-http + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.loopj.android.http; + +import cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.HttpRequest; +import cz.msebera.android.httpclient.auth.AUTH; +import cz.msebera.android.httpclient.auth.AuthScheme; +import cz.msebera.android.httpclient.auth.AuthSchemeFactory; +import cz.msebera.android.httpclient.auth.AuthenticationException; +import cz.msebera.android.httpclient.auth.ContextAwareAuthScheme; +import cz.msebera.android.httpclient.auth.Credentials; +import cz.msebera.android.httpclient.auth.MalformedChallengeException; +import cz.msebera.android.httpclient.message.BufferedHeader; +import cz.msebera.android.httpclient.params.HttpParams; +import cz.msebera.android.httpclient.protocol.HttpContext; +import cz.msebera.android.httpclient.util.CharArrayBuffer; + +public class BearerAuthSchemeFactory implements AuthSchemeFactory { + + @Override + public AuthScheme newInstance(HttpParams params) { + return new BearerAuthScheme(); + } + + public static class BearerAuthScheme implements ContextAwareAuthScheme { + private boolean complete = false; + + @Override + public void processChallenge(Header header) throws MalformedChallengeException { + this.complete = true; + } + + @Override + public Header authenticate(Credentials credentials, HttpRequest request) throws AuthenticationException { + return authenticate(credentials, request, null); + } + + @Override + public Header authenticate(Credentials credentials, HttpRequest request, HttpContext httpContext) + throws AuthenticationException { + CharArrayBuffer buffer = new CharArrayBuffer(32); + buffer.append(AUTH.WWW_AUTH_RESP); + buffer.append(": Bearer "); + buffer.append(credentials.getUserPrincipal().getName()); + return new BufferedHeader(buffer); + } + + @Override + public String getSchemeName() { + return "Bearer"; + } + + @Override + public String getParameter(String name) { + return null; + } + + @Override + public String getRealm() { + return null; + } + + @Override + public boolean isConnectionBased() { + return false; + } + + @Override + public boolean isComplete() { + return this.complete; + } + } +} \ No newline at end of file diff --git a/library/src/main/java/com/loopj/android/http/BinaryHttpResponseHandler.java b/library/src/main/java/com/loopj/android/http/BinaryHttpResponseHandler.java index 3b844dfc5..2372a94e4 100755 --- a/library/src/main/java/com/loopj/android/http/BinaryHttpResponseHandler.java +++ b/library/src/main/java/com/loopj/android/http/BinaryHttpResponseHandler.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Copyright (c) 2011 James Smith - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -18,17 +18,17 @@ package com.loopj.android.http; -import android.util.Log; - -import org.apache.http.Header; -import org.apache.http.HttpResponse; -import org.apache.http.StatusLine; -import org.apache.http.client.HttpResponseException; +import android.os.Looper; import java.io.IOException; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; +import cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.HttpResponse; +import cz.msebera.android.httpclient.StatusLine; +import cz.msebera.android.httpclient.client.HttpResponseException; + /** * Used to intercept and handle the responses from requests made using {@link AsyncHttpClient}. * Receives response body as byte array with a content-type whitelist. (e.g. checks Content-Type @@ -36,7 +36,7 @@ *
  * AsyncHttpClient client = new AsyncHttpClient();
  * String[] allowedTypes = new String[] { "image/png" };
- * client.get("http://www.example.com/image.png", new BinaryHttpResponseHandler(allowedTypes) {
+ * client.get("https://www.example.com/image.png", new BinaryHttpResponseHandler(allowedTypes) {
  *     @Override
  *     public void onSuccess(byte[] imageData) {
  *         // Successfully got a response
@@ -51,7 +51,7 @@
  */
 public abstract class BinaryHttpResponseHandler extends AsyncHttpResponseHandler {
 
-    private static final String LOG_TAG = "BinaryHttpResponseHandler";
+    private static final String LOG_TAG = "BinaryHttpRH";
 
     private String[] mAllowedContentTypes = new String[]{
             RequestParams.APPLICATION_OCTET_STREAM,
@@ -60,16 +60,6 @@ public abstract class BinaryHttpResponseHandler extends AsyncHttpResponseHandler
             "image/gif"
     };
 
-    /**
-     * Method can be overriden to return allowed content types, can be sometimes better than passing
-     * data in constructor
-     *
-     * @return array of content-types or Pattern string templates (eg. '.*' to match every response)
-     */
-    public String[] getAllowedContentTypes() {
-        return mAllowedContentTypes;
-    }
-
     /**
      * Creates a new BinaryHttpResponseHandler
      */
@@ -88,10 +78,36 @@ public BinaryHttpResponseHandler(String[] allowedContentTypes) {
         if (allowedContentTypes != null) {
             mAllowedContentTypes = allowedContentTypes;
         } else {
-            Log.e(LOG_TAG, "Constructor passed allowedContentTypes was null !");
+            AsyncHttpClient.log.e(LOG_TAG, "Constructor passed allowedContentTypes was null !");
         }
     }
 
+    /**
+     * Creates a new BinaryHttpResponseHandler with a user-supplied looper, and overrides the default allowed content types with
+     * passed String array (hopefully) of content types.
+     *
+     * @param allowedContentTypes content types array, eg. 'image/jpeg' or pattern '.*'
+     * @param looper              The looper to work with
+     */
+    public BinaryHttpResponseHandler(String[] allowedContentTypes, Looper looper) {
+        super(looper);
+        if (allowedContentTypes != null) {
+            mAllowedContentTypes = allowedContentTypes;
+        } else {
+            AsyncHttpClient.log.e(LOG_TAG, "Constructor passed allowedContentTypes was null !");
+        }
+    }
+
+    /**
+     * Method can be overriden to return allowed content types, can be sometimes better than passing
+     * data in constructor
+     *
+     * @return array of content-types or Pattern string templates (eg. '.*' to match every response)
+     */
+    public String[] getAllowedContentTypes() {
+        return mAllowedContentTypes;
+    }
+
     @Override
     public abstract void onSuccess(int statusCode, Header[] headers, byte[] binaryData);
 
@@ -123,7 +139,7 @@ public final void sendResponseMessage(HttpResponse response) throws IOException
                     foundAllowedContentType = true;
                 }
             } catch (PatternSyntaxException e) {
-                Log.e("BinaryHttpResponseHandler", "Given pattern is not valid: " + anAllowedContentType, e);
+                AsyncHttpClient.log.e(LOG_TAG, "Given pattern is not valid: " + anAllowedContentType, e);
             }
         }
         if (!foundAllowedContentType) {
@@ -134,7 +150,7 @@ public final void sendResponseMessage(HttpResponse response) throws IOException
                     null,
                     new HttpResponseException(
                             status.getStatusCode(),
-                            "Content-Type not allowed!"
+                            "Content-Type (" + contentTypeHeader.getValue() + ") not allowed!"
                     )
             );
             return;
diff --git a/library/src/main/java/com/loopj/android/http/BlackholeHttpResponseHandler.java b/library/src/main/java/com/loopj/android/http/BlackholeHttpResponseHandler.java
new file mode 100644
index 000000000..a3e7b914e
--- /dev/null
+++ b/library/src/main/java/com/loopj/android/http/BlackholeHttpResponseHandler.java
@@ -0,0 +1,64 @@
+package com.loopj.android.http;
+
+import cz.msebera.android.httpclient.Header;
+import cz.msebera.android.httpclient.HttpResponse;
+
+/**
+ * Blank implementation of ResponseHandlerInterface, which ignores all contents returned by
+ * remote HTTP endpoint, and discards all various log messages
+ * 

 

+ * Use this implementation, if you deliberately want to ignore all response, because you cannot + * pass null ResponseHandlerInterface into AsyncHttpClient implementation + */ +public class BlackholeHttpResponseHandler extends AsyncHttpResponseHandler { + + @Override + public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) { + + } + + @Override + public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) { + + } + + @Override + public void onProgress(long bytesWritten, long totalSize) { + + } + + @Override + public void onCancel() { + + } + + @Override + public void onFinish() { + + } + + @Override + public void onPostProcessResponse(ResponseHandlerInterface instance, HttpResponse response) { + + } + + @Override + public void onPreProcessResponse(ResponseHandlerInterface instance, HttpResponse response) { + + } + + @Override + public void onRetry(int retryNo) { + + } + + @Override + public void onStart() { + + } + + @Override + public void onUserException(Throwable error) { + + } +} diff --git a/library/src/main/java/com/loopj/android/http/ConscryptSSLProvider.java b/library/src/main/java/com/loopj/android/http/ConscryptSSLProvider.java new file mode 100644 index 000000000..67ee69060 --- /dev/null +++ b/library/src/main/java/com/loopj/android/http/ConscryptSSLProvider.java @@ -0,0 +1,18 @@ +package com.loopj.android.http; + +import android.util.Log; + +import org.conscrypt.Conscrypt; + +import java.security.Security; + +public class ConscryptSSLProvider { + public static void install(){ + try { + Security.insertProviderAt(Conscrypt.newProviderBuilder().build(),1); + }catch (NoClassDefFoundError ex){ + Log.e(AsyncHttpClient.LOG_TAG, "java.lang.NoClassDefFoundError: org.conscrypt.Conscrypt, Please add org.conscrypt.Conscrypt to your dependency"); + } + + } +} diff --git a/library/src/main/java/com/loopj/android/http/DataAsyncHttpResponseHandler.java b/library/src/main/java/com/loopj/android/http/DataAsyncHttpResponseHandler.java index bea9a929c..a8d43a189 100755 --- a/library/src/main/java/com/loopj/android/http/DataAsyncHttpResponseHandler.java +++ b/library/src/main/java/com/loopj/android/http/DataAsyncHttpResponseHandler.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Copyright (c) 2011 James Smith - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -19,18 +19,17 @@ package com.loopj.android.http; import android.os.Message; -import android.util.Log; - -import org.apache.http.HttpEntity; -import org.apache.http.util.ByteArrayBuffer; import java.io.IOException; import java.io.InputStream; -public abstract class DataAsyncHttpResponseHandler extends AsyncHttpResponseHandler { - private static final String LOG_TAG = "DataAsyncHttpResponseHandler"; +import cz.msebera.android.httpclient.HttpEntity; +import cz.msebera.android.httpclient.util.ByteArrayBuffer; - protected static final int PROGRESS_DATA_MESSAGE = 6; +@SuppressWarnings("ALL") +public abstract class DataAsyncHttpResponseHandler extends AsyncHttpResponseHandler { + protected static final int PROGRESS_DATA_MESSAGE = 7; + private static final String LOG_TAG = "DataAsyncHttpRH"; /** * Creates a new AsyncHttpResponseHandler @@ -39,16 +38,45 @@ public DataAsyncHttpResponseHandler() { super(); } + /** + * Copies elements from {@code original} into a new array, from indexes start (inclusive) to end + * (exclusive). The original order of elements is preserved. If {@code end} is greater than + * {@code original.length}, the result is padded with the value {@code (byte) 0}. + * + * @param original the original array + * @param start the start index, inclusive + * @param end the end index, exclusive + * @return the new array + * @throws ArrayIndexOutOfBoundsException if {@code start < 0 || start > original.length} + * @throws IllegalArgumentException if {@code start > end} + * @throws NullPointerException if {@code original == null} + * @see java.util.Arrays + * @since 1.6 + */ + public static byte[] copyOfRange(byte[] original, int start, int end) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, NullPointerException { + if (start > end) { + throw new IllegalArgumentException(); + } + int originalLength = original.length; + if (start < 0 || start > originalLength) { + throw new ArrayIndexOutOfBoundsException(); + } + int resultLength = end - start; + int copyLength = Math.min(resultLength, originalLength - start); + byte[] result = new byte[resultLength]; + System.arraycopy(original, start, result, 0, copyLength); + return result; + } + /** * Fired when the request progress, override to handle in your own code * * @param responseBody response body received so far */ public void onProgressData(byte[] responseBody) { - Log.d(LOG_TAG, "onProgressData(byte[]) was not overriden, but callback was received"); + AsyncHttpClient.log.d(LOG_TAG, "onProgressData(byte[]) was not overriden, but callback was received"); } - final public void sendProgressDataMessage(byte[] responseBytes) { sendMessage(obtainMessage(PROGRESS_DATA_MESSAGE, new Object[]{responseBytes})); } @@ -66,10 +94,10 @@ protected void handleMessage(Message message) { try { onProgressData((byte[]) response[0]); } catch (Throwable t) { - Log.e(LOG_TAG, "custom onProgressData contains an error", t); + AsyncHttpClient.log.e(LOG_TAG, "custom onProgressData contains an error", t); } } else { - Log.e(LOG_TAG, "PROGRESS_DATA_MESSAGE didn't got enough params"); + AsyncHttpClient.log.e(LOG_TAG, "PROGRESS_DATA_MESSAGE didn't got enough params"); } break; } @@ -105,7 +133,7 @@ byte[] getResponseData(HttpEntity entity) throws IOException { while ((l = instream.read(tmp)) != -1 && !Thread.currentThread().isInterrupted()) { buffer.append(tmp, 0, l); sendProgressDataMessage(copyOfRange(tmp, 0, l)); - sendProgressMessage(count, (int) contentLength); + sendProgressMessage(count, contentLength); } } finally { AsyncHttpClient.silentCloseInputStream(instream); @@ -119,35 +147,5 @@ byte[] getResponseData(HttpEntity entity) throws IOException { } return responseBody; } - - /** - * Copies elements from {@code original} into a new array, from indexes start (inclusive) to end - * (exclusive). The original order of elements is preserved. If {@code end} is greater than - * {@code original.length}, the result is padded with the value {@code (byte) 0}. - * - * @param original the original array - * @param start the start index, inclusive - * @param end the end index, exclusive - * @return the new array - * @throws ArrayIndexOutOfBoundsException if {@code start < 0 || start > original.length} - * @throws IllegalArgumentException if {@code start > end} - * @throws NullPointerException if {@code original == null} - * @see java.util.Arrays - * @since 1.6 - */ - public static byte[] copyOfRange(byte[] original, int start, int end) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, NullPointerException { - if (start > end) { - throw new IllegalArgumentException(); - } - int originalLength = original.length; - if (start < 0 || start > originalLength) { - throw new ArrayIndexOutOfBoundsException(); - } - int resultLength = end - start; - int copyLength = Math.min(resultLength, originalLength - start); - byte[] result = new byte[resultLength]; - System.arraycopy(original, start, result, 0, copyLength); - return result; - } } diff --git a/library/src/main/java/com/loopj/android/http/FileAsyncHttpResponseHandler.java b/library/src/main/java/com/loopj/android/http/FileAsyncHttpResponseHandler.java index 13de7fe2f..ee0d5b2c5 100755 --- a/library/src/main/java/com/loopj/android/http/FileAsyncHttpResponseHandler.java +++ b/library/src/main/java/com/loopj/android/http/FileAsyncHttpResponseHandler.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Copyright (c) 2011 James Smith - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -19,21 +19,22 @@ package com.loopj.android.http; import android.content.Context; -import android.util.Log; - -import org.apache.http.Header; -import org.apache.http.HttpEntity; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.HttpEntity; + public abstract class FileAsyncHttpResponseHandler extends AsyncHttpResponseHandler { - protected final File mFile; + private static final String LOG_TAG = "FileAsyncHttpRH"; + protected final File file; protected final boolean append; - private static final String LOG_TAG = "FileAsyncHttpResponseHandler"; + protected final boolean renameIfExists; + protected File frontendFile; /** * Obtains new FileAsyncHttpResponseHandler and stores response in passed file @@ -51,10 +52,43 @@ public FileAsyncHttpResponseHandler(File file) { * @param append whether data should be appended to existing file */ public FileAsyncHttpResponseHandler(File file, boolean append) { - super(); - AssertUtils.asserts(file != null, "File passed into FileAsyncHttpResponseHandler constructor must not be null"); - this.mFile = file; + this(file, append, false); + } + + /** + * Obtains new FileAsyncHttpResponseHandler and stores response in passed file + * + * @param file File to store response within, must not be null + * @param append whether data should be appended to existing file + * @param renameTargetFileIfExists whether target file should be renamed if it already exists + */ + public FileAsyncHttpResponseHandler(File file, boolean append, boolean renameTargetFileIfExists) { + this(file,append,renameTargetFileIfExists,false); + } + + + /** + * Obtains new FileAsyncHttpResponseHandler and stores response in passed file + * + * @param file File to store response within, must not be null + * @param append whether data should be appended to existing file + * @param renameTargetFileIfExists whether target file should be renamed if it already exists + * @param usePoolThread Whether to use the pool's thread to fire callbacks + */ + public FileAsyncHttpResponseHandler(File file, boolean append, boolean renameTargetFileIfExists,boolean usePoolThread) { + super(usePoolThread); + Utils.asserts(file != null, "File passed into FileAsyncHttpResponseHandler constructor must not be null"); + if (!file.isDirectory() && !file.getParentFile().isDirectory()) { + Utils.asserts(file.getParentFile().mkdirs(), "Cannot create parent directories for requested File location"); + } + if (file.isDirectory()) { + if (!file.mkdirs()) { + AsyncHttpClient.log.d(LOG_TAG, "Cannot create directories for requested Directory location, might not be a problem"); + } + } + this.file = file; this.append = append; + this.renameIfExists = renameTargetFileIfExists; } /** @@ -64,8 +98,9 @@ public FileAsyncHttpResponseHandler(File file, boolean append) { */ public FileAsyncHttpResponseHandler(Context context) { super(); - this.mFile = getTemporaryFile(context); + this.file = getTemporaryFile(context); this.append = false; + this.renameIfExists = false; } /** @@ -84,13 +119,11 @@ public boolean deleteTargetFile() { * @return temporary file or null if creating file failed */ protected File getTemporaryFile(Context context) { - AssertUtils.asserts(context != null, "Tried creating temporary file without having Context"); + Utils.asserts(context != null, "Tried creating temporary file without having Context"); try { - // not effective in release mode - assert context != null; return File.createTempFile("temp_", "_handled", context.getCacheDir()); } catch (IOException e) { - Log.e(LOG_TAG, "Cannot create temporary file", e); + AsyncHttpClient.log.e(LOG_TAG, "Cannot create temporary file", e); } return null; } @@ -98,11 +131,55 @@ protected File getTemporaryFile(Context context) { /** * Retrieves File object in which the response is stored * - * @return File file in which the response is stored + * @return File file in which the response was to be stored + */ + protected File getOriginalFile() { + Utils.asserts(file != null, "Target file is null, fatal!"); + return file; + } + + /** + * Retrieves File which represents response final location after possible renaming + * + * @return File final target file + */ + public File getTargetFile() { + if (frontendFile == null) { + frontendFile = getOriginalFile().isDirectory() ? getTargetFileByParsingURL() : getOriginalFile(); + } + return frontendFile; + } + + /** + * Will return File instance for file representing last URL segment in given folder. + * If file already exists and renameTargetFileIfExists was set as true, will try to find file + * which doesn't exist, naming template for such cases is "filename.ext" => "filename (%d).ext", + * or without extension "filename" => "filename (%d)" + * + * @return File in given directory constructed by last segment of request URL */ - protected File getTargetFile() { - assert (mFile != null); - return mFile; + protected File getTargetFileByParsingURL() { + Utils.asserts(getOriginalFile().isDirectory(), "Target file is not a directory, cannot proceed"); + Utils.asserts(getRequestURI() != null, "RequestURI is null, cannot proceed"); + String requestURL = getRequestURI().toString(); + String filename = requestURL.substring(requestURL.lastIndexOf('/') + 1, requestURL.length()); + File targetFileRtn = new File(getOriginalFile(), filename); + if (targetFileRtn.exists() && renameIfExists) { + String format; + if (!filename.contains(".")) { + format = filename + " (%d)"; + } else { + format = filename.substring(0, filename.lastIndexOf('.')) + " (%d)" + filename.substring(filename.lastIndexOf('.'), filename.length()); + } + int index = 0; + while (true) { + targetFileRtn = new File(getOriginalFile(), String.format(format, index)); + if (!targetFileRtn.exists()) + return targetFileRtn; + index++; + } + } + return targetFileRtn; } @Override @@ -149,7 +226,7 @@ protected byte[] getResponseData(HttpEntity entity) throws IOException { while ((l = instream.read(tmp)) != -1 && !Thread.currentThread().isInterrupted()) { count += l; buffer.write(tmp, 0, l); - sendProgressMessage(count, (int) contentLength); + sendProgressMessage(count, contentLength); } } finally { AsyncHttpClient.silentCloseInputStream(instream); diff --git a/library/src/main/java/com/loopj/android/http/HttpDelete.java b/library/src/main/java/com/loopj/android/http/HttpDelete.java new file mode 100644 index 000000000..5a426a76b --- /dev/null +++ b/library/src/main/java/com/loopj/android/http/HttpDelete.java @@ -0,0 +1,59 @@ +/* + Android Asynchronous Http Client + Copyright (c) 2011 James Smith + https://github.com/android-async-http/android-async-http + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.loopj.android.http; + +import java.net.URI; + +import cz.msebera.android.httpclient.client.methods.HttpEntityEnclosingRequestBase; + +/** + * The current Android (API level 21) bundled version of the Apache Http Client does not implement + * a HttpEntityEnclosingRequestBase type of HTTP DELETE method. + * Until the Android version is updated this can serve in it's stead. + * This implementation can and should go away when the official solution arrives. + */ +public final class HttpDelete extends HttpEntityEnclosingRequestBase { + public final static String METHOD_NAME = "DELETE"; + + public HttpDelete() { + super(); + } + + /** + * @param uri target url as URI + */ + public HttpDelete(final URI uri) { + super(); + setURI(uri); + } + + /** + * @param uri target url as String + * @throws IllegalArgumentException if the uri is invalid. + */ + public HttpDelete(final String uri) { + super(); + setURI(URI.create(uri)); + } + + @Override + public String getMethod() { + return METHOD_NAME; + } +} diff --git a/library/src/main/java/com/loopj/android/http/HttpGet.java b/library/src/main/java/com/loopj/android/http/HttpGet.java new file mode 100644 index 000000000..7fd882a52 --- /dev/null +++ b/library/src/main/java/com/loopj/android/http/HttpGet.java @@ -0,0 +1,60 @@ +/* + Android Asynchronous Http Client + Copyright (c) 2011 James Smith + https://github.com/android-async-http/android-async-http + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.loopj.android.http; + +import java.net.URI; + +import cz.msebera.android.httpclient.client.methods.HttpEntityEnclosingRequestBase; + +/** + * The current Android (API level 21) bundled version of the Apache Http Client does not implement + * a HttpEntityEnclosingRequestBase type of HTTP GET method. + * Until the Android version is updated this can serve in it's stead. + * This implementation can and should go away when the official solution arrives. + */ +public final class HttpGet extends HttpEntityEnclosingRequestBase { + + public final static String METHOD_NAME = "GET"; + + public HttpGet() { + super(); + } + + /** + * @param uri target url as URI + */ + public HttpGet(final URI uri) { + super(); + setURI(uri); + } + + /** + * @param uri target url as String + * @throws IllegalArgumentException if the uri is invalid. + */ + public HttpGet(final String uri) { + super(); + setURI(URI.create(uri)); + } + + @Override + public String getMethod() { + return METHOD_NAME; + } +} diff --git a/library/src/main/java/com/loopj/android/http/JsonHttpResponseHandler.java b/library/src/main/java/com/loopj/android/http/JsonHttpResponseHandler.java index b12afce68..28af16d28 100755 --- a/library/src/main/java/com/loopj/android/http/JsonHttpResponseHandler.java +++ b/library/src/main/java/com/loopj/android/http/JsonHttpResponseHandler.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Copyright (c) 2011 James Smith - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -18,26 +18,28 @@ package com.loopj.android.http; -import android.util.Log; - -import org.apache.http.Header; -import org.apache.http.HttpStatus; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.json.JSONTokener; +import cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.HttpStatus; + /** * Used to intercept and handle the responses from requests made using {@link AsyncHttpClient}, with * automatic parsing into a {@link JSONObject} or {@link JSONArray}.

 

This class is * designed to be passed to get, post, put and delete requests with the {@link #onSuccess(int, - * org.apache.http.Header[], org.json.JSONArray)} or {@link #onSuccess(int, - * org.apache.http.Header[], org.json.JSONObject)} methods anonymously overridden.

 

+ * cz.msebera.android.httpclient.Header[], org.json.JSONArray)} or {@link #onSuccess(int, + * cz.msebera.android.httpclient.Header[], org.json.JSONObject)} methods anonymously overridden.

 

* Additionally, you can override the other event methods from the parent class. */ public class JsonHttpResponseHandler extends TextHttpResponseHandler { - private static final String LOG_TAG = "JsonHttpResponseHandler"; + private static final String LOG_TAG = "JsonHttpRH"; + + + private boolean useRFC5179CompatibilityMode = true; /** * Creates new JsonHttpResponseHandler, with JSON String encoding UTF-8 @@ -47,7 +49,7 @@ public JsonHttpResponseHandler() { } /** - * Creates new JsonHttpRespnseHandler with given JSON String encoding + * Creates new JsonHttpResponseHandler with given JSON String encoding * * @param encoding String encoding to be used when parsing JSON */ @@ -55,6 +57,27 @@ public JsonHttpResponseHandler(String encoding) { super(encoding); } + /** + * Creates new JsonHttpResponseHandler with JSON String encoding UTF-8 and given RFC5179CompatibilityMode + * + * @param useRFC5179CompatibilityMode Boolean mode to use RFC5179 or latest + */ + public JsonHttpResponseHandler(boolean useRFC5179CompatibilityMode) { + super(DEFAULT_CHARSET); + this.useRFC5179CompatibilityMode = useRFC5179CompatibilityMode; + } + + /** + * Creates new JsonHttpResponseHandler with given JSON String encoding and RFC5179CompatibilityMode + * + * @param encoding String encoding to be used when parsing JSON + * @param useRFC5179CompatibilityMode Boolean mode to use RFC5179 or latest + */ + public JsonHttpResponseHandler(String encoding, boolean useRFC5179CompatibilityMode) { + super(encoding); + this.useRFC5179CompatibilityMode = useRFC5179CompatibilityMode; + } + /** * Returns when request succeeds * @@ -63,7 +86,7 @@ public JsonHttpResponseHandler(String encoding) { * @param response parsed response if any */ public void onSuccess(int statusCode, Header[] headers, JSONObject response) { - Log.w(LOG_TAG, "onSuccess(int, Header[], JSONObject) was not overriden, but callback was received"); + AsyncHttpClient.log.w(LOG_TAG, "onSuccess(int, Header[], JSONObject) was not overriden, but callback was received"); } /** @@ -74,7 +97,7 @@ public void onSuccess(int statusCode, Header[] headers, JSONObject response) { * @param response parsed response if any */ public void onSuccess(int statusCode, Header[] headers, JSONArray response) { - Log.w(LOG_TAG, "onSuccess(int, Header[], JSONArray) was not overriden, but callback was received"); + AsyncHttpClient.log.w(LOG_TAG, "onSuccess(int, Header[], JSONArray) was not overriden, but callback was received"); } /** @@ -86,7 +109,7 @@ public void onSuccess(int statusCode, Header[] headers, JSONArray response) { * @param errorResponse parsed response if any */ public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) { - Log.w(LOG_TAG, "onFailure(int, Header[], Throwable, JSONObject) was not overriden, but callback was received", throwable); + AsyncHttpClient.log.w(LOG_TAG, "onFailure(int, Header[], Throwable, JSONObject) was not overriden, but callback was received", throwable); } /** @@ -98,17 +121,17 @@ public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSO * @param errorResponse parsed response if any */ public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONArray errorResponse) { - Log.w(LOG_TAG, "onFailure(int, Header[], Throwable, JSONArray) was not overriden, but callback was received", throwable); + AsyncHttpClient.log.w(LOG_TAG, "onFailure(int, Header[], Throwable, JSONArray) was not overriden, but callback was received", throwable); } @Override public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) { - Log.w(LOG_TAG, "onFailure(int, Header[], String, Throwable) was not overriden, but callback was received", throwable); + AsyncHttpClient.log.w(LOG_TAG, "onFailure(int, Header[], String, Throwable) was not overriden, but callback was received", throwable); } @Override public void onSuccess(int statusCode, Header[] headers, String responseString) { - Log.w(LOG_TAG, "onSuccess(int, Header[], String) was not overriden, but callback was received"); + AsyncHttpClient.log.w(LOG_TAG, "onSuccess(int, Header[], String) was not overriden, but callback was received"); } @Override @@ -122,16 +145,23 @@ public void run() { postRunnable(new Runnable() { @Override public void run() { - if (jsonResponse instanceof JSONObject) { + // In RFC5179 a null value is not a valid JSON + if (!useRFC5179CompatibilityMode && jsonResponse == null) { + onSuccess(statusCode, headers, (String) null); + } else if (jsonResponse instanceof JSONObject) { onSuccess(statusCode, headers, (JSONObject) jsonResponse); } else if (jsonResponse instanceof JSONArray) { onSuccess(statusCode, headers, (JSONArray) jsonResponse); } else if (jsonResponse instanceof String) { - onFailure(statusCode, headers, (String) jsonResponse, new JSONException("Response cannot be parsed as JSON data")); + // In RFC5179 a simple string value is not a valid JSON + if (useRFC5179CompatibilityMode) { + onFailure(statusCode, headers, (String) jsonResponse, new JSONException("Response cannot be parsed as JSON data")); + } else { + onSuccess(statusCode, headers, (String) jsonResponse); + } } else { onFailure(statusCode, headers, new JSONException("Unexpected response type " + jsonResponse.getClass().getName()), (JSONObject) null); } - } }); } catch (final JSONException ex) { @@ -144,7 +174,7 @@ public void run() { } } }; - if (!getUseSynchronousMode()) { + if (!getUseSynchronousMode() && !getUsePoolThread()) { new Thread(parser).start(); } else { // In synchronous mode everything should be run on one thread @@ -166,7 +196,10 @@ public void run() { postRunnable(new Runnable() { @Override public void run() { - if (jsonResponse instanceof JSONObject) { + // In RFC5179 a null value is not a valid JSON + if (!useRFC5179CompatibilityMode && jsonResponse == null) { + onFailure(statusCode, headers, (String) null, throwable); + } else if (jsonResponse instanceof JSONObject) { onFailure(statusCode, headers, throwable, (JSONObject) jsonResponse); } else if (jsonResponse instanceof JSONArray) { onFailure(statusCode, headers, throwable, (JSONArray) jsonResponse); @@ -189,14 +222,14 @@ public void run() { } } }; - if (!getUseSynchronousMode()) { + if (!getUseSynchronousMode() && !getUsePoolThread()) { new Thread(parser).start(); } else { // In synchronous mode everything should be run on one thread parser.run(); } } else { - Log.v(LOG_TAG, "response body is null, calling onFailure(Throwable, JSONObject)"); + AsyncHttpClient.log.v(LOG_TAG, "response body is null, calling onFailure(Throwable, JSONObject)"); onFailure(statusCode, headers, throwable, (JSONObject) null); } } @@ -217,11 +250,22 @@ protected Object parseResponse(byte[] responseBody) throws JSONException { String jsonString = getResponseString(responseBody, getCharset()); if (jsonString != null) { jsonString = jsonString.trim(); - if (jsonString.startsWith(UTF8_BOM)) { - jsonString = jsonString.substring(1); - } - if (jsonString.startsWith("{") || jsonString.startsWith("[")) { - result = new JSONTokener(jsonString).nextValue(); + if (useRFC5179CompatibilityMode) { + if (jsonString.startsWith("{") || jsonString.startsWith("[")) { + result = new JSONTokener(jsonString).nextValue(); + } + } else { + // Check if the string is an JSONObject style {} or JSONArray style [] + // If not we consider this as a string + if ((jsonString.startsWith("{") && jsonString.endsWith("}")) + || jsonString.startsWith("[") && jsonString.endsWith("]")) { + result = new JSONTokener(jsonString).nextValue(); + } + // Check if this is a String "my String value" and remove quote + // Other value type (numerical, boolean) should be without quote + else if (jsonString.startsWith("\"") && jsonString.endsWith("\"")) { + result = jsonString.substring(1, jsonString.length() - 1); + } } } if (result == null) { @@ -229,4 +273,13 @@ protected Object parseResponse(byte[] responseBody) throws JSONException { } return result; } + + public boolean isUseRFC5179CompatibilityMode() { + return useRFC5179CompatibilityMode; + } + + public void setUseRFC5179CompatibilityMode(boolean useRFC5179CompatibilityMode) { + this.useRFC5179CompatibilityMode = useRFC5179CompatibilityMode; + } + } diff --git a/library/src/main/java/com/loopj/android/http/JsonStreamerEntity.java b/library/src/main/java/com/loopj/android/http/JsonStreamerEntity.java index 0df47d477..6fbee5040 100755 --- a/library/src/main/java/com/loopj/android/http/JsonStreamerEntity.java +++ b/library/src/main/java/com/loopj/android/http/JsonStreamerEntity.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Copyright (c) 2011 James Smith - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -18,11 +18,7 @@ package com.loopj.android.http; -import android.util.Log; - -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.message.BasicHeader; +import android.text.TextUtils; import java.io.FileInputStream; import java.io.IOException; @@ -34,6 +30,10 @@ import java.util.Set; import java.util.zip.GZIPOutputStream; +import cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.HttpEntity; +import cz.msebera.android.httpclient.message.BasicHeader; + /** * HTTP entity to upload JSON data using streams. This has very low memory footprint; suitable for * uploading large files using base64 encoding. @@ -47,44 +47,100 @@ public class JsonStreamerEntity implements HttpEntity { // Size of the byte-array buffer used in I/O streams. private static final int BUFFER_SIZE = 4096; - - // Buffer used for reading from input streams. - private final byte[] buffer = new byte[BUFFER_SIZE]; - - // Reusable StringBuilder used by escape() method. - // Its size is just initial, if more space is needed, the system will - // automatically enlarge the buffer. - private static final StringBuilder BUILDER = new StringBuilder(128); - private static final byte[] JSON_TRUE = "true".getBytes(); private static final byte[] JSON_FALSE = "false".getBytes(); private static final byte[] JSON_NULL = "null".getBytes(); private static final byte[] STREAM_NAME = escape("name"); private static final byte[] STREAM_TYPE = escape("type"); private static final byte[] STREAM_CONTENTS = escape("contents"); - private static final byte[] STREAM_ELAPSED = escape("_elapsed"); - private static final Header HEADER_JSON_CONTENT = new BasicHeader( AsyncHttpClient.HEADER_CONTENT_TYPE, RequestParams.APPLICATION_JSON); - private static final Header HEADER_GZIP_ENCODING = new BasicHeader( AsyncHttpClient.HEADER_CONTENT_ENCODING, AsyncHttpClient.ENCODING_GZIP); - + // Buffer used for reading from input streams. + private final byte[] buffer = new byte[BUFFER_SIZE]; // JSON data and associated meta-data to be uploaded. private final Map jsonParams = new HashMap(); // Whether to use gzip compression while uploading private final Header contentEncoding; + private final byte[] elapsedField; + private final ResponseHandlerInterface progressHandler; - public JsonStreamerEntity(ResponseHandlerInterface progressHandler, boolean useGZipCompression) { + public JsonStreamerEntity(ResponseHandlerInterface progressHandler, boolean useGZipCompression, String elapsedField) { this.progressHandler = progressHandler; this.contentEncoding = useGZipCompression ? HEADER_GZIP_ENCODING : null; + this.elapsedField = TextUtils.isEmpty(elapsedField) + ? null + : escape(elapsedField); + } + + // Curtosy of Simple-JSON: https://goo.gl/XoW8RF + // Changed a bit to suit our needs in this class. + static byte[] escape(String string) { + // If it's null, just return prematurely. + if (string == null) { + return JSON_NULL; + } + + // Create a string builder to generate the escaped string. + StringBuilder sb = new StringBuilder(128); + + // Surround with quotations. + sb.append('"'); + + int length = string.length(), pos = -1; + while (++pos < length) { + char ch = string.charAt(pos); + switch (ch) { + case '"': + sb.append("\\\""); + break; + case '\\': + sb.append("\\\\"); + break; + case '\b': + sb.append("\\b"); + break; + case '\f': + sb.append("\\f"); + break; + case '\n': + sb.append("\\n"); + break; + case '\r': + sb.append("\\r"); + break; + case '\t': + sb.append("\\t"); + break; + default: + // Reference: https://www.unicode.org/versions/Unicode5.1.0/ + if ((ch <= '\u001F') || (ch >= '\u007F' && ch <= '\u009F') || (ch >= '\u2000' && ch <= '\u20FF')) { + String intString = Integer.toHexString(ch); + sb.append("\\u"); + int intLength = 4 - intString.length(); + for (int zero = 0; zero < intLength; zero++) { + sb.append('0'); + } + sb.append(intString.toUpperCase(Locale.US)); + } else { + sb.append(ch); + } + break; + } + } + + // Surround with quotations. + sb.append('"'); + + return sb.toString().getBytes(); } /** @@ -147,7 +203,7 @@ public void writeTo(final OutputStream out) throws IOException { // Use GZIP compression when sending streams, otherwise just use // a buffered output stream to speed things up a bit. - OutputStream os = null != contentEncoding + OutputStream os = contentEncoding != null ? new GZIPOutputStream(out, BUFFER_SIZE) : out; @@ -157,71 +213,90 @@ public void writeTo(final OutputStream out) throws IOException { // Keys used by the HashMaps. Set keys = jsonParams.keySet(); - boolean isFileWrapper; - - // Go over all keys and handle each's value. - for (String key : keys) { - // Evaluate the value (which cannot be null). - Object value = jsonParams.get(key); - - // Bail out prematurely if value's null. - if (value == null) { - continue; - } + int keysCount = keys.size(); + if (0 < keysCount) { + int keysProcessed = 0; + boolean isFileWrapper; - // Write the JSON object's key. - os.write(escape(key)); - os.write(':'); + // Go over all keys and handle each's value. + for (String key : keys) { + // Indicate that this key has been processed. + keysProcessed++; - // Check if this is a FileWrapper. - isFileWrapper = value instanceof RequestParams.FileWrapper; + try { + // Evaluate the value (which cannot be null). + Object value = jsonParams.get(key); - // If a file should be uploaded. - if (isFileWrapper || value instanceof RequestParams.StreamWrapper) { - // All uploads are sent as an object containing the file's details. - os.write('{'); + // Write the JSON object's key. + os.write(escape(key)); + os.write(':'); - // Determine how to handle this entry. - if (isFileWrapper) { - writeToFromFile(os, (RequestParams.FileWrapper) value); - } else { - writeToFromStream(os, (RequestParams.StreamWrapper) value); + // Bail out prematurely if value's null. + if (value == null) { + os.write(JSON_NULL); + } else { + // Check if this is a FileWrapper. + isFileWrapper = value instanceof RequestParams.FileWrapper; + + // If a file should be uploaded. + if (isFileWrapper || value instanceof RequestParams.StreamWrapper) { + // All uploads are sent as an object containing the file's details. + os.write('{'); + + // Determine how to handle this entry. + if (isFileWrapper) { + writeToFromFile(os, (RequestParams.FileWrapper) value); + } else { + writeToFromStream(os, (RequestParams.StreamWrapper) value); + } + + // End the file's object and prepare for next one. + os.write('}'); + } else if (value instanceof JsonValueInterface) { + os.write(((JsonValueInterface) value).getEscapedJsonValue()); + } else if (value instanceof org.json.JSONObject) { + os.write(value.toString().getBytes()); + } else if (value instanceof org.json.JSONArray) { + os.write(value.toString().getBytes()); + } else if (value instanceof Boolean) { + os.write((Boolean) value ? JSON_TRUE : JSON_FALSE); + } else if (value instanceof Long) { + os.write((((Number) value).longValue() + "").getBytes()); + } else if (value instanceof Double) { + os.write((((Number) value).doubleValue() + "").getBytes()); + } else if (value instanceof Float) { + os.write((((Number) value).floatValue() + "").getBytes()); + } else if (value instanceof Integer) { + os.write((((Number) value).intValue() + "").getBytes()); + } else { + os.write(escape(value.toString())); + } + } + } finally { + // Separate each K:V with a comma, except the last one. + if (elapsedField != null || keysProcessed < keysCount) { + os.write(','); + } } + } - // End the file's object and prepare for next one. - os.write('}'); - } else if (value instanceof JsonValueInterface) { - os.write(((JsonValueInterface) value).getEscapedJsonValue()); - } else if (value instanceof org.json.JSONObject) { - os.write(((org.json.JSONObject) value).toString().getBytes()); - } else if (value instanceof org.json.JSONArray) { - os.write(((org.json.JSONArray) value).toString().getBytes()); - } else if (value instanceof Boolean) { - os.write((Boolean) value ? JSON_TRUE : JSON_FALSE); - } else if (value instanceof Long) { - os.write((((Number) value).longValue() + "").getBytes()); - } else if (value instanceof Double) { - os.write((((Number) value).doubleValue() + "").getBytes()); - } else if (value instanceof Float) { - os.write((((Number) value).floatValue() + "").getBytes()); - } else if (value instanceof Integer) { - os.write((((Number) value).intValue() + "").getBytes()); - } else { - os.write(escape(value.toString())); + // Calculate how many milliseconds it took to upload the contents. + long elapsedTime = System.currentTimeMillis() - now; + + // Include the elapsed time taken to upload everything. + // This might be useful for somebody, but it serves us well since + // there will almost always be a ',' as the last sent character. + if (elapsedField != null) { + os.write(elapsedField); + os.write(':'); + os.write((elapsedTime + "").getBytes()); } - os.write(','); + AsyncHttpClient.log.i(LOG_TAG, "Uploaded JSON in " + Math.floor(elapsedTime / 1000) + " seconds"); } - // Include the elapsed time taken to upload everything. - // This might be useful for somebody, but it serves us well since - // there will almost always be a ',' as the last sent character. - os.write(STREAM_ELAPSED); - os.write(':'); - long elapsedTime = System.currentTimeMillis() - now; - os.write((elapsedTime + "}").getBytes()); - - Log.i(LOG_TAG, "Uploaded JSON in " + Math.floor(elapsedTime / 1000) + " seconds"); + // Close the JSON object. + os.write('}'); // Flush the contents up the stream. os.flush(); @@ -264,7 +339,8 @@ private void writeToFromFile(OutputStream os, RequestParams.FileWrapper wrapper) // Send the meta data. writeMetaData(os, wrapper.file.getName(), wrapper.contentType); - int bytesRead, bytesWritten = 0, totalSize = (int) wrapper.file.length(); + int bytesRead; + long bytesWritten = 0, totalSize = wrapper.file.length(); // Open the file for reading. FileInputStream in = new FileInputStream(wrapper.file); @@ -312,69 +388,4 @@ private void writeMetaData(OutputStream os, String name, String contentType) thr private void endMetaData(OutputStream os) throws IOException { os.write('"'); } - - // Curtosy of Simple-JSON: http://goo.gl/XoW8RF - // Changed a bit to suit our needs in this class. - static byte[] escape(String string) { - // If it's null, just return prematurely. - if (string == null) { - return JSON_NULL; - } - - // Surround with quotations. - BUILDER.append('"'); - - int length = string.length(), pos = -1; - while (++pos < length) { - char ch = string.charAt(pos); - switch (ch) { - case '"': - BUILDER.append("\\\""); - break; - case '\\': - BUILDER.append("\\\\"); - break; - case '\b': - BUILDER.append("\\b"); - break; - case '\f': - BUILDER.append("\\f"); - break; - case '\n': - BUILDER.append("\\n"); - break; - case '\r': - BUILDER.append("\\r"); - break; - case '\t': - BUILDER.append("\\t"); - break; - default: - // Reference: http://www.unicode.org/versions/Unicode5.1.0/ - if ((ch >= '\u0000' && ch <= '\u001F') || (ch >= '\u007F' && ch <= '\u009F') || (ch >= '\u2000' && ch <= '\u20FF')) { - String intString = Integer.toHexString(ch); - BUILDER.append("\\u"); - int intLength = 4 - intString.length(); - for (int zero = 0; zero < intLength; zero++) { - BUILDER.append('0'); - } - BUILDER.append(intString.toUpperCase(Locale.US)); - } else { - BUILDER.append(ch); - } - break; - } - } - - // Surround with quotations. - BUILDER.append('"'); - - try { - return BUILDER.toString().getBytes(); - } finally { - // Empty the String buffer. - // This is 20-30% faster than instantiating a new object. - BUILDER.setLength(0); - } - } } diff --git a/library/src/main/java/com/loopj/android/http/JsonValueInterface.java b/library/src/main/java/com/loopj/android/http/JsonValueInterface.java index 110b5c1c3..303d96b22 100644 --- a/library/src/main/java/com/loopj/android/http/JsonValueInterface.java +++ b/library/src/main/java/com/loopj/android/http/JsonValueInterface.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Copyright (c) 2011 James Smith - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -24,7 +24,7 @@ * not rely on {@link org.json.JSONArray} or {@link org.json.JSONObject} to * exchange data. * - * @author Noor Dawod + * @author Noor Dawod {@literal } */ public interface JsonValueInterface { diff --git a/library/src/main/java/com/loopj/android/http/LogHandler.java b/library/src/main/java/com/loopj/android/http/LogHandler.java new file mode 100644 index 000000000..2b7080a15 --- /dev/null +++ b/library/src/main/java/com/loopj/android/http/LogHandler.java @@ -0,0 +1,130 @@ +package com.loopj.android.http; + +import android.annotation.TargetApi; +import android.os.Build; +import android.util.Log; + +public class LogHandler implements LogInterface { + + boolean mLoggingEnabled = true; + int mLoggingLevel = VERBOSE; + + @Override + public boolean isLoggingEnabled() { + return mLoggingEnabled; + } + + @Override + public void setLoggingEnabled(boolean loggingEnabled) { + this.mLoggingEnabled = loggingEnabled; + } + + @Override + public int getLoggingLevel() { + return mLoggingLevel; + } + + @Override + public void setLoggingLevel(int loggingLevel) { + this.mLoggingLevel = loggingLevel; + } + + @Override + public boolean shouldLog(int logLevel) { + return logLevel >= mLoggingLevel; + } + + public void log(int logLevel, String tag, String msg) { + logWithThrowable(logLevel, tag, msg, null); + } + + public void logWithThrowable(int logLevel, String tag, String msg, Throwable t) { + if (isLoggingEnabled() && shouldLog(logLevel)) { + switch (logLevel) { + case VERBOSE: + Log.v(tag, msg, t); + break; + case WARN: + Log.w(tag, msg, t); + break; + case ERROR: + Log.e(tag, msg, t); + break; + case DEBUG: + Log.d(tag, msg, t); + break; + case WTF: + checkedWtf(tag, msg, t); + break; + case INFO: + Log.i(tag, msg, t); + break; + } + } + } + + @TargetApi(Build.VERSION_CODES.FROYO) + private void checkedWtf(String tag, String msg, Throwable t) { + Log.wtf(tag, msg, t); + } + + @Override + public void v(String tag, String msg) { + log(VERBOSE, tag, msg); + } + + @Override + public void v(String tag, String msg, Throwable t) { + logWithThrowable(VERBOSE, tag, msg, t); + } + + @Override + public void d(String tag, String msg) { + log(VERBOSE, tag, msg); + } + + @Override + public void d(String tag, String msg, Throwable t) { + logWithThrowable(DEBUG, tag, msg, t); + } + + @Override + public void i(String tag, String msg) { + log(INFO, tag, msg); + } + + @Override + public void i(String tag, String msg, Throwable t) { + logWithThrowable(INFO, tag, msg, t); + } + + @Override + public void w(String tag, String msg) { + log(WARN, tag, msg); + } + + @Override + public void w(String tag, String msg, Throwable t) { + logWithThrowable(WARN, tag, msg, t); + } + + @Override + public void e(String tag, String msg) { + log(ERROR, tag, msg); + } + + @Override + public void e(String tag, String msg, Throwable t) { + logWithThrowable(ERROR, tag, msg, t); + } + + @Override + public void wtf(String tag, String msg) { + log(WTF, tag, msg); + } + + @Override + public void wtf(String tag, String msg, Throwable t) { + logWithThrowable(WTF, tag, msg, t); + } +} diff --git a/library/src/main/java/com/loopj/android/http/LogInterface.java b/library/src/main/java/com/loopj/android/http/LogInterface.java new file mode 100644 index 000000000..f5a06b19a --- /dev/null +++ b/library/src/main/java/com/loopj/android/http/LogInterface.java @@ -0,0 +1,51 @@ +package com.loopj.android.http; + +/** + * Interface independent to any library, which currently uses same interface as {@link android.util.Log} implementation + * You can change currently used LogInterface through {@link AsyncHttpClient#setLogInterface(LogInterface)} + */ +public interface LogInterface { + + int VERBOSE = 2; + int DEBUG = 3; + int INFO = 4; + int WARN = 5; + int ERROR = 6; + int WTF = 8; + + boolean isLoggingEnabled(); + + void setLoggingEnabled(boolean loggingEnabled); + + int getLoggingLevel(); + + void setLoggingLevel(int loggingLevel); + + boolean shouldLog(int logLevel); + + void v(String tag, String msg); + + void v(String tag, String msg, Throwable t); + + void d(String tag, String msg); + + void d(String tag, String msg, Throwable t); + + void i(String tag, String msg); + + void i(String tag, String msg, Throwable t); + + void w(String tag, String msg); + + void w(String tag, String msg, Throwable t); + + void e(String tag, String msg); + + void e(String tag, String msg, Throwable t); + + void wtf(String tag, String msg); + + void wtf(String tag, String msg, Throwable t); + + +} diff --git a/library/src/main/java/com/loopj/android/http/MyRedirectHandler.java b/library/src/main/java/com/loopj/android/http/MyRedirectHandler.java index dfc45ec04..54f385ede 100644 --- a/library/src/main/java/com/loopj/android/http/MyRedirectHandler.java +++ b/library/src/main/java/com/loopj/android/http/MyRedirectHandler.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Copyright (c) 2014 Aymon Fournier - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -18,24 +18,24 @@ package com.loopj.android.http; -import org.apache.http.Header; -import org.apache.http.HttpHost; -import org.apache.http.HttpRequest; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.ProtocolException; -import org.apache.http.client.CircularRedirectException; -import org.apache.http.client.params.ClientPNames; -import org.apache.http.client.utils.URIUtils; -import org.apache.http.impl.client.DefaultRedirectHandler; -import org.apache.http.impl.client.RedirectLocations; -import org.apache.http.params.HttpParams; -import org.apache.http.protocol.ExecutionContext; -import org.apache.http.protocol.HttpContext; - import java.net.URI; import java.net.URISyntaxException; +import cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.HttpHost; +import cz.msebera.android.httpclient.HttpRequest; +import cz.msebera.android.httpclient.HttpResponse; +import cz.msebera.android.httpclient.HttpStatus; +import cz.msebera.android.httpclient.ProtocolException; +import cz.msebera.android.httpclient.client.CircularRedirectException; +import cz.msebera.android.httpclient.client.params.ClientPNames; +import cz.msebera.android.httpclient.client.utils.URIUtils; +import cz.msebera.android.httpclient.impl.client.DefaultRedirectHandler; +import cz.msebera.android.httpclient.impl.client.RedirectLocations; +import cz.msebera.android.httpclient.params.HttpParams; +import cz.msebera.android.httpclient.protocol.ExecutionContext; +import cz.msebera.android.httpclient.protocol.HttpContext; + /** * Taken from StackOverflow * diff --git a/library/src/main/java/com/loopj/android/http/MySSLSocketFactory.java b/library/src/main/java/com/loopj/android/http/MySSLSocketFactory.java index 4808bcbf1..7a2e6f432 100755 --- a/library/src/main/java/com/loopj/android/http/MySSLSocketFactory.java +++ b/library/src/main/java/com/loopj/android/http/MySSLSocketFactory.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Copyright (c) 2011 James Smith - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -18,19 +18,6 @@ package com.loopj.android.http; -import org.apache.http.HttpVersion; -import org.apache.http.conn.ClientConnectionManager; -import org.apache.http.conn.scheme.PlainSocketFactory; -import org.apache.http.conn.scheme.Scheme; -import org.apache.http.conn.scheme.SchemeRegistry; -import org.apache.http.conn.ssl.SSLSocketFactory; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.HttpParams; -import org.apache.http.params.HttpProtocolParams; -import org.apache.http.protocol.HTTP; - import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; @@ -47,16 +34,31 @@ import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; +import cz.msebera.android.httpclient.HttpVersion; +import cz.msebera.android.httpclient.conn.ClientConnectionManager; +import cz.msebera.android.httpclient.conn.scheme.PlainSocketFactory; +import cz.msebera.android.httpclient.conn.scheme.Scheme; +import cz.msebera.android.httpclient.conn.scheme.SchemeRegistry; +import cz.msebera.android.httpclient.conn.ssl.SSLSocketFactory; +import cz.msebera.android.httpclient.impl.client.DefaultHttpClient; +import cz.msebera.android.httpclient.impl.conn.tsccm.ThreadSafeClientConnManager; +import cz.msebera.android.httpclient.params.BasicHttpParams; +import cz.msebera.android.httpclient.params.HttpParams; +import cz.msebera.android.httpclient.params.HttpProtocolParams; +import cz.msebera.android.httpclient.protocol.HTTP; + /** * This file is introduced to fix HTTPS Post bug on API < ICS see - * http://code.google.com/p/android/issues/detail?id=13117#c14

 

Warning! This omits SSL + * https://code.google.com/p/android/issues/detail?id=13117#c14

 

Warning! This omits SSL * certificate validation on every device, use with caution */ public class MySSLSocketFactory extends SSLSocketFactory { - SSLContext sslContext = SSLContext.getInstance("TLS"); + final SSLContext sslContext = SSLContext.getInstance("TLS"); /** * Creates a new SSL Socket Factory with the given KeyStore. @@ -72,9 +74,19 @@ public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, X509TrustManager tm = new X509TrustManager() { public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + try { + chain[0].checkValidity(); + } catch (Exception e) { + throw new CertificateException("Certificate not valid or trusted."); + } } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + try { + chain[0].checkValidity(); + } catch (Exception e) { + throw new CertificateException("Certificate not valid or trusted."); + } } public X509Certificate[] getAcceptedIssuers() { @@ -85,23 +97,6 @@ public X509Certificate[] getAcceptedIssuers() { sslContext.init(null, new TrustManager[]{tm}, null); } - @Override - public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException { - return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose); - } - - @Override - public Socket createSocket() throws IOException { - return sslContext.getSocketFactory().createSocket(); - } - - /** - * Makes HttpsURLConnection trusts a set of certificates specified by the KeyStore - */ - public void fixHttpsURLConnection() { - HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); - } - /** * Gets a KeyStore containing the Certificate * @@ -201,4 +196,35 @@ public static DefaultHttpClient getNewHttpClient(KeyStore keyStore) { } } -} + @Override + public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException { + Socket localSocket = sslContext.getSocketFactory().createSocket(socket, host, port, autoClose); + enableSecureProtocols(localSocket); + return localSocket; + } + + @Override + public Socket createSocket() throws IOException { + Socket socket = sslContext.getSocketFactory().createSocket(); + enableSecureProtocols(socket); + return socket; + } + + /** + * Activate supported protocols on the socket. + * + * @param socket The socket on which to activate secure protocols. + */ + private void enableSecureProtocols(Socket socket) { + // set all supported protocols + SSLParameters params = sslContext.getSupportedSSLParameters(); + ((SSLSocket) socket).setEnabledProtocols(params.getProtocols()); + } + + /** + * Makes HttpsURLConnection trusts a set of certificates specified by the KeyStore + */ + public void fixHttpsURLConnection() { + HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); + } +} \ No newline at end of file diff --git a/library/src/main/java/com/loopj/android/http/PersistentCookieStore.java b/library/src/main/java/com/loopj/android/http/PersistentCookieStore.java index e5e99c8c8..8065d4821 100755 --- a/library/src/main/java/com/loopj/android/http/PersistentCookieStore.java +++ b/library/src/main/java/com/loopj/android/http/PersistentCookieStore.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Copyright (c) 2011 James Smith - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -21,10 +21,6 @@ import android.content.Context; import android.content.SharedPreferences; import android.text.TextUtils; -import android.util.Log; - -import org.apache.http.client.CookieStore; -import org.apache.http.cookie.Cookie; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -37,6 +33,9 @@ import java.util.Locale; import java.util.concurrent.ConcurrentHashMap; +import cz.msebera.android.httpclient.client.CookieStore; +import cz.msebera.android.httpclient.cookie.Cookie; + /** * A persistent cookie store which implements the Apache HttpClient {@link CookieStore} interface. * Cookies are stored and will persist on the user's device between application sessions since they @@ -49,10 +48,9 @@ public class PersistentCookieStore implements CookieStore { private static final String COOKIE_PREFS = "CookiePrefsFile"; private static final String COOKIE_NAME_STORE = "names"; private static final String COOKIE_NAME_PREFIX = "cookie_"; - private boolean omitNonPersistentCookies = false; - private final ConcurrentHashMap cookies; private final SharedPreferences cookiePrefs; + private boolean omitNonPersistentCookies = false; /** * Construct a persistent cookie store. @@ -99,7 +97,7 @@ public void addCookie(Cookie cookie) { SharedPreferences.Editor prefsWriter = cookiePrefs.edit(); prefsWriter.putString(COOKIE_NAME_STORE, TextUtils.join(",", cookies.keySet())); prefsWriter.putString(COOKIE_NAME_PREFIX + name, encodeCookie(new SerializableCookie(cookie))); - prefsWriter.commit(); + prefsWriter.apply(); } @Override @@ -110,7 +108,7 @@ public void clear() { prefsWriter.remove(COOKIE_NAME_PREFIX + name); } prefsWriter.remove(COOKIE_NAME_STORE); - prefsWriter.commit(); + prefsWriter.apply(); // Clear cookies from local store cookies.clear(); @@ -140,7 +138,7 @@ public boolean clearExpired(Date date) { if (clearedAny) { prefsWriter.putString(COOKIE_NAME_STORE, TextUtils.join(",", cookies.keySet())); } - prefsWriter.commit(); + prefsWriter.apply(); return clearedAny; } @@ -170,7 +168,7 @@ public void deleteCookie(Cookie cookie) { cookies.remove(name); SharedPreferences.Editor prefsWriter = cookiePrefs.edit(); prefsWriter.remove(COOKIE_NAME_PREFIX + name); - prefsWriter.commit(); + prefsWriter.apply(); } /** @@ -187,7 +185,7 @@ protected String encodeCookie(SerializableCookie cookie) { ObjectOutputStream outputStream = new ObjectOutputStream(os); outputStream.writeObject(cookie); } catch (IOException e) { - Log.d(LOG_TAG, "IOException in encodeCookie", e); + AsyncHttpClient.log.d(LOG_TAG, "IOException in encodeCookie", e); return null; } @@ -208,9 +206,9 @@ protected Cookie decodeCookie(String cookieString) { ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); cookie = ((SerializableCookie) objectInputStream.readObject()).getCookie(); } catch (IOException e) { - Log.d(LOG_TAG, "IOException in decodeCookie", e); + AsyncHttpClient.log.d(LOG_TAG, "IOException in decodeCookie", e); } catch (ClassNotFoundException e) { - Log.d(LOG_TAG, "ClassNotFoundException in decodeCookie", e); + AsyncHttpClient.log.d(LOG_TAG, "ClassNotFoundException in decodeCookie", e); } return cookie; diff --git a/library/src/main/java/com/loopj/android/http/PreemtiveAuthorizationHttpRequestInterceptor.java b/library/src/main/java/com/loopj/android/http/PreemptiveAuthorizationHttpRequestInterceptor.java old mode 100755 new mode 100644 similarity index 62% rename from library/src/main/java/com/loopj/android/http/PreemtiveAuthorizationHttpRequestInterceptor.java rename to library/src/main/java/com/loopj/android/http/PreemptiveAuthorizationHttpRequestInterceptor.java index 3f52da79b..806c7be25 --- a/library/src/main/java/com/loopj/android/http/PreemtiveAuthorizationHttpRequestInterceptor.java +++ b/library/src/main/java/com/loopj/android/http/PreemptiveAuthorizationHttpRequestInterceptor.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Copyright (c) 2014 Marek Sebera - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -18,22 +18,22 @@ package com.loopj.android.http; -import org.apache.http.HttpException; -import org.apache.http.HttpHost; -import org.apache.http.HttpRequest; -import org.apache.http.HttpRequestInterceptor; -import org.apache.http.auth.AuthScope; -import org.apache.http.auth.AuthState; -import org.apache.http.auth.Credentials; -import org.apache.http.client.CredentialsProvider; -import org.apache.http.client.protocol.ClientContext; -import org.apache.http.impl.auth.BasicScheme; -import org.apache.http.protocol.ExecutionContext; -import org.apache.http.protocol.HttpContext; - import java.io.IOException; -public class PreemtiveAuthorizationHttpRequestInterceptor implements HttpRequestInterceptor { +import cz.msebera.android.httpclient.HttpException; +import cz.msebera.android.httpclient.HttpHost; +import cz.msebera.android.httpclient.HttpRequest; +import cz.msebera.android.httpclient.HttpRequestInterceptor; +import cz.msebera.android.httpclient.auth.AuthScope; +import cz.msebera.android.httpclient.auth.AuthState; +import cz.msebera.android.httpclient.auth.Credentials; +import cz.msebera.android.httpclient.client.CredentialsProvider; +import cz.msebera.android.httpclient.client.protocol.ClientContext; +import cz.msebera.android.httpclient.impl.auth.BasicScheme; +import cz.msebera.android.httpclient.protocol.ExecutionContext; +import cz.msebera.android.httpclient.protocol.HttpContext; + +public class PreemptiveAuthorizationHttpRequestInterceptor implements HttpRequestInterceptor { public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException { AuthState authState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE); diff --git a/library/src/main/java/com/loopj/android/http/RangeFileAsyncHttpResponseHandler.java b/library/src/main/java/com/loopj/android/http/RangeFileAsyncHttpResponseHandler.java index 7abffb1a4..f327a8caf 100755 --- a/library/src/main/java/com/loopj/android/http/RangeFileAsyncHttpResponseHandler.java +++ b/library/src/main/java/com/loopj/android/http/RangeFileAsyncHttpResponseHandler.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Copyright (c) 2014 Marek Sebera - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -18,24 +18,22 @@ package com.loopj.android.http; -import android.util.Log; - -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.StatusLine; -import org.apache.http.client.HttpResponseException; -import org.apache.http.client.methods.HttpUriRequest; - import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.HttpEntity; +import cz.msebera.android.httpclient.HttpResponse; +import cz.msebera.android.httpclient.HttpStatus; +import cz.msebera.android.httpclient.StatusLine; +import cz.msebera.android.httpclient.client.HttpResponseException; +import cz.msebera.android.httpclient.client.methods.HttpUriRequest; + public abstract class RangeFileAsyncHttpResponseHandler extends FileAsyncHttpResponseHandler { - private static final String LOG_TAG = "RangeFileAsyncHttpResponseHandler"; + private static final String LOG_TAG = "RangeFileAsyncHttpRH"; private long current = 0; private boolean append = false; @@ -67,7 +65,7 @@ public void sendResponseMessage(HttpResponse response) throws IOException { append = false; current = 0; } else { - Log.v(LOG_TAG, AsyncHttpClient.HEADER_CONTENT_RANGE + ": " + header.getValue()); + AsyncHttpClient.log.v(LOG_TAG, AsyncHttpClient.HEADER_CONTENT_RANGE + ": " + header.getValue()); } sendSuccessMessage(status.getStatusCode(), response.getAllHeaders(), getResponseData(response.getEntity())); } @@ -88,7 +86,7 @@ protected byte[] getResponseData(HttpEntity entity) throws IOException { while (current < contentLength && (l = instream.read(tmp)) != -1 && !Thread.currentThread().isInterrupted()) { current += l; buffer.write(tmp, 0, l); - sendProgressMessage((int) current, (int) contentLength); + sendProgressMessage(current, contentLength); } } finally { instream.close(); @@ -101,8 +99,8 @@ protected byte[] getResponseData(HttpEntity entity) throws IOException { } public void updateRequestHeaders(HttpUriRequest uriRequest) { - if (mFile.exists() && mFile.canWrite()) - current = mFile.length(); + if (file.exists() && file.canWrite()) + current = file.length(); if (current > 0) { append = true; uriRequest.setHeader("Range", "bytes=" + current + "-"); diff --git a/library/src/main/java/com/loopj/android/http/RequestHandle.java b/library/src/main/java/com/loopj/android/http/RequestHandle.java index 5185c7499..959190e13 100755 --- a/library/src/main/java/com/loopj/android/http/RequestHandle.java +++ b/library/src/main/java/com/loopj/android/http/RequestHandle.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Copyright (c) 2013 Jason Choy - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -40,7 +40,8 @@ public RequestHandle(AsyncHttpRequest request) { * thread executing this request should be interrupted in an attempt to stop the request. *

 

After this method returns, subsequent calls to isDone() will always return * true. Subsequent calls to isCancelled() will always return true if this method returned - * true. + * true. Subsequent calls to isDone() will return true either if the request got cancelled by + * this method, or if the request completed normally * * @param mayInterruptIfRunning true if the thread executing this request should be interrupted; * otherwise, in-progress requests are allowed to complete @@ -57,8 +58,11 @@ public void run() { _request.cancel(mayInterruptIfRunning); } }).start(); + // Cannot reliably tell if the request got immediately canceled at this point + // we'll assume it got cancelled + return true; } else { - _request.cancel(mayInterruptIfRunning); + return _request.cancel(mayInterruptIfRunning); } } return false; @@ -91,4 +95,27 @@ public boolean shouldBeGarbageCollected() { request.clear(); return should; } + + /** + * Will return TAG of underlying AsyncHttpRequest if it's not already GCed + * + * @return Object TAG, can be null + */ + public Object getTag() { + AsyncHttpRequest _request = request.get(); + return _request == null ? null : _request.getTag(); + } + + /** + * Will set Object as TAG to underlying AsyncHttpRequest + * + * @param tag Object used as TAG to underlying AsyncHttpRequest + * @return this RequestHandle to allow fluid syntax + */ + public RequestHandle setTag(Object tag) { + AsyncHttpRequest _request = request.get(); + if (_request != null) + _request.setRequestTag(tag); + return this; + } } \ No newline at end of file diff --git a/library/src/main/java/com/loopj/android/http/RequestParams.java b/library/src/main/java/com/loopj/android/http/RequestParams.java index 28a7a93a4..e594f7909 100755 --- a/library/src/main/java/com/loopj/android/http/RequestParams.java +++ b/library/src/main/java/com/loopj/android/http/RequestParams.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Copyright (c) 2011 James Smith - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -18,14 +18,6 @@ package com.loopj.android.http; -import android.util.Log; - -import org.apache.http.HttpEntity; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.utils.URLEncodedUtils; -import org.apache.http.message.BasicNameValuePair; -import org.apache.http.protocol.HTTP; - import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -38,9 +30,17 @@ import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentSkipListMap; +//import java.util.concurrent.ConcurrentSkipList; + +import cz.msebera.android.httpclient.HttpEntity; +import cz.msebera.android.httpclient.client.entity.UrlEncodedFormEntity; +import cz.msebera.android.httpclient.client.utils.URLEncodedUtils; +import cz.msebera.android.httpclient.message.BasicNameValuePair; +import cz.msebera.android.httpclient.protocol.HTTP; /** * A collection of string request parameters or files to send along with requests made from an @@ -67,10 +67,13 @@ * List<String> list = new ArrayList<String>(); // Ordered collection * list.add("Java"); * list.add("C"); - * params.put("languages", list); // url params: "languages[]=Java&languages[]=C" + * params.put("languages", list); // url params: "languages[0]=Java&languages[1]=C" * * String[] colors = { "blue", "yellow" }; // Ordered collection - * params.put("colors", colors); // url params: "colors[]=blue&colors[]=yellow" + * params.put("colors", colors); // url params: "colors[0]=blue&colors[1]=yellow" + * + * File[] files = { new File("pic.jpg"), new File("pic1.jpg") }; // Ordered collection + * params.put("files", files); // url params: "files[]=pic.jpg&files[]=pic1.jpg" * * List<Map<String, String>> listOfMaps = new ArrayList<Map<String, * String>>(); @@ -85,7 +88,7 @@ * params.put("users", listOfMaps); // url params: "users[][age]=30&users[][gender]=male&users[][age]=25&users[][gender]=female" * * AsyncHttpClient client = new AsyncHttpClient(); - * client.post("http://myendpoint.com/", params, responseHandler); + * client.post("https://myendpoint.com/", params, responseHandler); *
*/ public class RequestParams implements Serializable { @@ -97,29 +100,18 @@ public class RequestParams implements Serializable { "application/json"; protected final static String LOG_TAG = "RequestParams"; + protected final ConcurrentSkipListMap urlParams = new ConcurrentSkipListMap(); + protected final ConcurrentSkipListMap streamParams = new ConcurrentSkipListMap(); + protected final ConcurrentSkipListMap fileParams = new ConcurrentSkipListMap(); + protected final ConcurrentSkipListMap> fileArrayParams = new ConcurrentSkipListMap>(); + protected final ConcurrentSkipListMap urlParamsWithObjects = new ConcurrentSkipListMap(); protected boolean isRepeatable; + protected boolean forceMultipartEntity = false; protected boolean useJsonStreamer; + protected String elapsedFieldInJsonStreamer = "_elapsed"; protected boolean autoCloseInputStreams; - protected final ConcurrentHashMap urlParams = new ConcurrentHashMap(); - protected final ConcurrentHashMap streamParams = new ConcurrentHashMap(); - protected final ConcurrentHashMap fileParams = new ConcurrentHashMap(); - protected final ConcurrentHashMap urlParamsWithObjects = new ConcurrentHashMap(); protected String contentEncoding = HTTP.UTF_8; - /** - * Sets content encoding for return value of {@link #getParamString()} and {@link - * #createFormEntity()}

 

Default encoding is "UTF-8" - * - * @param encoding String constant from {@link org.apache.http.protocol.HTTP} - */ - public void setContentEncoding(final String encoding) { - if (encoding != null) { - this.contentEncoding = encoding; - } else { - Log.d(LOG_TAG, "setContentEncoding called with null attribute"); - } - } - /** * Constructs a new empty {@code RequestParams} instance. */ @@ -173,6 +165,32 @@ public RequestParams(Object... keysAndValues) { } } + /** + * Sets content encoding for return value of {@link #getParamString()} and {@link + * #createFormEntity()}

 

Default encoding is "UTF-8" + * + * @param encoding String constant from {@link HTTP} + */ + public void setContentEncoding(final String encoding) { + if (encoding != null) { + this.contentEncoding = encoding; + } else { + AsyncHttpClient.log.d(LOG_TAG, "setContentEncoding called with null attribute"); + } + } + + /** + * If set to true will force Content-Type header to `multipart/form-data` + * even if there are not Files or Streams to be send + *

 

+ * Default value is false + * + * @param force boolean, should declare content-type multipart/form-data even without files or streams present + */ + public void setForceMultipartEntityContentType(boolean force) { + this.forceMultipartEntity = force; + } + /** * Adds a key/value string pair to the request. * @@ -185,12 +203,46 @@ public void put(String key, String value) { } } + /** + * Adds files array to the request. + * + * @param key the key name for the new param. + * @param files the files array to add. + * @throws FileNotFoundException if one of passed files is not found at time of assembling the requestparams into request + */ + public void put(String key, File files[]) throws FileNotFoundException { + put(key, files, null, null); + } + + /** + * Adds files array to the request with both custom provided file content-type and files name + * + * @param key the key name for the new param. + * @param files the files array to add. + * @param contentType the content type of the file, eg. application/json + * @param customFileName file name to use instead of real file name + * @throws FileNotFoundException throws if wrong File argument was passed + */ + public void put(String key, File files[], String contentType, String customFileName) throws FileNotFoundException { + + if (key != null) { + List fileWrappers = new ArrayList(); + for (File file : files) { + if (file == null || !file.exists()) { + throw new FileNotFoundException(); + } + fileWrappers.add(new FileWrapper(file, contentType, customFileName)); + } + fileArrayParams.put(key, fileWrappers); + } + } + /** * Adds a file to the request. * * @param key the key name for the new param. * @param file the file to add. - * @throws java.io.FileNotFoundException throws if wrong File argument was passed + * @throws FileNotFoundException throws if wrong File argument was passed */ public void put(String key, File file) throws FileNotFoundException { put(key, file, null, null); @@ -202,7 +254,7 @@ public void put(String key, File file) throws FileNotFoundException { * @param key the key name for the new param. * @param file the file to add. * @param customFileName file name to use instead of real file name - * @throws java.io.FileNotFoundException throws if wrong File argument was passed + * @throws FileNotFoundException throws if wrong File argument was passed */ public void put(String key, String customFileName, File file) throws FileNotFoundException { put(key, file, null, customFileName); @@ -214,7 +266,7 @@ public void put(String key, String customFileName, File file) throws FileNotFoun * @param key the key name for the new param. * @param file the file to add. * @param contentType the content type of the file, eg. application/json - * @throws java.io.FileNotFoundException throws if wrong File argument was passed + * @throws FileNotFoundException throws if wrong File argument was passed */ public void put(String key, File file, String contentType) throws FileNotFoundException { put(key, file, contentType, null); @@ -227,7 +279,7 @@ public void put(String key, File file, String contentType) throws FileNotFoundEx * @param file the file to add. * @param contentType the content type of the file, eg. application/json * @param customFileName file name to use instead of real file name - * @throws java.io.FileNotFoundException throws if wrong File argument was passed + * @throws FileNotFoundException throws if wrong File argument was passed */ public void put(String key, File file, String contentType, String customFileName) throws FileNotFoundException { if (file == null || !file.exists()) { @@ -354,6 +406,7 @@ public void remove(String key) { streamParams.remove(key); fileParams.remove(key); urlParamsWithObjects.remove(key); + fileArrayParams.remove(key); } /** @@ -366,13 +419,14 @@ public boolean has(String key) { return urlParams.get(key) != null || streamParams.get(key) != null || fileParams.get(key) != null || - urlParamsWithObjects.get(key) != null; + urlParamsWithObjects.get(key) != null || + fileArrayParams.get(key) != null; } @Override public String toString() { StringBuilder result = new StringBuilder(); - for (ConcurrentHashMap.Entry entry : urlParams.entrySet()) { + for (ConcurrentSkipListMap.Entry entry : urlParams.entrySet()) { if (result.length() > 0) result.append("&"); @@ -381,7 +435,7 @@ public String toString() { result.append(entry.getValue()); } - for (ConcurrentHashMap.Entry entry : streamParams.entrySet()) { + for (ConcurrentSkipListMap.Entry entry : streamParams.entrySet()) { if (result.length() > 0) result.append("&"); @@ -390,7 +444,7 @@ public String toString() { result.append("STREAM"); } - for (ConcurrentHashMap.Entry entry : fileParams.entrySet()) { + for (ConcurrentSkipListMap.Entry entry : fileParams.entrySet()) { if (result.length() > 0) result.append("&"); @@ -399,6 +453,15 @@ public String toString() { result.append("FILE"); } + for (ConcurrentSkipListMap.Entry> entry : fileArrayParams.entrySet()) { + if (result.length() > 0) + result.append("&"); + + result.append(entry.getKey()); + result.append("="); + result.append("FILES(SIZE=").append(entry.getValue().size()).append(")"); + } + List params = getParamsList(null, urlParamsWithObjects); for (BasicNameValuePair kv : params) { if (result.length() > 0) @@ -412,12 +475,25 @@ public String toString() { return result.toString(); } - public void setHttpEntityIsRepeatable(boolean isRepeatable) { - this.isRepeatable = isRepeatable; + public void setHttpEntityIsRepeatable(boolean flag) { + this.isRepeatable = flag; } - public void setUseJsonStreamer(boolean useJsonStreamer) { - this.useJsonStreamer = useJsonStreamer; + public void setUseJsonStreamer(boolean flag) { + this.useJsonStreamer = flag; + } + + /** + * Sets an additional field when upload a JSON object through the streamer + * to hold the time, in milliseconds, it took to upload the payload. By + * default, this field is set to "_elapsed". + *

 

+ * To disable this feature, call this method with null as the field value. + * + * @param value field name to add elapsed time, or null to disable + */ + public void setElapsedFieldInJsonStreamer(String value) { + this.elapsedFieldInJsonStreamer = value; } /** @@ -435,13 +511,13 @@ public void setAutoCloseInputStreams(boolean flag) { * * @param progressHandler HttpResponseHandler for reporting progress on entity submit * @return HttpEntity resulting HttpEntity to be included along with {@link - * org.apache.http.client.methods.HttpEntityEnclosingRequestBase} + * cz.msebera.android.httpclient.client.methods.HttpEntityEnclosingRequestBase} * @throws IOException if one of the streams cannot be read */ public HttpEntity getEntity(ResponseHandlerInterface progressHandler) throws IOException { if (useJsonStreamer) { return createJsonStreamerEntity(progressHandler); - } else if (streamParams.isEmpty() && fileParams.isEmpty()) { + } else if (!forceMultipartEntity && streamParams.isEmpty() && fileParams.isEmpty() && fileArrayParams.isEmpty()) { return createFormEntity(); } else { return createMultipartEntity(progressHandler); @@ -449,26 +525,28 @@ public HttpEntity getEntity(ResponseHandlerInterface progressHandler) throws IOE } private HttpEntity createJsonStreamerEntity(ResponseHandlerInterface progressHandler) throws IOException { - JsonStreamerEntity entity = new JsonStreamerEntity(progressHandler, - !fileParams.isEmpty() || !streamParams.isEmpty()); + JsonStreamerEntity entity = new JsonStreamerEntity( + progressHandler, + !fileParams.isEmpty() || !streamParams.isEmpty(), + elapsedFieldInJsonStreamer); // Add string params - for (ConcurrentHashMap.Entry entry : urlParams.entrySet()) { + for (ConcurrentSkipListMap.Entry entry : urlParams.entrySet()) { entity.addPart(entry.getKey(), entry.getValue()); } // Add non-string params - for (ConcurrentHashMap.Entry entry : urlParamsWithObjects.entrySet()) { + for (ConcurrentSkipListMap.Entry entry : urlParamsWithObjects.entrySet()) { entity.addPart(entry.getKey(), entry.getValue()); } // Add file params - for (ConcurrentHashMap.Entry entry : fileParams.entrySet()) { + for (ConcurrentSkipListMap.Entry entry : fileParams.entrySet()) { entity.addPart(entry.getKey(), entry.getValue()); } // Add stream params - for (ConcurrentHashMap.Entry entry : streamParams.entrySet()) { + for (ConcurrentSkipListMap.Entry entry : streamParams.entrySet()) { StreamWrapper stream = entry.getValue(); if (stream.inputStream != null) { entity.addPart(entry.getKey(), @@ -488,7 +566,7 @@ private HttpEntity createFormEntity() { try { return new UrlEncodedFormEntity(getParamsList(), contentEncoding); } catch (UnsupportedEncodingException e) { - Log.e(LOG_TAG, "createFormEntity failed", e); + AsyncHttpClient.log.e(LOG_TAG, "createFormEntity failed", e); return null; // Can happen, if the 'contentEncoding' won't be HTTP.UTF_8 } } @@ -498,7 +576,7 @@ private HttpEntity createMultipartEntity(ResponseHandlerInterface progressHandle entity.setIsRepeatable(isRepeatable); // Add string params - for (ConcurrentHashMap.Entry entry : urlParams.entrySet()) { + for (ConcurrentSkipListMap.Entry entry : urlParams.entrySet()) { entity.addPartWithCharset(entry.getKey(), entry.getValue(), contentEncoding); } @@ -509,7 +587,7 @@ private HttpEntity createMultipartEntity(ResponseHandlerInterface progressHandle } // Add stream params - for (ConcurrentHashMap.Entry entry : streamParams.entrySet()) { + for (ConcurrentSkipListMap.Entry entry : streamParams.entrySet()) { StreamWrapper stream = entry.getValue(); if (stream.inputStream != null) { entity.addPart(entry.getKey(), stream.name, stream.inputStream, @@ -518,18 +596,26 @@ private HttpEntity createMultipartEntity(ResponseHandlerInterface progressHandle } // Add file params - for (ConcurrentHashMap.Entry entry : fileParams.entrySet()) { + for (ConcurrentSkipListMap.Entry entry : fileParams.entrySet()) { FileWrapper fileWrapper = entry.getValue(); entity.addPart(entry.getKey(), fileWrapper.file, fileWrapper.contentType, fileWrapper.customFileName); } + // Add file collection + for (ConcurrentSkipListMap.Entry> entry : fileArrayParams.entrySet()) { + List fileWrapper = entry.getValue(); + for (FileWrapper fw : fileWrapper) { + entity.addPart(entry.getKey(), fw.file, fw.contentType, fw.customFileName); + } + } + return entity; } protected List getParamsList() { List lparams = new LinkedList(); - for (ConcurrentHashMap.Entry entry : urlParams.entrySet()) { + for (ConcurrentSkipListMap.Entry entry : urlParams.entrySet()) { lparams.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); } @@ -551,7 +637,7 @@ private List getParamsList(String key, Object value) { if (nestedKey instanceof String) { Object nestedValue = map.get(nestedKey); if (nestedValue != null) { - params.addAll(getParamsList(key == null ? (String) nestedKey : String.format("%s[%s]", key, nestedKey), + params.addAll(getParamsList(key == null ? (String) nestedKey : String.format(Locale.US, "%s[%s]", key, nestedKey), nestedValue)); } } @@ -560,13 +646,13 @@ private List getParamsList(String key, Object value) { List list = (List) value; int listSize = list.size(); for (int nestedValueIndex = 0; nestedValueIndex < listSize; nestedValueIndex++) { - params.addAll(getParamsList(String.format("%s[%d]", key, nestedValueIndex), list.get(nestedValueIndex))); + params.addAll(getParamsList(String.format(Locale.US, "%s[%d]", key, nestedValueIndex), list.get(nestedValueIndex))); } } else if (value instanceof Object[]) { Object[] array = (Object[]) value; int arrayLength = array.length; for (int nestedValueIndex = 0; nestedValueIndex < arrayLength; nestedValueIndex++) { - params.addAll(getParamsList(String.format("%s[%d]", key, nestedValueIndex), array[nestedValueIndex])); + params.addAll(getParamsList(String.format(Locale.US, "%s[%d]", key, nestedValueIndex), array[nestedValueIndex])); } } else if (value instanceof Set) { Set set = (Set) value; diff --git a/library/src/main/java/com/loopj/android/http/ResponseHandlerInterface.java b/library/src/main/java/com/loopj/android/http/ResponseHandlerInterface.java index 193f44fea..4040a965b 100755 --- a/library/src/main/java/com/loopj/android/http/ResponseHandlerInterface.java +++ b/library/src/main/java/com/loopj/android/http/ResponseHandlerInterface.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Copyright (c) 2013 Marek Sebera - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -18,12 +18,12 @@ package com.loopj.android.http; -import org.apache.http.Header; -import org.apache.http.HttpResponse; - import java.io.IOException; import java.net.URI; +import cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.HttpResponse; + /** * Interface to standardize implementations */ @@ -53,7 +53,7 @@ public interface ResponseHandlerInterface { * @param bytesWritten number of written bytes * @param bytesTotal number of total bytes to be written */ - void sendProgressMessage(int bytesWritten, int bytesTotal); + void sendProgressMessage(long bytesWritten, long bytesTotal); /** * Notifies callback, that request was cancelled @@ -91,28 +91,35 @@ public interface ResponseHandlerInterface { * * @return uri of origin request */ - public URI getRequestURI(); + URI getRequestURI(); /** - * Returns Header[] which were used to request + * Helper for handlers to receive Request URI info * - * @return headers from origin request + * @param requestURI claimed request URI */ - public Header[] getRequestHeaders(); + void setRequestURI(URI requestURI); /** - * Helper for handlers to receive Request URI info + * Returns Header[] which were used to request * - * @param requestURI claimed request URI + * @return headers from origin request */ - public void setRequestURI(URI requestURI); + Header[] getRequestHeaders(); /** * Helper for handlers to receive Request Header[] info * * @param requestHeaders Headers, claimed to be from original request */ - public void setRequestHeaders(Header[] requestHeaders); + void setRequestHeaders(Header[] requestHeaders); + + /** + * Returns whether the handler is asynchronous or synchronous + * + * @return boolean if the ResponseHandler is running in synchronous mode + */ + boolean getUseSynchronousMode(); /** * Can set, whether the handler should be asynchronous or synchronous @@ -122,17 +129,26 @@ public interface ResponseHandlerInterface { void setUseSynchronousMode(boolean useSynchronousMode); /** - * Returns whether the handler is asynchronous or synchronous + * Returns whether the handler should be executed on the pool's thread + * or the UI thread * - * @return boolean if the ResponseHandler is running in synchronous mode + * @return boolean if the ResponseHandler should run on pool's thread */ - boolean getUseSynchronousMode(); + boolean getUsePoolThread(); + + /** + * Sets whether the handler should be executed on the pool's thread or the + * UI thread + * + * @param usePoolThread if the ResponseHandler should run on pool's thread + */ + void setUsePoolThread(boolean usePoolThread); /** * This method is called once by the system when the response is about to be * processed by the system. The library makes sure that a single response * is pre-processed only once. - * + *

 

* Please note: pre-processing does NOT run on the main thread, and thus * any UI activities that you must perform should be properly dispatched to * the app's UI thread. @@ -146,7 +162,7 @@ public interface ResponseHandlerInterface { * This method is called once by the system when the request has been fully * sent, handled and finished. The library makes sure that a single response * is post-processed only once. - * + *

 

* Please note: post-processing does NOT run on the main thread, and thus * any UI activities that you must perform should be properly dispatched to * the app's UI thread. @@ -155,4 +171,19 @@ public interface ResponseHandlerInterface { * @param response The response to post-process */ void onPostProcessResponse(ResponseHandlerInterface instance, HttpResponse response); + + /** + * Will retrieve TAG Object if it's not already freed from memory + * + * @return Object TAG or null if it's been garbage collected + */ + Object getTag(); + + /** + * Will set TAG to ResponseHandlerInterface implementation, which can be then obtained + * in implemented methods, such as onSuccess, onFailure, ... + * + * @param TAG Object to be set as TAG, will be placed in WeakReference + */ + void setTag(Object TAG); } diff --git a/library/src/main/java/com/loopj/android/http/RetryHandler.java b/library/src/main/java/com/loopj/android/http/RetryHandler.java index 3b6ad1969..c9e549a2b 100755 --- a/library/src/main/java/com/loopj/android/http/RetryHandler.java +++ b/library/src/main/java/com/loopj/android/http/RetryHandler.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Copyright (c) 2011 James Smith - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -25,12 +25,6 @@ import android.os.SystemClock; -import org.apache.http.NoHttpResponseException; -import org.apache.http.client.HttpRequestRetryHandler; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.protocol.ExecutionContext; -import org.apache.http.protocol.HttpContext; - import java.io.IOException; import java.io.InterruptedIOException; import java.net.SocketException; @@ -39,6 +33,12 @@ import javax.net.ssl.SSLException; +import cz.msebera.android.httpclient.NoHttpResponseException; +import cz.msebera.android.httpclient.client.HttpRequestRetryHandler; +import cz.msebera.android.httpclient.client.methods.HttpUriRequest; +import cz.msebera.android.httpclient.protocol.ExecutionContext; +import cz.msebera.android.httpclient.protocol.HttpContext; + class RetryHandler implements HttpRequestRetryHandler { private final static HashSet> exceptionWhitelist = new HashSet>(); private final static HashSet> exceptionBlacklist = new HashSet>(); @@ -65,6 +65,14 @@ public RetryHandler(int maxRetries, int retrySleepTimeMS) { this.retrySleepTimeMS = retrySleepTimeMS; } + static void addClassToWhitelist(Class cls) { + exceptionWhitelist.add(cls); + } + + static void addClassToBlacklist(Class cls) { + exceptionBlacklist.add(cls); + } + @Override public boolean retryRequest(IOException exception, int executionCount, HttpContext context) { boolean retry = true; @@ -103,14 +111,6 @@ public boolean retryRequest(IOException exception, int executionCount, HttpConte return retry; } - static void addClassToWhitelist(Class cls) { - exceptionWhitelist.add(cls); - } - - static void addClassToBlacklist(Class cls) { - exceptionBlacklist.add(cls); - } - protected boolean isInList(HashSet> list, Throwable error) { for (Class aList : list) { if (aList.isInstance(error)) { diff --git a/library/src/main/java/com/loopj/android/http/SaxAsyncHttpResponseHandler.java b/library/src/main/java/com/loopj/android/http/SaxAsyncHttpResponseHandler.java index f4663b7bb..8198fb549 100644 --- a/library/src/main/java/com/loopj/android/http/SaxAsyncHttpResponseHandler.java +++ b/library/src/main/java/com/loopj/android/http/SaxAsyncHttpResponseHandler.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Copyright (c) 2011 James Smith - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -18,10 +18,6 @@ package com.loopj.android.http; -import android.util.Log; - -import org.apache.http.Header; -import org.apache.http.HttpEntity; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; @@ -35,14 +31,17 @@ import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; +import cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.HttpEntity; + /** * Provides interface to deserialize SAX responses, using AsyncHttpResponseHandler. Can be used like * this - * + *

 

*
  *     AsyncHttpClient ahc = new AsyncHttpClient();
  *     FontHandler handlerInstance = ... ; // init handler instance
- *     ahc.post("https://server.tld/api/call", new SaxAsyncHttpResponseHandler(handlerInstance){
+ *     ahc.post("https://server.tld/api/call", new SaxAsyncHttpResponseHandler{@literal <}FontHandler{@literal >}(handlerInstance){
  *         @Override
  *         public void onSuccess(int statusCode, Header[] headers, FontHandler t) {
  *              // Request got HTTP success statusCode
@@ -52,7 +51,7 @@
  *              // Request got HTTP fail statusCode
  *         }
  *     });
- * 
+ * 
* * @param Handler extending {@link org.xml.sax.helpers.DefaultHandler} * @see org.xml.sax.helpers.DefaultHandler @@ -60,11 +59,11 @@ */ public abstract class SaxAsyncHttpResponseHandler extends AsyncHttpResponseHandler { + private final static String LOG_TAG = "SaxAsyncHttpRH"; /** * Generic Type of handler */ private T handler = null; - private final static String LOG_TAG = "SaxAsyncHttpResponseHandler"; /** * Constructs new SaxAsyncHttpResponseHandler with given handler instance @@ -85,8 +84,8 @@ public SaxAsyncHttpResponseHandler(T t) { * * @param entity returned HttpEntity * @return deconstructed response - * @throws java.io.IOException - * @see org.apache.http.HttpEntity + * @throws java.io.IOException if there is problem assembling SAX response from stream + * @see cz.msebera.android.httpclient.HttpEntity */ @Override protected byte[] getResponseData(HttpEntity entity) throws IOException { @@ -99,12 +98,12 @@ protected byte[] getResponseData(HttpEntity entity) throws IOException { SAXParser sparser = sfactory.newSAXParser(); XMLReader rssReader = sparser.getXMLReader(); rssReader.setContentHandler(handler); - inputStreamReader = new InputStreamReader(instream, DEFAULT_CHARSET); + inputStreamReader = new InputStreamReader(instream, getCharset()); rssReader.parse(new InputSource(inputStreamReader)); } catch (SAXException e) { - Log.e(LOG_TAG, "getResponseData exception", e); + AsyncHttpClient.log.e(LOG_TAG, "getResponseData exception", e); } catch (ParserConfigurationException e) { - Log.e(LOG_TAG, "getResponseData exception", e); + AsyncHttpClient.log.e(LOG_TAG, "getResponseData exception", e); } finally { AsyncHttpClient.silentCloseInputStream(instream); if (inputStreamReader != null) { @@ -144,6 +143,6 @@ public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) { @Override public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) { - onSuccess(statusCode, headers, handler); + onFailure(statusCode, headers, handler); } } diff --git a/library/src/main/java/com/loopj/android/http/SerializableCookie.java b/library/src/main/java/com/loopj/android/http/SerializableCookie.java index 855105b1c..f6b88ffc0 100755 --- a/library/src/main/java/com/loopj/android/http/SerializableCookie.java +++ b/library/src/main/java/com/loopj/android/http/SerializableCookie.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Copyright (c) 2011 James Smith - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -18,15 +18,15 @@ package com.loopj.android.http; -import org.apache.http.cookie.Cookie; -import org.apache.http.impl.cookie.BasicClientCookie; - import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Date; +import cz.msebera.android.httpclient.cookie.Cookie; +import cz.msebera.android.httpclient.impl.cookie.BasicClientCookie; + /** * A wrapper class around {@link Cookie} and/or {@link BasicClientCookie} designed for use in {@link * PersistentCookieStore}. @@ -61,9 +61,9 @@ private void writeObject(ObjectOutputStream out) throws IOException { } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - String name = (String) in.readObject(); + String key = (String) in.readObject(); String value = (String) in.readObject(); - clientCookie = new BasicClientCookie(name, value); + clientCookie = new BasicClientCookie(key, value); clientCookie.setComment((String) in.readObject()); clientCookie.setDomain((String) in.readObject()); clientCookie.setExpiryDate((Date) in.readObject()); diff --git a/library/src/main/java/com/loopj/android/http/SimpleMultipartEntity.java b/library/src/main/java/com/loopj/android/http/SimpleMultipartEntity.java index a6883068d..2b4758f70 100755 --- a/library/src/main/java/com/loopj/android/http/SimpleMultipartEntity.java +++ b/library/src/main/java/com/loopj/android/http/SimpleMultipartEntity.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Copyright (c) 2011 James Smith - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -17,19 +17,13 @@ */ /* - This code is taken from Rafael Sanches' blog. - http://blog.rafaelsanches.com/2011/01/29/upload-using-multipart-post-using-httpclient-in-android/ + This code is taken from Rafael Sanches' blog. Link is no longer working (as of 17th July 2015) + https://blog.rafaelsanches.com/2011/01/29/upload-using-multipart-post-using-httpclient-in-android/ */ package com.loopj.android.http; import android.text.TextUtils; -import android.util.Log; - -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.message.BasicHeader; -import org.apache.http.protocol.HTTP; import java.io.ByteArrayOutputStream; import java.io.File; @@ -41,6 +35,11 @@ import java.util.List; import java.util.Random; +import cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.HttpEntity; +import cz.msebera.android.httpclient.message.BasicHeader; +import cz.msebera.android.httpclient.protocol.HTTP; + /** * Simplified multipart entity mainly used for sending one or more files. */ @@ -59,19 +58,15 @@ class SimpleMultipartEntity implements HttpEntity { private final String boundary; private final byte[] boundaryLine; private final byte[] boundaryEnd; - private boolean isRepeatable; - private final List fileParts = new ArrayList(); - // The buffer we use for building the message excluding files and the last // boundary private final ByteArrayOutputStream out = new ByteArrayOutputStream(); - private final ResponseHandlerInterface progressHandler; + private boolean isRepeatable; + private long bytesWritten; - private int bytesWritten; - - private int totalSize; + private long totalSize; public SimpleMultipartEntity(ResponseHandlerInterface progressHandler) { final StringBuilder buf = new StringBuilder(); @@ -97,7 +92,7 @@ public void addPart(String key, String value, String contentType) { out.write(CR_LF); } catch (final IOException e) { // Shall not happen on ByteArrayOutputStream - Log.e(LOG_TAG, "addPart ByteArrayOutputStream exception", e); + AsyncHttpClient.log.e(LOG_TAG, "addPart ByteArrayOutputStream exception", e); } } @@ -117,6 +112,7 @@ public void addPart(String key, File file) { public void addPart(String key, File file, String type) { fileParts.add(new FilePart(key, file, normalizeContentType(type))); } + public void addPart(String key, File file, String type, String customFileName) { fileParts.add(new FilePart(key, file, normalizeContentType(type), customFileName)); } @@ -141,8 +137,6 @@ public void addPart(String key, String streamName, InputStream inputStream, Stri out.write(CR_LF); out.flush(); - - AsyncHttpClient.silentCloseOutputStream(out); } private String normalizeContentType(String type) { @@ -167,67 +161,11 @@ private byte[] createContentDisposition(String key, String fileName) { "; filename=\"" + fileName + "\"" + STR_CR_LF).getBytes(); } - private void updateProgress(int count) { + private void updateProgress(long count) { bytesWritten += count; progressHandler.sendProgressMessage(bytesWritten, totalSize); } - private class FilePart { - public File file; - public byte[] header; - - public FilePart(String key, File file, String type, String customFileName) { - header = createHeader(key, TextUtils.isEmpty(customFileName) ? file.getName() : customFileName, type); - this.file = file; - } - - public FilePart(String key, File file, String type) { - header = createHeader(key, file.getName(), type); - this.file = file; - } - - private byte[] createHeader(String key, String filename, String type) { - ByteArrayOutputStream headerStream = new ByteArrayOutputStream(); - try { - headerStream.write(boundaryLine); - - // Headers - headerStream.write(createContentDisposition(key, filename)); - headerStream.write(createContentType(type)); - headerStream.write(TRANSFER_ENCODING_BINARY); - headerStream.write(CR_LF); - } catch (IOException e) { - // Can't happen on ByteArrayOutputStream - Log.e(LOG_TAG, "createHeader ByteArrayOutputStream exception", e); - } - return headerStream.toByteArray(); - } - - public long getTotalLength() { - long streamLength = file.length() + CR_LF.length; - return header.length + streamLength; - } - - public void writeTo(OutputStream out) throws IOException { - out.write(header); - updateProgress(header.length); - - FileInputStream inputStream = new FileInputStream(file); - final byte[] tmp = new byte[4096]; - int bytesRead; - while ((bytesRead = inputStream.read(tmp)) != -1) { - out.write(tmp, 0, bytesRead); - updateProgress(bytesRead); - } - out.write(CR_LF); - updateProgress(CR_LF.length); - out.flush(); - AsyncHttpClient.silentCloseInputStream(inputStream); - } - } - - // The following methods are from the HttpEntity interface - @Override public long getContentLength() { long contentLen = out.size(); @@ -242,6 +180,8 @@ public long getContentLength() { return contentLen; } + // The following methods are from the HttpEntity interface + @Override public Header getContentType() { return new BasicHeader( @@ -300,4 +240,58 @@ public InputStream getContent() throws IOException, UnsupportedOperationExceptio throw new UnsupportedOperationException( "getContent() is not supported. Use writeTo() instead."); } -} \ No newline at end of file + + private class FilePart { + public final File file; + public final byte[] header; + + public FilePart(String key, File file, String type, String customFileName) { + header = createHeader(key, TextUtils.isEmpty(customFileName) ? file.getName() : customFileName, type); + this.file = file; + } + + public FilePart(String key, File file, String type) { + header = createHeader(key, file.getName(), type); + this.file = file; + } + + private byte[] createHeader(String key, String filename, String type) { + ByteArrayOutputStream headerStream = new ByteArrayOutputStream(); + try { + headerStream.write(boundaryLine); + + // Headers + headerStream.write(createContentDisposition(key, filename)); + headerStream.write(createContentType(type)); + headerStream.write(TRANSFER_ENCODING_BINARY); + headerStream.write(CR_LF); + } catch (IOException e) { + // Can't happen on ByteArrayOutputStream + AsyncHttpClient.log.e(LOG_TAG, "createHeader ByteArrayOutputStream exception", e); + } + return headerStream.toByteArray(); + } + + public long getTotalLength() { + long streamLength = file.length() + CR_LF.length; + return header.length + streamLength; + } + + public void writeTo(OutputStream out) throws IOException { + out.write(header); + updateProgress(header.length); + + FileInputStream inputStream = new FileInputStream(file); + final byte[] tmp = new byte[4096]; + int bytesRead; + while ((bytesRead = inputStream.read(tmp)) != -1) { + out.write(tmp, 0, bytesRead); + updateProgress(bytesRead); + } + out.write(CR_LF); + updateProgress(CR_LF.length); + out.flush(); + AsyncHttpClient.silentCloseInputStream(inputStream); + } + } +} diff --git a/library/src/main/java/com/loopj/android/http/SyncHttpClient.java b/library/src/main/java/com/loopj/android/http/SyncHttpClient.java index 9626a71cb..dd7d8f532 100755 --- a/library/src/main/java/com/loopj/android/http/SyncHttpClient.java +++ b/library/src/main/java/com/loopj/android/http/SyncHttpClient.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Copyright (c) 2011 James Smith - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -19,10 +19,10 @@ import android.content.Context; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.conn.scheme.SchemeRegistry; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.protocol.HttpContext; +import cz.msebera.android.httpclient.client.methods.HttpUriRequest; +import cz.msebera.android.httpclient.conn.scheme.SchemeRegistry; +import cz.msebera.android.httpclient.impl.client.DefaultHttpClient; +import cz.msebera.android.httpclient.protocol.HttpContext; /** * Processes http requests in synchronous mode, so your caller thread will be blocked on each diff --git a/library/src/main/java/com/loopj/android/http/TextHttpResponseHandler.java b/library/src/main/java/com/loopj/android/http/TextHttpResponseHandler.java index 71f49459d..6b8aa67ed 100755 --- a/library/src/main/java/com/loopj/android/http/TextHttpResponseHandler.java +++ b/library/src/main/java/com/loopj/android/http/TextHttpResponseHandler.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Copyright (c) 2011 James Smith - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -18,21 +18,19 @@ package com.loopj.android.http; -import android.util.Log; - -import org.apache.http.Header; - import java.io.UnsupportedEncodingException; +import cz.msebera.android.httpclient.Header; + /** * Used to intercept and handle the responses from requests made using {@link AsyncHttpClient}. The - * {@link #onSuccess(int, org.apache.http.Header[], String)} method is designed to be anonymously + * {@link #onSuccess(int, cz.msebera.android.httpclient.Header[], String)} method is designed to be anonymously * overridden with your own response handling code.

 

Additionally, you can override the - * {@link #onFailure(int, org.apache.http.Header[], String, Throwable)}, {@link #onStart()}, and + * {@link #onFailure(int, cz.msebera.android.httpclient.Header[], String, Throwable)}, {@link #onStart()}, and * {@link #onFinish()} methods as required.

 

For example:

 

*
  * AsyncHttpClient client = new AsyncHttpClient();
- * client.get("http://www.google.com/", new TextHttpResponseHandler() {
+ * client.get("https://www.google.com/", new TextHttpResponseHandler() {
  *     @Override
  *     public void onStart() {
  *         // Initiated the request
@@ -57,7 +55,7 @@
  */
 public abstract class TextHttpResponseHandler extends AsyncHttpResponseHandler {
 
-    private static final String LOG_TAG = "TextHttpResponseHandler";
+    private static final String LOG_TAG = "TextHttpRH";
 
     /**
      * Creates new instance with default UTF-8 encoding
@@ -76,6 +74,26 @@ public TextHttpResponseHandler(String encoding) {
         setCharset(encoding);
     }
 
+    /**
+     * Attempts to encode response bytes as string of set encoding
+     *
+     * @param charset     charset to create string with
+     * @param stringBytes response bytes
+     * @return String of set encoding or null
+     */
+    public static String getResponseString(byte[] stringBytes, String charset) {
+        try {
+            String toReturn = (stringBytes == null) ? null : new String(stringBytes, charset);
+            if (toReturn != null && toReturn.startsWith(UTF8_BOM)) {
+                return toReturn.substring(1);
+            }
+            return toReturn;
+        } catch (UnsupportedEncodingException e) {
+            AsyncHttpClient.log.e(LOG_TAG, "Encoding response into string failed", e);
+            return null;
+        }
+    }
+
     /**
      * Called when request fails
      *
@@ -104,24 +122,4 @@ public void onSuccess(int statusCode, Header[] headers, byte[] responseBytes) {
     public void onFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
         onFailure(statusCode, headers, getResponseString(responseBytes, getCharset()), throwable);
     }
-
-    /**
-     * Attempts to encode response bytes as string of set encoding
-     *
-     * @param charset     charset to create string with
-     * @param stringBytes response bytes
-     * @return String of set encoding or null
-     */
-    public static String getResponseString(byte[] stringBytes, String charset) {
-        try {
-            String toReturn = (stringBytes == null) ? null : new String(stringBytes, charset);
-            if (toReturn != null && toReturn.startsWith(UTF8_BOM)) {
-                return toReturn.substring(1);
-            }
-            return toReturn;
-        } catch (UnsupportedEncodingException e) {
-            Log.e(LOG_TAG, "Encoding response into string failed", e);
-            return null;
-        }
-    }
 }
diff --git a/library/src/main/java/com/loopj/android/http/AssertUtils.java b/library/src/main/java/com/loopj/android/http/TokenCredentials.java
similarity index 51%
rename from library/src/main/java/com/loopj/android/http/AssertUtils.java
rename to library/src/main/java/com/loopj/android/http/TokenCredentials.java
index ddd7c6e4a..aa361d395 100644
--- a/library/src/main/java/com/loopj/android/http/AssertUtils.java
+++ b/library/src/main/java/com/loopj/android/http/TokenCredentials.java
@@ -1,13 +1,13 @@
 /*
     Android Asynchronous Http Client
     Copyright (c) 2011 James Smith 
-    http://loopj.com
+    https://github.com/android-async-http/android-async-http
 
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
 
-        http://www.apache.org/licenses/LICENSE-2.0
+        https://www.apache.org/licenses/LICENSE-2.0
 
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
@@ -15,20 +15,28 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 */
-
 package com.loopj.android.http;
 
-/**
- * Internal class, used to make some asserts, throw AssertError if asserts fail.
- */
-class AssertUtils {
+import java.security.Principal;
+
+import cz.msebera.android.httpclient.auth.BasicUserPrincipal;
+import cz.msebera.android.httpclient.auth.Credentials;
+
+public class TokenCredentials implements Credentials {
+    private Principal userPrincipal;
 
-    private AssertUtils() {
+    public TokenCredentials(String token) {
+        this.userPrincipal = new BasicUserPrincipal(token);
     }
 
-    public static void asserts(final boolean expression, final String failedMessage) {
-        if (!expression) {
-            throw new AssertionError(failedMessage);
-        }
+    @Override
+    public Principal getUserPrincipal() {
+        return userPrincipal;
     }
+
+    @Override
+    public String getPassword() {
+        return null;
+    }
+
 }
diff --git a/library/src/main/java/com/loopj/android/http/Utils.java b/library/src/main/java/com/loopj/android/http/Utils.java
new file mode 100644
index 000000000..3f56df33f
--- /dev/null
+++ b/library/src/main/java/com/loopj/android/http/Utils.java
@@ -0,0 +1,56 @@
+/*
+    Android Asynchronous Http Client
+    Copyright (c) 2011 James Smith 
+    https://github.com/android-async-http/android-async-http
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        https://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
+package com.loopj.android.http;
+
+/**
+ * Provides general assert utils, which are stripped by Android SDK on
+ * compile/runtime, to work on release builds
+ */
+class Utils {
+
+    private Utils() {
+    }
+
+    /**
+     * Will throw AssertionError, if expression is not true
+     *
+     * @param expression    result of your asserted condition
+     * @param failedMessage message to be included in error log
+     * @throws java.lang.AssertionError
+     */
+    public static void asserts(final boolean expression, final String failedMessage) {
+        if (!expression) {
+            throw new AssertionError(failedMessage);
+        }
+    }
+
+    /**
+     * Will throw IllegalArgumentException if provided object is null on runtime
+     *
+     * @param argument object that should be asserted as not null
+     * @param name     name of the object asserted
+     * @throws java.lang.IllegalArgumentException
+     */
+    public static  T notNull(final T argument, final String name) {
+        if (argument == null) {
+            throw new IllegalArgumentException(name + " should not be null!");
+        }
+        return argument;
+    }
+}
diff --git a/library/src/main/java/com/loopj/android/http/package-info.java b/library/src/main/java/com/loopj/android/http/package-info.java
index 40174b195..1e99633ca 100644
--- a/library/src/main/java/com/loopj/android/http/package-info.java
+++ b/library/src/main/java/com/loopj/android/http/package-info.java
@@ -1,13 +1,13 @@
 /*
     Android Asynchronous Http Client
     Copyright (c) 2011 James Smith 
-    http://loopj.com
+    https://github.com/android-async-http/android-async-http
 
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
 
-        http://www.apache.org/licenses/LICENSE-2.0
+        https://www.apache.org/licenses/LICENSE-2.0
 
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/maven_push.gradle b/maven_push.gradle
index 27fff0d8f..b56473b28 100755
--- a/maven_push.gradle
+++ b/maven_push.gradle
@@ -5,7 +5,7 @@
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *     https://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -87,6 +87,20 @@ afterEvaluate { project ->
         }
     }
 
+    task installArchives(type: Upload) {
+        description "Installs the artifacts to the local Maven repository."
+        configuration = configurations['archives']
+        repositories {
+            mavenDeployer {
+                pom.groupId = GROUP
+                pom.artifactId = POM_ARTIFACT_ID
+                pom.version = VERSION_NAME
+
+                repository url: "file://${System.properties['user.home']}/.m2/repository"
+            }
+        }
+    }
+
     signing {
         required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") }
         sign configurations.archives
diff --git a/releases/android-async-http-1.2.0.jar b/releases/android-async-http-1.2.0.jar
old mode 100755
new mode 100644
diff --git a/releases/android-async-http-1.2.1.jar b/releases/android-async-http-1.2.1.jar
old mode 100755
new mode 100644
diff --git a/releases/android-async-http-1.3.0.jar b/releases/android-async-http-1.3.0.jar
old mode 100755
new mode 100644
diff --git a/releases/android-async-http-1.3.1.jar b/releases/android-async-http-1.3.1.jar
old mode 100755
new mode 100644
diff --git a/releases/android-async-http-1.3.2.jar b/releases/android-async-http-1.3.2.jar
old mode 100755
new mode 100644
diff --git a/releases/android-async-http-1.4.0.jar b/releases/android-async-http-1.4.0.jar
old mode 100755
new mode 100644
diff --git a/releases/android-async-http-1.4.1.jar b/releases/android-async-http-1.4.1.jar
old mode 100755
new mode 100644
diff --git a/releases/android-async-http-1.4.2.jar b/releases/android-async-http-1.4.2.jar
old mode 100755
new mode 100644
diff --git a/releases/android-async-http-1.4.3.jar b/releases/android-async-http-1.4.3.jar
old mode 100755
new mode 100644
diff --git a/releases/android-async-http-1.4.4.jar b/releases/android-async-http-1.4.4.jar
old mode 100755
new mode 100644
diff --git a/releases/android-async-http-1.4.5.jar b/releases/android-async-http-1.4.5.jar
old mode 100755
new mode 100644
diff --git a/releases/android-async-http-1.4.6.jar b/releases/android-async-http-1.4.6.jar
new file mode 100644
index 000000000..70391cb95
Binary files /dev/null and b/releases/android-async-http-1.4.6.jar differ
diff --git a/releases/android-async-http-1.4.7.jar b/releases/android-async-http-1.4.7.jar
new file mode 100644
index 000000000..d9e203e5f
Binary files /dev/null and b/releases/android-async-http-1.4.7.jar differ
diff --git a/releases/android-async-http-1.4.8.jar b/releases/android-async-http-1.4.8.jar
new file mode 100644
index 000000000..74664cca1
Binary files /dev/null and b/releases/android-async-http-1.4.8.jar differ
diff --git a/sample/build.gradle b/sample/build.gradle
index 3f4166a7c..a964a667f 100755
--- a/sample/build.gradle
+++ b/sample/build.gradle
@@ -1,11 +1,3 @@
-buildscript {
-    repositories {
-        mavenCentral()
-    }
-    dependencies {
-        classpath 'com.android.tools.build:gradle:0.12.2'
-    }
-}
 apply plugin: 'com.android.application'
 
 repositories {
@@ -16,17 +8,18 @@ repositories {
 }
 
 android {
-    compileSdkVersion 19
-    buildToolsVersion '19.1'
+    compileSdkVersion 28
 
     defaultConfig {
-        minSdkVersion 3
-        targetSdkVersion 19
+        minSdkVersion 9
+        targetSdkVersion 28
     }
 
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
+    flavorDimensions "version"
+    productFlavors {
+        standard {
+            dimension "version"
+        }
     }
 
     lintOptions {
@@ -34,7 +27,7 @@ android {
         warningsAsErrors true
         quiet false
         showAll true
-	disable 'OldTargetApi'
+        disable 'OldTargetApi', 'UnusedAttribute', 'LongLogTag', 'TrustAllX509TrustManager', 'GoogleAppIndexingWarning', 'Autofill', 'LabelFor', 'SetTextI18n'
     }
 
     packagingOptions {
@@ -45,6 +38,6 @@ android {
 }
 
 dependencies {
-    compile 'com.fasterxml.jackson.core:jackson-databind:2.2.3'
-    compile project(':library')
+    implementation 'com.fasterxml.jackson.core:jackson-databind:2.11.0'
+    implementation project(':android-async-http')
 }
diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml
index c3ebb2f1a..3f4f13d4f 100755
--- a/sample/src/main/AndroidManifest.xml
+++ b/sample/src/main/AndroidManifest.xml
@@ -1,51 +1,63 @@
 
 
 
+    package="com.loopj.android.http.sample"
+    android:versionCode="9"
+    android:versionName="1.4.11">
 
-    
+    
 
     
+
         
             
-                
-                
+                
+                
             
         
-        
-        
-        
-        
-        
-        
-        
-        
-        
-        
-        
-        
-        
-        
-        
-        
-        
-        
-        
-        
-        
-        
-        
-        
-        
-        
-        
-
-        
+
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+
+        
+
     
 
 
diff --git a/sample/src/main/java/com/loopj/android/http/sample/AsyncBackgroundThreadSample.java b/sample/src/main/java/com/loopj/android/http/sample/AsyncBackgroundThreadSample.java
index 15c818e84..3af0dbea7 100755
--- a/sample/src/main/java/com/loopj/android/http/sample/AsyncBackgroundThreadSample.java
+++ b/sample/src/main/java/com/loopj/android/http/sample/AsyncBackgroundThreadSample.java
@@ -1,13 +1,13 @@
 /*
     Android Asynchronous Http Client Sample
     Copyright (c) 2014 Marek Sebera 
-    http://loopj.com
+    https://github.com/android-async-http/android-async-http
 
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
 
-        http://www.apache.org/licenses/LICENSE-2.0
+        https://www.apache.org/licenses/LICENSE-2.0
 
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
@@ -28,23 +28,23 @@
 import com.loopj.android.http.RequestHandle;
 import com.loopj.android.http.ResponseHandlerInterface;
 
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
-
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.FutureTask;
 import java.util.concurrent.TimeUnit;
+import java.util.Locale;
+
+import cz.msebera.android.httpclient.Header;
+import cz.msebera.android.httpclient.HttpEntity;
 
 public class AsyncBackgroundThreadSample extends SampleParentActivity {
     private static final String LOG_TAG = "AsyncBackgroundThreadSample";
 
-    private ExecutorService executor = Executors.newSingleThreadExecutor();
+    private final ExecutorService executor = Executors.newSingleThreadExecutor();
 
     @Override
-    public void onStop()
-    {
+    public void onStop() {
         super.onStop();
     }
 
@@ -110,7 +110,7 @@ public void onStart() {
 
                     @Override
                     public void onSuccess(int statusCode, Header[] headers, byte[] response) {
-                        Log.d(LOG_TAG, String.format("onSuccess executing on main thread : %B", Looper.myLooper() == Looper.getMainLooper()));
+                        Log.d(LOG_TAG, String.format(Locale.US, "onSuccess executing on main thread : %B", Looper.myLooper() == Looper.getMainLooper()));
                         debugHeaders(LOG_TAG, headers);
                         debugStatusCode(LOG_TAG, statusCode);
                         debugResponse(LOG_TAG, new String(response));
@@ -118,7 +118,7 @@ public void onSuccess(int statusCode, Header[] headers, byte[] response) {
 
                     @Override
                     public void onFailure(int statusCode, Header[] headers, byte[] errorResponse, Throwable e) {
-                        Log.d(LOG_TAG, String.format("onFailure executing on main thread : %B", Looper.myLooper() == Looper.getMainLooper()));
+                        Log.d(LOG_TAG, String.format(Locale.US, "onFailure executing on main thread : %B", Looper.myLooper() == Looper.getMainLooper()));
                         debugHeaders(LOG_TAG, headers);
                         debugStatusCode(LOG_TAG, statusCode);
                         debugThrowable(LOG_TAG, e);
@@ -130,7 +130,7 @@ public void onFailure(int statusCode, Header[] headers, byte[] errorResponse, Th
                     @Override
                     public void onRetry(int retryNo) {
                         Toast.makeText(AsyncBackgroundThreadSample.this,
-                                String.format("Request is retried, retry no. %d", retryNo),
+                                String.format(Locale.US, "Request is retried, retry no. %d", retryNo),
                                 Toast.LENGTH_SHORT)
                                 .show();
                     }
diff --git a/sample/src/main/java/com/loopj/android/http/sample/BinarySample.java b/sample/src/main/java/com/loopj/android/http/sample/BinarySample.java
index dc3eea3d7..73d47a5ad 100755
--- a/sample/src/main/java/com/loopj/android/http/sample/BinarySample.java
+++ b/sample/src/main/java/com/loopj/android/http/sample/BinarySample.java
@@ -1,13 +1,13 @@
 /*
     Android Asynchronous Http Client Sample
     Copyright (c) 2014 Marek Sebera 
-    http://loopj.com
+    https://github.com/android-async-http/android-async-http
 
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
 
-        http://www.apache.org/licenses/LICENSE-2.0
+        https://www.apache.org/licenses/LICENSE-2.0
 
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
@@ -23,8 +23,8 @@
 import com.loopj.android.http.RequestHandle;
 import com.loopj.android.http.ResponseHandlerInterface;
 
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
+import cz.msebera.android.httpclient.Header;
+import cz.msebera.android.httpclient.HttpEntity;
 
 public class BinarySample extends SampleParentActivity {
     private static final String LOG_TAG = "BinarySample";
diff --git a/sample/src/main/java/com/loopj/android/http/sample/CancelAllRequestsSample.java b/sample/src/main/java/com/loopj/android/http/sample/CancelAllRequestsSample.java
index 84d23b5f9..7348e26e4 100644
--- a/sample/src/main/java/com/loopj/android/http/sample/CancelAllRequestsSample.java
+++ b/sample/src/main/java/com/loopj/android/http/sample/CancelAllRequestsSample.java
@@ -1,13 +1,13 @@
 /*
     Android Asynchronous Http Client Sample
     Copyright (c) 2014 Marek Sebera 
-    http://loopj.com
+    https://github.com/android-async-http/android-async-http
 
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
 
-        http://www.apache.org/licenses/LICENSE-2.0
+        https://www.apache.org/licenses/LICENSE-2.0
 
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/sample/src/main/java/com/loopj/android/http/sample/CancelRequestByTagSample.java b/sample/src/main/java/com/loopj/android/http/sample/CancelRequestByTagSample.java
new file mode 100644
index 000000000..38f1133bd
--- /dev/null
+++ b/sample/src/main/java/com/loopj/android/http/sample/CancelRequestByTagSample.java
@@ -0,0 +1,84 @@
+/*
+    Android Asynchronous Http Client Sample
+    Copyright (c) 2014 Marek Sebera 
+    https://github.com/android-async-http/android-async-http
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        https://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
+package com.loopj.android.http.sample;
+
+import android.util.Log;
+
+import com.loopj.android.http.AsyncHttpClient;
+import com.loopj.android.http.AsyncHttpResponseHandler;
+import com.loopj.android.http.RequestHandle;
+import com.loopj.android.http.ResponseHandlerInterface;
+
+import cz.msebera.android.httpclient.Header;
+import cz.msebera.android.httpclient.HttpEntity;
+
+public class CancelRequestByTagSample extends ThreadingTimeoutSample {
+
+    private static final String LOG_TAG = "CancelRequestByTagSample";
+    private static final Integer REQUEST_TAG = 132435;
+
+    @Override
+    public int getSampleTitle() {
+        return R.string.title_cancel_tag;
+    }
+
+    @Override
+    public void onCancelButtonPressed() {
+        Log.d(LOG_TAG, "Canceling requests by TAG: " + REQUEST_TAG);
+        getAsyncHttpClient().cancelRequestsByTAG(REQUEST_TAG, false);
+    }
+
+    @Override
+    public ResponseHandlerInterface getResponseHandler() {
+        return new AsyncHttpResponseHandler() {
+
+            private final int id = counter++;
+
+            @Override
+            public void onStart() {
+                setStatus(id, "TAG:" + getTag() + ", START");
+            }
+
+            @Override
+            public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
+                setStatus(id, "SUCCESS");
+            }
+
+            @Override
+            public void onFinish() {
+                setStatus(id, "FINISH");
+            }
+
+            @Override
+            public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
+                setStatus(id, "FAILURE");
+            }
+
+            @Override
+            public void onCancel() {
+                setStatus(id, "CANCEL");
+            }
+        };
+    }
+
+    @Override
+    public RequestHandle executeSample(AsyncHttpClient client, String URL, Header[] headers, HttpEntity entity, ResponseHandlerInterface responseHandler) {
+        return client.get(this, URL, headers, null, responseHandler).setTag(REQUEST_TAG);
+    }
+}
diff --git a/sample/src/main/java/com/loopj/android/http/sample/CancelRequestHandleSample.java b/sample/src/main/java/com/loopj/android/http/sample/CancelRequestHandleSample.java
index 7db7ba727..e41a1d1be 100644
--- a/sample/src/main/java/com/loopj/android/http/sample/CancelRequestHandleSample.java
+++ b/sample/src/main/java/com/loopj/android/http/sample/CancelRequestHandleSample.java
@@ -1,13 +1,13 @@
 /*
     Android Asynchronous Http Client Sample
     Copyright (c) 2014 Marek Sebera 
-    http://loopj.com
+    https://github.com/android-async-http/android-async-http
 
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
 
-        http://www.apache.org/licenses/LICENSE-2.0
+        https://www.apache.org/licenses/LICENSE-2.0
 
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
@@ -20,11 +20,13 @@
 
 import android.util.Log;
 
+import java.util.Locale;
+
 import com.loopj.android.http.RequestHandle;
 
 public class CancelRequestHandleSample extends ThreadingTimeoutSample {
 
-    private static final String LOG_TAG = "ThreadingTimeoutSample";
+    private static final String LOG_TAG = "CancelRequestHandleSample";
 
     @Override
     public int getSampleTitle() {
@@ -33,14 +35,14 @@ public int getSampleTitle() {
 
     @Override
     public void onCancelButtonPressed() {
-        Log.d(LOG_TAG, String.format("Number of handles found: %d", getRequestHandles().size()));
+        Log.d(LOG_TAG, String.format(Locale.US, "Number of handles found: %d", getRequestHandles().size()));
         int counter = 0;
         for (RequestHandle handle : getRequestHandles()) {
             if (!handle.isCancelled() && !handle.isFinished()) {
-                Log.d(LOG_TAG, String.format("Cancelling handle %d", counter));
-                Log.d(LOG_TAG, String.format("Handle %d cancel", counter) + (handle.cancel(true) ? " succeeded" : " failed"));
+                Log.d(LOG_TAG, String.format(Locale.US, "Cancelling handle %d", counter));
+                Log.d(LOG_TAG, String.format(Locale.US, "Handle %d cancel", counter) + (handle.cancel(true) ? " succeeded" : " failed"));
             } else {
-                Log.d(LOG_TAG, String.format("Handle %d already non-cancellable", counter));
+                Log.d(LOG_TAG, String.format(Locale.US, "Handle %d already non-cancellable", counter));
             }
             counter++;
         }
diff --git a/sample/src/main/java/com/loopj/android/http/sample/ContentTypeForHttpEntitySample.java b/sample/src/main/java/com/loopj/android/http/sample/ContentTypeForHttpEntitySample.java
index 111b91be6..aa7183c6a 100644
--- a/sample/src/main/java/com/loopj/android/http/sample/ContentTypeForHttpEntitySample.java
+++ b/sample/src/main/java/com/loopj/android/http/sample/ContentTypeForHttpEntitySample.java
@@ -8,12 +8,12 @@
 import com.loopj.android.http.ResponseHandlerInterface;
 import com.loopj.android.http.TextHttpResponseHandler;
 
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
-
 import java.io.File;
 import java.io.IOException;
 
+import cz.msebera.android.httpclient.Header;
+import cz.msebera.android.httpclient.HttpEntity;
+
 public class ContentTypeForHttpEntitySample extends SampleParentActivity {
     private static final String LOG_TAG = "ContentTypeForHttpEntitySample";
 
diff --git a/sample/src/main/java/com/loopj/android/http/sample/CustomCASample.java b/sample/src/main/java/com/loopj/android/http/sample/CustomCASample.java
index 621c7f8c9..2a3798ca2 100644
--- a/sample/src/main/java/com/loopj/android/http/sample/CustomCASample.java
+++ b/sample/src/main/java/com/loopj/android/http/sample/CustomCASample.java
@@ -1,13 +1,13 @@
 /*
     Android Asynchronous Http Client Sample
     Copyright (c) 2014 Marek Sebera 
-    http://loopj.com
+    https://github.com/android-async-http/android-async-http
 
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
 
-        http://www.apache.org/licenses/LICENSE-2.0
+        https://www.apache.org/licenses/LICENSE-2.0
 
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
@@ -23,9 +23,9 @@
 import android.content.res.Resources;
 import android.os.Bundle;
 import android.util.Log;
+
 import com.fasterxml.jackson.core.JsonFactory;
 import com.fasterxml.jackson.databind.ObjectMapper;
-
 import com.loopj.android.http.AsyncHttpClient;
 import com.loopj.android.http.BaseJsonHttpResponseHandler;
 import com.loopj.android.http.RequestHandle;
@@ -33,9 +33,6 @@
 import com.loopj.android.http.sample.util.SampleJSON;
 import com.loopj.android.http.sample.util.SecureSocketFactory;
 
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
-
 import java.io.IOException;
 import java.io.InputStream;
 import java.security.KeyManagementException;
@@ -45,6 +42,9 @@
 import java.security.UnrecoverableKeyException;
 import java.security.cert.CertificateException;
 
+import cz.msebera.android.httpclient.Header;
+import cz.msebera.android.httpclient.HttpEntity;
+
 /**
  * This sample demonstrates the implementation of self-signed CA's and connection to servers with
  * such certificates. Be sure to read 'res/raw/custom_ca.txt' for how-to instructions on how to
@@ -57,7 +57,7 @@ public class CustomCASample extends SampleParentActivity {
     private static final String LOG_TAG = "CustomCASample";
 
     // This is A TEST URL for use with AsyncHttpClient LIBRARY ONLY!
-    // It is provided courtesy of Fineswap (http://fineswap.com) and must never
+    // It is provided courtesy of Fineswap (https://fineswap.com) and must never
     // be used in ANY program except when running this sample (CustomCASample).
     private static final String SERVER_TEST_URL = "https://api.fineswap.io/ahc";
 
@@ -190,12 +190,12 @@ private void showCustomCAHelp() {
         builder.setTitle(R.string.title_custom_ca);
         builder.setMessage(getReadmeText());
         builder.setNeutralButton(android.R.string.cancel,
-            new DialogInterface.OnClickListener() {
-                @Override
-                public void onClick(DialogInterface dialog, int which) {
-                    dialog.dismiss();
+                new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        dialog.dismiss();
+                    }
                 }
-            }
         );
         builder.show();
     }
diff --git a/sample/src/main/java/com/loopj/android/http/sample/DeleteSample.java b/sample/src/main/java/com/loopj/android/http/sample/DeleteSample.java
index 1a5dc4aa9..69f443496 100755
--- a/sample/src/main/java/com/loopj/android/http/sample/DeleteSample.java
+++ b/sample/src/main/java/com/loopj/android/http/sample/DeleteSample.java
@@ -1,13 +1,13 @@
 /*
     Android Asynchronous Http Client Sample
     Copyright (c) 2014 Marek Sebera 
-    http://loopj.com
+    https://github.com/android-async-http/android-async-http
 
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
 
-        http://www.apache.org/licenses/LICENSE-2.0
+        https://www.apache.org/licenses/LICENSE-2.0
 
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
@@ -23,8 +23,8 @@
 import com.loopj.android.http.RequestHandle;
 import com.loopj.android.http.ResponseHandlerInterface;
 
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
+import cz.msebera.android.httpclient.Header;
+import cz.msebera.android.httpclient.HttpEntity;
 
 public class DeleteSample extends SampleParentActivity {
     private static final String LOG_TAG = "DeleteSample";
diff --git a/sample/src/main/java/com/loopj/android/http/sample/DigestAuthSample.java b/sample/src/main/java/com/loopj/android/http/sample/DigestAuthSample.java
index 14fa5a7f6..e4b64575a 100644
--- a/sample/src/main/java/com/loopj/android/http/sample/DigestAuthSample.java
+++ b/sample/src/main/java/com/loopj/android/http/sample/DigestAuthSample.java
@@ -8,10 +8,10 @@
 import com.loopj.android.http.RequestHandle;
 import com.loopj.android.http.ResponseHandlerInterface;
 
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
-import org.apache.http.auth.AuthScope;
-import org.apache.http.auth.UsernamePasswordCredentials;
+import cz.msebera.android.httpclient.Header;
+import cz.msebera.android.httpclient.HttpEntity;
+import cz.msebera.android.httpclient.auth.AuthScope;
+import cz.msebera.android.httpclient.auth.UsernamePasswordCredentials;
 
 public class DigestAuthSample extends GetSample {
 
diff --git a/sample/src/main/java/com/loopj/android/http/sample/DirectorySample.java b/sample/src/main/java/com/loopj/android/http/sample/DirectorySample.java
new file mode 100755
index 000000000..37b3f986d
--- /dev/null
+++ b/sample/src/main/java/com/loopj/android/http/sample/DirectorySample.java
@@ -0,0 +1,136 @@
+/*
+    Android Asynchronous Http Client Sample
+    Copyright (c) 2014 Marek Sebera 
+    https://github.com/android-async-http/android-async-http
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        https://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
+package com.loopj.android.http.sample;
+
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
+
+import com.loopj.android.http.AsyncHttpClient;
+import com.loopj.android.http.FileAsyncHttpResponseHandler;
+import com.loopj.android.http.RequestHandle;
+import com.loopj.android.http.ResponseHandlerInterface;
+import com.loopj.android.http.sample.util.FileUtil;
+
+import java.io.File;
+
+import java.util.Locale;
+
+import cz.msebera.android.httpclient.Header;
+import cz.msebera.android.httpclient.HttpEntity;
+
+public class DirectorySample extends SampleParentActivity {
+    private static final String LOG_TAG = "DirectorySample";
+    private FileAsyncHttpResponseHandler lastResponseHandler = null;
+    private CheckBox cbAppend, cbRename;
+
+    @Override
+    public int getSampleTitle() {
+        return R.string.title_directory_sample;
+    }
+
+    @Override
+    public boolean isRequestBodyAllowed() {
+        return false;
+    }
+
+    @Override
+    public boolean isRequestHeadersAllowed() {
+        return true;
+    }
+
+    @Override
+    public String getDefaultURL() {
+        return "https://httpbin.org/robots.txt";
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Button deleteTargetFile = new Button(this);
+        deleteTargetFile.setText(R.string.button_delete_target_file);
+        deleteTargetFile.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                clearOutputs();
+                if (lastResponseHandler != null) {
+                    File toBeDeleted = lastResponseHandler.getTargetFile();
+                    debugResponse(LOG_TAG, String.format(Locale.US, "File was deleted? %b", toBeDeleted.delete()));
+                    debugResponse(LOG_TAG, String.format(Locale.US, "Delete file path: %s", toBeDeleted.getAbsolutePath()));
+                } else {
+                    debugThrowable(LOG_TAG, new Error("You have to Run example first"));
+                }
+            }
+        });
+        cbAppend = new CheckBox(this);
+        cbAppend.setText("Constructor \"append\" is true?");
+        cbAppend.setChecked(false);
+        cbRename = new CheckBox(this);
+        cbRename.setText("Constructor \"renameTargetFileIfExists\" is true?");
+        cbRename.setChecked(true);
+        customFieldsLayout.addView(deleteTargetFile);
+        customFieldsLayout.addView(cbAppend);
+        customFieldsLayout.addView(cbRename);
+    }
+
+    @Override
+    public ResponseHandlerInterface getResponseHandler() {
+        lastResponseHandler = new FileAsyncHttpResponseHandler(getCacheDir(), cbAppend.isChecked(), cbRename.isChecked()) {
+            @Override
+            public void onStart() {
+                clearOutputs();
+            }
+
+            @Override
+            public void onSuccess(int statusCode, Header[] headers, File response) {
+                debugHeaders(LOG_TAG, headers);
+                debugStatusCode(LOG_TAG, statusCode);
+                debugFile(response);
+            }
+
+            @Override
+            public void onFailure(int statusCode, Header[] headers, Throwable throwable, File file) {
+                debugHeaders(LOG_TAG, headers);
+                debugStatusCode(LOG_TAG, statusCode);
+                debugThrowable(LOG_TAG, throwable);
+                debugFile(file);
+            }
+
+            private void debugFile(File file) {
+                if (file == null || !file.exists()) {
+                    debugResponse(LOG_TAG, "Response is null");
+                    return;
+                }
+                try {
+                    debugResponse(LOG_TAG, file.getAbsolutePath() + "\r\n\r\n" + FileUtil.getStringFromFile(file));
+                } catch (Throwable t) {
+                    Log.e(LOG_TAG, "Cannot debug file contents", t);
+                }
+            }
+        };
+        return lastResponseHandler;
+    }
+
+    @Override
+    public RequestHandle executeSample(AsyncHttpClient client, String URL, Header[] headers, HttpEntity entity, ResponseHandlerInterface responseHandler) {
+        return client.get(this, URL, headers, null, responseHandler);
+    }
+}
diff --git a/sample/src/main/java/com/loopj/android/http/sample/FileSample.java b/sample/src/main/java/com/loopj/android/http/sample/FileSample.java
index 4d774e60f..c06f9c480 100755
--- a/sample/src/main/java/com/loopj/android/http/sample/FileSample.java
+++ b/sample/src/main/java/com/loopj/android/http/sample/FileSample.java
@@ -1,13 +1,13 @@
 /*
     Android Asynchronous Http Client Sample
     Copyright (c) 2014 Marek Sebera 
-    http://loopj.com
+    https://github.com/android-async-http/android-async-http
 
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
 
-        http://www.apache.org/licenses/LICENSE-2.0
+        https://www.apache.org/licenses/LICENSE-2.0
 
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
@@ -26,11 +26,11 @@
 import com.loopj.android.http.ResponseHandlerInterface;
 import com.loopj.android.http.sample.util.FileUtil;
 
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
-
 import java.io.File;
 
+import cz.msebera.android.httpclient.Header;
+import cz.msebera.android.httpclient.HttpEntity;
+
 public class FileSample extends SampleParentActivity {
     private static final String LOG_TAG = "FileSample";
 
diff --git a/sample/src/main/java/com/loopj/android/http/sample/FilesSample.java b/sample/src/main/java/com/loopj/android/http/sample/FilesSample.java
index fb97a41b0..6eff61066 100644
--- a/sample/src/main/java/com/loopj/android/http/sample/FilesSample.java
+++ b/sample/src/main/java/com/loopj/android/http/sample/FilesSample.java
@@ -7,17 +7,17 @@
 import com.loopj.android.http.RequestParams;
 import com.loopj.android.http.ResponseHandlerInterface;
 
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
-
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.util.Random;
 
+import cz.msebera.android.httpclient.Header;
+import cz.msebera.android.httpclient.HttpEntity;
+
 public class FilesSample extends PostSample {
 
-    public static final String LOG_TAG = "PostFilesSample";
+    public static final String LOG_TAG = "FilesSample";
 
     @Override
     public int getSampleTitle() {
diff --git a/sample/src/main/java/com/loopj/android/http/sample/GetSample.java b/sample/src/main/java/com/loopj/android/http/sample/GetSample.java
index ac11151a4..27b0be0e0 100755
--- a/sample/src/main/java/com/loopj/android/http/sample/GetSample.java
+++ b/sample/src/main/java/com/loopj/android/http/sample/GetSample.java
@@ -1,13 +1,13 @@
 /*
     Android Asynchronous Http Client Sample
     Copyright (c) 2014 Marek Sebera 
-    http://loopj.com
+    https://github.com/android-async-http/android-async-http
 
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
 
-        http://www.apache.org/licenses/LICENSE-2.0
+        https://www.apache.org/licenses/LICENSE-2.0
 
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
@@ -25,8 +25,10 @@
 import com.loopj.android.http.RequestHandle;
 import com.loopj.android.http.ResponseHandlerInterface;
 
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
+import java.util.Locale;
+
+import cz.msebera.android.httpclient.Header;
+import cz.msebera.android.httpclient.HttpEntity;
 
 public class GetSample extends SampleParentActivity {
     private static final String LOG_TAG = "GetSample";
@@ -85,7 +87,7 @@ public void onFailure(int statusCode, Header[] headers, byte[] errorResponse, Th
             @Override
             public void onRetry(int retryNo) {
                 Toast.makeText(GetSample.this,
-                        String.format("Request is retried, retry no. %d", retryNo),
+                        String.format(Locale.US, "Request is retried, retry no. %d", retryNo),
                         Toast.LENGTH_SHORT)
                         .show();
             }
diff --git a/sample/src/main/java/com/loopj/android/http/sample/GzipSample.java b/sample/src/main/java/com/loopj/android/http/sample/GzipSample.java
index 3cf000a67..8da1c9372 100644
--- a/sample/src/main/java/com/loopj/android/http/sample/GzipSample.java
+++ b/sample/src/main/java/com/loopj/android/http/sample/GzipSample.java
@@ -1,13 +1,13 @@
 /*
     Android Asynchronous Http Client Sample
     Copyright (c) 2014 Marek Sebera 
-    http://loopj.com
+    https://github.com/android-async-http/android-async-http
 
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
 
-        http://www.apache.org/licenses/LICENSE-2.0
+        https://www.apache.org/licenses/LICENSE-2.0
 
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/sample/src/main/java/com/loopj/android/http/sample/HeadSample.java b/sample/src/main/java/com/loopj/android/http/sample/HeadSample.java
new file mode 100644
index 000000000..d3a6d0dcb
--- /dev/null
+++ b/sample/src/main/java/com/loopj/android/http/sample/HeadSample.java
@@ -0,0 +1,61 @@
+/*
+    Copyright (c) 2015 Marek Sebera 
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        https://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+package com.loopj.android.http.sample;
+
+import com.loopj.android.http.AsyncHttpClient;
+import com.loopj.android.http.AsyncHttpResponseHandler;
+import com.loopj.android.http.RequestHandle;
+import com.loopj.android.http.ResponseHandlerInterface;
+
+import cz.msebera.android.httpclient.Header;
+import cz.msebera.android.httpclient.HttpEntity;
+
+import java.util.Locale;
+
+public class HeadSample extends FileSample {
+
+    private static final String LOG_TAG = "HeadSample";
+
+    @Override
+    public ResponseHandlerInterface getResponseHandler() {
+        return new AsyncHttpResponseHandler() {
+            @Override
+            public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
+                debugStatusCode(LOG_TAG, statusCode);
+                debugHeaders(LOG_TAG, headers);
+                debugResponse(LOG_TAG, String.format(Locale.US, "Response of size: %d", responseBody == null ? 0 : responseBody.length));
+            }
+
+            @Override
+            public void onProgress(long bytesWritten, long totalSize) {
+                addView(getColoredView(LIGHTRED, String.format(Locale.US, "Progress %d from %d", bytesWritten, totalSize)));
+            }
+
+            @Override
+            public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable throwable) {
+                debugStatusCode(LOG_TAG, statusCode);
+                debugHeaders(LOG_TAG, headers);
+                debugThrowable(LOG_TAG, throwable);
+                debugResponse(LOG_TAG, String.format(Locale.US, "Response of size: %d", responseBody == null ? 0 : responseBody.length));
+            }
+        };
+    }
+
+    @Override
+    public RequestHandle executeSample(AsyncHttpClient client, String URL, Header[] headers, HttpEntity entity, ResponseHandlerInterface responseHandler) {
+        return client.head(this, URL, headers, null, responseHandler);
+    }
+}
diff --git a/sample/src/main/java/com/loopj/android/http/sample/Http401AuthSample.java b/sample/src/main/java/com/loopj/android/http/sample/Http401AuthSample.java
index 16122e0de..93db38c3c 100644
--- a/sample/src/main/java/com/loopj/android/http/sample/Http401AuthSample.java
+++ b/sample/src/main/java/com/loopj/android/http/sample/Http401AuthSample.java
@@ -1,13 +1,13 @@
 /*
     Android Asynchronous Http Client Sample
     Copyright (c) 2014 Marek Sebera 
-    http://loopj.com
+    https://github.com/android-async-http/android-async-http
 
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
 
-        http://www.apache.org/licenses/LICENSE-2.0
+        https://www.apache.org/licenses/LICENSE-2.0
 
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
@@ -36,13 +36,13 @@
 import com.loopj.android.http.ResponseHandlerInterface;
 import com.loopj.android.http.sample.util.SampleJSON;
 
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
-import org.apache.http.message.BasicHeader;
-
 import java.util.List;
 import java.util.Locale;
 
+import cz.msebera.android.httpclient.Header;
+import cz.msebera.android.httpclient.HttpEntity;
+import cz.msebera.android.httpclient.message.BasicHeader;
+
 /**
  * This sample demonstrates how to implement HTTP 401 Basic Authentication.
  *
@@ -50,7 +50,7 @@
  */
 public class Http401AuthSample extends GetSample {
 
-    private static final String LOG_TAG = "Http401Auth";
+    private static final String LOG_TAG = "Http401AuthSample";
     private static final String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate";
     private static final String HEADER_AUTHORIZATION = "Authorization";
     private static final String HEADER_REALM_PREFIX = "realm=";
@@ -188,7 +188,7 @@ public DialogRunnable(String realm) {
             String prefaceText = preface.getText().toString();
 
             // Substitute placeholders, and re-set the value.
-            preface.setText(String.format(prefaceText, SECRET_USERNAME, SECRET_PASSWORD));
+            preface.setText(String.format(Locale.US, prefaceText, SECRET_USERNAME, SECRET_PASSWORD));
         }
 
         @Override
diff --git a/sample/src/main/java/com/loopj/android/http/sample/IntentServiceSample.java b/sample/src/main/java/com/loopj/android/http/sample/IntentServiceSample.java
index 7b7dde623..718529d4a 100644
--- a/sample/src/main/java/com/loopj/android/http/sample/IntentServiceSample.java
+++ b/sample/src/main/java/com/loopj/android/http/sample/IntentServiceSample.java
@@ -11,8 +11,8 @@
 import com.loopj.android.http.sample.services.ExampleIntentService;
 import com.loopj.android.http.sample.util.IntentUtil;
 
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
+import cz.msebera.android.httpclient.Header;
+import cz.msebera.android.httpclient.HttpEntity;
 
 public class IntentServiceSample extends SampleParentActivity {
 
@@ -31,18 +31,18 @@ public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
 
             // switch() doesn't support strings in older JDK.
-            if(ACTION_START.equals(action)) {
+            if (ACTION_START.equals(action)) {
                 clearOutputs();
                 addView(getColoredView(LIGHTBLUE, "Request started"));
-            } else if(ACTION_FINISH.equals(action)) {
+            } else if (ACTION_FINISH.equals(action)) {
                 addView(getColoredView(LIGHTBLUE, "Request finished"));
-            } else if(ACTION_CANCEL.equals(action)) {
+            } else if (ACTION_CANCEL.equals(action)) {
                 addView(getColoredView(LIGHTBLUE, "Request cancelled"));
-            } else if(ACTION_RETRY.equals(action)) {
+            } else if (ACTION_RETRY.equals(action)) {
                 addView(getColoredView(LIGHTBLUE, "Request retried"));
-            } else if(ACTION_FAILURE.equals(action) || ACTION_SUCCESS.equals(action)) {
+            } else if (ACTION_FAILURE.equals(action) || ACTION_SUCCESS.equals(action)) {
                 debugThrowable(LOG_TAG, (Throwable) intent.getSerializableExtra(ExampleIntentService.INTENT_THROWABLE));
-                if(ACTION_SUCCESS.equals(action)) {
+                if (ACTION_SUCCESS.equals(action)) {
                     debugStatusCode(LOG_TAG, intent.getIntExtra(ExampleIntentService.INTENT_STATUS_CODE, 0));
                     debugHeaders(LOG_TAG, IntentUtil.deserializeHeaders(intent.getStringArrayExtra(ExampleIntentService.INTENT_HEADERS)));
                     byte[] returnedBytes = intent.getByteArrayExtra(ExampleIntentService.INTENT_DATA);
diff --git a/sample/src/main/java/com/loopj/android/http/sample/JsonSample.java b/sample/src/main/java/com/loopj/android/http/sample/JsonSample.java
index d9ed5f3ab..59469ea9d 100755
--- a/sample/src/main/java/com/loopj/android/http/sample/JsonSample.java
+++ b/sample/src/main/java/com/loopj/android/http/sample/JsonSample.java
@@ -1,13 +1,13 @@
 /*
     Android Asynchronous Http Client Sample
     Copyright (c) 2014 Marek Sebera 
-    http://loopj.com
+    https://github.com/android-async-http/android-async-http
 
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
 
-        http://www.apache.org/licenses/LICENSE-2.0
+        https://www.apache.org/licenses/LICENSE-2.0
 
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
@@ -26,8 +26,8 @@
 import com.loopj.android.http.ResponseHandlerInterface;
 import com.loopj.android.http.sample.util.SampleJSON;
 
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
+import cz.msebera.android.httpclient.Header;
+import cz.msebera.android.httpclient.HttpEntity;
 
 public class JsonSample extends SampleParentActivity {
 
diff --git a/sample/src/main/java/com/loopj/android/http/sample/JsonStreamerSample.java b/sample/src/main/java/com/loopj/android/http/sample/JsonStreamerSample.java
index cb308af9f..4d4d24d03 100644
--- a/sample/src/main/java/com/loopj/android/http/sample/JsonStreamerSample.java
+++ b/sample/src/main/java/com/loopj/android/http/sample/JsonStreamerSample.java
@@ -1,13 +1,13 @@
 /*
     Android Asynchronous Http Client Sample
     Copyright (c) 2014 Marek Sebera 
-    http://loopj.com
+    https://github.com/android-async-http/android-async-http
 
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
 
-        http://www.apache.org/licenses/LICENSE-2.0
+        https://www.apache.org/licenses/LICENSE-2.0
 
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
@@ -18,6 +18,7 @@
 
 package com.loopj.android.http.sample;
 
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.loopj.android.http.AsyncHttpClient;
@@ -25,17 +26,18 @@
 import com.loopj.android.http.RequestParams;
 import com.loopj.android.http.ResponseHandlerInterface;
 
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
 import org.json.JSONException;
 import org.json.JSONObject;
 
 import java.util.Iterator;
 
+import cz.msebera.android.httpclient.Header;
+import cz.msebera.android.httpclient.HttpEntity;
+
 /**
  * This sample demonstrates how to upload JSON data using streams, resulting
  * in a low-memory footprint even with extremely large data.
- *
+ * 

* Please note: You must prepare a server-side end-point to consume the uploaded * data. This is because the data is uploaded using "application/json" content * type and regular methods, expecting a multi-form content type, will fail to @@ -87,7 +89,7 @@ public boolean isRequestHeadersAllowed() { protected JSONObject getBodyTextAsJSON() { String bodyText = getBodyText(); - if (bodyText != null) { + if (bodyText != null && !TextUtils.isEmpty(bodyText)) { try { return new JSONObject(bodyText); } catch (JSONException e) { diff --git a/sample/src/main/java/com/loopj/android/http/sample/PatchSample.java b/sample/src/main/java/com/loopj/android/http/sample/PatchSample.java new file mode 100644 index 000000000..10926a551 --- /dev/null +++ b/sample/src/main/java/com/loopj/android/http/sample/PatchSample.java @@ -0,0 +1,68 @@ +package com.loopj.android.http.sample; + +import com.loopj.android.http.AsyncHttpClient; +import com.loopj.android.http.AsyncHttpResponseHandler; +import com.loopj.android.http.RequestHandle; +import com.loopj.android.http.ResponseHandlerInterface; + +import cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.HttpEntity; + +public class PatchSample extends SampleParentActivity { + + private static final String LOG_TAG = "PatchSample"; + + @Override + public RequestHandle executeSample(AsyncHttpClient client, String URL, Header[] headers, HttpEntity entity, ResponseHandlerInterface responseHandler) { + return client.patch(this, URL, entity, null, responseHandler); + } + + @Override + public int getSampleTitle() { + return R.string.title_patch_sample; + } + + @Override + public boolean isRequestBodyAllowed() { + return false; + } + + @Override + public boolean isRequestHeadersAllowed() { + return false; + } + + @Override + public String getDefaultURL() { + return PROTOCOL + "httpbin.org/patch"; + } + + @Override + public ResponseHandlerInterface getResponseHandler() { + return new AsyncHttpResponseHandler() { + @Override + public void onStart() { + clearOutputs(); + } + + @Override + public void onSuccess(int statusCode, Header[] headers, byte[] response) { + debugHeaders(LOG_TAG, headers); + debugStatusCode(LOG_TAG, statusCode); + debugResponse(LOG_TAG, new String(response)); + } + + @Override + public void onFailure(int statusCode, Header[] headers, byte[] errorResponse, Throwable e) { + debugHeaders(LOG_TAG, headers); + debugStatusCode(LOG_TAG, statusCode); + debugThrowable(LOG_TAG, e); + if (errorResponse != null) { + debugResponse(LOG_TAG, new String(errorResponse)); + } + } + }; + } + + +} diff --git a/sample/src/main/java/com/loopj/android/http/sample/PersistentCookiesSample.java b/sample/src/main/java/com/loopj/android/http/sample/PersistentCookiesSample.java index d2e734f46..69a72e2fb 100644 --- a/sample/src/main/java/com/loopj/android/http/sample/PersistentCookiesSample.java +++ b/sample/src/main/java/com/loopj/android/http/sample/PersistentCookiesSample.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Sample Copyright (c) 2014 Marek Sebera - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -22,7 +22,6 @@ import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.databind.ObjectMapper; - import com.loopj.android.http.AsyncHttpClient; import com.loopj.android.http.BaseJsonHttpResponseHandler; import com.loopj.android.http.PersistentCookieStore; @@ -30,9 +29,9 @@ import com.loopj.android.http.ResponseHandlerInterface; import com.loopj.android.http.sample.util.SampleJSON; -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.client.CookieStore; +import cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.HttpEntity; +import cz.msebera.android.httpclient.client.CookieStore; public class PersistentCookiesSample extends SampleParentActivity { @@ -72,7 +71,7 @@ public String getDefaultURL() { String url = PROTOCOL + "httpbin.org/cookies"; // If the cookie store is empty, suggest a cookie. - if(cookieStore.getCookies().isEmpty()) { + if (cookieStore.getCookies().isEmpty()) { url += "/set?time=" + System.currentTimeMillis(); } diff --git a/sample/src/main/java/com/loopj/android/http/sample/PostSample.java b/sample/src/main/java/com/loopj/android/http/sample/PostSample.java index 5cd9138da..815961f04 100755 --- a/sample/src/main/java/com/loopj/android/http/sample/PostSample.java +++ b/sample/src/main/java/com/loopj/android/http/sample/PostSample.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Sample Copyright (c) 2014 Marek Sebera - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -23,8 +23,8 @@ import com.loopj.android.http.RequestHandle; import com.loopj.android.http.ResponseHandlerInterface; -import org.apache.http.Header; -import org.apache.http.HttpEntity; +import cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.HttpEntity; public class PostSample extends SampleParentActivity { private static final String LOG_TAG = "PostSample"; diff --git a/sample/src/main/java/com/loopj/android/http/sample/PrePostProcessingSample.java b/sample/src/main/java/com/loopj/android/http/sample/PrePostProcessingSample.java index 3bbf400c7..654dab769 100644 --- a/sample/src/main/java/com/loopj/android/http/sample/PrePostProcessingSample.java +++ b/sample/src/main/java/com/loopj/android/http/sample/PrePostProcessingSample.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Sample Copyright (c) 2014 Marek Sebera - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -21,27 +21,28 @@ import android.content.Context; import android.graphics.Color; import android.util.Log; + import com.loopj.android.http.AsyncHttpClient; import com.loopj.android.http.AsyncHttpRequest; import com.loopj.android.http.AsyncHttpResponseHandler; import com.loopj.android.http.RequestHandle; import com.loopj.android.http.ResponseHandlerInterface; + import java.util.Locale; -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.impl.client.AbstractHttpClient; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.protocol.HttpContext; +import cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.HttpEntity; +import cz.msebera.android.httpclient.HttpResponse; +import cz.msebera.android.httpclient.client.methods.HttpUriRequest; +import cz.msebera.android.httpclient.impl.client.AbstractHttpClient; +import cz.msebera.android.httpclient.impl.client.DefaultHttpClient; +import cz.msebera.android.httpclient.protocol.HttpContext; public class PrePostProcessingSample extends SampleParentActivity { - private static final String LOG_TAG = "PrePostProcessingSample"; - protected static final int LIGHTGREY = Color.parseColor("#E0E0E0"); protected static final int DARKGREY = Color.parseColor("#888888"); + private static final String LOG_TAG = "PrePostProcessingSample"; @Override public RequestHandle executeSample(AsyncHttpClient client, String URL, Header[] headers, HttpEntity entity, ResponseHandlerInterface responseHandler) { @@ -80,13 +81,13 @@ public ResponseHandlerInterface getResponseHandler() { @Override public void onPreProcessResponse(ResponseHandlerInterface instance, HttpResponse response) { debugProcessing(LOG_TAG, "Pre", - "Response is about to be pre-processed", LIGHTGREY); + "Response is about to be pre-processed", LIGHTGREY); } - @Override + @Override public void onPostProcessResponse(ResponseHandlerInterface instance, HttpResponse response) { debugProcessing(LOG_TAG, "Post", - "Response is about to be post-processed", DARKGREY); + "Response is about to be post-processed", DARKGREY); } @Override @@ -119,27 +120,27 @@ protected void debugProcessing(String TAG, String state, String message, final i runOnUiThread(new Runnable() { @Override public void run() { - addView(getColoredView(color, debugMessage)); + addView(getColoredView(color, debugMessage)); } }); } private class PrePostProcessRequest extends AsyncHttpRequest { - public PrePostProcessRequest(AbstractHttpClient client, HttpContext context, HttpUriRequest request, ResponseHandlerInterface responseHandler) { - super(client, context, request, responseHandler); + public PrePostProcessRequest(AbstractHttpClient client, HttpContext httpContext, HttpUriRequest request, ResponseHandlerInterface responseHandler) { + super(client, httpContext, request, responseHandler); } @Override public void onPreProcessRequest(AsyncHttpRequest request) { debugProcessing(LOG_TAG, "Pre", - "Request is about to be pre-processed", LIGHTGREY); + "Request is about to be pre-processed", LIGHTGREY); } @Override public void onPostProcessRequest(AsyncHttpRequest request) { debugProcessing(LOG_TAG, "Post", - "Request is about to be post-processed", DARKGREY); + "Request is about to be post-processed", DARKGREY); } } } diff --git a/sample/src/main/java/com/loopj/android/http/sample/PutSample.java b/sample/src/main/java/com/loopj/android/http/sample/PutSample.java index 1b43c51f7..5cab76591 100755 --- a/sample/src/main/java/com/loopj/android/http/sample/PutSample.java +++ b/sample/src/main/java/com/loopj/android/http/sample/PutSample.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Sample Copyright (c) 2014 Marek Sebera - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -23,8 +23,8 @@ import com.loopj.android.http.RequestHandle; import com.loopj.android.http.ResponseHandlerInterface; -import org.apache.http.Header; -import org.apache.http.HttpEntity; +import cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.HttpEntity; public class PutSample extends SampleParentActivity { private static final String LOG_TAG = "PutSample"; diff --git a/sample/src/main/java/com/loopj/android/http/sample/RangeResponseSample.java b/sample/src/main/java/com/loopj/android/http/sample/RangeResponseSample.java index 3daf055c1..e7f2e3e84 100644 --- a/sample/src/main/java/com/loopj/android/http/sample/RangeResponseSample.java +++ b/sample/src/main/java/com/loopj/android/http/sample/RangeResponseSample.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Sample Copyright (c) 2014 Marek Sebera - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -21,22 +21,27 @@ import android.os.Bundle; import android.util.Log; import android.widget.Toast; + import com.loopj.android.http.AsyncHttpClient; import com.loopj.android.http.RangeFileAsyncHttpResponseHandler; import com.loopj.android.http.RequestHandle; import com.loopj.android.http.ResponseHandlerInterface; + import java.io.File; import java.io.IOException; -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.client.methods.HttpUriRequest; + +import java.util.Locale; + +import cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.HttpEntity; +import cz.msebera.android.httpclient.client.methods.HttpUriRequest; /** * This sample demonstrates use of {@link RangeFileAsyncHttpResponseHandler} to * download a remote file in multiple requests. While this response handler * class handles file storage, it's up to the app itself to request all chunks * of the file. - * + *

* Also demonstrated a method to query the remote file's size prior to sending * the actual GET requests. This ensures that the remote server is actually * capable of supporting the "Range" header, necessary to make this sample work. @@ -71,7 +76,9 @@ protected void onDestroy() { // Remove temporary file. if (file != null) { - file.delete(); + if (!file.delete()) { + Log.e(LOG_TAG, String.format(Locale.US, "Couldn't remove temporary file in path: %s", file.getAbsolutePath())); + } file = null; } } @@ -83,7 +90,7 @@ public boolean isRequestHeadersAllowed() { @Override public String getDefaultURL() { - return "http://upload.wikimedia.org/wikipedia/commons/f/fa/Geysers_on_Mars.jpg"; + return "https://upload.wikimedia.org/wikipedia/commons/f/fa/Geysers_on_Mars.jpg"; } @Override @@ -133,9 +140,9 @@ public void onSuccess(int statusCode, Header[] headers, File file) { // Is the content length known? if (!supportsRange || fileSize < 1) { Toast.makeText( - RangeResponseSample.this, - "Unable to determine remote file's size, or\nremote server doesn't support ranges", - Toast.LENGTH_LONG + RangeResponseSample.this, + "Unable to determine remote file's size, or\nremote server doesn't support ranges", + Toast.LENGTH_LONG ).show(); } } diff --git a/sample/src/main/java/com/loopj/android/http/sample/Redirect302Sample.java b/sample/src/main/java/com/loopj/android/http/sample/Redirect302Sample.java index 0907fac2b..832b6a6eb 100644 --- a/sample/src/main/java/com/loopj/android/http/sample/Redirect302Sample.java +++ b/sample/src/main/java/com/loopj/android/http/sample/Redirect302Sample.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Sample Copyright (c) 2014 Marek Sebera - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -24,32 +24,37 @@ import com.loopj.android.http.AsyncHttpClient; -import org.apache.http.client.HttpClient; -import org.apache.http.impl.client.DefaultHttpClient; +import cz.msebera.android.httpclient.client.HttpClient; +import cz.msebera.android.httpclient.impl.client.DefaultHttpClient; + +import java.util.Locale; public class Redirect302Sample extends GetSample { + private static final int MENU_ENABLE_REDIRECTS = 10; + private static final int MENU_ENABLE_CIRCULAR_REDIRECTS = 11; + private static final int MENU_ENABLE_RELATIVE_REDIRECTs = 12; private boolean enableRedirects = true; private boolean enableRelativeRedirects = true; private boolean enableCircularRedirects = true; @Override public boolean onCreateOptionsMenu(Menu menu) { - menu.add(Menu.NONE, 0, Menu.NONE, "Enable redirects").setCheckable(true); - menu.add(Menu.NONE, 1, Menu.NONE, "Enable relative redirects").setCheckable(true); - menu.add(Menu.NONE, 2, Menu.NONE, "Enable circular redirects").setCheckable(true); + menu.add(Menu.NONE, MENU_ENABLE_REDIRECTS, Menu.NONE, "Enable redirects").setCheckable(true); + menu.add(Menu.NONE, MENU_ENABLE_RELATIVE_REDIRECTs, Menu.NONE, "Enable relative redirects").setCheckable(true); + menu.add(Menu.NONE, MENU_ENABLE_CIRCULAR_REDIRECTS, Menu.NONE, "Enable circular redirects").setCheckable(true); return super.onCreateOptionsMenu(menu); } @Override public boolean onPrepareOptionsMenu(Menu menu) { - MenuItem menuItemEnableRedirects = menu.findItem(0); + MenuItem menuItemEnableRedirects = menu.findItem(MENU_ENABLE_REDIRECTS); if (menuItemEnableRedirects != null) menuItemEnableRedirects.setChecked(enableRedirects); - MenuItem menuItemEnableRelativeRedirects = menu.findItem(1); + MenuItem menuItemEnableRelativeRedirects = menu.findItem(MENU_ENABLE_RELATIVE_REDIRECTs); if (menuItemEnableRelativeRedirects != null) menuItemEnableRelativeRedirects.setChecked(enableRelativeRedirects); - MenuItem menuItemEnableCircularRedirects = menu.findItem(2); + MenuItem menuItemEnableCircularRedirects = menu.findItem(MENU_ENABLE_CIRCULAR_REDIRECTS); if (menuItemEnableCircularRedirects != null) menuItemEnableCircularRedirects.setChecked(enableCircularRedirects); return super.onPrepareOptionsMenu(menu); @@ -59,11 +64,11 @@ public boolean onPrepareOptionsMenu(Menu menu) { public boolean onOptionsItemSelected(MenuItem item) { if (item.isCheckable()) { item.setChecked(!item.isChecked()); - if (item.getItemId() == 0) { + if (item.getItemId() == MENU_ENABLE_REDIRECTS) { enableRedirects = item.isChecked(); - } else if (item.getItemId() == 1) { + } else if (item.getItemId() == MENU_ENABLE_RELATIVE_REDIRECTs) { enableRelativeRedirects = item.isChecked(); - } else if (item.getItemId() == 2) { + } else if (item.getItemId() == MENU_ENABLE_CIRCULAR_REDIRECTS) { enableCircularRedirects = item.isChecked(); } } @@ -86,7 +91,7 @@ public AsyncHttpClient getAsyncHttpClient() { HttpClient client = ahc.getHttpClient(); if (client instanceof DefaultHttpClient) { Toast.makeText(this, - String.format("redirects: %b\nrelative redirects: %b\ncircular redirects: %b", + String.format(Locale.US, "redirects: %b\nrelative redirects: %b\ncircular redirects: %b", enableRedirects, enableRelativeRedirects, enableCircularRedirects), Toast.LENGTH_SHORT ).show(); diff --git a/sample/src/main/java/com/loopj/android/http/sample/RequestParamsDebug.java b/sample/src/main/java/com/loopj/android/http/sample/RequestParamsDebug.java new file mode 100644 index 000000000..a753be40a --- /dev/null +++ b/sample/src/main/java/com/loopj/android/http/sample/RequestParamsDebug.java @@ -0,0 +1,160 @@ +package com.loopj.android.http.sample; + +import android.os.Bundle; +import android.widget.EditText; + +import com.loopj.android.http.AsyncHttpClient; +import com.loopj.android.http.RequestHandle; +import com.loopj.android.http.RequestParams; +import com.loopj.android.http.ResponseHandlerInterface; +import com.loopj.android.http.TextHttpResponseHandler; +import com.loopj.android.http.sample.util.API8Util; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.HttpEntity; + +public class RequestParamsDebug extends SampleParentActivity { + + public static final String LOG_TAG = "RequestParamsDebug"; + private static final String DEMO_RP_CONTENT = "array=java\n" + + "array=C\n" + + "list=blue\n" + + "list=yellow\n" + + "set=music\n" + + "set=art\n" + + "map=first_name\n" + + "map=last_name\n"; + private EditText customParams; + + @Override + public ResponseHandlerInterface getResponseHandler() { + return new TextHttpResponseHandler() { + + @Override + public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) { + debugStatusCode(LOG_TAG, statusCode); + debugHeaders(LOG_TAG, headers); + debugResponse(LOG_TAG, responseString); + debugThrowable(LOG_TAG, throwable); + } + + @Override + public void onSuccess(int statusCode, Header[] headers, String responseString) { + debugStatusCode(LOG_TAG, statusCode); + debugHeaders(LOG_TAG, headers); + debugResponse(LOG_TAG, responseString); + } + }; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + customParams = new EditText(this); + customParams.setLines(8); + customParams.setText(DEMO_RP_CONTENT); + customFieldsLayout.addView(customParams); + } + + @Override + public String getDefaultURL() { + return PROTOCOL + "httpbin.org/get"; + } + + @Override + public boolean isRequestHeadersAllowed() { + return false; + } + + @Override + public boolean isRequestBodyAllowed() { + return false; + } + + @Override + public int getSampleTitle() { + return R.string.title_request_params_debug; + } + + @Override + public RequestHandle executeSample(AsyncHttpClient client, String URL, Header[] headers, HttpEntity entity, ResponseHandlerInterface responseHandler) { + return getAsyncHttpClient().get(this, getDefaultURL(), getRequestParams(), getResponseHandler()); + } + + // TODO: allow parsing multiple values for each type, maybe like "type.key=value" ? + private RequestParams getRequestParams() { + RequestParams rp = new RequestParams(); + // contents of customParams custom field view + String customParamsText = customParams.getText().toString(); + String[] pairs = customParamsText.split("\n"); + // temp content holders + Map> mapOfMaps = new HashMap<>(); + Map> mapOfLists = new HashMap<>(); + Map mapOfArrays = new HashMap<>(); + Map> mapOfSets = new HashMap<>(); + for (String pair : pairs) { + String[] kv = pair.split("="); + if (kv.length != 2) + continue; + String key = kv[0].trim(); + String value = kv[1].trim(); + if ("array".equals(key)) { + String[] values = mapOfArrays.get(key); + if (values == null) { + values = new String[]{value}; + } else { + values = API8Util.copyOfRange(values, 0, values.length + 1); + values[values.length - 1] = value; + } + mapOfArrays.put(key, values); + } else if ("list".equals(key)) { + List values = mapOfLists.get(key); + if (values == null) { + values = new ArrayList<>(); + } + values.add(value); + mapOfLists.put(key, values); + } else if ("set".equals(key)) { + Set values = mapOfSets.get(key); + if (values == null) { + values = new HashSet<>(); + } + values.add(value); + mapOfSets.put(key, values); + } else if ("map".equals(key)) { + Map values = mapOfMaps.get(key); + if (values == null) { + values = new HashMap<>(); + } + values.put(key + values.size(), value); + mapOfMaps.put(key, values); + } + } + // fill in string list + for (Map.Entry> entry : mapOfLists.entrySet()) { + rp.put(entry.getKey(), entry.getValue()); + } + // fill in string array + for (Map.Entry entry : mapOfArrays.entrySet()) { + rp.put(entry.getKey(), entry.getValue()); + } + // fill in string set + for (Map.Entry> entry : mapOfSets.entrySet()) { + rp.put(entry.getKey(), entry.getValue()); + } + // fill in string map + for (Map.Entry> entry : mapOfMaps.entrySet()) { + rp.put(entry.getKey(), entry.getValue()); + } + // debug final URL construction into UI + debugResponse(LOG_TAG, rp.toString()); + return rp; + } +} diff --git a/sample/src/main/java/com/loopj/android/http/sample/ResumeDownloadSample.java b/sample/src/main/java/com/loopj/android/http/sample/ResumeDownloadSample.java index f7d332a91..b81902849 100644 --- a/sample/src/main/java/com/loopj/android/http/sample/ResumeDownloadSample.java +++ b/sample/src/main/java/com/loopj/android/http/sample/ResumeDownloadSample.java @@ -7,16 +7,16 @@ import com.loopj.android.http.RequestHandle; import com.loopj.android.http.ResponseHandlerInterface; -import org.apache.http.Header; -import org.apache.http.HttpEntity; - import java.io.File; import java.io.IOException; +import cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.HttpEntity; + public class ResumeDownloadSample extends SampleParentActivity { - private File downloadTarget; private static final String LOG_TAG = "ResumeDownloadSample"; + private File downloadTarget; private File getDownloadTarget() { try { diff --git a/sample/src/main/java/com/loopj/android/http/sample/RetryRequestSample.java b/sample/src/main/java/com/loopj/android/http/sample/RetryRequestSample.java index f9d66f6c9..64c797c7f 100644 --- a/sample/src/main/java/com/loopj/android/http/sample/RetryRequestSample.java +++ b/sample/src/main/java/com/loopj/android/http/sample/RetryRequestSample.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Sample Copyright (c) 2014 Marek Sebera - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -22,12 +22,13 @@ import android.widget.Toast; import com.loopj.android.http.AsyncHttpClient; + import java.io.IOException; import java.net.SocketTimeoutException; import java.net.UnknownHostException; -import org.apache.http.conn.ConnectTimeoutException; -import org.apache.http.conn.ConnectionPoolTimeoutException; +import cz.msebera.android.httpclient.conn.ConnectTimeoutException; +import cz.msebera.android.httpclient.conn.ConnectionPoolTimeoutException; /** * This sample demonstrates use of @@ -62,12 +63,12 @@ protected void onCreate(Bundle savedInstanceState) { protected void onResume() { super.onResume(); - if(!wasToastShown) { + if (!wasToastShown) { wasToastShown = true; Toast.makeText( - this, - "Exceptions' whitelist and blacklist updated\nSee RetryRequestSample.java for details", - Toast.LENGTH_LONG + this, + "Exceptions' whitelist and blacklist updated\nSee RetryRequestSample.java for details", + Toast.LENGTH_LONG ).show(); } } diff --git a/sample/src/main/java/com/loopj/android/http/sample/SampleInterface.java b/sample/src/main/java/com/loopj/android/http/sample/SampleInterface.java index 225ff6a71..a259833c7 100644 --- a/sample/src/main/java/com/loopj/android/http/sample/SampleInterface.java +++ b/sample/src/main/java/com/loopj/android/http/sample/SampleInterface.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Sample Copyright (c) 2014 Marek Sebera - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -19,18 +19,19 @@ package com.loopj.android.http.sample; import android.content.Context; + import com.loopj.android.http.AsyncHttpClient; import com.loopj.android.http.AsyncHttpRequest; import com.loopj.android.http.RequestHandle; import com.loopj.android.http.ResponseHandlerInterface; -import org.apache.http.Header; -import org.apache.http.HttpEntity; - import java.util.List; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.protocol.HttpContext; + +import cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.HttpEntity; +import cz.msebera.android.httpclient.client.methods.HttpUriRequest; +import cz.msebera.android.httpclient.impl.client.DefaultHttpClient; +import cz.msebera.android.httpclient.protocol.HttpContext; public interface SampleInterface { diff --git a/sample/src/main/java/com/loopj/android/http/sample/SampleParentActivity.java b/sample/src/main/java/com/loopj/android/http/sample/SampleParentActivity.java index 9336bf2ba..165b2baf8 100755 --- a/sample/src/main/java/com/loopj/android/http/sample/SampleParentActivity.java +++ b/sample/src/main/java/com/loopj/android/http/sample/SampleParentActivity.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Sample Copyright (c) 2014 Marek Sebera - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -20,7 +20,9 @@ import android.annotation.TargetApi; import android.app.Activity; +import android.app.AlertDialog; import android.content.Context; +import android.content.DialogInterface; import android.graphics.Color; import android.os.Build; import android.os.Bundle; @@ -39,14 +41,6 @@ import com.loopj.android.http.RequestHandle; import com.loopj.android.http.ResponseHandlerInterface; -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.message.BasicHeader; -import org.apache.http.protocol.HttpContext; - import java.io.PrintWriter; import java.io.StringWriter; import java.io.UnsupportedEncodingException; @@ -55,8 +49,30 @@ import java.util.List; import java.util.Locale; +import cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.HttpEntity; +import cz.msebera.android.httpclient.client.methods.HttpUriRequest; +import cz.msebera.android.httpclient.entity.StringEntity; +import cz.msebera.android.httpclient.impl.client.DefaultHttpClient; +import cz.msebera.android.httpclient.message.BasicHeader; +import cz.msebera.android.httpclient.protocol.HttpContext; + public abstract class SampleParentActivity extends Activity implements SampleInterface { + protected static final String PROTOCOL_HTTP = "http://"; + protected static final String PROTOCOL_HTTPS = "https://"; + protected static final int LIGHTGREEN = Color.parseColor("#00FF66"); + protected static final int LIGHTRED = Color.parseColor("#FF3300"); + protected static final int YELLOW = Color.parseColor("#FFFF00"); + protected static final int LIGHTBLUE = Color.parseColor("#99CCFF"); + private static final String LOG_TAG = "SampleParentActivity"; + private static final int MENU_USE_HTTPS = 0; + private static final int MENU_CLEAR_VIEW = 1; + private static final int MENU_LOGGING_VERBOSITY = 2; + private static final int MENU_ENABLE_LOGGING = 3; + protected static String PROTOCOL = PROTOCOL_HTTPS; + private final List requestHandles = new LinkedList(); + public LinearLayout customFieldsLayout; private AsyncHttpClient asyncHttpClient = new AsyncHttpClient() { @Override @@ -68,24 +84,36 @@ protected AsyncHttpRequest newAsyncHttpRequest(DefaultHttpClient client, HttpCon } }; private EditText urlEditText, headersEditText, bodyEditText; + protected final View.OnClickListener onClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.button_run: + onRunButtonPressed(); + break; + case R.id.button_cancel: + onCancelButtonPressed(); + break; + } + } + }; private LinearLayout responseLayout; - public LinearLayout customFieldsLayout; - private final List requestHandles = new LinkedList(); - private static final String LOG_TAG = "SampleParentActivity"; - - private static final int MENU_USE_HTTPS = 0; - private static final int MENU_CLEAR_VIEW = 1; - private boolean useHttps = true; + private boolean enableLogging = true; - protected static final String PROTOCOL_HTTP = "http://"; - protected static final String PROTOCOL_HTTPS = "https://"; + protected static String throwableToString(Throwable t) { + if (t == null) + return null; - protected static String PROTOCOL = PROTOCOL_HTTPS; - protected static final int LIGHTGREEN = Color.parseColor("#00FF66"); - protected static final int LIGHTRED = Color.parseColor("#FF3300"); - protected static final int YELLOW = Color.parseColor("#FFFF00"); - protected static final int LIGHTBLUE = Color.parseColor("#99CCFF"); + StringWriter sw = new StringWriter(); + t.printStackTrace(new PrintWriter(sw)); + return sw.toString(); + } + + public static int getContrastColor(int color) { + double y = (299 * Color.red(color) + 587 * Color.green(color) + 114 * Color.blue(color)) / 1000; + return y >= 128 ? Color.BLACK : Color.WHITE; + } @Override protected void onCreate(Bundle savedInstanceState) { @@ -128,6 +156,10 @@ public boolean onPrepareOptionsMenu(Menu menu) { if (useHttpsMenuItem != null) { useHttpsMenuItem.setChecked(useHttps); } + MenuItem enableLoggingMenuItem = menu.findItem(MENU_ENABLE_LOGGING); + if (enableLoggingMenuItem != null) { + enableLoggingMenuItem.setChecked(enableLogging); + } return super.onPrepareOptionsMenu(menu); } @@ -135,6 +167,8 @@ public boolean onPrepareOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) { menu.add(Menu.NONE, MENU_USE_HTTPS, Menu.NONE, R.string.menu_use_https).setCheckable(true); menu.add(Menu.NONE, MENU_CLEAR_VIEW, Menu.NONE, R.string.menu_clear_view); + menu.add(Menu.NONE, MENU_ENABLE_LOGGING, Menu.NONE, "Enable Logging").setCheckable(true); + menu.add(Menu.NONE, MENU_LOGGING_VERBOSITY, Menu.NONE, "Set Logging Verbosity"); return super.onCreateOptionsMenu(menu); } @@ -146,6 +180,13 @@ public boolean onOptionsItemSelected(MenuItem item) { PROTOCOL = useHttps ? PROTOCOL_HTTPS : PROTOCOL_HTTP; urlEditText.setText(getDefaultURL()); return true; + case MENU_ENABLE_LOGGING: + enableLogging = !enableLogging; + getAsyncHttpClient().setLoggingEnabled(enableLogging); + return true; + case MENU_LOGGING_VERBOSITY: + showLoggingVerbosityDialog(); + return true; case MENU_CLEAR_VIEW: clearOutputs(); return true; @@ -172,6 +213,29 @@ public void addRequestHandle(RequestHandle handle) { } } + private void showLoggingVerbosityDialog() { + AlertDialog ad = new AlertDialog.Builder(this) + .setTitle("Set Logging Verbosity") + .setSingleChoiceItems(new String[]{ + "VERBOSE", + "DEBUG", + "INFO", + "WARN", + "ERROR", + "WTF" + }, getAsyncHttpClient().getLoggingLevel() - 2, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + getAsyncHttpClient().setLoggingLevel(which + 2); + dialog.dismiss(); + } + }) + .setCancelable(true) + .setNeutralButton("Cancel", null) + .create(); + ad.show(); + } + public void onRunButtonPressed() { addRequestHandle(executeSample(getAsyncHttpClient(), getUrlText(getDefaultURL()), @@ -184,20 +248,6 @@ public void onCancelButtonPressed() { asyncHttpClient.cancelRequests(SampleParentActivity.this, true); } - protected View.OnClickListener onClickListener = new View.OnClickListener() { - @Override - public void onClick(View v) { - switch (v.getId()) { - case R.id.button_run: - onRunButtonPressed(); - break; - case R.id.button_cancel: - onCancelButtonPressed(); - break; - } - } - }; - public List

getRequestHeadersList() { List
headers = new ArrayList
(); String headersRaw = headersEditText.getText() == null ? null : headersEditText.getText().toString(); @@ -213,7 +263,7 @@ public List
getRequestHeadersList() { String headerName = line.substring(0, equalSignPos).trim(); String headerValue = line.substring(1 + equalSignPos).trim(); - Log.d(LOG_TAG, String.format("Added header: [%s:%s]", headerName, headerValue)); + Log.d(LOG_TAG, String.format(Locale.US, "Added header: [%s:%s]", headerName, headerValue)); headers.add(new BasicHeader(headerName, headerValue)); } catch (Throwable t) { @@ -285,15 +335,6 @@ protected final void debugHeaders(String TAG, Header[] headers) { } } - protected static String throwableToString(Throwable t) { - if (t == null) - return null; - - StringWriter sw = new StringWriter(); - t.printStackTrace(new PrintWriter(sw)); - return sw.toString(); - } - protected final void debugThrowable(String TAG, Throwable t) { if (t != null) { Log.e(TAG, "AsyncHttpClient returned error", t); @@ -315,11 +356,6 @@ protected final void debugStatusCode(String TAG, int statusCode) { addView(getColoredView(LIGHTBLUE, msg)); } - public static int getContrastColor(int color) { - double y = (299 * Color.red(color) + 587 * Color.green(color) + 114 * Color.blue(color)) / 1000; - return y >= 128 ? Color.BLACK : Color.WHITE; - } - protected View getColoredView(int bgColor, String msg) { TextView tv = new TextView(this); tv.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); @@ -358,7 +394,7 @@ public void setAsyncHttpClient(AsyncHttpClient client) { @TargetApi(Build.VERSION_CODES.HONEYCOMB) private void setHomeAsUpEnabled() { - if (Integer.valueOf(Build.VERSION.SDK) >= 11) { + if (Build.VERSION.SDK_INT >= 11) { if (getActionBar() != null) getActionBar().setDisplayHomeAsUpEnabled(true); } diff --git a/sample/src/main/java/com/loopj/android/http/sample/SaxSample.java b/sample/src/main/java/com/loopj/android/http/sample/SaxSample.java index ca85eb8d6..be310272c 100644 --- a/sample/src/main/java/com/loopj/android/http/sample/SaxSample.java +++ b/sample/src/main/java/com/loopj/android/http/sample/SaxSample.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Sample Copyright (c) 2014 Marek Sebera - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -23,24 +23,19 @@ import com.loopj.android.http.ResponseHandlerInterface; import com.loopj.android.http.SaxAsyncHttpResponseHandler; -import org.apache.http.Header; -import org.apache.http.HttpEntity; import org.xml.sax.Attributes; import org.xml.sax.helpers.DefaultHandler; import java.util.ArrayList; import java.util.List; +import cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.HttpEntity; + public class SaxSample extends SampleParentActivity { private static final String LOG_TAG = "SaxSample"; - - @Override - public ResponseHandlerInterface getResponseHandler() { - return saxAsyncHttpResponseHandler; - } - - private SaxAsyncHttpResponseHandler saxAsyncHttpResponseHandler = new SaxAsyncHttpResponseHandler(new SAXTreeStructure()) { + private final SaxAsyncHttpResponseHandler saxAsyncHttpResponseHandler = new SaxAsyncHttpResponseHandler(new SAXTreeStructure()) { @Override public void onStart() { clearOutputs(); @@ -67,6 +62,11 @@ private void debugHandler(SAXTreeStructure handler) { } }; + @Override + public ResponseHandlerInterface getResponseHandler() { + return saxAsyncHttpResponseHandler; + } + @Override public String getDefaultURL() { return "http://bin-iin.com/sitemap.xml"; @@ -93,8 +93,8 @@ public RequestHandle executeSample(AsyncHttpClient client, String URL, Header[] } private class Tuple { - public Integer color; - public String text; + public final Integer color; + public final String text; public Tuple(int _color, String _text) { this.color = _color; @@ -104,7 +104,7 @@ public Tuple(int _color, String _text) { private class SAXTreeStructure extends DefaultHandler { - public List responseViews = new ArrayList(); + public final List responseViews = new ArrayList(); public void startElement(String namespaceURI, String localName, String rawName, Attributes atts) { diff --git a/sample/src/main/java/com/loopj/android/http/sample/SynchronousClientSample.java b/sample/src/main/java/com/loopj/android/http/sample/SynchronousClientSample.java index bb92f02c4..59ff04186 100644 --- a/sample/src/main/java/com/loopj/android/http/sample/SynchronousClientSample.java +++ b/sample/src/main/java/com/loopj/android/http/sample/SynchronousClientSample.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Sample Copyright (c) 2014 Marek Sebera - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -27,8 +27,8 @@ import com.loopj.android.http.ResponseHandlerInterface; import com.loopj.android.http.SyncHttpClient; -import org.apache.http.Header; -import org.apache.http.HttpEntity; +import cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.HttpEntity; public class SynchronousClientSample extends GetSample { private static final String LOG_TAG = "SyncSample"; diff --git a/sample/src/main/java/com/loopj/android/http/sample/ThreadingTimeoutSample.java b/sample/src/main/java/com/loopj/android/http/sample/ThreadingTimeoutSample.java index 2bf842294..f65ba17d8 100755 --- a/sample/src/main/java/com/loopj/android/http/sample/ThreadingTimeoutSample.java +++ b/sample/src/main/java/com/loopj/android/http/sample/ThreadingTimeoutSample.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Sample Copyright (c) 2014 Marek Sebera - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -25,14 +25,16 @@ import com.loopj.android.http.RequestHandle; import com.loopj.android.http.ResponseHandlerInterface; -import org.apache.http.Header; -import org.apache.http.HttpEntity; +import java.util.Locale; + +import cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.HttpEntity; public class ThreadingTimeoutSample extends SampleParentActivity { private static final String LOG_TAG = "ThreadingTimeoutSample"; - private final SparseArray states = new SparseArray(); - private int counter = 0; + protected final SparseArray states = new SparseArray(); + protected int counter = 0; @Override public int getSampleTitle() { @@ -59,12 +61,12 @@ public String getDefaultURL() { return PROTOCOL + "httpbin.org/delay/6"; } - private synchronized void setStatus(int id, String status) { + protected synchronized void setStatus(int id, String status) { String current = states.get(id, null); states.put(id, current == null ? status : current + "," + status); clearOutputs(); for (int i = 0; i < states.size(); i++) { - debugResponse(LOG_TAG, String.format("%d (from %d): %s", states.keyAt(i), getCounter(), states.get(states.keyAt(i)))); + debugResponse(LOG_TAG, String.format(Locale.US, "%d (from %d): %s", states.keyAt(i), getCounter(), states.get(states.keyAt(i)))); } } diff --git a/sample/src/main/java/com/loopj/android/http/sample/UsePoolThreadSample.java b/sample/src/main/java/com/loopj/android/http/sample/UsePoolThreadSample.java new file mode 100644 index 000000000..a9e6da958 --- /dev/null +++ b/sample/src/main/java/com/loopj/android/http/sample/UsePoolThreadSample.java @@ -0,0 +1,114 @@ +package com.loopj.android.http.sample; + +import android.util.Log; + +import com.loopj.android.http.AsyncHttpResponseHandler; +import com.loopj.android.http.ResponseHandlerInterface; + +import java.io.File; + +import cz.msebera.android.httpclient.Header; + +public class UsePoolThreadSample extends GetSample { + + private static final String LOG_TAG = "UsePoolThreadSample"; + + @Override + public String getDefaultURL() { + return PROTOCOL + "httpbin.org/bytes/1024000"; + } + + @Override + public boolean isRequestHeadersAllowed() { + return false; + } + + @Override + public int getSampleTitle() { + return R.string.title_use_pool_thread; + } + + @Override + public ResponseHandlerInterface getResponseHandler() { + return new UsePoolThreadResponseHandler(); + } + + private class UsePoolThreadResponseHandler extends AsyncHttpResponseHandler { + + private final File destFile; + + public UsePoolThreadResponseHandler() { + super(); + + // Destination file to save the downloaded bytes to. + destFile = getRandomCacheFile(); + Log.d(LOG_TAG, "Bytes will be saved in file: " + destFile.getAbsolutePath()); + + // We wish to use the same pool thread to run the response. + setUsePoolThread(true); + } + + @Override + public void onSuccess(final int statusCode, final Header[] headers, final byte[] responseBody) { + // Response body includes 1MB of data, and it might take few + // milliseconds, maybe a second or two on old devices, to save it in + // the filesystem. However, since this callback method is running + // within the pool thread's execution scope, the UI thread will be + // relaxed to continue its work of updating the UI while this + // handler saves the bytes on disk. + + // Save the response body's bytes on disk. + saveBytesOnDisk(destFile, responseBody); + + // This callback is now running within the pool thread execution + // scope and not within Android's UI thread, so if we must update + // the UI, we'll have to dispatch a runnable to the UI thread. + runOnUiThread(new Runnable() { + + @Override + public void run() { + debugStatusCode(LOG_TAG, statusCode); + debugHeaders(LOG_TAG, headers); + if (responseBody != null) { + addView(getColoredView(LIGHTGREEN, "Request succeeded (" + statusCode + "): (bytes=" + destFile.length() + "), path: " + destFile.getAbsolutePath())); + } + } + }); + } + + @Override + public void onFailure(final int statusCode, final Header[] headers, final byte[] responseBody, final Throwable error) { + // This callback is now running within the pool thread execution + // scope and not within Android's UI thread, so if we must update + // the UI, we'll have to dispatch a runnable to the UI thread. + runOnUiThread(new Runnable() { + + @Override + public void run() { + debugStatusCode(LOG_TAG, statusCode); + debugHeaders(LOG_TAG, headers); + debugThrowable(LOG_TAG, error); + if (responseBody != null) { + addView(getColoredView(LIGHTGREEN, "Download interrupted (" + statusCode + "): (bytes=" + responseBody.length + "), path: " + destFile.getAbsolutePath())); + } + } + }); + } + + private File getRandomCacheFile() { + File dir = getCacheDir(); + if (dir == null) { + dir = getFilesDir(); + } + + return new File(dir, "sample-" + System.currentTimeMillis() + ".bin"); + } + + private void saveBytesOnDisk(File destination, byte[] bytes) { + // TODO: Spin your own implementation to save the bytes on disk/SD card. + if (bytes != null && destination != null) { + Log.d(LOG_TAG, "Saved " + bytes.length + " bytes into file: " + destination.getAbsolutePath()); + } + } + } +} diff --git a/sample/src/main/java/com/loopj/android/http/sample/WaypointsActivity.java b/sample/src/main/java/com/loopj/android/http/sample/WaypointsActivity.java index 099dc1344..f1dba9d7a 100755 --- a/sample/src/main/java/com/loopj/android/http/sample/WaypointsActivity.java +++ b/sample/src/main/java/com/loopj/android/http/sample/WaypointsActivity.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Sample Copyright (c) 2014 Marek Sebera - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -35,16 +35,20 @@ public class WaypointsActivity extends ListActivity { new SampleConfig(R.string.title_post_sample, PostSample.class), new SampleConfig(R.string.title_delete_sample, DeleteSample.class), new SampleConfig(R.string.title_put_sample, PutSample.class), + new SampleConfig(R.string.title_patch_sample, PatchSample.class), + new SampleConfig(R.string.title_head_sample, HeadSample.class), new SampleConfig(R.string.title_json_sample, JsonSample.class), new SampleConfig(R.string.title_json_streamer_sample, JsonStreamerSample.class), new SampleConfig(R.string.title_sax_example, SaxSample.class), new SampleConfig(R.string.title_file_sample, FileSample.class), + new SampleConfig(R.string.title_directory_sample, DirectorySample.class), new SampleConfig(R.string.title_binary_sample, BinarySample.class), new SampleConfig(R.string.title_gzip_sample, GzipSample.class), new SampleConfig(R.string.title_redirect_302, Redirect302Sample.class), new SampleConfig(R.string.title_threading_timeout, ThreadingTimeoutSample.class), new SampleConfig(R.string.title_cancel_all, CancelAllRequestsSample.class), new SampleConfig(R.string.title_cancel_handle, CancelRequestHandleSample.class), + new SampleConfig(R.string.title_cancel_tag, CancelRequestByTagSample.class), new SampleConfig(R.string.title_synchronous, SynchronousClientSample.class), new SampleConfig(R.string.title_intent_service_sample, IntentServiceSample.class), new SampleConfig(R.string.title_post_files, FilesSample.class), @@ -56,17 +60,19 @@ public class WaypointsActivity extends ListActivity { new SampleConfig(R.string.title_pre_post_processing, PrePostProcessingSample.class), new SampleConfig(R.string.title_content_type_http_entity, ContentTypeForHttpEntitySample.class), new SampleConfig(R.string.title_resume_download, ResumeDownloadSample.class), - new SampleConfig(R.string.title_digest_auth, DigestAuthSample.class) + new SampleConfig(R.string.title_digest_auth, DigestAuthSample.class), + new SampleConfig(R.string.title_use_pool_thread, UsePoolThreadSample.class), + new SampleConfig(R.string.title_request_params_debug, RequestParamsDebug.class) }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setListAdapter(new ArrayAdapter(this, android.R.layout.simple_list_item_1, getTitlesList())); + setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, getTitlesList())); } private List getTitlesList() { - List titles = new ArrayList(); + List titles = new ArrayList<>(); for (SampleConfig config : samplesConfig) { titles.add(getString(config.titleId)); } diff --git a/sample/src/main/java/com/loopj/android/http/sample/package-info.java b/sample/src/main/java/com/loopj/android/http/sample/package-info.java index d2d743a5b..704f0d47d 100644 --- a/sample/src/main/java/com/loopj/android/http/sample/package-info.java +++ b/sample/src/main/java/com/loopj/android/http/sample/package-info.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Copyright (c) 2014 Marek Sebera - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/sample/src/main/java/com/loopj/android/http/sample/services/ExampleIntentService.java b/sample/src/main/java/com/loopj/android/http/sample/services/ExampleIntentService.java index 7fe49c71e..53e069ec3 100644 --- a/sample/src/main/java/com/loopj/android/http/sample/services/ExampleIntentService.java +++ b/sample/src/main/java/com/loopj/android/http/sample/services/ExampleIntentService.java @@ -10,7 +10,9 @@ import com.loopj.android.http.sample.IntentServiceSample; import com.loopj.android.http.sample.util.IntentUtil; -import org.apache.http.Header; +import java.util.Locale; + +import cz.msebera.android.httpclient.Header; public class ExampleIntentService extends IntentService { @@ -21,7 +23,7 @@ public class ExampleIntentService extends IntentService { public static final String INTENT_DATA = "INTENT_DATA"; public static final String INTENT_THROWABLE = "INTENT_THROWABLE"; - private AsyncHttpClient aClient = new SyncHttpClient(); + private final AsyncHttpClient aClient = new SyncHttpClient(); public ExampleIntentService() { super("ExampleIntentService"); @@ -73,7 +75,7 @@ public void onCancel() { @Override public void onRetry(int retryNo) { sendBroadcast(new Intent(IntentServiceSample.ACTION_RETRY)); - Log.d(LOG_TAG, String.format("onRetry: %d", retryNo)); + Log.d(LOG_TAG, String.format(Locale.US, "onRetry: %d", retryNo)); } @Override diff --git a/sample/src/main/java/com/loopj/android/http/sample/services/package-info.java b/sample/src/main/java/com/loopj/android/http/sample/services/package-info.java index df4ac428e..df7d33ef2 100644 --- a/sample/src/main/java/com/loopj/android/http/sample/services/package-info.java +++ b/sample/src/main/java/com/loopj/android/http/sample/services/package-info.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Copyright (c) 2014 Marek Sebera - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/sample/src/main/java/com/loopj/android/http/sample/util/API8Util.java b/sample/src/main/java/com/loopj/android/http/sample/util/API8Util.java new file mode 100644 index 000000000..5f7364f4f --- /dev/null +++ b/sample/src/main/java/com/loopj/android/http/sample/util/API8Util.java @@ -0,0 +1,23 @@ +package com.loopj.android.http.sample.util; + +import java.lang.reflect.Array; + +public class API8Util { + + @SuppressWarnings("unchecked") + public static T[] copyOfRange(T[] original, int start, int end) { + int originalLength = original.length; // For exception priority compatibility. + if (start > end) { + throw new IllegalArgumentException(); + } + if (start < 0 || start > originalLength) { + throw new ArrayIndexOutOfBoundsException(); + } + int resultLength = end - start; + int copyLength = Math.min(resultLength, originalLength - start); + T[] result = (T[]) Array.newInstance(original.getClass().getComponentType(), resultLength); + System.arraycopy(original, start, result, 0, copyLength); + return result; + } + +} diff --git a/sample/src/main/java/com/loopj/android/http/sample/util/FileUtil.java b/sample/src/main/java/com/loopj/android/http/sample/util/FileUtil.java index 6838d595a..faa9b7f5d 100755 --- a/sample/src/main/java/com/loopj/android/http/sample/util/FileUtil.java +++ b/sample/src/main/java/com/loopj/android/http/sample/util/FileUtil.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Sample Copyright (c) 2014 Marek Sebera - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -24,7 +24,7 @@ import java.io.InputStream; import java.io.InputStreamReader; -// Source: http://stackoverflow.com/questions/12910503/android-read-file-as-string +// Source: https://stackoverflow.com/questions/12910503/android-read-file-as-string public class FileUtil { public static String convertStreamToString(InputStream is) throws Exception { diff --git a/sample/src/main/java/com/loopj/android/http/sample/util/IntentUtil.java b/sample/src/main/java/com/loopj/android/http/sample/util/IntentUtil.java index a0ccb54b3..dadb8be59 100644 --- a/sample/src/main/java/com/loopj/android/http/sample/util/IntentUtil.java +++ b/sample/src/main/java/com/loopj/android/http/sample/util/IntentUtil.java @@ -1,7 +1,7 @@ package com.loopj.android.http.sample.util; -import org.apache.http.Header; -import org.apache.http.message.BasicHeader; +import cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.message.BasicHeader; public class IntentUtil { diff --git a/sample/src/main/java/com/loopj/android/http/sample/util/SampleJSON.java b/sample/src/main/java/com/loopj/android/http/sample/util/SampleJSON.java index 8e6c4a911..60413e5ed 100755 --- a/sample/src/main/java/com/loopj/android/http/sample/util/SampleJSON.java +++ b/sample/src/main/java/com/loopj/android/http/sample/util/SampleJSON.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Sample Copyright (c) 2014 Marek Sebera - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/sample/src/main/java/com/loopj/android/http/sample/util/SecureSocketFactory.java b/sample/src/main/java/com/loopj/android/http/sample/util/SecureSocketFactory.java index 779c2f0e0..fddcafcbe 100644 --- a/sample/src/main/java/com/loopj/android/http/sample/util/SecureSocketFactory.java +++ b/sample/src/main/java/com/loopj/android/http/sample/util/SecureSocketFactory.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Sample Copyright (c) 2014 Marek Sebera - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -18,13 +18,10 @@ package com.loopj.android.http.sample.util; -import android.os.Build; import android.util.Log; import com.loopj.android.http.AsyncHttpClient; -import org.apache.http.conn.ssl.SSLSocketFactory; - import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -45,9 +42,12 @@ import java.security.cert.X509Certificate; import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; +import cz.msebera.android.httpclient.conn.ssl.SSLSocketFactory; + /** * A class to authenticate a secured connection against a custom CA using a BKS store. * @@ -166,7 +166,12 @@ public Socket createSocket(Socket socket, String host, int port, boolean autoClo throws IOException { injectHostname(socket, host); - return sslCtx.getSocketFactory().createSocket(socket, host, port, autoClose); + Socket sslSocket = sslCtx.getSocketFactory().createSocket(socket, host, port, autoClose); + + // throw an exception if the hostname does not match the certificate + getHostnameVerifier().verify(host, (SSLSocket) sslSocket); + + return sslSocket; } @Override @@ -183,11 +188,9 @@ public Socket createSocket() throws IOException { */ private void injectHostname(Socket socket, String host) { try { - if (Integer.valueOf(Build.VERSION.SDK) >= 4) { - Field field = InetAddress.class.getDeclaredField("hostName"); - field.setAccessible(true); - field.set(socket.getInetAddress(), host); - } + Field field = InetAddress.class.getDeclaredField("hostName"); + field.setAccessible(true); + field.set(socket.getInetAddress(), host); } catch (Exception ignored) { } } diff --git a/sample/src/main/java/com/loopj/android/http/sample/util/package-info.java b/sample/src/main/java/com/loopj/android/http/sample/util/package-info.java index 4937f7f56..7d28904fd 100644 --- a/sample/src/main/java/com/loopj/android/http/sample/util/package-info.java +++ b/sample/src/main/java/com/loopj/android/http/sample/util/package-info.java @@ -1,13 +1,13 @@ /* Android Asynchronous Http Client Copyright (c) 2014 Marek Sebera - http://loopj.com + https://github.com/android-async-http/android-async-http Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/sample/src/main/res/drawable-hdpi/ic_launcher.png b/sample/src/main/res/drawable-hdpi/ic_launcher.png old mode 100755 new mode 100644 index 96a442e5b..d95d16f9e Binary files a/sample/src/main/res/drawable-hdpi/ic_launcher.png and b/sample/src/main/res/drawable-hdpi/ic_launcher.png differ diff --git a/sample/src/main/res/drawable-mdpi/ic_launcher.png b/sample/src/main/res/drawable-mdpi/ic_launcher.png old mode 100755 new mode 100644 index 359047dfa..75b78337f Binary files a/sample/src/main/res/drawable-mdpi/ic_launcher.png and b/sample/src/main/res/drawable-mdpi/ic_launcher.png differ diff --git a/sample/src/main/res/drawable-xhdpi/ic_launcher.png b/sample/src/main/res/drawable-xhdpi/ic_launcher.png old mode 100755 new mode 100644 index 71c6d760f..52a6abdd7 Binary files a/sample/src/main/res/drawable-xhdpi/ic_launcher.png and b/sample/src/main/res/drawable-xhdpi/ic_launcher.png differ diff --git a/sample/src/main/res/drawable-xxhdpi/ic_launcher.png b/sample/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..d75067ca9 Binary files /dev/null and b/sample/src/main/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/sample/src/main/res/drawable-xxxhdpi/ic_launcher.png b/sample/src/main/res/drawable-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..927c4a26c Binary files /dev/null and b/sample/src/main/res/drawable-xxxhdpi/ic_launcher.png differ diff --git a/sample/src/main/res/layout-v14/parent_layout.xml b/sample/src/main/res/layout-v14/parent_layout.xml index cef3029dd..bf796eb99 100755 --- a/sample/src/main/res/layout-v14/parent_layout.xml +++ b/sample/src/main/res/layout-v14/parent_layout.xml @@ -1,8 +1,8 @@ + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:fillViewport="true"> - + + android:inputType="textUri" /> + android:text="@string/button_cancel" />