habitica-android/Habitica/src/com/habitrpg/android/habitica/APIHelper.java

336 lines
14 KiB
Java
Raw Normal View History

package com.habitrpg.android.habitica;
import android.app.Activity;
import android.content.DialogInterface;
import android.support.v7.app.AlertDialog;
2015-06-20 18:46:53 +00:00
2016-02-02 20:19:07 +00:00
import com.amplitude.api.Amplitude;
2015-07-06 15:47:12 +00:00
import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
2015-06-20 18:46:53 +00:00
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
2015-06-20 18:46:53 +00:00
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import com.habitrpg.android.habitica.callbacks.HabitRPGUserCallback;
import com.habitrpg.android.habitica.callbacks.TaskScoringCallback;
2015-11-29 13:35:07 +00:00
import com.habitrpg.android.habitica.database.CheckListItemExcludeStrategy;
2015-06-20 18:46:53 +00:00
import com.magicmicky.habitrpgwrapper.lib.api.ApiService;
2015-11-29 20:10:32 +00:00
import com.magicmicky.habitrpgwrapper.lib.api.InAppPurchasesApiService;
import com.magicmicky.habitrpgwrapper.lib.api.Server;
2015-06-20 18:46:53 +00:00
import com.magicmicky.habitrpgwrapper.lib.api.TypeAdapter.TagsAdapter;
import com.magicmicky.habitrpgwrapper.lib.models.Customization;
2016-01-22 17:13:41 +00:00
import com.magicmicky.habitrpgwrapper.lib.models.FAQArticle;
2015-11-29 20:10:32 +00:00
import com.magicmicky.habitrpgwrapper.lib.models.PurchaseValidationRequest;
import com.magicmicky.habitrpgwrapper.lib.models.PurchaseValidationResult;
import com.magicmicky.habitrpgwrapper.lib.models.Purchases;
2015-11-25 17:47:56 +00:00
import com.magicmicky.habitrpgwrapper.lib.models.SkillList;
import com.magicmicky.habitrpgwrapper.lib.models.TaskDirection;
2016-01-22 17:13:41 +00:00
import com.magicmicky.habitrpgwrapper.lib.models.TutorialStep;
import com.magicmicky.habitrpgwrapper.lib.models.UserAuth;
import com.magicmicky.habitrpgwrapper.lib.models.UserAuthResponse;
2015-11-13 19:33:26 +00:00
import com.magicmicky.habitrpgwrapper.lib.models.UserAuthSocial;
import com.magicmicky.habitrpgwrapper.lib.models.UserAuthSocialTokens;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.ChecklistItem;
2015-08-10 13:56:52 +00:00
import com.magicmicky.habitrpgwrapper.lib.models.tasks.Task;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.TaskTag;
import com.magicmicky.habitrpgwrapper.lib.utils.ChecklistItemSerializer;
import com.magicmicky.habitrpgwrapper.lib.utils.CustomizationDeserializer;
2016-01-21 13:02:09 +00:00
import com.magicmicky.habitrpgwrapper.lib.utils.DateDeserializer;
2016-01-22 17:13:41 +00:00
import com.magicmicky.habitrpgwrapper.lib.utils.FAQArticleListDeserilializer;
import com.magicmicky.habitrpgwrapper.lib.utils.PurchasedDeserializer;
2015-11-25 17:47:56 +00:00
import com.magicmicky.habitrpgwrapper.lib.utils.SkillDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.TaskListDeserializer;
2016-01-22 17:13:41 +00:00
import com.magicmicky.habitrpgwrapper.lib.utils.TutorialStepListDeserializer;
2015-07-06 15:47:12 +00:00
import com.raizlabs.android.dbflow.structure.ModelAdapter;
2015-06-20 18:46:53 +00:00
import java.io.IOException;
import java.lang.reflect.Type;
2016-01-21 13:02:09 +00:00
import java.util.Date;
import java.util.List;
import retrofit.Callback;
2015-06-20 18:46:53 +00:00
import retrofit.ErrorHandler;
import retrofit.Profiler;
import retrofit.RequestInterceptor;
import retrofit.RestAdapter;
import retrofit.RetrofitError;
import retrofit.converter.GsonConverter;
2015-06-20 18:46:53 +00:00
public class APIHelper implements ErrorHandler, Profiler {
private static final String TAG = "ApiHelper";
2015-11-08 18:45:05 +00:00
// I think we don't need the APIHelper anymore we could just use ApiService
public final ApiService apiService;
2015-11-29 20:10:32 +00:00
private final InAppPurchasesApiService inAppPurchasesService;
private HostConfig cfg;
2015-06-20 18:46:53 +00:00
2015-11-08 18:45:05 +00:00
//private OnHabitsAPIResult mResultListener;
//private HostConfig mConfig;
public APIHelper(final HostConfig cfg) {
2015-11-29 20:10:32 +00:00
this.cfg = cfg;
2016-02-02 20:19:07 +00:00
Amplitude.getInstance().setUserId(cfg.getUser());
2015-06-20 18:46:53 +00:00
2015-11-08 18:45:05 +00:00
RequestInterceptor requestInterceptor = new RequestInterceptor() {
@Override
public void intercept(RequestInterceptor.RequestFacade request) {
request.addHeader("x-api-key", cfg.getApi());
request.addHeader("x-api-user", cfg.getUser());
2015-12-08 23:35:18 +00:00
request.addHeader("x-client", "habitica-android");
2015-11-08 18:45:05 +00:00
}
};
Type taskTagClassListType = new TypeToken<List<TaskTag>>() {
}.getType();
Type taskClassListType = new TypeToken<List<Task>>() {}.getType();
2016-01-20 10:21:53 +00:00
Type checklistType = new TypeToken<List<ChecklistItem>>() {}.getType();
Type customizationListType = new TypeToken<List<Customization>>() {}.getType();
2016-01-22 17:13:41 +00:00
Type tutorialStepListType = new TypeToken<List<TutorialStep>>() {}.getType();
Type faqArticleListType = new TypeToken<List<FAQArticle>>() {}.getType();
//Exclusion stratety needed for DBFlow https://github.com/Raizlabs/DBFlow/issues/121
2015-11-08 18:45:05 +00:00
Gson gson = new GsonBuilder()
2015-11-29 13:35:07 +00:00
.setExclusionStrategies(new CheckListItemExcludeStrategy())
2015-11-08 18:45:05 +00:00
.setExclusionStrategies(new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getDeclaredClass().equals(ModelAdapter.class);
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
})
.registerTypeAdapter(taskTagClassListType, new TagsAdapter())
.registerTypeAdapter(Boolean.class, booleanAsIntAdapter)
.registerTypeAdapter(boolean.class, booleanAsIntAdapter)
2015-11-25 17:47:56 +00:00
.registerTypeAdapter(SkillList.class, new SkillDeserializer())
.registerTypeAdapter(ChecklistItem.class, new ChecklistItemSerializer())
.registerTypeAdapter(taskClassListType, new TaskListDeserializer())
.registerTypeAdapter(Purchases.class, new PurchasedDeserializer())
.registerTypeAdapter(customizationListType, new CustomizationDeserializer())
2016-01-22 17:13:41 +00:00
.registerTypeAdapter(tutorialStepListType, new TutorialStepListDeserializer())
.registerTypeAdapter(faqArticleListType, new FAQArticleListDeserilializer())
2016-01-21 13:02:09 +00:00
.registerTypeAdapter(Date.class, new DateDeserializer())
2015-11-08 18:45:05 +00:00
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
.create();
Server server = new Server(cfg.getAddress());
RestAdapter adapter = new RestAdapter.Builder()
.setEndpoint(server.toString())
.setErrorHandler(this)
.setProfiler(this)
.setLogLevel(BuildConfig.DEBUG ? RestAdapter.LogLevel.FULL : RestAdapter.LogLevel.NONE)
.setRequestInterceptor(requestInterceptor)
.setConverter(new GsonConverter(gson))
.build();
this.apiService = adapter.create(ApiService.class);
2015-06-20 18:46:53 +00:00
2015-11-29 20:10:32 +00:00
server = new Server(cfg.getAddress(), false);
adapter = new RestAdapter.Builder()
.setEndpoint(server.toString())
.setErrorHandler(this)
.setProfiler(this)
.setLogLevel(BuildConfig.DEBUG ? RestAdapter.LogLevel.FULL : RestAdapter.LogLevel.NONE)
.setRequestInterceptor(requestInterceptor)
.setConverter(new GsonConverter(gson))
.build();
this.inAppPurchasesService = adapter.create(InAppPurchasesApiService.class);
}
2015-11-08 18:45:05 +00:00
private static final TypeAdapter<Boolean> booleanAsIntAdapter = new TypeAdapter<Boolean>() {
@Override
public void write(JsonWriter out, Boolean value) throws IOException {
if (value == null) {
out.nullValue();
} else {
out.value(value);
}
}
@Override
public Boolean read(JsonReader in) throws IOException {
JsonToken peek = in.peek();
switch (peek) {
case BOOLEAN:
return in.nextBoolean();
case NULL:
in.nextNull();
return null;
case NUMBER:
return in.nextInt() != 0;
case STRING:
return Boolean.parseBoolean(in.nextString());
default:
throw new IllegalStateException("Expected BOOLEAN or NUMBER but was " + peek);
}
}
};
2015-06-20 18:46:53 +00:00
2015-08-10 13:56:52 +00:00
public void createNewTask(Task item, Callback cb) {
2015-11-08 18:45:05 +00:00
this.apiService.createItem(item, cb);
}
public void retrieveUser(final HabitRPGUserCallback callback) {
new Thread(new Runnable() {
public void run() {
apiService.getUser(callback);
}
}).start();
2015-11-08 18:45:05 +00:00
}
2015-11-08 18:45:05 +00:00
public void updateTaskDirection(String id, TaskDirection direction, TaskScoringCallback callback) {
this.apiService.postTaskDirection(id, direction.toString(), callback);
2015-11-08 18:45:05 +00:00
}
2015-12-07 20:49:49 +00:00
public void registerUser(String username, String email, String password, String confirmPassword, Callback<UserAuthResponse> callback) {
UserAuth auth = new UserAuth();
auth.setUsername(username);
auth.setPassword(password);
auth.setConfirmPassword(confirmPassword);
auth.setEmail(email);
this.apiService.registerUser(auth, callback);
2015-11-08 18:45:05 +00:00
}
public void connectUser(String username, String password, Callback<UserAuthResponse> callback) {
UserAuth auth = new UserAuth();
auth.setUsername(username);
auth.setPassword(password);
2015-06-20 18:46:53 +00:00
this.apiService.connectLocal(auth, callback);
2015-11-08 18:45:05 +00:00
}
2015-11-13 19:33:26 +00:00
public void connectSocial(String userId, String accessToken, Callback<UserAuthResponse> callback) {
UserAuthSocial auth = new UserAuthSocial();
auth.setNetwork("facebook");
UserAuthSocialTokens authResponse = new UserAuthSocialTokens();
authResponse.setClient_id(userId);
authResponse.setAccess_token(accessToken);
auth.setAuthResponse(authResponse);
this.apiService.connectSocial(auth, callback);
}
2015-11-08 18:45:05 +00:00
public void updateTask(Task item, Callback cb) {
this.apiService.updateTask(item.getId(), item, cb);
}
public class ErrorResponse{
public String err;
}
public class ErrorListResponse {
public List<String> err;
}
2015-06-20 18:46:53 +00:00
@Override
public Throwable handleError(RetrofitError cause) {
2015-11-08 18:45:05 +00:00
if (cause.getKind().equals(RetrofitError.Kind.NETWORK)) {
//It also handles timeouts
showConnectionProblemDialog(R.string.network_error_no_network_body);
2015-11-18 12:05:38 +00:00
return cause;
} else if (cause.getKind().equals(RetrofitError.Kind.HTTP)) {
retrofit.client.Response response = cause.getResponse();
ErrorResponse res = null;
try {
res = (ErrorResponse) cause.getBodyAs(ErrorResponse.class) ;
} catch (RuntimeException e) {
//Can cause errors when error is a list and not a string
ErrorListResponse resList = (ErrorListResponse) cause.getBodyAs(ErrorListResponse.class);
if (resList.err != null && resList.err.size() >= 1) {
res = new ErrorResponse();
res.err = resList.err.get(0);
}
}
int status = response.getStatus();
2015-11-18 12:05:38 +00:00
if (status == 401) {
if(res != null && res.err != null && !res.err.isEmpty()) {
showConnectionProblemDialog("", res.err);
} else {
showConnectionProblemDialog(R.string.authentication_error_title, R.string.authentication_error_body);
}
2015-11-18 12:05:38 +00:00
return cause;
} else if (status >= 500 && status < 600) {
this.showConnectionProblemDialog(R.string.internal_error_api);
2015-11-18 12:05:38 +00:00
return cause;
} else if (status == 404 && cause.getUrl().endsWith("party/chat")) {
return cause;
2015-11-18 12:05:38 +00:00
}
}
this.showConnectionProblemDialog(R.string.internal_error_api);
2015-11-18 12:05:38 +00:00
return cause;
2015-06-20 18:46:53 +00:00
}
private void showConnectionProblemDialog(final int resourceMessageString) {
showConnectionProblemDialog(R.string.network_error_title, resourceMessageString);
2015-11-18 12:05:38 +00:00
}
private void showConnectionProblemDialog(final int resourceTitleString, final int resourceMessageString) {
Activity currentActivity = HabiticaApplication.currentActivity;
if (currentActivity != null) {
showConnectionProblemDialog(currentActivity.getString(resourceTitleString), currentActivity.getString(resourceMessageString));
}
}
private void showConnectionProblemDialog(final String resourceTitleString, final String resourceMessageString){
HabiticaApplication.currentActivity.runOnUiThread(new Runnable() {
public void run() {
if (!(HabiticaApplication.currentActivity).isFinishing()) {
AlertDialog.Builder builder = new AlertDialog.Builder(HabiticaApplication.currentActivity)
.setTitle(resourceTitleString)
.setMessage(resourceMessageString)
.setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
});
if (!resourceTitleString.isEmpty()) {
builder.setIcon(R.drawable.ic_warning_black);
}
builder.show();
}
}
});
}
2015-06-20 18:46:53 +00:00
@Override
public Object beforeCall() {
return null;
}
@Override
public void afterCall(RequestInformation requestInfo, long elapsedTime, int statusCode, Object beforeCallData) {
}
public void toggleSleep(Callback<Void> cb){
apiService.sleep(cb);
}
public void reviveUser(HabitRPGUserCallback cb) {
apiService.revive(cb);
}
2015-11-29 20:10:32 +00:00
public PurchaseValidationResult validatePurchase(PurchaseValidationRequest request)
{
return inAppPurchasesService.validatePurchase(cfg.getUser(), cfg.getApi(), request);
}
}