Add last seen indicator

multisite
Floens 10 years ago
parent 3dfa729579
commit 754802392b
  1. 2
      Clover/app/src/main/java/org/floens/chan/chan/ChanLoader.java
  2. 2
      Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java
  3. 132
      Clover/app/src/main/java/org/floens/chan/ui/adapter/PostAdapter.java
  4. 1
      Clover/app/src/main/java/org/floens/chan/ui/controller/ViewThreadController.java
  5. 22
      Clover/app/src/main/res/layout/cell_post_last_seen.xml
  6. 1
      Clover/app/src/main/res/values/attrs.xml
  7. 1
      Clover/app/src/main/res/values/styles.xml

@ -168,6 +168,8 @@ public class ChanLoader implements Response.ErrorListener, Response.Listener<Cha
for (ChanLoaderCallback l : listeners) {
l.onChanLoaderData(thread);
}
requestMoreData();
}
/**

@ -278,6 +278,8 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt
}
threadPresenterCallback.showNewPostsNotification(false, -1);
// Update the last seen indicator
showPosts();
}
public void scrollTo(int position, boolean smooth) {

@ -36,6 +36,7 @@ public class PostAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_POST = 0;
private static final int TYPE_STATUS = 1;
private static final int TYPE_POST_STUB = 2;
private static final int TYPE_LAST_SEEN = 3;
private final PostAdapterCallback postAdapterCallback;
private final PostCellInterface.PostCellCallback postCellCallback;
@ -50,6 +51,8 @@ public class PostAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private String highlightedPostId;
private int highlightedPostNo = -1;
private String highlightedPostTripcode;
private int lastSeenIndicatorPosition = -1;
private boolean bound;
private PostCellInterface.PostViewMode postViewMode;
@ -64,58 +67,86 @@ public class PostAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_POST) {
int layout = 0;
switch (postViewMode) {
case LIST:
layout = R.layout.cell_post;
break;
case CARD:
layout = R.layout.cell_post_card;
break;
}
PostCellInterface postCell = (PostCellInterface) LayoutInflater.from(parent.getContext()).inflate(layout, parent, false);
return new PostViewHolder(postCell);
} else if (viewType == TYPE_POST_STUB) {
return new PostViewHolder((PostCellInterface) LayoutInflater.from(parent.getContext()).inflate(R.layout.cell_post_stub, parent, false));
} else {
StatusViewHolder statusViewHolder = new StatusViewHolder((ThreadStatusCell) LayoutInflater.from(parent.getContext()).inflate(R.layout.cell_thread_status, parent, false));
statusViewHolder.threadStatusCell.setCallback(statusCellCallback);
statusViewHolder.threadStatusCell.setError(error);
return statusViewHolder;
switch (viewType) {
case TYPE_POST:
int layout = 0;
switch (postViewMode) {
case LIST:
layout = R.layout.cell_post;
break;
case CARD:
layout = R.layout.cell_post_card;
break;
}
PostCellInterface postCell = (PostCellInterface) LayoutInflater.from(parent.getContext()).inflate(layout, parent, false);
return new PostViewHolder(postCell);
case TYPE_POST_STUB:
return new PostViewHolder((PostCellInterface) LayoutInflater.from(parent.getContext()).inflate(R.layout.cell_post_stub, parent, false));
case TYPE_LAST_SEEN:
return new LastSeenViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.cell_post_last_seen, parent, false));
case TYPE_STATUS:
StatusViewHolder statusViewHolder = new StatusViewHolder((ThreadStatusCell) LayoutInflater.from(parent.getContext()).inflate(R.layout.cell_thread_status, parent, false));
statusViewHolder.threadStatusCell.setCallback(statusCellCallback);
statusViewHolder.threadStatusCell.setError(error);
return statusViewHolder;
default:
throw new IllegalStateException();
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
int itemViewType = getItemViewType(position);
if (itemViewType == TYPE_POST || itemViewType == TYPE_POST_STUB) {
PostViewHolder postViewHolder = (PostViewHolder) holder;
Post post = displayList.get(position);
boolean highlight = post == highlightedPost || post.id.equals(highlightedPostId) || post.no == highlightedPostNo || post.tripcode.equals(highlightedPostTripcode);
postViewHolder.postView.setPost(null, post, postCellCallback, highlight, -1, postViewMode);
} else if (itemViewType == TYPE_STATUS) {
((StatusViewHolder) holder).threadStatusCell.update();
onScrolledToBottom();
switch (itemViewType) {
case TYPE_POST:
case TYPE_POST_STUB:
PostViewHolder postViewHolder = (PostViewHolder) holder;
Post post = displayList.get(getPostPosition(position));
boolean highlight = post == highlightedPost || post.id.equals(highlightedPostId) || post.no == highlightedPostNo || post.tripcode.equals(highlightedPostTripcode);
postViewHolder.postView.setPost(null, post, postCellCallback, highlight, -1, postViewMode);
break;
case TYPE_STATUS:
((StatusViewHolder) holder).threadStatusCell.update();
// Avoid calling in the RecyclerView layout pass
holder.itemView.post(new Runnable() {
@Override
public void run() {
onScrolledToBottom();
}
});
break;
case TYPE_LAST_SEEN:
break;
}
}
@Override
public int getItemCount() {
int size = displayList.size();
if (showStatusView()) {
return displayList.size() + 1;
} else {
return displayList.size();
size++;
}
if (lastSeenIndicatorPosition >= 0) {
size++;
}
return size;
}
@Override
public int getItemViewType(int position) {
if (showStatusView() && position == getItemCount() - 1) {
if (position == lastSeenIndicatorPosition) {
return TYPE_LAST_SEEN;
} else if (showStatusView() && position == getItemCount() - 1) {
return TYPE_STATUS;
} else {
Post post = displayList.get(position);
Post post = displayList.get(getPostPosition(position));
if (post.filterStub) {
return TYPE_POST_STUB;
} else {
@ -129,12 +160,15 @@ public class PostAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
int itemViewType = getItemViewType(position);
if (itemViewType == TYPE_STATUS) {
return -1;
} else if (itemViewType == TYPE_LAST_SEEN) {
return -2;
} else {
return displayList.get(position).no;
return displayList.get(getPostPosition(position)).no;
}
}
public void setThread(ChanThread thread, PostsFilter filter) {
bound = true;
showError(null);
sourceList.clear();
sourceList.addAll(thread.posts);
@ -142,6 +176,18 @@ public class PostAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
displayList.clear();
displayList.addAll(filter.apply(sourceList));
lastSeenIndicatorPosition = -1;
if (thread.loadable.lastViewed >= 0) {
// Do not process the last post, the indicator does not have to appear at the bottom
for (int i = 0, displayListSize = displayList.size() - 1; i < displayListSize; i++) {
Post post = displayList.get(i);
if (post.no == thread.loadable.lastViewed) {
lastSeenIndicatorPosition = i + 1;
break;
}
}
}
// Update all, recyclerview will figure out all the animations
notifyDataSetChanged();
}
@ -160,6 +206,8 @@ public class PostAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
highlightedPostNo = -1;
highlightedPostTripcode = null;
lastPostCount = 0;
lastSeenIndicatorPosition = -1;
bound = false;
}
public void showError(String error) {
@ -211,8 +259,16 @@ public class PostAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
this.postViewMode = postViewMode;
}
private int getPostPosition(int position) {
int postPosition = position;
if (lastSeenIndicatorPosition >= 0 && position > lastSeenIndicatorPosition) {
postPosition--;
}
return postPosition;
}
private void onScrolledToBottom() {
if (lastPostCount < sourceList.size()) {
if (bound && lastPostCount < sourceList.size()) {
lastPostCount = sourceList.size();
postAdapterCallback.onListScrolledToBottom();
}
@ -240,6 +296,12 @@ public class PostAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
}
}
public static class LastSeenViewHolder extends RecyclerView.ViewHolder {
public LastSeenViewHolder(View itemView) {
super(itemView);
}
}
public interface PostAdapterCallback {
Loadable getLoadable();

@ -147,6 +147,7 @@ public class ViewThreadController extends ThreadController implements ThreadLayo
switch ((Integer) item.getId()) {
case PIN_ID:
setPinIconState(threadLayout.getPresenter().pin());
updateDrawerHighlighting(loadable);
break;
}
}

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?><!--
Clover - 4chan browser https://github.com/Floens/Clover/
Copyright (C) 2014 Floens
This program 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.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
-->
<View
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="4dp"
android:background="?attr/post_last_seen_color" />

@ -37,6 +37,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<attr name="post_id_background_dark" format="color" />
<attr name="post_inline_quote_color" format="color" />
<attr name="post_options_drawable" format="integer" />
<attr name="post_last_seen_color" format="color" />
<attr name="divider_color" format="color" />
<attr name="divider_split_color" format="color" />

@ -49,6 +49,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<item name="post_link_color">#ff0000B4</item>
<item name="post_spoiler_color">#ff000000</item>
<item name="post_capcode_color">#ffff0000</item>
<item name="post_last_seen_color">#ffff0000</item>
<item name="post_id_background_light">#ff636363</item>
<item name="post_id_background_dark">#00000000</item>

Loading…
Cancel
Save