package com.andrewshu.android.reddit.comments;
import java.util.LinkedList;
import android.os.AsyncTask;
import android.text.Html;
import android.text.Spanned;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import com.andrewshu.android.reddit.R;
import com.andrewshu.android.reddit.common.Constants;
import com.andrewshu.android.reddit.common.util.Util;
import com.andrewshu.android.reddit.markdown.Markdown;
import com.andrewshu.android.reddit.things.ThingInfo;
public class ProcessCommentsTask extends AsyncTask<Void, Integer, Void> {
private static final String TAG = "ProcessCommentsSubTask";
private final CommentsListActivity mActivity;
private final Markdown markdown = new Markdown();
/**
* List holding the deferred processing list starting from the first object to handle
*/
private final LinkedList<DeferredCommentProcessing> mDeferredProcessingList = new LinkedList<DeferredCommentProcessing>();
/**
* Helper list holding the "context" comments, given first priority for processing since they'll be shown first.
*/
private final LinkedList<DeferredCommentProcessing> mDeferredProcessingHighPriorityList = new LinkedList<DeferredCommentProcessing>();
/**
* Helper list holding the tail of the deferred processing list, to be appended to mDeferredProcessingList
*/
private final LinkedList<DeferredCommentProcessing> mDeferredProcessingLowPriorityList = new LinkedList<DeferredCommentProcessing>();
public static class DeferredCommentProcessing {
public int commentIndex;
public ThingInfo comment;
public DeferredCommentProcessing(ThingInfo comment, int commentIndex) {
this.comment = comment;
this.commentIndex = commentIndex;
}
}
/**
* Constructor
*/
public ProcessCommentsTask(CommentsListActivity commentsListActivity) {
this.mActivity = commentsListActivity;
}
public void addDeferred(DeferredCommentProcessing deferredCommentProcessing) {
mDeferredProcessingList.add(deferredCommentProcessing);
}
public void addDeferredHighPriority(DeferredCommentProcessing deferredCommentProcessing) {
mDeferredProcessingHighPriorityList.add(deferredCommentProcessing);
}
public void addDeferredLowPriority(DeferredCommentProcessing deferredCommentProcessing) {
mDeferredProcessingLowPriorityList.add(deferredCommentProcessing);
}
public void moveHighPriorityOverflowToLowPriority(int highPriorityMaxSize) {
if (mDeferredProcessingHighPriorityList.size() > highPriorityMaxSize) {
DeferredCommentProcessing overflow = mDeferredProcessingHighPriorityList.removeFirst();
mDeferredProcessingLowPriorityList.add(overflow);
}
}
public void mergeHighPriorityListToMainList() {
mDeferredProcessingList.addAll(0, mDeferredProcessingHighPriorityList);
mDeferredProcessingHighPriorityList.clear();
}
public void mergeLowPriorityListToMainList() {
mDeferredProcessingList.addAll(mDeferredProcessingLowPriorityList);
mDeferredProcessingLowPriorityList.clear();
}
@Override
public Void doInBackground(Void... v) {
for (final DeferredCommentProcessing deferredCommentProcessing : mDeferredProcessingList) {
processCommentSlowSteps(deferredCommentProcessing.comment);
publishProgress(deferredCommentProcessing.commentIndex);
}
cleanupQueues();
return null;
}
private void cleanupQueues() {
mDeferredProcessingList.clear();
mDeferredProcessingHighPriorityList.clear();
mDeferredProcessingLowPriorityList.clear();
}
@Override
public void onProgressUpdate(Integer... commentsToShow) {
for (Integer commentIndex : commentsToShow) {
refreshDeferredCommentIfVisibleUI(commentIndex);
}
}
private void processCommentSlowSteps(ThingInfo comment) {
if (comment.getBody_html() != null) {
CharSequence spanned = createSpanned(comment.getBody_html());
comment.setSpannedBody(spanned);
}
markdown.getURLs(comment.getBody(), comment.getUrls());
}
/**
* @param bodyHtml escaped HTML (like in reddit Thing's body_html)
*/
private CharSequence createSpanned(String bodyHtml) {
try {
// get unescaped HTML
bodyHtml = Html.fromHtml(bodyHtml).toString();
// fromHtml doesn't support all HTML tags. convert <code> and <pre>
bodyHtml = Util.convertHtmlTags(bodyHtml);
Spanned body = Html.fromHtml(bodyHtml);
// remove last 2 newline characters
if (body.length() > 2)
return body.subSequence(0, body.length()-2);
else
return "";
} catch (Exception e) {
if (Constants.LOGGING) Log.e(TAG, "createSpanned failed", e);
return null;
}
}
private void refreshDeferredCommentIfVisibleUI(final int commentIndex) {
if (isPositionVisibleUI(commentIndex))
refreshCommentUI(commentIndex);
}
private void refreshCommentUI(int commentIndex) {
refreshCommentBodyTextViewUI(commentIndex);
refreshCommentSubmitterUI(commentIndex);
}
private void refreshCommentBodyTextViewUI(int commentIndex) {
int positionOnScreen = commentIndex - mActivity.getListView().getFirstVisiblePosition();
View v = mActivity.getListView().getChildAt(positionOnScreen);
if (v != null) {
View bodyTextView = v.findViewById(R.id.body);
if (bodyTextView != null) {
((TextView) bodyTextView).setText(mActivity.mCommentsList.get(commentIndex).getSpannedBody());
}
}
}
private void refreshCommentSubmitterUI(int commentIndex) {
int positionOnScreen = commentIndex - mActivity.getListView().getFirstVisiblePosition();
View v = mActivity.getListView().getChildAt(positionOnScreen);
if (v != null) {
View submitterTextView = v.findViewById(R.id.submitter);
if (submitterTextView != null) {
ThingInfo comment = mActivity.mCommentsList.get(commentIndex);
if (comment.getSSAuthor() != null)
((TextView) submitterTextView).setText(comment.getSSAuthor());
else
((TextView) submitterTextView).setText(comment.getAuthor());
}
}
}
/**
* Call from UI Thread
*/
boolean isPositionVisibleUI(int position) {
return position <= mActivity.getListView().getLastVisiblePosition() &&
position >= mActivity.getListView().getFirstVisiblePosition();
}
}