summaryrefslogtreecommitdiffstats
path: root/app/src/main/java/app/fedilab/android/mastodon/client/entities/app/CachedBundle.java
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/app/fedilab/android/mastodon/client/entities/app/CachedBundle.java')
-rw-r--r--app/src/main/java/app/fedilab/android/mastodon/client/entities/app/CachedBundle.java413
1 files changed, 413 insertions, 0 deletions
diff --git a/app/src/main/java/app/fedilab/android/mastodon/client/entities/app/CachedBundle.java b/app/src/main/java/app/fedilab/android/mastodon/client/entities/app/CachedBundle.java
new file mode 100644
index 000000000..d414269cf
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/mastodon/client/entities/app/CachedBundle.java
@@ -0,0 +1,413 @@
+package app.fedilab.android.mastodon.client.entities.app;
+/* Copyright 2024 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 android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Parcel;
+import android.util.Base64;
+
+import com.google.gson.annotations.SerializedName;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+import app.fedilab.android.mastodon.client.entities.api.Account;
+import app.fedilab.android.mastodon.client.entities.api.Status;
+import app.fedilab.android.mastodon.exception.DBException;
+import app.fedilab.android.mastodon.helper.Helper;
+import app.fedilab.android.sqlite.Sqlite;
+
+/**
+ * Class that manages Bundle of Intent from database
+ */
+public class CachedBundle {
+
+ public String id;
+ public Bundle bundle;
+ public CacheType cacheType;
+ public String instance;
+ public String user_id;
+ public String target_id;
+ public Date created_at;
+
+ private SQLiteDatabase db;
+
+ private transient Context context;
+
+ public CachedBundle() {
+ }
+
+ public CachedBundle(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();
+ }
+
+
+ public void insertAccountBundle(Account account, BaseAccount currentUser) throws DBException {
+ if (db == null) {
+ throw new DBException("db is null. Wrong initialization.");
+ }
+ ContentValues valuesAccount = new ContentValues();
+ Bundle bundleAccount = new Bundle();
+ if (account != null) {
+ bundleAccount.putSerializable(Helper.ARG_ACCOUNT, account);
+ valuesAccount.put(Sqlite.COL_BUNDLE, serializeBundle(bundleAccount));
+ valuesAccount.put(Sqlite.COL_CREATED_AT, Helper.dateToString(new Date()));
+ valuesAccount.put(Sqlite.COL_TARGET_ID, account.id);
+ valuesAccount.put(Sqlite.COL_USER_ID, currentUser.user_id);
+ valuesAccount.put(Sqlite.COL_INSTANCE, currentUser.instance);
+ valuesAccount.put(Sqlite.COL_TYPE, CacheType.ACCOUNT.getValue());
+ removeIntent(currentUser, account.id);
+ db.insertOrThrow(Sqlite.TABLE_INTENT, null, valuesAccount);
+ }
+ }
+
+ /**
+ * Insert a bundle in db
+ *
+ * @param bundle {@link Bundle}
+ * @return long - db id
+ * @throws DBException exception with database
+ */
+ private long insertIntent(Bundle bundle, BaseAccount currentUser) throws DBException {
+ if (db == null) {
+ throw new DBException("db is null. Wrong initialization.");
+ }
+ ContentValues values = new ContentValues();
+ values.put(Sqlite.COL_BUNDLE, serializeBundle(bundle));
+ values.put(Sqlite.COL_CREATED_AT, Helper.dateToString(new Date()));
+ values.put(Sqlite.COL_TYPE, CacheType.ARGS.getValue());
+ if (bundle.containsKey(Helper.ARG_ACCOUNT) && currentUser != null) {
+ ContentValues valuesAccount = new ContentValues();
+ Bundle bundleAccount = new Bundle();
+ Account account = null;
+ try {
+ account = (Account) bundle.getSerializable(Helper.ARG_ACCOUNT);
+ }catch (ClassCastException ignored){}
+ if (account != null) {
+ bundleAccount.putSerializable(Helper.ARG_ACCOUNT, account);
+ valuesAccount.put(Sqlite.COL_BUNDLE, serializeBundle(bundleAccount));
+ valuesAccount.put(Sqlite.COL_CREATED_AT, Helper.dateToString(new Date()));
+ valuesAccount.put(Sqlite.COL_TARGET_ID, account.id);
+ valuesAccount.put(Sqlite.COL_USER_ID, currentUser.user_id);
+ valuesAccount.put(Sqlite.COL_INSTANCE, currentUser.instance);
+ valuesAccount.put(Sqlite.COL_TYPE, CacheType.ACCOUNT.getValue());
+ removeIntent(currentUser, account.id);
+ db.insertOrThrow(Sqlite.TABLE_INTENT, null, valuesAccount);
+ }
+ }
+ if (bundle.containsKey(Helper.ARG_STATUS) && currentUser != null) {
+ ContentValues valuesAccount = new ContentValues();
+ Bundle bundleStatus = new Bundle();
+ Status status = null;
+ try {
+ status = (Status) bundle.getSerializable(Helper.ARG_STATUS);
+ }catch (ClassCastException ignored){}
+ if (status != null) {
+ bundleStatus.putSerializable(Helper.ARG_STATUS, status);
+ valuesAccount.put(Sqlite.COL_BUNDLE, serializeBundle(bundleStatus));
+ valuesAccount.put(Sqlite.COL_CREATED_AT, Helper.dateToString(new Date()));
+ valuesAccount.put(Sqlite.COL_TARGET_ID, status.id);
+ valuesAccount.put(Sqlite.COL_USER_ID, currentUser.user_id);
+ valuesAccount.put(Sqlite.COL_INSTANCE, currentUser.instance);
+ valuesAccount.put(Sqlite.COL_TYPE, CacheType.STATUS.getValue());
+ removeIntent(currentUser, status.id);
+ db.insertOrThrow(Sqlite.TABLE_INTENT, null, valuesAccount);
+ }
+ }
+ try {
+ return db.insertOrThrow(Sqlite.TABLE_INTENT, null, values);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return -1;
+ }
+ }
+
+ public void getBundle(long id, BaseAccount Account, BundleCallback callback) {
+ new Thread(() -> {
+ Bundle bundle = null;
+ try {
+ CachedBundle cachedBundle = getCachedBundle(String.valueOf(id));
+ if (cachedBundle != null) {
+ bundle = cachedBundle.bundle;
+ if (bundle != null && bundle.containsKey(Helper.ARG_CACHED_ACCOUNT_ID)) {
+ Account cachedAccount = getCachedAccount(Account, bundle.getString(Helper.ARG_CACHED_ACCOUNT_ID));
+ if (cachedAccount != null) {
+ bundle.putSerializable(Helper.ARG_ACCOUNT, cachedAccount);
+ }
+ }
+ if (bundle != null && bundle.containsKey(Helper.ARG_CACHED_STATUS_ID)) {
+ Status cachedStatus = getCachedStatus(Account, bundle.getString(Helper.ARG_CACHED_STATUS_ID));
+ if (cachedStatus != null) {
+ bundle.putSerializable(Helper.ARG_STATUS, cachedStatus);
+ }
+ }
+ }
+ } catch (DBException ignored) {
+ }
+ if( bundle == null) {
+ bundle = new Bundle();
+ }
+ Handler mainHandler = new Handler(Looper.getMainLooper());
+ Bundle finalBundle = bundle;
+ Runnable myRunnable = () -> callback.get(finalBundle);
+ mainHandler.post(myRunnable);
+ }).start();
+ }
+
+ public void insertBundle(Bundle bundle, BaseAccount Account, BundleInsertCallback callback) {
+ new Thread(() -> {
+ long dbBundleId = -1;
+ try {
+ dbBundleId = insertIntent(bundle, Account);
+ } catch (DBException ignored) {
+ }
+ Handler mainHandler = new Handler(Looper.getMainLooper());
+ long finalDbBundleId = dbBundleId;
+ Runnable myRunnable = () -> callback.inserted(finalDbBundleId);
+ mainHandler.post(myRunnable);
+ }).start();
+ }
+
+ /**
+ * Returns a bundle by targeted account id
+ *
+ * @param target_id String
+ * @return Account {@link Account}
+ */
+ public Account getCachedAccount(BaseAccount account, String target_id) throws DBException {
+ if (db == null) {
+ throw new DBException("db is null. Wrong initialization.");
+ }
+ if (account == null || target_id == null) {
+ return null;
+ }
+ try {
+ Cursor c = db.query(Sqlite.TABLE_INTENT, null, Sqlite.COL_USER_ID + " = '" + account.user_id + "' AND "
+ + Sqlite.COL_INSTANCE + " = '" + account.instance + "' AND "
+ + Sqlite.COL_TYPE + " = '" + CacheType.ACCOUNT.getValue() + "' AND "
+ + Sqlite.COL_TARGET_ID + " = '" + target_id + "'", null, null, null, null, "1");
+ CachedBundle cachedBundle = cursorToCachedBundle(c);
+ if (cachedBundle != null && cachedBundle.bundle.containsKey(Helper.ARG_ACCOUNT)) {
+ return (Account) cachedBundle.bundle.getSerializable(Helper.ARG_ACCOUNT);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ return null;
+ }
+
+ /**
+ * Returns a bundle by targeted status id
+ *
+ * @param target_id String
+ * @return Status {@link Status}
+ */
+ private Status getCachedStatus(BaseAccount account, String target_id) throws DBException {
+ if (db == null) {
+ throw new DBException("db is null. Wrong initialization.");
+ }
+ if (account == null || target_id == null) {
+ return null;
+ }
+ try {
+ Cursor c = db.query(Sqlite.TABLE_INTENT, null, Sqlite.COL_USER_ID + " = '" + account.user_id + "' AND "
+ + Sqlite.COL_INSTANCE + " = '" + account.instance + "' AND "
+ + Sqlite.COL_TYPE + " = '" + CacheType.STATUS.getValue() + "' AND "
+ + Sqlite.COL_TARGET_ID + " = '" + target_id + "'", null, null, null, null, "1");
+ CachedBundle cachedBundle = cursorToCachedBundle(c);
+ if (cachedBundle != null && cachedBundle.bundle.containsKey(Helper.ARG_STATUS)) {
+ return (Status) cachedBundle.bundle.getSerializable(Helper.ARG_STATUS);
+ }
+ } catch (Exception e) {
+ return null;
+ }
+ return null;
+ }
+
+ /**
+ * Returns a bundle by its ID
+ *
+ * @param id String
+ * @return CachedBundle {@link CachedBundle}
+ */
+ private CachedBundle getCachedBundle(String id) throws DBException {
+ if (db == null) {
+ throw new DBException("db is null. Wrong initialization.");
+ }
+ try {
+ Cursor c = db.query(Sqlite.TABLE_INTENT, null, Sqlite.COL_ID + " = \"" + id + "\"", null, null, null, null, "1");
+ return cursorToCachedBundle(c);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * Remove a bundle from db
+ */
+ public void deleteOldIntent() 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, -1);
+ Date date = cal.getTime();
+ String dateStr = Helper.dateToString(date);
+ try {
+ db.delete(Sqlite.TABLE_INTENT, Sqlite.COL_CREATED_AT + " < ?", new String[]{dateStr});
+ }catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Remove a bundle from db
+ */
+ private void removeIntent(BaseAccount account, String target_id) throws DBException {
+ if (db == null) {
+ throw new DBException("db is null. Wrong initialization.");
+ }
+ if (account == null || target_id == null) {
+ return;
+ }
+ db.delete(Sqlite.TABLE_INTENT, Sqlite.COL_USER_ID + " = '" + account.user_id + "' AND "
+ + Sqlite.COL_INSTANCE + " = '" + account.instance + "' AND "
+ + Sqlite.COL_TARGET_ID + " = '" + target_id + "'", null);
+ }
+
+ /***
+ * Method to hydrate an CachedBundle from database
+ * @param c Cursor
+ * @return CachedBundle {@link CachedBundle}
+ */
+ private CachedBundle cursorToCachedBundle(Cursor c) {
+ //No element found
+ if (c.getCount() == 0) {
+ c.close();
+ return null;
+ }
+ //Take the first element
+ c.moveToFirst();
+ //New user
+ CachedBundle account = convertCursorToCachedBundle(c);
+ //Close the cursor
+ c.close();
+ return account;
+ }
+
+ /**
+ * Read cursor and hydrate without closing it
+ *
+ * @param c - Cursor
+ * @return Account
+ */
+ private CachedBundle convertCursorToCachedBundle(Cursor c) {
+ CachedBundle cachedBundle = new CachedBundle();
+ cachedBundle.id = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_ID));
+ cachedBundle.bundle = deserializeBundle(c.getString(c.getColumnIndexOrThrow(Sqlite.COL_BUNDLE)));
+ cachedBundle.user_id = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_USER_ID));
+ cachedBundle.instance = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_INSTANCE));
+ cachedBundle.target_id = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_TARGET_ID));
+ cachedBundle.cacheType = CacheType.valueOf(c.getString(c.getColumnIndexOrThrow(Sqlite.COL_TYPE)));
+ cachedBundle.created_at = Helper.stringToDate(context, c.getString(c.getColumnIndexOrThrow(Sqlite.COL_CREATED_AT)));
+ return cachedBundle;
+ }
+
+ private String serializeBundle(final Bundle bundle) {
+ String base64 = null;
+ final Parcel parcel = Parcel.obtain();
+ try {
+ parcel.writeBundle(bundle);
+ final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ final GZIPOutputStream zos = new GZIPOutputStream(new BufferedOutputStream(bos));
+ zos.write(parcel.marshall());
+ zos.close();
+ base64 = Base64.encodeToString(bos.toByteArray(), 0);
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ parcel.recycle();
+ }
+ return base64;
+ }
+
+ private Bundle deserializeBundle(final String base64) {
+ Bundle bundle = null;
+ final Parcel parcel = Parcel.obtain();
+ try {
+ final ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
+ final byte[] buffer = new byte[1024];
+ final GZIPInputStream zis = new GZIPInputStream(new ByteArrayInputStream(Base64.decode(base64, 0)));
+ int len;
+ while ((len = zis.read(buffer)) != -1) {
+ byteBuffer.write(buffer, 0, len);
+ }
+ zis.close();
+ parcel.unmarshall(byteBuffer.toByteArray(), 0, byteBuffer.size());
+ parcel.setDataPosition(0);
+ bundle = parcel.readBundle(getClass().getClassLoader());
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ parcel.recycle();
+ }
+ return bundle;
+ }
+
+ public enum CacheType {
+ @SerializedName("ARGS")
+ ARGS("ARGS"),
+ @SerializedName("ACCOUNT")
+ ACCOUNT("ACCOUNT"),
+ @SerializedName("STATUS")
+ STATUS("STATUS");
+
+ private final String value;
+
+ CacheType(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+ }
+
+ public interface BundleCallback {
+ void get(Bundle bundle);
+ }
+
+
+ public interface BundleInsertCallback {
+ void inserted(long bundleId);
+ }
+
+}