forked from mirrors/Fedilab
Timeline keep position
This commit is contained in:
parent
77e90ff41f
commit
6b043fc451
8 changed files with 190 additions and 79 deletions
|
@ -95,6 +95,7 @@ public class Status implements Serializable, Cloneable {
|
|||
public boolean isExpended = false;
|
||||
public boolean isTruncated = true;
|
||||
public boolean isFetchMore = false;
|
||||
public boolean isFetchMoreHidden = false;
|
||||
public boolean isMediaDisplayed = false;
|
||||
public boolean isMediaObfuscated = true;
|
||||
public boolean isChecked = false;
|
||||
|
|
|
@ -23,6 +23,7 @@ import com.google.gson.Gson;
|
|||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -284,22 +285,24 @@ public class StatusCache {
|
|||
* @return Statuses
|
||||
* @throws DBException - throws a db exception
|
||||
*/
|
||||
public Statuses geStatuses(CacheEnum type, String instance, String user_id, String max_id, String min_id) throws DBException {
|
||||
public Statuses geStatuses(CacheEnum type, String instance, String user_id, String max_id, String min_id, String since_id) throws DBException {
|
||||
if (db == null) {
|
||||
throw new DBException("db is null. Wrong initialization.");
|
||||
}
|
||||
String order = " DESC";
|
||||
String selection = Sqlite.COL_INSTANCE + "='" + instance + "' AND " + Sqlite.COL_USER_ID + "= '" + user_id + "'";
|
||||
String limit = String.valueOf(MastodonHelper.statusesPerCall(context));
|
||||
if (max_id == null && min_id != null) {
|
||||
if (min_id != null) {
|
||||
selection += "AND " + Sqlite.COL_STATUS_ID + " > '" + min_id + "'";
|
||||
} else if (max_id != null && min_id == null) {
|
||||
selection += "AND " + Sqlite.COL_STATUS_ID + " < '" + max_id + "'";
|
||||
order = " ASC";
|
||||
} else if (max_id != null) {
|
||||
selection += "AND " + Sqlite.COL_STATUS_ID + " > '" + min_id + "' AND " + Sqlite.COL_STATUS_ID + " < '" + max_id + "'";
|
||||
selection += "AND " + Sqlite.COL_STATUS_ID + " < '" + max_id + "'";
|
||||
} else if (since_id != null) {
|
||||
selection += "AND " + Sqlite.COL_STATUS_ID + " > '" + since_id + "'";
|
||||
limit = null;
|
||||
}
|
||||
try {
|
||||
Cursor c = db.query(Sqlite.TABLE_STATUS_CACHE, null, selection, null, null, null, Sqlite.COL_STATUS_ID + " DESC", limit);
|
||||
Cursor c = db.query(Sqlite.TABLE_STATUS_CACHE, null, selection, null, null, null, Sqlite.COL_STATUS_ID + order, limit);
|
||||
return createStatusReply(cursorToListOfStatuses(c));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
@ -373,6 +376,11 @@ public class StatusCache {
|
|||
statuses.statuses = statusList;
|
||||
Pagination pagination = new Pagination();
|
||||
if (statusList != null && statusList.size() > 0) {
|
||||
//Status list is inverted, it happens for min_id due to ASC ordering
|
||||
if (statusList.get(0).id.compareTo(statusList.get(statusList.size() - 1).id) < 0) {
|
||||
Collections.reverse(statusList);
|
||||
statuses.statuses = statusList;
|
||||
}
|
||||
pagination.max_id = statusList.get(0).id;
|
||||
pagination.min_id = statusList.get(statusList.size() - 1).id;
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ import java.util.List;
|
|||
|
||||
import app.fedilab.android.BaseMainActivity;
|
||||
import app.fedilab.android.R;
|
||||
import app.fedilab.android.activities.MainActivity;
|
||||
import app.fedilab.android.client.entities.api.MastodonList;
|
||||
import app.fedilab.android.client.entities.app.BottomMenu;
|
||||
import app.fedilab.android.client.entities.app.Pinned;
|
||||
|
@ -146,7 +147,8 @@ public class PinnedTimelineHelper {
|
|||
//Pinned tab position will start after BOTTOM_TIMELINE_COUNT (ie 5)
|
||||
activityMainBinding.tabLayout.removeAllTabs();
|
||||
//Small hack to hide first tabs (they represent the item of the bottom menu)
|
||||
for (int i = 0; i < BOTTOM_TIMELINE_COUNT; i++) {
|
||||
int toRemove = itemToRemoveInBottomMenu(activity);
|
||||
for (int i = 0; i < BOTTOM_TIMELINE_COUNT - toRemove; i++) {
|
||||
activityMainBinding.tabLayout.addTab(activityMainBinding.tabLayout.newTab());
|
||||
((ViewGroup) activityMainBinding.tabLayout.getChildAt(0)).getChildAt(i).setVisibility(View.GONE);
|
||||
}
|
||||
|
@ -203,7 +205,7 @@ public class PinnedTimelineHelper {
|
|||
activityMainBinding.viewPager.clearOnPageChangeListeners();
|
||||
activityMainBinding.tabLayout.clearOnTabSelectedListeners();
|
||||
|
||||
FedilabPageAdapter fedilabPageAdapter = new FedilabPageAdapter(activity.getSupportFragmentManager(), pinned, bottomMenu);
|
||||
FedilabPageAdapter fedilabPageAdapter = new FedilabPageAdapter(activity, activity.getSupportFragmentManager(), pinned, bottomMenu);
|
||||
activityMainBinding.viewPager.setAdapter(fedilabPageAdapter);
|
||||
activityMainBinding.viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(activityMainBinding.tabLayout));
|
||||
activityMainBinding.viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
||||
|
@ -254,6 +256,25 @@ public class PinnedTimelineHelper {
|
|||
|
||||
}
|
||||
|
||||
public static int itemToRemoveInBottomMenu(Context context) {
|
||||
//Small hack to hide first tabs (they represent the item of the bottom menu)
|
||||
BottomMenu bottomMenuDb;
|
||||
int toRemove = 0;
|
||||
try {
|
||||
//If some menu items have been hidden we should not create tab for them
|
||||
bottomMenuDb = new BottomMenu(context).getAllBottomMenu(MainActivity.accountWeakReference.get());
|
||||
if (bottomMenuDb != null && bottomMenuDb.bottom_menu != null) {
|
||||
for (BottomMenu.MenuItem menuItem : bottomMenuDb.bottom_menu) {
|
||||
if (!menuItem.visible) {
|
||||
toRemove++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (DBException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return toRemove;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage long clicks on Tag timelines
|
||||
|
|
|
@ -1675,6 +1675,9 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
} else if (viewType == STATUS_ART) { //Art statuses
|
||||
DrawerStatusArtBinding itemBinding = DrawerStatusArtBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new StatusViewHolder(itemBinding);
|
||||
} else if (viewType == STATUS_FETCH_MORE) { //Fetch more button
|
||||
DrawerFetchMoreBinding itemBinding = DrawerFetchMoreBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new StatusViewHolder(itemBinding);
|
||||
} else { //Classic statuses
|
||||
if (!minified) {
|
||||
DrawerStatusBinding itemBinding = DrawerStatusBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
|
@ -1764,7 +1767,19 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
});
|
||||
} else if (viewHolder.getItemViewType() == STATUS_FETCH_MORE) {
|
||||
StatusViewHolder holder = (StatusViewHolder) viewHolder;
|
||||
holder.bindingFetchMore.fetchMore.setOnClickListener(v -> fetchMoreCallBack.onClick(status.id));
|
||||
if (status.isFetchMoreHidden) {
|
||||
holder.bindingFetchMore.fetchMore.setVisibility(View.GONE);
|
||||
} else {
|
||||
holder.bindingFetchMore.fetchMore.setVisibility(View.VISIBLE);
|
||||
}
|
||||
holder.bindingFetchMore.fetchMore.setOnClickListener(v -> {
|
||||
//We hide the button
|
||||
status.isFetchMoreHidden = true;
|
||||
notifyItemChanged(position);
|
||||
if (position + 1 < statusList.size()) {
|
||||
fetchMoreCallBack.onClick(statusList.get(position + 1).id);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -75,6 +75,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
private String max_id, min_id, min_id_fetch_more;
|
||||
private StatusAdapter statusAdapter;
|
||||
private Timeline.TimeLineEnum timelineType;
|
||||
private static final int STATUS_PRESENT = -1;
|
||||
//Handle actions that can be done in other fragments
|
||||
private final BroadcastReceiver receive_action = new BroadcastReceiver() {
|
||||
@Override
|
||||
|
@ -179,6 +180,15 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
}
|
||||
}
|
||||
|
||||
private static final int STATUS_AT_THE_BOTTOM = -2;
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
}
|
||||
|
||||
private ArrayList<String> idOfAddedStatuses;
|
||||
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
ViewGroup container, Bundle savedInstanceState) {
|
||||
|
||||
|
@ -201,6 +211,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
minified = getArguments().getBoolean(Helper.ARG_MINIFIED, false);
|
||||
statusReport = (Status) getArguments().getSerializable(Helper.ARG_STATUS_REPORT);
|
||||
}
|
||||
idOfAddedStatuses = new ArrayList<>();
|
||||
if (tagTimeline != null) {
|
||||
ident = "@T@" + tagTimeline.name;
|
||||
if (tagTimeline.isART) {
|
||||
|
@ -234,16 +245,11 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
markers = new ArrayList<>();
|
||||
max_id = statusReport != null ? statusReport.id : null;
|
||||
flagLoading = false;
|
||||
router(null, false);
|
||||
router(null);
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Intialize the common view for statuses on different timelines
|
||||
*
|
||||
|
@ -259,9 +265,8 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
binding.swipeContainer.setRefreshing(false);
|
||||
binding.swipeContainer.setOnRefreshListener(() -> {
|
||||
binding.swipeContainer.setRefreshing(true);
|
||||
max_id = null;
|
||||
flagLoading = false;
|
||||
router(null, false);
|
||||
route(DIRECTION.REFRESH, true);
|
||||
});
|
||||
|
||||
if (statuses == null || statuses.statuses == null || statuses.statuses.size() == 0) {
|
||||
|
@ -289,6 +294,9 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
statuses.statuses = mediaStatuses;
|
||||
}
|
||||
}
|
||||
for (Status status : statuses.statuses) {
|
||||
idOfAddedStatuses.add(status.id);
|
||||
}
|
||||
flagLoading = statuses.pagination.max_id == null;
|
||||
binding.recyclerView.setVisibility(View.VISIBLE);
|
||||
if (statusAdapter != null && this.statuses != null) {
|
||||
|
@ -308,12 +316,11 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
if (max_id == null || (statuses.pagination.max_id != null && statuses.pagination.max_id.compareTo(max_id) < 0)) {
|
||||
max_id = statuses.pagination.max_id;
|
||||
}
|
||||
if (min_id == null || (statuses.pagination.max_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;
|
||||
}
|
||||
|
||||
statusAdapter = new StatusAdapter(this.statuses, timelineType, minified);
|
||||
|
||||
statusAdapter.fetchMoreCallBack = this;
|
||||
if (statusReport != null) {
|
||||
scrollToTop();
|
||||
}
|
||||
|
@ -340,7 +347,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
if (!flagLoading) {
|
||||
flagLoading = true;
|
||||
binding.loadingNextElements.setVisibility(View.VISIBLE);
|
||||
router(DIRECTION.BOTTOM, false);
|
||||
router(DIRECTION.BOTTOM);
|
||||
}
|
||||
} else {
|
||||
binding.loadingNextElements.setVisibility(View.GONE);
|
||||
|
@ -349,7 +356,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
if (!flagLoading) {
|
||||
flagLoading = true;
|
||||
binding.loadingNextElements.setVisibility(View.VISIBLE);
|
||||
router(DIRECTION.TOP, false);
|
||||
router(DIRECTION.TOP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -357,7 +364,6 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update view and pagination when scrolling down
|
||||
*
|
||||
|
@ -367,10 +373,10 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
if (binding == null) {
|
||||
return;
|
||||
}
|
||||
binding.swipeContainer.setRefreshing(false);
|
||||
binding.loadingNextElements.setVisibility(View.GONE);
|
||||
flagLoading = false;
|
||||
if (statuses != null && fetched_statuses != null && fetched_statuses.statuses != null && fetched_statuses.statuses.size() > 0) {
|
||||
|
||||
flagLoading = fetched_statuses.pagination.max_id == null;
|
||||
if (timelineType == Timeline.TimeLineEnum.ART) {
|
||||
//We have to split media in different statuses
|
||||
|
@ -386,20 +392,11 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
}
|
||||
fetched_statuses.statuses = mediaStatuses;
|
||||
}
|
||||
//There are some statuses present in the timeline
|
||||
int startId = statuses.size();
|
||||
if (direction == DIRECTION.TOP) {
|
||||
statuses.addAll(0, fetched_statuses.statuses);
|
||||
statusAdapter.notifyItemRangeInserted(0, fetched_statuses.statuses.size());
|
||||
//Maybe a better solution but max_id excludes fetched id, so when fetching with min_id we have to scroll top of one status to get it.
|
||||
if (fetched_statuses.statuses.size() > 0) {
|
||||
binding.recyclerView.scrollToPosition(fetched_statuses.statuses.size() - 1);
|
||||
}
|
||||
} else {
|
||||
statuses.addAll(fetched_statuses.statuses);
|
||||
statusAdapter.notifyItemRangeInserted(startId, fetched_statuses.statuses.size());
|
||||
}
|
||||
if (max_id == null || (fetched_statuses.pagination.max_id != null && fetched_statuses.pagination.max_id.compareTo(max_id) < 0)) {
|
||||
//Update the timeline with new statuses
|
||||
updateStatusListWith(fetched_statuses.statuses, fetchingMissing);
|
||||
if (fetched_statuses.pagination.max_id == null) {
|
||||
flagLoading = true;
|
||||
} else if (max_id == null || fetched_statuses.pagination.max_id.compareTo(max_id) < 0) {
|
||||
max_id = fetched_statuses.pagination.max_id;
|
||||
}
|
||||
if (min_id == null || (fetched_statuses.pagination.min_id != null && fetched_statuses.pagination.min_id.compareTo(min_id) > 0)) {
|
||||
|
@ -410,6 +407,47 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
}
|
||||
}
|
||||
|
||||
private void updateStatusListWith(List<Status> statusListReceived, boolean fetchingMissing) {
|
||||
if (statusListReceived != null && statusListReceived.size() > 0) {
|
||||
int insertedPosition = STATUS_PRESENT;
|
||||
for (Status statusReceived : statusListReceived) {
|
||||
insertedPosition = insertStatus(statusReceived);
|
||||
}
|
||||
//If there were no overlap for top status
|
||||
if (fetchingMissing && insertedPosition != STATUS_PRESENT && insertedPosition != STATUS_AT_THE_BOTTOM && this.statuses.size() > insertedPosition) {
|
||||
Status statusFetchMore = new Status();
|
||||
statusFetchMore.isFetchMore = true;
|
||||
int insertAt = insertedPosition + 1;
|
||||
this.statuses.add(insertAt, statusFetchMore);
|
||||
statusAdapter.notifyItemInserted(insertAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int insertStatus(Status statusReceived) {
|
||||
if (idOfAddedStatuses.contains(statusReceived.id)) {
|
||||
return STATUS_PRESENT;
|
||||
}
|
||||
int position = 0;
|
||||
for (Status statusAlreadyPresent : this.statuses) {
|
||||
if (statusAlreadyPresent.created_at != null && statusReceived.created_at != null && statusReceived.created_at.after(statusAlreadyPresent.created_at)) {
|
||||
idOfAddedStatuses.add(statusReceived.id);
|
||||
this.statuses.add(position, statusReceived);
|
||||
statusAdapter.notifyItemInserted(position);
|
||||
break;
|
||||
}
|
||||
position++;
|
||||
}
|
||||
//Statuses added at the bottom, we flag them by position = -2 for not dealing with them and fetch more
|
||||
if (position == this.statuses.size()) {
|
||||
idOfAddedStatuses.add(statusReceived.id);
|
||||
this.statuses.add(position, statusReceived);
|
||||
statusAdapter.notifyItemInserted(position);
|
||||
return STATUS_AT_THE_BOTTOM;
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
if (mLayoutManager != null) {
|
||||
|
@ -460,18 +498,18 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
}
|
||||
}
|
||||
|
||||
private void router(DIRECTION direction, boolean fetchingMissing) {
|
||||
private void router(DIRECTION direction) {
|
||||
if (networkAvailable == BaseMainActivity.status.UNKNOWN) {
|
||||
new Thread(() -> {
|
||||
if (networkAvailable == BaseMainActivity.status.UNKNOWN) {
|
||||
networkAvailable = Helper.isConnectedToInternet(requireActivity(), BaseMainActivity.currentInstance);
|
||||
}
|
||||
Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
Runnable myRunnable = () -> route(direction, fetchingMissing);
|
||||
Runnable myRunnable = () -> route(direction, false);
|
||||
mainHandler.post(myRunnable);
|
||||
}).start();
|
||||
} else {
|
||||
route(direction, fetchingMissing);
|
||||
route(direction, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -486,7 +524,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
if (binding == null) {
|
||||
return;
|
||||
}
|
||||
if (!fetchingMissing && !binding.swipeContainer.isRefreshing() && direction == null && quickLoad != null && quickLoad.statuses != null && quickLoad.statuses.size() > 0) {
|
||||
if (direction != DIRECTION.REFRESH && !fetchingMissing && !binding.swipeContainer.isRefreshing() && direction == null && quickLoad != null && quickLoad.statuses != null && quickLoad.statuses.size() > 0) {
|
||||
Statuses statuses = new Statuses();
|
||||
statuses.statuses = quickLoad.statuses;
|
||||
statuses.pagination = new Pagination();
|
||||
|
@ -515,6 +553,9 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
} else if (direction == DIRECTION.TOP) {
|
||||
timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, true, false, false, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing));
|
||||
} else if (direction == DIRECTION.REFRESH) {
|
||||
timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, true, false, false, null, null, null, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesTop -> dealWithPagination(statusesTop, DIRECTION.REFRESH, fetchingMissing));
|
||||
}
|
||||
} else if (timelineType == Timeline.TimeLineEnum.PUBLIC) { //PUBLIC TIMELINE
|
||||
if (direction == null) {
|
||||
|
@ -526,6 +567,9 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
} else if (direction == DIRECTION.TOP) {
|
||||
timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, false, true, false, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing));
|
||||
} else if (direction == DIRECTION.REFRESH) {
|
||||
timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, false, true, false, null, null, null, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesTop -> dealWithPagination(statusesTop, DIRECTION.REFRESH, fetchingMissing));
|
||||
}
|
||||
} else if (timelineType == Timeline.TimeLineEnum.REMOTE) { //REMOTE TIMELINE
|
||||
if (direction == null) {
|
||||
|
@ -537,6 +581,9 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
} else if (direction == DIRECTION.TOP) {
|
||||
timelinesVM.getPublic(null, remoteInstance, true, false, false, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing));
|
||||
} else if (direction == DIRECTION.REFRESH) {
|
||||
timelinesVM.getPublic(null, remoteInstance, true, false, false, null, null, null, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesTop -> dealWithPagination(statusesTop, DIRECTION.REFRESH, fetchingMissing));
|
||||
}
|
||||
} else if (timelineType == Timeline.TimeLineEnum.LIST) { //LIST TIMELINE
|
||||
if (direction == null) {
|
||||
|
@ -548,6 +595,9 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
} else if (direction == DIRECTION.TOP) {
|
||||
timelinesVM.getList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, list_id, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing));
|
||||
} else if (direction == DIRECTION.REFRESH) {
|
||||
timelinesVM.getList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, list_id, null, null, null, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesTop -> dealWithPagination(statusesTop, DIRECTION.REFRESH, fetchingMissing));
|
||||
}
|
||||
} else if (timelineType == Timeline.TimeLineEnum.TAG || timelineType == Timeline.TimeLineEnum.ART) { //TAG TIMELINE
|
||||
if (tagTimeline == null) {
|
||||
|
@ -563,6 +613,9 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
} else if (direction == DIRECTION.TOP) {
|
||||
timelinesVM.getHashTag(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, tagTimeline.name, false, tagTimeline.isART, tagTimeline.all, tagTimeline.any, tagTimeline.none, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing));
|
||||
} else if (direction == DIRECTION.REFRESH) {
|
||||
timelinesVM.getHashTag(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, tagTimeline.name, false, tagTimeline.isART, tagTimeline.all, tagTimeline.any, tagTimeline.none, null, null, null, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesTop -> dealWithPagination(statusesTop, DIRECTION.REFRESH, fetchingMissing));
|
||||
}
|
||||
} else if (timelineType == Timeline.TimeLineEnum.ACCOUNT_TIMELINE) { //PROFILE TIMELINES
|
||||
if (direction == null) {
|
||||
|
@ -585,7 +638,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
}
|
||||
} else if (direction == DIRECTION.BOTTOM) {
|
||||
accountsVM.getAccountStatuses(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, accountTimeline.id, max_id, null, null, exclude_replies, exclude_reblogs, media_only, false, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, fetchingMissing));
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false));
|
||||
} else {
|
||||
flagLoading = false;
|
||||
}
|
||||
|
@ -613,7 +666,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
.observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
|
||||
} else if (direction == DIRECTION.BOTTOM) {
|
||||
accountsVM.getFavourites(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, String.valueOf(MastodonHelper.statusesPerCall(requireActivity())), null, max_id)
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, fetchingMissing));
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false));
|
||||
} else {
|
||||
flagLoading = false;
|
||||
}
|
||||
|
@ -623,7 +676,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
.observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
|
||||
} else if (direction == DIRECTION.BOTTOM) {
|
||||
accountsVM.getBookmarks(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, String.valueOf(MastodonHelper.statusesPerCall(requireActivity())), max_id, null, null)
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, fetchingMissing));
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false));
|
||||
} else {
|
||||
flagLoading = false;
|
||||
}
|
||||
|
@ -667,16 +720,16 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
} else {
|
||||
max_id = null;
|
||||
}
|
||||
timelinesVM.getHome(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()), false)
|
||||
timelinesVM.getHome(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, false, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()), false)
|
||||
.observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
|
||||
});
|
||||
} else {
|
||||
timelinesVM.getHome(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()), false)
|
||||
timelinesVM.getHome(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, false, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()), false)
|
||||
.observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
|
||||
}
|
||||
|
||||
} else {
|
||||
timelinesVM.getHomeCache(BaseMainActivity.currentInstance, BaseMainActivity.currentUserID, null, null)
|
||||
timelinesVM.getHomeCache(BaseMainActivity.currentInstance, BaseMainActivity.currentUserID, null, null, null)
|
||||
.observe(getViewLifecycleOwner(), cachedStatus -> {
|
||||
if (cachedStatus != null && cachedStatus.statuses != null) {
|
||||
initializeStatusesCommonView(cachedStatus);
|
||||
|
@ -687,35 +740,38 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
} else if (direction == DIRECTION.BOTTOM) {
|
||||
if (networkAvailable == BaseMainActivity.status.CONNECTED) {
|
||||
//We first if we get results from cache
|
||||
timelinesVM.getHomeCache(BaseMainActivity.currentInstance, BaseMainActivity.currentUserID, max_id, null)
|
||||
timelinesVM.getHomeCache(BaseMainActivity.currentInstance, BaseMainActivity.currentUserID, max_id, null, null)
|
||||
.observe(getViewLifecycleOwner(), statusesBottomCache -> {
|
||||
if (statusesBottomCache != null && statusesBottomCache.statuses != null && statusesBottomCache.statuses.size() > 0) {
|
||||
dealWithPagination(statusesBottomCache, DIRECTION.BOTTOM, fetchingMissing);
|
||||
} else { // If not, we fetch remotely
|
||||
timelinesVM.getHome(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()), false)
|
||||
timelinesVM.getHome(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, fetchingMissing, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()), false)
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, fetchingMissing));
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
timelinesVM.getHomeCache(BaseMainActivity.currentInstance, BaseMainActivity.currentUserID, max_id, null)
|
||||
timelinesVM.getHomeCache(BaseMainActivity.currentInstance, BaseMainActivity.currentUserID, max_id, null, null)
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, fetchingMissing));
|
||||
}
|
||||
} else if (direction == DIRECTION.TOP) {
|
||||
if (networkAvailable == BaseMainActivity.status.CONNECTED) {
|
||||
timelinesVM.getHomeCache(BaseMainActivity.currentInstance, BaseMainActivity.currentUserID, null, fetchingMissing ? min_id_fetch_more : min_id)
|
||||
timelinesVM.getHomeCache(BaseMainActivity.currentInstance, BaseMainActivity.currentUserID, null, fetchingMissing ? min_id_fetch_more : min_id, null)
|
||||
.observe(getViewLifecycleOwner(), statusesTopCache -> {
|
||||
if (statusesTopCache != null && statusesTopCache.statuses != null && statusesTopCache.statuses.size() > 0) {
|
||||
dealWithPagination(statusesTopCache, DIRECTION.TOP, fetchingMissing);
|
||||
} else {
|
||||
timelinesVM.getHome(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity()), false)
|
||||
timelinesVM.getHome(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, fetchingMissing, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity()), false)
|
||||
.observe(getViewLifecycleOwner(), statusesTop -> dealWithPagination(statusesTop, DIRECTION.TOP, fetchingMissing));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
timelinesVM.getHomeCache(BaseMainActivity.currentInstance, BaseMainActivity.currentUserID, null, fetchingMissing ? min_id_fetch_more : min_id)
|
||||
timelinesVM.getHomeCache(BaseMainActivity.currentInstance, BaseMainActivity.currentUserID, null, fetchingMissing ? min_id_fetch_more : min_id, null)
|
||||
.observe(getViewLifecycleOwner(), statusesTop -> dealWithPagination(statusesTop, DIRECTION.TOP, fetchingMissing));
|
||||
}
|
||||
} else if (direction == DIRECTION.REFRESH) {
|
||||
timelinesVM.getHome(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, fetchingMissing, null, null, null, MastodonHelper.statusesPerCall(requireActivity()), false)
|
||||
.observe(getViewLifecycleOwner(), statusesTop -> dealWithPagination(statusesTop, DIRECTION.REFRESH, fetchingMissing));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -738,6 +794,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
|
||||
public enum DIRECTION {
|
||||
TOP,
|
||||
BOTTOM
|
||||
BOTTOM,
|
||||
REFRESH
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@ package app.fedilab.android.ui.pageadapter;
|
|||
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
|
@ -27,6 +28,7 @@ import app.fedilab.android.client.entities.app.Pinned;
|
|||
import app.fedilab.android.client.entities.app.PinnedTimeline;
|
||||
import app.fedilab.android.client.entities.app.Timeline;
|
||||
import app.fedilab.android.helper.Helper;
|
||||
import app.fedilab.android.helper.PinnedTimelineHelper;
|
||||
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonConversation;
|
||||
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline;
|
||||
import app.fedilab.android.ui.fragment.timeline.FragmentNotificationContainer;
|
||||
|
@ -37,11 +39,15 @@ public class FedilabPageAdapter extends FragmentStatePagerAdapter {
|
|||
private final Pinned pinned;
|
||||
private final BottomMenu bottomMenu;
|
||||
private Fragment mCurrentFragment;
|
||||
private final Context context;
|
||||
private final int toRemove;
|
||||
|
||||
public FedilabPageAdapter(FragmentManager fm, Pinned pinned, BottomMenu bottomMenu) {
|
||||
public FedilabPageAdapter(Context context, FragmentManager fm, Pinned pinned, BottomMenu bottomMenu) {
|
||||
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
|
||||
this.pinned = pinned;
|
||||
this.bottomMenu = bottomMenu;
|
||||
this.context = context;
|
||||
toRemove = PinnedTimelineHelper.itemToRemoveInBottomMenu(context);
|
||||
}
|
||||
|
||||
public Fragment getCurrentFragment() {
|
||||
|
@ -68,9 +74,10 @@ public class FedilabPageAdapter extends FragmentStatePagerAdapter {
|
|||
FragmentMastodonTimeline fragment = new FragmentMastodonTimeline();
|
||||
Bundle bundle = new Bundle();
|
||||
//Position 3 is for notifications
|
||||
if (position < 5) {
|
||||
if (position < BOTTOM_TIMELINE_COUNT - toRemove) {
|
||||
if (bottomMenu != null) {
|
||||
BottomMenu.ItemMenuType type = BottomMenu.getType(bottomMenu, position);
|
||||
|
||||
if (type == null) {
|
||||
return fragment;
|
||||
}
|
||||
|
@ -91,7 +98,7 @@ public class FedilabPageAdapter extends FragmentStatePagerAdapter {
|
|||
}
|
||||
|
||||
} else {
|
||||
int pinnedPosition = position - BOTTOM_TIMELINE_COUNT; //Real position has an offset.
|
||||
int pinnedPosition = position - (BOTTOM_TIMELINE_COUNT - toRemove); //Real position has an offset.
|
||||
PinnedTimeline pinnedTimeline = pinned.pinnedTimelines.get(pinnedPosition);
|
||||
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, pinnedTimeline.type);
|
||||
|
||||
|
@ -112,10 +119,11 @@ public class FedilabPageAdapter extends FragmentStatePagerAdapter {
|
|||
|
||||
@Override
|
||||
public int getCount() {
|
||||
|
||||
if (pinned != null && pinned.pinnedTimelines != null) {
|
||||
return pinned.pinnedTimelines.size() + BOTTOM_TIMELINE_COUNT;
|
||||
return pinned.pinnedTimelines.size() + BOTTOM_TIMELINE_COUNT - toRemove;
|
||||
} else {
|
||||
return BOTTOM_TIMELINE_COUNT;
|
||||
return BOTTOM_TIMELINE_COUNT - toRemove;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -193,6 +193,7 @@ public class TimelinesVM extends AndroidViewModel {
|
|||
* @return {@link LiveData} containing a {@link Statuses}
|
||||
*/
|
||||
public LiveData<Statuses> getHome(@NonNull String instance, String token,
|
||||
boolean fetchingMissing,
|
||||
String maxId,
|
||||
String sinceId,
|
||||
String minId,
|
||||
|
@ -211,18 +212,20 @@ public class TimelinesVM extends AndroidViewModel {
|
|||
List<Status> filteredStatuses = TimelineHelper.filterStatus(getApplication().getApplicationContext(), notFilteredStatuses, TimelineHelper.FilterTimeLineType.HOME);
|
||||
statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), filteredStatuses);
|
||||
statuses.pagination = MastodonHelper.getPagination(homeTlResponse.headers());
|
||||
for (Status status : statuses.statuses) {
|
||||
StatusCache statusCacheDAO = new StatusCache(getApplication().getApplicationContext());
|
||||
StatusCache statusCache = new StatusCache();
|
||||
statusCache.instance = instance;
|
||||
statusCache.user_id = BaseMainActivity.currentUserID;
|
||||
statusCache.status = status;
|
||||
statusCache.type = StatusCache.CacheEnum.HOME;
|
||||
statusCache.status_id = status.id;
|
||||
try {
|
||||
statusCacheDAO.insertOrUpdate(statusCache);
|
||||
} catch (DBException e) {
|
||||
e.printStackTrace();
|
||||
if (!fetchingMissing) {
|
||||
for (Status status : statuses.statuses) {
|
||||
StatusCache statusCacheDAO = new StatusCache(getApplication().getApplicationContext());
|
||||
StatusCache statusCache = new StatusCache();
|
||||
statusCache.instance = instance;
|
||||
statusCache.user_id = BaseMainActivity.currentUserID;
|
||||
statusCache.status = status;
|
||||
statusCache.type = StatusCache.CacheEnum.HOME;
|
||||
statusCache.status_id = status.id;
|
||||
try {
|
||||
statusCacheDAO.insertOrUpdate(statusCache);
|
||||
} catch (DBException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -249,13 +252,14 @@ public class TimelinesVM extends AndroidViewModel {
|
|||
*/
|
||||
public LiveData<Statuses> getHomeCache(@NonNull String instance, String user_id,
|
||||
String maxId,
|
||||
String minId) {
|
||||
String minId,
|
||||
String sinceId) {
|
||||
statusesMutableLiveData = new MutableLiveData<>();
|
||||
new Thread(() -> {
|
||||
StatusCache statusCacheDAO = new StatusCache(getApplication().getApplicationContext());
|
||||
Statuses statuses = null;
|
||||
try {
|
||||
statuses = statusCacheDAO.geStatuses(StatusCache.CacheEnum.HOME, instance, user_id, maxId, minId);
|
||||
statuses = statusCacheDAO.geStatuses(StatusCache.CacheEnum.HOME, instance, user_id, maxId, minId, sinceId);
|
||||
if (statuses != null) {
|
||||
statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statuses.statuses);
|
||||
if (statuses.statuses != null && statuses.statuses.size() > 0) {
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.button.MaterialButton xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/fetch_more"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
style="@style/MyOutlinedButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="6dp"
|
||||
android:text="@string/fetch_more_messages"
|
||||
android:textColor="?colorAccent"
|
||||
app:strokeColor="@color/cyanea_accent_dark_reference" />
|
||||
android:text="@string/fetch_more_messages" />
|
Loading…
Reference in a new issue