Commit e1c833fc authored by Thomas's avatar Thomas
Browse files

Records home logs

parent 0dc1fcf3
Loading
Loading
Loading
Loading
+246 −0
Original line number Diff line number Diff line
package app.fedilab.android.mastodon.client.entities.app;
/* Copyright 2023 Thomas Schneider
 *
 * This file is a part of Fedilab
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the
 * GNU General Public License as published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 *
 * Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with Fedilab; if not,
 * see <http://www.gnu.org/licenses>. */

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import com.google.gson.annotations.SerializedName;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.mastodon.exception.DBException;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.sqlite.Sqlite;

public class TimelineCacheLogs {

    private final SQLiteDatabase db;
    @SerializedName("id")
    public long id;
    @SerializedName("user_id")
    public String user_id;
    @SerializedName("instance")
    public String instance;
    @SerializedName("slug")
    public String slug;
    @SerializedName("type")
    public Timeline.TimeLineEnum type;
    @SerializedName("created_at")
    public Date created_at;
    @SerializedName("fetched")
    public int fetched;
    @SerializedName("failed")
    public int failed;
    @SerializedName("inserted")
    public int inserted;
    @SerializedName("updated")
    public int updated;
    @SerializedName("frequency")
    public int frequency;
    private Context context;

    public TimelineCacheLogs() {
        db = null;
    }


    public TimelineCacheLogs(Context context) {
        //Creation of the DB with tables
        this.context = context;
        this.db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
    }


    /**
     * get all cache timelineCacheLogs for home
     *
     * @param baseAccount Status {@link BaseAccount}
     * @return List<Status>
     * @throws DBException Exception
     */
    public List<TimelineCacheLogs> getHome(BaseAccount baseAccount) throws DBException {
        if (db == null) {
            throw new DBException("db is null. Wrong initialization.");
        }
        String selection = Sqlite.COL_INSTANCE + "='" + baseAccount.instance + "' AND " + Sqlite.COL_USER_ID + "= '" + baseAccount.user_id + "' AND " + Sqlite.COL_SLUG + "= '" + Timeline.TimeLineEnum.HOME.getValue() + "' ";
        try {
            Cursor c = db.query(Sqlite.TABLE_TIMELINE_CACHE_LOGS, null, selection, null, Sqlite.COL_STATUS_ID, null, Sqlite.COL_STATUS_ID + " ASC", null);
            return cursorToListOfStatuses(c);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * Insert a status in db
     *
     * @param timelineCacheLogs {@link TimelineCacheLogs}
     * @return long - db id
     * @throws DBException exception with database
     */
    public long insert(TimelineCacheLogs timelineCacheLogs) throws DBException {
        if (db == null) {
            throw new DBException("db is null. Wrong initialization.");
        }
        ContentValues values = new ContentValues();
        values.put(Sqlite.COL_USER_ID, timelineCacheLogs.user_id);
        values.put(Sqlite.COL_INSTANCE, timelineCacheLogs.instance);
        values.put(Sqlite.COL_SLUG, timelineCacheLogs.slug);
        values.put(Sqlite.COL_TYPE, timelineCacheLogs.type.getValue());
        values.put(Sqlite.COL_CREATED_AT, Helper.dateToString(new Date()));
        values.put(Sqlite.COL_FAILED, timelineCacheLogs.failed);
        values.put(Sqlite.COL_FETCHED, timelineCacheLogs.fetched);
        values.put(Sqlite.COL_FREQUENCY, timelineCacheLogs.frequency);
        values.put(Sqlite.COL_INSERTED, timelineCacheLogs.inserted);
        values.put(Sqlite.COL_UPDATED, timelineCacheLogs.updated);
        //Inserts token
        try {
            return db.insertOrThrow(Sqlite.TABLE_TIMELINE_CACHE_LOGS, null, values);
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }


    /**
     * delete all cache for all account
     *
     * @return long - db id
     * @throws DBException exception with database
     */
    public long deleteForAllAccount() throws DBException {
        if (db == null) {
            throw new DBException("db is null. Wrong initialization.");
        }
        try {
            return db.delete(Sqlite.TABLE_TIMELINE_CACHE_LOGS, null, null);
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

    /**
     * delete all cache for all account after 7 days
     *
     * @return long - db id
     * @throws DBException exception with database
     */
    public long deleteForAllAccountAfter7Days() throws DBException {
        if (db == null) {
            throw new DBException("db is null. Wrong initialization.");
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date());
        cal.add(Calendar.DATE, -7);
        Date date = cal.getTime();
        String dateStr = Helper.dateToString(date);
        try {
            return db.delete(Sqlite.TABLE_TIMELINE_CACHE_LOGS, Sqlite.COL_CREATED_AT + " <  ?", new String[]{dateStr});
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

    /**
     * delete all cache for an slug
     *
     * @return long - db id
     * @throws DBException exception with database
     */
    public long deleteForSlug(String slug) throws DBException {
        if (db == null) {
            throw new DBException("db is null. Wrong initialization.");
        }
        try {
            return db.delete(Sqlite.TABLE_TIMELINE_CACHE_LOGS,
                    Sqlite.COL_SLUG + " = ? AND " + Sqlite.COL_USER_ID + " =  ? AND " + Sqlite.COL_INSTANCE + " =?",
                    new String[]{slug, MainActivity.currentUserID, MainActivity.currentInstance});
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }


    public int count(BaseAccount account) throws DBException {
        if (db == null) {
            throw new DBException("db is null. Wrong initialization.");
        }
        Cursor mCount = db.rawQuery("select count(*) from " + Sqlite.TABLE_TIMELINE_CACHE_LOGS
                + " where " + Sqlite.COL_INSTANCE + " = '" + account.instance + "' AND " + Sqlite.COL_USER_ID + " = '" + account.user_id + "'", null);
        mCount.moveToFirst();
        int count = mCount.getInt(0);
        mCount.close();
        return count;
    }

    /**
     * Convert a cursor to list of TimelineCacheLogs
     *
     * @param c Cursor
     * @return List<TimelineCacheLogs>
     */
    private List<TimelineCacheLogs> cursorToListOfStatuses(Cursor c) {
        //No element found
        if (c.getCount() == 0) {
            c.close();
            return null;
        }
        List<TimelineCacheLogs> timelineCacheLogsList = new ArrayList<>();
        while (c.moveToNext()) {
            TimelineCacheLogs timelineCacheLogs = convertCursorToTimelineCacheLogs(c);
            timelineCacheLogsList.add(timelineCacheLogs);
        }
        //Close the cursor
        c.close();
        return timelineCacheLogsList;
    }

    /**
     * Read cursor and hydrate without closing it
     *
     * @param c - Cursor
     * @return Timeline
     */
    private TimelineCacheLogs convertCursorToTimelineCacheLogs(Cursor c) {
        TimelineCacheLogs timelineCacheLogs = new TimelineCacheLogs();
        timelineCacheLogs.id = c.getInt(c.getColumnIndexOrThrow(Sqlite.COL_ID));
        timelineCacheLogs.type = Timeline.TimeLineEnum.valueOf(c.getString(c.getColumnIndexOrThrow(Sqlite.COL_TYPE)));
        timelineCacheLogs.instance = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_INSTANCE));
        timelineCacheLogs.user_id = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_USER_ID));
        timelineCacheLogs.slug = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_SLUG));
        timelineCacheLogs.created_at = Helper.stringToDate(context, c.getString(c.getColumnIndexOrThrow(Sqlite.COL_CREATED_AT)));
        timelineCacheLogs.failed = c.getInt(c.getColumnIndexOrThrow(Sqlite.COL_FAILED));
        timelineCacheLogs.fetched = c.getInt(c.getColumnIndexOrThrow(Sqlite.COL_FETCHED));
        timelineCacheLogs.inserted = c.getInt(c.getColumnIndexOrThrow(Sqlite.COL_INSERTED));
        timelineCacheLogs.updated = c.getInt(c.getColumnIndexOrThrow(Sqlite.COL_UPDATED));
        timelineCacheLogs.frequency = c.getInt(c.getColumnIndexOrThrow(Sqlite.COL_FREQUENCY));
        return timelineCacheLogs;
    }


}
+38 −17
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import java.util.List;
import java.util.concurrent.TimeUnit;

import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.mastodon.client.endpoints.MastodonTimelinesService;
import app.fedilab.android.mastodon.client.entities.api.Pagination;
import app.fedilab.android.mastodon.client.entities.api.Status;
@@ -48,6 +49,7 @@ import app.fedilab.android.mastodon.client.entities.app.Account;
import app.fedilab.android.mastodon.client.entities.app.BaseAccount;
import app.fedilab.android.mastodon.client.entities.app.StatusCache;
import app.fedilab.android.mastodon.client.entities.app.Timeline;
import app.fedilab.android.mastodon.client.entities.app.TimelineCacheLogs;
import app.fedilab.android.mastodon.exception.DBException;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.MastodonHelper;
@@ -161,16 +163,21 @@ public class FetchHomeWorker extends Worker {
                .getDefaultSharedPreferences(context);
        boolean fetch_home = prefs.getBoolean(context.getString(R.string.SET_FETCH_HOME) + account.user_id + account.instance, false);

        boolean failed = false;
        int fetched = 0, inserted = 0, updated = 0, frequency = 0;
        String timeRefresh = prefs.getString(context.getString(R.string.SET_FETCH_HOME_DELAY_VALUE) + MainActivity.currentUserID + MainActivity.currentInstance, "60");
        try {
            frequency = Integer.parseInt(timeRefresh);
        } catch (Exception ignored) {
        }
        if (fetch_home) {
            int max_calls = 5;
            int status_per_page = 80;
            int max_calls = 10;
            int status_per_page = 40;
            //Browse last 400 home messages
            boolean canContinue = true;
            int call = 0;
            String max_id = null;
            MastodonTimelinesService mastodonTimelinesService = init(account.instance);
            int insertValue = 0;
            StatusCache lastStatusCache = null;
            while (canContinue && call < max_calls) {
                Call<List<Status>> homeCall = mastodonTimelinesService.getHome(account.token, max_id, null, null, status_per_page, null);
                if (homeCall != null) {
@@ -178,6 +185,7 @@ public class FetchHomeWorker extends Worker {
                    if (homeResponse.isSuccessful()) {
                        List<Status> statusList = homeResponse.body();
                        if (statusList != null && statusList.size() > 0) {
                            fetched += statusList.size();
                            for (Status status : statusList) {
                                StatusCache statusCacheDAO = new StatusCache(getApplicationContext());
                                StatusCache statusCache = new StatusCache();
@@ -186,9 +194,13 @@ public class FetchHomeWorker extends Worker {
                                statusCache.status = status;
                                statusCache.type = Timeline.TimeLineEnum.HOME;
                                statusCache.status_id = status.id;
                                lastStatusCache = statusCache;
                                try {
                                    insertValue = statusCacheDAO.insertOrUpdate(statusCache, Timeline.TimeLineEnum.HOME.getValue());
                                    int insertOrUpdate = statusCacheDAO.insertOrUpdate(statusCache, Timeline.TimeLineEnum.HOME.getValue());
                                    if (insertOrUpdate == 1) {
                                        inserted++;
                                    } else {
                                        updated++;
                                    }
                                } catch (DBException e) {
                                    e.printStackTrace();
                                }
@@ -205,27 +217,36 @@ public class FetchHomeWorker extends Worker {
                        }
                    } else {
                        canContinue = false;
                        failed = true;
                    }
                } else {
                    canContinue = false;
                    failed = true;
                }
                //Pause between calls (1 second)
                try {
                    Thread.sleep(1000);
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                call++;
            }
            //insertValue is for last status and equals zero if updated or 1 if inserted
            if (lastStatusCache != null && insertValue == 1) { //Last inserted message was not in cache.
                StatusCache statusCacheDAO = new StatusCache(getApplicationContext());
                lastStatusCache.status.isFetchMore = true;
                lastStatusCache.status.positionFetchMore = Status.PositionFetchMore.TOP;
            TimelineCacheLogs timelineCacheLogs = new TimelineCacheLogs();
            timelineCacheLogs.frequency = frequency;
            timelineCacheLogs.fetched = fetched;
            timelineCacheLogs.failed = failed ? 1 : 0;
            timelineCacheLogs.updated = updated;
            timelineCacheLogs.inserted = inserted;
            timelineCacheLogs.slug = Timeline.TimeLineEnum.HOME.getValue();
            timelineCacheLogs.type = Timeline.TimeLineEnum.HOME;
            timelineCacheLogs.user_id = account.user_id;
            timelineCacheLogs.instance = account.instance;
            try {
                    statusCacheDAO.updateIfExists(lastStatusCache);
                new TimelineCacheLogs(context).insert(timelineCacheLogs);
            } catch (DBException e) {
                throw new RuntimeException(e);
            }
            }

        }
    }

+22 −2
Original line number Diff line number Diff line
@@ -23,7 +23,7 @@ import android.database.sqlite.SQLiteOpenHelper;
public class Sqlite extends SQLiteOpenHelper {


    public static final int DB_VERSION = 10;
    public static final int DB_VERSION = 11;
    public static final String DB_NAME = "fedilab_db";

    //Table of owned accounts
@@ -99,11 +99,13 @@ public class Sqlite extends SQLiteOpenHelper {
    public static final String COL_UPDATED = "UPDATED";
    public static final String COL_FAILED = "FAILED";
    public static final String COL_FREQUENCY = "FREQUENCY";
    public static final String COL_FETCHED_COUNT = "FETCHED_COUNT";
    public static final String COL_FETCHED = "FETCHED";

    public static final String TABLE_CACHE_TAGS = "CACHE_TAGS";
    public static final String COL_TAG = "TAG";

    public static final String TABLE_TIMELINE_CACHE_LOGS = "TIMELINE_CACHE_LOGS";


    private static final String CREATE_TABLE_USER_ACCOUNT = "CREATE TABLE " + TABLE_USER_ACCOUNT + " ("
            + COL_USER_ID + " TEXT NOT NULL, "
@@ -219,6 +221,21 @@ public class Sqlite extends SQLiteOpenHelper {
            + COL_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
            + COL_TAG + " TEXT NOT NULL)";

    private final String CREATE_TABLE_TIMELINE_CACHE_LOGS = "CREATE TABLE "
            + TABLE_TIMELINE_CACHE_LOGS + "("
            + COL_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
            + COL_INSTANCE + " TEXT NOT NULL, "
            + COL_USER_ID + " TEXT NOT NULL, "
            + COL_FETCHED + " INTEGER NOT NULL DEFAULT 0, "
            + COL_INSERTED + " INTEGER NOT NULL DEFAULT 0, "
            + COL_UPDATED + " INTEGER NOT NULL DEFAULT 0, "
            + COL_FAILED + " INTEGER NOT NULL DEFAULT 0, "
            + COL_FREQUENCY + " INTEGER NOT NULL DEFAULT 0, "
            + COL_SLUG + " TEXT NOT NULL, "
            + COL_TYPE + " TEXT NOT NULL, "
            + COL_CREATED_AT + " TEXT NOT NULL)";


    public static SQLiteDatabase db;
    private static Sqlite sInstance;

@@ -251,6 +268,7 @@ public class Sqlite extends SQLiteOpenHelper {
        db.execSQL(CREATE_TABLE_MUTED);
        db.execSQL(CREATE_TABLE_STORED_INSTANCES);
        db.execSQL(CREATE_TABLE_CACHE_TAGS);
        db.execSQL(CREATE_TABLE_TIMELINE_CACHE_LOGS);
    }

    @Override
@@ -281,6 +299,8 @@ public class Sqlite extends SQLiteOpenHelper {
                db.execSQL(CREATE_TABLE_STORED_INSTANCES);
            case 9:
                db.execSQL(CREATE_TABLE_CACHE_TAGS);
            case 10:
                db.execSQL(CREATE_TABLE_TIMELINE_CACHE_LOGS);
            default:
                break;
        }