Timeline footer improvements

This commit is contained in:
Raphael Michel 2017-04-15 19:25:39 +02:00
parent 69c1b88ff4
commit d732c5d0a7
8 changed files with 123 additions and 5 deletions

View File

@ -23,6 +23,8 @@ class FooterViewHolder extends RecyclerView.ViewHolder {
FooterViewHolder(View itemView) { FooterViewHolder(View itemView) {
super(itemView); super(itemView);
ProgressBar progressBar = (ProgressBar) itemView.findViewById(R.id.footer_progress_bar); ProgressBar progressBar = (ProgressBar) itemView.findViewById(R.id.footer_progress_bar);
if (progressBar != null) {
progressBar.setIndeterminate(true); progressBar.setIndeterminate(true);
} }
}
} }

View File

@ -42,9 +42,16 @@ class NotificationsAdapter extends RecyclerView.Adapter implements AdapterItemRe
private static final int VIEW_TYPE_STATUS_NOTIFICATION = 2; private static final int VIEW_TYPE_STATUS_NOTIFICATION = 2;
private static final int VIEW_TYPE_FOLLOW = 3; private static final int VIEW_TYPE_FOLLOW = 3;
enum FooterState {
EMPTY,
END,
LOADING
}
private List<Notification> notifications; private List<Notification> notifications;
private StatusActionListener statusListener; private StatusActionListener statusListener;
private NotificationActionListener notificationActionListener; private NotificationActionListener notificationActionListener;
private FooterState footerState = FooterState.END;
NotificationsAdapter(StatusActionListener statusListener, NotificationsAdapter(StatusActionListener statusListener,
NotificationActionListener notificationActionListener) { NotificationActionListener notificationActionListener) {
@ -54,6 +61,15 @@ class NotificationsAdapter extends RecyclerView.Adapter implements AdapterItemRe
this.notificationActionListener = notificationActionListener; this.notificationActionListener = notificationActionListener;
} }
public void setFooterState(FooterState newFooterState) {
FooterState oldValue = footerState;
footerState = newFooterState;
if (footerState != oldValue) {
notifyItemChanged(notifications.size());
}
}
@Override @Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) { switch (viewType) {
@ -64,8 +80,24 @@ class NotificationsAdapter extends RecyclerView.Adapter implements AdapterItemRe
return new StatusViewHolder(view); return new StatusViewHolder(view);
} }
case VIEW_TYPE_FOOTER: { case VIEW_TYPE_FOOTER: {
View view = LayoutInflater.from(parent.getContext()) View view;
switch (footerState) {
default:
case LOADING:
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_footer, parent, false); .inflate(R.layout.item_footer, parent, false);
break;
case END: {
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_footer_end, parent, false);
break;
}
case EMPTY: {
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_footer_empty, parent, false);
break;
}
}
return new FooterViewHolder(view); return new FooterViewHolder(view);
} }
case VIEW_TYPE_STATUS_NOTIFICATION: { case VIEW_TYPE_STATUS_NOTIFICATION: {

View File

@ -144,6 +144,10 @@ public class NotificationsFragment extends SFragment implements
private void sendFetchNotificationsRequest(final String fromId, String uptoId) { private void sendFetchNotificationsRequest(final String fromId, String uptoId) {
MastodonAPI api = ((BaseActivity) getActivity()).mastodonAPI; MastodonAPI api = ((BaseActivity) getActivity()).mastodonAPI;
if (fromId != null || adapter.getItemCount() <= 1) {
adapter.setFooterState(NotificationsAdapter.FooterState.LOADING);
}
listCall = api.notifications(fromId, uptoId, null); listCall = api.notifications(fromId, uptoId, null);
listCall.enqueue(new Callback<List<Notification>>() { listCall.enqueue(new Callback<List<Notification>>() {
@ -192,6 +196,11 @@ public class NotificationsFragment extends SFragment implements
} else { } else {
adapter.update(notifications); adapter.update(notifications);
} }
if (notifications.size() == 0 && adapter.getItemCount() == 1) {
adapter.setFooterState(NotificationsAdapter.FooterState.EMPTY);
} else if (fromId != null) {
adapter.setFooterState(NotificationsAdapter.FooterState.END);
}
swipeRefreshLayout.setRefreshing(false); swipeRefreshLayout.setRefreshing(false);
} }

View File

@ -30,8 +30,15 @@ class TimelineAdapter extends RecyclerView.Adapter implements AdapterItemRemover
private static final int VIEW_TYPE_STATUS = 0; private static final int VIEW_TYPE_STATUS = 0;
private static final int VIEW_TYPE_FOOTER = 1; private static final int VIEW_TYPE_FOOTER = 1;
enum FooterState {
EMPTY,
END,
LOADING
}
private List<Status> statuses; private List<Status> statuses;
private StatusActionListener statusListener; private StatusActionListener statusListener;
private FooterState footerState = FooterState.END;
TimelineAdapter(StatusActionListener statusListener) { TimelineAdapter(StatusActionListener statusListener) {
super(); super();
@ -49,13 +56,37 @@ class TimelineAdapter extends RecyclerView.Adapter implements AdapterItemRemover
return new StatusViewHolder(view); return new StatusViewHolder(view);
} }
case VIEW_TYPE_FOOTER: { case VIEW_TYPE_FOOTER: {
View view = LayoutInflater.from(viewGroup.getContext()) View view;
switch (footerState) {
default:
case LOADING:
view = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.item_footer, viewGroup, false); .inflate(R.layout.item_footer, viewGroup, false);
break;
case END: {
view = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.item_footer_end, viewGroup, false);
break;
}
case EMPTY: {
view = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.item_footer_empty, viewGroup, false);
break;
}
}
return new FooterViewHolder(view); return new FooterViewHolder(view);
} }
} }
} }
public void setFooterState(FooterState newFooterState) {
FooterState oldValue = footerState;
footerState = newFooterState;
if (footerState != oldValue) {
notifyItemChanged(statuses.size());
}
}
@Override @Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) { public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
if (position < statuses.size()) { if (position < statuses.size()) {

View File

@ -214,6 +214,10 @@ public class TimelineFragment extends SFragment implements
private void sendFetchTimelineRequest(@Nullable final String fromId, @Nullable String uptoId) { private void sendFetchTimelineRequest(@Nullable final String fromId, @Nullable String uptoId) {
MastodonAPI api = ((BaseActivity) getActivity()).mastodonAPI; MastodonAPI api = ((BaseActivity) getActivity()).mastodonAPI;
if (fromId != null || adapter.getItemCount() <= 1) {
adapter.setFooterState(TimelineAdapter.FooterState.LOADING);
}
Callback<List<Status>> cb = new Callback<List<Status>>() { Callback<List<Status>> cb = new Callback<List<Status>>() {
@Override @Override
public void onResponse(Call<List<Status>> call, retrofit2.Response<List<Status>> response) { public void onResponse(Call<List<Status>> call, retrofit2.Response<List<Status>> response) {
@ -282,6 +286,11 @@ public class TimelineFragment extends SFragment implements
} else { } else {
adapter.update(statuses); adapter.update(statuses);
} }
if (statuses.size() == 0 && adapter.getItemCount() == 1) {
adapter.setFooterState(TimelineAdapter.FooterState.EMPTY);
} else if(fromId != null) {
adapter.setFooterState(TimelineAdapter.FooterState.END);
}
swipeRefreshLayout.setRefreshing(false); swipeRefreshLayout.setRefreshing(false);
} }

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:srcCompat="@drawable/elephant_friend" />
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/footer_empty"
android:textAlignment="center" />
</LinearLayout>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
</LinearLayout>

View File

@ -38,6 +38,7 @@
<string name="footer_end_of_statuses">end of the statuses</string> <string name="footer_end_of_statuses">end of the statuses</string>
<string name="footer_end_of_notifications">end of the notifications</string> <string name="footer_end_of_notifications">end of the notifications</string>
<string name="footer_end_of_accounts">end of the accounts</string> <string name="footer_end_of_accounts">end of the accounts</string>
<string name="footer_empty">There are no toots here so far. Pull down to refresh!</string>
<string name="notification_reblog_format">%s boosted your toot</string> <string name="notification_reblog_format">%s boosted your toot</string>
<string name="notification_favourite_format">%s favourited your toot</string> <string name="notification_favourite_format">%s favourited your toot</string>