/*
*
* * Copyright (C) 2016 Amit Shekhar
* * Copyright (C) 2011 Android Open Source Project
* *
* * 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.
*
*/
package com.rx2androidnetworking;
import android.net.TrafficStats;
import com.androidnetworking.common.ANConstants;
import com.androidnetworking.common.ANResponse;
import com.androidnetworking.common.ConnectionClassManager;
import com.androidnetworking.error.ANError;
import com.androidnetworking.internal.InternalNetworking;
import com.androidnetworking.internal.RequestProgressBody;
import com.androidnetworking.internal.ResponseProgressBody;
import com.androidnetworking.utils.SourceCloseUtil;
import com.androidnetworking.utils.Utils;
import java.io.File;
import java.io.IOException;
import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;
import io.reactivex.exceptions.CompositeException;
import io.reactivex.exceptions.Exceptions;
import io.reactivex.plugins.RxJavaPlugins;
import okhttp3.Call;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import static com.androidnetworking.common.Method.DELETE;
import static com.androidnetworking.common.Method.GET;
import static com.androidnetworking.common.Method.HEAD;
import static com.androidnetworking.common.Method.PATCH;
import static com.androidnetworking.common.Method.POST;
import static com.androidnetworking.common.Method.PUT;
/**
* Created by Prashant Gupta on 30-01-2017.
*/
@SuppressWarnings("unchecked")
public class Rx2InternalNetworking {
public static <T> Observable<T> generateSimpleObservable(Rx2ANRequest request) {
Request okHttpRequest;
Request.Builder builder = new Request.Builder().url(request.getUrl());
InternalNetworking.addHeadersToRequestBuilder(builder, request);
RequestBody requestBody;
switch (request.getMethod()) {
case GET: {
builder = builder.get();
break;
}
case POST: {
requestBody = request.getRequestBody();
builder = builder.post(requestBody);
break;
}
case PUT: {
requestBody = request.getRequestBody();
builder = builder.put(requestBody);
break;
}
case DELETE: {
requestBody = request.getRequestBody();
builder = builder.delete(requestBody);
break;
}
case HEAD: {
builder = builder.head();
break;
}
case PATCH: {
requestBody = request.getRequestBody();
builder = builder.patch(requestBody);
break;
}
}
if (request.getCacheControl() != null) {
builder.cacheControl(request.getCacheControl());
}
okHttpRequest = builder.build();
if (request.getOkHttpClient() != null) {
request.setCall(request
.getOkHttpClient()
.newBuilder()
.cache(InternalNetworking.sHttpClient.cache())
.build()
.newCall(okHttpRequest));
} else {
request.setCall(InternalNetworking.sHttpClient.newCall(okHttpRequest));
}
return new SimpleANObservable<>(request);
}
public static <T> Observable<T> generateDownloadObservable(final Rx2ANRequest request) {
Request okHttpRequest;
Request.Builder builder = new Request.Builder().url(request.getUrl());
InternalNetworking.addHeadersToRequestBuilder(builder, request);
builder = builder.get();
if (request.getCacheControl() != null) {
builder.cacheControl(request.getCacheControl());
}
okHttpRequest = builder.build();
OkHttpClient okHttpClient;
if (request.getOkHttpClient() != null) {
okHttpClient = request
.getOkHttpClient()
.newBuilder()
.cache(InternalNetworking.sHttpClient.cache())
.addNetworkInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
return originalResponse.newBuilder()
.body(new ResponseProgressBody(originalResponse.body(),
request.getDownloadProgressListener()))
.build();
}
}).build();
} else {
okHttpClient = InternalNetworking.sHttpClient.newBuilder()
.addNetworkInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
return originalResponse.newBuilder()
.body(new ResponseProgressBody(originalResponse.body(),
request.getDownloadProgressListener()))
.build();
}
}).build();
}
request.setCall(okHttpClient.newCall(okHttpRequest));
return new DownloadANObservable<>(request);
}
public static <T> Observable<T> generateMultipartObservable(final Rx2ANRequest request) {
return new MultipartANObservable<>(request);
}
static final class SimpleANObservable<T> extends Observable<T> {
private Rx2ANRequest request;
private final Call originalCall;
SimpleANObservable(Rx2ANRequest request) {
this.request = request;
this.originalCall = request.getCall();
}
@Override
protected void subscribeActual(Observer<? super T> observer) {
Call call = originalCall.clone();
observer.onSubscribe(new ANDisposable(call));
boolean doNotSwallowError = false;
Response okHttpResponse = null;
try {
final long startTime = System.currentTimeMillis();
final long startBytes = TrafficStats.getTotalRxBytes();
okHttpResponse = call.execute();
final long timeTaken = System.currentTimeMillis() - startTime;
if (okHttpResponse.cacheResponse() == null) {
final long finalBytes = TrafficStats.getTotalRxBytes();
final long diffBytes;
if (startBytes == TrafficStats.UNSUPPORTED || finalBytes == TrafficStats.UNSUPPORTED) {
diffBytes = okHttpResponse.body().contentLength();
} else {
diffBytes = finalBytes - startBytes;
}
ConnectionClassManager.getInstance().updateBandwidth(diffBytes, timeTaken);
Utils.sendAnalytics(request.getAnalyticsListener(), timeTaken,
(request.getRequestBody() != null &&
request.getRequestBody().contentLength() != 0) ?
request.getRequestBody().contentLength() : -1,
okHttpResponse.body().contentLength(), false);
} else if (request.getAnalyticsListener() != null) {
if (okHttpResponse.networkResponse() == null) {
Utils.sendAnalytics(request.getAnalyticsListener(), timeTaken, 0, 0, true);
} else {
Utils.sendAnalytics(request.getAnalyticsListener(), timeTaken,
(request.getRequestBody() != null && request.getRequestBody().contentLength() != 0) ?
request.getRequestBody().contentLength() : -1, 0, true);
}
}
if (okHttpResponse.code() >= 400) {
if (!call.isCanceled()) {
observer.onError(Utils.getErrorForServerResponse(new ANError(okHttpResponse),
request, okHttpResponse.code()));
}
} else {
ANResponse<T> response = request.parseResponse(okHttpResponse);
if (!response.isSuccess()) {
if (!call.isCanceled()) {
observer.onError(response.getError());
}
} else {
if (!call.isCanceled()) {
observer.onNext(response.getResult());
}
if (!call.isCanceled()) {
doNotSwallowError = true;
observer.onComplete();
}
}
}
} catch (IOException ioe) {
if (!call.isCanceled()) {
observer.onError(Utils.getErrorForConnection(new ANError(ioe)));
}
} catch (Exception e) {
Exceptions.throwIfFatal(e);
if (doNotSwallowError) {
RxJavaPlugins.onError(e);
} else if (!call.isCanceled()) {
try {
observer.onError(Utils.getErrorForNetworkOnMainThreadOrConnection(e));
} catch (Exception e1) {
Exceptions.throwIfFatal(e1);
RxJavaPlugins.onError(new CompositeException(e, e1));
}
}
} finally {
SourceCloseUtil.close(okHttpResponse, request);
}
}
}
static final class DownloadANObservable<T> extends Observable<T> {
private final Rx2ANRequest request;
private final Call originalCall;
DownloadANObservable(Rx2ANRequest request) {
this.request = request;
this.originalCall = request.getCall();
}
@Override
protected void subscribeActual(Observer<? super T> observer) {
Call call = originalCall.clone();
observer.onSubscribe(new ANDisposable(call));
boolean doNotSwallowError = false;
Response okHttpResponse;
try {
final long startTime = System.currentTimeMillis();
final long startBytes = TrafficStats.getTotalRxBytes();
okHttpResponse = request.getCall().execute();
Utils.saveFile(okHttpResponse, request.getDirPath(), request.getFileName());
final long timeTaken = System.currentTimeMillis() - startTime;
if (okHttpResponse.cacheResponse() == null) {
final long finalBytes = TrafficStats.getTotalRxBytes();
final long diffBytes;
if (startBytes == TrafficStats.UNSUPPORTED ||
finalBytes == TrafficStats.UNSUPPORTED) {
diffBytes = okHttpResponse.body().contentLength();
} else {
diffBytes = finalBytes - startBytes;
}
ConnectionClassManager.getInstance().updateBandwidth(diffBytes, timeTaken);
Utils.sendAnalytics(request.getAnalyticsListener(),
timeTaken, -1, okHttpResponse.body().contentLength(), false);
} else if (request.getAnalyticsListener() != null) {
Utils.sendAnalytics(request.getAnalyticsListener(), timeTaken, -1, 0, true);
}
if (okHttpResponse.code() >= 400) {
if (!call.isCanceled()) {
observer.onError(Utils.getErrorForServerResponse(new ANError(okHttpResponse),
request, okHttpResponse.code()));
}
} else {
if (!call.isCanceled()) {
ANResponse<T> response = (ANResponse<T>) ANResponse.success(ANConstants.SUCCESS);
observer.onNext(response.getResult());
}
if (!call.isCanceled()) {
doNotSwallowError = true;
observer.onComplete();
}
}
} catch (IOException ioe) {
try {
File destinationFile = new File(request.getDirPath() + File.separator + request.getFileName());
if (destinationFile.exists()) {
destinationFile.delete();
}
} catch (Exception e) {
e.printStackTrace();
}
if (!call.isCanceled()) {
observer.onError(Utils.getErrorForConnection(new ANError(ioe)));
}
} catch (Exception e) {
Exceptions.throwIfFatal(e);
if (doNotSwallowError) {
RxJavaPlugins.onError(e);
} else if (!call.isCanceled()) {
try {
observer.onError(Utils.getErrorForNetworkOnMainThreadOrConnection(e));
} catch (Exception e1) {
Exceptions.throwIfFatal(e1);
RxJavaPlugins.onError(new CompositeException(e, e1));
}
}
}
}
}
static final class MultipartANObservable<T> extends Observable<T> {
private final Rx2ANRequest request;
MultipartANObservable(Rx2ANRequest request) {
this.request = request;
}
@Override
protected void subscribeActual(Observer<? super T> observer) {
boolean doNotSwallowError = false;
Response okHttpResponse = null;
Request okHttpRequest;
try {
Request.Builder builder = new Request.Builder().url(request.getUrl());
InternalNetworking.addHeadersToRequestBuilder(builder, request);
final RequestBody requestBody = request.getMultiPartRequestBody();
final long requestBodyLength = requestBody.contentLength();
builder = builder.post(new RequestProgressBody(requestBody, request.getUploadProgressListener()));
if (request.getCacheControl() != null) {
builder.cacheControl(request.getCacheControl());
}
okHttpRequest = builder.build();
if (request.getOkHttpClient() != null) {
request.setCall(request
.getOkHttpClient()
.newBuilder()
.cache(InternalNetworking.sHttpClient.cache())
.build()
.newCall(okHttpRequest));
} else {
request.setCall(InternalNetworking.sHttpClient.newCall(okHttpRequest));
}
observer.onSubscribe(new ANDisposable(request.getCall()));
final long startTime = System.currentTimeMillis();
okHttpResponse = request.getCall().execute();
final long timeTaken = System.currentTimeMillis() - startTime;
if (request.getAnalyticsListener() != null) {
if (okHttpResponse.cacheResponse() == null) {
Utils.sendAnalytics(request.getAnalyticsListener(), timeTaken,
requestBodyLength, okHttpResponse.body().contentLength(), false);
} else {
if (okHttpResponse.networkResponse() == null) {
Utils.sendAnalytics(request.getAnalyticsListener(), timeTaken, 0, 0, true);
} else {
Utils.sendAnalytics(request.getAnalyticsListener(), timeTaken,
requestBodyLength != 0 ? requestBodyLength : -1, 0, true);
}
}
}
if (okHttpResponse.code() >= 400) {
if (!request.getCall().isCanceled()) {
observer.onError(Utils.getErrorForServerResponse(new ANError(okHttpResponse),
request, okHttpResponse.code()));
}
} else {
ANResponse<T> response = request.parseResponse(okHttpResponse);
if (!response.isSuccess()) {
if (!request.getCall().isCanceled()) {
observer.onError(response.getError());
}
} else {
if (!request.getCall().isCanceled()) {
observer.onNext(response.getResult());
}
if (!request.getCall().isCanceled()) {
doNotSwallowError = true;
observer.onComplete();
}
}
}
} catch (IOException ioe) {
if (!request.getCall().isCanceled()) {
observer.onError(Utils.getErrorForConnection(new ANError(ioe)));
}
} catch (Exception e) {
Exceptions.throwIfFatal(e);
if (doNotSwallowError) {
RxJavaPlugins.onError(e);
} else if (!request.getCall().isCanceled()) {
try {
observer.onError(Utils.getErrorForNetworkOnMainThreadOrConnection(e));
} catch (Exception e1) {
Exceptions.throwIfFatal(e1);
RxJavaPlugins.onError(new CompositeException(e, e1));
}
}
} finally {
SourceCloseUtil.close(okHttpResponse, request);
}
}
}
private static final class ANDisposable implements Disposable {
private final Call call;
private ANDisposable(Call call) {
this.call = call;
}
@Override
public void dispose() {
this.call.cancel();
}
@Override
public boolean isDisposed() {
return this.call.isCanceled();
}
}
}