mirror of
https://codeberg.org/tom79/Fedilab.git
synced 2024-12-22 16:50:04 +02:00
Apply logic
This commit is contained in:
parent
5d2f7c3475
commit
e1677b82d9
5 changed files with 415 additions and 32 deletions
|
@ -14,7 +14,9 @@ package app.fedilab.android.client.entities.api;
|
||||||
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
|
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
|
||||||
* see <http://www.gnu.org/licenses>. */
|
* see <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -33,4 +35,38 @@ public class Notification {
|
||||||
public Status status;
|
public Status status;
|
||||||
|
|
||||||
public transient List<Notification> relatedNotifications;
|
public transient List<Notification> relatedNotifications;
|
||||||
|
public boolean isFetchMore;
|
||||||
|
public boolean isFetchMoreHidden = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialized a list of Notification class
|
||||||
|
*
|
||||||
|
* @param notifications List of {@link Notification} to serialize
|
||||||
|
* @return String serialized emoji list
|
||||||
|
*/
|
||||||
|
public static String notificationsToStringStorage(List<Notification> notifications) {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
try {
|
||||||
|
return gson.toJson(notifications);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unserialized a notification List
|
||||||
|
*
|
||||||
|
* @param serializedNotificationList String serialized Status list
|
||||||
|
* @return List of {@link Notification}
|
||||||
|
*/
|
||||||
|
public static List<Notification> restoreNotificationsFromString(String serializedNotificationList) {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
try {
|
||||||
|
return gson.fromJson(serializedNotificationList, new TypeToken<List<Notification>>() {
|
||||||
|
}.getType());
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,11 +24,13 @@ import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import app.fedilab.android.client.entities.api.Notification;
|
||||||
import app.fedilab.android.client.entities.api.Status;
|
import app.fedilab.android.client.entities.api.Status;
|
||||||
import app.fedilab.android.exception.DBException;
|
import app.fedilab.android.exception.DBException;
|
||||||
import app.fedilab.android.helper.MastodonHelper;
|
import app.fedilab.android.helper.MastodonHelper;
|
||||||
import app.fedilab.android.helper.SpannableHelper;
|
import app.fedilab.android.helper.SpannableHelper;
|
||||||
import app.fedilab.android.sqlite.Sqlite;
|
import app.fedilab.android.sqlite.Sqlite;
|
||||||
|
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonNotification;
|
||||||
|
|
||||||
public class QuickLoad {
|
public class QuickLoad {
|
||||||
|
|
||||||
|
@ -45,6 +47,8 @@ public class QuickLoad {
|
||||||
public int position;
|
public int position;
|
||||||
@SerializedName("statuses")
|
@SerializedName("statuses")
|
||||||
public List<Status> statuses;
|
public List<Status> statuses;
|
||||||
|
@SerializedName("notifications")
|
||||||
|
public List<Notification> notifications;
|
||||||
private Context _mContext;
|
private Context _mContext;
|
||||||
|
|
||||||
public QuickLoad() {
|
public QuickLoad() {
|
||||||
|
@ -134,6 +138,29 @@ public class QuickLoad {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param position - current position in timeline
|
||||||
|
* @param notificationTypeEnum - Timeline.NotificationTypeEnum
|
||||||
|
* @param notificationList - List<Notification> to save
|
||||||
|
*/
|
||||||
|
public void storeNotifications(int position, String user_id, String instance, FragmentMastodonNotification.NotificationTypeEnum notificationTypeEnum, List<Notification> notificationList) {
|
||||||
|
|
||||||
|
String key = notificationTypeEnum.getValue();
|
||||||
|
QuickLoad quickLoad = new QuickLoad();
|
||||||
|
quickLoad.position = position;
|
||||||
|
quickLoad.notifications = notificationList;
|
||||||
|
quickLoad.slug = key;
|
||||||
|
quickLoad.instance = instance;
|
||||||
|
quickLoad.user_id = user_id;
|
||||||
|
purge(quickLoad);
|
||||||
|
try {
|
||||||
|
insertOrUpdate(quickLoad);
|
||||||
|
} catch (DBException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert a QuickLoad in db
|
* Insert a QuickLoad in db
|
||||||
*
|
*
|
||||||
|
@ -149,7 +176,11 @@ public class QuickLoad {
|
||||||
values.put(Sqlite.COL_INSTANCE, quickLoad.instance);
|
values.put(Sqlite.COL_INSTANCE, quickLoad.instance);
|
||||||
values.put(Sqlite.COL_SLUG, quickLoad.slug);
|
values.put(Sqlite.COL_SLUG, quickLoad.slug);
|
||||||
values.put(Sqlite.COL_POSITION, quickLoad.position);
|
values.put(Sqlite.COL_POSITION, quickLoad.position);
|
||||||
values.put(Sqlite.COL_STATUSES, StatusDraft.mastodonStatusListToStringStorage(quickLoad.statuses));
|
if (quickLoad.statuses != null) {
|
||||||
|
values.put(Sqlite.COL_STATUSES, StatusDraft.mastodonStatusListToStringStorage(quickLoad.statuses));
|
||||||
|
} else if (quickLoad.notifications != null) {
|
||||||
|
values.put(Sqlite.COL_STATUSES, Notification.notificationsToStringStorage(quickLoad.notifications));
|
||||||
|
}
|
||||||
//Inserts token
|
//Inserts token
|
||||||
try {
|
try {
|
||||||
db.insertOrThrow(Sqlite.TABLE_QUICK_LOAD, null, values);
|
db.insertOrThrow(Sqlite.TABLE_QUICK_LOAD, null, values);
|
||||||
|
@ -370,6 +401,40 @@ public class QuickLoad {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves saved values
|
||||||
|
*
|
||||||
|
* @param notificationTypeEnum - FragmentMastodonNotification.NotificationTypeEnum
|
||||||
|
* @return SavedValues
|
||||||
|
*/
|
||||||
|
public QuickLoad getSavedValue(String user_id, String instance, FragmentMastodonNotification.NotificationTypeEnum notificationTypeEnum) {
|
||||||
|
String key = notificationTypeEnum.getValue();
|
||||||
|
try {
|
||||||
|
return get(user_id, instance, key);
|
||||||
|
} catch (DBException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves saved values
|
||||||
|
*
|
||||||
|
* @param notificationTypeEnum - FragmentMastodonNotification.NotificationTypeEnum
|
||||||
|
* @return SavedValues
|
||||||
|
*/
|
||||||
|
public QuickLoad getSavedValue(BaseAccount account, FragmentMastodonNotification.NotificationTypeEnum notificationTypeEnum) {
|
||||||
|
String key = notificationTypeEnum.getValue();
|
||||||
|
try {
|
||||||
|
return get(key, account);
|
||||||
|
} catch (DBException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves saved values
|
* Retrieves saved values
|
||||||
*
|
*
|
||||||
|
|
|
@ -38,6 +38,7 @@ import app.fedilab.android.R;
|
||||||
import app.fedilab.android.activities.ProfileActivity;
|
import app.fedilab.android.activities.ProfileActivity;
|
||||||
import app.fedilab.android.client.entities.api.Notification;
|
import app.fedilab.android.client.entities.api.Notification;
|
||||||
import app.fedilab.android.client.entities.app.Timeline;
|
import app.fedilab.android.client.entities.app.Timeline;
|
||||||
|
import app.fedilab.android.databinding.DrawerFetchMoreBinding;
|
||||||
import app.fedilab.android.databinding.DrawerFollowBinding;
|
import app.fedilab.android.databinding.DrawerFollowBinding;
|
||||||
import app.fedilab.android.databinding.DrawerStatusNotificationBinding;
|
import app.fedilab.android.databinding.DrawerStatusNotificationBinding;
|
||||||
import app.fedilab.android.databinding.NotificationsRelatedAccountsBinding;
|
import app.fedilab.android.databinding.NotificationsRelatedAccountsBinding;
|
||||||
|
@ -56,7 +57,9 @@ public class NotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
|
||||||
private final int TYPE_FAVOURITE = 4;
|
private final int TYPE_FAVOURITE = 4;
|
||||||
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 NOTIFICATION_FETCH_MORE = 7;
|
||||||
private Context context;
|
private Context context;
|
||||||
|
public FetchMoreCallBack fetchMoreCallBack;
|
||||||
|
|
||||||
public NotificationAdapter(List<Notification> notificationList) {
|
public NotificationAdapter(List<Notification> notificationList) {
|
||||||
this.notificationList = notificationList;
|
this.notificationList = notificationList;
|
||||||
|
@ -72,6 +75,9 @@ public class NotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemViewType(int position) {
|
public int getItemViewType(int position) {
|
||||||
|
if (notificationList.get(position).isFetchMore) {
|
||||||
|
return NOTIFICATION_FETCH_MORE;
|
||||||
|
}
|
||||||
String type = notificationList.get(position).type;
|
String type = notificationList.get(position).type;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "follow":
|
case "follow":
|
||||||
|
@ -99,6 +105,9 @@ public class NotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
|
||||||
if (viewType == TYPE_FOLLOW || viewType == TYPE_FOLLOW_REQUEST) {
|
if (viewType == TYPE_FOLLOW || viewType == TYPE_FOLLOW_REQUEST) {
|
||||||
DrawerFollowBinding itemBinding = DrawerFollowBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
DrawerFollowBinding itemBinding = DrawerFollowBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||||
return new ViewHolderFollow(itemBinding);
|
return new ViewHolderFollow(itemBinding);
|
||||||
|
} else if (viewType == NOTIFICATION_FETCH_MORE) { //Fetch more button
|
||||||
|
DrawerFetchMoreBinding itemBinding = DrawerFetchMoreBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||||
|
return new StatusAdapter.StatusViewHolder(itemBinding);
|
||||||
} else {
|
} else {
|
||||||
DrawerStatusNotificationBinding itemBinding = DrawerStatusNotificationBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
DrawerStatusNotificationBinding itemBinding = DrawerStatusNotificationBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||||
return new StatusAdapter.StatusViewHolder(itemBinding);
|
return new StatusAdapter.StatusViewHolder(itemBinding);
|
||||||
|
@ -132,6 +141,17 @@ public class NotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
|
||||||
// start the new activity
|
// start the new activity
|
||||||
context.startActivity(intent, options.toBundle());
|
context.startActivity(intent, options.toBundle());
|
||||||
});
|
});
|
||||||
|
} else if (viewHolder.getItemViewType() == NOTIFICATION_FETCH_MORE) {
|
||||||
|
StatusAdapter.StatusViewHolder holder = (StatusAdapter.StatusViewHolder) viewHolder;
|
||||||
|
holder.bindingFetchMore.fetchMore.setEnabled(!notification.isFetchMoreHidden);
|
||||||
|
holder.bindingFetchMore.fetchMore.setOnClickListener(v -> {
|
||||||
|
if (position + 1 < notificationList.size()) {
|
||||||
|
//We hide the button
|
||||||
|
notification.isFetchMoreHidden = true;
|
||||||
|
notifyItemChanged(position);
|
||||||
|
fetchMoreCallBack.onClick(notificationList.get(position + 1).id, notification.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
StatusAdapter.StatusViewHolder holderStatus = (StatusAdapter.StatusViewHolder) viewHolder;
|
StatusAdapter.StatusViewHolder holderStatus = (StatusAdapter.StatusViewHolder) viewHolder;
|
||||||
holderStatus.bindingNotification.status.typeOfNotification.setVisibility(View.VISIBLE);
|
holderStatus.bindingNotification.status.typeOfNotification.setVisibility(View.VISIBLE);
|
||||||
|
@ -234,6 +254,10 @@ public class NotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface FetchMoreCallBack {
|
||||||
|
void onClick(String min_id, String fetchmoreId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public long getItemId(int position) {
|
public long getItemId(int position) {
|
||||||
return position;
|
return position;
|
||||||
|
|
|
@ -14,6 +14,8 @@ package app.fedilab.android.ui.fragment.timeline;
|
||||||
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
|
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
|
||||||
* see <http://www.gnu.org/licenses>. */
|
* see <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
import static app.fedilab.android.BaseMainActivity.networkAvailable;
|
||||||
|
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
@ -21,6 +23,8 @@ import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
@ -41,9 +45,13 @@ import java.util.List;
|
||||||
|
|
||||||
import app.fedilab.android.BaseMainActivity;
|
import app.fedilab.android.BaseMainActivity;
|
||||||
import app.fedilab.android.R;
|
import app.fedilab.android.R;
|
||||||
|
import app.fedilab.android.activities.MainActivity;
|
||||||
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.Status;
|
import app.fedilab.android.client.entities.api.Status;
|
||||||
|
import app.fedilab.android.client.entities.api.Statuses;
|
||||||
|
import app.fedilab.android.client.entities.app.QuickLoad;
|
||||||
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;
|
||||||
|
@ -52,15 +60,16 @@ import app.fedilab.android.ui.drawer.NotificationAdapter;
|
||||||
import app.fedilab.android.viewmodel.mastodon.NotificationsVM;
|
import app.fedilab.android.viewmodel.mastodon.NotificationsVM;
|
||||||
|
|
||||||
|
|
||||||
public class FragmentMastodonNotification extends Fragment {
|
public class FragmentMastodonNotification extends Fragment implements NotificationAdapter.FetchMoreCallBack {
|
||||||
|
|
||||||
|
|
||||||
private FragmentPaginationBinding binding;
|
private FragmentPaginationBinding binding;
|
||||||
private NotificationsVM notificationsVM;
|
private NotificationsVM notificationsVM;
|
||||||
private FragmentMastodonNotification currentFragment;
|
private FragmentMastodonNotification currentFragment;
|
||||||
private boolean flagLoading;
|
private boolean flagLoading;
|
||||||
private List<Notification> notifications;
|
private static final int NOTIFICATION_PRESENT = -1;
|
||||||
private String max_id;
|
private static final int NOTIFICATION__AT_THE_BOTTOM = -2;
|
||||||
|
private List<Notification> notificationList;
|
||||||
private NotificationAdapter notificationAdapter;
|
private NotificationAdapter notificationAdapter;
|
||||||
private final BroadcastReceiver receive_action = new BroadcastReceiver() {
|
private final BroadcastReceiver receive_action = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -71,10 +80,10 @@ public class FragmentMastodonNotification extends Fragment {
|
||||||
if (receivedStatus != null && notificationAdapter != null) {
|
if (receivedStatus != null && notificationAdapter != null) {
|
||||||
int position = getPosition(receivedStatus);
|
int position = getPosition(receivedStatus);
|
||||||
if (position >= 0) {
|
if (position >= 0) {
|
||||||
if (notifications.get(position).status != null) {
|
if (notificationList.get(position).status != null) {
|
||||||
notifications.get(position).status.reblog = receivedStatus.reblog;
|
notificationList.get(position).status.reblog = receivedStatus.reblog;
|
||||||
notifications.get(position).status.favourited = receivedStatus.favourited;
|
notificationList.get(position).status.favourited = receivedStatus.favourited;
|
||||||
notifications.get(position).status.bookmarked = receivedStatus.bookmarked;
|
notificationList.get(position).status.bookmarked = receivedStatus.bookmarked;
|
||||||
notificationAdapter.notifyItemChanged(position);
|
notificationAdapter.notifyItemChanged(position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,6 +91,11 @@ public class FragmentMastodonNotification extends Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
private Notifications notifications;
|
||||||
|
private String max_id, min_id, min_id_fetch_more;
|
||||||
|
private LinearLayoutManager mLayoutManager;
|
||||||
|
private String instance, user_id;
|
||||||
|
private ArrayList<String> idOfAddedNotifications;
|
||||||
private NotificationTypeEnum notificationType;
|
private NotificationTypeEnum notificationType;
|
||||||
private List<String> excludeType;
|
private List<String> excludeType;
|
||||||
private boolean aggregateNotification;
|
private boolean aggregateNotification;
|
||||||
|
@ -95,7 +109,7 @@ public class FragmentMastodonNotification extends Fragment {
|
||||||
private int getPosition(Status status) {
|
private int getPosition(Status status) {
|
||||||
int position = 0;
|
int position = 0;
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
for (Notification _notification : notifications) {
|
for (Notification _notification : notificationList) {
|
||||||
if (_notification.status != null && _notification.status.id.compareTo(status.id) == 0) {
|
if (_notification.status != null && _notification.status.id.compareTo(status.id) == 0) {
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
|
@ -110,6 +124,9 @@ public class FragmentMastodonNotification extends Fragment {
|
||||||
|
|
||||||
currentFragment = this;
|
currentFragment = this;
|
||||||
flagLoading = false;
|
flagLoading = false;
|
||||||
|
instance = MainActivity.currentInstance;
|
||||||
|
user_id = MainActivity.currentUserID;
|
||||||
|
idOfAddedNotifications = new ArrayList<>();
|
||||||
binding = FragmentPaginationBinding.inflate(inflater, container, false);
|
binding = FragmentPaginationBinding.inflate(inflater, container, false);
|
||||||
View root = binding.getRoot();
|
View root = binding.getRoot();
|
||||||
if (getArguments() != null) {
|
if (getArguments() != null) {
|
||||||
|
@ -160,12 +177,30 @@ public class FragmentMastodonNotification extends Fragment {
|
||||||
excludeType.remove("follow");
|
excludeType.remove("follow");
|
||||||
excludeType.remove("follow_request");
|
excludeType.remove("follow_request");
|
||||||
}
|
}
|
||||||
notificationsVM.getNotifications(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, null, null, null, MastodonHelper.statusesPerCall(requireActivity()), excludeType, null)
|
new Thread(() -> {
|
||||||
.observe(getViewLifecycleOwner(), this::initializeNotificationView);
|
QuickLoad quickLoad = new QuickLoad(requireActivity()).getSavedValue(MainActivity.currentUserID, MainActivity.currentInstance, notificationType);
|
||||||
|
}).start();
|
||||||
|
router(null);
|
||||||
LocalBroadcastManager.getInstance(requireActivity()).registerReceiver(receive_action, new IntentFilter(Helper.RECEIVE_STATUS_ACTION));
|
LocalBroadcastManager.getInstance(requireActivity()).registerReceiver(receive_action, new IntentFilter(Helper.RECEIVE_STATUS_ACTION));
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void router(FragmentMastodonTimeline.DIRECTION direction) {
|
||||||
|
if (networkAvailable == BaseMainActivity.status.UNKNOWN) {
|
||||||
|
new Thread(() -> {
|
||||||
|
if (networkAvailable == BaseMainActivity.status.UNKNOWN) {
|
||||||
|
networkAvailable = Helper.isConnectedToInternet(requireActivity(), BaseMainActivity.currentInstance);
|
||||||
|
}
|
||||||
|
Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||||
|
Runnable myRunnable = () -> route(direction, false);
|
||||||
|
mainHandler.post(myRunnable);
|
||||||
|
}).start();
|
||||||
|
} else {
|
||||||
|
route(direction, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
@ -173,6 +208,7 @@ public class FragmentMastodonNotification extends Fragment {
|
||||||
notificationManager.cancel(Helper.NOTIFICATION_USER_NOTIF);
|
notificationManager.cancel(Helper.NOTIFICATION_USER_NOTIF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Intialize the view for notifications
|
* Intialize the view for notifications
|
||||||
*
|
*
|
||||||
|
@ -197,20 +233,26 @@ public class FragmentMastodonNotification extends Fragment {
|
||||||
notifications.notifications = aggregateNotifications(notifications.notifications);
|
notifications.notifications = aggregateNotifications(notifications.notifications);
|
||||||
}
|
}
|
||||||
if (notificationAdapter != null && this.notifications != null) {
|
if (notificationAdapter != null && this.notifications != null) {
|
||||||
int size = this.notifications.size();
|
int size = this.notificationList.size();
|
||||||
this.notifications.clear();
|
this.notificationList.clear();
|
||||||
this.notifications = new ArrayList<>();
|
this.notificationList = new ArrayList<>();
|
||||||
notificationAdapter.notifyItemRangeRemoved(0, size);
|
notificationAdapter.notifyItemRangeRemoved(0, size);
|
||||||
}
|
}
|
||||||
this.notifications = notifications.notifications;
|
this.notificationList = notifications.notifications;
|
||||||
notificationAdapter = new NotificationAdapter(this.notifications);
|
notificationAdapter = new NotificationAdapter(this.notificationList);
|
||||||
LinearLayoutManager mLayoutManager = new LinearLayoutManager(requireActivity());
|
mLayoutManager = new LinearLayoutManager(requireActivity());
|
||||||
binding.recyclerView.setLayoutManager(mLayoutManager);
|
binding.recyclerView.setLayoutManager(mLayoutManager);
|
||||||
binding.recyclerView.setAdapter(notificationAdapter);
|
binding.recyclerView.setAdapter(notificationAdapter);
|
||||||
max_id = notifications.pagination.max_id;
|
max_id = notifications.pagination.max_id;
|
||||||
binding.recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
binding.recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
|
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
|
||||||
|
if (requireActivity() instanceof BaseMainActivity) {
|
||||||
|
if (dy < 0 && !((BaseMainActivity) requireActivity()).getFloatingVisibility())
|
||||||
|
((BaseMainActivity) requireActivity()).manageFloatingButton(true);
|
||||||
|
if (dy > 0 && ((BaseMainActivity) requireActivity()).getFloatingVisibility())
|
||||||
|
((BaseMainActivity) requireActivity()).manageFloatingButton(false);
|
||||||
|
}
|
||||||
int firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition();
|
int firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition();
|
||||||
if (dy > 0) {
|
if (dy > 0) {
|
||||||
int visibleItemCount = mLayoutManager.getChildCount();
|
int visibleItemCount = mLayoutManager.getChildCount();
|
||||||
|
@ -219,28 +261,83 @@ public class FragmentMastodonNotification extends Fragment {
|
||||||
if (!flagLoading) {
|
if (!flagLoading) {
|
||||||
flagLoading = true;
|
flagLoading = true;
|
||||||
binding.loadingNextElements.setVisibility(View.VISIBLE);
|
binding.loadingNextElements.setVisibility(View.VISIBLE);
|
||||||
notificationsVM.getNotifications(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()), excludeType, null)
|
router(FragmentMastodonTimeline.DIRECTION.BOTTOM);
|
||||||
.observe(FragmentMastodonNotification.this, fetched_notifications -> dealWithPagination(fetched_notifications));
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
binding.loadingNextElements.setVisibility(View.GONE);
|
binding.loadingNextElements.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
} else if (firstVisibleItem == 0) { //Scroll top and item is zero
|
||||||
|
if (!flagLoading) {
|
||||||
|
flagLoading = true;
|
||||||
|
binding.loadingNextElements.setVisibility(View.VISIBLE);
|
||||||
|
router(FragmentMastodonTimeline.DIRECTION.TOP);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
binding.swipeContainer.setOnRefreshListener(() -> {
|
binding.swipeContainer.setOnRefreshListener(() -> {
|
||||||
if (this.notifications.size() > 0) {
|
binding.swipeContainer.setRefreshing(true);
|
||||||
binding.swipeContainer.setRefreshing(true);
|
flagLoading = false;
|
||||||
max_id = null;
|
route(FragmentMastodonTimeline.DIRECTION.REFRESH, true);
|
||||||
flagLoading = false;
|
|
||||||
notificationsVM.getNotifications(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, null, null, null, MastodonHelper.statusesPerCall(requireActivity()), excludeType, null)
|
|
||||||
.observe(FragmentMastodonNotification.this, this::initializeNotificationView);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
new Thread(() -> {
|
||||||
|
if (binding == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QuickLoad quickLoad = new QuickLoad(requireActivity()).getSavedValue(MainActivity.currentUserID, MainActivity.currentInstance, notificationType);
|
||||||
|
if (direction != FragmentMastodonTimeline.DIRECTION.REFRESH && !fetchingMissing && !binding.swipeContainer.isRefreshing() && direction == null && quickLoad != null && quickLoad.statuses != null && quickLoad.statuses.size() > 0) {
|
||||||
|
Statuses statuses = new Statuses();
|
||||||
|
statuses.statuses = quickLoad.statuses;
|
||||||
|
statuses.pagination = new Pagination();
|
||||||
|
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 = () -> initializeNotificationView(notifications);
|
||||||
|
mainHandler.post(myRunnable);
|
||||||
|
} else {
|
||||||
|
Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||||
|
Runnable myRunnable = () -> {
|
||||||
|
if (!isAdded()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (direction == null) {
|
||||||
|
notificationsVM.getNotifications(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, null, null, null, MastodonHelper.statusesPerCall(requireActivity()), excludeType, null)
|
||||||
|
.observe(getViewLifecycleOwner(), this::initializeNotificationView);
|
||||||
|
} else if (direction == FragmentMastodonTimeline.DIRECTION.BOTTOM) {
|
||||||
|
notificationsVM.getNotifications(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()), excludeType, null)
|
||||||
|
.observe(getViewLifecycleOwner(), this::initializeNotificationView);
|
||||||
|
} 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)
|
||||||
|
.observe(getViewLifecycleOwner(), this::initializeNotificationView);
|
||||||
|
} else if (direction == FragmentMastodonTimeline.DIRECTION.REFRESH) {
|
||||||
|
notificationsVM.getNotifications(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, null, null, null, MastodonHelper.statusesPerCall(requireActivity()), excludeType, null)
|
||||||
|
.observe(getViewLifecycleOwner(), notificationsReceived -> {
|
||||||
|
if (notificationAdapter != null) {
|
||||||
|
dealWithPagination(notificationsReceived);
|
||||||
|
} else {
|
||||||
|
initializeNotificationView(notificationsReceived);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mainHandler.post(myRunnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private List<Notification> aggregateNotifications(List<Notification> notifications) {
|
private List<Notification> aggregateNotifications(List<Notification> notifications) {
|
||||||
List<Notification> notificationList = new ArrayList<>();
|
List<Notification> notificationList = new ArrayList<>();
|
||||||
int refPosition = 0;
|
int refPosition = 0;
|
||||||
|
@ -271,6 +368,124 @@ public class FragmentMastodonNotification extends Fragment {
|
||||||
binding.recyclerView.scrollToPosition(0);
|
binding.recyclerView.scrollToPosition(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update view and pagination when scrolling down
|
||||||
|
*
|
||||||
|
* @param fetched_notifications Notifications
|
||||||
|
*/
|
||||||
|
private synchronized void dealWithPagination(Notifications fetched_notifications, FragmentMastodonTimeline.DIRECTION direction, boolean fetchingMissing) {
|
||||||
|
if (binding == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int currentPosition = mLayoutManager.findFirstVisibleItemPosition();
|
||||||
|
binding.swipeContainer.setRefreshing(false);
|
||||||
|
binding.loadingNextElements.setVisibility(View.GONE);
|
||||||
|
flagLoading = false;
|
||||||
|
if (notificationList != null && fetched_notifications != null && fetched_notifications.notifications != null && fetched_notifications.notifications.size() > 0) {
|
||||||
|
flagLoading = fetched_notifications.pagination.max_id == null;
|
||||||
|
binding.noAction.setVisibility(View.GONE);
|
||||||
|
//Update the timeline with new statuses
|
||||||
|
int inserted = updateNotificationListWith(direction, fetched_notifications.notifications, fetchingMissing);
|
||||||
|
if (fetchingMissing) {
|
||||||
|
// binding.recyclerView.scrollToPosition(currentPosition + inserted);
|
||||||
|
}
|
||||||
|
if (!fetchingMissing) {
|
||||||
|
if (fetched_notifications.pagination.max_id == null) {
|
||||||
|
flagLoading = true;
|
||||||
|
} else if (max_id == null || fetched_notifications.pagination.max_id.compareTo(max_id) < 0) {
|
||||||
|
max_id = fetched_notifications.pagination.max_id;
|
||||||
|
}
|
||||||
|
if (min_id == null || (fetched_notifications.pagination.min_id != null && fetched_notifications.pagination.min_id.compareTo(min_id) > 0)) {
|
||||||
|
min_id = fetched_notifications.pagination.min_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (direction == FragmentMastodonTimeline.DIRECTION.BOTTOM) {
|
||||||
|
flagLoading = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the timeline with received statuses
|
||||||
|
*
|
||||||
|
* @param notificationsReceived - List<Notification> Notifications received
|
||||||
|
* @param fetchingMissing - boolean if the call concerns fetching messages (ie: refresh of from fetch more button)
|
||||||
|
* @return int - Number of messages that have been inserted in the middle of the timeline (ie between other statuses)
|
||||||
|
*/
|
||||||
|
private int updateNotificationListWith(FragmentMastodonTimeline.DIRECTION direction, List<Notification> notificationsReceived, boolean fetchingMissing) {
|
||||||
|
int numberInserted = 0;
|
||||||
|
int lastInsertedPosition = 0;
|
||||||
|
int initialInsertedPosition = NOTIFICATION_PRESENT;
|
||||||
|
if (notificationsReceived != null && notificationsReceived.size() > 0) {
|
||||||
|
int insertedPosition = NOTIFICATION_PRESENT;
|
||||||
|
for (Notification notificationReceived : notificationsReceived) {
|
||||||
|
insertedPosition = insertNotification(notificationReceived);
|
||||||
|
if (insertedPosition != NOTIFICATION_PRESENT && insertedPosition != NOTIFICATION__AT_THE_BOTTOM) {
|
||||||
|
numberInserted++;
|
||||||
|
if (initialInsertedPosition == NOTIFICATION_PRESENT) {
|
||||||
|
initialInsertedPosition = insertedPosition;
|
||||||
|
}
|
||||||
|
if (insertedPosition < initialInsertedPosition) {
|
||||||
|
initialInsertedPosition = lastInsertedPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastInsertedPosition = initialInsertedPosition + numberInserted;
|
||||||
|
//lastInsertedPosition contains the position of the last inserted status
|
||||||
|
//If there were no overlap for top status
|
||||||
|
if (fetchingMissing && insertedPosition != NOTIFICATION_PRESENT && insertedPosition != NOTIFICATION__AT_THE_BOTTOM && this.notificationList.size() > insertedPosition) {
|
||||||
|
Notification notificationFetchMore = new Notification();
|
||||||
|
notificationFetchMore.isFetchMore = true;
|
||||||
|
notificationFetchMore.id = Helper.generateString();
|
||||||
|
int insertAt;
|
||||||
|
if (direction == FragmentMastodonTimeline.DIRECTION.REFRESH) {
|
||||||
|
insertAt = lastInsertedPosition;
|
||||||
|
} else {
|
||||||
|
insertAt = initialInsertedPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.notificationList.add(insertAt, notificationFetchMore);
|
||||||
|
notificationAdapter.notifyItemInserted(insertAt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return numberInserted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert a status if not yet in the timeline
|
||||||
|
*
|
||||||
|
* @param notificationReceived - Notification coming from the api/db
|
||||||
|
* @return int >= 0 | STATUS_PRESENT = -1 | STATUS_AT_THE_BOTTOM = -2
|
||||||
|
*/
|
||||||
|
private int insertNotification(Notification notificationReceived) {
|
||||||
|
if (idOfAddedNotifications.contains(notificationReceived.id)) {
|
||||||
|
return NOTIFICATION_PRESENT;
|
||||||
|
}
|
||||||
|
int position = 0;
|
||||||
|
//We loop through messages already in the timeline
|
||||||
|
for (Notification notificationsAlreadyPresent : this.notificationList) {
|
||||||
|
//We compare the date of each status and we only add status having a date greater than the another, it is inserted at this position
|
||||||
|
//Pinned messages are ignored because their date can be older
|
||||||
|
if (notificationReceived.id.compareTo(notificationsAlreadyPresent.id) > 0) {
|
||||||
|
//We add the status to a list of id - thus we know it is already in the timeline
|
||||||
|
idOfAddedNotifications.add(notificationReceived.id);
|
||||||
|
this.notificationList.add(position, notificationReceived);
|
||||||
|
notificationAdapter.notifyItemInserted(position);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
position++;
|
||||||
|
}
|
||||||
|
//Statuses added at the bottom, we flag them by position = -2 for not dealing with them and fetch more
|
||||||
|
if (position == this.notificationList.size()) {
|
||||||
|
//We add the status to a list of id - thus we know it is already in the timeline
|
||||||
|
idOfAddedNotifications.add(notificationReceived.id);
|
||||||
|
this.notificationList.add(position, notificationReceived);
|
||||||
|
notificationAdapter.notifyItemInserted(position);
|
||||||
|
return NOTIFICATION__AT_THE_BOTTOM;
|
||||||
|
}
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update view and pagination when scrolling down
|
* Update view and pagination when scrolling down
|
||||||
*
|
*
|
||||||
|
@ -286,10 +501,10 @@ public class FragmentMastodonNotification extends Fragment {
|
||||||
}
|
}
|
||||||
int startId = 0;
|
int startId = 0;
|
||||||
//There are some statuses present in the timeline
|
//There are some statuses present in the timeline
|
||||||
if (currentFragment.notifications.size() > 0) {
|
if (currentFragment.notificationList.size() > 0) {
|
||||||
startId = currentFragment.notifications.size();
|
startId = currentFragment.notificationList.size();
|
||||||
}
|
}
|
||||||
currentFragment.notifications.addAll(fetched_notifications.notifications);
|
currentFragment.notificationList.addAll(fetched_notifications.notifications);
|
||||||
max_id = fetched_notifications.pagination.max_id;
|
max_id = fetched_notifications.pagination.max_id;
|
||||||
notificationAdapter.notifyItemRangeInserted(startId, fetched_notifications.notifications.size());
|
notificationAdapter.notifyItemRangeInserted(startId, fetched_notifications.notifications.size());
|
||||||
} else {
|
} else {
|
||||||
|
@ -299,6 +514,15 @@ public class FragmentMastodonNotification extends Fragment {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroyView() {
|
public void onDestroyView() {
|
||||||
|
if (mLayoutManager != null) {
|
||||||
|
int position = mLayoutManager.findFirstVisibleItemPosition();
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
new QuickLoad(requireActivity()).storeNotifications(position, user_id, instance, notificationType, notificationList);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
super.onDestroyView();
|
super.onDestroyView();
|
||||||
binding.recyclerView.setAdapter(null);
|
binding.recyclerView.setAdapter(null);
|
||||||
LocalBroadcastManager.getInstance(requireActivity()).unregisterReceiver(receive_action);
|
LocalBroadcastManager.getInstance(requireActivity()).unregisterReceiver(receive_action);
|
||||||
|
@ -306,6 +530,20 @@ public class FragmentMastodonNotification extends Fragment {
|
||||||
binding = null;
|
binding = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
if (mLayoutManager != null) {
|
||||||
|
int position = mLayoutManager.findFirstVisibleItemPosition();
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
new QuickLoad(requireActivity()).storeNotifications(position, user_id, instance, notificationType, notificationList);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public enum NotificationTypeEnum {
|
public enum NotificationTypeEnum {
|
||||||
@SerializedName("ALL")
|
@SerializedName("ALL")
|
||||||
|
@ -334,4 +572,24 @@ public class FragmentMastodonNotification extends Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(String min_id, String id) {
|
||||||
|
//Fetch more has been pressed
|
||||||
|
min_id_fetch_more = min_id;
|
||||||
|
Notification notification = null;
|
||||||
|
int position = 0;
|
||||||
|
for (Notification currentNotification : this.notificationList) {
|
||||||
|
if (currentNotification.id.compareTo(id) == 0) {
|
||||||
|
notification = currentNotification;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
position++;
|
||||||
|
}
|
||||||
|
if (notification != null) {
|
||||||
|
this.notificationList.remove(position);
|
||||||
|
notificationAdapter.notifyItemRemoved(position);
|
||||||
|
}
|
||||||
|
route(FragmentMastodonTimeline.DIRECTION.TOP, true);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -566,10 +566,10 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
||||||
*/
|
*/
|
||||||
private void route(DIRECTION direction, boolean fetchingMissing) {
|
private void route(DIRECTION direction, boolean fetchingMissing) {
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
QuickLoad quickLoad = new QuickLoad(requireActivity()).getSavedValue(MainActivity.currentUserID, MainActivity.currentInstance, timelineType, ident);
|
|
||||||
if (binding == null) {
|
if (binding == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
QuickLoad quickLoad = new QuickLoad(requireActivity()).getSavedValue(MainActivity.currentUserID, MainActivity.currentInstance, timelineType, ident);
|
||||||
if (direction != DIRECTION.REFRESH && !fetchingMissing && !binding.swipeContainer.isRefreshing() && direction == null && quickLoad != null && quickLoad.statuses != null && quickLoad.statuses.size() > 0) {
|
if (direction != DIRECTION.REFRESH && !fetchingMissing && !binding.swipeContainer.isRefreshing() && direction == null && quickLoad != null && quickLoad.statuses != null && quickLoad.statuses.size() > 0) {
|
||||||
Statuses statuses = new Statuses();
|
Statuses statuses = new Statuses();
|
||||||
statuses.statuses = quickLoad.statuses;
|
statuses.statuses = quickLoad.statuses;
|
||||||
|
|
Loading…
Reference in a new issue