Java tutorial
/******************************************************************************* * This file is part of RedReader. * * RedReader 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. * * RedReader 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 RedReader. If not, see <http://www.gnu.org/licenses/>. ******************************************************************************/ package org.lol.reddit.reddit.prepared; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Color; import android.text.SpannableStringBuilder; import android.view.ViewGroup; import org.apache.commons.lang3.StringEscapeUtils; import org.apache.http.StatusLine; import org.holoeverywhere.app.Activity; import org.lol.reddit.R; import org.lol.reddit.account.RedditAccount; import org.lol.reddit.account.RedditAccountManager; import org.lol.reddit.cache.CacheManager; import org.lol.reddit.cache.RequestFailureType; import org.lol.reddit.common.*; import org.lol.reddit.reddit.APIResponseHandler; import org.lol.reddit.reddit.RedditAPI; import org.lol.reddit.reddit.RedditPreparedInboxItem; import org.lol.reddit.reddit.prepared.markdown.MarkdownParagraphGroup; import org.lol.reddit.reddit.prepared.markdown.MarkdownParser; import org.lol.reddit.reddit.things.RedditComment; import org.lol.reddit.views.RedditCommentView; import java.net.URI; import java.util.EnumSet; import java.util.HashSet; import java.util.LinkedList; public final class RedditPreparedComment implements RedditPreparedInboxItem { public SpannableStringBuilder header; private final MarkdownParagraphGroup body; private final LinkedList<RedditPreparedComment> directReplies = new LinkedList<RedditPreparedComment>(); private boolean collapsed = false; public final String idAlone, idAndType, flair; private int voteDirection; private boolean saved; public long lastChange; public final RedditComment src; private RedditCommentView boundView; private final int rrCommentHeaderBoldCol, rrCommentHeaderAuthorCol, rrPostSubtitleUpvoteCol, rrPostSubtitleDownvoteCol, rrFlairBackCol, rrFlairTextCol, rrGoldBackCol, rrGoldTextCol; private final EnumSet<PrefsUtility.AppearanceCommentHeaderItems> headerItems; private final RedditPreparedPost parentPost; public RedditPreparedComment(final Context context, final RedditComment comment, final long timestamp, final boolean needsUpdating, final RedditPreparedPost parentPost, final RedditAccount user, final EnumSet<PrefsUtility.AppearanceCommentHeaderItems> headerItems) { this.src = comment; this.parentPost = parentPost; this.headerItems = headerItems; // TODO custom time // TODO don't fetch these every time final TypedArray appearance = context.obtainStyledAttributes(new int[] { R.attr.rrCommentHeaderBoldCol, R.attr.rrCommentHeaderAuthorCol, R.attr.rrPostSubtitleUpvoteCol, R.attr.rrPostSubtitleDownvoteCol, R.attr.rrFlairBackCol, R.attr.rrFlairTextCol, R.attr.rrGoldBackCol, R.attr.rrGoldTextCol }); rrCommentHeaderBoldCol = appearance.getColor(0, 255); rrCommentHeaderAuthorCol = appearance.getColor(1, 255); rrPostSubtitleUpvoteCol = appearance.getColor(2, 255); rrPostSubtitleDownvoteCol = appearance.getColor(3, 255); rrFlairBackCol = appearance.getColor(4, 0); rrFlairTextCol = appearance.getColor(5, 255); rrGoldBackCol = appearance.getColor(6, 0); rrGoldTextCol = appearance.getColor(7, 255); body = MarkdownParser.parse(StringEscapeUtils.unescapeHtml4(comment.body).toCharArray()); if (comment.author_flair_text != null) { flair = StringEscapeUtils.unescapeHtml4(comment.author_flair_text); } else { flair = null; } idAlone = comment.id; idAndType = comment.name; if (comment.likes == null) { voteDirection = 0; } else { voteDirection = Boolean.TRUE.equals(comment.likes) ? 1 : -1; } saved = Boolean.TRUE.equals(comment.saved); lastChange = timestamp; if (src.likes != null) { RedditChangeDataManager.getInstance(context).update(src.link_id, user, this, true); } else if (needsUpdating) { RedditChangeDataManager.getInstance(context).update(src.link_id, user, this, false); } rebuildHeader(context); } private void rebuildHeader(final Context context) { final BetterSSB sb = new BetterSSB(); final int pointsCol; int score = src.ups - src.downs; if (Boolean.TRUE.equals(src.likes)) score--; if (Boolean.FALSE.equals(src.likes)) score++; if (isUpvoted()) { pointsCol = rrPostSubtitleUpvoteCol; score++; } else if (isDownvoted()) { pointsCol = rrPostSubtitleDownvoteCol; score--; } else { pointsCol = rrCommentHeaderBoldCol; } if (headerItems.contains(PrefsUtility.AppearanceCommentHeaderItems.AUTHOR)) { if (parentPost != null && src.author.equalsIgnoreCase(parentPost.src.author) && !src.author.equals("[deleted]")) { sb.append(" " + src.author + " ", BetterSSB.BACKGROUND_COLOR | BetterSSB.FOREGROUND_COLOR | BetterSSB.BOLD, Color.WHITE, Color.rgb(0, 126, 168), 1f); // TODO color } else { sb.append(src.author, BetterSSB.FOREGROUND_COLOR | BetterSSB.BOLD, rrCommentHeaderAuthorCol, 0, 1f); } } if (headerItems.contains(PrefsUtility.AppearanceCommentHeaderItems.FLAIR) && flair != null && flair.length() > 0) { if (headerItems.contains(PrefsUtility.AppearanceCommentHeaderItems.AUTHOR)) { sb.append(" ", 0); } sb.append(" " + flair + " ", BetterSSB.FOREGROUND_COLOR | BetterSSB.BACKGROUND_COLOR, rrFlairTextCol, rrFlairBackCol, 1f); } if (headerItems.contains(PrefsUtility.AppearanceCommentHeaderItems.AUTHOR) || headerItems.contains(PrefsUtility.AppearanceCommentHeaderItems.FLAIR)) { sb.append(" ", 0); } if (headerItems.contains(PrefsUtility.AppearanceCommentHeaderItems.SCORE)) { if (!Boolean.TRUE.equals(src.score_hidden)) { sb.append(String.valueOf(score), BetterSSB.FOREGROUND_COLOR | BetterSSB.BOLD, pointsCol, 0, 1f); } else { sb.append("??", BetterSSB.FOREGROUND_COLOR | BetterSSB.BOLD, pointsCol, 0, 1f); } sb.append(" " + context.getString(R.string.subtitle_points) + " ", 0); } if (headerItems.contains(PrefsUtility.AppearanceCommentHeaderItems.GOLD)) { if (src.gilded > 0) { sb.append(" ", 0); sb.append(" " + context.getString(R.string.gold) + " x" + src.gilded + " ", BetterSSB.FOREGROUND_COLOR | BetterSSB.BACKGROUND_COLOR, rrGoldTextCol, rrGoldBackCol, 1f); sb.append(" ", 0); } } if (headerItems.contains(PrefsUtility.AppearanceCommentHeaderItems.AGE)) { sb.append(RRTime.formatDurationFrom(context, src.created_utc * 1000L), BetterSSB.FOREGROUND_COLOR | BetterSSB.BOLD, rrCommentHeaderBoldCol, 0, 1f); if (src.edited != null && src.edited instanceof Long) { sb.append("*", BetterSSB.FOREGROUND_COLOR | BetterSSB.BOLD, rrCommentHeaderBoldCol, 0, 1f); } } header = sb.get(); } public void bind(RedditCommentView view) { boundView = view; } public void unbind(RedditCommentView view) { if (boundView == view) boundView = null; } public void addChild(final RedditPreparedComment child) { directReplies.add(child); } public void toggleVisibility() { collapsed = !collapsed; } public boolean isCollapsed() { return collapsed; } public void refreshView(final Context context) { General.UI_THREAD_HANDLER.post(new Runnable() { public void run() { rebuildHeader(context); if (boundView != null) { boundView.updateAppearance(); boundView.requestLayout(); boundView.invalidate(); } } }); } public void action(final Activity activity, final RedditAPI.RedditAction action) { final RedditAccount user = RedditAccountManager.getInstance(activity).getDefaultAccount(); if (user.isAnonymous()) { General.quickToast(activity, "You must be logged in to do that."); return; } final int lastVoteDirection = voteDirection; switch (action) { case DOWNVOTE: voteDirection = -1; break; case UNVOTE: voteDirection = 0; break; case UPVOTE: voteDirection = 1; break; case SAVE: saved = true; break; case UNSAVE: saved = false; break; } refreshView(activity); RedditAPI.action(CacheManager.getInstance(activity), new APIResponseHandler.ActionResponseHandler(activity) { @Override protected void onCallbackException(final Throwable t) { throw new RuntimeException(t); } @Override protected void onFailure(final RequestFailureType type, final Throwable t, final StatusLine status, final String readableMessage) { revertOnFailure(); if (t != null) t.printStackTrace(); final RRError error = General.getGeneralErrorForFailure(context, type, t, status, null); General.UI_THREAD_HANDLER.post(new Runnable() { public void run() { General.showResultDialog(activity, error); } }); } @Override protected void onFailure(final APIFailureType type) { revertOnFailure(); final RRError error = General.getGeneralErrorForFailure(context, type); General.UI_THREAD_HANDLER.post(new Runnable() { public void run() { General.showResultDialog(activity, error); } }); } @Override protected void onSuccess() { lastChange = RRTime.utcCurrentTimeMillis(); RedditChangeDataManager.getInstance(context).update(src.link_id, user, RedditPreparedComment.this, true); refreshView(activity); } private void revertOnFailure() { switch (action) { case DOWNVOTE: case UNVOTE: case UPVOTE: voteDirection = lastVoteDirection; break; case SAVE: saved = false; break; case UNSAVE: saved = true; break; } refreshView(context); } }, user, idAndType, action, activity); } public int getVoteDirection() { return voteDirection; } public boolean isUpvoted() { return voteDirection == 1; } public boolean isDownvoted() { return voteDirection == -1; } public void updateFromChangeDb(final long timestamp, final int voteDirection, final boolean saved) { this.lastChange = timestamp; this.voteDirection = voteDirection; this.saved = saved; } public int replyCount() { return directReplies.size(); } @Override public boolean equals(Object o) { return o instanceof RedditPreparedComment && (o == this || ((RedditPreparedComment) o).idAlone.equals(idAlone)); } public HashSet<String> computeAllLinks() { return LinkHandler.computeAllLinks(StringEscapeUtils.unescapeHtml4(src.body_html)); } public SpannableStringBuilder getHeader() { return header; } public ViewGroup getBody(Activity activity, float textSize, Integer textCol, boolean showLinkButtons) { return body.buildView(activity, textCol, textSize, showLinkButtons); } public RedditCommentView getBoundView() { return boundView; } public void handleInboxClick(Activity activity) { final URI commentContext = Constants.Reddit.getUri(src.context); LinkHandler.onLinkClicked(activity, commentContext.toString()); } public boolean isSaved() { return saved; } }