/******************************************************************************* * 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.quantumbadger.redreader.reddit.api; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.text.ClipboardManager; import android.widget.Toast; import org.apache.commons.lang3.StringEscapeUtils; import org.quantumbadger.redreader.R; import org.quantumbadger.redreader.account.RedditAccount; import org.quantumbadger.redreader.account.RedditAccountManager; import org.quantumbadger.redreader.activities.CommentEditActivity; import org.quantumbadger.redreader.activities.CommentReplyActivity; import org.quantumbadger.redreader.cache.CacheManager; import org.quantumbadger.redreader.cache.CacheRequest; import org.quantumbadger.redreader.common.AndroidApi; import org.quantumbadger.redreader.common.General; import org.quantumbadger.redreader.common.LinkHandler; import org.quantumbadger.redreader.common.RRError; import org.quantumbadger.redreader.common.RRTime; import org.quantumbadger.redreader.fragments.CommentListingFragment; import org.quantumbadger.redreader.fragments.CommentPropertiesDialog; import org.quantumbadger.redreader.reddit.APIResponseHandler; import org.quantumbadger.redreader.reddit.RedditAPI; import org.quantumbadger.redreader.reddit.prepared.RedditChangeDataManager; import org.quantumbadger.redreader.reddit.prepared.RedditRenderableComment; import org.quantumbadger.redreader.reddit.things.RedditComment; import org.quantumbadger.redreader.reddit.url.UserProfileURL; import org.quantumbadger.redreader.views.RedditCommentView; import java.util.ArrayList; import java.util.HashSet; public class RedditAPICommentAction { public enum RedditCommentAction { UPVOTE, UNVOTE, DOWNVOTE, SAVE, UNSAVE, REPORT, SHARE, COPY_TEXT, COPY_URL, REPLY, USER_PROFILE, COMMENT_LINKS, COLLAPSE, EDIT, DELETE, PROPERTIES, CONTEXT, GO_TO_COMMENT, ACTION_MENU, BACK } private static class RCVMenuItem { public final String title; public final RedditCommentAction action; private RCVMenuItem(Context context, int titleRes, RedditCommentAction action) { this.title = context.getString(titleRes); this.action = action; } } public static void showActionMenu( final AppCompatActivity activity, final CommentListingFragment commentListingFragment, final RedditRenderableComment comment, final RedditCommentView commentView, final RedditChangeDataManager changeDataManager, final boolean isArchived) { final RedditAccount user = RedditAccountManager.getInstance(activity).getDefaultAccount(); final ArrayList<RCVMenuItem> menu = new ArrayList<>(); if(!user.isAnonymous()) { if (!isArchived) { if (!changeDataManager.isUpvoted(comment)) { menu.add(new RCVMenuItem(activity, R.string.action_upvote, RedditCommentAction.UPVOTE)); } else { menu.add(new RCVMenuItem(activity, R.string.action_upvote_remove, RedditCommentAction.UNVOTE)); } if (!changeDataManager.isDownvoted(comment)) { menu.add(new RCVMenuItem(activity, R.string.action_downvote, RedditCommentAction.DOWNVOTE)); } else { menu.add(new RCVMenuItem(activity, R.string.action_downvote_remove, RedditCommentAction.UNVOTE)); } } if(changeDataManager.isSaved(comment)) { menu.add(new RCVMenuItem(activity, R.string.action_unsave, RedditCommentAction.UNSAVE)); } else { menu.add(new RCVMenuItem(activity, R.string.action_save, RedditCommentAction.SAVE)); } menu.add(new RCVMenuItem(activity, R.string.action_report, RedditCommentAction.REPORT)); if (!isArchived) menu.add(new RCVMenuItem(activity, R.string.action_reply, RedditCommentAction.REPLY)); if(user.username.equalsIgnoreCase(comment.getParsedComment().getRawComment().author)) { if (!isArchived) menu.add(new RCVMenuItem(activity, R.string.action_edit, RedditCommentAction.EDIT)); menu.add(new RCVMenuItem(activity, R.string.action_delete, RedditCommentAction.DELETE)); } } menu.add(new RCVMenuItem(activity, R.string.action_comment_context, RedditCommentAction.CONTEXT)); menu.add(new RCVMenuItem(activity, R.string.action_comment_go_to, RedditCommentAction.GO_TO_COMMENT)); menu.add(new RCVMenuItem(activity, R.string.action_comment_links, RedditCommentAction.COMMENT_LINKS)); if(commentListingFragment != null) { menu.add(new RCVMenuItem(activity, R.string.action_collapse, RedditCommentAction.COLLAPSE)); } menu.add(new RCVMenuItem(activity, R.string.action_share, RedditCommentAction.SHARE)); menu.add(new RCVMenuItem(activity, R.string.action_copy_text, RedditCommentAction.COPY_TEXT)); menu.add(new RCVMenuItem(activity, R.string.action_copy_link, RedditCommentAction.COPY_URL)); menu.add(new RCVMenuItem(activity, R.string.action_user_profile, RedditCommentAction.USER_PROFILE)); menu.add(new RCVMenuItem(activity, R.string.action_properties, RedditCommentAction.PROPERTIES)); final String[] menuText = new String[menu.size()]; for(int i = 0; i < menuText.length; i++) { menuText[i] = menu.get(i).title; } final AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.setItems(menuText, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { onActionMenuItemSelected( comment, commentView, activity, commentListingFragment, menu.get(which).action, changeDataManager); } }); final AlertDialog alert = builder.create(); alert.setCanceledOnTouchOutside(true); alert.show(); } public static void onActionMenuItemSelected( final RedditRenderableComment renderableComment, final RedditCommentView commentView, final AppCompatActivity activity, final CommentListingFragment commentListingFragment, final RedditCommentAction action, final RedditChangeDataManager changeDataManager) { final RedditComment comment = renderableComment.getParsedComment().getRawComment(); switch(action) { case UPVOTE: action(activity, comment, RedditAPI.ACTION_UPVOTE, changeDataManager); break; case DOWNVOTE: action(activity, comment, RedditAPI.ACTION_DOWNVOTE, changeDataManager); break; case UNVOTE: action(activity, comment, RedditAPI.ACTION_UNVOTE, changeDataManager); break; case SAVE: action(activity, comment, RedditAPI.ACTION_SAVE, changeDataManager); break; case UNSAVE: action(activity, comment, RedditAPI.ACTION_UNSAVE, changeDataManager); break; case REPORT: new AlertDialog.Builder(activity) .setTitle(R.string.action_report) .setMessage(R.string.action_report_sure) .setPositiveButton(R.string.action_report, new DialogInterface.OnClickListener() { @Override public void onClick(final DialogInterface dialog, final int which) { action(activity, comment, RedditAPI.ACTION_REPORT, changeDataManager); } }) .setNegativeButton(R.string.dialog_cancel, null) .show(); break; case REPLY: { final Intent intent = new Intent(activity, CommentReplyActivity.class); intent.putExtra(CommentReplyActivity.PARENT_ID_AND_TYPE_KEY, comment.getIdAndType()); intent.putExtra(CommentReplyActivity.PARENT_MARKDOWN_KEY, StringEscapeUtils.unescapeHtml4(comment.body)); activity.startActivity(intent); break; } case EDIT: { final Intent intent = new Intent(activity, CommentEditActivity.class); intent.putExtra("commentIdAndType", comment.getIdAndType()); intent.putExtra("commentText", StringEscapeUtils.unescapeHtml4(comment.body)); activity.startActivity(intent); break; } case DELETE: { new AlertDialog.Builder(activity) .setTitle(R.string.accounts_delete) .setMessage(R.string.delete_confirm) .setPositiveButton(R.string.action_delete, new DialogInterface.OnClickListener() { @Override public void onClick(final DialogInterface dialog, final int which) { action(activity, comment, RedditAPI.ACTION_DELETE, changeDataManager); } }) .setNegativeButton(R.string.dialog_cancel, null) .show(); break; } case COMMENT_LINKS: final HashSet<String> linksInComment = comment.computeAllLinks(); if(linksInComment.isEmpty()) { General.quickToast(activity, R.string.error_toast_no_urls_in_comment); } else { final String[] linksArr = linksInComment.toArray(new String[linksInComment.size()]); final AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.setItems(linksArr, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { LinkHandler.onLinkClicked(activity, linksArr[which], false); dialog.dismiss(); } }); final AlertDialog alert = builder.create(); alert.setTitle(R.string.action_comment_links); alert.setCanceledOnTouchOutside(true); alert.show(); } break; case SHARE: final Intent mailer = new Intent(Intent.ACTION_SEND); mailer.setType("text/plain"); mailer.putExtra(Intent.EXTRA_SUBJECT, "Comment by " + comment.author + " on Reddit"); // TODO this currently just dumps the markdown mailer.putExtra(Intent.EXTRA_TEXT, StringEscapeUtils.unescapeHtml4(comment.body) + "\r\n\r\n" + comment.getContextUrl().generateNonJsonUri().toString()); activity.startActivityForResult(Intent.createChooser(mailer, activity.getString(R.string.action_share)), 1); break; case COPY_TEXT: { ClipboardManager manager = (ClipboardManager)activity.getSystemService(Context.CLIPBOARD_SERVICE); // TODO this currently just dumps the markdown manager.setText(StringEscapeUtils.unescapeHtml4(comment.body)); break; } case COPY_URL: { ClipboardManager manager = (ClipboardManager)activity.getSystemService(Context.CLIPBOARD_SERVICE); // TODO this currently just dumps the markdown manager.setText(comment.getContextUrl().context(null).generateNonJsonUri().toString()); break; } case COLLAPSE: { commentListingFragment.handleCommentVisibilityToggle(commentView); break; } case USER_PROFILE: LinkHandler.onLinkClicked(activity, new UserProfileURL(comment.author).toString()); break; case PROPERTIES: CommentPropertiesDialog.newInstance(comment).show(activity.getSupportFragmentManager(), null); break; case GO_TO_COMMENT: { LinkHandler.onLinkClicked(activity, comment.getContextUrl().context(null).toString()); break; } case CONTEXT: { LinkHandler.onLinkClicked(activity, comment.getContextUrl().toString()); break; } case ACTION_MENU: showActionMenu( activity, commentListingFragment, renderableComment, commentView, changeDataManager, comment.isArchived()); break; case BACK: activity.onBackPressed(); break; } } public static void action( final AppCompatActivity activity, final RedditComment comment, final @RedditAPI.RedditAction int action, final RedditChangeDataManager changeDataManager) { final RedditAccount user = RedditAccountManager.getInstance(activity).getDefaultAccount(); if(user.isAnonymous()) { General.quickToast(activity, activity.getString(R.string.error_toast_notloggedin)); return; } final boolean wasUpvoted = changeDataManager.isUpvoted(comment); final boolean wasDownvoted = changeDataManager.isUpvoted(comment); switch(action) { case RedditAPI.ACTION_DOWNVOTE: if(!comment.isArchived()) { changeDataManager.markDownvoted(RRTime.utcCurrentTimeMillis(), comment); } break; case RedditAPI.ACTION_UNVOTE: if(!comment.isArchived()) { changeDataManager.markUnvoted(RRTime.utcCurrentTimeMillis(), comment); } break; case RedditAPI.ACTION_UPVOTE: if(!comment.isArchived()) { changeDataManager.markUpvoted(RRTime.utcCurrentTimeMillis(), comment); } break; case RedditAPI.ACTION_SAVE: changeDataManager.markSaved(RRTime.utcCurrentTimeMillis(), comment, true); break; case RedditAPI.ACTION_UNSAVE: changeDataManager.markSaved(RRTime.utcCurrentTimeMillis(), comment, false); break; } boolean vote = (action == RedditAPI.ACTION_DOWNVOTE | action == RedditAPI.ACTION_UPVOTE | action == RedditAPI.ACTION_UNVOTE); if(comment.isArchived() && vote){ Toast.makeText(activity, R.string.error_archived_vote, Toast.LENGTH_SHORT) .show(); return; } 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 @CacheRequest.RequestFailureType int type, final Throwable t, final Integer status, final String readableMessage) { revertOnFailure(); if(t != null) t.printStackTrace(); final RRError error = General.getGeneralErrorForFailure(context, type, t, status, null); AndroidApi.UI_THREAD_HANDLER.post(new Runnable() { @Override public void run() { General.showResultDialog(activity, error); } }); } @Override protected void onFailure(final APIFailureType type) { revertOnFailure(); final RRError error = General.getGeneralErrorForFailure(context, type); AndroidApi.UI_THREAD_HANDLER.post(new Runnable() { @Override public void run() { General.showResultDialog(activity, error); } }); } @Override protected void onSuccess() { if(action == RedditAPI.ACTION_DELETE) { General.quickToast(context, R.string.delete_success); } } private void revertOnFailure() { switch(action) { case RedditAPI.ACTION_DOWNVOTE: case RedditAPI.ACTION_UNVOTE: case RedditAPI.ACTION_UPVOTE: if(wasUpvoted) { changeDataManager.markUpvoted(RRTime.utcCurrentTimeMillis(), comment); } else if(wasDownvoted) { changeDataManager.markDownvoted(RRTime.utcCurrentTimeMillis(), comment); } else { changeDataManager.markUnvoted(RRTime.utcCurrentTimeMillis(), comment); } case RedditAPI.ACTION_SAVE: changeDataManager.markSaved(RRTime.utcCurrentTimeMillis(), comment, false); break; case RedditAPI.ACTION_UNSAVE: changeDataManager.markSaved(RRTime.utcCurrentTimeMillis(), comment, true); break; } } }, user, comment.getIdAndType(), action, activity); } }