forked from mirrors/Fedilab
cache for notifications
This commit is contained in:
parent
62b0caec11
commit
9a665f3c8a
8 changed files with 424 additions and 100 deletions
|
@ -35,6 +35,12 @@ public class Notification {
|
||||||
public Account account;
|
public Account account;
|
||||||
@SerializedName("status")
|
@SerializedName("status")
|
||||||
public Status status;
|
public Status status;
|
||||||
|
public PositionFetchMore positionFetchMore = PositionFetchMore.BOTTOM;
|
||||||
|
|
||||||
|
public enum PositionFetchMore {
|
||||||
|
TOP,
|
||||||
|
BOTTOM
|
||||||
|
}
|
||||||
|
|
||||||
public transient List<Notification> relatedNotifications;
|
public transient List<Notification> relatedNotifications;
|
||||||
public boolean isFetchMore;
|
public boolean isFetchMore;
|
||||||
|
|
|
@ -28,6 +28,8 @@ import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import app.fedilab.android.activities.MainActivity;
|
import app.fedilab.android.activities.MainActivity;
|
||||||
|
import app.fedilab.android.client.entities.api.Notification;
|
||||||
|
import app.fedilab.android.client.entities.api.Notifications;
|
||||||
import app.fedilab.android.client.entities.api.Pagination;
|
import app.fedilab.android.client.entities.api.Pagination;
|
||||||
import app.fedilab.android.client.entities.api.Status;
|
import app.fedilab.android.client.entities.api.Status;
|
||||||
import app.fedilab.android.client.entities.api.Statuses;
|
import app.fedilab.android.client.entities.api.Statuses;
|
||||||
|
@ -53,6 +55,8 @@ public class StatusCache {
|
||||||
public String status_id;
|
public String status_id;
|
||||||
@SerializedName("status")
|
@SerializedName("status")
|
||||||
public Status status;
|
public Status status;
|
||||||
|
@SerializedName("notification")
|
||||||
|
public Notification notification;
|
||||||
@SerializedName("created_at")
|
@SerializedName("created_at")
|
||||||
public Date created_at;
|
public Date created_at;
|
||||||
@SerializedName("updated_at")
|
@SerializedName("updated_at")
|
||||||
|
@ -84,6 +88,21 @@ public class StatusCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialized a Notification class
|
||||||
|
*
|
||||||
|
* @param mastodon_notification {@link Notification} to serialize
|
||||||
|
* @return String serialized status
|
||||||
|
*/
|
||||||
|
public static String mastodonNotificationToStringStorage(Notification mastodon_notification) {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
try {
|
||||||
|
return gson.toJson(mastodon_notification);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unserialized a Mastodon Status
|
* Unserialized a Mastodon Status
|
||||||
*
|
*
|
||||||
|
@ -100,6 +119,22 @@ public class StatusCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unserialized a Mastodon Notification
|
||||||
|
*
|
||||||
|
* @param serializedNotification String serialized status
|
||||||
|
* @return {@link Notification}
|
||||||
|
*/
|
||||||
|
public static Notification restoreNotificationFromString(String serializedNotification) {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
try {
|
||||||
|
return gson.fromJson(serializedNotification, Notification.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert or update a status
|
* Insert or update a status
|
||||||
*
|
*
|
||||||
|
@ -197,7 +232,12 @@ public class StatusCache {
|
||||||
values.put(Sqlite.COL_SLUG, slug);
|
values.put(Sqlite.COL_SLUG, slug);
|
||||||
values.put(Sqlite.COL_STATUS_ID, statusCache.status_id);
|
values.put(Sqlite.COL_STATUS_ID, statusCache.status_id);
|
||||||
values.put(Sqlite.COL_TYPE, statusCache.type.getValue());
|
values.put(Sqlite.COL_TYPE, statusCache.type.getValue());
|
||||||
values.put(Sqlite.COL_STATUS, mastodonStatusToStringStorage(statusCache.status));
|
if (statusCache.status != null) {
|
||||||
|
values.put(Sqlite.COL_STATUS, mastodonStatusToStringStorage(statusCache.status));
|
||||||
|
}
|
||||||
|
if (statusCache.notification != null) {
|
||||||
|
values.put(Sqlite.COL_STATUS, mastodonNotificationToStringStorage(statusCache.notification));
|
||||||
|
}
|
||||||
values.put(Sqlite.COL_CREATED_AT, Helper.dateToString(new Date()));
|
values.put(Sqlite.COL_CREATED_AT, Helper.dateToString(new Date()));
|
||||||
//Inserts token
|
//Inserts token
|
||||||
try {
|
try {
|
||||||
|
@ -222,7 +262,12 @@ public class StatusCache {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put(Sqlite.COL_USER_ID, statusCache.user_id);
|
values.put(Sqlite.COL_USER_ID, statusCache.user_id);
|
||||||
values.put(Sqlite.COL_STATUS_ID, statusCache.status_id);
|
values.put(Sqlite.COL_STATUS_ID, statusCache.status_id);
|
||||||
values.put(Sqlite.COL_STATUS, mastodonStatusToStringStorage(statusCache.status));
|
if (statusCache.status != null) {
|
||||||
|
values.put(Sqlite.COL_STATUS, mastodonStatusToStringStorage(statusCache.status));
|
||||||
|
}
|
||||||
|
if (statusCache.notification != null) {
|
||||||
|
values.put(Sqlite.COL_STATUS, mastodonNotificationToStringStorage(statusCache.notification));
|
||||||
|
}
|
||||||
values.put(Sqlite.COL_UPDATED_AT, Helper.dateToString(new Date()));
|
values.put(Sqlite.COL_UPDATED_AT, Helper.dateToString(new Date()));
|
||||||
//Inserts token
|
//Inserts token
|
||||||
try {
|
try {
|
||||||
|
@ -298,6 +343,48 @@ public class StatusCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get paginated notifications from db
|
||||||
|
*
|
||||||
|
* @param instance String - instance
|
||||||
|
* @param user_id String - us
|
||||||
|
* @param max_id String - status having max id
|
||||||
|
* @param min_id String - status having min id
|
||||||
|
* @return Statuses
|
||||||
|
* @throws DBException - throws a db exception
|
||||||
|
*/
|
||||||
|
public Notifications getNotifications(List<String> exclude_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 + "' AND " + Sqlite.COL_SLUG + "= '" + Timeline.TimeLineEnum.NOTIFICATION.getValue() + "' ";
|
||||||
|
String limit = String.valueOf(MastodonHelper.statusesPerCall(context));
|
||||||
|
if (min_id != null) {
|
||||||
|
selection += "AND " + Sqlite.COL_STATUS_ID + " > '" + min_id + "' ";
|
||||||
|
order = " ASC";
|
||||||
|
} else if (max_id != null) {
|
||||||
|
selection += "AND " + Sqlite.COL_STATUS_ID + " < '" + max_id + "' ";
|
||||||
|
} else if (since_id != null) {
|
||||||
|
selection += "AND " + Sqlite.COL_STATUS_ID + " > '" + since_id + "' ";
|
||||||
|
limit = null;
|
||||||
|
}
|
||||||
|
if (exclude_type != null && exclude_type.size() > 0) {
|
||||||
|
StringBuilder exclude = new StringBuilder();
|
||||||
|
for (String excluded : exclude_type) {
|
||||||
|
exclude.append("'").append(excluded).append("'").append(",");
|
||||||
|
}
|
||||||
|
exclude = new StringBuilder(exclude.substring(0, exclude.length() - 1));
|
||||||
|
selection += "AND " + Sqlite.COL_SLUG + " NOT IN (" + exclude + ") ";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Cursor c = db.query(Sqlite.TABLE_STATUS_CACHE, null, selection, null, null, null, Sqlite.COL_STATUS_ID + order, limit);
|
||||||
|
return createNotificationReply(cursorToListOfNotifications(c));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -403,6 +490,52 @@ public class StatusCache {
|
||||||
return statusList;
|
return statusList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a cursor to list of notifications
|
||||||
|
*
|
||||||
|
* @param c Cursor
|
||||||
|
* @return List<Status>
|
||||||
|
*/
|
||||||
|
private List<Notification> cursorToListOfNotifications(Cursor c) {
|
||||||
|
//No element found
|
||||||
|
if (c.getCount() == 0) {
|
||||||
|
c.close();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<Notification> notificationList = new ArrayList<>();
|
||||||
|
while (c.moveToNext()) {
|
||||||
|
Notification notification = convertCursorToNotification(c);
|
||||||
|
notificationList.add(notification);
|
||||||
|
}
|
||||||
|
//Close the cursor
|
||||||
|
c.close();
|
||||||
|
return notificationList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a reply from db in the same way than API call
|
||||||
|
*
|
||||||
|
* @param notificationList List<Notification>
|
||||||
|
* @return Notifications (with pagination)
|
||||||
|
*/
|
||||||
|
private Notifications createNotificationReply(List<Notification> notificationList) {
|
||||||
|
Notifications notifications = new Notifications();
|
||||||
|
notifications.notifications = notificationList;
|
||||||
|
Pagination pagination = new Pagination();
|
||||||
|
if (notificationList != null && notificationList.size() > 0) {
|
||||||
|
//Status list is inverted, it happens for min_id due to ASC ordering
|
||||||
|
if (notificationList.get(0).id.compareTo(notificationList.get(notificationList.size() - 1).id) < 0) {
|
||||||
|
Collections.reverse(notificationList);
|
||||||
|
notifications.notifications = notificationList;
|
||||||
|
}
|
||||||
|
pagination.max_id = notificationList.get(0).id;
|
||||||
|
pagination.min_id = notificationList.get(notificationList.size() - 1).id;
|
||||||
|
}
|
||||||
|
notifications.pagination = pagination;
|
||||||
|
return notifications;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a reply from db in the same way than API call
|
* Create a reply from db in the same way than API call
|
||||||
*
|
*
|
||||||
|
@ -437,6 +570,18 @@ public class StatusCache {
|
||||||
return restoreStatusFromString(serializedStatus);
|
return restoreStatusFromString(serializedStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read cursor and hydrate without closing it
|
||||||
|
*
|
||||||
|
* @param c - Cursor
|
||||||
|
* @return Notification
|
||||||
|
*/
|
||||||
|
private Notification convertCursorToNotification(Cursor c) {
|
||||||
|
String serializedNotification = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_STATUS));
|
||||||
|
return restoreNotificationFromString(serializedNotification);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public enum order {
|
public enum order {
|
||||||
@SerializedName("ASC")
|
@SerializedName("ASC")
|
||||||
ASC("ASC"),
|
ASC("ASC"),
|
||||||
|
|
|
@ -62,7 +62,7 @@ public class NotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
|
||||||
private final int TYPE_POLL = 5;
|
private final int TYPE_POLL = 5;
|
||||||
private final int TYPE_STATUS = 6;
|
private final int TYPE_STATUS = 6;
|
||||||
private final int TYPE_REACTION = 8;
|
private final int TYPE_REACTION = 8;
|
||||||
public StatusAdapter.FetchMoreCallBack fetchMoreCallBack;
|
public FetchMoreCallBack fetchMoreCallBack;
|
||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
public NotificationAdapter(List<Notification> notificationList) {
|
public NotificationAdapter(List<Notification> notificationList) {
|
||||||
|
@ -114,7 +114,6 @@ public class NotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
|
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
|
||||||
Notification notification = notificationList.get(position);
|
Notification notification = notificationList.get(position);
|
||||||
|
@ -160,14 +159,29 @@ public class NotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
|
||||||
holderFollow.binding.layoutFetchMore.fetchMoreContainer.setVisibility(View.VISIBLE);
|
holderFollow.binding.layoutFetchMore.fetchMoreContainer.setVisibility(View.VISIBLE);
|
||||||
holderFollow.binding.layoutFetchMore.fetchMoreMin.setOnClickListener(v -> {
|
holderFollow.binding.layoutFetchMore.fetchMoreMin.setOnClickListener(v -> {
|
||||||
notification.isFetchMore = false;
|
notification.isFetchMore = false;
|
||||||
notifyItemChanged(position);
|
if (holderFollow.getBindingAdapterPosition() < notificationList.size() - 1) {
|
||||||
fetchMoreCallBack.onClickMinId(notification.id);
|
String fromId;
|
||||||
|
if (notification.positionFetchMore == Notification.PositionFetchMore.TOP) {
|
||||||
|
fromId = notificationList.get(position + 1).id;
|
||||||
|
} else {
|
||||||
|
fromId = notification.id;
|
||||||
|
}
|
||||||
|
fetchMoreCallBack.onClickMinId(fromId, notification);
|
||||||
|
notifyItemChanged(position);
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
holderFollow.binding.layoutFetchMore.fetchMoreMax.setOnClickListener(v -> {
|
holderFollow.binding.layoutFetchMore.fetchMoreMax.setOnClickListener(v -> {
|
||||||
//We hide the button
|
//We hide the button
|
||||||
notification.isFetchMore = false;
|
notification.isFetchMore = false;
|
||||||
|
String fromId;
|
||||||
|
if (notification.positionFetchMore == Notification.PositionFetchMore.TOP) {
|
||||||
|
fromId = notificationList.get(position).id;
|
||||||
|
} else {
|
||||||
|
fromId = notificationList.get(position - 1).id;
|
||||||
|
}
|
||||||
notifyItemChanged(position);
|
notifyItemChanged(position);
|
||||||
fetchMoreCallBack.onClickMaxId(notification.id);
|
fetchMoreCallBack.onClickMaxId(fromId, notification);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
holderFollow.binding.layoutFetchMore.fetchMoreContainer.setVisibility(View.GONE);
|
holderFollow.binding.layoutFetchMore.fetchMoreContainer.setVisibility(View.GONE);
|
||||||
|
@ -191,7 +205,7 @@ public class NotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
|
||||||
}
|
}
|
||||||
StatusesVM statusesVM = new ViewModelProvider((ViewModelStoreOwner) context).get(StatusesVM.class);
|
StatusesVM statusesVM = new ViewModelProvider((ViewModelStoreOwner) context).get(StatusesVM.class);
|
||||||
SearchVM searchVM = new ViewModelProvider((ViewModelStoreOwner) context).get(SearchVM.class);
|
SearchVM searchVM = new ViewModelProvider((ViewModelStoreOwner) context).get(SearchVM.class);
|
||||||
statusManagement(context, statusesVM, searchVM, holderStatus, this, null, notification.status, Timeline.TimeLineEnum.NOTIFICATION, false, true, fetchMoreCallBack);
|
statusManagement(context, statusesVM, searchVM, holderStatus, this, null, notification.status, Timeline.TimeLineEnum.NOTIFICATION, false, true, null);
|
||||||
holderStatus.bindingNotification.status.dateShort.setText(Helper.dateDiff(context, notification.created_at));
|
holderStatus.bindingNotification.status.dateShort.setText(Helper.dateDiff(context, notification.created_at));
|
||||||
holderStatus.bindingNotification.containerTransparent.setAlpha(.3f);
|
holderStatus.bindingNotification.containerTransparent.setAlpha(.3f);
|
||||||
if (getItemViewType(position) == TYPE_MENTION || getItemViewType(position) == TYPE_STATUS || getItemViewType(position) == TYPE_REACTION) {
|
if (getItemViewType(position) == TYPE_MENTION || getItemViewType(position) == TYPE_STATUS || getItemViewType(position) == TYPE_REACTION) {
|
||||||
|
@ -300,6 +314,12 @@ public class NotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface FetchMoreCallBack {
|
||||||
|
void onClickMinId(String min_id, Notification notificationToUpdate);
|
||||||
|
|
||||||
|
void onClickMaxId(String max_id, Notification notificationToUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public long getItemId(int position) {
|
public long getItemId(int position) {
|
||||||
return position;
|
return position;
|
||||||
|
|
|
@ -1878,21 +1878,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
||||||
} else {
|
} else {
|
||||||
fromId = status.id;
|
fromId = status.id;
|
||||||
}
|
}
|
||||||
fetchMoreCallBack.onClickMinId(fromId);
|
fetchMoreCallBack.onClickMinId(fromId, status);
|
||||||
if (!remote) {
|
|
||||||
new Thread(() -> {
|
|
||||||
StatusCache statusCache = new StatusCache();
|
|
||||||
statusCache.instance = BaseMainActivity.currentInstance;
|
|
||||||
statusCache.user_id = BaseMainActivity.currentUserID;
|
|
||||||
statusCache.status = status;
|
|
||||||
statusCache.status_id = status.id;
|
|
||||||
try {
|
|
||||||
new StatusCache(context).updateIfExists(statusCache);
|
|
||||||
} catch (DBException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
holder.binding.layoutFetchMore.fetchMoreMax.setOnClickListener(v -> {
|
holder.binding.layoutFetchMore.fetchMoreMax.setOnClickListener(v -> {
|
||||||
|
@ -1904,22 +1890,8 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
||||||
} else {
|
} else {
|
||||||
fromId = statusList.get(holder.getBindingAdapterPosition() - 1).id;
|
fromId = statusList.get(holder.getBindingAdapterPosition() - 1).id;
|
||||||
}
|
}
|
||||||
fetchMoreCallBack.onClickMaxId(fromId);
|
fetchMoreCallBack.onClickMaxId(fromId, status);
|
||||||
adapter.notifyItemChanged(holder.getBindingAdapterPosition());
|
adapter.notifyItemChanged(holder.getBindingAdapterPosition());
|
||||||
if (!remote) {
|
|
||||||
new Thread(() -> {
|
|
||||||
StatusCache statusCache = new StatusCache();
|
|
||||||
statusCache.instance = BaseMainActivity.currentInstance;
|
|
||||||
statusCache.user_id = BaseMainActivity.currentUserID;
|
|
||||||
statusCache.status = status;
|
|
||||||
statusCache.status_id = status.id;
|
|
||||||
try {
|
|
||||||
new StatusCache(context).updateIfExists(statusCache);
|
|
||||||
} catch (DBException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
holder.binding.layoutFetchMore.fetchMoreContainer.setVisibility(View.GONE);
|
holder.binding.layoutFetchMore.fetchMoreContainer.setVisibility(View.GONE);
|
||||||
|
@ -2073,9 +2045,9 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface FetchMoreCallBack {
|
public interface FetchMoreCallBack {
|
||||||
void onClickMinId(String min_id);
|
void onClickMinId(String min_id, Status statusToUpdate);
|
||||||
|
|
||||||
void onClickMaxId(String max_id);
|
void onClickMaxId(String max_id, Status statusToUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class StatusViewHolder extends RecyclerView.ViewHolder {
|
public static class StatusViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
|
|
@ -43,16 +43,17 @@ import app.fedilab.android.R;
|
||||||
import app.fedilab.android.client.entities.api.Notification;
|
import app.fedilab.android.client.entities.api.Notification;
|
||||||
import app.fedilab.android.client.entities.api.Notifications;
|
import app.fedilab.android.client.entities.api.Notifications;
|
||||||
import app.fedilab.android.client.entities.api.Status;
|
import app.fedilab.android.client.entities.api.Status;
|
||||||
|
import app.fedilab.android.client.entities.app.Timeline;
|
||||||
import app.fedilab.android.databinding.FragmentPaginationBinding;
|
import app.fedilab.android.databinding.FragmentPaginationBinding;
|
||||||
import app.fedilab.android.helper.Helper;
|
import app.fedilab.android.helper.Helper;
|
||||||
import app.fedilab.android.helper.MastodonHelper;
|
import app.fedilab.android.helper.MastodonHelper;
|
||||||
import app.fedilab.android.helper.ThemeHelper;
|
import app.fedilab.android.helper.ThemeHelper;
|
||||||
import app.fedilab.android.ui.drawer.NotificationAdapter;
|
import app.fedilab.android.ui.drawer.NotificationAdapter;
|
||||||
import app.fedilab.android.ui.drawer.StatusAdapter;
|
|
||||||
import app.fedilab.android.viewmodel.mastodon.NotificationsVM;
|
import app.fedilab.android.viewmodel.mastodon.NotificationsVM;
|
||||||
|
import app.fedilab.android.viewmodel.mastodon.TimelinesVM;
|
||||||
|
|
||||||
|
|
||||||
public class FragmentMastodonNotification extends Fragment implements StatusAdapter.FetchMoreCallBack {
|
public class FragmentMastodonNotification extends Fragment implements NotificationAdapter.FetchMoreCallBack {
|
||||||
|
|
||||||
|
|
||||||
private static final int NOTIFICATION_PRESENT = -1;
|
private static final int NOTIFICATION_PRESENT = -1;
|
||||||
|
@ -270,26 +271,108 @@ public class FragmentMastodonNotification extends Fragment implements StatusAdap
|
||||||
* @param direction - DIRECTION null if first call, then is set to TOP or BOTTOM depending of scroll
|
* @param direction - DIRECTION null if first call, then is set to TOP or BOTTOM depending of scroll
|
||||||
*/
|
*/
|
||||||
private void route(FragmentMastodonTimeline.DIRECTION direction, boolean fetchingMissing) {
|
private void route(FragmentMastodonTimeline.DIRECTION direction, boolean fetchingMissing) {
|
||||||
|
route(direction, fetchingMissing, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Router for timelines
|
||||||
|
*
|
||||||
|
* @param direction - DIRECTION null if first call, then is set to TOP or BOTTOM depending of scroll
|
||||||
|
*/
|
||||||
|
private void route(FragmentMastodonTimeline.DIRECTION direction, boolean fetchingMissing, Notification notificationToUpdate) {
|
||||||
if (binding == null || !isAdded() || getActivity() == null) {
|
if (binding == null || !isAdded() || getActivity() == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!isAdded()) {
|
if (!isAdded()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (direction == null) {
|
|
||||||
notificationsVM.getNotifications(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, null, null, null, MastodonHelper.statusesPerCall(requireActivity()), excludeType, null)
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
|
||||||
.observe(getViewLifecycleOwner(), this::initializeNotificationView);
|
boolean useCache = sharedpreferences.getBoolean(getString(R.string.SET_USE_CACHE), true);
|
||||||
|
|
||||||
|
TimelinesVM.TimelineParams timelineParams = new TimelinesVM.TimelineParams(Timeline.TimeLineEnum.NOTIFICATION, direction, null);
|
||||||
|
timelineParams.limit = MastodonHelper.notificationsPerCall(requireActivity());
|
||||||
|
if (direction == FragmentMastodonTimeline.DIRECTION.REFRESH || direction == FragmentMastodonTimeline.DIRECTION.SCROLL_TOP) {
|
||||||
|
timelineParams.maxId = null;
|
||||||
|
timelineParams.minId = null;
|
||||||
} else if (direction == FragmentMastodonTimeline.DIRECTION.BOTTOM) {
|
} else if (direction == FragmentMastodonTimeline.DIRECTION.BOTTOM) {
|
||||||
notificationsVM.getNotifications(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, fetchingMissing ? max_id_fetch_more : max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()), excludeType, null)
|
timelineParams.maxId = fetchingMissing ? max_id_fetch_more : max_id;
|
||||||
.observe(getViewLifecycleOwner(), notificationsBottom -> dealWithPagination(notificationsBottom, FragmentMastodonTimeline.DIRECTION.BOTTOM, fetchingMissing));
|
timelineParams.minId = null;
|
||||||
} else if (direction == FragmentMastodonTimeline.DIRECTION.TOP) {
|
} else if (direction == FragmentMastodonTimeline.DIRECTION.TOP) {
|
||||||
notificationsVM.getNotifications(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity()), excludeType, null)
|
timelineParams.minId = fetchingMissing ? min_id_fetch_more : min_id;
|
||||||
.observe(getViewLifecycleOwner(), notificationsTop -> dealWithPagination(notificationsTop, FragmentMastodonTimeline.DIRECTION.TOP, fetchingMissing));
|
timelineParams.maxId = null;
|
||||||
|
} else {
|
||||||
|
timelineParams.maxId = max_id;
|
||||||
|
}
|
||||||
|
timelineParams.excludeType = excludeType;
|
||||||
|
timelineParams.fetchingMissing = fetchingMissing;
|
||||||
|
|
||||||
|
if (useCache) {
|
||||||
|
getCachedNotifications(direction, fetchingMissing, timelineParams);
|
||||||
|
} else {
|
||||||
|
getLiveNotifications(direction, fetchingMissing, timelineParams, notificationToUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getCachedNotifications(FragmentMastodonTimeline.DIRECTION direction, boolean fetchingMissing, TimelinesVM.TimelineParams timelineParams) {
|
||||||
|
|
||||||
|
if (direction == null) {
|
||||||
|
notificationsVM.getNotificationCache(notificationList, timelineParams)
|
||||||
|
.observe(getViewLifecycleOwner(), notificationsCached -> {
|
||||||
|
if (notificationsCached == null || notificationsCached.notifications == null || notificationsCached.notifications.size() == 0) {
|
||||||
|
getLiveNotifications(null, fetchingMissing, timelineParams, null);
|
||||||
|
} else {
|
||||||
|
initializeNotificationView(notificationsCached);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (direction == FragmentMastodonTimeline.DIRECTION.BOTTOM) {
|
||||||
|
notificationsVM.getNotificationCache(notificationList, timelineParams)
|
||||||
|
.observe(getViewLifecycleOwner(), notificationsBottom -> {
|
||||||
|
if (notificationsBottom == null || notificationsBottom.notifications == null || notificationsBottom.notifications.size() == 0) {
|
||||||
|
getLiveNotifications(FragmentMastodonTimeline.DIRECTION.BOTTOM, fetchingMissing, timelineParams, null);
|
||||||
|
} else {
|
||||||
|
dealWithPagination(notificationsBottom, FragmentMastodonTimeline.DIRECTION.BOTTOM, fetchingMissing, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
} else if (direction == FragmentMastodonTimeline.DIRECTION.TOP) {
|
||||||
|
notificationsVM.getNotificationCache(notificationList, timelineParams)
|
||||||
|
.observe(getViewLifecycleOwner(), notificationsTop -> {
|
||||||
|
if (notificationsTop == null || notificationsTop.notifications == null || notificationsTop.notifications.size() == 0) {
|
||||||
|
getLiveNotifications(FragmentMastodonTimeline.DIRECTION.BOTTOM, fetchingMissing, timelineParams, null);
|
||||||
|
} else {
|
||||||
|
dealWithPagination(notificationsTop, FragmentMastodonTimeline.DIRECTION.TOP, fetchingMissing, null);
|
||||||
|
}
|
||||||
|
});
|
||||||
} else if (direction == FragmentMastodonTimeline.DIRECTION.REFRESH) {
|
} else if (direction == FragmentMastodonTimeline.DIRECTION.REFRESH) {
|
||||||
notificationsVM.getNotifications(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, null, null, null, MastodonHelper.statusesPerCall(requireActivity()), excludeType, null)
|
notificationsVM.getNotifications(notificationList, timelineParams)
|
||||||
.observe(getViewLifecycleOwner(), notificationsRefresh -> {
|
.observe(getViewLifecycleOwner(), notificationsRefresh -> {
|
||||||
if (notificationAdapter != null) {
|
if (notificationAdapter != null) {
|
||||||
dealWithPagination(notificationsRefresh, FragmentMastodonTimeline.DIRECTION.REFRESH, true);
|
dealWithPagination(notificationsRefresh, FragmentMastodonTimeline.DIRECTION.REFRESH, true, null);
|
||||||
|
} else {
|
||||||
|
initializeNotificationView(notificationsRefresh);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getLiveNotifications(FragmentMastodonTimeline.DIRECTION direction, boolean fetchingMissing, TimelinesVM.TimelineParams timelineParams, Notification notificationToUpdate) {
|
||||||
|
if (direction == null) {
|
||||||
|
notificationsVM.getNotifications(notificationList, timelineParams)
|
||||||
|
.observe(getViewLifecycleOwner(), this::initializeNotificationView);
|
||||||
|
} else if (direction == FragmentMastodonTimeline.DIRECTION.BOTTOM) {
|
||||||
|
notificationsVM.getNotifications(notificationList, timelineParams)
|
||||||
|
.observe(getViewLifecycleOwner(), notificationsBottom -> dealWithPagination(notificationsBottom, FragmentMastodonTimeline.DIRECTION.BOTTOM, fetchingMissing, notificationToUpdate));
|
||||||
|
} else if (direction == FragmentMastodonTimeline.DIRECTION.TOP) {
|
||||||
|
notificationsVM.getNotifications(notificationList, timelineParams)
|
||||||
|
.observe(getViewLifecycleOwner(), notificationsTop -> dealWithPagination(notificationsTop, FragmentMastodonTimeline.DIRECTION.TOP, fetchingMissing, notificationToUpdate));
|
||||||
|
} else if (direction == FragmentMastodonTimeline.DIRECTION.REFRESH) {
|
||||||
|
notificationsVM.getNotifications(notificationList, timelineParams)
|
||||||
|
.observe(getViewLifecycleOwner(), notificationsRefresh -> {
|
||||||
|
if (notificationAdapter != null) {
|
||||||
|
dealWithPagination(notificationsRefresh, FragmentMastodonTimeline.DIRECTION.REFRESH, true, notificationToUpdate);
|
||||||
} else {
|
} else {
|
||||||
initializeNotificationView(notificationsRefresh);
|
initializeNotificationView(notificationsRefresh);
|
||||||
}
|
}
|
||||||
|
@ -333,7 +416,7 @@ public class FragmentMastodonNotification extends Fragment implements StatusAdap
|
||||||
*
|
*
|
||||||
* @param fetched_notifications Notifications
|
* @param fetched_notifications Notifications
|
||||||
*/
|
*/
|
||||||
private synchronized void dealWithPagination(Notifications fetched_notifications, FragmentMastodonTimeline.DIRECTION direction, boolean fetchingMissing) {
|
private synchronized void dealWithPagination(Notifications fetched_notifications, FragmentMastodonTimeline.DIRECTION direction, boolean fetchingMissing, Notification notificationToUpdate) {
|
||||||
if (binding == null || !isAdded() || getActivity() == null) {
|
if (binding == null || !isAdded() || getActivity() == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -456,17 +539,17 @@ public class FragmentMastodonNotification extends Fragment implements StatusAdap
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClickMinId(String min_id) {
|
public void onClickMinId(String min_id, Notification notificationToUpdate) {
|
||||||
//Fetch more has been pressed
|
//Fetch more has been pressed
|
||||||
min_id_fetch_more = min_id;
|
min_id_fetch_more = min_id;
|
||||||
route(FragmentMastodonTimeline.DIRECTION.TOP, true);
|
route(FragmentMastodonTimeline.DIRECTION.TOP, true, notificationToUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClickMaxId(String max_id) {
|
public void onClickMaxId(String max_id, Notification notificationToUpdate) {
|
||||||
//Fetch more has been pressed
|
//Fetch more has been pressed
|
||||||
max_id_fetch_more = max_id;
|
max_id_fetch_more = max_id;
|
||||||
route(FragmentMastodonTimeline.DIRECTION.BOTTOM, true);
|
route(FragmentMastodonTimeline.DIRECTION.BOTTOM, true, notificationToUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum NotificationTypeEnum {
|
public enum NotificationTypeEnum {
|
||||||
|
|
|
@ -49,9 +49,11 @@ import app.fedilab.android.client.entities.api.Status;
|
||||||
import app.fedilab.android.client.entities.api.Statuses;
|
import app.fedilab.android.client.entities.api.Statuses;
|
||||||
import app.fedilab.android.client.entities.app.PinnedTimeline;
|
import app.fedilab.android.client.entities.app.PinnedTimeline;
|
||||||
import app.fedilab.android.client.entities.app.RemoteInstance;
|
import app.fedilab.android.client.entities.app.RemoteInstance;
|
||||||
|
import app.fedilab.android.client.entities.app.StatusCache;
|
||||||
import app.fedilab.android.client.entities.app.TagTimeline;
|
import app.fedilab.android.client.entities.app.TagTimeline;
|
||||||
import app.fedilab.android.client.entities.app.Timeline;
|
import app.fedilab.android.client.entities.app.Timeline;
|
||||||
import app.fedilab.android.databinding.FragmentPaginationBinding;
|
import app.fedilab.android.databinding.FragmentPaginationBinding;
|
||||||
|
import app.fedilab.android.exception.DBException;
|
||||||
import app.fedilab.android.helper.Helper;
|
import app.fedilab.android.helper.Helper;
|
||||||
import app.fedilab.android.helper.MastodonHelper;
|
import app.fedilab.android.helper.MastodonHelper;
|
||||||
import app.fedilab.android.helper.ThemeHelper;
|
import app.fedilab.android.helper.ThemeHelper;
|
||||||
|
@ -378,7 +380,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
||||||
*
|
*
|
||||||
* @param fetched_statuses Statuses
|
* @param fetched_statuses Statuses
|
||||||
*/
|
*/
|
||||||
private synchronized void dealWithPagination(Statuses fetched_statuses, DIRECTION direction, boolean fetchingMissing) {
|
private synchronized void dealWithPagination(Statuses fetched_statuses, DIRECTION direction, boolean fetchingMissing, Status statusToUpdate) {
|
||||||
if (binding == null || !isAdded() || getActivity() == null) {
|
if (binding == null || !isAdded() || getActivity() == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -386,6 +388,23 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
||||||
binding.loadingNextElements.setVisibility(View.GONE);
|
binding.loadingNextElements.setVisibility(View.GONE);
|
||||||
flagLoading = false;
|
flagLoading = false;
|
||||||
if (timelineStatuses != 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) {
|
||||||
|
try {
|
||||||
|
new Thread(() -> {
|
||||||
|
StatusCache statusCache = new StatusCache();
|
||||||
|
statusCache.instance = BaseMainActivity.currentInstance;
|
||||||
|
statusCache.user_id = BaseMainActivity.currentUserID;
|
||||||
|
statusCache.status = statusToUpdate;
|
||||||
|
if (statusToUpdate != null) {
|
||||||
|
statusCache.status_id = statusToUpdate.id;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
new StatusCache(requireActivity()).updateIfExists(statusCache);
|
||||||
|
} catch (DBException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
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) {
|
||||||
|
@ -425,6 +444,15 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update view and pagination when scrolling down
|
||||||
|
*
|
||||||
|
* @param fetched_statuses Statuses
|
||||||
|
*/
|
||||||
|
private synchronized void dealWithPagination(Statuses fetched_statuses, DIRECTION direction, boolean fetchingMissing) {
|
||||||
|
dealWithPagination(fetched_statuses, direction, fetchingMissing, null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the timeline with received statuses
|
* Update the timeline with received statuses
|
||||||
*
|
*
|
||||||
|
@ -521,7 +549,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
||||||
*
|
*
|
||||||
* @param direction - DIRECTION null if first call, then is set to TOP or BOTTOM depending of scroll
|
* @param direction - DIRECTION null if first call, then is set to TOP or BOTTOM depending of scroll
|
||||||
*/
|
*/
|
||||||
private void routeCommon(DIRECTION direction, boolean fetchingMissing) {
|
private void routeCommon(DIRECTION direction, boolean fetchingMissing, Status status) {
|
||||||
if (binding == null || getActivity() == null || !isAdded()) {
|
if (binding == null || getActivity() == null || !isAdded()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -575,7 +603,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
||||||
if (useCache) {
|
if (useCache) {
|
||||||
getCachedStatus(direction, fetchingMissing, timelineParams);
|
getCachedStatus(direction, fetchingMissing, timelineParams);
|
||||||
} else {
|
} else {
|
||||||
getLiveStatus(direction, fetchingMissing, timelineParams);
|
getLiveStatus(direction, fetchingMissing, timelineParams, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -585,7 +613,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
||||||
timelinesVM.getTimelineCache(timelineStatuses, 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, null);
|
||||||
} else {
|
} else {
|
||||||
initializeStatusesCommonView(statusesCached);
|
initializeStatusesCommonView(statusesCached);
|
||||||
}
|
}
|
||||||
|
@ -594,7 +622,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
||||||
timelinesVM.getTimelineCache(timelineStatuses, 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, null);
|
||||||
} else {
|
} else {
|
||||||
dealWithPagination(statusesCachedBottom, DIRECTION.BOTTOM, fetchingMissing);
|
dealWithPagination(statusesCachedBottom, DIRECTION.BOTTOM, fetchingMissing);
|
||||||
}
|
}
|
||||||
|
@ -603,7 +631,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
||||||
timelinesVM.getTimelineCache(timelineStatuses, 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, null);
|
||||||
} else {
|
} else {
|
||||||
dealWithPagination(statusesCachedTop, DIRECTION.TOP, fetchingMissing);
|
dealWithPagination(statusesCachedTop, DIRECTION.TOP, fetchingMissing);
|
||||||
}
|
}
|
||||||
|
@ -625,21 +653,22 @@ 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, Status status) {
|
||||||
if (direction == null) {
|
if (direction == null) {
|
||||||
timelinesVM.getTimeline(timelineStatuses, 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(timelineStatuses, timelineParams)
|
timelinesVM.getTimeline(timelineStatuses, timelineParams)
|
||||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, fetchingMissing));
|
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, fetchingMissing, status));
|
||||||
} else if (direction == DIRECTION.TOP) {
|
} else if (direction == DIRECTION.TOP) {
|
||||||
timelinesVM.getTimeline(timelineStatuses, timelineParams)
|
timelinesVM.getTimeline(timelineStatuses, timelineParams)
|
||||||
.observe(getViewLifecycleOwner(), statusesTop -> dealWithPagination(statusesTop, DIRECTION.TOP, fetchingMissing));
|
.observe(getViewLifecycleOwner(), statusesTop -> dealWithPagination(statusesTop, DIRECTION.TOP, fetchingMissing, status));
|
||||||
} else if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP) {
|
} else if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP) {
|
||||||
timelinesVM.getTimeline(timelineStatuses, 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, status);
|
||||||
} else {
|
} else {
|
||||||
initializeStatusesCommonView(statusesRefresh);
|
initializeStatusesCommonView(statusesRefresh);
|
||||||
}
|
}
|
||||||
|
@ -647,23 +676,33 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Router for timelines
|
* Router for timelines
|
||||||
*
|
*
|
||||||
* @param direction - DIRECTION null if first call, then is set to TOP or BOTTOM depending of scroll
|
* @param direction - DIRECTION null if first call, then is set to TOP or BOTTOM depending of scroll
|
||||||
*/
|
*/
|
||||||
private void route(DIRECTION direction, boolean fetchingMissing) {
|
private void route(DIRECTION direction, boolean fetchingMissing) {
|
||||||
|
route(direction, fetchingMissing, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Router for timelines
|
||||||
|
*
|
||||||
|
* @param direction - DIRECTION null if first call, then is set to TOP or BOTTOM depending of scroll
|
||||||
|
*/
|
||||||
|
private void route(DIRECTION direction, boolean fetchingMissing, Status statusToUpdate) {
|
||||||
if (binding == null || getActivity() == null || !isAdded()) {
|
if (binding == null || getActivity() == null || !isAdded()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// --- HOME TIMELINE ---
|
// --- HOME TIMELINE ---
|
||||||
if (timelineType == Timeline.TimeLineEnum.HOME) {
|
if (timelineType == Timeline.TimeLineEnum.HOME) {
|
||||||
//for more visibility it's done through loadHomeStrategy method
|
//for more visibility it's done through loadHomeStrategy method
|
||||||
routeCommon(direction, fetchingMissing);
|
routeCommon(direction, fetchingMissing, statusToUpdate);
|
||||||
} else if (timelineType == Timeline.TimeLineEnum.LOCAL) { //LOCAL TIMELINE
|
} else if (timelineType == Timeline.TimeLineEnum.LOCAL) { //LOCAL TIMELINE
|
||||||
routeCommon(direction, fetchingMissing);
|
routeCommon(direction, fetchingMissing, statusToUpdate);
|
||||||
} else if (timelineType == Timeline.TimeLineEnum.PUBLIC) { //PUBLIC TIMELINE
|
} else if (timelineType == Timeline.TimeLineEnum.PUBLIC) { //PUBLIC TIMELINE
|
||||||
routeCommon(direction, fetchingMissing);
|
routeCommon(direction, fetchingMissing, statusToUpdate);
|
||||||
} else if (timelineType == Timeline.TimeLineEnum.REMOTE) { //REMOTE TIMELINE
|
} else if (timelineType == Timeline.TimeLineEnum.REMOTE) { //REMOTE TIMELINE
|
||||||
//NITTER TIMELINES
|
//NITTER TIMELINES
|
||||||
if (pinnedTimeline != null && pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.NITTER) {
|
if (pinnedTimeline != null && pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.NITTER) {
|
||||||
|
@ -729,12 +768,12 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else { //Other remote timelines
|
} else { //Other remote timelines
|
||||||
routeCommon(direction, fetchingMissing);
|
routeCommon(direction, fetchingMissing, statusToUpdate);
|
||||||
}
|
}
|
||||||
} else if (timelineType == Timeline.TimeLineEnum.LIST) { //LIST TIMELINE
|
} else if (timelineType == Timeline.TimeLineEnum.LIST) { //LIST TIMELINE
|
||||||
routeCommon(direction, fetchingMissing);
|
routeCommon(direction, fetchingMissing, statusToUpdate);
|
||||||
} else if (timelineType == Timeline.TimeLineEnum.TAG || timelineType == Timeline.TimeLineEnum.ART) { //TAG TIMELINE
|
} else if (timelineType == Timeline.TimeLineEnum.TAG || timelineType == Timeline.TimeLineEnum.ART) { //TAG TIMELINE
|
||||||
routeCommon(direction, fetchingMissing);
|
routeCommon(direction, fetchingMissing, statusToUpdate);
|
||||||
} else if (timelineType == Timeline.TimeLineEnum.ACCOUNT_TIMELINE) { //PROFILE TIMELINES
|
} else if (timelineType == Timeline.TimeLineEnum.ACCOUNT_TIMELINE) { //PROFILE TIMELINES
|
||||||
if (direction == null) {
|
if (direction == null) {
|
||||||
if (show_pinned) {
|
if (show_pinned) {
|
||||||
|
@ -821,10 +860,11 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refresh status in list
|
* Refresh status in list
|
||||||
*/
|
*/
|
||||||
|
@ -835,16 +875,16 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClickMinId(String min_id) {
|
public void onClickMinId(String min_id, Status statusToUpdate) {
|
||||||
//Fetch more has been pressed
|
//Fetch more has been pressed
|
||||||
min_id_fetch_more = min_id;
|
min_id_fetch_more = min_id;
|
||||||
route(DIRECTION.TOP, true);
|
route(DIRECTION.TOP, true, statusToUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClickMaxId(String max_id) {
|
public void onClickMaxId(String max_id, Status statusToUpdate) {
|
||||||
max_id_fetch_more = max_id;
|
max_id_fetch_more = max_id;
|
||||||
route(DIRECTION.BOTTOM, true);
|
route(DIRECTION.BOTTOM, true, statusToUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum DIRECTION {
|
public enum DIRECTION {
|
||||||
|
|
|
@ -23,19 +23,21 @@ import androidx.lifecycle.AndroidViewModel;
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.GsonBuilder;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import app.fedilab.android.client.endpoints.MastodonNotificationsService;
|
import app.fedilab.android.client.endpoints.MastodonNotificationsService;
|
||||||
import app.fedilab.android.client.entities.api.Notification;
|
import app.fedilab.android.client.entities.api.Notification;
|
||||||
import app.fedilab.android.client.entities.api.Notifications;
|
import app.fedilab.android.client.entities.api.Notifications;
|
||||||
|
import app.fedilab.android.client.entities.api.Pagination;
|
||||||
import app.fedilab.android.client.entities.api.PushSubscription;
|
import app.fedilab.android.client.entities.api.PushSubscription;
|
||||||
|
import app.fedilab.android.client.entities.app.StatusCache;
|
||||||
|
import app.fedilab.android.client.entities.app.Timeline;
|
||||||
|
import app.fedilab.android.exception.DBException;
|
||||||
import app.fedilab.android.helper.Helper;
|
import app.fedilab.android.helper.Helper;
|
||||||
import app.fedilab.android.helper.MastodonHelper;
|
import app.fedilab.android.helper.MastodonHelper;
|
||||||
import app.fedilab.android.helper.TimelineHelper;
|
import app.fedilab.android.helper.TimelineHelper;
|
||||||
|
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.Response;
|
import retrofit2.Response;
|
||||||
|
@ -62,7 +64,6 @@ public class NotificationsVM extends AndroidViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
private MastodonNotificationsService init(@NonNull String instance) {
|
private MastodonNotificationsService init(@NonNull String instance) {
|
||||||
Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").create();
|
|
||||||
Retrofit retrofit = new Retrofit.Builder()
|
Retrofit retrofit = new Retrofit.Builder()
|
||||||
.baseUrl("https://" + instance + "/api/v1/")
|
.baseUrl("https://" + instance + "/api/v1/")
|
||||||
.addConverterFactory(GsonConverterFactory.create(Helper.getDateBuilder()))
|
.addConverterFactory(GsonConverterFactory.create(Helper.getDateBuilder()))
|
||||||
|
@ -71,38 +72,66 @@ public class NotificationsVM extends AndroidViewModel {
|
||||||
return retrofit.create(MastodonNotificationsService.class);
|
return retrofit.create(MastodonNotificationsService.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void addFetchMoreNotifications(List<Notification> notificationList, List<Notification> timelineNotifications, TimelinesVM.TimelineParams timelineParams) throws DBException {
|
||||||
|
if (notificationList != null && notificationList.size() > 0 && timelineNotifications != null && timelineNotifications.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 (notificationList.get(notificationList.size() - 1).id.compareToIgnoreCase(timelineNotifications.get(0).id) > 0) {
|
||||||
|
notificationList.get(notificationList.size() - 1).isFetchMore = true;
|
||||||
|
notificationList.get(notificationList.size() - 1).positionFetchMore = Notification.PositionFetchMore.TOP;
|
||||||
|
}
|
||||||
|
} else if (timelineParams.direction == FragmentMastodonTimeline.DIRECTION.TOP && timelineParams.fetchingMissing) {
|
||||||
|
if (!timelineNotifications.contains(notificationList.get(0))) {
|
||||||
|
notificationList.get(0).isFetchMore = true;
|
||||||
|
notificationList.get(0).positionFetchMore = Notification.PositionFetchMore.BOTTOM;
|
||||||
|
}
|
||||||
|
} else if (timelineParams.direction == FragmentMastodonTimeline.DIRECTION.BOTTOM && timelineParams.fetchingMissing) {
|
||||||
|
if (!timelineNotifications.contains(notificationList.get(notificationList.size() - 1))) {
|
||||||
|
notificationList.get(notificationList.size() - 1).isFetchMore = true;
|
||||||
|
notificationList.get(notificationList.size() - 1).positionFetchMore = Notification.PositionFetchMore.TOP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get notifications for the authenticated account
|
* Get notifications for the authenticated account
|
||||||
*
|
*
|
||||||
* @param instance String - Instance for the api call
|
|
||||||
* @param token String - Token of the authenticated account
|
|
||||||
* @param maxId String - max id for pagination
|
|
||||||
* @param sinceId String - since id for pagination
|
|
||||||
* @param minId String - min id for pagination
|
|
||||||
* @param limit int - result fetched
|
|
||||||
* @param exlude_types List<String> - type of notifications to exclude in reply
|
|
||||||
* @param account_id String - target notifications from an account
|
|
||||||
* @return {@link LiveData} containing a {@link Notifications}
|
* @return {@link LiveData} containing a {@link Notifications}
|
||||||
*/
|
*/
|
||||||
public LiveData<Notifications> getNotifications(@NonNull String instance, String token,
|
public LiveData<Notifications> getNotifications(List<Notification> notificationList, TimelinesVM.TimelineParams timelineParams) {
|
||||||
String maxId,
|
|
||||||
String sinceId,
|
|
||||||
String minId,
|
|
||||||
int limit,
|
|
||||||
List<String> exlude_types,
|
|
||||||
String account_id) {
|
|
||||||
notificationsMutableLiveData = new MutableLiveData<>();
|
notificationsMutableLiveData = new MutableLiveData<>();
|
||||||
MastodonNotificationsService mastodonNotificationsService = init(instance);
|
MastodonNotificationsService mastodonNotificationsService = init(timelineParams.instance);
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
Notifications notifications = new Notifications();
|
Notifications notifications = new Notifications();
|
||||||
Call<List<Notification>> notificationsCall = mastodonNotificationsService.getNotifications(token, exlude_types, account_id, maxId, sinceId, minId, limit);
|
Call<List<Notification>> notificationsCall = mastodonNotificationsService.getNotifications(timelineParams.token, timelineParams.excludeType, timelineParams.userId, timelineParams.maxId, timelineParams.sinceId, timelineParams.minId, timelineParams.limit);
|
||||||
if (notificationsCall != null) {
|
if (notificationsCall != null) {
|
||||||
try {
|
try {
|
||||||
Response<List<Notification>> notificationsResponse = notificationsCall.execute();
|
Response<List<Notification>> notificationsResponse = notificationsCall.execute();
|
||||||
if (notificationsResponse.isSuccessful()) {
|
if (notificationsResponse.isSuccessful()) {
|
||||||
List<Notification> notFilteredNotifications = notificationsResponse.body();
|
notifications.notifications = notificationsResponse.body();
|
||||||
notifications.notifications = TimelineHelper.filterNotification(getApplication().getApplicationContext(), notFilteredNotifications);
|
TimelineHelper.filterNotification(getApplication().getApplicationContext(), notifications.notifications);
|
||||||
|
addFetchMoreNotifications(notifications.notifications, notificationList, timelineParams);
|
||||||
notifications.pagination = MastodonHelper.getPagination(notificationsResponse.headers());
|
notifications.pagination = MastodonHelper.getPagination(notificationsResponse.headers());
|
||||||
|
|
||||||
|
if (notifications.notifications != null && notifications.notifications.size() > 0) {
|
||||||
|
for (Notification notification : notifications.notifications) {
|
||||||
|
StatusCache statusCacheDAO = new StatusCache(getApplication().getApplicationContext());
|
||||||
|
StatusCache statusCache = new StatusCache();
|
||||||
|
statusCache.instance = timelineParams.instance;
|
||||||
|
statusCache.user_id = timelineParams.userId;
|
||||||
|
statusCache.notification = notification;
|
||||||
|
statusCache.slug = notification.type;
|
||||||
|
statusCache.type = Timeline.TimeLineEnum.NOTIFICATION;
|
||||||
|
statusCache.status_id = notification.id;
|
||||||
|
try {
|
||||||
|
statusCacheDAO.insertOrUpdate(statusCache, timelineParams.slug);
|
||||||
|
} catch (DBException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addFetchMoreNotifications(notifications.notifications, notificationList, timelineParams);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
@ -116,6 +145,32 @@ public class NotificationsVM extends AndroidViewModel {
|
||||||
return notificationsMutableLiveData;
|
return notificationsMutableLiveData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LiveData<Notifications> getNotificationCache(List<Notification> notificationList, TimelinesVM.TimelineParams timelineParams) {
|
||||||
|
notificationsMutableLiveData = new MutableLiveData<>();
|
||||||
|
new Thread(() -> {
|
||||||
|
StatusCache statusCacheDAO = new StatusCache(getApplication().getApplicationContext());
|
||||||
|
Notifications notifications = null;
|
||||||
|
try {
|
||||||
|
notifications = statusCacheDAO.getNotifications(timelineParams.excludeType, timelineParams.instance, timelineParams.userId, timelineParams.maxId, timelineParams.minId, timelineParams.sinceId);
|
||||||
|
if (notifications != null) {
|
||||||
|
if (notifications.notifications != null && notifications.notifications.size() > 0) {
|
||||||
|
TimelineHelper.filterNotification(getApplication().getApplicationContext(), notifications.notifications);
|
||||||
|
addFetchMoreNotifications(notifications.notifications, notificationList, timelineParams);
|
||||||
|
notifications.pagination = new Pagination();
|
||||||
|
notifications.pagination.min_id = notifications.notifications.get(0).id;
|
||||||
|
notifications.pagination.max_id = notifications.notifications.get(notifications.notifications.size() - 1).id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (DBException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||||
|
Notifications finalNotifications = notifications;
|
||||||
|
Runnable myRunnable = () -> notificationsMutableLiveData.setValue(finalNotifications);
|
||||||
|
mainHandler.post(myRunnable);
|
||||||
|
}).start();
|
||||||
|
return notificationsMutableLiveData;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a notification for the authenticated account by its id
|
* Get a notification for the authenticated account by its id
|
||||||
|
|
|
@ -363,6 +363,7 @@ public class TimelinesVM extends AndroidViewModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public LiveData<Statuses> getTimeline(List<Status> timelineStatuses, TimelineParams timelineParams) {
|
public LiveData<Statuses> getTimeline(List<Status> timelineStatuses, TimelineParams timelineParams) {
|
||||||
|
|
||||||
statusesMutableLiveData = new MutableLiveData<>();
|
statusesMutableLiveData = new MutableLiveData<>();
|
||||||
|
@ -451,6 +452,7 @@ public class TimelinesVM extends AndroidViewModel {
|
||||||
return statusesMutableLiveData;
|
return statusesMutableLiveData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get user drafts
|
* Get user drafts
|
||||||
*
|
*
|
||||||
|
@ -843,6 +845,7 @@ public class TimelinesVM extends AndroidViewModel {
|
||||||
public String minId;
|
public String minId;
|
||||||
public int limit = 40;
|
public int limit = 40;
|
||||||
public Boolean local;
|
public Boolean local;
|
||||||
|
public List<String> excludeType;
|
||||||
|
|
||||||
public TimelineParams(@NonNull Timeline.TimeLineEnum timeLineEnum, @Nullable FragmentMastodonTimeline.DIRECTION timelineDirection, @Nullable String ident) {
|
public TimelineParams(@NonNull Timeline.TimeLineEnum timeLineEnum, @Nullable FragmentMastodonTimeline.DIRECTION timelineDirection, @Nullable String ident) {
|
||||||
if (type != Timeline.TimeLineEnum.REMOTE) {
|
if (type != Timeline.TimeLineEnum.REMOTE) {
|
||||||
|
|
Loading…
Reference in a new issue