package com.airlocksoftware.hackernews.loader; import java.io.IOException; import android.util.Log; import com.crashlytics.android.Crashlytics; import org.jsoup.Connection; import org.jsoup.Connection.Method; import org.jsoup.Connection.Response; import android.content.Context; import android.support.v4.content.AsyncTaskLoader; import com.airlocksoftware.hackernews.activity.SubmitActivity.SendMode; import com.airlocksoftware.hackernews.data.ConnectionManager; import com.airlocksoftware.hackernews.data.UserPrefs; import com.airlocksoftware.hackernews.model.NewStoryResult; public class SubmitLoader extends AsyncTaskLoader<NewStoryResult> { final String mSelfText, mUrl, mTitle; final SendMode mSendMode; // Constants private static final String TAG = SubmitLoader.class.getSimpleName(); private static final String REPLY_EXTENSION = "/r"; // Don't care about HTTP vs HTTPS private static final String MATCH_NEWEST_PAGE = "://news.ycombinator.com/newest"; // Match substrings for error messages private static final String MATCH_POST_TOO_FAST = "submitting too fast"; // Match duplicate posts private static final String MATCH_DUPLICATE_PAGE = "item?id="; private enum ErrorMessage { POST_SUCCESS, POST_TOO_FAST, POST_DUPLICATE } public ErrorMessage mErrorMessage = ErrorMessage.POST_SUCCESS; public SubmitLoader(Context context, SendMode sendMode, String title, String content) { super(context); mSendMode = sendMode; switch (mSendMode) { case URL: mSelfText = ""; mTitle = title; mUrl = content; break; case SELF_TEXT: mSelfText = content; mTitle = title; mUrl = ""; break; default: mSelfText = mUrl = mTitle = null; break; } } @Override public NewStoryResult loadInBackground() { if (mSendMode == SendMode.EMPTY) return NewStoryResult.EMPTY; UserPrefs data = new UserPrefs(getContext()); try { String replyFnid = getReplyFnid(data); Connection.Response response = sendSubmission(data, replyFnid); validateResponse(response); switch (mErrorMessage) { case POST_SUCCESS: return NewStoryResult.SUCCESS; case POST_DUPLICATE: return NewStoryResult.POST_DUPLICATE; case POST_TOO_FAST: return NewStoryResult.POST_TOO_FAST; default: return NewStoryResult.FAILURE; } } catch (Exception e) { // any exception here probably means we have NO_CONNECTION or there's an error with the website. e.printStackTrace(); } return NewStoryResult.FAILURE; } private boolean validateResponse(Connection.Response res) { if (res == null) return false; if (res.headers() != null && res.headers().get("Location") != null) { Crashlytics.setString("SubmitLoader :: responseLocationHeader", res.headers().get("Location")); } if (res.url() != null) { Crashlytics.setString("SubmitLoader :: responseURL", res.url().toString()); } // This used to work if (res.statusCode() == 302 && res.headers().get("Location").equals("newest")) { mErrorMessage = ErrorMessage.POST_SUCCESS; // This currently works } else if (res.statusCode() == 200 && res.url().toString().contains(MATCH_NEWEST_PAGE)) { mErrorMessage = ErrorMessage.POST_SUCCESS; // If you post too fast, HN complains } else if (res.body().contains(MATCH_POST_TOO_FAST)) { Crashlytics.setBool("SubmitLoader :: responsePostTooFast", true); mErrorMessage = ErrorMessage.POST_TOO_FAST; // If the URL contains 'item?id=', it's a duplicate post } else if (res.url() != null && res.url().toString().contains(MATCH_DUPLICATE_PAGE)) { Crashlytics.setBool("SubmitLoader :: responsePostDuplicate", true); mErrorMessage = ErrorMessage.POST_DUPLICATE; } // Only POST_SUCCESS is 100% clean and successful return mErrorMessage == ErrorMessage.POST_SUCCESS; } private Response sendSubmission(UserPrefs data, String replyFnid) throws IOException { return ConnectionManager.authConnect(REPLY_EXTENSION, data.getUserCookie()) .data("fnid", replyFnid) .data("t", mTitle) .data("u", mUrl) .data("x", mSelfText) .method(Method.POST) .execute(); } private String getReplyFnid(UserPrefs data) throws IOException { return ConnectionManager.authConnect(ConnectionManager.SUBMIT_URL, data.getUserCookie()) .get() .select("input[name=fnid]") .first() .attr("value"); } /** * Handles a request to start the Loader. */ @Override protected void onStartLoading() { forceLoad(); } }