/*
* Copyright (C) 2016 Simon Vig Therkildsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.simonvt.cathode.ui.comments;
import android.content.Context;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
import android.widget.TextView;
import butterknife.BindView;
import butterknife.ButterKnife;
import java.text.DateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import javax.inject.Inject;
import net.simonvt.cathode.CathodeApp;
import net.simonvt.cathode.R;
import net.simonvt.cathode.provider.DatabaseContract.CommentColumns;
import net.simonvt.cathode.provider.DatabaseContract.LastModifiedColumns;
import net.simonvt.cathode.provider.DatabaseContract.UserColumns;
import net.simonvt.cathode.provider.DatabaseSchematic.Tables;
import net.simonvt.cathode.scheduler.CommentsTaskScheduler;
import net.simonvt.cathode.ui.adapter.RecyclerCursorAdapter;
import net.simonvt.cathode.util.SqlColumn;
import net.simonvt.cathode.widget.CircleTransformation;
import net.simonvt.cathode.widget.RemoteImageView;
import net.simonvt.schematic.Cursors;
public class CommentsAdapter extends RecyclerCursorAdapter<CommentsAdapter.ViewHolder> {
private static final String STATE_REVEALED_SPOILERS =
"net.simonvt.cathode.ui.comments.CommentsAdapter";
private static final int COMMENT = 0;
private static final int REPLY = 1;
private static final int USER_COMMENT = 2;
private static final int USER_REPLY = 3;
public interface OnCommentClickListener {
void onCommentClick(long commentId, String comment, boolean spoiler, boolean isUserComment);
}
public static final String[] PROJECTION = {
SqlColumn.table(Tables.COMMENTS).column(CommentColumns.ID),
SqlColumn.table(Tables.COMMENTS).column(CommentColumns.COMMENT),
SqlColumn.table(Tables.COMMENTS).column(CommentColumns.SPOILER),
SqlColumn.table(Tables.COMMENTS).column(CommentColumns.REVIEW),
SqlColumn.table(Tables.COMMENTS).column(CommentColumns.CREATED_AT),
SqlColumn.table(Tables.COMMENTS).column(CommentColumns.REPLIES),
SqlColumn.table(Tables.COMMENTS).column(CommentColumns.LIKES),
SqlColumn.table(Tables.COMMENTS).column(CommentColumns.USER_RATING),
SqlColumn.table(Tables.COMMENTS).column(CommentColumns.LIKED),
SqlColumn.table(Tables.COMMENTS).column(CommentColumns.IS_USER_COMMENT),
SqlColumn.table(Tables.COMMENTS).column(LastModifiedColumns.LAST_MODIFIED),
SqlColumn.table(Tables.USERS).column(UserColumns.USERNAME),
SqlColumn.table(Tables.USERS).column(UserColumns.NAME),
SqlColumn.table(Tables.USERS).column(UserColumns.AVATAR),
};
@Inject CommentsTaskScheduler commentsScheduler;
private boolean showsReplies;
private OnCommentClickListener listener;
private Set<Long> revealedSpoilers = new HashSet<>();
private int tintColor;
private int likedTintColor;
public CommentsAdapter(Context context, Cursor cursor, boolean showsReplies,
OnCommentClickListener listener) {
super(context, cursor);
this.showsReplies = showsReplies;
this.listener = listener;
CathodeApp.inject(context, this);
tintColor = context.getResources().getColor(R.color.commentIconTint);
likedTintColor = context.getResources().getColor(R.color.commentLikedTint);
}
public void restoreState(Bundle state) {
long[] revealed = state.getLongArray(STATE_REVEALED_SPOILERS);
for (long id : revealed) {
revealedSpoilers.add(id);
}
}
public Bundle saveState() {
Bundle state = new Bundle();
long[] revealed = new long[revealedSpoilers.size()];
int i = 0;
for (Long id : revealedSpoilers) {
revealed[i++] = id;
}
state.putLongArray(STATE_REVEALED_SPOILERS, revealed);
return state;
}
@Override public int getItemViewType(int position) {
if (showsReplies && position > 0) {
return REPLY;
}
return COMMENT;
}
@Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v;
if (viewType == COMMENT) {
v = LayoutInflater.from(getContext()).inflate(R.layout.comment_post, parent, false);
} else {
v = LayoutInflater.from(getContext()).inflate(R.layout.comment_reply, parent, false);
}
final ViewHolder holder = new ViewHolder(v);
holder.avatar.addTransformation(new CircleTransformation());
Drawable likes = getContext().getResources().getDrawable(R.drawable.ic_thumb_up);
holder.likeDrawable = DrawableCompat.wrap(likes);
holder.likes.setCompoundDrawablesWithIntrinsicBounds(holder.likeDrawable, null, null, null);
if (holder.replies != null) {
Drawable replies = getContext().getResources().getDrawable(R.drawable.ic_comment);
holder.repliesDrawable = DrawableCompat.wrap(replies);
DrawableCompat.setTint(holder.repliesDrawable, tintColor);
holder.replies.setCompoundDrawablesWithIntrinsicBounds(holder.repliesDrawable, null, null,
null);
}
if (!showsReplies) {
v.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
if (holder.isSpoiler && !holder.isRevealed) {
revealedSpoilers.add(holder.getItemId());
holder.isRevealed = true;
holder.setIsRecyclable(false);
holder.commentText.setVisibility(View.VISIBLE);
holder.commentText.setAlpha(0.0f);
holder.commentText.animate().alpha(1.0f);
holder.spoilerOverlay.animate().alpha(0.0f).withEndAction(new Runnable() {
@Override public void run() {
holder.setIsRecyclable(true);
holder.spoilerOverlay.setVisibility(View.GONE);
}
});
} else {
listener.onCommentClick(holder.getItemId(), holder.comment, holder.isSpoiler,
holder.isUserComment);
}
}
});
}
holder.infoPane.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
final boolean liked = holder.liked;
if (liked) {
commentsScheduler.unlike(holder.getItemId());
holder.likeCount--;
DrawableCompat.setTint(holder.likeDrawable, tintColor);
} else {
commentsScheduler.like(holder.getItemId());
holder.likeCount++;
DrawableCompat.setTint(holder.likeDrawable, likedTintColor);
}
holder.likes.setText(String.valueOf(holder.likeCount));
holder.liked = !liked;
}
});
return holder;
}
@Override public void onViewRecycled(ViewHolder holder) {
super.onViewRecycled(holder);
if (holder.commentAnimator != null) {
holder.commentAnimator.cancel();
}
if (holder.spoilerAnimator != null) {
holder.spoilerAnimator.cancel();
}
}
@Override protected void onBindViewHolder(final ViewHolder holder, Cursor cursor, int position) {
final long commentId = Cursors.getLong(cursor, CommentColumns.ID);
final String comment = Cursors.getString(cursor, CommentColumns.COMMENT);
final boolean spoiler = Cursors.getBoolean(cursor, CommentColumns.SPOILER);
final boolean review = Cursors.getBoolean(cursor, CommentColumns.REVIEW);
final long createdAt = Cursors.getLong(cursor, CommentColumns.CREATED_AT);
final int likes = Cursors.getInt(cursor, CommentColumns.LIKES);
final int replies = Cursors.getInt(cursor, CommentColumns.REPLIES);
final int userRating = Cursors.getInt(cursor, CommentColumns.USER_RATING);
final boolean liked = Cursors.getBoolean(cursor, CommentColumns.LIKED);
final boolean isUserComment = Cursors.getBoolean(cursor, CommentColumns.IS_USER_COMMENT);
final String username = Cursors.getString(cursor, UserColumns.USERNAME);
final String name = Cursors.getString(cursor, UserColumns.NAME);
final String avatar = Cursors.getString(cursor, UserColumns.AVATAR);
final boolean revealed = revealedSpoilers.contains(commentId);
DateFormat format = DateFormat.getDateTimeInstance();
final String dateString = format.format(new Date(createdAt));
String visibleName;
if (!TextUtils.isEmpty(name)) {
visibleName = name;
} else {
visibleName = username;
}
holder.avatar.setImage(avatar);
String usernameText = getContext().getResources()
.getString(R.string.comments_username_rating, visibleName, userRating);
holder.username.setText(usernameText);
holder.date.setText(dateString);
holder.commentText.setText(comment);
holder.likes.setText(String.valueOf(likes));
if (holder.replies != null) {
holder.replies.setText(String.valueOf(replies));
}
holder.likeCount = likes;
if (liked) {
DrawableCompat.setTint(holder.likeDrawable, likedTintColor);
} else {
DrawableCompat.setTint(holder.likeDrawable, tintColor);
}
holder.isRevealed = revealed;
if (spoiler && !revealed) {
holder.commentText.setVisibility(View.GONE);
holder.spoilerOverlay.setVisibility(View.VISIBLE);
holder.spoilerOverlay.setAlpha(1.0f);
} else {
holder.commentText.setVisibility(View.VISIBLE);
holder.commentText.setAlpha(1.0f);
holder.spoilerOverlay.setVisibility(View.GONE);
}
if (showsReplies) {
if (spoiler && !revealed) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
revealedSpoilers.add(holder.getItemId());
holder.isRevealed = true;
holder.commentText.setVisibility(View.VISIBLE);
holder.commentText.setAlpha(0.0f);
holder.commentAnimator = holder.commentText.animate().alpha(1.0f);
holder.spoilerAnimator =
holder.spoilerOverlay.animate().alpha(0.0f).withEndAction(new Runnable() {
@Override public void run() {
holder.spoilerOverlay.setVisibility(View.GONE);
}
});
}
});
} else {
holder.itemView.setOnClickListener(null);
}
}
holder.spoiler.setVisibility(spoiler ? View.VISIBLE : View.INVISIBLE);
holder.comment = comment;
holder.isSpoiler = spoiler;
holder.isUserComment = isUserComment;
holder.liked = liked;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.infoPane) View infoPane;
@BindView(R.id.avatar) RemoteImageView avatar;
@BindView(R.id.username) TextView username;
@BindView(R.id.date) TextView date;
@BindView(R.id.commentText) TextView commentText;
@BindView(R.id.likes) TextView likes;
@BindView(R.id.replies) @Nullable TextView replies;
@BindView(R.id.spoiler) View spoiler;
@BindView(R.id.spoilerOverlay) View spoilerOverlay;
ViewPropertyAnimator commentAnimator;
ViewPropertyAnimator spoilerAnimator;
Drawable likeDrawable;
Drawable repliesDrawable;
int likeCount;
String comment;
boolean isUserComment;
boolean isSpoiler;
boolean isRevealed;
boolean liked;
public ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}