/* 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.fragment; import java.util.ArrayList; import java.util.Iterator; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.text.Html; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.animation.AnimationUtils; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; import com.prasanna.android.stacknetwork.PageSelectAdapter; import com.prasanna.android.stacknetwork.R; import com.prasanna.android.stacknetwork.fragment.CommentFragment.OnCommentChangeListener; import com.prasanna.android.stacknetwork.fragment.CommentFragment.OnShowCommentsListener; import com.prasanna.android.stacknetwork.model.Answer; import com.prasanna.android.stacknetwork.model.Comment; import com.prasanna.android.stacknetwork.model.Question; import com.prasanna.android.stacknetwork.service.AnswersIntentService; import com.prasanna.android.stacknetwork.utils.AppUtils; import com.prasanna.android.stacknetwork.utils.DateTimeUtils; import com.prasanna.android.stacknetwork.utils.MarkdownFormatter; import com.prasanna.android.stacknetwork.utils.QuestionsCache; import com.prasanna.android.stacknetwork.utils.StackXQuickActionMenu; import com.prasanna.android.stacknetwork.utils.StringConstants; import com.prasanna.android.utils.LogWrapper; import com.prasanna.android.views.HtmlTextView; public class AnswerFragment extends AbstractVotableFragment implements OnCommentChangeListener { private static final String TAG = AnswerFragment.class.getSimpleName(); private FrameLayout parentLayout; private LinearLayout answerBodyLayout; private Answer answer; private RelativeLayout answerMetaInfoLayout; private ImageView answerCtxMenuImageView; private PageSelectAdapter pageSelectAdapter; private StackXQuickActionMenu quickActionMenu; private OnShowCommentsListener onShowCommentsListener; private boolean forMyQuestion = false; private boolean questionHasAcceptedAnswer = false; private TextView scoreTextView; private ImageView acceptAnswerImage; public static AnswerFragment newFragment(Answer answer, PageSelectAdapter pageSelectAdapter, boolean forMyQuestion, boolean questionHasAcceptedAnswer) { AnswerFragment answerFragment = new AnswerFragment(); answerFragment.setRetainInstance(true); answerFragment.answer = answer; answerFragment.pageSelectAdapter = pageSelectAdapter; answerFragment.forMyQuestion = forMyQuestion; answerFragment.questionHasAcceptedAnswer = questionHasAcceptedAnswer; return answerFragment; } @Override public void onAttach(Activity activity) { super.onAttach(activity); if (!(activity instanceof OnShowCommentsListener)) throw new IllegalArgumentException("Activity must implement OnShowCommentsListener"); onShowCommentsListener = (OnShowCommentsListener) activity; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (parentLayout == null) { parentLayout = (FrameLayout) inflater.inflate(R.layout.answer, null); answerBodyLayout = (LinearLayout) parentLayout.findViewById(R.id.answerBody); answerMetaInfoLayout = (RelativeLayout) parentLayout.findViewById(R.id.answerMetaInfo); answerCtxMenuImageView = (ImageView) answerMetaInfoLayout.findViewById(R.id.answerOptionsContextMenu); } if (savedInstanceState != null) answer = (Answer) savedInstanceState.getSerializable(StringConstants.ANSWER); return parentLayout; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); registerForContextMenu(answerCtxMenuImageView); } @Override public void onResume() { super.onResume(); displayAnswer(); } @Override public void onSaveInstanceState(Bundle outState) { if (answer != null) outState.putSerializable(StringConstants.ANSWER, answer); super.onSaveInstanceState(outState); } private void displayAnswer() { if (answer != null) { if (quickActionMenu == null) quickActionMenu = initQuickActionMenu(); scoreTextView = (TextView) answerMetaInfoLayout.findViewById(R.id.score); scoreTextView.setText(AppUtils.formatNumber(answer.score)); prepareUpDownVote(answer, parentLayout, AnswersIntentService.class); String acceptRate = answer.owner.acceptRate > 0 ? (answer.owner.acceptRate + "%, ") : ""; TextView textView = (TextView) answerMetaInfoLayout.findViewById(R.id.answerAuthor); textView.setText(getOwnerDisplayText(acceptRate)); displayNumComments(); prepareAcceptAnswer(); final ImageView questionMarkImageView = (ImageView) parentLayout.findViewById(R.id.goBackToQ); showQuestionTitleOnClick(questionMarkImageView); gotoQuestionPageOnLongClick(questionMarkImageView); setupQuickActionMenuForAnswer(); answerBodyLayout.removeAllViews(); for (View answerView : MarkdownFormatter.parse(getActivity(), answer.body)) answerBodyLayout.addView(answerView); } } private void prepareAcceptAnswer() { if (forMyQuestion) { prepareAcceptAnswerForMyQuestion(); } else { if (answer.accepted) { ImageView acceptedAnswerImage = (ImageView) parentLayout.findViewById(R.id.answerAccepted); acceptedAnswerImage.setVisibility(View.VISIBLE); } } } private void prepareAcceptAnswerForMyQuestion() { acceptAnswerImage = (ImageView) parentLayout.findViewById(R.id.acceptAnswer); acceptAnswerImage.setVisibility(View.VISIBLE); if (questionHasAcceptedAnswer && answer.accepted) { acceptAnswerImage.setImageResource(R.drawable.answer_accepted); } acceptAnswerImage.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(getActivity().getApplicationContext(), AnswersIntentService.class); if (questionHasAcceptedAnswer) intent.putExtra(StringConstants.ACTION, AnswersIntentService.ACCEPT_UNDO); else intent.putExtra(StringConstants.ACTION, AnswersIntentService.ACCEPT); intent.putExtra(StringConstants.RESULT_RECEIVER, resultReceiver); intent.putExtra(StringConstants.SITE, getActivity().getIntent().getStringExtra(StringConstants.SITE)); intent.putExtra(StringConstants.ID, answer.id); getActivity().setProgressBarIndeterminateVisibility(true); getActivity().startService(intent); } }); } private StackXQuickActionMenu initQuickActionMenu() { StackXQuickActionMenu quickActionMenu = new StackXQuickActionMenu(getActivity()); quickActionMenu.addCommentsItem(onShowCommentsListener); if (answer.owner.isRegistered()) { quickActionMenu.addUserProfileItem(answer.owner.id, Html.fromHtml(answer.owner.displayName).toString()); } return quickActionMenu; } private void displayNumComments() { TextView textView = (TextView) parentLayout.findViewById(R.id.answerCommentsCount); if (answer.comments != null && !answer.comments.isEmpty()) { textView.setText(getString(R.string.comments) + ":" + String.valueOf(answer.comments.size())); textView.setVisibility(View.VISIBLE); } else textView.setVisibility(View.GONE); } private void setupQuickActionMenuForAnswer() { answerCtxMenuImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { quickActionMenu.build().show(v); } }); } private void showQuestionTitleOnClick(final ImageView questionMarkImageView) { questionMarkImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { setupQuestionTitleAction(questionMarkImageView); } }); } private void gotoQuestionPageOnLongClick(final ImageView questionMarkImageView) { questionMarkImageView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { pageSelectAdapter.selectQuestionPage(); return true; } }); } private void setupQuestionTitleAction(final ImageView questionViewAction) { final LinearLayout layout = (LinearLayout) parentLayout.findViewById(R.id.qTitleLayout); layout.startAnimation(AnimationUtils.loadAnimation(getActivity(), android.R.anim.slide_in_left)); HtmlTextView tv = (HtmlTextView) parentLayout.findViewById(R.id.qTitle); tv.setText(answer.title); layout.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { gotoQuestionPage(questionViewAction, layout); } }); ImageView ivCloseAction = (ImageView) parentLayout.findViewById(R.id.questionCloseAction); ivCloseAction.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { layout.startAnimation(AnimationUtils.loadAnimation(getActivity(), android.R.anim.slide_out_right)); layout.setVisibility(View.GONE); questionViewAction.setVisibility(View.VISIBLE); } }); layout.setVisibility(View.VISIBLE); questionViewAction.setVisibility(View.GONE); } private String getOwnerDisplayText(String acceptRate) { String ownerText = DateTimeUtils.getElapsedDurationSince(answer.creationDate) + " by " + Html.fromHtml(answer.owner.getDisplayName()); if (answer.owner.isRegistered()) { ownerText += " [" + acceptRate + AppUtils.formatReputation(answer.owner.reputation) + "]"; } return ownerText; } private void gotoQuestionPage(final ImageView questionViewAction, final LinearLayout layout) { pageSelectAdapter.selectQuestionPage(); layout.setVisibility(View.GONE); questionViewAction.setVisibility(View.VISIBLE); } @Override public void onCommentUpdate(Comment comment) { if (answer.comments != null) { LogWrapper.d(TAG, "Removing comment: " + comment.id); Iterator<Comment> iterator = answer.comments.iterator(); while (iterator.hasNext()) { if (iterator.next().id == comment.id) { LogWrapper.d(TAG, "comment " + comment.id + " removed"); removeQuestionFromCache(); break; } } } } @Override public void onCommentDelete(long commentId) { if (answer.comments != null) { LogWrapper.d(TAG, "Removing comment: " + commentId); Iterator<Comment> iterator = answer.comments.iterator(); while (iterator.hasNext()) { if (iterator.next().id == commentId) { LogWrapper.d(TAG, "comment " + commentId + " removed"); removeQuestionFromCache(); iterator.remove(); break; } } } displayNumComments(); } private void removeQuestionFromCache() { if (QuestionsCache.getInstance().containsKey(answer.questionId)) QuestionsCache.getInstance().remove(answer.questionId); } @Override public void onCommentAdd(Comment comment) { // One reply to comments come here, add new comment is directly handled // by QuestionAcitivy. So no need to initialize comments if null because // it can never be null here. if (comment != null) { if (answer.comments == null) answer.comments = new ArrayList<Comment>(); answer.comments.add(comment); updateCacheWithNewCommentIfExists(comment); } displayNumComments(); } private void updateCacheWithNewCommentIfExists(Comment comment) { if (QuestionsCache.getInstance().containsKey(answer.questionId)) { Question cachedQuestion = QuestionsCache.getInstance().get(answer.questionId); for (Answer answer : cachedQuestion.answers) { if (answer.id == comment.post_id) { if (answer.comments == null) answer.comments = new ArrayList<Comment>(); if (!answer.comments.contains(comment)) { answer.comments.add(comment); QuestionsCache.getInstance().add(answer.questionId, cachedQuestion); } break; } } } } @Override public void onReceiveResult(int resultCode, Bundle resultData) { getActivity().setProgressBarIndeterminateVisibility(false); switch (resultCode) { case AnswersIntentService.RESULT_CODE_ACCEPT_ANS_SUCCESS: questionHasAcceptedAnswer = true; answer.accepted = true; acceptAnswerImage.setImageResource(R.drawable.answer_accepted); break; case AnswersIntentService.RESULT_CODE_ACCEPT_ANS_UNDO_SUCCESS: questionHasAcceptedAnswer = false; answer.accepted = false; acceptAnswerImage.setImageResource(R.drawable.answer_accept); break; default: super.onReceiveResult(resultCode, resultData); break; } } @Override protected void onScoreChange(int newScore) { scoreTextView.setText(AppUtils.formatNumber(newScore)); } }