some fixes with fetch more

This commit is contained in:
Thomas 2022-09-27 11:19:49 +02:00
parent d349062fea
commit a8f0125a8f
3 changed files with 88 additions and 189 deletions

View file

@ -298,57 +298,7 @@ public class StatusCache {
} }
/**
* Get newest status for a timeline
*
* @param slug String - slug for the timeline (it's a unique string value for a timeline)
* @param instance String - instance
* @param user_id String - us
* @return Statuses
* @throws DBException - throws a db exception
*/
public Status getNewestStatus(String slug, String instance, String user_id) throws DBException {
if (db == null) {
throw new DBException("db is null. Wrong initialization.");
}
String selection = Sqlite.COL_INSTANCE + "='" + instance + "' AND " + Sqlite.COL_USER_ID + "= '" + user_id + "' AND " + Sqlite.COL_SLUG + "= '" + slug + "'";
try {
Cursor c = db.query(Sqlite.TABLE_STATUS_CACHE, null, selection, null, null, null, Sqlite.COL_STATUS_ID + " DESC", "1");
Statuses statuses = createStatusReply(cursorToListOfStatuses(c));
if (statuses.statuses != null && statuses.statuses.size() > 0) {
return statuses.statuses.get(0);
} else return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* Get oldest status for a timeline
*
* @param slug String - slug for the timeline (it's a unique string value for a timeline)
* @param instance String - instance
* @param user_id String - us
* @return Statuses
* @throws DBException - throws a db exception
*/
public Status getOldestStatus(String slug, String instance, String user_id) throws DBException {
if (db == null) {
throw new DBException("db is null. Wrong initialization.");
}
String selection = Sqlite.COL_INSTANCE + "='" + instance + "' AND " + Sqlite.COL_USER_ID + "= '" + user_id + "' AND " + Sqlite.COL_SLUG + "= '" + slug + "'";
try {
Cursor c = db.query(Sqlite.TABLE_STATUS_CACHE, null, selection, null, null, null, Sqlite.COL_STATUS_ID + " ASC", "1");
Statuses statuses = createStatusReply(cursorToListOfStatuses(c));
if (statuses.statuses != null && statuses.statuses.size() > 0) {
return statuses.statuses.get(0);
} else return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/** /**
* Get paginated statuses from db * Get paginated statuses from db
@ -366,15 +316,15 @@ public class StatusCache {
throw new DBException("db is null. Wrong initialization."); throw new DBException("db is null. Wrong initialization.");
} }
String order = " DESC"; String order = " DESC";
String selection = Sqlite.COL_INSTANCE + "='" + instance + "' AND " + Sqlite.COL_USER_ID + "= '" + user_id + "' AND " + Sqlite.COL_SLUG + "= '" + slug + "'"; String selection = Sqlite.COL_INSTANCE + "='" + instance + "' AND " + Sqlite.COL_USER_ID + "= '" + user_id + "' AND " + Sqlite.COL_SLUG + "= '" + slug + "' ";
String limit = String.valueOf(MastodonHelper.statusesPerCall(context)); String limit = String.valueOf(MastodonHelper.statusesPerCall(context));
if (min_id != null) { if (min_id != null) {
selection += "AND " + Sqlite.COL_STATUS_ID + " > '" + min_id + "'"; selection += "AND " + Sqlite.COL_STATUS_ID + " > '" + min_id + "' ";
order = " ASC"; order = " ASC";
} else if (max_id != null) { } else if (max_id != null) {
selection += "AND " + Sqlite.COL_STATUS_ID + " < '" + max_id + "'"; selection += "AND " + Sqlite.COL_STATUS_ID + " < '" + max_id + "' ";
} else if (since_id != null) { } else if (since_id != null) {
selection += "AND " + Sqlite.COL_STATUS_ID + " > '" + since_id + "'"; selection += "AND " + Sqlite.COL_STATUS_ID + " > '" + since_id + "' ";
limit = null; limit = null;
} }
try { try {
@ -386,59 +336,6 @@ public class StatusCache {
} }
} }
/**
* Get statuses from db
*
* @return Statuses
* @throws DBException - throws a db exception
*/
public Status getTopFetchMore(String slug, String instance, String user_id, String status_id) throws DBException {
if (db == null) {
throw new DBException("db is null. Wrong initialization.");
}
String selection = Sqlite.COL_INSTANCE + "='" + instance + "' AND "
+ Sqlite.COL_USER_ID + "= '" + user_id + "' AND "
+ Sqlite.COL_SLUG + "= '" + slug + "' AND "
+ Sqlite.COL_STATUS_ID + " > '" + status_id + "'";
try {
Cursor c = db.query(Sqlite.TABLE_STATUS_CACHE, null, selection, null, null, null, Sqlite.COL_STATUS_ID + " ASC", "1");
if (c != null && c.getCount() > 0) {
return convertCursorToStatus(c);
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* Get statuses from db
*
* @return Statuses
* @throws DBException - throws a db exception
*/
public Status getBottomFetchMore(String slug, String instance, String user_id, String status_id) throws DBException {
if (db == null) {
throw new DBException("db is null. Wrong initialization.");
}
String selection = Sqlite.COL_INSTANCE + "='" + instance + "' AND "
+ Sqlite.COL_USER_ID + "= '" + user_id + "' AND "
+ Sqlite.COL_SLUG + "= '" + slug + "' AND "
+ Sqlite.COL_STATUS_ID + " < '" + status_id + "'";
try {
Cursor c = db.query(Sqlite.TABLE_STATUS_CACHE, null, selection, null, null, null, Sqlite.COL_STATUS_ID + " DESC", "1");
if (c != null && c.getCount() > 0) {
return convertCursorToStatus(c);
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/** /**
* @param instance String - instance * @param instance String - instance

View file

@ -69,12 +69,12 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
private TimelinesVM timelinesVM; private TimelinesVM timelinesVM;
private AccountsVM accountsVM; private AccountsVM accountsVM;
private boolean flagLoading; private boolean flagLoading;
private List<Status> statuses;
private String search, searchCache; private String search, searchCache;
private Status statusReport; private Status statusReport;
private String max_id, min_id, min_id_fetch_more, max_id_fetch_more; private String max_id, min_id, min_id_fetch_more, max_id_fetch_more;
private StatusAdapter statusAdapter; private StatusAdapter statusAdapter;
private Timeline.TimeLineEnum timelineType; private Timeline.TimeLineEnum timelineType;
private List<Status> timelineStatuses;
//Handle actions that can be done in other fragments //Handle actions that can be done in other fragments
private final BroadcastReceiver receive_action = new BroadcastReceiver() { private final BroadcastReceiver receive_action = new BroadcastReceiver() {
@Override @Override
@ -88,17 +88,17 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
if (receivedStatus != null && statusAdapter != null) { if (receivedStatus != null && statusAdapter != null) {
int position = getPosition(receivedStatus); int position = getPosition(receivedStatus);
if (position >= 0) { if (position >= 0) {
statuses.get(position).reblog = receivedStatus.reblog; timelineStatuses.get(position).reblog = receivedStatus.reblog;
statuses.get(position).reblogged = receivedStatus.reblogged; timelineStatuses.get(position).reblogged = receivedStatus.reblogged;
statuses.get(position).favourited = receivedStatus.favourited; timelineStatuses.get(position).favourited = receivedStatus.favourited;
statuses.get(position).bookmarked = receivedStatus.bookmarked; timelineStatuses.get(position).bookmarked = receivedStatus.bookmarked;
statuses.get(position).reblogs_count = receivedStatus.reblogs_count; timelineStatuses.get(position).reblogs_count = receivedStatus.reblogs_count;
statuses.get(position).favourites_count = receivedStatus.favourites_count; timelineStatuses.get(position).favourites_count = receivedStatus.favourites_count;
statusAdapter.notifyItemChanged(position); statusAdapter.notifyItemChanged(position);
} }
} else if (delete_statuses_for_user != null && statusAdapter != null) { } else if (delete_statuses_for_user != null && statusAdapter != null) {
List<Status> statusesToRemove = new ArrayList<>(); List<Status> statusesToRemove = new ArrayList<>();
for (Status status : statuses) { for (Status status : timelineStatuses) {
if (status != null && status.account != null && status.account.id != null && status.account.id.equals(delete_statuses_for_user)) { if (status != null && status.account != null && status.account.id != null && status.account.id.equals(delete_statuses_for_user)) {
statusesToRemove.add(status); statusesToRemove.add(status);
} }
@ -106,18 +106,18 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
for (Status statusToRemove : statusesToRemove) { for (Status statusToRemove : statusesToRemove) {
int position = getPosition(statusToRemove); int position = getPosition(statusToRemove);
if (position >= 0) { if (position >= 0) {
statuses.remove(position); timelineStatuses.remove(position);
statusAdapter.notifyItemRemoved(position); statusAdapter.notifyItemRemoved(position);
} }
} }
} else if (status_to_delete != null && statusAdapter != null) { } else if (status_to_delete != null && statusAdapter != null) {
int position = getPosition(status_to_delete); int position = getPosition(status_to_delete);
if (position >= 0) { if (position >= 0) {
statuses.remove(position); timelineStatuses.remove(position);
statusAdapter.notifyItemRemoved(position); statusAdapter.notifyItemRemoved(position);
} }
} else if (statusPosted != null && statusAdapter != null && timelineType == Timeline.TimeLineEnum.HOME) { } else if (statusPosted != null && statusAdapter != null && timelineType == Timeline.TimeLineEnum.HOME) {
statuses.add(0, statusPosted); timelineStatuses.add(0, statusPosted);
statusAdapter.notifyItemInserted(0); statusAdapter.notifyItemInserted(0);
} }
} }
@ -132,7 +132,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
private PinnedTimeline pinnedTimeline; private PinnedTimeline pinnedTimeline;
private String ident; private String ident;
private String slug; private String slug;
private TimelinesVM.TimelineParams timelineParams;
private boolean canBeFederated; private boolean canBeFederated;
/** /**
@ -147,7 +147,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
if (status.id == null) { if (status.id == null) {
return -1; return -1;
} }
for (Status _status : statuses) { for (Status _status : timelineStatuses) {
if (_status.id != null && _status.id.compareTo(status.id) == 0) { if (_status.id != null && _status.id.compareTo(status.id) == 0) {
found = true; found = true;
break; break;
@ -164,7 +164,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
*/ */
public List<String> getCheckedStatusesId() { public List<String> getCheckedStatusesId() {
List<String> stringList = new ArrayList<>(); List<String> stringList = new ArrayList<>();
for (Status status : statuses) { for (Status status : timelineStatuses) {
if (status.isChecked) { if (status.isChecked) {
stringList.add(status.id); stringList.add(status.id);
} }
@ -280,7 +280,6 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
route(DIRECTION.REFRESH, true); route(DIRECTION.REFRESH, true);
}); });
} }
if (statuses == null || statuses.statuses == null || statuses.statuses.size() == 0) { if (statuses == null || statuses.statuses == null || statuses.statuses.size() == 0) {
binding.noAction.setVisibility(View.VISIBLE); binding.noAction.setVisibility(View.VISIBLE);
return; return;
@ -308,27 +307,26 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
} }
flagLoading = statuses.pagination.max_id == null; flagLoading = statuses.pagination.max_id == null;
binding.recyclerView.setVisibility(View.VISIBLE); binding.recyclerView.setVisibility(View.VISIBLE);
if (statusAdapter != null && this.statuses != null) { if (statusAdapter != null && timelineStatuses != null) {
int size = this.statuses.size(); int size = timelineStatuses.size();
this.statuses.clear(); timelineStatuses.clear();
this.statuses = new ArrayList<>(); timelineStatuses = new ArrayList<>();
statusAdapter.notifyItemRangeRemoved(0, size); statusAdapter.notifyItemRangeRemoved(0, size);
} }
if (this.statuses == null) { if (timelineStatuses == null) {
this.statuses = new ArrayList<>(); timelineStatuses = new ArrayList<>();
} }
if (statusReport != null) { if (statusReport != null) {
this.statuses.add(statusReport); timelineStatuses.add(statusReport);
} }
this.statuses.addAll(statuses.statuses); timelineStatuses.addAll(statuses.statuses);
if (max_id == null || (statuses.pagination.max_id != null && statuses.pagination.max_id.compareTo(max_id) < 0)) { if (max_id == null || (statuses.pagination.max_id != null && statuses.pagination.max_id.compareTo(max_id) < 0)) {
max_id = statuses.pagination.max_id; max_id = statuses.pagination.max_id;
} }
if (min_id == null || (statuses.pagination.min_id != null && statuses.pagination.min_id.compareTo(min_id) > 0)) { if (min_id == null || (statuses.pagination.min_id != null && statuses.pagination.min_id.compareTo(min_id) > 0)) {
min_id = statuses.pagination.min_id; min_id = statuses.pagination.min_id;
} }
statusAdapter = new StatusAdapter(this.statuses, timelineType, minified, canBeFederated); statusAdapter = new StatusAdapter(timelineStatuses, timelineType, minified, canBeFederated);
statusAdapter.fetchMoreCallBack = this; statusAdapter.fetchMoreCallBack = this;
if (statusReport != null) { if (statusReport != null) {
scrollToTop(); scrollToTop();
@ -343,6 +341,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
binding.recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { binding.recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override @Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
if (requireActivity() instanceof BaseMainActivity) { if (requireActivity() instanceof BaseMainActivity) {
if (dy < 0 && !((BaseMainActivity) requireActivity()).getFloatingVisibility()) if (dy < 0 && !((BaseMainActivity) requireActivity()).getFloatingVisibility())
((BaseMainActivity) requireActivity()).manageFloatingButton(true); ((BaseMainActivity) requireActivity()).manageFloatingButton(true);
@ -386,7 +385,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
binding.swipeContainer.setRefreshing(false); binding.swipeContainer.setRefreshing(false);
binding.loadingNextElements.setVisibility(View.GONE); binding.loadingNextElements.setVisibility(View.GONE);
flagLoading = false; flagLoading = false;
if (statuses != null && fetched_statuses != null && fetched_statuses.statuses != null && fetched_statuses.statuses.size() > 0) { if (timelineStatuses != null && fetched_statuses != null && fetched_statuses.statuses != null && fetched_statuses.statuses.size() > 0) {
flagLoading = fetched_statuses.pagination.max_id == null; flagLoading = fetched_statuses.pagination.max_id == null;
binding.noAction.setVisibility(View.GONE); binding.noAction.setVisibility(View.GONE);
if (timelineType == Timeline.TimeLineEnum.ART) { if (timelineType == Timeline.TimeLineEnum.ART) {
@ -432,17 +431,17 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
if (statusListReceived != null && statusListReceived.size() > 0) { if (statusListReceived != null && statusListReceived.size() > 0) {
for (Status statusReceived : statusListReceived) { for (Status statusReceived : statusListReceived) {
int position = 0; int position = 0;
if (this.statuses != null) { if (timelineStatuses != null) {
//First we refresh statuses //First we refresh statuses
statusAdapter.notifyItemRangeChanged(0, this.statuses.size()); statusAdapter.notifyItemRangeChanged(0, timelineStatuses.size());
//We loop through messages already in the timeline //We loop through messages already in the timeline
for (Status statusAlreadyPresent : this.statuses) { for (Status statusAlreadyPresent : timelineStatuses) {
//We compare the id of each status and we only add status having an id greater than the another, it is inserted at this position //We compare the id of each status and we only add status having an id greater than the another, it is inserted at this position
//Pinned messages are ignored because their date can be older //Pinned messages are ignored because their date can be older
if (statusReceived.id.compareTo(statusAlreadyPresent.id) > 0) { if (statusReceived.id.compareTo(statusAlreadyPresent.id) > 0) {
//We add the status to a list of id - thus we know it is already in the timeline //We add the status to a list of id - thus we know it is already in the timeline
if (!this.statuses.contains(statusReceived) && !statusReceived.pinned && timelineType != Timeline.TimeLineEnum.ACCOUNT_TIMELINE) { if (!timelineStatuses.contains(statusReceived) && !statusReceived.pinned && timelineType != Timeline.TimeLineEnum.ACCOUNT_TIMELINE) {
this.statuses.add(position, statusReceived); timelineStatuses.add(position, statusReceived);
statusAdapter.notifyItemInserted(position); statusAdapter.notifyItemInserted(position);
} }
break; break;
@ -450,9 +449,9 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
position++; position++;
} }
//Statuses added at the bottom //Statuses added at the bottom
if (position == this.statuses.size() && !this.statuses.contains(statusReceived)) { if (position == timelineStatuses.size() && !timelineStatuses.contains(statusReceived)) {
//We add the status to a list of id - thus we know it is already in the timeline //We add the status to a list of id - thus we know it is already in the timeline
this.statuses.add(position, statusReceived); timelineStatuses.add(position, statusReceived);
statusAdapter.notifyItemInserted(position); statusAdapter.notifyItemInserted(position);
} }
} }
@ -480,9 +479,9 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
private void storeMarker() { private void storeMarker() {
if (timelineType == Timeline.TimeLineEnum.HOME && mLayoutManager != null) { if (timelineType == Timeline.TimeLineEnum.HOME && mLayoutManager != null) {
int position = mLayoutManager.findFirstVisibleItemPosition(); int position = mLayoutManager.findFirstVisibleItemPosition();
if (statuses != null && statuses.size() > position) { if (timelineStatuses != null && timelineStatuses.size() > position) {
try { try {
Status status = statuses.get(position); Status status = timelineStatuses.get(position);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity()); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
SharedPreferences.Editor editor = sharedpreferences.edit(); SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putString(getString(R.string.SET_INNER_MARKER) + BaseMainActivity.currentUserID + BaseMainActivity.currentInstance + slug, status.id); editor.putString(getString(R.string.SET_INNER_MARKER) + BaseMainActivity.currentUserID + BaseMainActivity.currentInstance + slug, status.id);
@ -521,9 +520,8 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
return; return;
} }
//Initialize with default params //Initialize with default params
TimelinesVM.TimelineParams timelineParams = new TimelinesVM.TimelineParams(timelineType, direction, ident); timelineParams = new TimelinesVM.TimelineParams(timelineType, direction, ident);
timelineParams.limit = MastodonHelper.statusesPerCall(requireActivity()); timelineParams.limit = MastodonHelper.statusesPerCall(requireActivity());
if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP) { if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP) {
timelineParams.maxId = null; timelineParams.maxId = null;
timelineParams.minId = null; timelineParams.minId = null;
@ -578,7 +576,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
private void getCachedStatus(DIRECTION direction, boolean fetchingMissing, TimelinesVM.TimelineParams timelineParams) { private void getCachedStatus(DIRECTION direction, boolean fetchingMissing, TimelinesVM.TimelineParams timelineParams) {
if (direction == null) { if (direction == null) {
timelinesVM.getTimelineCache(timelineParams) timelinesVM.getTimelineCache(timelineStatuses, timelineParams)
.observe(getViewLifecycleOwner(), statusesCached -> { .observe(getViewLifecycleOwner(), statusesCached -> {
if (statusesCached == null || statusesCached.statuses == null || statusesCached.statuses.size() == 0) { if (statusesCached == null || statusesCached.statuses == null || statusesCached.statuses.size() == 0) {
getLiveStatus(null, fetchingMissing, timelineParams); getLiveStatus(null, fetchingMissing, timelineParams);
@ -587,7 +585,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
} }
}); });
} else if (direction == DIRECTION.BOTTOM) { } else if (direction == DIRECTION.BOTTOM) {
timelinesVM.getTimelineCache(timelineParams) timelinesVM.getTimelineCache(timelineStatuses, timelineParams)
.observe(getViewLifecycleOwner(), statusesCachedBottom -> { .observe(getViewLifecycleOwner(), statusesCachedBottom -> {
if (statusesCachedBottom == null || statusesCachedBottom.statuses == null || statusesCachedBottom.statuses.size() == 0) { if (statusesCachedBottom == null || statusesCachedBottom.statuses == null || statusesCachedBottom.statuses.size() == 0) {
getLiveStatus(DIRECTION.BOTTOM, fetchingMissing, timelineParams); getLiveStatus(DIRECTION.BOTTOM, fetchingMissing, timelineParams);
@ -596,9 +594,8 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
} }
}); });
} else if (direction == DIRECTION.TOP) { } else if (direction == DIRECTION.TOP) {
timelinesVM.getTimelineCache(timelineParams) timelinesVM.getTimelineCache(timelineStatuses, timelineParams)
.observe(getViewLifecycleOwner(), statusesCachedTop -> { .observe(getViewLifecycleOwner(), statusesCachedTop -> {
if (statusesCachedTop == null || statusesCachedTop.statuses == null || statusesCachedTop.statuses.size() == 0) { if (statusesCachedTop == null || statusesCachedTop.statuses == null || statusesCachedTop.statuses.size() == 0) {
getLiveStatus(DIRECTION.TOP, fetchingMissing, timelineParams); getLiveStatus(DIRECTION.TOP, fetchingMissing, timelineParams);
} else { } else {
@ -607,7 +604,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
}); });
} else if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP) { } else if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP) {
timelinesVM.getTimelineCache(timelineParams) timelinesVM.getTimelineCache(timelineStatuses, timelineParams)
.observe(getViewLifecycleOwner(), statusesRefresh -> { .observe(getViewLifecycleOwner(), statusesRefresh -> {
if (statusesRefresh == null || statusesRefresh.statuses == null || statusesRefresh.statuses.size() == 0) { if (statusesRefresh == null || statusesRefresh.statuses == null || statusesRefresh.statuses.size() == 0) {
getLiveStatus(direction, fetchingMissing, timelineParams); getLiveStatus(direction, fetchingMissing, timelineParams);
@ -624,16 +621,16 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
private void getLiveStatus(DIRECTION direction, boolean fetchingMissing, TimelinesVM.TimelineParams timelineParams) { private void getLiveStatus(DIRECTION direction, boolean fetchingMissing, TimelinesVM.TimelineParams timelineParams) {
if (direction == null) { if (direction == null) {
timelinesVM.getTimeline(timelineParams) timelinesVM.getTimeline(timelineStatuses, timelineParams)
.observe(getViewLifecycleOwner(), this::initializeStatusesCommonView); .observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
} else if (direction == DIRECTION.BOTTOM) { } else if (direction == DIRECTION.BOTTOM) {
timelinesVM.getTimeline(timelineParams) timelinesVM.getTimeline(timelineStatuses, timelineParams)
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, fetchingMissing)); .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, fetchingMissing));
} else if (direction == DIRECTION.TOP) { } else if (direction == DIRECTION.TOP) {
timelinesVM.getTimeline(timelineParams) timelinesVM.getTimeline(timelineStatuses, timelineParams)
.observe(getViewLifecycleOwner(), statusesTop -> dealWithPagination(statusesTop, DIRECTION.TOP, fetchingMissing)); .observe(getViewLifecycleOwner(), statusesTop -> dealWithPagination(statusesTop, DIRECTION.TOP, fetchingMissing));
} else if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP) { } else if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP) {
timelinesVM.getTimeline(timelineParams) timelinesVM.getTimeline(timelineStatuses, timelineParams)
.observe(getViewLifecycleOwner(), statusesRefresh -> { .observe(getViewLifecycleOwner(), statusesRefresh -> {
if (statusAdapter != null) { if (statusAdapter != null) {
dealWithPagination(statusesRefresh, direction, true); dealWithPagination(statusesRefresh, direction, true);
@ -711,7 +708,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
timelinesVM.getPeertube(remoteInstance, null, MastodonHelper.statusesPerCall(requireActivity())) timelinesVM.getPeertube(remoteInstance, null, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), this::initializeStatusesCommonView); .observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
} else if (direction == DIRECTION.BOTTOM) { } else if (direction == DIRECTION.BOTTOM) {
timelinesVM.getPeertube(remoteInstance, String.valueOf(statuses.size()), MastodonHelper.statusesPerCall(requireActivity())) timelinesVM.getPeertube(remoteInstance, String.valueOf(timelineStatuses.size()), MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false)); .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false));
} else if (direction == DIRECTION.TOP) { } else if (direction == DIRECTION.TOP) {
flagLoading = false; flagLoading = false;
@ -826,8 +823,8 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
* Refresh status in list * Refresh status in list
*/ */
public void refreshAllAdapters() { public void refreshAllAdapters() {
if (statusAdapter != null && statuses != null) { if (statusAdapter != null && timelineStatuses != null) {
statusAdapter.notifyItemRangeChanged(0, statuses.size()); statusAdapter.notifyItemRangeChanged(0, timelineStatuses.size());
} }
} }
@ -837,7 +834,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
min_id_fetch_more = min_id; min_id_fetch_more = min_id;
Status status = null; Status status = null;
int position = 0; int position = 0;
for (Status currentStatus : this.statuses) { for (Status currentStatus : timelineStatuses) {
if (currentStatus.id.compareTo(id) == 0) { if (currentStatus.id.compareTo(id) == 0) {
status = currentStatus; status = currentStatus;
break; break;
@ -845,7 +842,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
position++; position++;
} }
if (status != null) { if (status != null) {
this.statuses.remove(position); timelineStatuses.remove(position);
statusAdapter.notifyItemRemoved(position); statusAdapter.notifyItemRemoved(position);
} }
route(DIRECTION.TOP, true); route(DIRECTION.TOP, true);
@ -856,7 +853,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
max_id_fetch_more = max_id; max_id_fetch_more = max_id;
Status status = null; Status status = null;
int position = 0; int position = 0;
for (Status currentStatus : this.statuses) { for (Status currentStatus : timelineStatuses) {
if (currentStatus.id.compareTo(id) == 0) { if (currentStatus.id.compareTo(id) == 0) {
status = currentStatus; status = currentStatus;
break; break;
@ -864,7 +861,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
position++; position++;
} }
if (status != null) { if (status != null) {
this.statuses.remove(position); timelineStatuses.remove(position);
statusAdapter.notifyItemRemoved(position); statusAdapter.notifyItemRemoved(position);
} }
route(DIRECTION.BOTTOM, true); route(DIRECTION.BOTTOM, true);

View file

@ -341,7 +341,35 @@ public class TimelinesVM extends AndroidViewModel {
} }
public LiveData<Statuses> getTimeline(TimelineParams timelineParams) { private static void addFetchMore(List<Status> statusList, List<Status> timelineStatuses, TimelineParams timelineParams) throws DBException {
if (statusList != null && statusList.size() > 0 && timelineStatuses != null && timelineStatuses.size() > 0) {
if (timelineParams.direction == FragmentMastodonTimeline.DIRECTION.REFRESH || timelineParams.direction == FragmentMastodonTimeline.DIRECTION.SCROLL_TOP) {
//When refreshing/scrolling to TOP, if last statuses fetched has a greater id from newest in cache, there is potential hole
if (statusList.get(statusList.size() - 1).id.compareToIgnoreCase(timelineStatuses.get(0).id) > 0) {
Status statusFetchMore = new Status();
statusFetchMore.isFetchMore = true;
statusFetchMore.id = Helper.generateString();
statusList.add(statusFetchMore);
}
} else if (timelineParams.direction == FragmentMastodonTimeline.DIRECTION.TOP && timelineParams.fetchingMissing) {
if (!timelineStatuses.contains(statusList.get(0))) {
Status statusFetchMore = new Status();
statusFetchMore.isFetchMore = true;
statusFetchMore.id = Helper.generateString();
statusList.add(0, statusFetchMore);
}
} else if (timelineParams.direction == FragmentMastodonTimeline.DIRECTION.BOTTOM && timelineParams.fetchingMissing) {
if (!timelineStatuses.contains(statusList.get(statusList.size() - 1))) {
Status statusFetchMore = new Status();
statusFetchMore.isFetchMore = true;
statusFetchMore.id = Helper.generateString();
statusList.add(statusFetchMore);
}
}
}
}
public LiveData<Statuses> getTimeline(List<Status> timelineStatuses, TimelineParams timelineParams) {
statusesMutableLiveData = new MutableLiveData<>(); statusesMutableLiveData = new MutableLiveData<>();
MastodonTimelinesService mastodonTimelinesService = init(timelineParams.instance); MastodonTimelinesService mastodonTimelinesService = init(timelineParams.instance);
@ -375,32 +403,6 @@ public class TimelinesVM extends AndroidViewModel {
statuses.pagination = MastodonHelper.getPagination(timelineResponse.headers()); statuses.pagination = MastodonHelper.getPagination(timelineResponse.headers());
if (statusList != null && statusList.size() > 0) { if (statusList != null && statusList.size() > 0) {
if (timelineParams.direction == FragmentMastodonTimeline.DIRECTION.REFRESH || timelineParams.direction == FragmentMastodonTimeline.DIRECTION.SCROLL_TOP) {
Status newestStatus = new StatusCache(getApplication().getApplicationContext()).getNewestStatus(timelineParams.slug, timelineParams.instance, timelineParams.userId);
//When refreshing/scrolling to TOP, if last statuses fetched has a greater id from newest in cache, there is potential hole
if (newestStatus != null && statusList.get(statusList.size() - 1).id.compareToIgnoreCase(newestStatus.id) > 0) {
Status statusFetchMore = new Status();
statusFetchMore.isFetchMore = true;
statusFetchMore.id = Helper.generateString();
statusList.add(statusFetchMore);
}
} else if (timelineParams.direction == FragmentMastodonTimeline.DIRECTION.TOP && timelineParams.fetchingMissing) {
Status topStatus = new StatusCache(getApplication().getApplicationContext()).getTopFetchMore(timelineParams.slug, timelineParams.instance, timelineParams.slug, statusList.get(0).id);
if (topStatus != null && statusList.get(0).id.compareToIgnoreCase(topStatus.id) < 0) {
Status statusFetchMore = new Status();
statusFetchMore.isFetchMore = true;
statusFetchMore.id = Helper.generateString();
statusList.add(0, statusFetchMore);
}
} else if (timelineParams.direction == FragmentMastodonTimeline.DIRECTION.BOTTOM && timelineParams.fetchingMissing) {
Status bottomStatus = new StatusCache(getApplication().getApplicationContext()).getBottomFetchMore(timelineParams.slug, timelineParams.instance, timelineParams.slug, statusList.get(0).id);
if (bottomStatus != null && statusList.get(statusList.size() - 1).id.compareToIgnoreCase(bottomStatus.id) > 0) {
Status statusFetchMore = new Status();
statusFetchMore.isFetchMore = true;
statusFetchMore.id = Helper.generateString();
statusList.add(statusFetchMore);
}
}
for (Status status : statuses.statuses) { for (Status status : statuses.statuses) {
StatusCache statusCacheDAO = new StatusCache(getApplication().getApplicationContext()); StatusCache statusCacheDAO = new StatusCache(getApplication().getApplicationContext());
StatusCache statusCache = new StatusCache(); StatusCache statusCache = new StatusCache();
@ -415,6 +417,7 @@ public class TimelinesVM extends AndroidViewModel {
e.printStackTrace(); e.printStackTrace();
} }
} }
addFetchMore(statusList, timelineStatuses, timelineParams);
} }
} }
} catch (Exception e) { } catch (Exception e) {
@ -428,7 +431,7 @@ public class TimelinesVM extends AndroidViewModel {
return statusesMutableLiveData; return statusesMutableLiveData;
} }
public LiveData<Statuses> getTimelineCache(TimelineParams timelineParams) { public LiveData<Statuses> getTimelineCache(List<Status> timelineStatuses, TimelineParams timelineParams) {
statusesMutableLiveData = new MutableLiveData<>(); statusesMutableLiveData = new MutableLiveData<>();
new Thread(() -> { new Thread(() -> {
StatusCache statusCacheDAO = new StatusCache(getApplication().getApplicationContext()); StatusCache statusCacheDAO = new StatusCache(getApplication().getApplicationContext());
@ -438,6 +441,7 @@ public class TimelinesVM extends AndroidViewModel {
if (statuses != null) { if (statuses != null) {
TimelineHelper.filterStatus(getApplication().getApplicationContext(), statuses.statuses, timelineParams.type, true); TimelineHelper.filterStatus(getApplication().getApplicationContext(), statuses.statuses, timelineParams.type, true);
if (statuses.statuses != null && statuses.statuses.size() > 0) { if (statuses.statuses != null && statuses.statuses.size() > 0) {
addFetchMore(statuses.statuses, timelineStatuses, timelineParams);
statuses.pagination = new Pagination(); statuses.pagination = new Pagination();
statuses.pagination.min_id = statuses.statuses.get(0).id; statuses.pagination.min_id = statuses.statuses.get(0).id;
statuses.pagination.max_id = statuses.statuses.get(statuses.statuses.size() - 1).id; statuses.pagination.max_id = statuses.statuses.get(statuses.statuses.size() - 1).id;
@ -876,7 +880,8 @@ public class TimelinesVM extends AndroidViewModel {
"local: " + local + "\n" + "local: " + local + "\n" +
"maxId: " + maxId + "\n" + "maxId: " + maxId + "\n" +
"sinceId: " + sinceId + "\n" + "sinceId: " + sinceId + "\n" +
"minId: " + minId + "\n"; "minId: " + minId + "\n" +
"fetchingMissing: " + fetchingMissing;
} }
} }
} }