diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/EditTaskDTO.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/EditTaskDTO.java new file mode 100644 index 00000000..4c135b30 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/EditTaskDTO.java @@ -0,0 +1,49 @@ +package gr.thmmy.mthmmy.activities.topic; + +public class EditTaskDTO { + private int position; + private final String url, subject, message, numReplies, seqnum, sc, topic; + + public EditTaskDTO(int position, String url, String subject, String message, String numReplies, String seqnum, String sc, String topic) { + this.position = position; + this.url = url; + this.subject = subject; + this.message = message; + this.numReplies = numReplies; + this.seqnum = seqnum; + this.sc = sc; + this.topic = topic; + } + + public int getPosition() { + return position; + } + + public String getUrl() { + return url; + } + + public String getSubject() { + return subject; + } + + public String getMessage() { + return message; + } + + public String getNumReplies() { + return numReplies; + } + + public String getSeqnum() { + return seqnum; + } + + public String getSc() { + return sc; + } + + public String getTopic() { + return topic; + } +} diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java index 041c26a6..cd8817b3 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java @@ -675,6 +675,7 @@ public class TopicActivity extends BaseActivity { postsList.addAll(localPostsList); topicAdapter.notifyItemRangeInserted(0, postsList.size()); topicAdapter.prepareForDelete(new DeleteTask()); + topicAdapter.prepareForPrepareForEdit(new PrepareForEdit()); progressBar.setVisibility(ProgressBar.INVISIBLE); if (replyPageUrl == null) { @@ -894,7 +895,7 @@ public class TopicActivity extends BaseActivity { @Override protected void onPostExecute(Boolean result) { - postsList.add(null); + postsList.add(Post.newQuickReply()); topicAdapter.notifyItemInserted(postsList.size()); topicAdapter.prepareForReply(new ReplyTask(), topicTitle, numReplies, seqnum, sc, topic, buildedQuotes); @@ -1035,4 +1036,134 @@ public class TopicActivity extends BaseActivity { } } } + + class PrepareForEdit extends AsyncTask { + int position; + String commitEditURL, numReplies, seqnum, sc, topic, postText = ""; + + @Override + protected void onPreExecute() { + progressBar.setVisibility(ProgressBar.VISIBLE); + paginationEnabled(false); + replyFAB.setEnabled(false); + replyFAB.hide(); + bottomNavBar.setVisibility(View.GONE); + topicAdapter.disablePostEditing(); + } + + @Override + protected Boolean doInBackground(Integer... positions) { + Document document; + position = positions[0]; + String url = postsList.get(position).getPostEditURL(); + Request request = new Request.Builder() + .url(url + ";wap2") + .build(); + + try { + Response response = client.newCall(request).execute(); + document = Jsoup.parse(response.body().string()); + + Element message = document.select("textarea").first(); + postText = message.text(); + + commitEditURL = document.select("form").first().attr("action"); + numReplies = replyPageUrl.substring(replyPageUrl.indexOf("num_replies=") + 12); + seqnum = document.select("input[name=seqnum]").first().attr("value"); + sc = document.select("input[name=sc]").first().attr("value"); + topic = document.select("input[name=topic]").first().attr("value"); + + return true; + } catch (IOException | Selector.SelectorParseException e) { + Timber.e(e, "Prepare failed."); + return false; + } + } + + + @Override + protected void onPostExecute(Boolean result) { + postsList.get(position).setPostType(Post.TYPE_EDIT); + topicAdapter.notifyItemChanged(position); + topicAdapter.prepareForEdit(new EditTask(), commitEditURL, numReplies, seqnum, sc, topic, postText); + recyclerView.scrollToPosition(position); + progressBar.setVisibility(ProgressBar.GONE); + } + } + + public class EditTask extends AsyncTask { + EditTaskDTO dto; + + @Override + protected void onPreExecute() { + progressBar.setVisibility(ProgressBar.VISIBLE); + paginationEnabled(false); + replyFAB.setEnabled(false); + } + + @Override + protected Boolean doInBackground(EditTaskDTO... editTaskDTOS) { + dto = editTaskDTOS[0]; + RequestBody postBody = new MultipartBody.Builder() + .setType(MultipartBody.FORM) + .addFormDataPart("message", dto.getMessage()) + .addFormDataPart("num_replies", dto.getNumReplies()) + .addFormDataPart("seqnum", dto.getSeqnum()) + .addFormDataPart("sc", dto.getSc()) + .addFormDataPart("subject", dto.getSubject()) + .addFormDataPart("topic", dto.getTopic()) + .build(); + Request post = new Request.Builder() + .url(dto.getUrl()) + .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36") + .post(postBody) + .build(); + + try { + client.newCall(post).execute(); + Response response = client.newCall(post).execute(); + switch (replyStatus(response)) { + case SUCCESSFUL: + return true; + case NEW_REPLY_WHILE_POSTING: + //TODO this... + return true; + default: + Timber.e("Malformed post. Request string: %s", post.toString()); + return true; + } + } catch (IOException e) { + Timber.e(e, "Edit failed."); + return false; + } + } + + @Override + protected void onPostExecute(Boolean result) { + View view = getCurrentFocus(); + if (view != null) { + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(view.getWindowToken(), 0); + } + + postsList.get(dto.getPosition()).setPostType(Post.TYPE_POST); + topicAdapter.notifyItemChanged(dto.getPosition()); + + progressBar.setVisibility(ProgressBar.GONE); + replyFAB.setVisibility(View.VISIBLE); + bottomNavBar.setVisibility(View.VISIBLE); + + if (!result) + Toast.makeText(TopicActivity.this, "Edit failed!", Toast.LENGTH_SHORT).show(); + paginationEnabled(true); + replyFAB.setEnabled(true); + topicAdapter.enablePostEditing(); + + if (result) { + topicTask = new TopicTask(); + reloadingPage = true; + topicTask.execute(loadedPageUrl); + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java index 345f9c5b..df252fc2 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java @@ -91,13 +91,14 @@ class TopicAdapter extends RecyclerView.Adapter { private TopicActivity.TopicTask topicTask; private TopicActivity.ReplyTask replyTask; private TopicActivity.DeleteTask deleteTask; - private final int VIEW_TYPE_POST = 0; - private final int VIEW_TYPE_QUICK_REPLY = 1; + private TopicActivity.PrepareForEdit prepareForEditTask; + private TopicActivity.EditTask editTask; private final String[] replyDataHolder = new String[2]; private final int replySubject = 0, replyText = 1; - private String numReplies, seqnum, sc, topic, buildedQuotes; + private String commitEditURL, numReplies, seqnum, sc, topic, buildedQuotes, postText; private boolean canReply = false; + private boolean postEditingDisabled = false; /** * @param context the context of the {@link RecyclerView} @@ -136,18 +137,33 @@ class TopicAdapter extends RecyclerView.Adapter { this.deleteTask = deleteTask; } + void prepareForPrepareForEdit(TopicActivity.PrepareForEdit prepareForEditTask) { + this.prepareForEditTask = prepareForEditTask; + } + + void prepareForEdit(TopicActivity.EditTask editTask, String commitEditURL, String numReplies, String seqnum, String sc, + String topic, String postText) { + this.commitEditURL = commitEditURL; + this.editTask = editTask; + this.numReplies = numReplies; + this.seqnum = seqnum; + this.sc = sc; + this.topic = topic; + this.postText = postText; + } + @Override public int getItemViewType(int position) { - return postsList.get(position) == null ? VIEW_TYPE_QUICK_REPLY : VIEW_TYPE_POST; + return postsList.get(position).getPostType(); } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - if (viewType == VIEW_TYPE_POST) { + if (viewType == Post.TYPE_POST) { View itemView = LayoutInflater.from(parent.getContext()) .inflate(R.layout.activity_topic_post_row, parent, false); return new PostViewHolder(itemView); - } else if (viewType == VIEW_TYPE_QUICK_REPLY) { + } else if (viewType == Post.TYPE_QUICK_REPLY) { View view = LayoutInflater.from(parent.getContext()). inflate(R.layout.activity_topic_quick_reply_row, parent, false); view.findViewById(R.id.quick_reply_submit).setEnabled(true); @@ -175,6 +191,12 @@ class TopicAdapter extends RecyclerView.Adapter { replyDataHolder[replyText] = buildedQuotes; return new QuickReplyViewHolder(view, new CustomEditTextListener(replySubject), new CustomEditTextListener(replyText)); + } else if (viewType == Post.TYPE_EDIT) { + View view = LayoutInflater.from(parent.getContext()). + inflate(R.layout.activity_topic_edit_row, parent, false); + view.findViewById(R.id.edit_message_submit).setEnabled(true); + + return new EditMessageViewHolder(view); } return null; } @@ -470,6 +492,19 @@ class TopicAdapter extends RecyclerView.Adapter { }); } + final TextView editPostButton = popUpContent.findViewById(R.id.edit_post); + + if (postEditingDisabled || currentPost.getPostEditURL() == null || currentPost.getPostEditURL().equals("")) { + editPostButton.setVisibility(View.GONE); + } else { + editPostButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + prepareForEditTask.execute(position); + } + }); + } + //Displays the popup popUp.showAsDropDown(holder.overflowButton); } @@ -518,28 +553,69 @@ class TopicAdapter extends RecyclerView.Adapter { .transform(new CircleTransform()) .into(holder.thumbnail); holder.username.setText(getSessionManager().getUsername()); - holder.quickReplySubject.setText(replyDataHolder[replySubject]); - if (replyDataHolder[replyText] != null && !Objects.equals(replyDataHolder[replyText], "")) + + if (replyDataHolder[replyText] != null && !Objects.equals(replyDataHolder[replyText], "")) { holder.quickReply.setText(replyDataHolder[replyText]); + holder.quickReplySubject.setText(replyDataHolder[replySubject]); + + holder.submitButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (holder.quickReplySubject.getText().toString().isEmpty()) return; + if (holder.quickReply.getText().toString().isEmpty()) return; + holder.submitButton.setEnabled(false); + replyTask.execute(holder.quickReplySubject.getText().toString(), + holder.quickReply.getText().toString(), numReplies, seqnum, sc, topic); + + holder.quickReplySubject.getText().clear(); + holder.quickReplySubject.setText("Re: " + topicTitle); + holder.quickReply.getText().clear(); + holder.submitButton.setEnabled(true); + } + }); + } + + if (backPressHidden) { + holder.quickReply.requestFocus(); + backPressHidden = false; + } + } else if (currentHolder instanceof EditMessageViewHolder) { + final EditMessageViewHolder holder = (EditMessageViewHolder) currentHolder; + + //noinspection ConstantConditions + Picasso.with(context) + .load(getSessionManager().getAvatarLink()) + .resize(THUMBNAIL_SIZE, THUMBNAIL_SIZE) + .centerCrop() + .error(ResourcesCompat.getDrawable(context.getResources() + , R.drawable.ic_default_user_thumbnail, null)) + .placeholder(ResourcesCompat.getDrawable(context.getResources() + , R.drawable.ic_default_user_thumbnail, null)) + .transform(new CircleTransform()) + .into(holder.thumbnail); + holder.username.setText(getSessionManager().getUsername()); + + holder.editSubject.setText(postsList.get(position).getSubject()); + holder.editMessage.setText(postText); holder.submitButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - if (holder.quickReplySubject.getText().toString().isEmpty()) return; - if (holder.quickReply.getText().toString().isEmpty()) return; + if (holder.editSubject.getText().toString().isEmpty()) return; + if (holder.editMessage.getText().toString().isEmpty()) return; holder.submitButton.setEnabled(false); - replyTask.execute(holder.quickReplySubject.getText().toString(), - holder.quickReply.getText().toString(), numReplies, seqnum, sc, topic); + editTask.execute(new EditTaskDTO(position, commitEditURL, holder.editSubject.getText().toString(), + holder.editMessage.getText().toString(), numReplies, seqnum, sc, topic)); - holder.quickReplySubject.getText().clear(); - holder.quickReplySubject.setText("Re: " + topicTitle); - holder.quickReply.getText().clear(); + holder.editSubject.getText().clear(); + holder.editSubject.setText(postsList.get(position).getSubject()); holder.submitButton.setEnabled(true); } }); + if (backPressHidden) { - holder.quickReply.requestFocus(); + holder.editMessage.requestFocus(); backPressHidden = false; } } @@ -635,6 +711,23 @@ class TopicAdapter extends RecyclerView.Adapter { } } + private static class EditMessageViewHolder extends RecyclerView.ViewHolder { + final ImageView thumbnail; + final TextView username; + final EditText editMessage, editSubject; + final AppCompatImageButton submitButton; + + public EditMessageViewHolder(View editView) { + super(editView); + + thumbnail = editView.findViewById(R.id.thumbnail); + username = editView.findViewById(R.id.username); + editMessage = editView.findViewById(R.id.edit_message_text); + editSubject = editView.findViewById(R.id.edit_message_subject); + submitButton = editView.findViewById(R.id.edit_message_submit); + } + } + /** * This class is used to handle link clicks in WebViews. When link url is one that the app can * handle internally, it does. Otherwise user is prompt to open the link in a browser. @@ -773,4 +866,12 @@ class TopicAdapter extends RecyclerView.Adapter { return context.getResources().getString(R.string.fa_file); } + + public void disablePostEditing() { + postEditingDisabled = true; + } + + public void enablePostEditing() { + postEditingDisabled = false; + } } \ No newline at end of file diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicParser.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicParser.java index 3ead462c..a753fc8c 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicParser.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicParser.java @@ -157,7 +157,7 @@ class TopicParser { //Variables for Post constructor String p_userName, p_thumbnailURL, p_subject, p_post, p_postDate, p_profileURL, p_rank, p_specialRank, p_gender, p_personalText, p_numberOfPosts, p_postLastEditDate, - p_postURL, p_deletePostURL; + p_postURL, p_deletePostURL, p_editPostURL; int p_postNum, p_postIndex, p_numberOfStars, p_userColor; boolean p_isDeleted = false; ArrayList p_attachedFiles; @@ -174,6 +174,7 @@ class TopicParser { p_attachedFiles = new ArrayList<>(); p_postLastEditDate = null; p_deletePostURL = null; + p_editPostURL = null; //Language independent parsing //Finds thumbnail url @@ -306,6 +307,12 @@ class TopicParser { p_deletePostURL = postDelete.attr("href"); } + //Finds post modify url + Element postEdit = thisRow.select("a:has(img[alt='Modify message'])").first(); + if (postEdit != null) { + p_editPostURL = postEdit.attr("href"); + } + //Finds post's submit date Element postDate = thisRow.select("div.smalltext:matches(on:)").first(); p_postDate = postDate.text(); @@ -431,13 +438,13 @@ class TopicParser { parsedPostsList.add(new Post(p_thumbnailURL, p_userName, p_subject, p_post, p_postIndex , p_postNum, p_postDate, p_profileURL, p_rank, p_specialRank, p_gender , p_numberOfPosts, p_personalText, p_numberOfStars, p_userColor - , p_attachedFiles, p_postLastEditDate, p_postURL, p_deletePostURL)); + , p_attachedFiles, p_postLastEditDate, p_postURL, p_deletePostURL, p_editPostURL, Post.TYPE_POST)); } else { //Deleted user //Add new post in postsList, only standard information needed parsedPostsList.add(new Post(p_thumbnailURL, p_userName, p_subject, p_post , p_postIndex , p_postNum, p_postDate, p_userColor, p_attachedFiles - , p_postLastEditDate, p_postURL, p_deletePostURL)); + , p_postLastEditDate, p_postURL, p_deletePostURL, p_editPostURL, Post.TYPE_POST)); } } return parsedPostsList; diff --git a/app/src/main/java/gr/thmmy/mthmmy/model/Post.java b/app/src/main/java/gr/thmmy/mthmmy/model/Post.java index 3206061d..8641f288 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/model/Post.java +++ b/app/src/main/java/gr/thmmy/mthmmy/model/Post.java @@ -16,6 +16,10 @@ import java.util.Objects; * previous fields.

*/ public class Post { + public static final int TYPE_POST = 0; + public static final int TYPE_QUICK_REPLY = 1; + public static final int TYPE_EDIT = 2; + //Standard info (exists in every post) private final String thumbnailUrl; private final String author; @@ -30,6 +34,8 @@ public class Post { private final String lastEdit; private final String postURL; private final String postDeleteURL; + private final String postEditURL; + private int postType; //Extra info private final String profileURL; @@ -63,6 +69,8 @@ public class Post { lastEdit = null; postURL = null; postDeleteURL = null; + postEditURL = null; + postType = -1; } /** @@ -87,14 +95,14 @@ public class Post { * @param userColor author's user color * @param attachedFiles post's attached files * @param lastEdit post's last edit date - * @param postURL post's URL + * @param postURL post's URL */ public Post(@Nullable String thumbnailUrl, String author, String subject, String content , int postIndex, int postNumber, String postDate, String profileURl, @Nullable String rank , @Nullable String special_rank, @Nullable String gender, @Nullable String numberOfPosts , @Nullable String personalText, int numberOfStars, int userColor , @Nullable ArrayList attachedFiles, @Nullable String lastEdit, String postURL - , @Nullable String postDeleteURL) { + , @Nullable String postDeleteURL, @Nullable String postEditURL, int postType) { if (Objects.equals(thumbnailUrl, "")) this.thumbnailUrl = null; else this.thumbnailUrl = thumbnailUrl; this.author = author; @@ -116,6 +124,8 @@ public class Post { this.numberOfStars = numberOfStars; this.postURL = postURL; this.postDeleteURL = postDeleteURL; + this.postEditURL = postEditURL; + this.postType = postType; } /** @@ -138,7 +148,7 @@ public class Post { public Post(@Nullable String thumbnailUrl, String author, String subject, String content , int postIndex, int postNumber, String postDate, int userColor , @Nullable ArrayList attachedFiles, @Nullable String lastEdit, String postURL - , @Nullable String postDeleteURL) { + , @Nullable String postDeleteURL, @Nullable String postEditURL, int postType) { if (Objects.equals(thumbnailUrl, "")) this.thumbnailUrl = null; else this.thumbnailUrl = thumbnailUrl; this.author = author; @@ -160,6 +170,13 @@ public class Post { numberOfStars = 0; this.postURL = postURL; this.postDeleteURL = postDeleteURL; + this.postEditURL = postEditURL; + this.postType = postType; + } + + public static Post newQuickReply() { + return new Post(null, null, null, null, 0, 0, null, + 0, null, null, null, null, null, TYPE_QUICK_REPLY); } //Getters @@ -358,4 +375,22 @@ public class Post { public String getPostDeleteURL() { return postDeleteURL; } + + /** + * Gets this post's modify url. + * + * @return post's edit url + */ + @Nullable + public String getPostEditURL() { + return postEditURL; + } + + public int getPostType() { + return postType; + } + + public void setPostType(int postType) { + this.postType = postType; + } } diff --git a/app/src/main/res/drawable/ic_edit_white_24dp.xml b/app/src/main/res/drawable/ic_edit_white_24dp.xml new file mode 100644 index 00000000..46462b57 --- /dev/null +++ b/app/src/main/res/drawable/ic_edit_white_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/activity_topic_edit_row.xml b/app/src/main/res/layout/activity_topic_edit_row.xml new file mode 100644 index 00000000..8251e987 --- /dev/null +++ b/app/src/main/res/layout/activity_topic_edit_row.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_topic_overflow_menu.xml b/app/src/main/res/layout/activity_topic_overflow_menu.xml index 9ea4b0d2..324972d4 100644 --- a/app/src/main/res/layout/activity_topic_overflow_menu.xml +++ b/app/src/main/res/layout/activity_topic_overflow_menu.xml @@ -34,4 +34,19 @@ android:text="@string/post_delete_button" android:textColor="@color/primary_text" /> + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_topic_quick_reply_row.xml b/app/src/main/res/layout/activity_topic_quick_reply_row.xml index c77d3c07..29c342f0 100644 --- a/app/src/main/res/layout/activity_topic_quick_reply_row.xml +++ b/app/src/main/res/layout/activity_topic_quick_reply_row.xml @@ -73,7 +73,7 @@ android:layout_height="wrap_content" android:layout_below="@+id/username" android:layout_toEndOf="@+id/thumbnail_holder" - android:hint="@string/quick_reply_subject" + android:hint="@string/subject" android:inputType="textMultiLine" android:maxLength="80" android:textSize="10sp" @@ -109,7 +109,7 @@ android:layout_marginBottom="5dp" android:layout_marginEnd="5dp" android:background="@color/card_background" - android:contentDescription="@string/quick_reply_submit" + android:contentDescription="@string/submit" android:src="@drawable/ic_send" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 309f24ce..879671a4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -42,6 +42,7 @@ Overflow menu button Share Delete + Edit #%1$d first previous @@ -49,8 +50,9 @@ next last Quick reply… - Subject… - Submit + Subject… + Submit + Message… Username