/* Copyright (C) 2014 Prasanna Thirumalai This file is part of StackX. StackX is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. StackX is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with StackX. If not, see <http://www.gnu.org/licenses/>. */ package com.prasanna.android.stacknetwork.service; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import com.prasanna.android.http.AbstractHttpException; import com.prasanna.android.http.ClientException; import com.prasanna.android.http.HttpContentTypes; import com.prasanna.android.http.HttpHeaderParams; import com.prasanna.android.stacknetwork.model.Answer; import com.prasanna.android.stacknetwork.model.Comment; import com.prasanna.android.stacknetwork.model.Post.PostType; import com.prasanna.android.stacknetwork.model.Question; import com.prasanna.android.stacknetwork.model.SearchCriteria; import com.prasanna.android.stacknetwork.model.StackXPage; import com.prasanna.android.stacknetwork.utils.AppUtils; import com.prasanna.android.stacknetwork.utils.JSONObjectWrapper; import com.prasanna.android.stacknetwork.utils.JsonFields; import com.prasanna.android.stacknetwork.utils.OperatingSite; import com.prasanna.android.stacknetwork.utils.StackUri; import com.prasanna.android.stacknetwork.utils.StackUri.Order; import com.prasanna.android.stacknetwork.utils.StringConstants; import com.prasanna.android.utils.LogWrapper; public class QuestionServiceHelper extends AbstractBaseServiceHelper { private static final String TAG = QuestionServiceHelper.class.getSimpleName(); private static final QuestionServiceHelper questionService = new QuestionServiceHelper(); protected QuestionServiceHelper() { } public static QuestionServiceHelper getInstance() { return questionService; } public String getQuestionBodyForId(long id, String site) { String restEndPoint = "questions/" + id; String questionBody = null; Map<String, String> queryParams = getDefaultQueryParams(site); JSONObjectWrapper questionJsonResponse = executeHttpGetRequest(restEndPoint, queryParams); if (questionJsonResponse != null) { try { JSONArray jsonArray = questionJsonResponse.getJSONArray(JsonFields.ITEMS); if (jsonArray != null && jsonArray.length() == 1) { JSONObjectWrapper jsonObject = JSONObjectWrapper.wrap(jsonArray.getJSONObject(0)); questionBody = jsonObject.getString(JsonFields.Question.BODY); } } catch (JSONException e) { LogWrapper.d(TAG, e.getMessage()); } } return questionBody; } public String getQuestionBodyForId(long id) { return getQuestionBodyForId(id, OperatingSite.getSite().apiSiteParameter); } public ArrayList<Answer> getAnswersForQuestion(long id, String site, int page) { ArrayList<Answer> answers = new ArrayList<Answer>(); String restEndPoint = "questions/" + id + "/answers"; Map<String, String> queryParams = getDefaultQueryParams(site); queryParams.put(StackUri.QueryParams.PAGE, String.valueOf(page)); queryParams.put(StackUri.QueryParams.PAGE_SIZE, String.valueOf(StackUri.QueryParamDefaultValues.ANSWERS_PAGE_SIZE)); queryParams.put(StackUri.QueryParams.SORT, StackUri.Sort.VOTES); JSONObjectWrapper answersJson = executeHttpGetRequest(restEndPoint, queryParams); if (answersJson != null) { try { JSONArray jsonArray = answersJson.getJSONArray(JsonFields.ITEMS); if (jsonArray != null) { for (int i = 0; i < jsonArray.length(); i++) { JSONObjectWrapper jsonObject = JSONObjectWrapper.wrap(jsonArray.getJSONObject(i)); Answer answer = getSerializedAnswerObject(jsonObject); if (answer.accepted && answers.size() > 0) answers.add(0, answer); else answers.add(answer); } if (!answers.isEmpty()) getCommentsForAnswers(answers); } } catch (JSONException e) { LogWrapper.d(TAG, e.getMessage()); } } return answers; } private void getCommentsForAnswers(ArrayList<Answer> answers) { try { StringBuilder stringBuilder = new StringBuilder(); HashMap<Long, Answer> idAnswerMap = new HashMap<Long, Answer>(); for (Answer answer : answers) { stringBuilder.append(answer.id).append(";"); idAnswerMap.put(answer.id, answer); } stringBuilder.deleteCharAt(stringBuilder.length() - 1); getCommensAndUpdateAnswer(stringBuilder.toString(), idAnswerMap); } catch (AbstractHttpException e) { LogWrapper.e(TAG, e.getMessage()); } } private void getCommensAndUpdateAnswer(String answerIds, HashMap<Long, Answer> idAnswerMap) { boolean hasMore = true; int page = 1; while (hasMore) { StackXPage<Comment> commentsPage = getComments(StringConstants.ANSWERS, answerIds, page++); if (commentsPage != null && commentsPage.items != null) { for (Comment comment : commentsPage.items) { if (idAnswerMap.containsKey(comment.post_id)) { if (idAnswerMap.get(comment.post_id).comments == null) idAnswerMap.get(comment.post_id).comments = new ArrayList<Comment>(); idAnswerMap.get(comment.post_id).comments.add(comment); } } hasMore = commentsPage.hasMore; } else hasMore = false; } } public StackXPage<Comment> getComments(String parent, String site, String parentIds, int page) { StackXPage<Comment> commentsPage = null; String restEndPoint = parent + "/" + parentIds + "/comments"; Map<String, String> queryParams = getDefaultQueryParams(site); queryParams.put(StackUri.QueryParams.PAGE, String.valueOf(page)); queryParams.put(StackUri.QueryParams.SORT, StackUri.Sort.CREATION); queryParams.put(StackUri.QueryParams.ORDER, Order.ASC); queryParams.put(StackUri.QueryParams.FILTER, StackUri.QueryParamDefaultValues.COMMENT_FILTER); JSONObjectWrapper commentsJsonResponse = executeHttpGetRequest(restEndPoint, queryParams); if (commentsJsonResponse != null) { try { getPageInfo(commentsJsonResponse, commentsPage); JSONArray jsonArray = commentsJsonResponse.getJSONArray(JsonFields.ITEMS); for (int count = 0; count < jsonArray.length(); count++) { JSONObject jsonObject = jsonArray.getJSONObject(count); if (commentsPage == null) { commentsPage = new StackXPage<Comment>(); commentsPage.items = new ArrayList<Comment>(); } commentsPage.items.add(Comment.parse(JSONObjectWrapper.wrap(jsonObject))); } } catch (JSONException e) { LogWrapper.d(TAG, e.getMessage()); } } return commentsPage; } public StackXPage<Comment> getComments(String parent, String parentIds, int page) { return getComments(parent, OperatingSite.getSite().apiSiteParameter, parentIds, page); } public StackXPage<Question> search(String query, int page) { String restEndPoint = "search"; Map<String, String> queryParams = AppUtils.getDefaultQueryParams(); queryParams.put(StackUri.QueryParams.SITE, OperatingSite.getSite().apiSiteParameter); queryParams.put(StackUri.QueryParams.IN_TITLE, query); queryParams.put(StackUri.QueryParams.PAGE, String.valueOf(page)); queryParams.put(StackUri.QueryParams.PAGE_SIZE, String.valueOf(StackUri.QueryParamDefaultValues.PAGE_SIZE)); JSONObjectWrapper questionsJsonResponse = executeHttpGetRequest(restEndPoint, queryParams); return getQuestionModel(questionsJsonResponse); } public StackXPage<Question> advancedSearch(SearchCriteria searchCriteria) { String restEndPoint = "search/advanced"; Map<String, String> queryParams = AppUtils.getDefaultQueryParams(); queryParams.put(StackUri.QueryParams.SITE, OperatingSite.getSite().apiSiteParameter); queryParams.putAll(searchCriteria.getMap()); return getQuestionModel(executeHttpGetRequest(restEndPoint, queryParams)); } @Override protected String getLogTag() { return TAG; } public Question getQuestionFullDetails(long id, String site) { String restEndPoint = "questions/" + id; Question question = null; Map<String, String> queryParams = getDefaultQueryParams(site); JSONObjectWrapper questionJsonResponse = executeHttpGetRequest(restEndPoint, queryParams); if (questionJsonResponse != null) { try { JSONArray jsonArray = questionJsonResponse.getJSONArray(JsonFields.ITEMS); if (jsonArray != null && jsonArray.length() == 1) { JSONObjectWrapper jsonObject = JSONObjectWrapper.wrap(jsonArray.getJSONObject(0)); question = getSerializedQuestionObject(jsonObject); question.body = jsonObject.getString(JsonFields.Question.BODY); } } catch (JSONException e) { LogWrapper.d(TAG, e.getMessage()); } } return question; } public Question getQuestionFullDetails(long id) { return getQuestionFullDetails(id, OperatingSite.getSite().apiSiteParameter); } public StackXPage<Question> getAllQuestions(String sort, int page) { if (sort == null) sort = StackUri.Sort.ACTIVITY; Map<String, String> queryParams = AppUtils.getDefaultQueryParams(); queryParams.put(StackUri.QueryParams.ORDER, StackUri.QueryParamDefaultValues.ORDER); queryParams.put(StackUri.QueryParams.SORT, sort); queryParams.put(StackUri.QueryParams.SITE, OperatingSite.getSite().apiSiteParameter); queryParams.put(StackUri.QueryParams.PAGE, String.valueOf(page)); queryParams.put(StackUri.QueryParams.PAGE_SIZE, String.valueOf(StackUri.QueryParamDefaultValues.PAGE_SIZE)); return getQuestionPage(StringConstants.QUESTIONS, queryParams); } public StackXPage<Question> getFeatured(int page) { Map<String, String> queryParams = AppUtils.getDefaultQueryParams(); queryParams.put(StackUri.QueryParams.ORDER, StackUri.QueryParamDefaultValues.ORDER); queryParams.put(StackUri.QueryParams.SORT, StackUri.Sort.ACTIVITY); queryParams.put(StackUri.QueryParams.SITE, OperatingSite.getSite().apiSiteParameter); queryParams.put(StackUri.QueryParams.PAGE, String.valueOf(page)); queryParams.put(StackUri.QueryParams.PAGE_SIZE, String.valueOf(StackUri.QueryParamDefaultValues.PAGE_SIZE)); return getQuestionPage("questions/featured", queryParams); } public StackXPage<Question> getRelatedQuestions(long questionId, int page) { String restEndPoint = "questions/" + questionId + "/related"; Map<String, String> queryParams = AppUtils.getDefaultQueryParams(); queryParams.put(StackUri.QueryParams.ORDER, StackUri.QueryParamDefaultValues.ORDER); queryParams.put(StackUri.QueryParams.SORT, StackUri.Sort.ACTIVITY); queryParams.put(StackUri.QueryParams.SITE, OperatingSite.getSite().apiSiteParameter); queryParams.put(StackUri.QueryParams.PAGE, String.valueOf(page)); queryParams.put(StackUri.QueryParams.PAGE_SIZE, String.valueOf(StackUri.QueryParamDefaultValues.PAGE_SIZE)); return getQuestionPage(restEndPoint, queryParams); } public StackXPage<Question> getFaqForTag(String tag, int page) { if (tag != null) { String restEndPoint = "tags/" + tag + "/faq"; Map<String, String> queryParams = AppUtils.getDefaultQueryParams(); queryParams.put(StackUri.QueryParams.SITE, OperatingSite.getSite().apiSiteParameter); queryParams.put(StackUri.QueryParams.PAGE, String.valueOf(page)); queryParams.put(StackUri.QueryParams.PAGE_SIZE, String.valueOf(StackUri.QueryParamDefaultValues.PAGE_SIZE)); return getQuestionPage(restEndPoint, queryParams); } return null; } public StackXPage<Question> getQuestionsForTag(String tag, String sort, int page) { if (sort == null) sort = StackUri.Sort.ACTIVITY; if (tag != null) { String restEndPoint = "search/advanced"; Map<String, String> queryParams = AppUtils.getDefaultQueryParams(); queryParams.put(StackUri.QueryParams.SITE, OperatingSite.getSite().apiSiteParameter); queryParams.put(StackUri.QueryParams.TAGGED, tag); queryParams.put(StackUri.QueryParams.SORT, sort); queryParams.put(StackUri.QueryParams.ORDER, StackUri.QueryParamDefaultValues.ORDER); queryParams.put(StackUri.QueryParams.PAGE, String.valueOf(page)); queryParams.put(StackUri.QueryParams.PAGE_SIZE, String.valueOf(StackUri.QueryParamDefaultValues.PAGE_SIZE)); return getQuestionPage(restEndPoint, queryParams); } return null; } public StackXPage<Question> getSimilar(String title, int page) { if (title != null) { String restEndPoint = "similar"; Map<String, String> queryParams = AppUtils.getDefaultQueryParams(); queryParams.put(StackUri.QueryParams.SITE, OperatingSite.getSite().apiSiteParameter); queryParams.put(StackUri.QueryParams.TITLE, title); queryParams.put(StackUri.QueryParams.SORT, StackUri.Sort.RELEVANCE); queryParams.put(StackUri.QueryParams.PAGE, String.valueOf(page)); queryParams.put(StackUri.QueryParams.PAGE_SIZE, String.valueOf(StackUri.QueryParamDefaultValues.PAGE_SIZE)); return getQuestionPage(restEndPoint, queryParams); } return null; } private StackXPage<Question> getQuestionPage(String restEndPoint, Map<String, String> queryParams) { JSONObjectWrapper questionsJsonResponse = executeHttpGetRequest(restEndPoint, queryParams); if (questionsJsonResponse != null) return getQuestionModel(questionsJsonResponse); return null; } public Answer getAnswer(long id) { return getAnswer(id, OperatingSite.getSite().apiSiteParameter); } public Answer getAnswer(long id, String site) { String restEndPoint = "answers/" + id; JSONObjectWrapper jsonObjectWrapper = executeHttpGetRequest(restEndPoint, getDefaultQueryParams(site)); if (jsonObjectWrapper != null) { try { JSONArray jsonArray = jsonObjectWrapper.getJSONArray(JsonFields.ITEMS); if (jsonArray != null && jsonArray.length() == 1) { JSONObjectWrapper jsonObject = JSONObjectWrapper.wrap(jsonArray.getJSONObject(0)); Answer answer = getSerializedAnswerObject(jsonObject); answer.body = jsonObject.getString(JsonFields.Question.BODY); return answer; } } catch (JSONException e) { LogWrapper.d(TAG, e.getMessage()); } } return null; } public int upvote(final PostType postType, final String site, final long id) { return score(urlEncodedPost(postType.getValue() + "s/" + id + "/upvote")); } public int downvote(final PostType postType, final String site, final long id) { return score(urlEncodedPost(postType.getValue() + "s/" + id + "/downvote")); } public int undoUpvote(final PostType postType, final String site, final long id) { return score(urlEncodedPost(postType.getValue() + "s/" + id + "/upvote/undo")); } public int undoDownvote(final PostType postType, final String site, final long id) { return score(urlEncodedPost(postType.getValue() + "s/" + id + "/downvote/undo")); } private int score(JSONObjectWrapper jsonObjectWrapper) { if (jsonObjectWrapper != null) { try { JSONArray jsonArray = jsonObjectWrapper.getJSONArray(JsonFields.ITEMS); if (jsonArray != null && jsonArray.length() == 1) { JSONObjectWrapper jsonObject = JSONObjectWrapper.wrap(jsonArray.getJSONObject(0)); return jsonObject.getInt(JsonFields.Answer.SCORE); } } catch (JSONException e) { LogWrapper.d(TAG, e.getMessage()); } } return Integer.MIN_VALUE; } public void favorite(final String site, final long id) { urlEncodedPost("questions/" + id + "/favorite"); } public void undoFavorite(final String site, final long id) { urlEncodedPost("questions/" + id + "/favorite/undo"); } public void acceptAnswer(final long answerId, final String site) { urlEncodedPost("answers/" + answerId + "/accept"); } public void undoAcceptAnswer(final long answerId, final String site) { urlEncodedPost("answers/" + answerId + "/accept/undo"); } private JSONObjectWrapper urlEncodedPost(String restEndPoint) { Map<String, String> requestHeaders = new HashMap<String, String>(); requestHeaders.put(HttpHeaderParams.CONTENT_TYPE, HttpContentTypes.APPLICATION_FROM_URL_ENCODED); requestHeaders.put(HttpHeaderParams.ACCEPT, HttpContentTypes.APPLICATION_JSON); try { return executeHttpPostRequest(restEndPoint, requestHeaders, null, new UrlEncodedFormEntity( getBasicNameValuePartListForPost())); } catch (UnsupportedEncodingException e) { throw new ClientException(ClientException.ClientErrorCode.INVALID_ENCODING); } } }