improve cache

pull/434/head
Thomas 2 years ago
parent 550187afc5
commit 03bf06b6a0

@ -76,8 +76,8 @@ public class QuickLoad {
* @param timeLineType - Timeline.TimeLineEnum
* @return boolean
*/
private static boolean cannotBeStored(Timeline.TimeLineEnum timeLineType) {
return timeLineType != Timeline.TimeLineEnum.HOME && timeLineType != Timeline.TimeLineEnum.LOCAL && timeLineType != Timeline.TimeLineEnum.PUBLIC && timeLineType != Timeline.TimeLineEnum.REMOTE && timeLineType != Timeline.TimeLineEnum.LIST && timeLineType != Timeline.TimeLineEnum.TAG;
public static boolean cannotBeStored(Timeline.TimeLineEnum timeLineType) {
return timeLineType != Timeline.TimeLineEnum.LOCAL && timeLineType != Timeline.TimeLineEnum.PUBLIC && timeLineType != Timeline.TimeLineEnum.REMOTE && timeLineType != Timeline.TimeLineEnum.LIST && timeLineType != Timeline.TimeLineEnum.TAG;
}
/**

@ -27,6 +27,7 @@ import java.util.Collections;
import java.util.Date;
import java.util.List;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.client.entities.api.Pagination;
import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.api.Statuses;
@ -44,8 +45,10 @@ public class StatusCache {
public String user_id;
@SerializedName("instance")
public String instance;
@SerializedName("slug")
public String slug;
@SerializedName("type")
public CacheEnum type;
public Timeline.TimeLineEnum type;
@SerializedName("status_id")
public String status_id;
@SerializedName("status")
@ -104,16 +107,17 @@ public class StatusCache {
* @return long - db id
* @throws DBException exception with database
*/
public long insertOrUpdate(StatusCache statusCache) throws DBException {
public long insertOrUpdate(StatusCache statusCache, String slug) throws DBException {
if (db == null) {
throw new DBException("db is null. Wrong initialization.");
}
statusCache.slug = slug;
boolean exists = statusExist(statusCache);
long idReturned;
if (exists) {
idReturned = updateStatus(statusCache);
} else {
idReturned = insertStatus(statusCache);
idReturned = insertStatus(statusCache, slug);
}
return idReturned;
}
@ -155,6 +159,27 @@ public class StatusCache {
return (count > 0);
}
/**
* Check if a status exists in db
*
* @param status Status {@link Status}
* @return boolean - StatusCache exists
* @throws DBException Exception
*/
public boolean statusExist(Status status) throws DBException {
if (db == null) {
throw new DBException("db is null. Wrong initialization.");
}
Cursor mCount = db.rawQuery("select count(*) from " + Sqlite.TABLE_STATUS_CACHE
+ " where " + Sqlite.COL_STATUS_ID + " = '" + status.id + "'"
+ " AND " + Sqlite.COL_INSTANCE + " = '" + MainActivity.currentInstance + "'"
+ " AND " + Sqlite.COL_USER_ID + "= '" + MainActivity.currentUserID + "'", null);
mCount.moveToFirst();
int count = mCount.getInt(0);
mCount.close();
return (count > 0);
}
/**
* Insert a status in db
*
@ -162,14 +187,14 @@ public class StatusCache {
* @return long - db id
* @throws DBException exception with database
*/
private long insertStatus(StatusCache statusCache) throws DBException {
private long insertStatus(StatusCache statusCache, String slug) throws DBException {
if (db == null) {
throw new DBException("db is null. Wrong initialization.");
}
ContentValues values = new ContentValues();
values.put(Sqlite.COL_USER_ID, statusCache.user_id);
values.put(Sqlite.COL_INSTANCE, statusCache.instance);
values.put(Sqlite.COL_TYPE, statusCache.type.getValue());
values.put(Sqlite.COL_SLUG, statusCache.slug);
values.put(Sqlite.COL_STATUS_ID, statusCache.status_id);
values.put(Sqlite.COL_STATUS, mastodonStatusToStringStorage(statusCache.status));
values.put(Sqlite.COL_CREATED_AT, Helper.dateToString(new Date()));
@ -195,9 +220,6 @@ public class StatusCache {
}
ContentValues values = new ContentValues();
values.put(Sqlite.COL_USER_ID, statusCache.user_id);
if (statusCache.type != null) {
values.put(Sqlite.COL_TYPE, statusCache.type.getValue());
}
values.put(Sqlite.COL_STATUS_ID, statusCache.status_id);
values.put(Sqlite.COL_STATUS, mastodonStatusToStringStorage(statusCache.status));
values.put(Sqlite.COL_UPDATED_AT, Helper.dateToString(new Date()));
@ -274,10 +296,63 @@ 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
*
* @param type CacheEnum - not used yet but will allow to extend cache to other timelines
* @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
* @param max_id String - status having max id
@ -285,12 +360,12 @@ 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, String since_id) throws DBException {
public Statuses geStatuses(String slug, 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 selection = Sqlite.COL_INSTANCE + "='" + instance + "' AND " + Sqlite.COL_USER_ID + "= '" + user_id + "' AND " + Sqlite.COL_SLUG + "= '" + slug + "'";
String limit = String.valueOf(MastodonHelper.statusesPerCall(context));
if (min_id != null) {
selection += "AND " + Sqlite.COL_STATUS_ID + " > '" + min_id + "'";
@ -310,16 +385,70 @@ 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;
}
}
/**
* @param type CacheEnum - not used yet but will allow to extend cache to other timelines
* Get statuses from db
*
* @param statusCache StatusCache - status in cache to compare
* @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 slug String - slug for the timeline (it's a unique string value for a timeline)
* @param instance String - instance
* @param user_id String - us
* @param search String search
* @return - List<Status>
* @throws DBException exception
*/
public List<Status> searchStatus(CacheEnum type, String instance, String user_id, String search) throws DBException {
public List<Status> searchStatus(String slug, String instance, String user_id, String search) throws DBException {
if (db == null) {
throw new DBException("db is null. Wrong initialization.");
}
@ -343,6 +472,22 @@ public class StatusCache {
return reply;
}
public enum order {
@SerializedName("ASC")
ASC("ASC"),
@SerializedName("DESC")
DESC("DESC");
private final String value;
order(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
public int count(BaseAccount account) throws DBException {
if (db == null) {
@ -412,17 +557,4 @@ public class StatusCache {
return restoreStatusFromString(serializedStatus);
}
public enum CacheEnum {
@SerializedName("HOME")
HOME("HOME");
private final String value;
CacheEnum(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
}

@ -23,7 +23,7 @@ import android.database.sqlite.SQLiteOpenHelper;
public class Sqlite extends SQLiteOpenHelper {
public static final int DB_VERSION = 5;
public static final int DB_VERSION = 6;
public static final String DB_NAME = "fedilab_db";
//Table of owned accounts
@ -111,6 +111,7 @@ public class Sqlite extends SQLiteOpenHelper {
+ COL_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ COL_USER_ID + " TEXT NOT NULL, " + COL_INSTANCE + " TEXT NOT NULL, "
+ COL_TYPE + " TEXT NOT NULL, "
+ COL_SLUG + " TEXT, "
+ COL_STATUS_ID + " TEXT NOT NULL, "
+ COL_STATUS + " TEXT NOT NULL, "
+ COL_CREATED_AT + " TEXT NOT NULL,"
@ -202,7 +203,6 @@ public class Sqlite extends SQLiteOpenHelper {
db.execSQL(CREATE_TABLE_STATUS_DRAFT);
db.execSQL(CREATE_TABLE_PINNED_TIMELINES);
db.execSQL(CREATE_TABLE_SCHEDULE_BOOST);
db.execSQL(CREATE_TABLE_QUICK_LOAD);
db.execSQL(CREATE_TABLE_BOTTOM_MENU);
db.execSQL(CREATE_DOMAINS_TRACKING);
}
@ -223,6 +223,10 @@ public class Sqlite extends SQLiteOpenHelper {
db.execSQL("ALTER TABLE " + TABLE_USER_ACCOUNT + " ADD COLUMN " + COL_ADMIN + " INTEGER NOT NULL DEFAULT 0");
case 4:
db.execSQL(CREATE_DOMAINS_TRACKING);
case 5:
db.execSQL("ALTER TABLE " + TABLE_STATUS_CACHE + " ADD COLUMN " + COL_SLUG + " TEXT");
db.execSQL("DELETE FROM " + TABLE_STATUS_CACHE);
db.execSQL("DROP TABLE IF EXISTS " + TABLE_QUICK_LOAD);
default:
break;
}

@ -44,7 +44,6 @@ import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.client.entities.api.Attachment;
import app.fedilab.android.client.entities.api.Marker;
import app.fedilab.android.client.entities.api.Pagination;
import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.api.Statuses;
@ -127,7 +126,6 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
}
}
};
private List<String> markers;
private String list_id;
private TagTimeline tagTimeline;
private LinearLayoutManager mLayoutManager;
@ -251,8 +249,6 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
binding.loader.setVisibility(View.VISIBLE);
binding.recyclerView.setVisibility(View.GONE);
//Markers for home and notifications to get last read ones
markers = new ArrayList<>();
max_id = statusReport != null ? statusReport.id : null;
flagLoading = false;
router(null);
@ -557,7 +553,12 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
if (statuses != null && statuses.size() > position) {
try {
Status status = statuses.get(position);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putString(getString(R.string.SET_HOME_INNER_MARKER) + BaseMainActivity.currentUserID + BaseMainActivity.currentInstance, status.id);
editor.apply();
timelinesVM.addMarker(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, status.id, null);
} catch (Exception ignored) {
}
}
@ -579,6 +580,66 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
}
}
/**
* Router for timelines
*
* @param direction - DIRECTION null if first call, then is set to TOP or BOTTOM depending of scroll
*/
private void routeCommon(DIRECTION direction, boolean fetchingMissing) {
if (binding == null || getActivity() == null || !isAdded()) {
return;
}
//Initialize with default params
TimelinesVM.TimelineParams timelineParams = new TimelinesVM.TimelineParams(timelineType, direction, ident);
timelineParams.limit = MastodonHelper.statusesPerCall(requireActivity());
timelineParams.maxId = fetchingMissing ? max_id_fetch_more : max_id;
timelineParams.minId = fetchingMissing ? min_id_fetch_more : min_id;
timelineParams.fetchingMissing = fetchingMissing;
switch (timelineType) {
case LOCAL:
timelineParams.local = true;
timelineParams.remote = false;
break;
case PUBLIC:
timelineParams.local = false;
timelineParams.remote = true;
break;
case LIST:
timelineParams.listId = list_id;
break;
case TAG:
if (tagTimeline == null) {
tagTimeline = new TagTimeline();
tagTimeline.name = search;
}
timelineParams.onlyMedia = timelineType == Timeline.TimeLineEnum.ART;
timelineParams.none = tagTimeline.none;
timelineParams.all = tagTimeline.all;
timelineParams.any = tagTimeline.any;
timelineParams.hashtagTrim = tagTimeline.name;
break;
}
if (direction == null) {
timelinesVM.getTimeline(timelineParams)
.observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
} else if (direction == DIRECTION.BOTTOM) {
timelinesVM.getTimeline(timelineParams)
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, fetchingMissing));
} else if (direction == DIRECTION.TOP) {
timelinesVM.getTimeline(timelineParams)
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing));
} else if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP) {
timelinesVM.getTimeline(timelineParams)
.observe(getViewLifecycleOwner(), statusesRefresh -> {
if (statusAdapter != null) {
dealWithPagination(statusesRefresh, direction, true);
} else {
initializeStatusesCommonView(statusesRefresh);
}
});
}
}
/**
* Router for timelines
*
@ -589,17 +650,26 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
if (binding == null || getActivity() == null || !isAdded()) {
return;
}
boolean nitterInstance = timelineType == Timeline.TimeLineEnum.REMOTE && pinnedTimeline != null && pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.NITTER;
QuickLoad quickLoad = new QuickLoad(requireActivity()).getSavedValue(BaseMainActivity.currentUserID, BaseMainActivity.currentInstance, timelineType, ident);
boolean nitterInstance = false;
//For remote instance, we check if it's a Nitter timeline
if (timelineType == Timeline.TimeLineEnum.REMOTE) {
nitterInstance = pinnedTimeline != null && pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.NITTER;
}
QuickLoad quickLoad = null;
//For timelines that can be stored we check if there is a stored TL in db
//HOME TL, is excluded and should use its own cache
if (!QuickLoad.cannotBeStored(timelineType)) {
quickLoad = new QuickLoad(requireActivity()).getSavedValue(BaseMainActivity.currentUserID, BaseMainActivity.currentInstance, timelineType, ident);
}
if (!nitterInstance && !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();
statuses.pagination.max_id = quickLoad.statuses.get(quickLoad.statuses.size() - 1).id;
statuses.pagination.min_id = quickLoad.statuses.get(0).id;
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> initializeStatusesCommonView(statuses, quickLoad.position);
QuickLoad finalQuickLoad = quickLoad;
Runnable myRunnable = () -> initializeStatusesCommonView(statuses, finalQuickLoad.position);
mainHandler.post(myRunnable);
} else {
Handler mainHandler = new Handler(Looper.getMainLooper());
@ -610,49 +680,12 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
// --- HOME TIMELINE ---
if (timelineType == Timeline.TimeLineEnum.HOME) {
//for more visibility it's done through loadHomeStrategy method
loadHomeStrategy(direction, fetchingMissing);
routeCommon(direction, fetchingMissing);
} else if (timelineType == Timeline.TimeLineEnum.LOCAL) { //LOCAL TIMELINE
if (direction == null) {
timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, true, false, false, null, null, null, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
} else if (direction == DIRECTION.BOTTOM) {
timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, true, false, false, fetchingMissing ? max_id_fetch_more : max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, fetchingMissing));
} 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 || direction == DIRECTION.SCROLL_TOP) {
timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, true, false, false, null, null, null, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), statusesRefresh -> {
if (statusAdapter != null) {
dealWithPagination(statusesRefresh, direction, true);
} else {
initializeStatusesCommonView(statusesRefresh);
}
});
}
routeCommon(direction, fetchingMissing);
} else if (timelineType == Timeline.TimeLineEnum.PUBLIC) { //PUBLIC TIMELINE
if (direction == null) {
timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, false, true, false, null, null, null, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
} else if (direction == DIRECTION.BOTTOM) {
timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, false, true, false, fetchingMissing ? max_id_fetch_more : max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, fetchingMissing));
} 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 || direction == DIRECTION.SCROLL_TOP) {
timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, false, true, false, null, null, null, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), statusesRefresh -> {
if (statusAdapter != null) {
dealWithPagination(statusesRefresh, direction, true);
} else {
initializeStatusesCommonView(statusesRefresh);
}
});
}
routeCommon(direction, fetchingMissing);
} else if (timelineType == Timeline.TimeLineEnum.REMOTE) { //REMOTE TIMELINE
//NITTER TIMELINES
if (pinnedTimeline != null && pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.NITTER) {
if (direction == null) {
@ -718,70 +751,12 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
});
}
} else { //Other remote timelines
if (direction == null) {
timelinesVM.getPublic(null, remoteInstance, true, false, false, null, null, null, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
} else if (direction == DIRECTION.BOTTOM) {
timelinesVM.getPublic(null, remoteInstance, true, false, false, fetchingMissing ? max_id_fetch_more : max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, fetchingMissing));
} 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 || direction == DIRECTION.SCROLL_TOP) {
timelinesVM.getPublic(null, remoteInstance, true, false, false, null, null, null, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), statusesRefresh -> {
if (statusAdapter != null) {
dealWithPagination(statusesRefresh, direction, true);
} else {
initializeStatusesCommonView(statusesRefresh);
}
});
}
routeCommon(direction, fetchingMissing);
}
} else if (timelineType == Timeline.TimeLineEnum.LIST) { //LIST TIMELINE
if (direction == null) {
timelinesVM.getList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, list_id, null, null, null, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
} else if (direction == DIRECTION.BOTTOM) {
timelinesVM.getList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, list_id, fetchingMissing ? max_id_fetch_more : max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, fetchingMissing));
} 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 || direction == DIRECTION.SCROLL_TOP) {
timelinesVM.getList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, list_id, null, null, null, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), statusesRefresh -> {
if (statusAdapter != null) {
dealWithPagination(statusesRefresh, direction, true);
} else {
initializeStatusesCommonView(statusesRefresh);
}
});
}
routeCommon(direction, fetchingMissing);
} else if (timelineType == Timeline.TimeLineEnum.TAG || timelineType == Timeline.TimeLineEnum.ART) { //TAG TIMELINE
if (tagTimeline == null) {
tagTimeline = new TagTimeline();
tagTimeline.name = search;
}
if (direction == null) {
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(), this::initializeStatusesCommonView);
} else if (direction == DIRECTION.BOTTOM) {
timelinesVM.getHashTag(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, tagTimeline.name, false, tagTimeline.isART, tagTimeline.all, tagTimeline.any, tagTimeline.none, fetchingMissing ? max_id_fetch_more : max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, fetchingMissing));
} 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 || direction == DIRECTION.SCROLL_TOP) {
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(), statusesRefresh -> {
if (statusAdapter != null) {
dealWithPagination(statusesRefresh, direction, true);
} else {
initializeStatusesCommonView(statusesRefresh);
}
});
}
routeCommon(direction, fetchingMissing);
} else if (timelineType == Timeline.TimeLineEnum.ACCOUNT_TIMELINE) { //PROFILE TIMELINES
if (direction == null) {
if (show_pinned) {
@ -875,110 +850,6 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
}
/**
* Load home timeline strategy
*
* @param direction - DIRECTION enum
*/
private void loadHomeStrategy(DIRECTION direction, boolean fetchingMissing) {
//When no direction is provided, it means it's the first call
if (direction == null && !fetchingMissing) {
//Two ways, depending of the Internet connection
//Connection is available toots are loaded remotely
if (networkAvailable == BaseMainActivity.status.CONNECTED) {
boolean fetchMarker = false;
if (markers.isEmpty()) {
markers.add("home");
fetchMarker = true;
}
//We search for marker only once - It should not be fetched again when pull to refresh
if (fetchMarker && !binding.swipeContainer.isRefreshing()) {
//Search for last position
timelinesVM.getMarker(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, markers).observe(getViewLifecycleOwner(), marker -> {
if (marker != null) {
Marker.MarkerContent markerContent = marker.home;
if (markerContent != null) {
max_id = markerContent.last_read_id;
min_id = markerContent.last_read_id;
} else {
max_id = null;
}
} else {
max_id = null;
}
timelinesVM.getHome(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, BaseMainActivity.currentUserID, false, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()), false)
.observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
});
} else {
timelinesVM.getHome(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, BaseMainActivity.currentUserID, false, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()), false)
.observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
}
} else {
timelinesVM.getHomeCache(BaseMainActivity.currentInstance, BaseMainActivity.currentUserID, null, null, null)
.observe(getViewLifecycleOwner(), cachedStatus -> {
if (cachedStatus != null && cachedStatus.statuses != null) {
initializeStatusesCommonView(cachedStatus);
}
});
}
} else if (direction == DIRECTION.BOTTOM) {
if (!fetchingMissing) {
if (networkAvailable == BaseMainActivity.status.CONNECTED) {
//We first if we get results from cache
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, false);
} else { // If not, we fetch remotely
timelinesVM.getHome(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, BaseMainActivity.currentUserID, false, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()), false)
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false));
}
});
} else {
timelinesVM.getHomeCache(BaseMainActivity.currentInstance, BaseMainActivity.currentUserID, max_id, null, null)
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false));
}
} else {
timelinesVM.getHome(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, BaseMainActivity.currentUserID, true, max_id_fetch_more, null, null, MastodonHelper.statusesPerCall(requireActivity()), false)
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, true));
}
} else if (direction == DIRECTION.TOP) {
if (!fetchingMissing) {
if (networkAvailable == BaseMainActivity.status.CONNECTED) {
timelinesVM.getHomeCache(BaseMainActivity.currentInstance, BaseMainActivity.currentUserID, null, min_id, null)
.observe(getViewLifecycleOwner(), statusesTopCache -> {
if (statusesTopCache != null && statusesTopCache.statuses != null && statusesTopCache.statuses.size() > 0) {
dealWithPagination(statusesTopCache, DIRECTION.TOP, false);
} else {
timelinesVM.getHome(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, BaseMainActivity.currentUserID, false, null, null, min_id, MastodonHelper.statusesPerCall(requireActivity()), false)
.observe(getViewLifecycleOwner(), statusesTop -> dealWithPagination(statusesTop, DIRECTION.TOP, false));
}
});
} else {
timelinesVM.getHomeCache(BaseMainActivity.currentInstance, BaseMainActivity.currentUserID, null, min_id, null)
.observe(getViewLifecycleOwner(), statusesTop -> dealWithPagination(statusesTop, DIRECTION.TOP, false));
}
} else {
timelinesVM.getHome(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, BaseMainActivity.currentUserID, true, null, null, min_id_fetch_more, MastodonHelper.statusesPerCall(requireActivity()), false)
.observe(getViewLifecycleOwner(), statusesTop -> dealWithPagination(statusesTop, DIRECTION.TOP, true));
}
} else if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP) {
timelinesVM.getHome(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, BaseMainActivity.currentUserID, true, null, null, null, MastodonHelper.statusesPerCall(requireActivity()), false)
.observe(getViewLifecycleOwner(), statusRefresh -> {
if (statusAdapter != null) {
dealWithPagination(statusRefresh, direction, true);
} else {
initializeStatusesCommonView(statusRefresh);
}
}
);
}
}
/**
* Refresh status in list

@ -22,6 +22,7 @@ import android.os.Handler;
import android.os.Looper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
@ -32,6 +33,7 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.client.endpoints.MastodonTimelinesService;
import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.client.entities.api.Conversation;
@ -45,6 +47,7 @@ import app.fedilab.android.client.entities.api.Tag;
import app.fedilab.android.client.entities.app.BaseAccount;
import app.fedilab.android.client.entities.app.StatusCache;
import app.fedilab.android.client.entities.app.StatusDraft;
import app.fedilab.android.client.entities.app.Timeline;
import app.fedilab.android.client.entities.misskey.MisskeyNote;
import app.fedilab.android.client.entities.nitter.Nitter;
import app.fedilab.android.client.entities.peertube.PeertubeVideo;
@ -52,6 +55,7 @@ import app.fedilab.android.exception.DBException;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.MastodonHelper;
import app.fedilab.android.helper.TimelineHelper;
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline;
import okhttp3.OkHttpClient;
import retrofit2.Call;
import retrofit2.Response;
@ -162,50 +166,6 @@ public class TimelinesVM extends AndroidViewModel {
return tagListMutableLiveData;
}
/**
* Public timeline
*
* @param local Show only local statuses? Defaults to false.
* @param remote Show only remote statuses? Defaults to false.
* @param onlyMedia Show only statuses with media attached? Defaults to false.
* @param maxId Return results older than this id
* @param sinceId Return results newer than this id
* @param minId Return results immediately newer than this id
* @param limit Maximum number of results to return. Defaults to 20.
* @return {@link LiveData} containing a {@link Statuses}
*/
public LiveData<Statuses> getPublic(String token, @NonNull String instance,
Boolean local,
Boolean remote,
Boolean onlyMedia,
String maxId,
String sinceId,
String minId,
Integer limit) {
MastodonTimelinesService mastodonTimelinesService = init(instance);
statusesMutableLiveData = new MutableLiveData<>();
new Thread(() -> {
Call<List<Status>> publicTlCall = mastodonTimelinesService.getPublic(token, local, remote, onlyMedia, maxId, sinceId, minId, limit);
Statuses statuses = new Statuses();
if (publicTlCall != null) {
try {
Response<List<Status>> publicTlResponse = publicTlCall.execute();
if (publicTlResponse.isSuccessful()) {
List<Status> notFilteredStatuses = publicTlResponse.body();
statuses.statuses = TimelineHelper.filterStatus(getApplication(), notFilteredStatuses, TimelineHelper.FilterTimeLineType.PUBLIC);
statuses.pagination = MastodonHelper.getPagination(publicTlResponse.headers());
}
} catch (Exception e) {
e.printStackTrace();
}
}
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> statusesMutableLiveData.setValue(statuses);
mainHandler.post(myRunnable);
}).start();
return statusesMutableLiveData;
}
/**
* Public timeline for Nitter
@ -382,96 +342,65 @@ public class TimelinesVM extends AndroidViewModel {
}
/**
* View public statuses containing the given hashtag.
*
* @param hashtag Content of a #hashtag, not including # symbol.
* @param local If true, return only local statuses. Defaults to false.
* @param onlyMedia If true, return only statuses with media attachments. Defaults to false.
* @param maxId Return results older than this ID.
* @param sinceId Return results newer than this ID.
* @param minId Return results immediately newer than this ID.
* @param limit Maximum number of results to return. Defaults to 20.
* @return {@link LiveData} containing a {@link Statuses}
*/
public LiveData<Statuses> getHashTag(String token, @NonNull String instance,
@NonNull String hashtag,
boolean local,
boolean onlyMedia,
List<String> all,
List<String> any,
List<String> none,
String maxId,
String sinceId,
String minId,
int limit) {
statusesMutableLiveData = new MutableLiveData<>();
MastodonTimelinesService mastodonTimelinesService = init(instance);
new Thread(() -> {
Statuses statuses = new Statuses();
String hashtagTrim = hashtag.replaceAll("\\#", "");
Call<List<Status>> hashTagTlCall = mastodonTimelinesService.getHashTag(token, hashtagTrim, local, onlyMedia, all, any, none, maxId, sinceId, minId, limit);
if (hashTagTlCall != null) {
try {
Response<List<Status>> hashTagTlResponse = hashTagTlCall.execute();
if (hashTagTlResponse.isSuccessful()) {
List<Status> notFilteredStatuses = hashTagTlResponse.body();
statuses.statuses = TimelineHelper.filterStatus(getApplication().getApplicationContext(), notFilteredStatuses, TimelineHelper.FilterTimeLineType.PUBLIC);
statuses.pagination = MastodonHelper.getPagination(hashTagTlResponse.headers());
}
} catch (Exception e) {
e.printStackTrace();
}
}
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> statusesMutableLiveData.setValue(statuses);
mainHandler.post(myRunnable);
}).start();
return statusesMutableLiveData;
}
public LiveData<Statuses> getTimeline(TimelineParams timelineParams) {
/**
* View statuses from followed users.
*
* @param maxId Return results older than id
* @param sinceId Return results newer than id
* @param minId Return results immediately newer than id
* @param limit Maximum number of results to return. Defaults to 20.
* @param local Return only local statuses?
* @return {@link LiveData} containing a {@link Statuses}
*/
public LiveData<Statuses> getHome(@NonNull String instance, String token,
String userId,
boolean fetchingMissing,
String maxId,
String sinceId,
String minId,
int limit,
boolean local) {
statusesMutableLiveData = new MutableLiveData<>();
MastodonTimelinesService mastodonTimelinesService = init(instance);
MastodonTimelinesService mastodonTimelinesService = init(timelineParams.instance);
new Thread(() -> {
Statuses statuses = new Statuses();
Call<List<Status>> homeTlCall = mastodonTimelinesService.getHome(token, maxId, sinceId, minId, limit, local);
if (homeTlCall != null) {
Call<List<Status>> timelineCall = null;
switch (timelineParams.type) {
case HOME:
timelineCall = mastodonTimelinesService.getHome(timelineParams.token, timelineParams.maxId, timelineParams.sinceId, timelineParams.minId, timelineParams.limit, timelineParams.local);
break;
case LOCAL:
timelineCall = mastodonTimelinesService.getPublic(timelineParams.token, true, false, timelineParams.onlyMedia, timelineParams.maxId, timelineParams.sinceId, timelineParams.minId, timelineParams.limit);
break;
case PUBLIC:
timelineCall = mastodonTimelinesService.getPublic(timelineParams.token, false, true, timelineParams.onlyMedia, timelineParams.maxId, timelineParams.sinceId, timelineParams.minId, timelineParams.limit);
break;
case TAG:
timelineCall = mastodonTimelinesService.getHashTag(timelineParams.token, timelineParams.hashtagTrim, timelineParams.local, timelineParams.onlyMedia, timelineParams.all, timelineParams.any, timelineParams.none, timelineParams.maxId, timelineParams.sinceId, timelineParams.minId, timelineParams.limit);
break;
case LIST:
timelineCall = mastodonTimelinesService.getList(timelineParams.token, timelineParams.listId, timelineParams.maxId, timelineParams.sinceId, timelineParams.minId, timelineParams.limit);
break;
}
if (timelineCall != null) {
try {
Response<List<Status>> homeTlResponse = homeTlCall.execute();
if (homeTlResponse.isSuccessful()) {
List<Status> notFilteredStatuses = homeTlResponse.body();
statuses.statuses = TimelineHelper.filterStatus(getApplication().getApplicationContext(), notFilteredStatuses, TimelineHelper.FilterTimeLineType.HOME);
statuses.pagination = MastodonHelper.getPagination(homeTlResponse.headers());
if (!fetchingMissing) {
Response<List<Status>> timelineResponse = timelineCall.execute();
if (timelineResponse.isSuccessful()) {
List<Status> statusList = timelineResponse.body();
statuses.statuses = TimelineHelper.filterStatus(getApplication().getApplicationContext(), statusList, TimelineHelper.FilterTimeLineType.PUBLIC);
statuses.pagination = MastodonHelper.getPagination(timelineResponse.headers());
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) {
statusList.get(statusList.size() - 1).isFetchMore = true;
}
} 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) {
statusList.get(0).isFetchMore = true;
}
} 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) {
statusList.get(statusList.size() - 1).isFetchMore = true;
}
}
for (Status status : statuses.statuses) {
StatusCache statusCacheDAO = new StatusCache(getApplication().getApplicationContext());
StatusCache statusCache = new StatusCache();
statusCache.instance = instance;
statusCache.user_id = userId;
statusCache.instance = timelineParams.instance;
statusCache.user_id = timelineParams.userId;
statusCache.status = status;
statusCache.type = StatusCache.CacheEnum.HOME;
statusCache.type = timelineParams.type;
statusCache.status_id = status.id;
try {
statusCacheDAO.insertOrUpdate(statusCache);
statusCacheDAO.insertOrUpdate(statusCache, timelineParams.slug);
} catch (DBException e) {
e.printStackTrace();
}
@ -486,32 +415,18 @@ public class TimelinesVM extends AndroidViewModel {
Runnable myRunnable = () -> statusesMutableLiveData.setValue(statuses);
mainHandler.post(myRunnable);
}).start();
return statusesMutableLiveData;
}
/**
* Get home status from cache
*
* @param instance String - instance
* @param user_id String - user id
* @param maxId String - max id
* @param minId String - min id
* @return LiveData<Statuses>
*/
public LiveData<Statuses> getHomeCache(@NonNull String instance, String user_id,
String maxId,
String minId,
String sinceId) {
public LiveData<Statuses> getTimelineCache(TimelineParams timelineParams) {
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, sinceId);
statuses = statusCacheDAO.geStatuses(timelineParams.slug, timelineParams.instance, timelineParams.userId, timelineParams.maxId, timelineParams.minId, timelineParams.sinceId);
if (statuses != null) {
statuses.statuses = TimelineHelper.filterStatus(getApplication().getApplicationContext(), statuses.statuses, TimelineHelper.FilterTimeLineType.HOME);
TimelineHelper.filterStatus(getApplication().getApplicationContext(), statuses.statuses, TimelineHelper.FilterTimeLineType.HOME);
if (statuses.statuses != null && statuses.statuses.size() > 0) {
statuses.pagination = new Pagination();
statuses.pagination.min_id = statuses.statuses.get(0).id;
@ -529,6 +444,42 @@ public class TimelinesVM extends AndroidViewModel {
return statusesMutableLiveData;
}
public static class TimelineParams {
public FragmentMastodonTimeline.DIRECTION direction;
public String instance;
public String token;
public Timeline.TimeLineEnum type;
public String slug;
public String userId;
public Boolean remote;
public Boolean onlyMedia;
public String hashtagTrim;
public List<String> all;
public List<String> any;
public List<String> none;
public String listId;
public Boolean fetchingMissing;
public String maxId;
public String sinceId;
public String minId;
public int limit = 40;
public Boolean local;
public TimelineParams(@NonNull Timeline.TimeLineEnum type, @Nullable FragmentMastodonTimeline.DIRECTION direction, @Nullable String ident) {
if (type != Timeline.TimeLineEnum.REMOTE) {
instance = MainActivity.currentInstance;
token = MainActivity.currentToken;
userId = MainActivity.currentUserID;
}
String key = type.getValue();
if (ident != null) {
key += "|" + ident;
}
slug = key;
}
}
/**
* Get user drafts
@ -553,45 +504,6 @@ public class TimelinesVM extends AndroidViewModel {
return statusDraftListMutableLiveData;
}
/**
* View statuses in the given list timeline.
*
* @param listId Local ID of the list in the database.
* @param maxId Return results older than this ID.
* @param sinceId Return results newer than this ID.
* @param minId Return results immediately newer than this ID.
* @param limit Maximum number of results to return. Defaults to 20.Return results older than this ID.
* @return {@link LiveData} containing a {@link Statuses}
*/
public LiveData<Statuses> getList(@NonNull String instance, String token,
@NonNull String listId,
String maxId,
String sinceId,
String minId,
int limit) {
statusesMutableLiveData = new MutableLiveData<>();
MastodonTimelinesService mastodonTimelinesService = init(instance);
new Thread(() -> {
Statuses statuses = new Statuses();
Call<List<Status>> listTlCall = mastodonTimelinesService.getList(token, listId, maxId, sinceId, minId, limit);
if (listTlCall != null) {
try {
Response<List<Status>> listTlResponse = listTlCall.execute();
if (listTlResponse.isSuccessful()) {
statuses.statuses = listTlResponse.body();
statuses.pagination = MastodonHelper.getPagination(listTlResponse.headers());
}
} catch (Exception e) {
e.printStackTrace();
}
}
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> statusesMutableLiveData.setValue(statuses);
mainHandler.post(myRunnable);
}).start();
return statusesMutableLiveData;
}
/**
* Show conversations

@ -906,6 +906,8 @@
<string name="SET_DISPLAY_BOOKMARK" translatable="false">SET_DISPLAY_BOOKMARK</string>
<string name="SET_NOTIF_VALIDATION_FAV" translatable="false">SET_NOTIF_VALIDATION_FAV</string>
<string name="SET_DISPLAY_COUNTER_FAV_BOOST" translatable="false">SET_DISPLAY_COUNTER_FAV_BOOST</string>
<string name="SET_HOME_INNER_MARKER" translatable="false">SET_HOME_INNER_MARKER</string>
<string name="SET_NOTIF_SILENT" translatable="false">SET_NOTIF_SILENT</string>
<string name="SET_EXPAND_CW" translatable="false">SET_EXPAND_CW</string>
<string name="SET_DISPLAY_ALL_NOTIFICATIONS_TYPE" translatable="false">SET_DISPLAY_ALL_NOTIFICATIONS_TYPE</string>

Loading…
Cancel
Save