package kr.kdev.dg1s.biowiki.ui.comments;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.AsyncTask;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import java.util.HashSet;
import kr.kdev.dg1s.biowiki.BioWiki;
import kr.kdev.dg1s.biowiki.R;
import kr.kdev.dg1s.biowiki.datasets.CommentTable;
import kr.kdev.dg1s.biowiki.models.Comment;
import kr.kdev.dg1s.biowiki.models.CommentList;
import kr.kdev.dg1s.biowiki.util.AniUtils;
import kr.kdev.dg1s.biowiki.util.AppLog;
import kr.kdev.dg1s.biowiki.util.DateTimeUtils;
import kr.kdev.dg1s.biowiki.util.SysUtils;
import kr.kdev.dg1s.biowiki.widgets.BWNetworkImageView;
/**
* Created by nbradbury on 1/29/14.
*/
class CommentAdapter extends BaseAdapter {
private final LayoutInflater mInflater;
private final DataLoadedListener mDataLoadedListener;
private final OnLoadMoreListener mOnLoadMoreListener;
private final OnSelectedItemsChangeListener mOnSelectedChangeListener;
private final HashSet<Integer> mSelectedPositions = new HashSet<Integer>();
private final int mStatusColorSpam;
private final int mStatusColorUnapproved;
private final int mSelectionColor;
private final int mAvatarSz;
private final String mStatusTextSpam;
private final String mStatusTextUnapproved;
private CommentList mComments = new CommentList();
private long mHighlightedCommentId = -1;
private boolean mEnableSelection;
/*
* AsyncTask to load comments from SQLite
*/
private boolean mIsLoadTaskRunning = false;
CommentAdapter(Context context,
DataLoadedListener onDataLoadedListener,
OnLoadMoreListener onLoadMoreListener,
OnSelectedItemsChangeListener onChangeListener) {
mInflater = LayoutInflater.from(context);
mDataLoadedListener = onDataLoadedListener;
mOnLoadMoreListener = onLoadMoreListener;
mOnSelectedChangeListener = onChangeListener;
mStatusColorSpam = context.getResources().getColor(R.color.comment_status_spam);
mStatusColorUnapproved = context.getResources().getColor(R.color.comment_status_unapproved);
mSelectionColor = context.getResources().getColor(R.color.blue_extra_light);
mStatusTextSpam = context.getResources().getString(R.string.comment_status_spam);
mStatusTextUnapproved = context.getResources().getString(R.string.comment_status_unapproved);
mAvatarSz = context.getResources().getDimensionPixelSize(R.dimen.avatar_sz_medium);
}
@Override
public int getCount() {
return (mComments != null ? mComments.size() : 0);
}
@Override
public Object getItem(int position) {
return mComments.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
void clear() {
if (mComments.size() > 0) {
mComments.clear();
notifyDataSetChanged();
}
}
void setEnableSelection(boolean enable) {
if (enable == mEnableSelection)
return;
mEnableSelection = enable;
if (mEnableSelection) {
notifyDataSetChanged();
} else {
clearSelectedComments();
}
}
void clearSelectedComments() {
if (mSelectedPositions.size() > 0) {
mSelectedPositions.clear();
notifyDataSetChanged();
if (mOnSelectedChangeListener != null)
mOnSelectedChangeListener.onSelectedItemsChanged();
}
}
int getSelectedCommentCount() {
return mSelectedPositions.size();
}
CommentList getSelectedComments() {
CommentList comments = new CommentList();
if (!mEnableSelection)
return comments;
for (Integer position : mSelectedPositions) {
if (isPositionValid(position))
comments.add(mComments.get(position));
}
return comments;
}
private boolean isItemSelected(int position) {
return mSelectedPositions.contains(position);
}
void setItemSelected(int position, boolean isSelected, View view) {
if (isItemSelected(position) == isSelected)
return;
if (isSelected) {
mSelectedPositions.add(position);
} else {
mSelectedPositions.remove(position);
}
notifyDataSetChanged();
if (view != null && view.getTag() instanceof CommentHolder) {
CommentHolder holder = (CommentHolder) view.getTag();
// animate the selection change on ICS or later (looks wonky on Gingerbread)
if (SysUtils.isGteAndroid4()) {
holder.imgCheckmark.clearAnimation();
AniUtils.startAnimation(holder.imgCheckmark, isSelected ? R.anim.cab_select : R.anim.cab_deselect);
}
holder.imgCheckmark.setVisibility(isSelected ? View.VISIBLE : View.GONE);
}
if (mOnSelectedChangeListener != null)
mOnSelectedChangeListener.onSelectedItemsChanged();
}
void toggleItemSelected(int position, View view) {
setItemSelected(position, !isItemSelected(position), view);
}
/*
* this is used for tablet UI to highlight the comment displayed in the detail view
*/
long getHighlightedCommentId() {
return mHighlightedCommentId;
}
void setHighlightedCommentId(long commentId) {
if (mHighlightedCommentId == commentId)
return;
mHighlightedCommentId = commentId;
notifyDataSetChanged();
}
private boolean isPositionValid(int position) {
return (position >= 0 && position < mComments.size());
}
void replaceComments(final CommentList comments) {
mComments.replaceComments(comments);
notifyDataSetChanged();
}
void deleteComments(final CommentList comments) {
mComments.deleteComments(comments);
notifyDataSetChanged();
}
public View getView(final int position, View convertView, ViewGroup parent) {
final Comment comment = mComments.get(position);
final CommentHolder holder;
if (convertView == null || convertView.getTag() == null) {
convertView = mInflater.inflate(R.layout.comment_listitem, null);
holder = new CommentHolder(convertView);
convertView.setTag(holder);
} else {
holder = (CommentHolder) convertView.getTag();
}
holder.txtTitle.setText(Html.fromHtml(comment.getFormattedTitle()));
holder.txtComment.setText(comment.getUnescapedCommentText());
holder.txtDate.setText(DateTimeUtils.javaDateToTimeSpan(comment.getDatePublished()));
// status is only shown for comments that haven't been approved
final boolean showStatus;
switch (comment.getStatusEnum()) {
case SPAM:
showStatus = true;
holder.txtStatus.setText(mStatusTextSpam);
holder.txtStatus.setTextColor(mStatusColorSpam);
break;
case UNAPPROVED:
showStatus = true;
holder.txtStatus.setText(mStatusTextUnapproved);
holder.txtStatus.setTextColor(mStatusColorUnapproved);
break;
default:
showStatus = false;
break;
}
holder.txtStatus.setVisibility(showStatus ? View.VISIBLE : View.GONE);
final boolean useSelectionBackground;
if (mEnableSelection && isItemSelected(position)) {
useSelectionBackground = true;
if (holder.imgCheckmark.getVisibility() != View.VISIBLE)
holder.imgCheckmark.setVisibility(View.VISIBLE);
} else {
useSelectionBackground = (mHighlightedCommentId == comment.commentID);
if (holder.imgCheckmark.getVisibility() == View.VISIBLE)
holder.imgCheckmark.setVisibility(View.GONE);
holder.imgAvatar.setImageUrl(comment.getAvatarForDisplay(mAvatarSz), BWNetworkImageView.ImageType.AVATAR);
}
if (useSelectionBackground) {
convertView.setBackgroundColor(mSelectionColor);
} else {
convertView.setBackgroundDrawable(null);
}
// comment text needs to be to the left of date/status when the title is a single line and
// the status is displayed or else the status may overlap the comment text - note that
// getLineCount() will return 0 if the view hasn't been rendered yet, which is why we
// check getLineCount() <= 1
boolean adjustComment = (showStatus && holder.txtTitle.getLineCount() <= 1);
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) holder.txtComment.getLayoutParams();
if (adjustComment) {
params.addRule(RelativeLayout.LEFT_OF, R.id.layout_date_status);
} else {
params.addRule(RelativeLayout.LEFT_OF, 0);
}
// request to load more comments when we near the end
if (mOnLoadMoreListener != null && position >= getCount() - 1)
mOnLoadMoreListener.onLoadMore();
return convertView;
}
/*
* load comments using an AsyncTask
*/
@SuppressLint("NewApi")
void loadComments() {
if (mIsLoadTaskRunning)
AppLog.w(AppLog.T.COMMENTS, "load comments task already active");
if (SysUtils.canUseExecuteOnExecutor()) {
new LoadCommentsTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else {
new LoadCommentsTask().execute();
}
}
static interface DataLoadedListener {
public void onDataLoaded(boolean isEmpty);
}
static interface OnLoadMoreListener {
public void onLoadMore();
}
static interface OnSelectedItemsChangeListener {
public void onSelectedItemsChanged();
}
private class CommentHolder {
private final TextView txtTitle;
private final TextView txtComment;
private final TextView txtStatus;
private final TextView txtDate;
private final BWNetworkImageView imgAvatar;
private final ImageView imgCheckmark;
private CommentHolder(View row) {
txtTitle = (TextView) row.findViewById(R.id.title);
txtComment = (TextView) row.findViewById(R.id.comment);
txtStatus = (TextView) row.findViewById(R.id.status);
txtDate = (TextView) row.findViewById(R.id.text_date);
imgCheckmark = (ImageView) row.findViewById(R.id.image_checkmark);
imgAvatar = (BWNetworkImageView) row.findViewById(R.id.avatar);
}
}
private class LoadCommentsTask extends AsyncTask<Void, Void, Boolean> {
CommentList tmpComments;
@Override
protected void onPreExecute() {
mIsLoadTaskRunning = true;
}
@Override
protected void onCancelled() {
mIsLoadTaskRunning = false;
}
@Override
protected Boolean doInBackground(Void... params) {
int localBlogId = BioWiki.getCurrentLocalTableBlogId();
tmpComments = CommentTable.getCommentsForBlog(localBlogId);
if (mComments.isSameList(tmpComments))
return false;
// pre-calc transient values so they're cached when used by getView()
for (Comment comment : tmpComments) {
comment.getDatePublished();
comment.getUnescapedCommentText();
comment.getUnescapedPostTitle();
comment.getAvatarForDisplay(mAvatarSz);
comment.getFormattedTitle();
}
return true;
}
@Override
protected void onPostExecute(Boolean result) {
if (result) {
mComments = (CommentList) (tmpComments.clone());
notifyDataSetChanged();
}
if (mDataLoadedListener != null)
mDataLoadedListener.onDataLoaded(isEmpty());
mIsLoadTaskRunning = false;
}
}
}