mirror of
https://codeberg.org/tom79/Fedilab.git
synced 2025-02-25 18:39:45 +02:00
Implement cache for conversations
This commit is contained in:
parent
c4dbe80a05
commit
204cb84dca
11 changed files with 607 additions and 63 deletions
|
@ -1,5 +1,7 @@
|
||||||
package app.fedilab.android.client.entities.api;
|
package app.fedilab.android.client.entities.api;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -27,4 +29,25 @@ public class Conversation {
|
||||||
public List<Account> accounts;
|
public List<Account> accounts;
|
||||||
@SerializedName("last_status")
|
@SerializedName("last_status")
|
||||||
public Status last_status;
|
public Status last_status;
|
||||||
|
public boolean isFetchMore = false;
|
||||||
|
@SerializedName("cached")
|
||||||
|
public boolean cached = false;
|
||||||
|
|
||||||
|
|
||||||
|
public PositionFetchMore positionFetchMore = PositionFetchMore.BOTTOM;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(@Nullable Object obj) {
|
||||||
|
boolean same = false;
|
||||||
|
if (obj instanceof Conversation) {
|
||||||
|
same = this.id.equals(((Conversation) obj).id);
|
||||||
|
}
|
||||||
|
return same;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum PositionFetchMore {
|
||||||
|
TOP,
|
||||||
|
BOTTOM
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ public class Notification {
|
||||||
|
|
||||||
public transient List<Notification> relatedNotifications;
|
public transient List<Notification> relatedNotifications;
|
||||||
public boolean isFetchMore;
|
public boolean isFetchMore;
|
||||||
public boolean isFetchMoreHidden = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serialized a list of Notification class
|
* Serialized a list of Notification class
|
||||||
|
|
|
@ -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.Conversation;
|
||||||
|
import app.fedilab.android.client.entities.api.Conversations;
|
||||||
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.Pagination;
|
||||||
|
@ -57,6 +59,8 @@ public class StatusCache {
|
||||||
public Status status;
|
public Status status;
|
||||||
@SerializedName("notification")
|
@SerializedName("notification")
|
||||||
public Notification notification;
|
public Notification notification;
|
||||||
|
@SerializedName("conversation")
|
||||||
|
public Conversation conversation;
|
||||||
@SerializedName("created_at")
|
@SerializedName("created_at")
|
||||||
public Date created_at;
|
public Date created_at;
|
||||||
@SerializedName("updated_at")
|
@SerializedName("updated_at")
|
||||||
|
@ -103,6 +107,21 @@ public class StatusCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialized a Conversation class
|
||||||
|
*
|
||||||
|
* @param mastodon_conversation {@link Conversation} to serialize
|
||||||
|
* @return String serialized Conversation
|
||||||
|
*/
|
||||||
|
public static String mastodonConversationToStringStorage(Conversation mastodon_conversation) {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
try {
|
||||||
|
return gson.toJson(mastodon_conversation);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unserialized a Mastodon Status
|
* Unserialized a Mastodon Status
|
||||||
*
|
*
|
||||||
|
@ -135,6 +154,23 @@ public class StatusCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unserialized a Mastodon Conversation
|
||||||
|
*
|
||||||
|
* @param serializedConversation String serialized Conversation
|
||||||
|
* @return {@link Conversation}
|
||||||
|
*/
|
||||||
|
public static Conversation restoreConversationFromString(String serializedConversation) {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
try {
|
||||||
|
return gson.fromJson(serializedConversation, Conversation.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert or update a status
|
* Insert or update a status
|
||||||
*
|
*
|
||||||
|
@ -238,6 +274,9 @@ public class StatusCache {
|
||||||
if (statusCache.notification != null) {
|
if (statusCache.notification != null) {
|
||||||
values.put(Sqlite.COL_STATUS, mastodonNotificationToStringStorage(statusCache.notification));
|
values.put(Sqlite.COL_STATUS, mastodonNotificationToStringStorage(statusCache.notification));
|
||||||
}
|
}
|
||||||
|
if (statusCache.conversation != null) {
|
||||||
|
values.put(Sqlite.COL_STATUS, mastodonConversationToStringStorage(statusCache.conversation));
|
||||||
|
}
|
||||||
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 {
|
||||||
|
@ -268,6 +307,9 @@ public class StatusCache {
|
||||||
if (statusCache.notification != null) {
|
if (statusCache.notification != null) {
|
||||||
values.put(Sqlite.COL_STATUS, mastodonNotificationToStringStorage(statusCache.notification));
|
values.put(Sqlite.COL_STATUS, mastodonNotificationToStringStorage(statusCache.notification));
|
||||||
}
|
}
|
||||||
|
if (statusCache.conversation != null) {
|
||||||
|
values.put(Sqlite.COL_STATUS, mastodonConversationToStringStorage(statusCache.conversation));
|
||||||
|
}
|
||||||
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 {
|
||||||
|
@ -388,6 +430,42 @@ public class StatusCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get paginated conversations 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 Conversations getConversations(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_TYPE + "= '" + Timeline.TimeLineEnum.CONVERSATION.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Cursor c = db.query(Sqlite.TABLE_STATUS_CACHE, null, selection, null, null, null, Sqlite.COL_STATUS_ID + order, limit);
|
||||||
|
return createConversationReply(cursorToListOfConversations(c));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get paginated statuses from db
|
* Get paginated statuses from db
|
||||||
*
|
*
|
||||||
|
@ -495,7 +573,7 @@ public class StatusCache {
|
||||||
* Convert a cursor to list of notifications
|
* Convert a cursor to list of notifications
|
||||||
*
|
*
|
||||||
* @param c Cursor
|
* @param c Cursor
|
||||||
* @return List<Status>
|
* @return List<Notification>
|
||||||
*/
|
*/
|
||||||
private List<Notification> cursorToListOfNotifications(Cursor c) {
|
private List<Notification> cursorToListOfNotifications(Cursor c) {
|
||||||
//No element found
|
//No element found
|
||||||
|
@ -514,6 +592,28 @@ public class StatusCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a cursor to list of Conversation
|
||||||
|
*
|
||||||
|
* @param c Cursor
|
||||||
|
* @return List<Conversation>
|
||||||
|
*/
|
||||||
|
private List<Conversation> cursorToListOfConversations(Cursor c) {
|
||||||
|
//No element found
|
||||||
|
if (c.getCount() == 0) {
|
||||||
|
c.close();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<Conversation> conversationList = new ArrayList<>();
|
||||||
|
while (c.moveToNext()) {
|
||||||
|
Conversation conversation = convertCursorToConversation(c);
|
||||||
|
conversationList.add(conversation);
|
||||||
|
}
|
||||||
|
//Close the cursor
|
||||||
|
c.close();
|
||||||
|
return conversationList;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a reply from db in the same way than API call
|
* Create a reply from db in the same way than API call
|
||||||
*
|
*
|
||||||
|
@ -537,6 +637,30 @@ public class StatusCache {
|
||||||
return notifications;
|
return notifications;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a reply from db in the same way than API call
|
||||||
|
*
|
||||||
|
* @param conversationList List<Conversation>
|
||||||
|
* @return Conversations (with pagination)
|
||||||
|
*/
|
||||||
|
private Conversations createConversationReply(List<Conversation> conversationList) {
|
||||||
|
Conversations conversations = new Conversations();
|
||||||
|
conversations.conversations = conversationList;
|
||||||
|
Pagination pagination = new Pagination();
|
||||||
|
if (conversationList != null && conversationList.size() > 0) {
|
||||||
|
//Status list is inverted, it happens for min_id due to ASC ordering
|
||||||
|
if (conversationList.get(0).id.compareTo(conversationList.get(conversationList.size() - 1).id) < 0) {
|
||||||
|
Collections.reverse(conversationList);
|
||||||
|
conversations.conversations = conversationList;
|
||||||
|
}
|
||||||
|
pagination.max_id = conversationList.get(0).id;
|
||||||
|
pagination.min_id = conversationList.get(conversationList.size() - 1).id;
|
||||||
|
}
|
||||||
|
conversations.pagination = pagination;
|
||||||
|
return conversations;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a reply from db in the same way than API call
|
* Create a reply from db in the same way than API call
|
||||||
*
|
*
|
||||||
|
@ -583,6 +707,17 @@ public class StatusCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read cursor and hydrate without closing it
|
||||||
|
*
|
||||||
|
* @param c - Cursor
|
||||||
|
* @return Conversation
|
||||||
|
*/
|
||||||
|
private Conversation convertCursorToConversation(Cursor c) {
|
||||||
|
String serializedNotification = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_STATUS));
|
||||||
|
return restoreConversationFromString(serializedNotification);
|
||||||
|
}
|
||||||
|
|
||||||
public enum order {
|
public enum order {
|
||||||
@SerializedName("ASC")
|
@SerializedName("ASC")
|
||||||
ASC("ASC"),
|
ASC("ASC"),
|
||||||
|
|
|
@ -358,6 +358,8 @@ public class Timeline {
|
||||||
DIRECT("DIRECT"),
|
DIRECT("DIRECT"),
|
||||||
@SerializedName("NOTIFICATION")
|
@SerializedName("NOTIFICATION")
|
||||||
NOTIFICATION("NOTIFICATION"),
|
NOTIFICATION("NOTIFICATION"),
|
||||||
|
@SerializedName("CONVERSATION")
|
||||||
|
CONVERSATION("CONVERSATION"),
|
||||||
@SerializedName("LOCAL")
|
@SerializedName("LOCAL")
|
||||||
LOCAL("LOCAL"),
|
LOCAL("LOCAL"),
|
||||||
@SerializedName("PUBLIC")
|
@SerializedName("PUBLIC")
|
||||||
|
|
|
@ -58,6 +58,7 @@ public class ConversationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
|
||||||
private final List<Conversation> conversationList;
|
private final List<Conversation> conversationList;
|
||||||
private Context context;
|
private Context context;
|
||||||
private boolean isExpended = false;
|
private boolean isExpended = false;
|
||||||
|
public FetchMoreCallBack fetchMoreCallBack;
|
||||||
|
|
||||||
public ConversationAdapter(List<Conversation> conversations) {
|
public ConversationAdapter(List<Conversation> conversations) {
|
||||||
if (conversations == null) {
|
if (conversations == null) {
|
||||||
|
@ -127,6 +128,42 @@ public class ConversationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
|
||||||
if (conversation.last_status == null) {
|
if (conversation.last_status == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (conversation.isFetchMore && fetchMoreCallBack != null) {
|
||||||
|
holder.binding.layoutFetchMore.fetchMoreContainer.setVisibility(View.VISIBLE);
|
||||||
|
holder.binding.layoutFetchMore.fetchMoreMin.setOnClickListener(v -> {
|
||||||
|
conversation.isFetchMore = false;
|
||||||
|
if (holder.getBindingAdapterPosition() < conversationList.size() - 1) {
|
||||||
|
String fromId;
|
||||||
|
if (conversation.positionFetchMore == Conversation.PositionFetchMore.TOP) {
|
||||||
|
fromId = conversationList.get(position + 1).id;
|
||||||
|
} else {
|
||||||
|
fromId = conversation.id;
|
||||||
|
}
|
||||||
|
fetchMoreCallBack.onClickMinId(fromId, conversation);
|
||||||
|
notifyItemChanged(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
holder.binding.layoutFetchMore.fetchMoreMax.setOnClickListener(v -> {
|
||||||
|
//We hide the button
|
||||||
|
conversation.isFetchMore = false;
|
||||||
|
String fromId;
|
||||||
|
if (conversation.positionFetchMore == Conversation.PositionFetchMore.TOP) {
|
||||||
|
fromId = conversationList.get(position).id;
|
||||||
|
} else {
|
||||||
|
fromId = conversationList.get(position - 1).id;
|
||||||
|
}
|
||||||
|
notifyItemChanged(position);
|
||||||
|
fetchMoreCallBack.onClickMaxId(fromId, conversation);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
holder.binding.layoutFetchMore.fetchMoreContainer.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
if (conversation.cached) {
|
||||||
|
holder.binding.cacheIndicator.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
holder.binding.cacheIndicator.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
//---- SPOILER TEXT -----
|
//---- SPOILER TEXT -----
|
||||||
boolean expand_cw = sharedpreferences.getBoolean(context.getString(R.string.SET_EXPAND_CW), false);
|
boolean expand_cw = sharedpreferences.getBoolean(context.getString(R.string.SET_EXPAND_CW), false);
|
||||||
if (conversation.last_status.spoiler_text != null && !conversation.last_status.spoiler_text.trim().isEmpty()) {
|
if (conversation.last_status.spoiler_text != null && !conversation.last_status.spoiler_text.trim().isEmpty()) {
|
||||||
|
@ -257,5 +294,9 @@ public class ConversationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface FetchMoreCallBack {
|
||||||
|
void onClickMinId(String min_id, Conversation conversationToUpdate);
|
||||||
|
|
||||||
|
void onClickMaxId(String max_id, Conversation conversationToUpdate);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -15,6 +15,7 @@ package app.fedilab.android.ui.fragment.timeline;
|
||||||
* see <http://www.gnu.org/licenses>. */
|
* see <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -24,6 +25,7 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
@ -34,33 +36,149 @@ import app.fedilab.android.BaseMainActivity;
|
||||||
import app.fedilab.android.R;
|
import app.fedilab.android.R;
|
||||||
import app.fedilab.android.client.entities.api.Conversation;
|
import app.fedilab.android.client.entities.api.Conversation;
|
||||||
import app.fedilab.android.client.entities.api.Conversations;
|
import app.fedilab.android.client.entities.api.Conversations;
|
||||||
|
import app.fedilab.android.client.entities.app.StatusCache;
|
||||||
|
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.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.ConversationAdapter;
|
import app.fedilab.android.ui.drawer.ConversationAdapter;
|
||||||
import app.fedilab.android.viewmodel.mastodon.TimelinesVM;
|
import app.fedilab.android.viewmodel.mastodon.TimelinesVM;
|
||||||
|
|
||||||
|
|
||||||
public class FragmentMastodonConversation extends Fragment {
|
public class FragmentMastodonConversation extends Fragment implements ConversationAdapter.FetchMoreCallBack {
|
||||||
|
|
||||||
|
|
||||||
private FragmentPaginationBinding binding;
|
private FragmentPaginationBinding binding;
|
||||||
private TimelinesVM timelinesVM;
|
private TimelinesVM timelinesVM;
|
||||||
private FragmentMastodonConversation currentFragment;
|
|
||||||
private boolean flagLoading;
|
private boolean flagLoading;
|
||||||
private List<Conversation> conversations;
|
private List<Conversation> conversationList;
|
||||||
private String max_id;
|
private String max_id, min_id, min_id_fetch_more, max_id_fetch_more;
|
||||||
private ConversationAdapter conversationAdapter;
|
private ConversationAdapter conversationAdapter;
|
||||||
|
private LinearLayoutManager mLayoutManager;
|
||||||
|
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||||
ViewGroup container, Bundle savedInstanceState) {
|
ViewGroup container, Bundle savedInstanceState) {
|
||||||
currentFragment = this;
|
|
||||||
flagLoading = false;
|
flagLoading = false;
|
||||||
binding = FragmentPaginationBinding.inflate(inflater, container, false);
|
binding = FragmentPaginationBinding.inflate(inflater, container, false);
|
||||||
binding.getRoot().setBackgroundColor(ThemeHelper.getBackgroundColor(requireActivity()));
|
binding.getRoot().setBackgroundColor(ThemeHelper.getBackgroundColor(requireActivity()));
|
||||||
return binding.getRoot();
|
return binding.getRoot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
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, Conversation conversationToUpdate) {
|
||||||
|
if (binding == null || !isAdded() || getActivity() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!isAdded()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
|
||||||
|
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) {
|
||||||
|
timelineParams.maxId = fetchingMissing ? max_id_fetch_more : max_id;
|
||||||
|
timelineParams.minId = null;
|
||||||
|
} else if (direction == FragmentMastodonTimeline.DIRECTION.TOP) {
|
||||||
|
timelineParams.minId = fetchingMissing ? min_id_fetch_more : min_id;
|
||||||
|
timelineParams.maxId = null;
|
||||||
|
} else {
|
||||||
|
timelineParams.maxId = max_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
timelineParams.fetchingMissing = fetchingMissing;
|
||||||
|
|
||||||
|
if (useCache) {
|
||||||
|
getCachedConversations(direction, fetchingMissing, timelineParams);
|
||||||
|
} else {
|
||||||
|
getLiveConversations(direction, fetchingMissing, timelineParams, conversationToUpdate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getCachedConversations(FragmentMastodonTimeline.DIRECTION direction, boolean fetchingMissing, TimelinesVM.TimelineParams timelineParams) {
|
||||||
|
|
||||||
|
if (direction == null) {
|
||||||
|
timelinesVM.getConversations(conversationList, timelineParams)
|
||||||
|
.observe(getViewLifecycleOwner(), conversationsCached -> {
|
||||||
|
if (conversationsCached == null || conversationsCached.conversations == null || conversationsCached.conversations.size() == 0) {
|
||||||
|
getLiveConversations(null, fetchingMissing, timelineParams, null);
|
||||||
|
} else {
|
||||||
|
initializeConversationCommonView(conversationsCached);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (direction == FragmentMastodonTimeline.DIRECTION.BOTTOM) {
|
||||||
|
timelinesVM.getConversationsCache(conversationList, timelineParams)
|
||||||
|
.observe(getViewLifecycleOwner(), conversationsBottom -> {
|
||||||
|
if (conversationsBottom == null || conversationsBottom.conversations == null || conversationsBottom.conversations.size() == 0) {
|
||||||
|
getLiveConversations(FragmentMastodonTimeline.DIRECTION.BOTTOM, fetchingMissing, timelineParams, null);
|
||||||
|
} else {
|
||||||
|
dealWithPagination(conversationsBottom, FragmentMastodonTimeline.DIRECTION.BOTTOM, fetchingMissing, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
} else if (direction == FragmentMastodonTimeline.DIRECTION.TOP) {
|
||||||
|
timelinesVM.getConversationsCache(conversationList, timelineParams)
|
||||||
|
.observe(getViewLifecycleOwner(), conversationsTop -> {
|
||||||
|
if (conversationsTop == null || conversationsTop.conversations == null || conversationsTop.conversations.size() == 0) {
|
||||||
|
getLiveConversations(FragmentMastodonTimeline.DIRECTION.TOP, fetchingMissing, timelineParams, null);
|
||||||
|
} else {
|
||||||
|
dealWithPagination(conversationsTop, FragmentMastodonTimeline.DIRECTION.TOP, fetchingMissing, null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (direction == FragmentMastodonTimeline.DIRECTION.REFRESH) {
|
||||||
|
timelinesVM.getConversations(conversationList, timelineParams)
|
||||||
|
.observe(getViewLifecycleOwner(), notificationsRefresh -> {
|
||||||
|
if (conversationAdapter != null) {
|
||||||
|
dealWithPagination(notificationsRefresh, FragmentMastodonTimeline.DIRECTION.REFRESH, true, null);
|
||||||
|
} else {
|
||||||
|
initializeConversationCommonView(notificationsRefresh);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getLiveConversations(FragmentMastodonTimeline.DIRECTION direction, boolean fetchingMissing, TimelinesVM.TimelineParams timelineParams, Conversation conversationToUpdate) {
|
||||||
|
if (direction == null) {
|
||||||
|
timelinesVM.getConversations(conversationList, timelineParams)
|
||||||
|
.observe(getViewLifecycleOwner(), this::initializeConversationCommonView);
|
||||||
|
} else if (direction == FragmentMastodonTimeline.DIRECTION.BOTTOM) {
|
||||||
|
timelinesVM.getConversations(conversationList, timelineParams)
|
||||||
|
.observe(getViewLifecycleOwner(), conversationsBottom -> dealWithPagination(conversationsBottom, FragmentMastodonTimeline.DIRECTION.BOTTOM, fetchingMissing, conversationToUpdate));
|
||||||
|
} else if (direction == FragmentMastodonTimeline.DIRECTION.TOP) {
|
||||||
|
timelinesVM.getConversations(conversationList, timelineParams)
|
||||||
|
.observe(getViewLifecycleOwner(), conversationsTop -> dealWithPagination(conversationsTop, FragmentMastodonTimeline.DIRECTION.TOP, fetchingMissing, conversationToUpdate));
|
||||||
|
} else if (direction == FragmentMastodonTimeline.DIRECTION.REFRESH) {
|
||||||
|
timelinesVM.getConversations(conversationList, timelineParams)
|
||||||
|
.observe(getViewLifecycleOwner(), conversationsRefresh -> {
|
||||||
|
if (conversationAdapter != null) {
|
||||||
|
dealWithPagination(conversationsRefresh, FragmentMastodonTimeline.DIRECTION.REFRESH, true, conversationToUpdate);
|
||||||
|
} else {
|
||||||
|
initializeConversationCommonView(conversationsRefresh);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
@ -74,8 +192,7 @@ public class FragmentMastodonConversation extends Fragment {
|
||||||
binding.recyclerView.setVisibility(View.GONE);
|
binding.recyclerView.setVisibility(View.GONE);
|
||||||
timelinesVM = new ViewModelProvider(FragmentMastodonConversation.this).get(TimelinesVM.class);
|
timelinesVM = new ViewModelProvider(FragmentMastodonConversation.this).get(TimelinesVM.class);
|
||||||
max_id = null;
|
max_id = null;
|
||||||
timelinesVM.getConversations(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, null, null, null, MastodonHelper.statusesPerCall(requireActivity()))
|
route(null, false);
|
||||||
.observe(getViewLifecycleOwner(), this::initializeConversationCommonView);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -85,71 +202,214 @@ public class FragmentMastodonConversation extends Fragment {
|
||||||
*/
|
*/
|
||||||
private void initializeConversationCommonView(final Conversations conversations) {
|
private void initializeConversationCommonView(final Conversations conversations) {
|
||||||
flagLoading = false;
|
flagLoading = false;
|
||||||
|
if (binding == null || !isAdded() || getActivity() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
binding.loader.setVisibility(View.GONE);
|
binding.loader.setVisibility(View.GONE);
|
||||||
binding.noAction.setVisibility(View.GONE);
|
binding.noAction.setVisibility(View.GONE);
|
||||||
if (conversationAdapter != null && this.conversations != null) {
|
binding.swipeContainer.setRefreshing(false);
|
||||||
int size = this.conversations.size();
|
binding.swipeContainer.setOnRefreshListener(() -> {
|
||||||
this.conversations.clear();
|
binding.swipeContainer.setRefreshing(true);
|
||||||
this.conversations = new ArrayList<>();
|
flagLoading = false;
|
||||||
|
route(FragmentMastodonTimeline.DIRECTION.REFRESH, true);
|
||||||
|
});
|
||||||
|
if (conversations == null || conversations.conversations == null || conversations.conversations.size() == 0) {
|
||||||
|
binding.noAction.setVisibility(View.VISIBLE);
|
||||||
|
binding.recyclerView.setVisibility(View.GONE);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
binding.noAction.setVisibility(View.GONE);
|
||||||
|
binding.recyclerView.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
flagLoading = conversations.pagination.max_id == null;
|
||||||
|
|
||||||
|
if (conversationAdapter != null && conversationList != null) {
|
||||||
|
int size = conversationList.size();
|
||||||
|
conversationList.clear();
|
||||||
|
conversationList = new ArrayList<>();
|
||||||
conversationAdapter.notifyItemRangeRemoved(0, size);
|
conversationAdapter.notifyItemRangeRemoved(0, size);
|
||||||
}
|
}
|
||||||
binding.recyclerView.setVisibility(View.VISIBLE);
|
if (conversationList == null) {
|
||||||
this.conversations = conversations.conversations;
|
conversationList = new ArrayList<>();
|
||||||
conversationAdapter = new ConversationAdapter(this.conversations);
|
}
|
||||||
//conversationAdapter.itemListener = currentFragment;
|
conversationList.addAll(conversations.conversations);
|
||||||
binding.swipeContainer.setRefreshing(false);
|
|
||||||
LinearLayoutManager mLayoutManager = new LinearLayoutManager(requireActivity());
|
if (max_id == null || (conversations.pagination.max_id != null && conversations.pagination.max_id.compareTo(max_id) < 0)) {
|
||||||
|
max_id = conversations.pagination.max_id;
|
||||||
|
}
|
||||||
|
if (min_id == null || (conversations.pagination.min_id != null && conversations.pagination.min_id.compareTo(min_id) > 0)) {
|
||||||
|
min_id = conversations.pagination.min_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
conversationAdapter = new ConversationAdapter(conversationList);
|
||||||
|
conversationAdapter.fetchMoreCallBack = this;
|
||||||
|
mLayoutManager = new LinearLayoutManager(requireActivity());
|
||||||
binding.recyclerView.setLayoutManager(mLayoutManager);
|
binding.recyclerView.setLayoutManager(mLayoutManager);
|
||||||
binding.recyclerView.setAdapter(conversationAdapter);
|
binding.recyclerView.setAdapter(conversationAdapter);
|
||||||
max_id = conversations.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();
|
||||||
int totalItemCount = mLayoutManager.getItemCount();
|
int totalItemCount = mLayoutManager.getItemCount();
|
||||||
|
|
||||||
if (firstVisibleItem + visibleItemCount == totalItemCount) {
|
if (firstVisibleItem + visibleItemCount == totalItemCount) {
|
||||||
if (!flagLoading) {
|
if (!flagLoading) {
|
||||||
flagLoading = true;
|
flagLoading = true;
|
||||||
binding.loadingNextElements.setVisibility(View.VISIBLE);
|
binding.loadingNextElements.setVisibility(View.VISIBLE);
|
||||||
timelinesVM.getConversations(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()))
|
route(FragmentMastodonTimeline.DIRECTION.BOTTOM, false);
|
||||||
.observe(FragmentMastodonConversation.this, fetched_conversations -> {
|
|
||||||
binding.loadingNextElements.setVisibility(View.GONE);
|
|
||||||
if (currentFragment.conversations != null && fetched_conversations != null) {
|
|
||||||
int startId = 0;
|
|
||||||
flagLoading = fetched_conversations.pagination.max_id == null;
|
|
||||||
//There are some statuses present in the timeline
|
|
||||||
if (currentFragment.conversations.size() > 0) {
|
|
||||||
startId = currentFragment.conversations.size();
|
|
||||||
}
|
|
||||||
currentFragment.conversations.addAll(fetched_conversations.conversations);
|
|
||||||
max_id = fetched_conversations.pagination.max_id;
|
|
||||||
conversationAdapter.notifyItemRangeInserted(startId, fetched_conversations.conversations.size());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} 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);
|
||||||
|
route(FragmentMastodonTimeline.DIRECTION.TOP, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
binding.swipeContainer.setOnRefreshListener(() -> {
|
|
||||||
if (this.conversations != null && this.conversations.size() > 0) {
|
}
|
||||||
binding.swipeContainer.setRefreshing(true);
|
|
||||||
max_id = null;
|
|
||||||
|
/**
|
||||||
|
* Update view and pagination when scrolling down
|
||||||
|
*
|
||||||
|
* @param fetched_conversations Conversations
|
||||||
|
*/
|
||||||
|
private synchronized void dealWithPagination(Conversations fetched_conversations, FragmentMastodonTimeline.DIRECTION direction, boolean fetchingMissing, Conversation conversationToUpdate) {
|
||||||
|
if (binding == null || !isAdded() || getActivity() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
binding.swipeContainer.setRefreshing(false);
|
||||||
|
binding.loadingNextElements.setVisibility(View.GONE);
|
||||||
flagLoading = false;
|
flagLoading = false;
|
||||||
timelinesVM.getConversations(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, null, null, null, MastodonHelper.statusesPerCall(requireActivity()))
|
if (conversationList != null && fetched_conversations != null && fetched_conversations.conversations != null && fetched_conversations.conversations.size() > 0) {
|
||||||
.observe(FragmentMastodonConversation.this, this::initializeConversationCommonView);
|
try {
|
||||||
|
if (conversationToUpdate != null) {
|
||||||
|
new Thread(() -> {
|
||||||
|
StatusCache statusCache = new StatusCache();
|
||||||
|
statusCache.instance = BaseMainActivity.currentInstance;
|
||||||
|
statusCache.user_id = BaseMainActivity.currentUserID;
|
||||||
|
conversationToUpdate.isFetchMore = false;
|
||||||
|
statusCache.conversation = conversationToUpdate;
|
||||||
|
statusCache.status_id = conversationToUpdate.id;
|
||||||
|
try {
|
||||||
|
new StatusCache(requireActivity()).updateIfExists(statusCache);
|
||||||
|
} catch (DBException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
|
flagLoading = fetched_conversations.pagination.max_id == null;
|
||||||
|
binding.noAction.setVisibility(View.GONE);
|
||||||
|
//Update the timeline with new statuses
|
||||||
|
updateConversationListWith(fetched_conversations.conversations);
|
||||||
|
if (direction == FragmentMastodonTimeline.DIRECTION.TOP && fetchingMissing) {
|
||||||
|
binding.recyclerView.scrollToPosition(getPosition(fetched_conversations.conversations.get(fetched_conversations.conversations.size() - 1)) + 1);
|
||||||
}
|
}
|
||||||
|
if (!fetchingMissing) {
|
||||||
|
if (fetched_conversations.pagination.max_id == null) {
|
||||||
|
flagLoading = true;
|
||||||
|
} else if (max_id == null || fetched_conversations.pagination.max_id.compareTo(max_id) < 0) {
|
||||||
|
max_id = fetched_conversations.pagination.max_id;
|
||||||
|
}
|
||||||
|
if (min_id == null || (fetched_conversations.pagination.min_id != null && fetched_conversations.pagination.min_id.compareTo(min_id) > 0)) {
|
||||||
|
min_id = fetched_conversations.pagination.min_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (direction == FragmentMastodonTimeline.DIRECTION.BOTTOM) {
|
||||||
|
flagLoading = true;
|
||||||
|
}
|
||||||
|
if (direction == FragmentMastodonTimeline.DIRECTION.SCROLL_TOP) {
|
||||||
|
binding.recyclerView.scrollToPosition(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the position of the convnersation in the ArrayList
|
||||||
|
*
|
||||||
|
* @param conversation - Conversation to fetch
|
||||||
|
* @return position or -1 if not found
|
||||||
|
*/
|
||||||
|
private int getPosition(Conversation conversation) {
|
||||||
|
int position = 0;
|
||||||
|
boolean found = false;
|
||||||
|
for (Conversation _conversation : conversationList) {
|
||||||
|
if (conversation != null && _conversation.id.compareTo(conversation.id) == 0) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
position++;
|
||||||
|
}
|
||||||
|
return found ? position : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the timeline with received Conversations
|
||||||
|
*
|
||||||
|
* @param conversationsReceived - List<Conversation> Conversation received
|
||||||
|
*/
|
||||||
|
private void updateConversationListWith(List<Conversation> conversationsReceived) {
|
||||||
|
if (conversationsReceived != null && conversationsReceived.size() > 0) {
|
||||||
|
for (Conversation conversationReceived : conversationsReceived) {
|
||||||
|
int position = 0;
|
||||||
|
//We loop through messages already in the timeline
|
||||||
|
if (conversationList != null) {
|
||||||
|
conversationAdapter.notifyItemRangeChanged(0, conversationList.size());
|
||||||
|
for (Conversation conversationsAlreadyPresent : conversationList) {
|
||||||
|
//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 (conversationReceived.id.compareTo(conversationsAlreadyPresent.id) > 0) {
|
||||||
|
if (!conversationList.contains(conversationReceived)) {
|
||||||
|
conversationList.add(position, conversationReceived);
|
||||||
|
conversationAdapter.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 == conversationList.size() && !conversationList.contains(conversationReceived)) {
|
||||||
|
conversationList.add(position, conversationReceived);
|
||||||
|
conversationAdapter.notifyItemInserted(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void scrollToTop() {
|
public void scrollToTop() {
|
||||||
binding.recyclerView.scrollToPosition(0);
|
binding.recyclerView.scrollToPosition(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClickMinId(String min_id, Conversation conversationToUpdate) {
|
||||||
|
min_id_fetch_more = min_id;
|
||||||
|
route(FragmentMastodonTimeline.DIRECTION.TOP, true, conversationToUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClickMaxId(String max_id, Conversation conversationToUpdate) {
|
||||||
|
max_id_fetch_more = max_id;
|
||||||
|
route(FragmentMastodonTimeline.DIRECTION.BOTTOM, true, conversationToUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -88,7 +88,6 @@ public class FragmentMastodonNotification extends Fragment implements Notificati
|
||||||
};
|
};
|
||||||
private String max_id, min_id, min_id_fetch_more, max_id_fetch_more;
|
private String max_id, min_id, min_id_fetch_more, max_id_fetch_more;
|
||||||
private LinearLayoutManager mLayoutManager;
|
private LinearLayoutManager mLayoutManager;
|
||||||
private ArrayList<String> idOfAddedNotifications;
|
|
||||||
private NotificationTypeEnum notificationType;
|
private NotificationTypeEnum notificationType;
|
||||||
private boolean aggregateNotification;
|
private boolean aggregateNotification;
|
||||||
|
|
||||||
|
@ -121,7 +120,7 @@ public class FragmentMastodonNotification extends Fragment implements Notificati
|
||||||
int position = 0;
|
int position = 0;
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
for (Notification _notification : notificationList) {
|
for (Notification _notification : notificationList) {
|
||||||
if (_notification.status != null && _notification.id.compareTo(notification.id) == 0) {
|
if (_notification != null && _notification.id.compareTo(notification.id) == 0) {
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -134,7 +133,6 @@ public class FragmentMastodonNotification extends Fragment implements Notificati
|
||||||
ViewGroup container, Bundle savedInstanceState) {
|
ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
|
||||||
flagLoading = false;
|
flagLoading = false;
|
||||||
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) {
|
||||||
|
@ -142,8 +140,6 @@ public class FragmentMastodonNotification extends Fragment implements Notificati
|
||||||
}
|
}
|
||||||
aggregateNotification = false;
|
aggregateNotification = false;
|
||||||
binding.getRoot().setBackgroundColor(ThemeHelper.getBackgroundColor(requireActivity()));
|
binding.getRoot().setBackgroundColor(ThemeHelper.getBackgroundColor(requireActivity()));
|
||||||
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
|
|
||||||
String excludedCategories = sharedpreferences.getString(getString(R.string.SET_EXCLUDED_NOTIFICATIONS_TYPE) + BaseMainActivity.currentUserID + BaseMainActivity.currentInstance, null);
|
|
||||||
int c1 = getResources().getColor(R.color.cyanea_accent_reference);
|
int c1 = getResources().getColor(R.color.cyanea_accent_reference);
|
||||||
binding.swipeContainer.setProgressBackgroundColorSchemeColor(getResources().getColor(R.color.cyanea_primary_reference));
|
binding.swipeContainer.setProgressBackgroundColorSchemeColor(getResources().getColor(R.color.cyanea_primary_reference));
|
||||||
binding.swipeContainer.setColorSchemeColors(
|
binding.swipeContainer.setColorSchemeColors(
|
||||||
|
@ -222,9 +218,7 @@ public class FragmentMastodonNotification extends Fragment implements Notificati
|
||||||
binding.noAction.setVisibility(View.GONE);
|
binding.noAction.setVisibility(View.GONE);
|
||||||
binding.recyclerView.setVisibility(View.VISIBLE);
|
binding.recyclerView.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
for (Notification notification : notifications.notifications) {
|
|
||||||
idOfAddedNotifications.add(notification.id);
|
|
||||||
}
|
|
||||||
flagLoading = notifications.pagination.max_id == null;
|
flagLoading = notifications.pagination.max_id == null;
|
||||||
if (aggregateNotification) {
|
if (aggregateNotification) {
|
||||||
notifications.notifications = aggregateNotifications(notifications.notifications);
|
notifications.notifications = aggregateNotifications(notifications.notifications);
|
||||||
|
@ -455,6 +449,7 @@ public class FragmentMastodonNotification extends Fragment implements Notificati
|
||||||
StatusCache statusCache = new StatusCache();
|
StatusCache statusCache = new StatusCache();
|
||||||
statusCache.instance = BaseMainActivity.currentInstance;
|
statusCache.instance = BaseMainActivity.currentInstance;
|
||||||
statusCache.user_id = BaseMainActivity.currentUserID;
|
statusCache.user_id = BaseMainActivity.currentUserID;
|
||||||
|
notificationToUpdate.isFetchMore = false;
|
||||||
statusCache.notification = notificationToUpdate;
|
statusCache.notification = notificationToUpdate;
|
||||||
statusCache.status_id = notificationToUpdate.id;
|
statusCache.status_id = notificationToUpdate.id;
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -396,6 +396,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
||||||
StatusCache statusCache = new StatusCache();
|
StatusCache statusCache = new StatusCache();
|
||||||
statusCache.instance = BaseMainActivity.currentInstance;
|
statusCache.instance = BaseMainActivity.currentInstance;
|
||||||
statusCache.user_id = BaseMainActivity.currentUserID;
|
statusCache.user_id = BaseMainActivity.currentUserID;
|
||||||
|
statusToUpdate.isFetchMore = false;
|
||||||
statusCache.status = statusToUpdate;
|
statusCache.status = statusToUpdate;
|
||||||
statusCache.status_id = statusToUpdate.id;
|
statusCache.status_id = statusToUpdate.id;
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -115,6 +115,7 @@ public class NotificationsVM extends AndroidViewModel {
|
||||||
notifications.pagination = MastodonHelper.getPagination(notificationsResponse.headers());
|
notifications.pagination = MastodonHelper.getPagination(notificationsResponse.headers());
|
||||||
|
|
||||||
if (notifications.notifications != null && notifications.notifications.size() > 0) {
|
if (notifications.notifications != null && notifications.notifications.size() > 0) {
|
||||||
|
addFetchMoreNotifications(notifications.notifications, notificationList, timelineParams);
|
||||||
for (Notification notification : notifications.notifications) {
|
for (Notification notification : notifications.notifications) {
|
||||||
StatusCache statusCacheDAO = new StatusCache(getApplication().getApplicationContext());
|
StatusCache statusCacheDAO = new StatusCache(getApplication().getApplicationContext());
|
||||||
StatusCache statusCache = new StatusCache();
|
StatusCache statusCache = new StatusCache();
|
||||||
|
@ -130,7 +131,6 @@ public class NotificationsVM extends AndroidViewModel {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addFetchMoreNotifications(notifications.notifications, notificationList, timelineParams);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
|
@ -363,6 +363,27 @@ public class TimelinesVM extends AndroidViewModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void addFetchMoreConversation(List<Conversation> conversationList, List<Conversation> timelineConversations, TimelineParams timelineParams) throws DBException {
|
||||||
|
if (conversationList != null && conversationList.size() > 0 && timelineConversations != null && timelineConversations.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 (conversationList.get(conversationList.size() - 1).id.compareToIgnoreCase(timelineConversations.get(0).id) > 0) {
|
||||||
|
conversationList.get(conversationList.size() - 1).isFetchMore = true;
|
||||||
|
conversationList.get(conversationList.size() - 1).positionFetchMore = Conversation.PositionFetchMore.TOP;
|
||||||
|
}
|
||||||
|
} else if (timelineParams.direction == FragmentMastodonTimeline.DIRECTION.TOP && timelineParams.fetchingMissing) {
|
||||||
|
if (!timelineConversations.contains(conversationList.get(0))) {
|
||||||
|
conversationList.get(0).isFetchMore = true;
|
||||||
|
conversationList.get(0).positionFetchMore = Conversation.PositionFetchMore.BOTTOM;
|
||||||
|
}
|
||||||
|
} else if (timelineParams.direction == FragmentMastodonTimeline.DIRECTION.BOTTOM && timelineParams.fetchingMissing) {
|
||||||
|
if (!timelineConversations.contains(conversationList.get(conversationList.size() - 1))) {
|
||||||
|
conversationList.get(conversationList.size() - 1).isFetchMore = true;
|
||||||
|
conversationList.get(conversationList.size() - 1).positionFetchMore = Conversation.PositionFetchMore.TOP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public LiveData<Statuses> getTimeline(List<Status> timelineStatuses, TimelineParams timelineParams) {
|
public LiveData<Statuses> getTimeline(List<Status> timelineStatuses, TimelineParams timelineParams) {
|
||||||
|
|
||||||
|
@ -397,6 +418,7 @@ public class TimelinesVM extends AndroidViewModel {
|
||||||
statuses.statuses = TimelineHelper.filterStatus(getApplication().getApplicationContext(), statusList, timelineParams.type);
|
statuses.statuses = TimelineHelper.filterStatus(getApplication().getApplicationContext(), statusList, timelineParams.type);
|
||||||
statuses.pagination = MastodonHelper.getPagination(timelineResponse.headers());
|
statuses.pagination = MastodonHelper.getPagination(timelineResponse.headers());
|
||||||
if (statusList != null && statusList.size() > 0) {
|
if (statusList != null && statusList.size() > 0) {
|
||||||
|
addFetchMore(statusList, timelineStatuses, timelineParams);
|
||||||
for (Status status : statuses.statuses) {
|
for (Status status : statuses.statuses) {
|
||||||
StatusCache statusCacheDAO = new StatusCache(getApplication().getApplicationContext());
|
StatusCache statusCacheDAO = new StatusCache(getApplication().getApplicationContext());
|
||||||
StatusCache statusCache = new StatusCache();
|
StatusCache statusCache = new StatusCache();
|
||||||
|
@ -411,7 +433,6 @@ public class TimelinesVM extends AndroidViewModel {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addFetchMore(statusList, timelineStatuses, timelineParams);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -476,28 +497,46 @@ public class TimelinesVM extends AndroidViewModel {
|
||||||
return statusDraftListMutableLiveData;
|
return statusDraftListMutableLiveData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show conversations
|
* Show conversations
|
||||||
*
|
*
|
||||||
* @return {@link LiveData} containing a {@link Conversations}
|
* @return {@link LiveData} containing a {@link Conversations}
|
||||||
*/
|
*/
|
||||||
public LiveData<Conversations> getConversations(@NonNull String instance, String token,
|
public LiveData<Conversations> getConversations(List<Conversation> conversationsTimeline, TimelineParams timelineParams) {
|
||||||
String maxId,
|
|
||||||
String sinceId,
|
|
||||||
String minId,
|
|
||||||
int limit) {
|
|
||||||
conversationListMutableLiveData = new MutableLiveData<>();
|
conversationListMutableLiveData = new MutableLiveData<>();
|
||||||
MastodonTimelinesService mastodonTimelinesService = init(instance);
|
MastodonTimelinesService mastodonTimelinesService = init(timelineParams.instance);
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
Conversations conversations = null;
|
Conversations conversations = null;
|
||||||
Call<List<Conversation>> conversationsCall = mastodonTimelinesService.getConversations(token, maxId, sinceId, minId, limit);
|
Call<List<Conversation>> conversationsCall = mastodonTimelinesService.getConversations(timelineParams.token, timelineParams.maxId, timelineParams.sinceId, timelineParams.minId, timelineParams.limit);
|
||||||
if (conversationsCall != null) {
|
if (conversationsCall != null) {
|
||||||
conversations = new Conversations();
|
conversations = new Conversations();
|
||||||
try {
|
try {
|
||||||
Response<List<Conversation>> conversationsResponse = conversationsCall.execute();
|
Response<List<Conversation>> conversationsResponse = conversationsCall.execute();
|
||||||
if (conversationsResponse.isSuccessful()) {
|
if (conversationsResponse.isSuccessful()) {
|
||||||
conversations.conversations = conversationsResponse.body();
|
List<Conversation> conversationList = conversationsResponse.body();
|
||||||
|
conversations.conversations = conversationList;
|
||||||
conversations.pagination = MastodonHelper.getPagination(conversationsResponse.headers());
|
conversations.pagination = MastodonHelper.getPagination(conversationsResponse.headers());
|
||||||
|
|
||||||
|
if (conversationList != null && conversationList.size() > 0) {
|
||||||
|
|
||||||
|
addFetchMoreConversation(conversationList, conversationsTimeline, timelineParams);
|
||||||
|
|
||||||
|
for (Conversation conversation : conversations.conversations) {
|
||||||
|
StatusCache statusCacheDAO = new StatusCache(getApplication().getApplicationContext());
|
||||||
|
StatusCache statusCache = new StatusCache();
|
||||||
|
statusCache.instance = timelineParams.instance;
|
||||||
|
statusCache.user_id = timelineParams.userId;
|
||||||
|
statusCache.conversation = conversation;
|
||||||
|
statusCache.type = Timeline.TimeLineEnum.CONVERSATION;
|
||||||
|
statusCache.status_id = conversation.id;
|
||||||
|
try {
|
||||||
|
statusCacheDAO.insertOrUpdate(statusCache, timelineParams.slug);
|
||||||
|
} catch (DBException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
@ -512,6 +551,36 @@ public class TimelinesVM extends AndroidViewModel {
|
||||||
return conversationListMutableLiveData;
|
return conversationListMutableLiveData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public LiveData<Conversations> getConversationsCache(List<Conversation> timelineConversations, TimelineParams timelineParams) {
|
||||||
|
conversationListMutableLiveData = new MutableLiveData<>();
|
||||||
|
new Thread(() -> {
|
||||||
|
StatusCache statusCacheDAO = new StatusCache(getApplication().getApplicationContext());
|
||||||
|
Conversations conversations = null;
|
||||||
|
try {
|
||||||
|
conversations = statusCacheDAO.getConversations(timelineParams.instance, timelineParams.userId, timelineParams.maxId, timelineParams.minId, timelineParams.sinceId);
|
||||||
|
if (conversations != null) {
|
||||||
|
if (conversations.conversations != null && conversations.conversations.size() > 0) {
|
||||||
|
for (Conversation conversation : conversations.conversations) {
|
||||||
|
conversation.cached = true;
|
||||||
|
}
|
||||||
|
addFetchMoreConversation(conversations.conversations, timelineConversations, timelineParams);
|
||||||
|
conversations.pagination = new Pagination();
|
||||||
|
conversations.pagination.min_id = conversations.conversations.get(0).id;
|
||||||
|
conversations.pagination.max_id = conversations.conversations.get(conversations.conversations.size() - 1).id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (DBException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||||
|
Conversations finalConversations = conversations;
|
||||||
|
Runnable myRunnable = () -> conversationListMutableLiveData.setValue(finalConversations);
|
||||||
|
mainHandler.post(myRunnable);
|
||||||
|
}).start();
|
||||||
|
return conversationListMutableLiveData;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove conversation
|
* Remove conversation
|
||||||
*
|
*
|
||||||
|
|
|
@ -32,8 +32,20 @@
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
android:padding="6dp">
|
android:padding="6dp">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/cache_indicator"
|
||||||
|
android:layout_width="16sp"
|
||||||
|
android:layout_height="16sp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
android:src="@drawable/ic_baseline_cached_24"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<HorizontalScrollView
|
<HorizontalScrollView
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
@ -104,6 +116,12 @@
|
||||||
tools:ignore="RtlSymmetry" />
|
tools:ignore="RtlSymmetry" />
|
||||||
|
|
||||||
</HorizontalScrollView>
|
</HorizontalScrollView>
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/layout_fetch_more"
|
||||||
|
layout="@layout/drawer_fetch_more"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible" />
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
Loading…
Reference in a new issue