/*
* Copyright 2014 OpenMarket Ltd
*
* 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 org.matrix.androidsdk.rest.callback;
import org.matrix.androidsdk.util.Log;
import org.matrix.androidsdk.rest.model.MatrixError;
import org.matrix.androidsdk.util.UnsentEventsManager;
import retrofit.Callback;
import retrofit.RetrofitError;
import retrofit.client.Response;
import retrofit.mime.TypedByteArray;
import retrofit.mime.TypedInput;
public class RestAdapterCallback<T> implements Callback<T> {
private static final String LOG_TAG = "RestAdapterCallback";
/**
* Callback when a request failed after a network error.
* This callback should manage the request auto resent.
*/
public interface RequestRetryCallBack {
void onRetry();
}
// the event description
private String mEventDescription;
// the callback
private final ApiCallback mApiCallback;
// the retry callback
private final RequestRetryCallBack mRequestRetryCallBack;
// the unsent events manager
private final UnsentEventsManager mUnsentEventsManager;
// true to do not test if he event time line when sending again
// the request when a data connection is retrieved.
private final boolean mIgnoreEventTimeLifeInOffline;
/**
* Constructor with unsent events management
* @param description the event description
* @param unsentEventsManager the unsent events manager
* @param apiCallback the callback
* @param requestRetryCallBack the retry callback
*/
public RestAdapterCallback(String description, UnsentEventsManager unsentEventsManager, ApiCallback apiCallback, RequestRetryCallBack requestRetryCallBack) {
this(description, unsentEventsManager, false, apiCallback, requestRetryCallBack);
}
/**
* Constructor with unsent events management
* @param description the event description
* @param ignoreEventTimeLifeOffline true to ignore the event time when resending the event.
* @param unsentEventsManager the unsent events manager
* @param apiCallback the callback
* @param requestRetryCallBack the retry callback
*/
public RestAdapterCallback(String description, UnsentEventsManager unsentEventsManager, boolean ignoreEventTimeLifeOffline, ApiCallback apiCallback, RequestRetryCallBack requestRetryCallBack) {
if (null != description) {
Log.d(LOG_TAG, "Trigger the event [" + description + "]");
}
this.mEventDescription = description;
this.mIgnoreEventTimeLifeInOffline = ignoreEventTimeLifeOffline;
this.mApiCallback = apiCallback;
this.mRequestRetryCallBack = requestRetryCallBack;
this.mUnsentEventsManager = unsentEventsManager;
}
/**
* Notify the {@link UnsentEventsManager} that the event has been successfully sent.
* This method must be called each time a REST call succeed, in order to warn
* the {@link UnsentEventsManager} to send the next unsent events.
*/
protected void onEventSent() {
if (null != mUnsentEventsManager) {
try {
// some users reported that their devices were connected
// whereas this receiver was not called
if (!mUnsentEventsManager.getNetworkConnectivityReceiver().isConnected()) {
Log.d(LOG_TAG, "## onEventSent(): request succeed, while network seen as disconnected => ask ConnectivityReceiver to dispatch info");
mUnsentEventsManager.getNetworkConnectivityReceiver().checkNetworkConnection(mUnsentEventsManager.getContext());
}
mUnsentEventsManager.onEventSent(mApiCallback);
} catch (Exception e) {
Log.d(LOG_TAG, "## onEventSent(): Exception " + e.getMessage());
}
}
}
@Override
public void success(T t, Response response) {
if (null != mEventDescription) {
Log.d(LOG_TAG, "## Succeed() : [" + mEventDescription + "]");
}
// add try catch to prevent application crashes while managing destroyed object
try {
onEventSent();
if (null != mApiCallback) {
try {
mApiCallback.onSuccess(t);
} catch (Exception e) {
Log.d(LOG_TAG, "## succeed() : onSuccess failed" + e.getMessage());
}
}
} catch (Exception e) {
// privacy
Log.e(LOG_TAG, "## succeed(): Exception " + e.getMessage());
}
}
/**
* Default failure implementation that calls the right error handler
* @param error the retrofit error
*/
@Override
public void failure(RetrofitError error) {
if (null != mEventDescription) {
Log.d(LOG_TAG, "## failure(): [" + mEventDescription + "]" + " with error " + error.getMessage());
}
boolean retry = true;
if (null != error.getResponse()) {
retry = (error.getResponse().getStatus() < 400) || (error.getResponse().getStatus() > 500);
}
if (retry && (null != mUnsentEventsManager)) {
Log.d(LOG_TAG, "Add it to the UnsentEventsManager");
mUnsentEventsManager.onEventSendingFailed(mEventDescription, mIgnoreEventTimeLifeInOffline, error, mApiCallback, mRequestRetryCallBack);
} else {
if (error.isNetworkError()) {
try {
if (null != mApiCallback) {
try {
mApiCallback.onNetworkError(error);
} catch (Exception e) {
Log.e(LOG_TAG, "## failure(): onNetworkError " + error.getLocalizedMessage());
}
}
} catch (Exception e) {
// privacy
//Log.e(LOG_TAG, "Exception NetworkError " + e.getMessage() + " while managing " + error.getUrl());
Log.e(LOG_TAG, "## failure(): NetworkError " + e.getLocalizedMessage());
}
}
else {
// Try to convert this into a Matrix error
MatrixError mxError;
try {
mxError = (MatrixError) error.getBodyAs(MatrixError.class);
mxError.mStatus = error.getResponse().getStatus();
mxError.mReason = error.getResponse().getReason();
TypedInput body = error.getResponse().getBody();
if (null != body) {
mxError.mErrorBodyMimeType = body.mimeType();
mxError.mErrorBody = body;
try {
if (body instanceof TypedByteArray) {
mxError.mErrorBodyAsString = new String(((TypedByteArray)body).getBytes());
} else {
mxError.mErrorBodyAsString = (String)error.getBodyAs(String.class);
}
} catch (Exception castException) {
Log.e(LOG_TAG, "## failure(): MatrixError cannot cast the response body" + castException.getMessage());
}
}
}
catch (Exception e) {
mxError = null;
}
if (mxError != null) {
if (MatrixError.LIMIT_EXCEEDED.equals(mxError.errcode) && (null != mUnsentEventsManager)) {
mUnsentEventsManager.onEventSendingFailed(mEventDescription, mIgnoreEventTimeLifeInOffline, error, mApiCallback, mRequestRetryCallBack);
} else {
try {
if (null != mApiCallback) {
mApiCallback.onMatrixError(mxError);
}
} catch (Exception e) {
// privacy
//Log.e(LOG_TAG, "Exception MatrixError " + e.getMessage() + " while managing " + error.getUrl());
Log.e(LOG_TAG, "## failure(): MatrixError " + e.getLocalizedMessage());
}
}
}
else {
try {
if (null != mApiCallback) {
mApiCallback.onUnexpectedError(error);
}
} catch (Exception e) {
// privacy
//Log.e(LOG_TAG, "Exception UnexpectedError " + e.getMessage() + " while managing " + error.getUrl());
Log.e(LOG_TAG, "## failure(): UnexpectedError " + e.getLocalizedMessage());
}
}
}
}
}
}