Commit 204cb84d authored by Thomas's avatar Thomas
Browse files

Implement cache for conversations

parent c4dbe80a
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
package app.fedilab.android.client.entities.api;

import androidx.annotation.Nullable;

import com.google.gson.annotations.SerializedName;

import java.util.List;
@@ -27,4 +29,25 @@ public class Conversation {
    public List<Account> accounts;
    @SerializedName("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
    }

}
+1 −1
Original line number Diff line number Diff line
@@ -58,7 +58,7 @@ public class Notification {

    public transient List<Notification> relatedNotifications;
    public boolean isFetchMore;
    public boolean isFetchMoreHidden = false;


    /**
     * Serialized a list of Notification class
+136 −1
Original line number Diff line number Diff line
@@ -28,6 +28,8 @@ import java.util.Date;
import java.util.List;

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.Notifications;
import app.fedilab.android.client.entities.api.Pagination;
@@ -57,6 +59,8 @@ public class StatusCache {
    public Status status;
    @SerializedName("notification")
    public Notification notification;
    @SerializedName("conversation")
    public Conversation conversation;
    @SerializedName("created_at")
    public Date created_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
     *
@@ -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
     *
@@ -238,6 +274,9 @@ public class StatusCache {
        if (statusCache.notification != null) {
            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()));
        //Inserts token
        try {
@@ -268,6 +307,9 @@ public class StatusCache {
        if (statusCache.notification != null) {
            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()));
        //Inserts token
        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
     *
@@ -495,7 +573,7 @@ public class StatusCache {
     * Convert a cursor to list of notifications
     *
     * @param c Cursor
     * @return List<Status>
     * @return List<Notification>
     */
    private List<Notification> cursorToListOfNotifications(Cursor c) {
        //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
     *
@@ -537,6 +637,30 @@ public class StatusCache {
        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
     *
@@ -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 {
        @SerializedName("ASC")
        ASC("ASC"),
+2 −0
Original line number Diff line number Diff line
@@ -358,6 +358,8 @@ public class Timeline {
        DIRECT("DIRECT"),
        @SerializedName("NOTIFICATION")
        NOTIFICATION("NOTIFICATION"),
        @SerializedName("CONVERSATION")
        CONVERSATION("CONVERSATION"),
        @SerializedName("LOCAL")
        LOCAL("LOCAL"),
        @SerializedName("PUBLIC")
+41 −0
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ public class ConversationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
    private final List<Conversation> conversationList;
    private Context context;
    private boolean isExpended = false;
    public FetchMoreCallBack fetchMoreCallBack;

    public ConversationAdapter(List<Conversation> conversations) {
        if (conversations == null) {
@@ -127,6 +128,42 @@ public class ConversationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
        if (conversation.last_status == null) {
            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 -----
        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()) {
@@ -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);
    }
}
 No newline at end of file
Loading