1
0
mirror of https://github.com/KokaKiwi/BarInsta synced 2024-11-26 08:37:29 +00:00

Migrate to Room

This commit is contained in:
Ammar Githam 2020-12-08 02:51:49 +09:00
parent 126a0f7f4b
commit 6b8df5fee2
20 changed files with 465 additions and 491 deletions

View File

@ -0,0 +1,229 @@
package awais.instagrabber.db;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.room.TypeConverters;
import androidx.room.migration.Migration;
import androidx.sqlite.db.SupportSQLiteDatabase;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import awais.instagrabber.db.dao.AccountDao;
import awais.instagrabber.db.dao.FavoriteDao;
import awais.instagrabber.db.entities.Account;
import awais.instagrabber.db.entities.Favorite;
import awais.instagrabber.models.enums.FavoriteType;
import awais.instagrabber.utils.Utils;
@Database(entities = {Account.class, Favorite.class},
version = 4)
@TypeConverters({Converters.class})
public abstract class AppDatabase extends RoomDatabase {
private static final String TAG = AppDatabase.class.getSimpleName();
private static AppDatabase INSTANCE;
public abstract AccountDao accountDao();
public abstract FavoriteDao favoriteDao();
public static AppDatabase getDatabase(final Context context) {
if (INSTANCE == null) {
synchronized (AppDatabase.class) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "cookiebox.db")
.addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4)
.build();
}
}
}
return INSTANCE;
}
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase db) {
db.execSQL("ALTER TABLE cookies ADD " + Account.COL_FULL_NAME + " TEXT");
db.execSQL("ALTER TABLE cookies ADD " + Account.COL_PROFILE_PIC + " TEXT");
}
};
static final Migration MIGRATION_2_3 = new Migration(2, 3) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase db) {
final List<Favorite> oldFavorites = backupOldFavorites(db);
// recreate with new columns (as there will be no doubt about the `query_display` column being present or not in the future versions)
db.execSQL("DROP TABLE " + Favorite.TABLE_NAME);
db.execSQL("CREATE TABLE " + Favorite.TABLE_NAME + " ("
+ Favorite.COL_ID + " INTEGER PRIMARY KEY,"
+ Favorite.COL_QUERY + " TEXT,"
+ Favorite.COL_TYPE + " TEXT,"
+ Favorite.COL_DISPLAY_NAME + " TEXT,"
+ Favorite.COL_PIC_URL + " TEXT,"
+ Favorite.COL_DATE_ADDED + " INTEGER)");
// add the old favorites back
for (final Favorite oldFavorite : oldFavorites) {
insertOrUpdateFavorite(db, oldFavorite);
}
}
};
static final Migration MIGRATION_3_4 = new Migration(3, 4) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase db) {
// Required when migrating to Room.
// The original table primary keys were not 'NOT NULL', so the migration to Room were failing without the below migration.
// Taking this opportunity to rename cookies table to accounts
// Create new table with name 'accounts'
db.execSQL("CREATE TABLE " + Account.TABLE_NAME + " ("
+ Account.COL_ID + " INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
+ Account.COL_UID + " TEXT,"
+ Account.COL_USERNAME + " TEXT,"
+ Account.COL_COOKIE + " TEXT,"
+ Account.COL_FULL_NAME + " TEXT,"
+ Account.COL_PROFILE_PIC + " TEXT)");
// Insert all data from table 'cookies' to 'accounts'
db.execSQL("INSERT INTO " + Account.TABLE_NAME + " ("
+ Account.COL_UID + ","
+ Account.COL_USERNAME + ","
+ Account.COL_COOKIE + ","
+ Account.COL_FULL_NAME + ","
+ Account.COL_PROFILE_PIC + ") "
+ "SELECT "
+ Account.COL_UID + ","
+ Account.COL_USERNAME + ","
+ Account.COL_COOKIE + ","
+ Account.COL_FULL_NAME + ","
+ Account.COL_PROFILE_PIC
+ " FROM cookies");
// Drop old cookies table
db.execSQL("DROP TABLE cookies");
// Create favorite backup table
db.execSQL("CREATE TABLE " + Favorite.TABLE_NAME + "_backup ("
+ Favorite.COL_ID + " INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
+ Favorite.COL_QUERY + " TEXT,"
+ Favorite.COL_TYPE + " TEXT,"
+ Favorite.COL_DISPLAY_NAME + " TEXT,"
+ Favorite.COL_PIC_URL + " TEXT,"
+ Favorite.COL_DATE_ADDED + " INTEGER)");
// Insert all data from table 'favorite' to 'favorite_backup'
db.execSQL("INSERT INTO " + Favorite.TABLE_NAME + "_backup ("
+ Favorite.COL_QUERY + ","
+ Favorite.COL_TYPE + ","
+ Favorite.COL_DISPLAY_NAME + ","
+ Favorite.COL_PIC_URL + ","
+ Favorite.COL_DATE_ADDED + ") "
+ "SELECT "
+ Favorite.COL_QUERY + ","
+ Favorite.COL_TYPE + ","
+ Favorite.COL_DISPLAY_NAME + ","
+ Favorite.COL_PIC_URL + ","
+ Favorite.COL_DATE_ADDED
+ " FROM " + Favorite.TABLE_NAME);
// Drop favorites
db.execSQL("DROP TABLE " + Favorite.TABLE_NAME);
// Rename favorite_backup to favorites
db.execSQL("ALTER TABLE " + Favorite.TABLE_NAME + "_backup RENAME TO " + Favorite.TABLE_NAME);
}
};
@NonNull
private static List<Favorite> backupOldFavorites(@NonNull final SupportSQLiteDatabase db) {
// check if old favorites table had the column query_display
final boolean queryDisplayExists = checkColumnExists(db, Favorite.TABLE_NAME, "query_display");
Log.d(TAG, "backupOldFavorites: queryDisplayExists: " + queryDisplayExists);
final List<Favorite> oldModels = new ArrayList<>();
final String sql = "SELECT "
+ "query_text,"
+ "date_added"
+ (queryDisplayExists ? ",query_display" : "")
+ " FROM " + Favorite.TABLE_NAME;
try (final Cursor cursor = db.query(sql)) {
if (cursor != null && cursor.moveToFirst()) {
do {
try {
final String queryText = cursor.getString(cursor.getColumnIndex("query_text"));
final Pair<FavoriteType, String> favoriteTypeQueryPair = Utils.migrateOldFavQuery(queryText);
if (favoriteTypeQueryPair == null) continue;
final FavoriteType type = favoriteTypeQueryPair.first;
final String query = favoriteTypeQueryPair.second;
oldModels.add(new Favorite(
-1,
query,
type,
queryDisplayExists ? cursor.getString(cursor.getColumnIndex("query_display"))
: null,
null,
new Date(cursor.getLong(cursor.getColumnIndex("date_added")))
));
} catch (Exception e) {
Log.e(TAG, "onUpgrade", e);
}
} while (cursor.moveToNext());
}
} catch (Exception e) {
Log.e(TAG, "onUpgrade", e);
}
Log.d(TAG, "backupOldFavorites: oldModels:" + oldModels);
return oldModels;
}
private static synchronized void insertOrUpdateFavorite(@NonNull final SupportSQLiteDatabase db, @NonNull final Favorite model) {
final ContentValues values = new ContentValues();
values.put(Favorite.COL_QUERY, model.getQuery());
values.put(Favorite.COL_TYPE, model.getType().toString());
values.put(Favorite.COL_DISPLAY_NAME, model.getDisplayName());
values.put(Favorite.COL_PIC_URL, model.getPicUrl());
values.put(Favorite.COL_DATE_ADDED, model.getDateAdded().getTime());
int rows;
if (model.getId() >= 1) {
rows = db.update(Favorite.TABLE_NAME,
SQLiteDatabase.CONFLICT_IGNORE,
values,
Favorite.COL_ID + "=?",
new String[]{String.valueOf(model.getId())});
} else {
rows = db.update(Favorite.TABLE_NAME,
SQLiteDatabase.CONFLICT_IGNORE,
values,
Favorite.COL_QUERY + "=?" + " AND " + Favorite.COL_TYPE + "=?",
new String[]{model.getQuery(), model.getType().toString()});
}
if (rows != 1) {
db.insert(Favorite.TABLE_NAME, SQLiteDatabase.CONFLICT_IGNORE, values);
}
}
private static boolean checkColumnExists(@NonNull final SupportSQLiteDatabase db,
@NonNull final String tableName,
@NonNull final String columnName) {
boolean exists = false;
try (Cursor cursor = db.query("PRAGMA table_info(" + tableName + ")")) {
if (cursor.moveToFirst()) {
do {
final String currentColumn = cursor.getString(cursor.getColumnIndex("name"));
if (currentColumn.equals(columnName)) {
exists = true;
}
} while (cursor.moveToNext());
}
} catch (Exception ex) {
Log.e(TAG, "checkColumnExists", ex);
}
return exists;
}
}

View File

@ -0,0 +1,33 @@
package awais.instagrabber.db;
import androidx.room.TypeConverter;
import java.util.Date;
import awais.instagrabber.models.enums.FavoriteType;
public class Converters {
@TypeConverter
public static Date fromTimestamp(Long value) {
return value == null ? null : new Date(value);
}
@TypeConverter
public static Long dateToTimestamp(Date date) {
return date == null ? null : date.getTime();
}
@TypeConverter
public static FavoriteType fromFavoriteTypeString(String value) {
try {
return FavoriteType.valueOf(value);
} catch (Exception e) {
return null;
}
}
@TypeConverter
public static String favoriteTypeToString(FavoriteType favoriteType) {
return favoriteType == null ? null : favoriteType.toString();
}
}

View File

@ -0,0 +1,34 @@
package awais.instagrabber.db.dao;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import androidx.room.Update;
import java.util.List;
import awais.instagrabber.db.entities.Account;
@Dao
public interface AccountDao {
@Query("SELECT * FROM accounts")
List<Account> getAllAccounts();
@Query("SELECT * FROM accounts WHERE uid = :uid")
Account findAccountByUid(String uid);
@Insert(onConflict = OnConflictStrategy.REPLACE)
List<Long> insertAccounts(Account... accounts);
@Update
void updateAccounts(Account... accounts);
@Delete
void deleteAccounts(Account... accounts);
@Query("DELETE from accounts")
void deleteAllAccounts();
}

View File

@ -0,0 +1,35 @@
package awais.instagrabber.db.dao;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import androidx.room.Update;
import java.util.List;
import awais.instagrabber.db.entities.Favorite;
import awais.instagrabber.models.enums.FavoriteType;
@Dao
public interface FavoriteDao {
@Query("SELECT * FROM favorites")
List<Favorite> getAllFavorites();
@Query("SELECT * FROM favorites WHERE query_text = :query and type = :type")
Favorite findFavoriteByQueryAndType(String query, FavoriteType type);
@Insert(onConflict = OnConflictStrategy.REPLACE)
List<Long> insertFavorites(Favorite... favorites);
@Update
void updateFavorites(Favorite... favorites);
@Delete
void deleteFavorites(Favorite... favorites);
@Query("DELETE from favorites")
void deleteAllFavorites();
}

View File

@ -1,182 +1,68 @@
package awais.instagrabber.db.datasources; package awais.instagrabber.db.datasources;
import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import awais.instagrabber.BuildConfig; import awais.instagrabber.db.AppDatabase;
import awais.instagrabber.db.dao.AccountDao;
import awais.instagrabber.db.entities.Account; import awais.instagrabber.db.entities.Account;
import awais.instagrabber.utils.DataBox;
import awais.instagrabber.utils.TextUtils;
import static awais.instagrabber.utils.DataBox.KEY_COOKIE;
import static awais.instagrabber.utils.DataBox.KEY_FULL_NAME;
import static awais.instagrabber.utils.DataBox.KEY_ID;
import static awais.instagrabber.utils.DataBox.KEY_PROFILE_PIC;
import static awais.instagrabber.utils.DataBox.KEY_UID;
import static awais.instagrabber.utils.DataBox.KEY_USERNAME;
import static awais.instagrabber.utils.DataBox.TABLE_COOKIES;
public class AccountDataSource { public class AccountDataSource {
private static final String TAG = AccountDataSource.class.getSimpleName(); private static final String TAG = AccountDataSource.class.getSimpleName();
private static AccountDataSource INSTANCE; private static AccountDataSource INSTANCE;
private final DataBox dataBox; private final AccountDao accountDao;
private AccountDataSource(@NonNull Context context) { private AccountDataSource(final AccountDao accountDao) {
dataBox = DataBox.getInstance(context); this.accountDao = accountDao;
} }
public static synchronized AccountDataSource getInstance(@NonNull Context context) { public static AccountDataSource getInstance(@NonNull Context context) {
if (INSTANCE == null) { if (INSTANCE == null) {
INSTANCE = new AccountDataSource(context); synchronized (AccountDataSource.class) {
if (INSTANCE == null) {
final AppDatabase database = AppDatabase.getDatabase(context);
INSTANCE = new AccountDataSource(database.accountDao());
}
}
} }
return INSTANCE; return INSTANCE;
} }
@Nullable @Nullable
public final Account getAccount(final String uid) { public final Account getAccount(final String uid) {
Account cookie = null; return accountDao.findAccountByUid(uid);
try (final SQLiteDatabase db = dataBox.getReadableDatabase();
final Cursor cursor = db.query(TABLE_COOKIES,
new String[]{
KEY_ID,
KEY_UID,
KEY_USERNAME,
KEY_COOKIE,
KEY_FULL_NAME,
KEY_PROFILE_PIC
},
KEY_UID + "=?",
new String[]{uid},
null,
null,
null)) {
if (cursor != null && cursor.moveToFirst())
cookie = new Account(
cursor.getInt(cursor.getColumnIndex(KEY_ID)),
cursor.getString(cursor.getColumnIndex(KEY_UID)),
cursor.getString(cursor.getColumnIndex(KEY_USERNAME)),
cursor.getString(cursor.getColumnIndex(KEY_COOKIE)),
cursor.getString(cursor.getColumnIndex(KEY_FULL_NAME)),
cursor.getString(cursor.getColumnIndex(KEY_PROFILE_PIC))
);
}
return cookie;
} }
@NonNull @NonNull
public final List<Account> getAllAccounts() { public final List<Account> getAllAccounts() {
final List<Account> cookies = new ArrayList<>(); return accountDao.getAllAccounts();
try (final SQLiteDatabase db = dataBox.getReadableDatabase();
final Cursor cursor = db.query(TABLE_COOKIES,
new String[]{
KEY_ID,
KEY_UID,
KEY_USERNAME,
KEY_COOKIE,
KEY_FULL_NAME,
KEY_PROFILE_PIC
},
null,
null,
null,
null,
null)) {
if (cursor != null && cursor.moveToFirst()) {
do {
cookies.add(new Account(
cursor.getInt(cursor.getColumnIndex(KEY_ID)),
cursor.getString(cursor.getColumnIndex(KEY_UID)),
cursor.getString(cursor.getColumnIndex(KEY_USERNAME)),
cursor.getString(cursor.getColumnIndex(KEY_COOKIE)),
cursor.getString(cursor.getColumnIndex(KEY_FULL_NAME)),
cursor.getString(cursor.getColumnIndex(KEY_PROFILE_PIC))
));
} while (cursor.moveToNext());
}
}
return cookies;
} }
// public final void insertOrUpdateAccount(@NonNull final Account account) {
// insertOrUpdateAccount(
// account.getUid(),
// account.getUsername(),
// account.getCookie(),
// account.getFullName(),
// account.getProfilePic()
// );
// }
public final void insertOrUpdateAccount(final String uid, public final void insertOrUpdateAccount(final String uid,
final String username, final String username,
final String cookie, final String cookie,
final String fullName, final String fullName,
final String profilePicUrl) { final String profilePicUrl) {
if (TextUtils.isEmpty(uid)) return; final Account account = getAccount(uid);
try (final SQLiteDatabase db = dataBox.getWritableDatabase()) { final Account toUpdate = new Account(account == null ? 0 : account.getId(), uid, username, cookie, fullName, profilePicUrl);
db.beginTransaction(); if (account != null) {
try { accountDao.updateAccounts(toUpdate);
final ContentValues values = new ContentValues(); return;
values.put(KEY_USERNAME, username);
values.put(KEY_COOKIE, cookie);
values.put(KEY_UID, uid);
values.put(KEY_FULL_NAME, fullName);
values.put(KEY_PROFILE_PIC, profilePicUrl);
final int rows = db.update(TABLE_COOKIES, values, KEY_UID + "=?", new String[]{uid});
if (rows != 1) {
db.insert(TABLE_COOKIES, null, values);
}
db.setTransactionSuccessful();
} catch (final Exception e) {
if (BuildConfig.DEBUG) Log.e(TAG, "Error", e);
} finally {
db.endTransaction();
}
} }
accountDao.insertAccounts(toUpdate);
} }
public final synchronized void deleteAccount(@NonNull final Account account) { public final void deleteAccount(@NonNull final Account account) {
final String cookieModelUid = account.getUid(); accountDao.deleteAccounts(account);
if (!TextUtils.isEmpty(cookieModelUid)) {
try (final SQLiteDatabase db = dataBox.getWritableDatabase()) {
db.beginTransaction();
try {
final int rowsDeleted = db.delete(TABLE_COOKIES, KEY_UID + "=? AND " + KEY_USERNAME + "=? AND " + KEY_COOKIE + "=?",
new String[]{cookieModelUid, account.getUsername(), account.getCookie()});
if (rowsDeleted > 0) db.setTransactionSuccessful();
} catch (final Exception e) {
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
} finally {
db.endTransaction();
}
}
}
} }
public final synchronized void deleteAllAccounts() { public final void deleteAllAccounts() {
try (final SQLiteDatabase db = dataBox.getWritableDatabase()) { accountDao.deleteAllAccounts();
db.beginTransaction();
try {
final int rowsDeleted = db.delete(TABLE_COOKIES, null, null);
if (rowsDeleted > 0) db.setTransactionSuccessful();
} catch (final Exception e) {
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
} finally {
db.endTransaction();
}
}
} }
} }

View File

@ -1,180 +1,62 @@
package awais.instagrabber.db.datasources; package awais.instagrabber.db.datasources;
import android.content.Context; import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List; import java.util.List;
import awais.instagrabber.BuildConfig; import awais.instagrabber.db.AppDatabase;
import awais.instagrabber.db.dao.FavoriteDao;
import awais.instagrabber.db.entities.Favorite; import awais.instagrabber.db.entities.Favorite;
import awais.instagrabber.models.enums.FavoriteType; import awais.instagrabber.models.enums.FavoriteType;
import awais.instagrabber.utils.DataBox;
import awais.instagrabber.utils.TextUtils;
import awaisomereport.LogCollector;
import static awais.instagrabber.utils.DataBox.FAV_COL_DATE_ADDED;
import static awais.instagrabber.utils.DataBox.FAV_COL_DISPLAY_NAME;
import static awais.instagrabber.utils.DataBox.FAV_COL_ID;
import static awais.instagrabber.utils.DataBox.FAV_COL_PIC_URL;
import static awais.instagrabber.utils.DataBox.FAV_COL_QUERY;
import static awais.instagrabber.utils.DataBox.FAV_COL_TYPE;
import static awais.instagrabber.utils.DataBox.TABLE_FAVORITES;
import static awais.instagrabber.utils.Utils.logCollector;
public class FavoriteDataSource { public class FavoriteDataSource {
private static final String TAG = FavoriteDataSource.class.getSimpleName(); private static final String TAG = FavoriteDataSource.class.getSimpleName();
private static FavoriteDataSource INSTANCE; private static FavoriteDataSource INSTANCE;
private final DataBox dataBox; private final FavoriteDao favoriteDao;
private FavoriteDataSource(@NonNull Context context) { private FavoriteDataSource(final FavoriteDao favoriteDao) {
dataBox = DataBox.getInstance(context); this.favoriteDao = favoriteDao;
} }
public static synchronized FavoriteDataSource getInstance(@NonNull Context context) { public static synchronized FavoriteDataSource getInstance(@NonNull Context context) {
if (INSTANCE == null) { if (INSTANCE == null) {
INSTANCE = new FavoriteDataSource(context); synchronized (FavoriteDataSource.class) {
if (INSTANCE == null) {
final AppDatabase database = AppDatabase.getDatabase(context);
INSTANCE = new FavoriteDataSource(database.favoriteDao());
}
}
} }
return INSTANCE; return INSTANCE;
} }
@Nullable @Nullable
public final Favorite getFavorite(@NonNull final String query, @NonNull final FavoriteType type) { public final Favorite getFavorite(@NonNull final String query, @NonNull final FavoriteType type) {
try (final SQLiteDatabase db = dataBox.getReadableDatabase(); return favoriteDao.findFavoriteByQueryAndType(query, type);
final Cursor cursor = db.query(TABLE_FAVORITES,
new String[]{
FAV_COL_ID,
FAV_COL_QUERY,
FAV_COL_TYPE,
FAV_COL_DISPLAY_NAME,
FAV_COL_PIC_URL,
FAV_COL_DATE_ADDED
},
FAV_COL_QUERY + "=?" + " AND " + FAV_COL_TYPE + "=?",
new String[]{
query,
type.toString()
},
null,
null,
null)) {
if (cursor != null && cursor.moveToFirst()) {
FavoriteType favoriteType = null;
try {
favoriteType = FavoriteType.valueOf(cursor.getString(cursor.getColumnIndex(FAV_COL_TYPE)));
} catch (IllegalArgumentException ignored) {}
return new Favorite(
cursor.getInt(cursor.getColumnIndex(FAV_COL_ID)),
cursor.getString(cursor.getColumnIndex(FAV_COL_QUERY)),
favoriteType,
cursor.getString(cursor.getColumnIndex(FAV_COL_DISPLAY_NAME)),
cursor.getString(cursor.getColumnIndex(FAV_COL_PIC_URL)),
new Date(cursor.getLong(cursor.getColumnIndex(FAV_COL_DATE_ADDED)))
);
}
}
return null;
} }
@NonNull @NonNull
public final List<Favorite> getAllFavorites() { public final List<Favorite> getAllFavorites() {
final List<Favorite> favorites = new ArrayList<>(); return favoriteDao.getAllFavorites();
try (final SQLiteDatabase db = dataBox.getWritableDatabase()) {
try (final Cursor cursor = db.query(TABLE_FAVORITES,
new String[]{
FAV_COL_ID,
FAV_COL_QUERY,
FAV_COL_TYPE,
FAV_COL_DISPLAY_NAME,
FAV_COL_PIC_URL,
FAV_COL_DATE_ADDED
},
null,
null,
null,
null,
null)) {
if (cursor != null && cursor.moveToFirst()) {
db.beginTransaction();
Favorite tempFav;
do {
FavoriteType type = null;
try {
type = FavoriteType.valueOf(cursor.getString(cursor.getColumnIndex(FAV_COL_TYPE)));
} catch (IllegalArgumentException ignored) {}
tempFav = new Favorite(
cursor.getInt(cursor.getColumnIndex(FAV_COL_ID)),
cursor.getString(cursor.getColumnIndex(FAV_COL_QUERY)),
type,
cursor.getString(cursor.getColumnIndex(FAV_COL_DISPLAY_NAME)),
cursor.getString(cursor.getColumnIndex(FAV_COL_PIC_URL)),
new Date(cursor.getLong(cursor.getColumnIndex(FAV_COL_DATE_ADDED)))
);
favorites.add(tempFav);
} while (cursor.moveToNext());
db.endTransaction();
}
} catch (final Exception e) {
Log.e(TAG, "", e);
}
}
return favorites;
} }
public final synchronized Favorite insertOrUpdateFavorite(@NonNull final Favorite model) { public final void insertOrUpdateFavorite(@NonNull final Favorite favorite) {
final String query = model.getQuery(); if (favorite.getId() > 0) {
if (!TextUtils.isEmpty(query)) { favoriteDao.updateFavorites(favorite);
try (final SQLiteDatabase db = dataBox.getWritableDatabase()) { return;
db.beginTransaction();
try {
dataBox.insertOrUpdateFavorite(db, model);
db.setTransactionSuccessful();
} catch (Exception e) {
if (logCollector != null) {
logCollector.appendException(e, LogCollector.LogFile.DATA_BOX_FAVORITES, "insertOrUpdateFavorite");
}
if (BuildConfig.DEBUG) {
Log.e(TAG, "Error adding/updating favorite", e);
}
} finally {
db.endTransaction();
}
}
return getFavorite(model.getQuery(), model.getType());
} }
return null; favoriteDao.insertFavorites(favorite);
} }
public final synchronized void deleteFavorite(@NonNull final String query, @NonNull final FavoriteType type) { public final void deleteFavorite(@NonNull final String query, @NonNull final FavoriteType type) {
if (!TextUtils.isEmpty(query)) { final Favorite favorite = getFavorite(query, type);
try (final SQLiteDatabase db = dataBox.getWritableDatabase()) { if (favorite == null) return;
db.beginTransaction(); favoriteDao.deleteFavorites(favorite);
try {
final int rowsDeleted = db.delete(TABLE_FAVORITES,
FAV_COL_QUERY + "=?" +
" AND " + FAV_COL_TYPE + "=?",
new String[]{query, type.toString()});
if (rowsDeleted > 0) db.setTransactionSuccessful();
} catch (final Exception e) {
if (logCollector != null) {
logCollector.appendException(e, LogCollector.LogFile.DATA_BOX_FAVORITES, "deleteFavorite");
}
if (BuildConfig.DEBUG) {
Log.e(TAG, "Error", e);
}
} finally {
db.endTransaction();
}
}
}
} }
} }

View File

@ -4,32 +4,42 @@ import androidx.annotation.NonNull;
import androidx.core.util.ObjectsCompat; import androidx.core.util.ObjectsCompat;
import androidx.room.ColumnInfo; import androidx.room.ColumnInfo;
import androidx.room.Entity; import androidx.room.Entity;
import androidx.room.Ignore;
import androidx.room.PrimaryKey; import androidx.room.PrimaryKey;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
@Entity(tableName = "cookies") @Entity(tableName = Account.TABLE_NAME)
public class Account { public class Account {
public final static String TABLE_NAME = "accounts";
public final static String COL_ID = "id";
public final static String COL_USERNAME = Constants.EXTRAS_USERNAME;
public final static String COL_COOKIE = "cookie";
public final static String COL_UID = "uid";
public final static String COL_FULL_NAME = "full_name";
public final static String COL_PROFILE_PIC = "profile_pic";
@PrimaryKey @PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id") @ColumnInfo(name = COL_ID)
private final int id; private final int id;
@ColumnInfo(name = "uid") @ColumnInfo(name = COL_UID)
private final String uid; private final String uid;
@ColumnInfo(name = "username") @ColumnInfo(name = COL_USERNAME)
private final String username; private final String username;
@ColumnInfo(name = "cookie") @ColumnInfo(name = COL_COOKIE)
private final String cookie; private final String cookie;
@ColumnInfo(name = "full_name") @ColumnInfo(name = COL_FULL_NAME)
private final String fullName; private final String fullName;
@ColumnInfo(name = "profile_pic") @ColumnInfo(name = COL_PROFILE_PIC)
private final String profilePic; private final String profilePic;
@Ignore
private boolean selected; private boolean selected;
public Account(final int id, public Account(final int id,

View File

@ -10,26 +10,33 @@ import java.util.Date;
import awais.instagrabber.models.enums.FavoriteType; import awais.instagrabber.models.enums.FavoriteType;
@Entity(tableName = "favorites") @Entity(tableName = Favorite.TABLE_NAME)
public class Favorite { public class Favorite {
public final static String TABLE_NAME = "favorites";
public final static String COL_ID = "id";
public final static String COL_QUERY = "query_text";
public final static String COL_TYPE = "type";
public final static String COL_DISPLAY_NAME = "display_name";
public final static String COL_PIC_URL = "pic_url";
public final static String COL_DATE_ADDED = "date_added";
@PrimaryKey @PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id") @ColumnInfo(name = COL_ID)
private final int id; private final int id;
@ColumnInfo(name = "query_text") @ColumnInfo(name = COL_QUERY)
private final String query; private final String query;
@ColumnInfo(name = "type") @ColumnInfo(name = COL_TYPE)
private final FavoriteType type; private final FavoriteType type;
@ColumnInfo(name = "display_name") @ColumnInfo(name = COL_DISPLAY_NAME)
private final String displayName; private final String displayName;
@ColumnInfo(name = "pic_url") @ColumnInfo(name = COL_PIC_URL)
private final String picUrl; private final String picUrl;
@ColumnInfo(name = "date_added") @ColumnInfo(name = COL_DATE_ADDED)
private final Date dateAdded; private final Date dateAdded;
public Favorite(final int id, public Favorite(final int id,

View File

@ -21,9 +21,9 @@ public class AccountRepository {
this.accountDataSource = accountDataSource; this.accountDataSource = accountDataSource;
} }
public static AccountRepository getInstance(final AppExecutors appExecutors, final AccountDataSource accountDataSource) { public static AccountRepository getInstance(final AccountDataSource accountDataSource) {
if (instance == null) { if (instance == null) {
instance = new AccountRepository(appExecutors, accountDataSource); instance = new AccountRepository(AppExecutors.getInstance(), accountDataSource);
} }
return instance; return instance;
} }

View File

@ -20,9 +20,9 @@ public class FavoriteRepository {
this.favoriteDataSource = favoriteDataSource; this.favoriteDataSource = favoriteDataSource;
} }
public static FavoriteRepository getInstance(final AppExecutors appExecutors, final FavoriteDataSource favoriteDataSource) { public static FavoriteRepository getInstance(final FavoriteDataSource favoriteDataSource) {
if (instance == null) { if (instance == null) {
instance = new FavoriteRepository(appExecutors, favoriteDataSource); instance = new FavoriteRepository(AppExecutors.getInstance(), favoriteDataSource);
} }
return instance; return instance;
} }
@ -60,18 +60,14 @@ public class FavoriteRepository {
} }
public void insertOrUpdateFavorite(final Favorite favorite, public void insertOrUpdateFavorite(final Favorite favorite,
final RepositoryCallback<Favorite> callback) { final RepositoryCallback<Void> callback) {
// request on the I/O thread // request on the I/O thread
appExecutors.diskIO().execute(() -> { appExecutors.diskIO().execute(() -> {
final Favorite updated = favoriteDataSource.insertOrUpdateFavorite(favorite); favoriteDataSource.insertOrUpdateFavorite(favorite);
// notify on the main thread // notify on the main thread
appExecutors.mainThread().execute(() -> { appExecutors.mainThread().execute(() -> {
if (callback == null) return; if (callback == null) return;
if (updated == null) { callback.onSuccess(null);
callback.onDataNotAvailable();
return;
}
callback.onSuccess(updated);
}); });
}); });
} }

View File

@ -25,7 +25,6 @@ import awais.instagrabber.db.datasources.AccountDataSource;
import awais.instagrabber.db.entities.Account; import awais.instagrabber.db.entities.Account;
import awais.instagrabber.db.repositories.AccountRepository; import awais.instagrabber.db.repositories.AccountRepository;
import awais.instagrabber.db.repositories.RepositoryCallback; import awais.instagrabber.db.repositories.RepositoryCallback;
import awais.instagrabber.utils.AppExecutors;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
@ -41,12 +40,12 @@ public class AccountSwitcherDialogFragment extends DialogFragment {
private DialogAccountSwitcherBinding binding; private DialogAccountSwitcherBinding binding;
public AccountSwitcherDialogFragment() { public AccountSwitcherDialogFragment() {
accountRepository = AccountRepository.getInstance(new AppExecutors(), AccountDataSource.getInstance(getContext())); accountRepository = AccountRepository.getInstance(AccountDataSource.getInstance(getContext()));
} }
public AccountSwitcherDialogFragment(final OnAddAccountClickListener onAddAccountClickListener) { public AccountSwitcherDialogFragment(final OnAddAccountClickListener onAddAccountClickListener) {
this.onAddAccountClickListener = onAddAccountClickListener; this.onAddAccountClickListener = onAddAccountClickListener;
accountRepository = AccountRepository.getInstance(new AppExecutors(), AccountDataSource.getInstance(getContext())); accountRepository = AccountRepository.getInstance(AccountDataSource.getInstance(getContext()));
} }
private final AccountSwitcherAdapter.OnAccountClickListener accountClickListener = (model, isCurrent) -> { private final AccountSwitcherAdapter.OnAccountClickListener accountClickListener = (model, isCurrent) -> {

View File

@ -32,7 +32,6 @@ import awais.instagrabber.db.datasources.FavoriteDataSource;
import awais.instagrabber.db.entities.Favorite; import awais.instagrabber.db.entities.Favorite;
import awais.instagrabber.db.repositories.FavoriteRepository; import awais.instagrabber.db.repositories.FavoriteRepository;
import awais.instagrabber.db.repositories.RepositoryCallback; import awais.instagrabber.db.repositories.RepositoryCallback;
import awais.instagrabber.utils.AppExecutors;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.viewmodels.FavoritesViewModel; import awais.instagrabber.viewmodels.FavoritesViewModel;
@ -49,7 +48,7 @@ public class FavoritesFragment extends Fragment {
@Override @Override
public void onCreate(@Nullable final Bundle savedInstanceState) { public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
favoriteRepository = FavoriteRepository.getInstance(new AppExecutors(), FavoriteDataSource.getInstance(getContext())); favoriteRepository = FavoriteRepository.getInstance(FavoriteDataSource.getInstance(getContext()));
} }
@NonNull @NonNull
@ -186,9 +185,9 @@ public class FavoritesFragment extends Fragment {
result.getSdProfilePic(), result.getSdProfilePic(),
model.getDateAdded() model.getDateAdded()
); );
favoriteRepository.insertOrUpdateFavorite(updated, new RepositoryCallback<Favorite>() { favoriteRepository.insertOrUpdateFavorite(updated, new RepositoryCallback<Void>() {
@Override @Override
public void onSuccess(final Favorite result) { public void onSuccess(final Void result) {
updatedList.add(i, updated); updatedList.add(i, updated);
try { try {
cyclicBarrier.await(); cyclicBarrier.await();
@ -225,9 +224,9 @@ public class FavoritesFragment extends Fragment {
result.getSdProfilePic(), result.getSdProfilePic(),
model.getDateAdded() model.getDateAdded()
); );
favoriteRepository.insertOrUpdateFavorite(updated, new RepositoryCallback<Favorite>() { favoriteRepository.insertOrUpdateFavorite(updated, new RepositoryCallback<Void>() {
@Override @Override
public void onSuccess(final Favorite result) { public void onSuccess(final Void result) {
try { try {
cyclicBarrier.await(); cyclicBarrier.await();
} catch (BrokenBarrierException | InterruptedException e) { } catch (BrokenBarrierException | InterruptedException e) {

View File

@ -59,7 +59,6 @@ import awais.instagrabber.models.HashtagModel;
import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.models.PostsLayoutPreferences;
import awais.instagrabber.models.StoryModel; import awais.instagrabber.models.StoryModel;
import awais.instagrabber.models.enums.FavoriteType; import awais.instagrabber.models.enums.FavoriteType;
import awais.instagrabber.utils.AppExecutors;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.DownloadUtils; import awais.instagrabber.utils.DownloadUtils;
@ -459,8 +458,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
hashtagDetailsBinding.btnFollowTag.setVisibility(View.GONE); hashtagDetailsBinding.btnFollowTag.setVisibility(View.GONE);
} }
hashtagDetailsBinding.favChip.setVisibility(View.VISIBLE); hashtagDetailsBinding.favChip.setVisibility(View.VISIBLE);
final FavoriteRepository favoriteRepository = FavoriteRepository final FavoriteRepository favoriteRepository = FavoriteRepository.getInstance(FavoriteDataSource.getInstance(getContext()));
.getInstance(new AppExecutors(), FavoriteDataSource.getInstance(getContext()));
favoriteRepository.getFavorite(hashtag.substring(1), FavoriteType.HASHTAG, new RepositoryCallback<Favorite>() { favoriteRepository.getFavorite(hashtag.substring(1), FavoriteType.HASHTAG, new RepositoryCallback<Favorite>() {
@Override @Override
public void onSuccess(final Favorite result) { public void onSuccess(final Favorite result) {
@ -500,9 +498,9 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
hashtagModel.getName(), hashtagModel.getName(),
null, null,
new Date() new Date()
), new RepositoryCallback<Favorite>() { ), new RepositoryCallback<Void>() {
@Override @Override
public void onSuccess(final Favorite result) { public void onSuccess(final Void result) {
hashtagDetailsBinding.favChip.setText(R.string.favorite_short); hashtagDetailsBinding.favChip.setText(R.string.favorite_short);
hashtagDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24); hashtagDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24);
showSnackbar(getString(R.string.added_to_favs)); showSnackbar(getString(R.string.added_to_favs));

View File

@ -62,7 +62,6 @@ import awais.instagrabber.models.LocationModel;
import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.models.PostsLayoutPreferences;
import awais.instagrabber.models.StoryModel; import awais.instagrabber.models.StoryModel;
import awais.instagrabber.models.enums.FavoriteType; import awais.instagrabber.models.enums.FavoriteType;
import awais.instagrabber.utils.AppExecutors;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.DownloadUtils; import awais.instagrabber.utils.DownloadUtils;
@ -448,7 +447,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
locationDetailsBinding.locationUrl.setText(TextUtils.getSpannableUrl(url)); locationDetailsBinding.locationUrl.setText(TextUtils.getSpannableUrl(url));
} }
final FavoriteDataSource dataSource = FavoriteDataSource.getInstance(getContext()); final FavoriteDataSource dataSource = FavoriteDataSource.getInstance(getContext());
final FavoriteRepository favoriteRepository = FavoriteRepository.getInstance(new AppExecutors(), dataSource); final FavoriteRepository favoriteRepository = FavoriteRepository.getInstance(dataSource);
locationDetailsBinding.favChip.setVisibility(View.VISIBLE); locationDetailsBinding.favChip.setVisibility(View.VISIBLE);
favoriteRepository.getFavorite(locationId, FavoriteType.LOCATION, new RepositoryCallback<Favorite>() { favoriteRepository.getFavorite(locationId, FavoriteType.LOCATION, new RepositoryCallback<Favorite>() {
@Override @Override
@ -491,9 +490,9 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
locationModel.getName(), locationModel.getName(),
locationModel.getSdProfilePic(), locationModel.getSdProfilePic(),
new Date() new Date()
), new RepositoryCallback<Favorite>() { ), new RepositoryCallback<Void>() {
@Override @Override
public void onSuccess(final Favorite result) { public void onSuccess(final Void result) {
locationDetailsBinding.favChip.setText(R.string.favorite_short); locationDetailsBinding.favChip.setText(R.string.favorite_short);
locationDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24); locationDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24);
showSnackbar(getString(R.string.added_to_favs)); showSnackbar(getString(R.string.added_to_favs));

View File

@ -82,7 +82,6 @@ import awais.instagrabber.models.enums.FavoriteType;
import awais.instagrabber.models.enums.PostItemType; import awais.instagrabber.models.enums.PostItemType;
import awais.instagrabber.repositories.responses.FriendshipRepoChangeRootResponse; import awais.instagrabber.repositories.responses.FriendshipRepoChangeRootResponse;
import awais.instagrabber.repositories.responses.FriendshipRepoRestrictRootResponse; import awais.instagrabber.repositories.responses.FriendshipRepoRestrictRootResponse;
import awais.instagrabber.utils.AppExecutors;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.DownloadUtils; import awais.instagrabber.utils.DownloadUtils;
@ -299,9 +298,8 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
fragmentActivity = (MainActivity) requireActivity(); fragmentActivity = (MainActivity) requireActivity();
friendshipService = FriendshipService.getInstance(); friendshipService = FriendshipService.getInstance();
storiesService = StoriesService.getInstance(); storiesService = StoriesService.getInstance();
final AppExecutors appExecutors = new AppExecutors(); accountRepository = AccountRepository.getInstance(AccountDataSource.getInstance(getContext()));
accountRepository = AccountRepository.getInstance(appExecutors, AccountDataSource.getInstance(getContext())); favoriteRepository = FavoriteRepository.getInstance(FavoriteDataSource.getInstance(getContext()));
favoriteRepository = FavoriteRepository.getInstance(appExecutors, FavoriteDataSource.getInstance(getContext()));
setHasOptionsMenu(true); setHasOptionsMenu(true);
} }
@ -907,9 +905,9 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
profileModel.getSdProfilePic(), profileModel.getSdProfilePic(),
new Date() new Date()
); );
favoriteRepository.insertOrUpdateFavorite(model, new RepositoryCallback<Favorite>() { favoriteRepository.insertOrUpdateFavorite(model, new RepositoryCallback<Void>() {
@Override @Override
public void onSuccess(final Favorite result) { public void onSuccess(final Void result) {
profileDetailsBinding.favCb.setButtonDrawable(R.drawable.ic_star_check_24); profileDetailsBinding.favCb.setButtonDrawable(R.drawable.ic_star_check_24);
profileDetailsBinding.favProgress.setVisibility(View.GONE); profileDetailsBinding.favProgress.setVisibility(View.GONE);
profileDetailsBinding.favCb.setVisibility(View.VISIBLE); profileDetailsBinding.favCb.setVisibility(View.VISIBLE);

View File

@ -33,7 +33,6 @@ import awais.instagrabber.db.repositories.AccountRepository;
import awais.instagrabber.db.repositories.RepositoryCallback; import awais.instagrabber.db.repositories.RepositoryCallback;
import awais.instagrabber.dialogs.AccountSwitcherDialogFragment; import awais.instagrabber.dialogs.AccountSwitcherDialogFragment;
import awais.instagrabber.repositories.responses.UserInfo; import awais.instagrabber.repositories.responses.UserInfo;
import awais.instagrabber.utils.AppExecutors;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.FlavorTown; import awais.instagrabber.utils.FlavorTown;
@ -49,7 +48,7 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
private final AccountRepository accountRepository; private final AccountRepository accountRepository;
public MorePreferencesFragment() { public MorePreferencesFragment() {
accountRepository = AccountRepository.getInstance(new AppExecutors(), AccountDataSource.getInstance(getContext())); accountRepository = AccountRepository.getInstance(AccountDataSource.getInstance(getContext()));
} }
@Override @Override

View File

@ -35,37 +35,48 @@ import java.util.concurrent.Executors;
*/ */
public class AppExecutors { public class AppExecutors {
// private static final int THREAD_COUNT = 3; private static final int THREAD_COUNT = 3;
private static final Object LOCK = new Object();
private static AppExecutors instance;
private final Executor diskIO; private final Executor diskIO;
// private final Executor networkIO; private final Executor networkIO;
private final Executor mainThread; private final Executor mainThread;
private final ListeningExecutorService tasksThread; private final ListeningExecutorService tasksThread;
private AppExecutors(Executor diskIO, private AppExecutors(Executor diskIO,
// Executor networkIO, Executor networkIO,
Executor mainThread, Executor mainThread,
ListeningExecutorService tasksThread) { ListeningExecutorService tasksThread) {
this.diskIO = diskIO; this.diskIO = diskIO;
// this.networkIO = networkIO; this.networkIO = networkIO;
this.mainThread = mainThread; this.mainThread = mainThread;
this.tasksThread = tasksThread; this.tasksThread = tasksThread;
} }
public AppExecutors() { public static AppExecutors getInstance() {
this(Executors.newSingleThreadExecutor(), if (instance == null) {
// Executors.newFixedThreadPool(THREAD_COUNT), synchronized (LOCK) {
new MainThreadExecutor(), if (instance == null) {
MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10))); instance = new AppExecutors(Executors.newSingleThreadExecutor(),
Executors.newFixedThreadPool(THREAD_COUNT),
new MainThreadExecutor(),
MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10)));
}
}
}
return instance;
} }
public Executor diskIO() { public Executor diskIO() {
return diskIO; return diskIO;
} }
// public Executor networkIO() { public Executor networkIO() {
// return networkIO; return networkIO;
// } }
public ListeningExecutorService tasksThread() { public ListeningExecutorService tasksThread() {

View File

@ -63,7 +63,7 @@ public final class CookieUtils {
if (cookieStore == null) return; if (cookieStore == null) return;
cookieStore.removeAll(); cookieStore.removeAll();
try { try {
AccountRepository.getInstance(new AppExecutors(), AccountDataSource.getInstance(context)) AccountRepository.getInstance(AccountDataSource.getInstance(context))
.deleteAllAccounts(callback); .deleteAllAccounts(callback);
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "setupCookies", e); Log.e(TAG, "setupCookies", e);

View File

@ -1,45 +1,19 @@
package awais.instagrabber.utils; package awais.instagrabber.utils;
import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log; import android.util.Log;
import android.util.Pair;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import awais.instagrabber.db.entities.Favorite;
import awais.instagrabber.models.enums.FavoriteType;
public final class DataBox extends SQLiteOpenHelper { public final class DataBox extends SQLiteOpenHelper {
private static final String TAG = "DataBox"; private static final String TAG = "DataBox";
private static DataBox sInstance; private static DataBox sInstance;
private final static int VERSION = 3; private final static int VERSION = 3;
public final static String TABLE_COOKIES = "cookies";
public final static String TABLE_FAVORITES = "favorites";
public final static String KEY_ID = "id";
public final static String KEY_USERNAME = Constants.EXTRAS_USERNAME;
public final static String KEY_COOKIE = "cookie";
public final static String KEY_UID = "uid";
public final static String KEY_FULL_NAME = "full_name";
public final static String KEY_PROFILE_PIC = "profile_pic";
public final static String FAV_COL_ID = "id";
public final static String FAV_COL_QUERY = "query_text";
public final static String FAV_COL_TYPE = "type";
public final static String FAV_COL_DISPLAY_NAME = "display_name";
public final static String FAV_COL_PIC_URL = "pic_url";
public final static String FAV_COL_DATE_ADDED = "date_added";
public static synchronized DataBox getInstance(final Context context) { public static synchronized DataBox getInstance(final Context context) {
if (sInstance == null) sInstance = new DataBox(context.getApplicationContext()); if (sInstance == null) sInstance = new DataBox(context.getApplicationContext());
@ -53,21 +27,6 @@ public final class DataBox extends SQLiteOpenHelper {
@Override @Override
public void onCreate(@NonNull final SQLiteDatabase db) { public void onCreate(@NonNull final SQLiteDatabase db) {
Log.i(TAG, "Creating tables..."); Log.i(TAG, "Creating tables...");
db.execSQL("CREATE TABLE " + TABLE_COOKIES + " ("
+ KEY_ID + " INTEGER PRIMARY KEY,"
+ KEY_UID + " TEXT,"
+ KEY_USERNAME + " TEXT,"
+ KEY_COOKIE + " TEXT,"
+ KEY_FULL_NAME + " TEXT,"
+ KEY_PROFILE_PIC + " TEXT)");
// db.execSQL("CREATE TABLE favorites (id INTEGER PRIMARY KEY, query_text TEXT, date_added INTEGER, query_display TEXT)");
db.execSQL("CREATE TABLE " + TABLE_FAVORITES + " ("
+ FAV_COL_ID + " INTEGER PRIMARY KEY,"
+ FAV_COL_QUERY + " TEXT,"
+ FAV_COL_TYPE + " TEXT,"
+ FAV_COL_DISPLAY_NAME + " TEXT,"
+ FAV_COL_PIC_URL + " TEXT,"
+ FAV_COL_DATE_ADDED + " INTEGER)");
Log.i(TAG, "Tables created!"); Log.i(TAG, "Tables created!");
} }
@ -77,107 +36,8 @@ public final class DataBox extends SQLiteOpenHelper {
// switch without break, so that all migrations from a previous version to new are run // switch without break, so that all migrations from a previous version to new are run
switch (oldVersion) { switch (oldVersion) {
case 1: case 1:
db.execSQL("ALTER TABLE " + TABLE_COOKIES + " ADD " + KEY_FULL_NAME + " TEXT");
db.execSQL("ALTER TABLE " + TABLE_COOKIES + " ADD " + KEY_PROFILE_PIC + " TEXT");
case 2: case 2:
final List<Favorite> oldFavorites = backupOldFavorites(db);
// recreate with new columns (as there will be no doubt about the `query_display` column being present or not in the future versions)
db.execSQL("DROP TABLE " + TABLE_FAVORITES);
db.execSQL("CREATE TABLE " + TABLE_FAVORITES + " ("
+ FAV_COL_ID + " INTEGER PRIMARY KEY,"
+ FAV_COL_QUERY + " TEXT,"
+ FAV_COL_TYPE + " TEXT,"
+ FAV_COL_DISPLAY_NAME + " TEXT,"
+ FAV_COL_PIC_URL + " TEXT,"
+ FAV_COL_DATE_ADDED + " INTEGER)");
// add the old favorites back
for (final Favorite oldFavorite : oldFavorites) {
insertOrUpdateFavorite(db, oldFavorite);
}
} }
Log.i(TAG, String.format("DB update from v%d to v%d completed!", oldVersion, newVersion)); Log.i(TAG, String.format("DB update from v%d to v%d completed!", oldVersion, newVersion));
} }
public synchronized void insertOrUpdateFavorite(@NonNull final SQLiteDatabase db, @NonNull final Favorite model) {
final ContentValues values = new ContentValues();
values.put(FAV_COL_QUERY, model.getQuery());
values.put(FAV_COL_TYPE, model.getType().toString());
values.put(FAV_COL_DISPLAY_NAME, model.getDisplayName());
values.put(FAV_COL_PIC_URL, model.getPicUrl());
values.put(FAV_COL_DATE_ADDED, model.getDateAdded().getTime());
int rows;
if (model.getId() >= 1) {
rows = db.update(TABLE_FAVORITES, values, FAV_COL_ID + "=?", new String[]{String.valueOf(model.getId())});
} else {
rows = db.update(TABLE_FAVORITES,
values,
FAV_COL_QUERY + "=?" +
" AND " + FAV_COL_TYPE + "=?",
new String[]{model.getQuery(), model.getType().toString()});
}
if (rows != 1) {
db.insert(TABLE_FAVORITES, null, values);
}
}
@NonNull
private List<Favorite> backupOldFavorites(@NonNull final SQLiteDatabase db) {
// check if old favorites table had the column query_display
final boolean queryDisplayExists = checkColumnExists(db, TABLE_FAVORITES, "query_display");
Log.d(TAG, "backupOldFavorites: queryDisplayExists: " + queryDisplayExists);
final List<Favorite> oldModels = new ArrayList<>();
final String sql = "SELECT "
+ "query_text,"
+ "date_added"
+ (queryDisplayExists ? ",query_display" : "")
+ " FROM " + TABLE_FAVORITES;
try (final Cursor cursor = db.rawQuery(sql, null)) {
if (cursor != null && cursor.moveToFirst()) {
do {
try {
final String queryText = cursor.getString(cursor.getColumnIndex("query_text"));
final Pair<FavoriteType, String> favoriteTypeQueryPair = Utils.migrateOldFavQuery(queryText);
if (favoriteTypeQueryPair == null) continue;
final FavoriteType type = favoriteTypeQueryPair.first;
final String query = favoriteTypeQueryPair.second;
oldModels.add(new Favorite(
-1,
query,
type,
queryDisplayExists ? cursor.getString(cursor.getColumnIndex("query_display"))
: null,
null,
new Date(cursor.getLong(cursor.getColumnIndex("date_added")))
));
} catch (Exception e) {
Log.e(TAG, "onUpgrade", e);
}
} while (cursor.moveToNext());
}
} catch (Exception e) {
Log.e(TAG, "onUpgrade", e);
}
Log.d(TAG, "backupOldFavorites: oldModels:" + oldModels);
return oldModels;
}
public boolean checkColumnExists(@NonNull final SQLiteDatabase db,
@NonNull final String tableName,
@NonNull final String columnName) {
boolean exists = false;
try (Cursor cursor = db.rawQuery("PRAGMA table_info(" + tableName + ")", null)) {
if (cursor.moveToFirst()) {
do {
final String currentColumn = cursor.getString(cursor.getColumnIndex("name"));
if (currentColumn.equals(columnName)) {
exists = true;
}
} while (cursor.moveToNext());
}
} catch (Exception ex) {
Log.e(TAG, "checkColumnExists", ex);
}
return exists;
}
} }

View File

@ -198,7 +198,7 @@ public final class ExportImportUtils {
: favsObject.optString("pic_url"), : favsObject.optString("pic_url"),
new Date(favsObject.getLong("d"))); new Date(favsObject.getLong("d")));
// Log.d(TAG, "importJson: favoriteModel: " + favoriteModel); // Log.d(TAG, "importJson: favoriteModel: " + favoriteModel);
FavoriteRepository.getInstance(new AppExecutors(), FavoriteDataSource.getInstance(context)) FavoriteRepository.getInstance(FavoriteDataSource.getInstance(context))
.insertOrUpdateFavorite(favorite, null); .insertOrUpdateFavorite(favorite, null);
} }
} }
@ -225,7 +225,7 @@ public final class ExportImportUtils {
Log.e(TAG, "importAccounts: Error parsing json", e); Log.e(TAG, "importAccounts: Error parsing json", e);
return; return;
} }
AccountRepository.getInstance(new AppExecutors(), AccountDataSource.getInstance(context)) AccountRepository.getInstance(AccountDataSource.getInstance(context))
.insertOrUpdateAccounts(accounts, null); .insertOrUpdateAccounts(accounts, null);
} }
@ -266,9 +266,8 @@ public final class ExportImportUtils {
private static void getExportString(@ExportImportFlags final int flags, private static void getExportString(@ExportImportFlags final int flags,
@NonNull final Context context, @NonNull final Context context,
final OnExportStringCreatedCallback callback) { final OnExportStringCreatedCallback callback) {
final AppExecutors appExecutors = new AppExecutors();
final Handler innerHandler = new Handler(); final Handler innerHandler = new Handler();
appExecutors.tasksThread().execute(() -> { AppExecutors.getInstance().tasksThread().execute(() -> {
final CountDownLatch responseWaiter = new CountDownLatch(3); final CountDownLatch responseWaiter = new CountDownLatch(3);
try { try {
final JSONObject jsonObject = new JSONObject(); final JSONObject jsonObject = new JSONObject();
@ -341,7 +340,7 @@ public final class ExportImportUtils {
private static void getFavorites(final Context context, final OnFavoritesJsonLoadedCallback callback) { private static void getFavorites(final Context context, final OnFavoritesJsonLoadedCallback callback) {
final FavoriteDataSource dataSource = FavoriteDataSource.getInstance(context); final FavoriteDataSource dataSource = FavoriteDataSource.getInstance(context);
final FavoriteRepository favoriteRepository = FavoriteRepository.getInstance(new AppExecutors(), dataSource); final FavoriteRepository favoriteRepository = FavoriteRepository.getInstance(dataSource);
try { try {
favoriteRepository.getAllFavorites(new RepositoryCallback<List<Favorite>>() { favoriteRepository.getAllFavorites(new RepositoryCallback<List<Favorite>>() {
@Override @Override
@ -379,7 +378,7 @@ public final class ExportImportUtils {
} }
private static void getCookies(final Context context, final OnAccountJsonLoadedCallback callback) { private static void getCookies(final Context context, final OnAccountJsonLoadedCallback callback) {
final AccountRepository accountRepository = AccountRepository.getInstance(new AppExecutors(), AccountDataSource.getInstance(context)); final AccountRepository accountRepository = AccountRepository.getInstance(AccountDataSource.getInstance(context));
accountRepository.getAllAccounts(new RepositoryCallback<List<Account>>() { accountRepository.getAllAccounts(new RepositoryCallback<List<Account>>() {
@Override @Override
public void onSuccess(final List<Account> accounts) { public void onSuccess(final List<Account> accounts) {