package edu.mit.mitmobile2;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import edu.mit.mitmobile2.ConnectionWrapper.ConnectionInterface;
import edu.mit.mitmobile2.ConnectionWrapper.ErrorType;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.widget.Toast;
public class MobileWebApi {
private static final String TAG = "MobileWebApi";
public static final String BASE_PATH = "/api";
public static enum HttpClientType {
Default,
MIT
};
public static enum ResponseType {
JSONObject,
JSONArray,
Raw
};
public static enum LoadingDialogType {
Generic,
Search
}
public HttpClientType httpClientType;
public static int ERROR = 0;
public static int SUCCESS = 1;
public static int CANCELLED = 2;
public final static String NETWORK_ERROR = "There was a problem connecting to the network.";
public final static String SERVER_ERROR = "There was a problem connecting to the server.";
public final static String SEARCH_ERROR_MESSAGE = "There was a problem loading the search results. Please try again.";
public final static String TOUCHSTONE_ERROR = "There was a problem logging on to Touchstone. Please try again with the correct username and password.";
final static String GENERIC_MESSAGE = "Loading...";
final static String SEARCH_MESSAGE = "Searching...";
public static interface ErrorResponseListener {
public void onError();
}
public static abstract class CancelRequestListener {
private boolean mRequestCancelled = false;
private void notifyRequestCancelled() {
mRequestCancelled = true;
onCancel();
}
public boolean wasRequestCancelled() {
return mRequestCancelled;
}
public abstract void onCancel();
}
public static abstract class ResponseListener {
private ErrorResponseListener mErrorResponseListener;
private CancelRequestListener mCancelRequestListener;
ResponseListener(ErrorResponseListener errorListener, CancelRequestListener cancelListener) {
mErrorResponseListener = errorListener;
mCancelRequestListener = cancelListener;
}
public void onError() {
mErrorResponseListener.onError();
}
public CancelRequestListener getCancelRequestListener() {
return mCancelRequestListener;
}
public boolean wasRequestCancelled() {
if(mCancelRequestListener != null) {
return mCancelRequestListener.wasRequestCancelled();
}
return false;
}
}
@SuppressWarnings("serial")
public static class ServerResponseException extends Exception {};
public static abstract class JSONArrayResponseListener extends ResponseListener {
public JSONArrayResponseListener(ErrorResponseListener errorListener, CancelRequestListener cancelListener) {
super(errorListener, cancelListener);
}
public abstract void onResponse(JSONArray array) throws ServerResponseException, JSONException;
}
public static abstract class JSONObjectResponseListener extends ResponseListener {
public JSONObjectResponseListener(ErrorResponseListener errorListener, CancelRequestListener cancelListener) {
super(errorListener, cancelListener);
}
public abstract void onResponse(JSONObject object) throws ServerResponseException, JSONException;
}
public static abstract class RawResponseListener extends ResponseListener {
public RawResponseListener(ErrorResponseListener errorListener, CancelRequestListener cancelListener) {
super(errorListener, cancelListener);
}
public abstract void onResponse(InputStream stream);
}
// convenience functions useful to send messages
// back into the the UI thread
public static void sendErrorMessage(Handler uiHandler) {
Message message = Message.obtain();
message.arg1 = MobileWebApi.ERROR;
uiHandler.sendMessage(message);
}
public static void sendCancelMessage(Handler uiHandler) {
Message message = Message.obtain();
message.arg1 = MobileWebApi.CANCELLED;
uiHandler.sendMessage(message);
}
public static void sendSuccessMessage(Handler uiHandler) {
sendSuccessMessage(uiHandler, null);
}
public static void sendSuccessMessage(Handler uiHandler, Object userData) {
Message message = Message.obtain();
message.arg1 = MobileWebApi.SUCCESS;
message.obj = userData;
uiHandler.sendMessage(message);
}
// the most common type of behavior need for handling network errors
// either ignore it, or let the UI thread know about it
public static class DefaultErrorListener implements ErrorResponseListener {
Handler mUIHandler;
public DefaultErrorListener(Handler uiHandler) {
mUIHandler = uiHandler;
}
@Override
public void onError() {
sendErrorMessage(mUIHandler);
}
}
public static class IgnoreErrorListener implements ErrorResponseListener {
@Override
public void onError() {
// do nothing
}
}
public static class DefaultCancelRequestListener extends CancelRequestListener {
Handler mUIHandler;
public DefaultCancelRequestListener(Handler uiHandler) {
mUIHandler = uiHandler;
}
@Override
public void onCancel() {
sendCancelMessage(mUIHandler);
}
}
@SuppressWarnings("unused")
private LoadingDialogType mLoadingDialogType;
private boolean mShowErrors;
private Context mContext = null;
private Handler mUIHandler = null;
private boolean mIsSearchQuery = false;
public MobileWebApi(boolean showLoading, boolean showErrors, String errorTitle, Context context, Handler UIHandler) {
mShowErrors = showErrors;
mLoadingDialogType = LoadingDialogType.Generic;
if(mShowErrors) {
if(context == null) {
throw new RuntimeException("Fatal error, context needs to be defined to show loading or errors");
}
}
mContext = context;
if(mShowErrors) {
if(errorTitle == null) {
throw new RuntimeException("Fatal error, errorTitle needs to be defined to show errors");
}
if(UIHandler == null) {
throw new RuntimeException("Fatal error, uiHandler needs to be defined to show errors");
}
mUIHandler = UIHandler;
}
// set http client to DefaultHttpClient
httpClientType = HttpClientType.Default;
}
public MobileWebApi(boolean showLoading, boolean showErrors, String errorTitle, Context context, Handler UIHandler, HttpClientType httpClientType) {
mShowErrors = showErrors;
mLoadingDialogType = LoadingDialogType.Generic;
if(mShowErrors) {
if(context == null) {
throw new RuntimeException("Fatal error, context needs to be defined to show loading or errors");
}
}
mContext = context;
if(mShowErrors) {
if(errorTitle == null) {
throw new RuntimeException("Fatal error, errorTitle needs to be defined to show errors");
}
if(UIHandler == null) {
throw new RuntimeException("Fatal error, uiHandler needs to be defined to show errors");
}
mUIHandler = UIHandler;
}
// set http client to DefaultHttpClient or MITClient
this.httpClientType = httpClientType;
Log.d(TAG,"httpClientType = " + this.httpClientType);
}
/*
* This will create a Mobile Web Api wrapper that is silent,
* i.e. does not display a loading message or error messages
*/
public MobileWebApi() {
this(false, false, null, null, null);
}
public void setIsSearchQuery(boolean isSearchQuery) {
mIsSearchQuery = true;
}
public void setLoadingDialogType(LoadingDialogType dialogType) {
mLoadingDialogType = dialogType;
}
public static ProgressDialog MobileWebLoadingDialog(Context context, LoadingDialogType dialogType, final CancelRequestListener cancelListener) {
ProgressDialog loadingDialog = new ProgressDialog(context);
String loadingMessage;
if(dialogType == LoadingDialogType.Generic) {
loadingMessage = GENERIC_MESSAGE;
} else if(dialogType == LoadingDialogType.Search) {
loadingMessage = SEARCH_MESSAGE;
} else {
loadingMessage = GENERIC_MESSAGE;
}
loadingDialog.setMessage(loadingMessage);
loadingDialog.setIndeterminate(true);
if(cancelListener != null) {
loadingDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
cancelListener.notifyRequestCancelled();
}
});
} else {
loadingDialog.setCancelable(false);
}
return loadingDialog;
}
private boolean requestResponse(String path, Map<String, String> parameters, final ResponseType expectedType, final ResponseListener responseListener) {
Log.d(TAG,"path = " + path);
Log.d(TAG,"requestResponse");
ConnectionInterface callback = new ConnectionInterface() {
@Override
public void onError(final ErrorType error) {
Log.d(TAG,"requestResponse onError = " + error);
if(responseListener.wasRequestCancelled()) {
// nothing to handle request was cancelled
return;
}
if(mShowErrors) {
mUIHandler.post(new Runnable() {
@Override
public void run () {
String errorMessage = null;
if(error == ErrorType.Network) {
errorMessage = NETWORK_ERROR;
} else if(error == ErrorType.Server) {
errorMessage = SERVER_ERROR;
}
if(mIsSearchQuery) {
errorMessage = SEARCH_ERROR_MESSAGE;
}
//Log.d("MobileWebApi","requestResponse() error");
///Log.d("MobileWebApi",errorMessage);
Toast.makeText(mContext, errorMessage, Toast.LENGTH_LONG).show();
}
});
}
responseListener.onError();
}
@SuppressWarnings("incomplete-switch")
@Override
public void onResponse(final InputStream stream) {
if(responseListener.wasRequestCancelled()) {
return;
}
if (Looper.myLooper() != null) {
final Handler responseHandler = new Handler();
new Thread() {
@Override
public void run() {
String responseText = null;
switch(expectedType) {
case JSONObject:
case JSONArray:
responseText = convertStreamToString(stream);
break;
}
final String finalResponseText = responseText;
responseHandler.post(new Runnable() {
@Override
public void run() {
processResponse(finalResponseText, stream);
}
});
}
}.start();
} else {
String responseText = null;
switch(expectedType) {
case JSONObject:
case JSONArray:
responseText = convertStreamToString(stream);
break;
}
processResponse(responseText, stream);
}
}
private void processResponse(String responseText, InputStream stream) {
try {
switch(expectedType) {
case JSONObject:
JSONObjectResponseListener objectResponseListener = (JSONObjectResponseListener) responseListener;
objectResponseListener.onResponse(new JSONObject(responseText));
break;
case JSONArray:
JSONArrayResponseListener arrayResponseListener = (JSONArrayResponseListener) responseListener;
arrayResponseListener.onResponse(new JSONArray(responseText));
break;
case Raw:
RawResponseListener rawResponseListener = (RawResponseListener) responseListener;
rawResponseListener.onResponse(stream);
break;
}
} catch (JSONException e) {
e.printStackTrace();
onError(ErrorType.Server);
} catch (ServerResponseException e) {
e.printStackTrace();
onError(ErrorType.Server);
}
}
};
ConnectionWrapper connection;
if(mContext != null) {
if (httpClientType == HttpClientType.MIT) {
Log.d(TAG,"httpClientType == MIT");
connection = new MITConnectionWrapper(mContext,httpClientType);
Log.d(TAG,"class of connection = " +connection.getClass() );
}
else {
connection = new ConnectionWrapper(mContext);
Log.d(TAG,"httpClientType != MIT");
}
} else {
Log.d(TAG,"mContext is null");
connection = new ConnectionWrapper();
}
String urlString = "http://" + Global.getMobileWebDomain() + BASE_PATH + path + "/?" + query(parameters);
Log.d(TAG, "requesting " + urlString);
boolean isStarted = connection.openURL(urlString, callback);
return isStarted;
}
public boolean requestJSONObject(String path, Map<String, String> parameters, JSONObjectResponseListener responseListener) {
Log.d(TAG,"json 1 - path = " + path);
return requestResponse(path, parameters, ResponseType.JSONObject, responseListener);
}
public boolean requestJSONObject(Map<String, String> parameters, JSONObjectResponseListener responseListener) {
Log.d(TAG,"json 2 module = " + parameters.get("module"));
return requestJSONObject("", parameters, responseListener);
}
public boolean requestJSONArray(String path, Map<String, String> parameters, JSONArrayResponseListener responseListener) {
return requestResponse(path, parameters, ResponseType.JSONArray, responseListener);
}
public boolean requestJSONArray(Map<String, String> parameters, JSONArrayResponseListener responseListener) {
return requestJSONArray("", parameters, responseListener);
}
public boolean requestRaw(String path, Map<String, String> parameters, RawResponseListener responseListener) {
return requestResponse(path, parameters, ResponseType.Raw, responseListener);
}
public static String query(Map<String, String> parameters) {
String query = "";
for(String key : parameters.keySet()) {
try {
if(query.length() > 0) {
query += "&";
}
Log.d("ZZZ","query = " + query);
Log.d("ZZZ","key = " + key);
query += key + "=" + java.net.URLEncoder.encode(parameters.get(key), "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
return query;
}
public static String convertStreamToString(InputStream stream) {
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
StringBuilder stringBuilder = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) != null) {
stringBuilder.append(line + "\n");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return stringBuilder.toString();
}
}