/*
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.os.Bundle;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
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.Comment;
import com.prasanna.android.stacknetwork.model.Question;
import com.prasanna.android.stacknetwork.service.QuestionDetailsIntentService;
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.stacknetwork.utils.TagsViewBuilder;
import com.prasanna.android.utils.LogWrapper;
public class QuestionFragment extends AbstractVotableFragment implements OnCommentChangeListener {
private static final String TAG = QuestionFragment.class.getSimpleName();
private FrameLayout parentLayout;
private Question question;
private ImageView backIv;
private ImageView ctxMenuImage;
private StackXQuickActionMenu quickActionMenu;
private OnShowCommentsListener onShowCommentsListener;
private View questionTitleLayout;
private boolean backToAnswerFlash = false;
private String STR_VIEWS;
private String STR_COMMENTS;
private TextView scoreTextView;
private boolean bodyDisplayed = false;
public static QuestionFragment newFragment() {
QuestionFragment questionFragment = new QuestionFragment();
questionFragment.setRetainInstance(true);
questionFragment.setHasOptionsMenu(true);
return questionFragment;
}
@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) createView(inflater);
if (savedInstanceState != null) question = (Question) savedInstanceState.getSerializable(StringConstants.QUESTION);
return parentLayout;
}
private void createView(LayoutInflater inflater) {
parentLayout = (FrameLayout) inflater.inflate(R.layout.question, null);
questionTitleLayout = parentLayout.findViewById(R.id.questionTitleLayout);
ctxMenuImage = (ImageView) parentLayout.findViewById(R.id.questionOptionsContextMenu);
ctxMenuImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (quickActionMenu == null) quickActionMenu = initQuickActionMenu();
quickActionMenu.build().show(v);
}
});
backIv = (ImageView) parentLayout.findViewById(R.id.navigateBack);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
STR_VIEWS = getString(R.string.views);
STR_COMMENTS = getString(R.string.comments);
if (question != null) {
getActivity().getActionBar().setTitle(Html.fromHtml(question.title));
displayQuestion();
}
}
@Override
public void onResume() {
super.onResume();
displayQuestion();
}
@Override
public void onSaveInstanceState(Bundle outState) {
if (question != null) outState.putSerializable(StringConstants.QUESTION, question);
super.onSaveInstanceState(outState);
}
private void displayQuestion() {
if (question != null) {
if (questionTitleLayout.getVisibility() == View.INVISIBLE) questionTitleLayout.setVisibility(View.VISIBLE);
if (quickActionMenu == null) quickActionMenu = initQuickActionMenu();
scoreTextView = (TextView) parentLayout.findViewById(R.id.score);
scoreTextView.setText(AppUtils.formatNumber(question.score));
prepareUpDownVote(question, parentLayout, QuestionDetailsIntentService.class);
// setupTextViewForAnswerCount();
TextView textView = (TextView) parentLayout.findViewById(R.id.questionTitle);
textView.setText(Html.fromHtml(question.title));
if (question.bountyAmount > 0) {
textView = (TextView) parentLayout.findViewById(R.id.questionBountyAmount);
textView.setText(Html.fromHtml(question.title));
textView.setText("+" + String.valueOf(question.bountyAmount));
textView.setVisibility(View.VISIBLE);
}
String acceptRate = question.owner.acceptRate > 0 ? (question.owner.acceptRate + "%, ") : "";
textView = (TextView) parentLayout.findViewById(R.id.questionOwner);
textView.setText(getTimeAndOwnerDisplay(acceptRate));
textView = (TextView) parentLayout.findViewById(R.id.questionViews);
textView.setText(STR_VIEWS + ":" + AppUtils.formatNumber(question.viewCount));
displayNumComments();
displayBody(question.body);
TagsViewBuilder.buildView(getActivity(), (LinearLayout) parentLayout.findViewById(R.id.questionTags),
question.tags);
}
}
private StackXQuickActionMenu initQuickActionMenu() {
StackXQuickActionMenu quickActionMenu = new StackXQuickActionMenu(getActivity());
quickActionMenu.addCommentsItem(onShowCommentsListener).addUserProfileItem(question.owner.id,
Html.fromHtml(question.owner.getDisplayName()).toString());
return quickActionMenu.addSimilarQuestionsItem(question.title).addRelatedQuickActionItem(question.id)
.addEmailQuickActionItem(question.title, AppUtils.createEmailBody(question));
}
private String getTimeAndOwnerDisplay(String acceptRate) {
return DateTimeUtils.getElapsedDurationSince(question.creationDate) + " by "
+ Html.fromHtml(question.owner.getDisplayName()) + " [" + acceptRate
+ AppUtils.formatReputation(question.owner.reputation) + "]";
}
private void displayNumComments() {
if (question.comments != null) {
TextView textView = (TextView) parentLayout.findViewById(R.id.questionComments);
textView.setText(STR_COMMENTS + ":" + String.valueOf(question.comments.size()));
textView.setVisibility(question.comments.isEmpty() ? View.GONE : View.VISIBLE);
}
}
public void displayBody(String text) {
if (text != null && parentLayout != null) {
question.body = text;
final LinearLayout questionBodyLayout = (LinearLayout) parentLayout.findViewById(R.id.questionBody);
if (isAdded() && !bodyDisplayed) {
ArrayList<View> views = MarkdownFormatter.parse(getActivity(), question.body);
if (views != null) {
for (final View questionBodyTextView : views)
questionBodyLayout.addView(questionBodyTextView);
}
bodyDisplayed = true;
}
}
}
public void setQuestion(Question question) {
this.question = question;
}
public void setComments(ArrayList<Comment> comments) {
if (question != null) {
question.comments = comments;
if (parentLayout != null) displayNumComments();
}
}
public void setAndDisplay(Question question) {
setQuestion(question);
if (parentLayout != null) {
if (isAdded()) getActivity().getActionBar().setTitle(Html.fromHtml(question.title));
displayQuestion();
}
}
public void enableNavigationBack(OnClickListener clickListener) {
backIv.setVisibility(View.VISIBLE);
backIv.setOnClickListener(clickListener);
if (!backToAnswerFlash) {
backIv.startAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.flash));
backToAnswerFlash = true;
}
}
@Override
public void onCommentAdd(Comment comment) {
// One reply to comments come here, add new comment is directly handled
// by QuestionAcitivy.
if (comment != null) {
if (question.comments == null) question.comments = new ArrayList<Comment>();
question.comments.add(comment);
updateCacheWithNewCommentIfExists(comment);
}
}
@Override
public void onCommentUpdate(Comment comment) {
if (question.comments != null) {
LogWrapper.d(TAG, "Removing comment: " + comment.id);
Iterator<Comment> iterator = question.comments.iterator();
while (iterator.hasNext()) {
if (iterator.next().id == comment.id) {
LogWrapper.d(TAG, "comment " + comment.id + " edited");
removeQuestionFromCache();
break;
}
}
updateCacheIfNeeded();
}
}
@Override
public void onCommentDelete(long commentId) {
if (question.comments != null) {
LogWrapper.d(TAG, "Removing comment: " + commentId);
Iterator<Comment> iterator = question.comments.iterator();
while (iterator.hasNext()) {
if (iterator.next().id == commentId) {
LogWrapper.d(TAG, "comment " + commentId + " removed");
iterator.remove();
break;
}
}
updateCacheIfNeeded();
}
displayNumComments();
}
private void updateCacheWithNewCommentIfExists(Comment comment) {
if (QuestionsCache.getInstance().containsKey(question.id)) {
Question cachedQuestion = QuestionsCache.getInstance().get(question.id);
if (cachedQuestion.comments == null) cachedQuestion.comments = new ArrayList<Comment>();
if (!cachedQuestion.comments.contains(comment)) {
cachedQuestion.comments.add(comment);
QuestionsCache.getInstance().add(question.id, cachedQuestion);
}
}
}
private void updateCacheIfNeeded() {
if (QuestionsCache.getInstance().containsKey(question.id)) {
Question cachedQuestion = QuestionsCache.getInstance().get(question.id);
if (cachedQuestion != null) {
cachedQuestion.comments = question.comments;
QuestionsCache.getInstance().add(question.id, cachedQuestion);
} else QuestionsCache.getInstance().remove(question.id);
}
}
private void removeQuestionFromCache() {
if (QuestionsCache.getInstance().containsKey(question.id)) QuestionsCache.getInstance().remove(question.id);
}
@Override
protected void onScoreChange(int newScore) {
scoreTextView.setText(AppUtils.formatNumber(newScore));
}
}