diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/NewPostSeparator.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/NewPostSeparator.java new file mode 100644 index 00000000..0c313f01 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/NewPostSeparator.java @@ -0,0 +1,5 @@ +package gr.thmmy.mthmmy.activities.topic; + +public class NewPostSeparator extends TopicRecyclerViewItem { + public static final int TYPE_NEW_MESSAGE_SEPARATOR = 3; +} 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 84f35817..0228e56a 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 @@ -80,7 +80,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo /** * Holds a list of this topic's posts */ - private ArrayList postsList; + private ArrayList topicRecyclerviewItems; //Reply related private FloatingActionButton replyFAB; //Topic's pages related @@ -165,7 +165,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo progressBar = findViewById(R.id.progressBar); emojiKeyboard = findViewById(R.id.emoji_keyboard); - postsList = new ArrayList<>(); + topicRecyclerviewItems = new ArrayList<>(); recyclerView = findViewById(R.id.topic_recycler_view); recyclerView.setHasFixedSize(true); @@ -174,7 +174,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo getApplicationContext(), topicPageUrl); recyclerView.setLayoutManager(layoutManager); - topicAdapter = new TopicAdapter(this, postsList); + topicAdapter = new TopicAdapter(this, topicRecyclerviewItems); recyclerView.setAdapter(topicAdapter); replyFAB = findViewById(R.id.topic_fab); @@ -275,15 +275,15 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo } return; } else if (viewModel.isWritingReply()) { - postsList.remove(postsList.size() - 1); - topicAdapter.notifyItemRemoved(postsList.size()); + topicRecyclerviewItems.remove(topicRecyclerviewItems.size() - 1); + topicAdapter.notifyItemRemoved(topicRecyclerviewItems.size()); topicAdapter.setBackButtonHidden(); viewModel.setWritingReply(false); replyFAB.show(); bottomNavBar.setVisibility(View.VISIBLE); return; } else if (viewModel.isEditingPost()) { - postsList.get(viewModel.getPostBeingEditedPosition()).setPostType(Post.TYPE_POST); + ((Post) topicRecyclerviewItems.get(viewModel.getPostBeingEditedPosition())).setPostType(Post.TYPE_POST); topicAdapter.notifyItemChanged(viewModel.getPostBeingEditedPosition()); topicAdapter.setBackButtonHidden(); viewModel.setEditingPost(false); @@ -533,7 +533,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo replyFAB.show(); bottomNavBar.setVisibility(View.VISIBLE); viewModel.setWritingReply(false); - if ((postsList.get(postsList.size() - 1).getPostNumber() + 1) % 15 == 0) { + if ((((Post) topicRecyclerviewItems.get(topicRecyclerviewItems.size() - 1)).getPostNumber() + 1) % 15 == 0) { Timber.i("Reply was posted in new page. Switching to last page."); viewModel.loadUrl(ParseHelpers.getBaseURL(viewModel.getTopicUrl()) + "." + 2147483647); } else { @@ -542,8 +542,8 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo } else { Timber.w("Post reply unsuccessful"); Toast.makeText(getBaseContext(), "Post failed!", Toast.LENGTH_SHORT).show(); - recyclerView.getChildAt(postsList.size() - 1).setAlpha(1); - recyclerView.getChildAt(postsList.size() - 1).setEnabled(true); + recyclerView.getChildAt(topicRecyclerviewItems.size() - 1).setAlpha(1); + recyclerView.getChildAt(topicRecyclerviewItems.size() - 1).setEnabled(true); } } }); @@ -576,7 +576,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo if (result) { Timber.i("Post edit successful"); - postsList.get(position).setPostType(Post.TYPE_POST); + ((Post) topicRecyclerviewItems.get(position)).setPostType(Post.TYPE_POST); topicAdapter.notifyItemChanged(position); replyFAB.show(); bottomNavBar.setVisibility(View.VISIBLE); @@ -629,20 +629,25 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo viewModel.getPostsList().observe(this, postList -> { if (postList == null) progressBar.setVisibility(ProgressBar.VISIBLE); recyclerView.getRecycledViewPool().clear(); //Avoid inconsistency detected bug - postsList.clear(); - postsList.addAll(postList); + topicRecyclerviewItems.clear(); + topicRecyclerviewItems.addAll(postList); topicAdapter.notifyDataSetChanged(); }); - /*viewModel.getFocusedPostIndex().observe(this, focusedPostIndex -> { + viewModel.getFocusedPostIndex().observe(this, focusedPostIndex -> { if (focusedPostIndex == null) return; - recyclerView.scrollToPosition(focusedPostIndex); - });*/ + if (viewModel.isFocusedPostLastSeenMessage() && focusedPostIndex != viewModel.postCount() - 1) { + topicRecyclerviewItems.add(focusedPostIndex, new NewPostSeparator()); + topicAdapter.notifyItemInserted(focusedPostIndex); + } + //recyclerView.scrollToPosition(focusedPostIndex); + }); viewModel.getTopicTaskResultCode().observe(this, resultCode -> { if (resultCode == null) return; progressBar.setVisibility(ProgressBar.GONE); switch (resultCode) { case SUCCESS: Timber.i("Successfully loaded topic with URL %s", viewModel.getTopicUrl()); + Timber.i("load " + viewModel.isFocusedPostLastSeenMessage()); paginationEnabled(true); break; case NETWORK_ERROR: @@ -688,9 +693,9 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo Timber.i("Prepare for reply successful"); //prepare for a reply viewModel.setWritingReply(true); - postsList.add(Post.newQuickReply()); - topicAdapter.notifyItemInserted(postsList.size()); - recyclerView.scrollToPosition(postsList.size() - 1); + topicRecyclerviewItems.add(Post.newQuickReply()); + topicAdapter.notifyItemInserted(topicRecyclerviewItems.size()); + recyclerView.scrollToPosition(topicRecyclerviewItems.size() - 1); replyFAB.hide(); bottomNavBar.setVisibility(View.GONE); } else { @@ -703,7 +708,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo if (result != null && result.isSuccessful()) { Timber.i("Prepare for edit successful"); viewModel.setEditingPost(true); - postsList.get(result.getPosition()).setPostType(Post.TYPE_EDIT); + ((Post) topicRecyclerviewItems.get(result.getPosition())).setPostType(Post.TYPE_EDIT); topicAdapter.notifyItemChanged(result.getPosition()); recyclerView.scrollToPosition(result.getPosition()); replyFAB.hide(); 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 4c853526..11d780f6 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 @@ -35,7 +35,7 @@ import android.widget.TextView; import com.squareup.picasso.Picasso; -import java.util.List; +import java.util.ArrayList; import java.util.Objects; import gr.thmmy.mthmmy.R; @@ -74,16 +74,16 @@ class TopicAdapter extends RecyclerView.Adapter { private final Context context; private final OnPostFocusChangeListener postFocusListener; private final EmojiKeyboard.EmojiKeyboardOwner emojiKeyboardOwner; - private final List postsList; + private final ArrayList topicRecyclerViewItems; private TopicViewModel viewModel; /** * @param context the context of the {@link RecyclerView} - * @param postsList List of {@link Post} objects to use + * @param topicRecyclerViewItems List of {@link Post} objects to use */ - TopicAdapter(TopicActivity context, List postsList) { + TopicAdapter(TopicActivity context, ArrayList topicRecyclerViewItems) { this.context = context; - this.postsList = postsList; + this.topicRecyclerViewItems = topicRecyclerViewItems; this.postFocusListener = context; this.emojiKeyboardOwner = context; @@ -94,7 +94,9 @@ class TopicAdapter extends RecyclerView.Adapter { @Override public int getItemViewType(int position) { - return postsList.get(position).getPostType(); + if (topicRecyclerViewItems.get(position) instanceof NewPostSeparator) + return NewPostSeparator.TYPE_NEW_MESSAGE_SEPARATOR; + return ((Post) topicRecyclerViewItems.get(position)).getPostType(); } @NonNull @@ -130,6 +132,10 @@ class TopicAdapter extends RecyclerView.Adapter { editPostEdittext.requestFocus(); return new EditMessageViewHolder(view); + } else if (viewType == NewPostSeparator.TYPE_NEW_MESSAGE_SEPARATOR) { + View view = LayoutInflater.from(parent.getContext()). + inflate(R.layout.activity_topic_new_message_separator, parent, false); + return new NewMessageSeparatorViewHolder(view); } else { throw new IllegalArgumentException("Unknown view type"); } @@ -140,7 +146,7 @@ class TopicAdapter extends RecyclerView.Adapter { public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder currentHolder, final int position) { if (currentHolder instanceof PostViewHolder) { - final Post currentPost = postsList.get(position); + final Post currentPost = (Post) topicRecyclerViewItems.get(position); final PostViewHolder holder = (PostViewHolder) currentHolder; //Post's WebView parameters @@ -396,7 +402,7 @@ class TopicAdapter extends RecyclerView.Adapter { editPostButton.setVisibility(View.GONE); } else { editPostButton.setOnClickListener(v -> { - viewModel.prepareForEdit(position, postsList.get(position).getPostEditURL()); + viewModel.prepareForEdit(position, ((Post) topicRecyclerViewItems.get(position)).getPostEditURL()); popUp.dismiss(); }); } @@ -504,7 +510,7 @@ class TopicAdapter extends RecyclerView.Adapter { .transform(new CircleTransform()) .into(holder.thumbnail); holder.username.setText(getSessionManager().getUsername()); - holder.editSubject.setText(postsList.get(position).getSubject()); + holder.editSubject.setText(((Post) topicRecyclerViewItems.get(position)).getSubject()); holder.editEditor.setEmojiKeyboardOwner(emojiKeyboardOwner); InputConnection ic = holder.editEditor.getInputConnection(); @@ -542,7 +548,7 @@ class TopicAdapter extends RecyclerView.Adapter { @Override public int getItemCount() { - return postsList.size(); + return topicRecyclerViewItems.size(); } /** @@ -631,6 +637,12 @@ class TopicAdapter extends RecyclerView.Adapter { } } + static class NewMessageSeparatorViewHolder extends RecyclerView.ViewHolder { + NewMessageSeparatorViewHolder(View itemView) { + super(itemView); + } + } + /** * 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. @@ -674,8 +686,9 @@ class TopicAdapter extends RecyclerView.Adapter { if (tmpUrlSbstr.contains("msg")) tmpUrlSbstr = tmpUrlSbstr.substring(0, tmpUrlSbstr.indexOf("msg") - 1); int testAgainst = Integer.parseInt(tmpUrlSbstr); - for (int i = 0; i < postsList.size(); i++) { - if (postsList.get(i).getPostIndex() == testAgainst) { + for (int i = 0; i < topicRecyclerViewItems.size(); i++) { + if (!(topicRecyclerViewItems.get(i) instanceof Post)) continue; + if (((Post) topicRecyclerViewItems.get(i)).getPostIndex() == testAgainst) { //same page Timber.e(Integer.toString(i)); postFocusListener.onPostFocusChange(i); diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicRecyclerViewItem.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicRecyclerViewItem.java new file mode 100644 index 00000000..8c31c8bb --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicRecyclerViewItem.java @@ -0,0 +1,4 @@ +package gr.thmmy.mthmmy.activities.topic; + +public class TopicRecyclerViewItem { +} diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/TopicTask.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/TopicTask.java index 7c5e5d1f..c2b53086 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/TopicTask.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/TopicTask.java @@ -45,13 +45,17 @@ public class TopicTask extends AsyncTask { //Finds the index of message focus if present int postFocus = 0; - { - if (newPageUrl.contains("msg")) { - String tmp = newPageUrl.substring(newPageUrl.indexOf("msg") + 3); - if (tmp.contains(";")) - postFocus = Integer.parseInt(tmp.substring(0, tmp.indexOf(";"))); - else if (tmp.contains("#")) - postFocus = Integer.parseInt(tmp.substring(0, tmp.indexOf("#"))); + boolean focusedPostLastSeenMessage = false; + if (newPageUrl.contains("msg")) { + String tmp = newPageUrl.substring(newPageUrl.indexOf("msg") + 3); + if (tmp.contains(";")) { + postFocus = Integer.parseInt(tmp.substring(0, tmp.indexOf(";"))); + if (newPageUrl.contains("topicseen")) + focusedPostLastSeenMessage = true; + } else if (tmp.contains("#")) { + postFocus = Integer.parseInt(tmp.substring(0, tmp.indexOf("#"))); + if (newPageUrl.contains("topicseen")) + focusedPostLastSeenMessage = true; } } @@ -105,18 +109,18 @@ public class TopicTask extends AsyncTask { } } return new TopicTaskResult(ResultCode.SUCCESS, topicTitle, replyPageUrl, newPostsList, loadedPageTopicId, - currentPageIndex, pageCount, focusedPostIndex, topicTreeAndMods, topicViewers); + currentPageIndex, pageCount, focusedPostIndex, topicTreeAndMods, topicViewers, focusedPostLastSeenMessage); } catch (IOException e) { return new TopicTaskResult(ResultCode.NETWORK_ERROR, null, null, null, - 0, 0, 0, 0, null, null); + 0, 0, 0, 0, null, null, false); } catch (Exception e) { if (isUnauthorized(topic)) { return new TopicTaskResult(ResultCode.UNAUTHORIZED, null, null, null, - 0, 0, 0, 0, null, null); + 0, 0, 0, 0, null, null, false); } else { Timber.e(e, "Topic parse failed"); return new TopicTaskResult(ResultCode.PARSING_ERROR, null, null, null, - 0, 0, 0, 0, null, null); + 0, 0, 0, 0, null, null, false); } } } @@ -139,6 +143,7 @@ public class TopicTask extends AsyncTask { public interface TopicTaskObserver { void onTopicTaskStarted(); + void onTopicTaskCancelled(); } diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/TopicTaskResult.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/TopicTaskResult.java index c713da1f..2e098c16 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/TopicTaskResult.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/TopicTaskResult.java @@ -36,11 +36,12 @@ public class TopicTaskResult { //Topic's info related private final String topicTreeAndMods; private final String topicViewers; + private final boolean focusedPostLastSeenMessage; public TopicTaskResult(TopicTask.ResultCode resultCode, String topicTitle, String replyPageUrl, ArrayList newPostsList, int loadedPageTopicId, int currentPageIndex, int pageCount, int focusedPostIndex, String topicTreeAndMods, - String topicViewers) { + String topicViewers, boolean focusedPostLastSeenMessage) { this.resultCode = resultCode; this.topicTitle = topicTitle; this.replyPageUrl = replyPageUrl; @@ -51,6 +52,7 @@ public class TopicTaskResult { this.focusedPostIndex = focusedPostIndex; this.topicTreeAndMods = topicTreeAndMods; this.topicViewers = topicViewers; + this.focusedPostLastSeenMessage = focusedPostLastSeenMessage; } public TopicTask.ResultCode getResultCode() { @@ -92,4 +94,8 @@ public class TopicTaskResult { public String getTopicViewers() { return topicViewers; } + + public boolean isFocusedPostLastSeenMessage() { + return focusedPostLastSeenMessage; + } } 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 078386dc..5a69e6a5 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/model/Post.java +++ b/app/src/main/java/gr/thmmy/mthmmy/model/Post.java @@ -5,6 +5,8 @@ import android.support.annotation.Nullable; import java.util.ArrayList; import java.util.Objects; +import gr.thmmy.mthmmy.activities.topic.TopicRecyclerViewItem; + /** * Class that defines a topic's post. All member variables are declared final (thus no setters are * supplied). Class has two constructors and getter methods for all variables. @@ -15,7 +17,7 @@ import java.util.Objects; * gender, number of posts, personal text and number of start to be described in addition to * previous fields.

*/ -public class Post { +public class Post extends TopicRecyclerViewItem { public static final int TYPE_POST = 0; public static final int TYPE_QUICK_REPLY = 1; public static final int TYPE_EDIT = 2; diff --git a/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java b/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java index 96cecf4a..fbc40ece 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java +++ b/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java @@ -73,6 +73,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa private String topicUrl; private int currentPageIndex; private int pageCount; + private boolean focusedPostLastSeenMessage; private MutableLiveData prepareForReplyResult = new MutableLiveData<>(); private MutableLiveData prepareForEditResult = new MutableLiveData<>(); @@ -187,6 +188,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa if (result.getResultCode() == TopicTask.ResultCode.SUCCESS) { currentPageIndex = result.getCurrentPageIndex(); pageCount = result.getPageCount(); + focusedPostLastSeenMessage = result.isFocusedPostLastSeenMessage(); topicTreeAndMods.setValue(result.getTopicTreeAndMods()); topicViewers.setValue(result.getTopicViewers()); pageTopicId.setValue(result.getLoadedPageTopicId()); @@ -392,4 +394,8 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa throw new NullPointerException("No page has been loaded yet!"); return postsList.getValue().size(); } + + public boolean isFocusedPostLastSeenMessage() { + return focusedPostLastSeenMessage; + } } diff --git a/app/src/main/res/layout/activity_topic_new_message_separator.xml b/app/src/main/res/layout/activity_topic_new_message_separator.xml new file mode 100644 index 00000000..b7e5507f --- /dev/null +++ b/app/src/main/res/layout/activity_topic_new_message_separator.xml @@ -0,0 +1,28 @@ + + + + + + + + + \ No newline at end of file