diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3d33cda0..556de192 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -108,7 +108,8 @@ + android:parentActivityName=".activities.MainActivity" + android:theme="@style/AppTheme.Light.White"> diff --git a/app/src/main/java/awais/instagrabber/InstaGrabberApplication.java b/app/src/main/java/awais/instagrabber/InstaGrabberApplication.java index 5f87ed0e..874b425f 100644 --- a/app/src/main/java/awais/instagrabber/InstaGrabberApplication.java +++ b/app/src/main/java/awais/instagrabber/InstaGrabberApplication.java @@ -13,7 +13,6 @@ import java.text.SimpleDateFormat; import java.util.UUID; import awais.instagrabber.utils.Constants; -import awais.instagrabber.utils.DataBox; import awais.instagrabber.utils.LocaleUtils; import awais.instagrabber.utils.SettingsHelper; import awaisomereport.CrashReporter; @@ -21,7 +20,6 @@ import awaisomereport.LogCollector; import static awais.instagrabber.utils.CookieUtils.NET_COOKIE_MANAGER; import static awais.instagrabber.utils.Utils.clipboardManager; -import static awais.instagrabber.utils.Utils.dataBox; import static awais.instagrabber.utils.Utils.datetimeParser; import static awais.instagrabber.utils.Utils.logCollector; import static awais.instagrabber.utils.Utils.settingsHelper; @@ -58,11 +56,6 @@ public final class InstaGrabberApplication extends Application { CookieHandler.setDefault(NET_COOKIE_MANAGER); - final Context appContext = getApplicationContext(); - - if (dataBox == null) - dataBox = DataBox.getInstance(appContext); - if (settingsHelper == null) settingsHelper = new SettingsHelper(this); diff --git a/app/src/main/java/awais/instagrabber/activities/DirectDownload.java b/app/src/main/java/awais/instagrabber/activities/DirectDownload.java index 4d675b3e..448aab78 100644 --- a/app/src/main/java/awais/instagrabber/activities/DirectDownload.java +++ b/app/src/main/java/awais/instagrabber/activities/DirectDownload.java @@ -26,9 +26,11 @@ import awais.instagrabber.models.FeedModel; import awais.instagrabber.models.IntentModel; import awais.instagrabber.models.enums.IntentModelType; import awais.instagrabber.utils.Constants; +import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.DownloadUtils; import awais.instagrabber.utils.IntentUtils; import awais.instagrabber.utils.TextUtils; +import awais.instagrabber.utils.Utils; public final class DirectDownload extends AppCompatActivity { private static final int NOTIFICATION_ID = 1900000000; @@ -88,6 +90,7 @@ public final class DirectDownload extends AppCompatActivity { } private synchronized void doDownload() { + CookieUtils.setupCookies(Utils.settingsHelper.getString(Constants.COOKIE)); notificationManager = NotificationManagerCompat.from(getApplicationContext()); final Intent intent = getIntent(); final String action = intent.getAction(); diff --git a/app/src/main/java/awais/instagrabber/adapters/AccountSwitcherAdapter.java b/app/src/main/java/awais/instagrabber/adapters/AccountSwitcherAdapter.java index 8e984ddc..10cd73dc 100644 --- a/app/src/main/java/awais/instagrabber/adapters/AccountSwitcherAdapter.java +++ b/app/src/main/java/awais/instagrabber/adapters/AccountSwitcherAdapter.java @@ -14,21 +14,21 @@ import androidx.recyclerview.widget.RecyclerView; import awais.instagrabber.R; import awais.instagrabber.databinding.PrefAccountSwitcherBinding; +import awais.instagrabber.db.entities.Account; import awais.instagrabber.utils.Constants; -import awais.instagrabber.utils.DataBox; import static awais.instagrabber.utils.Utils.settingsHelper; -public class AccountSwitcherAdapter extends ListAdapter { +public class AccountSwitcherAdapter extends ListAdapter { private static final String TAG = "AccountSwitcherAdapter"; - private static final DiffUtil.ItemCallback DIFF_CALLBACK = new DiffUtil.ItemCallback() { + private static final DiffUtil.ItemCallback DIFF_CALLBACK = new DiffUtil.ItemCallback() { @Override - public boolean areItemsTheSame(@NonNull final DataBox.CookieModel oldItem, @NonNull final DataBox.CookieModel newItem) { + public boolean areItemsTheSame(@NonNull final Account oldItem, @NonNull final Account newItem) { return oldItem.getUid().equals(newItem.getUid()); } @Override - public boolean areContentsTheSame(@NonNull final DataBox.CookieModel oldItem, @NonNull final DataBox.CookieModel newItem) { + public boolean areContentsTheSame(@NonNull final Account oldItem, @NonNull final Account newItem) { return oldItem.getUid().equals(newItem.getUid()); } }; @@ -53,7 +53,7 @@ public class AccountSwitcherAdapter extends ListAdapter { @@ -102,7 +102,7 @@ public class FavoritesAdapter extends RecyclerView.Adapter list) { + public void submitList(@Nullable final List list) { if (list == null) { differ.submitList(null); return; @@ -110,7 +110,7 @@ public class FavoritesAdapter extends RecyclerView.Adapter list, @Nullable final Runnable commitCallback) { + public void submitList(@Nullable final List list, @Nullable final Runnable commitCallback) { if (list == null) { differ.submitList(null, commitCallback); return; @@ -119,8 +119,8 @@ public class FavoritesAdapter extends RecyclerView.Adapter sectionAndSort(@NonNull final List list) { - final List listCopy = new ArrayList<>(list); + private List sectionAndSort(@NonNull final List list) { + final List listCopy = new ArrayList<>(list); Collections.sort(listCopy, (o1, o2) -> { if (o1.getType() == o2.getType()) return 0; // keep users at top @@ -133,7 +133,7 @@ public class FavoritesAdapter extends RecyclerView.Adapter modelOrHeaders = new ArrayList<>(); for (int i = 0; i < listCopy.size(); i++) { - final DataBox.FavoriteModel model = listCopy.get(i); + final Favorite model = listCopy.get(i); final FavoriteModelOrHeader prev = modelOrHeaders.isEmpty() ? null : modelOrHeaders.get(modelOrHeaders.size() - 1); boolean prevWasSameType = prev != null && prev.model.getType() == model.getType(); if (prevWasSameType) { @@ -156,7 +156,7 @@ public class FavoritesAdapter extends RecyclerView.Adapter> { private static final String TAG = "NotificationsFetcher"; @@ -37,8 +35,6 @@ public final class NotificationsFetcher extends AsyncTask doInBackground(final Void... voids) { List result = new ArrayList<>(); final String url = "https://www.instagram.com/accounts/activity/?__a=1"; - CookieUtils.setupCookies(settingsHelper.getString(Constants.COOKIE)); - try { final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); conn.setInstanceFollowRedirects(false); diff --git a/app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java index 39ba0d9e..f87e5103 100755 --- a/app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java +++ b/app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java @@ -17,11 +17,9 @@ import awais.instagrabber.models.PostChild; import awais.instagrabber.models.ProfileModel; import awais.instagrabber.models.enums.MediaItemType; import awais.instagrabber.utils.Constants; -import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.NetworkUtils; import awais.instagrabber.utils.ResponseBodyUtils; import awais.instagrabber.utils.TextUtils; -import awais.instagrabber.utils.Utils; import awaisomereport.LogCollector; import static awais.instagrabber.utils.Utils.logCollector; @@ -39,7 +37,6 @@ public final class PostFetcher extends AsyncTask { @Override protected FeedModel doInBackground(final Void... voids) { - CookieUtils.setupCookies(Utils.settingsHelper.getString(Constants.COOKIE)); // <- direct download HttpURLConnection conn = null; try { conn = (HttpURLConnection) new URL("https://www.instagram.com/p/" + shortCode + "/?__a=1").openConnection(); diff --git a/app/src/main/java/awais/instagrabber/db/datasources/AccountDataSource.java b/app/src/main/java/awais/instagrabber/db/datasources/AccountDataSource.java new file mode 100644 index 00000000..2aaa2f8a --- /dev/null +++ b/app/src/main/java/awais/instagrabber/db/datasources/AccountDataSource.java @@ -0,0 +1,182 @@ +package awais.instagrabber.db.datasources; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.ArrayList; +import java.util.List; + +import awais.instagrabber.BuildConfig; +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 { + private static final String TAG = AccountDataSource.class.getSimpleName(); + + private static AccountDataSource INSTANCE; + + private final DataBox dataBox; + + private AccountDataSource(@NonNull Context context) { + dataBox = DataBox.getInstance(context); + } + + public static synchronized AccountDataSource getInstance(@NonNull Context context) { + if (INSTANCE == null) { + INSTANCE = new AccountDataSource(context); + } + return INSTANCE; + } + + @Nullable + public final Account getAccount(final String uid) { + Account cookie = null; + 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 + public final List getAllAccounts() { + final List cookies = new ArrayList<>(); + 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, + final String username, + final String cookie, + final String fullName, + final String profilePicUrl) { + if (TextUtils.isEmpty(uid)) return; + try (final SQLiteDatabase db = dataBox.getWritableDatabase()) { + db.beginTransaction(); + try { + final ContentValues values = new ContentValues(); + 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(); + } + } + } + + public final synchronized void deleteAccount(@NonNull final Account account) { + final String cookieModelUid = account.getUid(); + 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() { + try (final SQLiteDatabase db = dataBox.getWritableDatabase()) { + 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(); + } + } + } +} diff --git a/app/src/main/java/awais/instagrabber/db/datasources/FavoriteDataSource.java b/app/src/main/java/awais/instagrabber/db/datasources/FavoriteDataSource.java new file mode 100644 index 00000000..92a566c6 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/db/datasources/FavoriteDataSource.java @@ -0,0 +1,180 @@ +package awais.instagrabber.db.datasources; + +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import awais.instagrabber.BuildConfig; +import awais.instagrabber.db.entities.Favorite; +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 { + private static final String TAG = FavoriteDataSource.class.getSimpleName(); + + private static FavoriteDataSource INSTANCE; + + private final DataBox dataBox; + + private FavoriteDataSource(@NonNull Context context) { + dataBox = DataBox.getInstance(context); + } + + public static synchronized FavoriteDataSource getInstance(@NonNull Context context) { + if (INSTANCE == null) { + INSTANCE = new FavoriteDataSource(context); + } + return INSTANCE; + } + + @Nullable + public final Favorite getFavorite(@NonNull final String query, @NonNull final FavoriteType type) { + try (final SQLiteDatabase db = dataBox.getReadableDatabase(); + 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 + public final List getAllFavorites() { + final List favorites = new ArrayList<>(); + 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) { + final String query = model.getQuery(); + if (!TextUtils.isEmpty(query)) { + try (final SQLiteDatabase db = dataBox.getWritableDatabase()) { + 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; + } + + public final synchronized void deleteFavorite(@NonNull final String query, @NonNull final FavoriteType type) { + if (!TextUtils.isEmpty(query)) { + try (final SQLiteDatabase db = dataBox.getWritableDatabase()) { + db.beginTransaction(); + 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(); + } + } + } + } +} diff --git a/app/src/main/java/awais/instagrabber/db/entities/Account.java b/app/src/main/java/awais/instagrabber/db/entities/Account.java new file mode 100644 index 00000000..7f849c6f --- /dev/null +++ b/app/src/main/java/awais/instagrabber/db/entities/Account.java @@ -0,0 +1,114 @@ +package awais.instagrabber.db.entities; + +import androidx.annotation.NonNull; +import androidx.core.util.ObjectsCompat; +import androidx.room.ColumnInfo; +import androidx.room.Entity; +import androidx.room.PrimaryKey; + +import awais.instagrabber.utils.TextUtils; + +@Entity(tableName = "cookies") +public class Account { + + @PrimaryKey + @ColumnInfo(name = "id") + private final int id; + + @ColumnInfo(name = "uid") + private final String uid; + + @ColumnInfo(name = "username") + private final String username; + + @ColumnInfo(name = "cookie") + private final String cookie; + + @ColumnInfo(name = "full_name") + private final String fullName; + + @ColumnInfo(name = "profile_pic") + private final String profilePic; + + private boolean selected; + + public Account(final int id, + final String uid, + final String username, + final String cookie, + final String fullName, + final String profilePic) { + this.id = id; + this.uid = uid; + this.username = username; + this.cookie = cookie; + this.fullName = fullName; + this.profilePic = profilePic; + } + + public int getId() { + return id; + } + + public String getUid() { + return uid; + } + + public String getUsername() { + return username; + } + + public String getCookie() { + return cookie; + } + + public String getFullName() { + return fullName; + } + + public String getProfilePic() { + return profilePic; + } + + public boolean isSelected() { + return selected; + } + + public void setSelected(final boolean selected) { + this.selected = selected; + } + + public boolean isValid() { + return !TextUtils.isEmpty(uid) + && !TextUtils.isEmpty(username) + && !TextUtils.isEmpty(cookie); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final Account that = (Account) o; + return ObjectsCompat.equals(uid, that.uid) && + ObjectsCompat.equals(username, that.username) && + ObjectsCompat.equals(cookie, that.cookie); + } + + @Override + public int hashCode() { + return ObjectsCompat.hash(uid, username, cookie); + } + + @NonNull + @Override + public String toString() { + return "Account{" + + "uid='" + uid + '\'' + + ", username='" + username + '\'' + + ", cookie='" + cookie + '\'' + + ", fullName='" + fullName + '\'' + + ", profilePic='" + profilePic + '\'' + + ", selected=" + selected + + '}'; + } +} diff --git a/app/src/main/java/awais/instagrabber/db/entities/Favorite.java b/app/src/main/java/awais/instagrabber/db/entities/Favorite.java new file mode 100644 index 00000000..7e3bada4 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/db/entities/Favorite.java @@ -0,0 +1,103 @@ +package awais.instagrabber.db.entities; + +import androidx.annotation.NonNull; +import androidx.core.util.ObjectsCompat; +import androidx.room.ColumnInfo; +import androidx.room.Entity; +import androidx.room.PrimaryKey; + +import java.util.Date; + +import awais.instagrabber.models.enums.FavoriteType; + +@Entity(tableName = "favorites") +public class Favorite { + + @PrimaryKey + @ColumnInfo(name = "id") + private final int id; + + @ColumnInfo(name = "query_text") + private final String query; + + @ColumnInfo(name = "type") + private final FavoriteType type; + + @ColumnInfo(name = "display_name") + private final String displayName; + + @ColumnInfo(name = "pic_url") + private final String picUrl; + + @ColumnInfo(name = "date_added") + private final Date dateAdded; + + public Favorite(final int id, + final String query, + final FavoriteType type, + final String displayName, + final String picUrl, + final Date dateAdded) { + this.id = id; + this.query = query; + this.type = type; + this.displayName = displayName; + this.picUrl = picUrl; + this.dateAdded = dateAdded; + } + + public int getId() { + return id; + } + + public String getQuery() { + return query; + } + + public FavoriteType getType() { + return type; + } + + public String getDisplayName() { + return displayName; + } + + public String getPicUrl() { + return picUrl; + } + + public Date getDateAdded() { + return dateAdded; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final Favorite that = (Favorite) o; + return id == that.id && + ObjectsCompat.equals(query, that.query) && + type == that.type && + ObjectsCompat.equals(displayName, that.displayName) && + ObjectsCompat.equals(picUrl, that.picUrl) && + ObjectsCompat.equals(dateAdded, that.dateAdded); + } + + @Override + public int hashCode() { + return ObjectsCompat.hash(id, query, type, displayName, picUrl, dateAdded); + } + + @NonNull + @Override + public String toString() { + return "FavoriteModel{" + + "id=" + id + + ", query='" + query + '\'' + + ", type=" + type + + ", displayName='" + displayName + '\'' + + ", picUrl='" + picUrl + '\'' + + ", dateAdded=" + dateAdded + + '}'; + } +} diff --git a/app/src/main/java/awais/instagrabber/db/repositories/AccountRepository.java b/app/src/main/java/awais/instagrabber/db/repositories/AccountRepository.java new file mode 100644 index 00000000..529d848d --- /dev/null +++ b/app/src/main/java/awais/instagrabber/db/repositories/AccountRepository.java @@ -0,0 +1,131 @@ +package awais.instagrabber.db.repositories; + +import java.util.List; + +import awais.instagrabber.db.datasources.AccountDataSource; +import awais.instagrabber.db.entities.Account; +import awais.instagrabber.utils.AppExecutors; + +public class AccountRepository { + private static final String TAG = AccountRepository.class.getSimpleName(); + + private static AccountRepository instance; + + private final AppExecutors appExecutors; + private final AccountDataSource accountDataSource; + + // private List cachedAccounts; + + private AccountRepository(final AppExecutors appExecutors, final AccountDataSource accountDataSource) { + this.appExecutors = appExecutors; + this.accountDataSource = accountDataSource; + } + + public static AccountRepository getInstance(final AppExecutors appExecutors, final AccountDataSource accountDataSource) { + if (instance == null) { + instance = new AccountRepository(appExecutors, accountDataSource); + } + return instance; + } + + public void getAccount(final String uid, + final RepositoryCallback callback) { + // request on the I/O thread + appExecutors.diskIO().execute(() -> { + final Account account = accountDataSource.getAccount(uid); + // notify on the main thread + appExecutors.mainThread().execute(() -> { + if (callback == null) return; + if (account == null) { + callback.onDataNotAvailable(); + return; + } + callback.onSuccess(account); + }); + }); + } + + public void getAllAccounts(final RepositoryCallback> callback) { + // request on the I/O thread + appExecutors.diskIO().execute(() -> { + final List accounts = accountDataSource.getAllAccounts(); + // notify on the main thread + appExecutors.mainThread().execute(() -> { + if (callback == null) return; + if (accounts == null) { + callback.onDataNotAvailable(); + return; + } + // cachedAccounts = accounts; + callback.onSuccess(accounts); + }); + }); + } + + public void insertOrUpdateAccounts(final List accounts, + final RepositoryCallback callback) { + // request on the I/O thread + appExecutors.diskIO().execute(() -> { + for (final Account account : accounts) { + accountDataSource.insertOrUpdateAccount(account.getUid(), + account.getUsername(), + account.getCookie(), + account.getFullName(), + account.getProfilePic()); + } + // notify on the main thread + appExecutors.mainThread().execute(() -> { + if (callback == null) return; + callback.onSuccess(null); + }); + }); + } + + public void insertOrUpdateAccount(final String uid, + final String username, + final String cookie, + final String fullName, + final String profilePicUrl, + final RepositoryCallback callback) { + // request on the I/O thread + appExecutors.diskIO().execute(() -> { + accountDataSource.insertOrUpdateAccount(uid, username, cookie, fullName, profilePicUrl); + final Account updated = accountDataSource.getAccount(uid); + // notify on the main thread + appExecutors.mainThread().execute(() -> { + if (callback == null) return; + if (updated == null) { + callback.onDataNotAvailable(); + return; + } + callback.onSuccess(updated); + }); + }); + } + + public void deleteAccount(final Account account, + final RepositoryCallback callback) { + // request on the I/O thread + appExecutors.diskIO().execute(() -> { + accountDataSource.deleteAccount(account); + // notify on the main thread + appExecutors.mainThread().execute(() -> { + if (callback == null) return; + callback.onSuccess(null); + }); + }); + } + + public void deleteAllAccounts(final RepositoryCallback callback) { + // request on the I/O thread + appExecutors.diskIO().execute(() -> { + accountDataSource.deleteAllAccounts(); + // notify on the main thread + appExecutors.mainThread().execute(() -> { + if (callback == null) return; + callback.onSuccess(null); + }); + }); + } + +} diff --git a/app/src/main/java/awais/instagrabber/db/repositories/FavoriteRepository.java b/app/src/main/java/awais/instagrabber/db/repositories/FavoriteRepository.java new file mode 100644 index 00000000..3f942b6b --- /dev/null +++ b/app/src/main/java/awais/instagrabber/db/repositories/FavoriteRepository.java @@ -0,0 +1,92 @@ +package awais.instagrabber.db.repositories; + +import java.util.List; + +import awais.instagrabber.db.datasources.FavoriteDataSource; +import awais.instagrabber.db.entities.Favorite; +import awais.instagrabber.models.enums.FavoriteType; +import awais.instagrabber.utils.AppExecutors; + +public class FavoriteRepository { + private static final String TAG = FavoriteRepository.class.getSimpleName(); + + private static FavoriteRepository instance; + + private final AppExecutors appExecutors; + private final FavoriteDataSource favoriteDataSource; + + private FavoriteRepository(final AppExecutors appExecutors, final FavoriteDataSource favoriteDataSource) { + this.appExecutors = appExecutors; + this.favoriteDataSource = favoriteDataSource; + } + + public static FavoriteRepository getInstance(final AppExecutors appExecutors, final FavoriteDataSource favoriteDataSource) { + if (instance == null) { + instance = new FavoriteRepository(appExecutors, favoriteDataSource); + } + return instance; + } + + public void getFavorite(final String query, final FavoriteType type, final RepositoryCallback callback) { + // request on the I/O thread + appExecutors.diskIO().execute(() -> { + final Favorite favorite = favoriteDataSource.getFavorite(query, type); + // notify on the main thread + appExecutors.mainThread().execute(() -> { + if (callback == null) return; + if (favorite == null) { + callback.onDataNotAvailable(); + return; + } + callback.onSuccess(favorite); + }); + }); + } + + public void getAllFavorites(final RepositoryCallback> callback) { + // request on the I/O thread + appExecutors.diskIO().execute(() -> { + final List favorites = favoriteDataSource.getAllFavorites(); + // notify on the main thread + appExecutors.mainThread().execute(() -> { + if (callback == null) return; + if (favorites == null) { + callback.onDataNotAvailable(); + return; + } + callback.onSuccess(favorites); + }); + }); + } + + public void insertOrUpdateFavorite(final Favorite favorite, + final RepositoryCallback callback) { + // request on the I/O thread + appExecutors.diskIO().execute(() -> { + final Favorite updated = favoriteDataSource.insertOrUpdateFavorite(favorite); + // notify on the main thread + appExecutors.mainThread().execute(() -> { + if (callback == null) return; + if (updated == null) { + callback.onDataNotAvailable(); + return; + } + callback.onSuccess(updated); + }); + }); + } + + public void deleteFavorite(final String query, + final FavoriteType type, + final RepositoryCallback callback) { + // request on the I/O thread + appExecutors.diskIO().execute(() -> { + favoriteDataSource.deleteFavorite(query, type); + // notify on the main thread + appExecutors.mainThread().execute(() -> { + if (callback == null) return; + callback.onSuccess(null); + }); + }); + } +} diff --git a/app/src/main/java/awais/instagrabber/db/repositories/RepositoryCallback.java b/app/src/main/java/awais/instagrabber/db/repositories/RepositoryCallback.java new file mode 100644 index 00000000..6663239c --- /dev/null +++ b/app/src/main/java/awais/instagrabber/db/repositories/RepositoryCallback.java @@ -0,0 +1,11 @@ +package awais.instagrabber.db.repositories; + +import androidx.annotation.MainThread; + +public interface RepositoryCallback { + @MainThread + void onSuccess(T result); + + @MainThread + void onDataNotAvailable(); +} diff --git a/app/src/main/java/awais/instagrabber/dialogs/AccountSwitcherDialogFragment.java b/app/src/main/java/awais/instagrabber/dialogs/AccountSwitcherDialogFragment.java index e6de23b3..d1830243 100644 --- a/app/src/main/java/awais/instagrabber/dialogs/AccountSwitcherDialogFragment.java +++ b/app/src/main/java/awais/instagrabber/dialogs/AccountSwitcherDialogFragment.java @@ -21,9 +21,13 @@ import java.util.List; import awais.instagrabber.R; import awais.instagrabber.adapters.AccountSwitcherAdapter; import awais.instagrabber.databinding.DialogAccountSwitcherBinding; +import awais.instagrabber.db.datasources.AccountDataSource; +import awais.instagrabber.db.entities.Account; +import awais.instagrabber.db.repositories.AccountRepository; +import awais.instagrabber.db.repositories.RepositoryCallback; +import awais.instagrabber.utils.AppExecutors; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; -import awais.instagrabber.utils.DataBox; import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.Utils; @@ -31,9 +35,20 @@ import static awais.instagrabber.utils.Utils.settingsHelper; public class AccountSwitcherDialogFragment extends DialogFragment { - private final OnAddAccountClickListener onAddAccountClickListener; + private AccountRepository accountRepository; + + private OnAddAccountClickListener onAddAccountClickListener; private DialogAccountSwitcherBinding binding; + public AccountSwitcherDialogFragment() { + accountRepository = AccountRepository.getInstance(new AppExecutors(), AccountDataSource.getInstance(getContext())); + } + + public AccountSwitcherDialogFragment(final OnAddAccountClickListener onAddAccountClickListener) { + this.onAddAccountClickListener = onAddAccountClickListener; + accountRepository = AccountRepository.getInstance(new AppExecutors(), AccountDataSource.getInstance(getContext())); + } + private final AccountSwitcherAdapter.OnAccountClickListener accountClickListener = (model, isCurrent) -> { if (isCurrent) { dismiss(); @@ -59,8 +74,18 @@ public class AccountSwitcherDialogFragment extends DialogFragment { new AlertDialog.Builder(context) .setMessage(getString(R.string.quick_access_confirm_delete, model.getUsername())) .setPositiveButton(R.string.yes, (dialog, which) -> { - Utils.dataBox.delUserCookie(model); - dismiss(); + if (accountRepository == null) return; + accountRepository.deleteAccount(model, new RepositoryCallback() { + @Override + public void onSuccess(final Void result) { + dismiss(); + } + + @Override + public void onDataNotAvailable() { + dismiss(); + } + }); }) .setNegativeButton(R.string.cancel, null) .show(); @@ -68,10 +93,6 @@ public class AccountSwitcherDialogFragment extends DialogFragment { return true; }; - public AccountSwitcherDialogFragment(final OnAddAccountClickListener onAddAccountClickListener) { - this.onAddAccountClickListener = onAddAccountClickListener; - } - @Override public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, @@ -102,11 +123,19 @@ public class AccountSwitcherDialogFragment extends DialogFragment { private void init() { final AccountSwitcherAdapter adapter = new AccountSwitcherAdapter(accountClickListener, accountLongClickListener); binding.accounts.setAdapter(adapter); - final List allUsers = Utils.dataBox.getAllCookies(); - if (allUsers == null) return; - final String cookie = settingsHelper.getString(Constants.COOKIE); - sortUserList(cookie, allUsers); - adapter.submitList(allUsers); + if (accountRepository == null) return; + accountRepository.getAllAccounts(new RepositoryCallback>() { + @Override + public void onSuccess(final List accounts) { + if (accounts == null) return; + final String cookie = settingsHelper.getString(Constants.COOKIE); + sortUserList(cookie, accounts); + adapter.submitList(accounts); + } + + @Override + public void onDataNotAvailable() {} + }); binding.addAccountBtn.setOnClickListener(v -> { if (onAddAccountClickListener == null) return; onAddAccountClickListener.onAddAccountClick(this); @@ -125,9 +154,9 @@ public class AccountSwitcherDialogFragment extends DialogFragment { * @param cookie active cookie * @param allUsers list of users */ - private void sortUserList(final String cookie, final List allUsers) { + private void sortUserList(final String cookie, final List allUsers) { boolean sortByName = true; - for (final DataBox.CookieModel user : allUsers) { + for (final Account user : allUsers) { if (TextUtils.isEmpty(user.getFullName())) { sortByName = false; break; diff --git a/app/src/main/java/awais/instagrabber/dialogs/RestoreBackupDialogFragment.java b/app/src/main/java/awais/instagrabber/dialogs/RestoreBackupDialogFragment.java index 9b9de1e2..7967b9e2 100644 --- a/app/src/main/java/awais/instagrabber/dialogs/RestoreBackupDialogFragment.java +++ b/app/src/main/java/awais/instagrabber/dialogs/RestoreBackupDialogFragment.java @@ -4,6 +4,7 @@ import android.app.Dialog; import android.content.Context; import android.content.pm.PackageManager; import android.os.Bundle; +import android.os.Handler; import android.text.Editable; import android.text.TextWatcher; import android.view.LayoutInflater; @@ -93,7 +94,7 @@ public class RestoreBackupDialogFragment extends DialogFragment { return; } binding.btnRestore.setEnabled(false); - binding.btnRestore.setOnClickListener(v -> { + binding.btnRestore.setOnClickListener(v -> new Handler().post(() -> { int flags = 0; if (binding.cbFavorites.isChecked()) { flags |= ExportImportUtils.FLAG_FAVORITES; @@ -122,7 +123,7 @@ public class RestoreBackupDialogFragment extends DialogFragment { } catch (IncorrectPasswordException e) { binding.passwordField.setError("Incorrect password"); } - }); + })); binding.etPassword.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {} diff --git a/app/src/main/java/awais/instagrabber/fragments/FavoritesFragment.java b/app/src/main/java/awais/instagrabber/fragments/FavoritesFragment.java index 3d344a59..8631add3 100644 --- a/app/src/main/java/awais/instagrabber/fragments/FavoritesFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/FavoritesFragment.java @@ -28,9 +28,12 @@ import awais.instagrabber.adapters.FavoritesAdapter; import awais.instagrabber.asyncs.LocationFetcher; import awais.instagrabber.asyncs.ProfileFetcher; import awais.instagrabber.databinding.FragmentFavoritesBinding; -import awais.instagrabber.utils.DataBox; +import awais.instagrabber.db.datasources.FavoriteDataSource; +import awais.instagrabber.db.entities.Favorite; +import awais.instagrabber.db.repositories.FavoriteRepository; +import awais.instagrabber.db.repositories.RepositoryCallback; +import awais.instagrabber.utils.AppExecutors; import awais.instagrabber.utils.TextUtils; -import awais.instagrabber.utils.Utils; import awais.instagrabber.viewmodels.FavoritesViewModel; public class FavoritesFragment extends Fragment { @@ -41,6 +44,13 @@ public class FavoritesFragment extends Fragment { private RecyclerView root; private FavoritesViewModel favoritesViewModel; private FavoritesAdapter adapter; + private FavoriteRepository favoriteRepository; + + @Override + public void onCreate(@Nullable final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + favoriteRepository = FavoriteRepository.getInstance(new AppExecutors(), FavoriteDataSource.getInstance(getContext())); + } @NonNull @Override @@ -68,9 +78,16 @@ public class FavoritesFragment extends Fragment { if (favoritesViewModel == null || adapter == null) return; // refresh list every time in onViewStateRestored since it is cheaper than implementing pull down to refresh favoritesViewModel.getList().observe(getViewLifecycleOwner(), adapter::submitList); - final List allFavorites = Utils.dataBox.getAllFavorites(); - favoritesViewModel.getList().postValue(allFavorites); - fetchMissingInfo(allFavorites); + favoriteRepository.getAllFavorites(new RepositoryCallback>() { + @Override + public void onSuccess(final List favorites) { + favoritesViewModel.getList().postValue(favorites); + fetchMissingInfo(favorites); + } + + @Override + public void onDataNotAvailable() {} + }); } private void init() { @@ -114,30 +131,43 @@ public class FavoritesFragment extends Fragment { if (context == null) return false; new MaterialAlertDialogBuilder(context) .setMessage(getString(R.string.quick_access_confirm_delete, model.getQuery())) - .setPositiveButton(R.string.yes, (d, which) -> { - Utils.dataBox.deleteFavorite(model.getQuery(), model.getType()); - d.dismiss(); - favoritesViewModel.getList().postValue(Utils.dataBox.getAllFavorites()); - }) + .setPositiveButton(R.string.yes, (d, which) -> favoriteRepository + .deleteFavorite(model.getQuery(), model.getType(), new RepositoryCallback() { + @Override + public void onSuccess(final Void result) { + d.dismiss(); + favoriteRepository.getAllFavorites(new RepositoryCallback>() { + @Override + public void onSuccess(final List result) { + favoritesViewModel.getList().postValue(result); + } + + @Override + public void onDataNotAvailable() {} + }); + } + + @Override + public void onDataNotAvailable() {} + })) .setNegativeButton(R.string.no, null) .show(); return true; }); binding.favoriteList.setAdapter(adapter); - // favoritesViewModel.getList().observe(getViewLifecycleOwner(), adapter::submitList); } - private void fetchMissingInfo(final List allFavorites) { + private void fetchMissingInfo(final List allFavorites) { final Runnable runnable = () -> { - final List updatedList = new ArrayList<>(allFavorites); + final List updatedList = new ArrayList<>(allFavorites); // cyclic barrier is to make the async calls synchronous final CyclicBarrier cyclicBarrier = new CyclicBarrier(2, () -> { // Log.d(TAG, "fetchMissingInfo: barrier action"); favoritesViewModel.getList().postValue(new ArrayList<>(updatedList)); }); try { - for (final DataBox.FavoriteModel model : allFavorites) { + for (final Favorite model : allFavorites) { cyclicBarrier.reset(); // if the model has missing pic or display name (for user and location), fetch those details switch (model.getType()) { @@ -145,27 +175,37 @@ public class FavoritesFragment extends Fragment { if (TextUtils.isEmpty(model.getDisplayName()) || TextUtils.isEmpty(model.getPicUrl())) { new LocationFetcher(model.getQuery(), result -> { - try { - if (result == null) return; - final int i = updatedList.indexOf(model); - updatedList.remove(i); - final DataBox.FavoriteModel updated = new DataBox.FavoriteModel( - model.getId(), - model.getQuery(), - model.getType(), - result.getName(), - result.getSdProfilePic(), - model.getDateAdded() - ); - Utils.dataBox.addOrUpdateFavorite(updated); - updatedList.add(i, updated); - } finally { - try { - cyclicBarrier.await(); - } catch (BrokenBarrierException | InterruptedException e) { - Log.e(TAG, "fetchMissingInfo: ", e); + if (result == null) return; + final int i = updatedList.indexOf(model); + updatedList.remove(i); + final Favorite updated = new Favorite( + model.getId(), + model.getQuery(), + model.getType(), + result.getName(), + result.getSdProfilePic(), + model.getDateAdded() + ); + favoriteRepository.insertOrUpdateFavorite(updated, new RepositoryCallback() { + @Override + public void onSuccess(final Favorite result) { + updatedList.add(i, updated); + try { + cyclicBarrier.await(); + } catch (BrokenBarrierException | InterruptedException e) { + Log.e(TAG, "fetchMissingInfo: ", e); + } + } + + @Override + public void onDataNotAvailable() { + try { + cyclicBarrier.await(); + } catch (BrokenBarrierException | InterruptedException e) { + Log.e(TAG, "fetchMissingInfo: ", e); + } } - } + }); }).execute(); cyclicBarrier.await(); } @@ -174,27 +214,37 @@ public class FavoritesFragment extends Fragment { if (TextUtils.isEmpty(model.getDisplayName()) || TextUtils.isEmpty(model.getPicUrl())) { new ProfileFetcher(model.getQuery(), result -> { - try { - if (result == null) return; - final int i = updatedList.indexOf(model); - updatedList.remove(i); - final DataBox.FavoriteModel updated = new DataBox.FavoriteModel( - model.getId(), - model.getQuery(), - model.getType(), - result.getName(), - result.getSdProfilePic(), - model.getDateAdded() - ); - Utils.dataBox.addOrUpdateFavorite(updated); - updatedList.add(i, updated); - } finally { - try { - cyclicBarrier.await(); - } catch (BrokenBarrierException | InterruptedException e) { - Log.e(TAG, "fetchMissingInfo: ", e); + if (result == null) return; + final int i = updatedList.indexOf(model); + updatedList.remove(i); + final Favorite updated = new Favorite( + model.getId(), + model.getQuery(), + model.getType(), + result.getName(), + result.getSdProfilePic(), + model.getDateAdded() + ); + favoriteRepository.insertOrUpdateFavorite(updated, new RepositoryCallback() { + @Override + public void onSuccess(final Favorite result) { + try { + cyclicBarrier.await(); + } catch (BrokenBarrierException | InterruptedException e) { + Log.e(TAG, "fetchMissingInfo: ", e); + } + } + + @Override + public void onDataNotAvailable() { + try { + cyclicBarrier.await(); + } catch (BrokenBarrierException | InterruptedException e) { + Log.e(TAG, "fetchMissingInfo: ", e); + } } - } + }); + updatedList.add(i, updated); }).execute(); cyclicBarrier.await(); } diff --git a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java index 593e5750..3400d4f6 100644 --- a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java @@ -49,15 +49,19 @@ import awais.instagrabber.asyncs.PostFetcher; import awais.instagrabber.customviews.PrimaryActionModeCallback; import awais.instagrabber.databinding.FragmentHashtagBinding; import awais.instagrabber.databinding.LayoutHashtagDetailsBinding; +import awais.instagrabber.db.datasources.FavoriteDataSource; +import awais.instagrabber.db.entities.Favorite; +import awais.instagrabber.db.repositories.FavoriteRepository; +import awais.instagrabber.db.repositories.RepositoryCallback; import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment; import awais.instagrabber.models.FeedModel; import awais.instagrabber.models.HashtagModel; import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.models.StoryModel; import awais.instagrabber.models.enums.FavoriteType; +import awais.instagrabber.utils.AppExecutors; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; -import awais.instagrabber.utils.DataBox; import awais.instagrabber.utils.DownloadUtils; import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.Utils; @@ -454,40 +458,61 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe } else { hashtagDetailsBinding.btnFollowTag.setVisibility(View.GONE); } - final DataBox.FavoriteModel favorite = Utils.dataBox.getFavorite(hashtag.substring(1), FavoriteType.HASHTAG); - final boolean isFav = favorite != null; hashtagDetailsBinding.favChip.setVisibility(View.VISIBLE); - hashtagDetailsBinding.favChip.setChipIconResource(isFav ? R.drawable.ic_star_check_24 - : R.drawable.ic_outline_star_plus_24); - hashtagDetailsBinding.favChip.setText(isFav ? R.string.favorite_short : R.string.add_to_favorites); - hashtagDetailsBinding.favChip.setOnClickListener(v -> { - final DataBox.FavoriteModel fav = Utils.dataBox.getFavorite(hashtag.substring(1), FavoriteType.HASHTAG); - final boolean isFavorite = fav != null; - final String message; - if (isFavorite) { - Utils.dataBox.deleteFavorite(hashtag.substring(1), FavoriteType.HASHTAG); - hashtagDetailsBinding.favChip.setText(R.string.add_to_favorites); - hashtagDetailsBinding.favChip.setChipIconResource(R.drawable.ic_outline_star_plus_24); - message = getString(R.string.removed_from_favs); - } else { - Utils.dataBox.addOrUpdateFavorite(new DataBox.FavoriteModel( - -1, - hashtag.substring(1), - FavoriteType.HASHTAG, - hashtagModel.getName(), - null, - new Date() - )); - hashtagDetailsBinding.favChip.setText(R.string.favorite_short); + final FavoriteRepository favoriteRepository = FavoriteRepository + .getInstance(new AppExecutors(), FavoriteDataSource.getInstance(getContext())); + favoriteRepository.getFavorite(hashtag.substring(1), FavoriteType.HASHTAG, new RepositoryCallback() { + @Override + public void onSuccess(final Favorite result) { hashtagDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24); - message = getString(R.string.added_to_favs); + hashtagDetailsBinding.favChip.setText(R.string.favorite_short); + } + + @Override + public void onDataNotAvailable() { + hashtagDetailsBinding.favChip.setChipIconResource(R.drawable.ic_outline_star_plus_24); + hashtagDetailsBinding.favChip.setText(R.string.add_to_favorites); } - final Snackbar snackbar = Snackbar.make(root, message, BaseTransientBottomBar.LENGTH_LONG); - snackbar.setAction(R.string.ok, v1 -> snackbar.dismiss()) - .setAnimationMode(BaseTransientBottomBar.ANIMATION_MODE_SLIDE) - .setAnchorView(fragmentActivity.getBottomNavView()) - .show(); }); + hashtagDetailsBinding.favChip.setOnClickListener( + v -> favoriteRepository.getFavorite(hashtag.substring(1), FavoriteType.HASHTAG, new RepositoryCallback() { + @Override + public void onSuccess(final Favorite result) { + favoriteRepository.deleteFavorite(hashtag.substring(1), FavoriteType.HASHTAG, new RepositoryCallback() { + @Override + public void onSuccess(final Void result) { + hashtagDetailsBinding.favChip.setText(R.string.add_to_favorites); + hashtagDetailsBinding.favChip.setChipIconResource(R.drawable.ic_outline_star_plus_24); + showSnackbar(getString(R.string.removed_from_favs)); + } + + @Override + public void onDataNotAvailable() {} + }); + } + + @Override + public void onDataNotAvailable() { + favoriteRepository.insertOrUpdateFavorite(new Favorite( + -1, + hashtag.substring(1), + FavoriteType.HASHTAG, + hashtagModel.getName(), + null, + new Date() + ), new RepositoryCallback() { + @Override + public void onSuccess(final Favorite result) { + hashtagDetailsBinding.favChip.setText(R.string.favorite_short); + hashtagDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24); + showSnackbar(getString(R.string.added_to_favs)); + } + + @Override + public void onDataNotAvailable() {} + }); + } + })); hashtagDetailsBinding.mainHashtagImage.setImageURI(hashtagModel.getSdProfilePic()); final String postCount = String.valueOf(hashtagModel.getPostCount()); final SpannableStringBuilder span = new SpannableStringBuilder(getString(R.string.main_posts_count_inline, postCount)); @@ -504,6 +529,14 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe }); } + private void showSnackbar(final String message) { + final Snackbar snackbar = Snackbar.make(root, message, BaseTransientBottomBar.LENGTH_LONG); + snackbar.setAction(R.string.ok, v1 -> snackbar.dismiss()) + .setAnimationMode(BaseTransientBottomBar.ANIMATION_MODE_SLIDE) + .setAnchorView(fragmentActivity.getBottomNavView()) + .show(); + } + private void fetchStories() { if (!isLoggedIn) return; storiesFetching = true; diff --git a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java index c66543fc..0ddde3fb 100644 --- a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java @@ -52,15 +52,19 @@ import awais.instagrabber.asyncs.PostFetcher; import awais.instagrabber.customviews.PrimaryActionModeCallback; import awais.instagrabber.databinding.FragmentLocationBinding; import awais.instagrabber.databinding.LayoutLocationDetailsBinding; +import awais.instagrabber.db.datasources.FavoriteDataSource; +import awais.instagrabber.db.entities.Favorite; +import awais.instagrabber.db.repositories.FavoriteRepository; +import awais.instagrabber.db.repositories.RepositoryCallback; import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment; import awais.instagrabber.models.FeedModel; import awais.instagrabber.models.LocationModel; import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.models.StoryModel; import awais.instagrabber.models.enums.FavoriteType; +import awais.instagrabber.utils.AppExecutors; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; -import awais.instagrabber.utils.DataBox; import awais.instagrabber.utils.DownloadUtils; import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.Utils; @@ -443,39 +447,63 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR locationDetailsBinding.locationUrl.setVisibility(View.VISIBLE); locationDetailsBinding.locationUrl.setText(TextUtils.getSpannableUrl(url)); } - final DataBox.FavoriteModel favorite = Utils.dataBox.getFavorite(locationId, FavoriteType.LOCATION); - final boolean isFav = favorite != null; + final FavoriteDataSource dataSource = FavoriteDataSource.getInstance(getContext()); + final FavoriteRepository favoriteRepository = FavoriteRepository.getInstance(new AppExecutors(), dataSource); locationDetailsBinding.favChip.setVisibility(View.VISIBLE); - locationDetailsBinding.favChip.setChipIconResource(isFav ? R.drawable.ic_star_check_24 - : R.drawable.ic_outline_star_plus_24); - locationDetailsBinding.favChip.setText(isFav ? R.string.favorite_short : R.string.add_to_favorites); - locationDetailsBinding.favChip.setOnClickListener(v -> { - final DataBox.FavoriteModel fav = Utils.dataBox.getFavorite(locationId, FavoriteType.LOCATION); - final boolean isFavorite = fav != null; - final String message; - if (isFavorite) { - Utils.dataBox.deleteFavorite(locationId, FavoriteType.LOCATION); - locationDetailsBinding.favChip.setText(R.string.add_to_favorites); - locationDetailsBinding.favChip.setChipIconResource(R.drawable.ic_outline_star_plus_24); - message = getString(R.string.removed_from_favs); - } else { - Utils.dataBox.addOrUpdateFavorite(new DataBox.FavoriteModel( - -1, - locationId, - FavoriteType.LOCATION, - locationModel.getName(), - locationModel.getSdProfilePic(), - new Date() - )); - locationDetailsBinding.favChip.setText(R.string.favorite_short); + favoriteRepository.getFavorite(locationId, FavoriteType.LOCATION, new RepositoryCallback() { + @Override + public void onSuccess(final Favorite result) { + locationDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24); locationDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24); - message = getString(R.string.added_to_favs); + locationDetailsBinding.favChip.setText(R.string.favorite_short); + } + + @Override + public void onDataNotAvailable() { + locationDetailsBinding.favChip.setChipIconResource(R.drawable.ic_outline_star_plus_24); + locationDetailsBinding.favChip.setChipIconResource(R.drawable.ic_outline_star_plus_24); + locationDetailsBinding.favChip.setText(R.string.add_to_favorites); } - final Snackbar snackbar = Snackbar.make(root, message, BaseTransientBottomBar.LENGTH_LONG); - snackbar.setAction(R.string.ok, v1 -> snackbar.dismiss()) - .setAnimationMode(BaseTransientBottomBar.ANIMATION_MODE_SLIDE) - .setAnchorView(fragmentActivity.getBottomNavView()) - .show(); + }); + locationDetailsBinding.favChip.setOnClickListener(v -> { + favoriteRepository.getFavorite(locationId, FavoriteType.LOCATION, new RepositoryCallback() { + @Override + public void onSuccess(final Favorite result) { + favoriteRepository.deleteFavorite(locationId, FavoriteType.LOCATION, new RepositoryCallback() { + @Override + public void onSuccess(final Void result) { + locationDetailsBinding.favChip.setText(R.string.add_to_favorites); + locationDetailsBinding.favChip.setChipIconResource(R.drawable.ic_outline_star_plus_24); + showSnackbar(getString(R.string.removed_from_favs)); + } + + @Override + public void onDataNotAvailable() {} + }); + } + + @Override + public void onDataNotAvailable() { + favoriteRepository.insertOrUpdateFavorite(new Favorite( + -1, + locationId, + FavoriteType.LOCATION, + locationModel.getName(), + locationModel.getSdProfilePic(), + new Date() + ), new RepositoryCallback() { + @Override + public void onSuccess(final Favorite result) { + locationDetailsBinding.favChip.setText(R.string.favorite_short); + locationDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24); + showSnackbar(getString(R.string.added_to_favs)); + } + + @Override + public void onDataNotAvailable() {} + }); + } + }); }); locationDetailsBinding.mainLocationImage.setOnClickListener(v -> { if (hasStories) { @@ -487,6 +515,14 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR }); } + private void showSnackbar(final String message) { + final Snackbar snackbar = Snackbar.make(root, message, BaseTransientBottomBar.LENGTH_LONG); + snackbar.setAction(R.string.ok, v1 -> snackbar.dismiss()) + .setAnimationMode(BaseTransientBottomBar.ANIMATION_MODE_SLIDE) + .setAnchorView(fragmentActivity.getBottomNavView()) + .show(); + } + private void fetchStories() { if (isLoggedIn) { storiesFetching = true; diff --git a/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java index 651bd8b3..f8733382 100644 --- a/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java @@ -43,6 +43,8 @@ import awais.instagrabber.webservices.FriendshipService; import awais.instagrabber.webservices.NewsService; import awais.instagrabber.webservices.ServiceCallback; +import static awais.instagrabber.utils.Utils.settingsHelper; + public final class NotificationsViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener { private static final String TAG = "NotificationsViewer"; @@ -189,10 +191,12 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe } private void init() { + final Context context = getContext(); + CookieUtils.setupCookies(settingsHelper.getString(Constants.COOKIE)); binding.swipeRefreshLayout.setOnRefreshListener(this); notificationViewModel = new ViewModelProvider(this).get(NotificationViewModel.class); final NotificationsAdapter adapter = new NotificationsAdapter(clickListener, mentionClickListener); - binding.rvComments.setLayoutManager(new LinearLayoutManager(getContext())); + binding.rvComments.setLayoutManager(new LinearLayoutManager(context)); binding.rvComments.setAdapter(adapter); notificationViewModel.getList().observe(getViewLifecycleOwner(), adapter::submitList); onRefresh(); diff --git a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java index 5743bdf7..2dc2238f 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java @@ -63,6 +63,13 @@ import awais.instagrabber.customviews.PrimaryActionModeCallback; import awais.instagrabber.customviews.PrimaryActionModeCallback.CallbacksHelper; import awais.instagrabber.databinding.FragmentProfileBinding; import awais.instagrabber.databinding.LayoutProfileDetailsBinding; +import awais.instagrabber.db.datasources.AccountDataSource; +import awais.instagrabber.db.datasources.FavoriteDataSource; +import awais.instagrabber.db.entities.Account; +import awais.instagrabber.db.entities.Favorite; +import awais.instagrabber.db.repositories.AccountRepository; +import awais.instagrabber.db.repositories.FavoriteRepository; +import awais.instagrabber.db.repositories.RepositoryCallback; import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment; import awais.instagrabber.dialogs.ProfilePicDialogFragment; import awais.instagrabber.fragments.PostViewV2Fragment; @@ -75,9 +82,9 @@ import awais.instagrabber.models.enums.FavoriteType; import awais.instagrabber.models.enums.PostItemType; import awais.instagrabber.repositories.responses.FriendshipRepoChangeRootResponse; import awais.instagrabber.repositories.responses.FriendshipRepoRestrictRootResponse; +import awais.instagrabber.utils.AppExecutors; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; -import awais.instagrabber.utils.DataBox; import awais.instagrabber.utils.DownloadUtils; import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.Utils; @@ -283,6 +290,8 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe } }; private LayoutProfileDetailsBinding profileDetailsBinding; + private AccountRepository accountRepository; + private FavoriteRepository favoriteRepository; @Override public void onCreate(@Nullable final Bundle savedInstanceState) { @@ -290,6 +299,9 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe fragmentActivity = (MainActivity) requireActivity(); friendshipService = FriendshipService.getInstance(); storiesService = StoriesService.getInstance(); + final AppExecutors appExecutors = new AppExecutors(); + accountRepository = AccountRepository.getInstance(appExecutors, AccountDataSource.getInstance(getContext())); + favoriteRepository = FavoriteRepository.getInstance(appExecutors, FavoriteDataSource.getInstance(getContext())); setHasOptionsMenu(true); } @@ -498,19 +510,26 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe setUsernameDelayed(); fetchProfileDetails(); }; - boolean found = false; - final DataBox.CookieModel cookieModel = Utils.dataBox.getCookie(uid); - if (cookieModel != null) { - final String username = cookieModel.getUsername(); - if (!TextUtils.isEmpty(username)) { - found = true; - fetchListener.onResult("@" + username); + accountRepository.getAccount(uid, new RepositoryCallback() { + @Override + public void onSuccess(final Account account) { + boolean found = false; + if (account != null) { + final String username = account.getUsername(); + if (!TextUtils.isEmpty(username)) { + found = true; + fetchListener.onResult("@" + username); + } + } + if (!found) { + // if not in database, fetch info from instagram + new UsernameFetcher(uid, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } } - } - if (!found) { - // if not in database, fetch info from instagram - new UsernameFetcher(uid, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } + + @Override + public void onDataNotAvailable() {} + }); return; } fetchProfileDetails(); @@ -556,9 +575,19 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe setupButtons(profileId, myId); if (!profileId.equals(myId)) { profileDetailsBinding.favCb.setVisibility(View.VISIBLE); - final boolean isFav = Utils.dataBox.getFavorite(username.substring(1), FavoriteType.USER) != null; - profileDetailsBinding.favCb.setChecked(isFav); - profileDetailsBinding.favCb.setButtonDrawable(isFav ? R.drawable.ic_star_check_24 : R.drawable.ic_outline_star_plus_24); + favoriteRepository.getFavorite(username.substring(1), FavoriteType.USER, new RepositoryCallback() { + @Override + public void onSuccess(final Favorite result) { + profileDetailsBinding.favCb.setChecked(true); + profileDetailsBinding.favCb.setButtonDrawable(R.drawable.ic_star_check_24); + } + + @Override + public void onDataNotAvailable() { + profileDetailsBinding.favCb.setChecked(false); + profileDetailsBinding.favCb.setButtonDrawable(R.drawable.ic_outline_star_plus_24); + } + }); } else { profileDetailsBinding.favCb.setVisibility(View.GONE); } @@ -842,41 +871,67 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe }); profileDetailsBinding.favCb.setOnCheckedChangeListener((buttonView, isChecked) -> { // do not do anything if state matches the db, as listener is set before profile details are set + final Context context = getContext(); + if (context == null) return; final String finalUsername = username.startsWith("@") ? username.substring(1) : username; - final DataBox.FavoriteModel favorite = Utils.dataBox.getFavorite(finalUsername, FavoriteType.USER); - if ((isChecked && favorite != null) || (!isChecked && favorite == null)) { - return; - } - buttonView.setVisibility(View.GONE); - profileDetailsBinding.favProgress.setVisibility(View.VISIBLE); - final String message; - if (isChecked) { - final DataBox.FavoriteModel model = new DataBox.FavoriteModel( - -1, - finalUsername, - FavoriteType.USER, - profileModel.getName(), - profileModel.getSdProfilePic(), - new Date() - ); - Utils.dataBox.addOrUpdateFavorite(model); - profileDetailsBinding.favCb.setButtonDrawable(R.drawable.ic_star_check_24); - message = getString(R.string.added_to_favs); - } else { - Utils.dataBox.deleteFavorite(finalUsername, FavoriteType.USER); - message = getString(R.string.removed_from_favs); - profileDetailsBinding.favCb.setButtonDrawable(R.drawable.ic_outline_star_plus_24); - } - final Snackbar snackbar = Snackbar.make(root, message, BaseTransientBottomBar.LENGTH_LONG); - snackbar.setAction(R.string.ok, v -> snackbar.dismiss()) - .setAnimationMode(BaseTransientBottomBar.ANIMATION_MODE_SLIDE) - .setAnchorView(fragmentActivity.getBottomNavView()) - .show(); - profileDetailsBinding.favProgress.setVisibility(View.GONE); - profileDetailsBinding.favCb.setVisibility(View.VISIBLE); + favoriteRepository.getFavorite(finalUsername, FavoriteType.USER, new RepositoryCallback() { + @Override + public void onSuccess(final Favorite result) { + if (isChecked) return; // already a fav + buttonView.setVisibility(View.GONE); + profileDetailsBinding.favProgress.setVisibility(View.VISIBLE); + favoriteRepository.deleteFavorite(finalUsername, FavoriteType.USER, new RepositoryCallback() { + @Override + public void onSuccess(final Void result) { + profileDetailsBinding.favCb.setButtonDrawable(R.drawable.ic_outline_star_plus_24); + profileDetailsBinding.favProgress.setVisibility(View.GONE); + profileDetailsBinding.favCb.setVisibility(View.VISIBLE); + showSnackbar(getString(R.string.removed_from_favs)); + } + + @Override + public void onDataNotAvailable() {} + }); + } + + @Override + public void onDataNotAvailable() { + if (!isChecked) return; // not in fav already + buttonView.setVisibility(View.GONE); + profileDetailsBinding.favProgress.setVisibility(View.VISIBLE); + final Favorite model = new Favorite( + -1, + finalUsername, + FavoriteType.USER, + profileModel.getName(), + profileModel.getSdProfilePic(), + new Date() + ); + favoriteRepository.insertOrUpdateFavorite(model, new RepositoryCallback() { + @Override + public void onSuccess(final Favorite result) { + profileDetailsBinding.favCb.setButtonDrawable(R.drawable.ic_star_check_24); + profileDetailsBinding.favProgress.setVisibility(View.GONE); + profileDetailsBinding.favCb.setVisibility(View.VISIBLE); + showSnackbar(getString(R.string.added_to_favs)); + } + + @Override + public void onDataNotAvailable() {} + }); + } + }); }); } + private void showSnackbar(final String message) { + final Snackbar snackbar = Snackbar.make(root, message, BaseTransientBottomBar.LENGTH_LONG); + snackbar.setAction(R.string.ok, v -> snackbar.dismiss()) + .setAnimationMode(BaseTransientBottomBar.ANIMATION_MODE_SLIDE) + .setAnchorView(fragmentActivity.getBottomNavView()) + .show(); + } + private void showProfilePicDialog() { if (profileModel != null) { final FragmentManager fragmentManager = getParentFragmentManager(); diff --git a/app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java b/app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java index 74cb4edd..3c8398ea 100644 --- a/app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java @@ -8,6 +8,7 @@ import android.util.Log; import android.view.View; import android.widget.Toast; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; @@ -26,14 +27,17 @@ import awais.instagrabber.BuildConfig; import awais.instagrabber.R; import awais.instagrabber.activities.Login; import awais.instagrabber.databinding.PrefAccountSwitcherBinding; +import awais.instagrabber.db.datasources.AccountDataSource; +import awais.instagrabber.db.entities.Account; +import awais.instagrabber.db.repositories.AccountRepository; +import awais.instagrabber.db.repositories.RepositoryCallback; import awais.instagrabber.dialogs.AccountSwitcherDialogFragment; import awais.instagrabber.repositories.responses.UserInfo; +import awais.instagrabber.utils.AppExecutors; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; -import awais.instagrabber.utils.DataBox; import awais.instagrabber.utils.FlavorTown; import awais.instagrabber.utils.TextUtils; -import awais.instagrabber.utils.Utils; import awais.instagrabber.webservices.ProfileService; import awais.instagrabber.webservices.ServiceCallback; @@ -42,20 +46,23 @@ import static awais.instagrabber.utils.Utils.settingsHelper; public class MorePreferencesFragment extends BasePreferencesFragment { private static final String TAG = "MorePreferencesFragment"; + private final AccountRepository accountRepository; + + public MorePreferencesFragment() { + accountRepository = AccountRepository.getInstance(new AppExecutors(), AccountDataSource.getInstance(getContext())); + } @Override void setupPreferenceScreen(final PreferenceScreen screen) { final String cookie = settingsHelper.getString(Constants.COOKIE); final boolean isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != null; // screen.addPreference(new MoreHeaderPreference(getContext())); - final Context context = getContext(); if (context == null) return; final PreferenceCategory accountCategory = new PreferenceCategory(context); accountCategory.setTitle(R.string.account); accountCategory.setIconSpaceReserved(false); screen.addPreference(accountCategory); - final List allCookies = Utils.dataBox.getAllCookies(); if (isLoggedIn) { accountCategory.setSummary(R.string.account_hint); accountCategory.addPreference(getAccountSwitcherPreference(cookie)); @@ -67,34 +74,59 @@ public class MorePreferencesFragment extends BasePreferencesFragment { settingsHelper.putString(Constants.COOKIE, ""); return true; })); - } else { - if (allCookies != null && allCookies.size() > 0) { - accountCategory.addPreference(getAccountSwitcherPreference(null)); - } - // Need to show something to trigger login activity - accountCategory.addPreference(getPreference(R.string.add_account, R.drawable.ic_add, preference -> { - startActivityForResult(new Intent(getContext(), Login.class), Constants.LOGIN_RESULT_CODE); - return true; - })); } + accountRepository.getAllAccounts(new RepositoryCallback>() { + @Override + public void onSuccess(@NonNull final List accounts) { + if (!isLoggedIn) { + if (accounts.size() > 0) { + accountCategory.addPreference(getAccountSwitcherPreference(null)); + } + // Need to show something to trigger login activity + accountCategory.addPreference(getPreference(R.string.add_account, R.drawable.ic_add, preference -> { + startActivityForResult(new Intent(getContext(), Login.class), Constants.LOGIN_RESULT_CODE); + return true; + })); + } + if (accounts.size() > 0) { + accountCategory + .addPreference(getPreference(R.string.remove_all_acc, null, R.drawable.ic_account_multiple_remove_24, preference -> { + if (getContext() == null) return false; + new AlertDialog.Builder(getContext()) + .setTitle(R.string.logout) + .setMessage(R.string.remove_all_acc_warning) + .setPositiveButton(R.string.yes, (dialog, which) -> { + CookieUtils.removeAllAccounts(context, new RepositoryCallback() { + @Override + public void onSuccess(final Void result) { + shouldRecreate(); + Toast.makeText(context, R.string.logout_success, Toast.LENGTH_SHORT).show(); + settingsHelper.putString(Constants.COOKIE, ""); + } - if (allCookies != null && allCookies.size() > 0) { - accountCategory.addPreference(getPreference(R.string.remove_all_acc, null, R.drawable.ic_account_multiple_remove_24, preference -> { - if (getContext() == null) return false; - new AlertDialog.Builder(getContext()) - .setTitle(R.string.logout) - .setMessage(R.string.remove_all_acc_warning) - .setPositiveButton(R.string.yes, (dialog, which) -> { - CookieUtils.setupCookies("REMOVE"); - shouldRecreate(); - Toast.makeText(context, R.string.logout_success, Toast.LENGTH_SHORT).show(); - settingsHelper.putString(Constants.COOKIE, ""); - }) - .setNegativeButton(R.string.cancel, null) - .show(); - return true; - })); - } + @Override + public void onDataNotAvailable() {} + }); + }) + .setNegativeButton(R.string.cancel, null) + .show(); + return true; + })); + } + } + + @Override + public void onDataNotAvailable() { + Log.d(TAG, "onDataNotAvailable"); + if (!isLoggedIn) { + // Need to show something to trigger login activity + accountCategory.addPreference(getPreference(R.string.add_account, R.drawable.ic_add, preference -> { + startActivityForResult(new Intent(getContext(), Login.class), Constants.LOGIN_RESULT_CODE); + return true; + })); + } + } + }); // final PreferenceCategory generalCategory = new PreferenceCategory(context); // generalCategory.setTitle(R.string.pref_category_general); @@ -163,11 +195,26 @@ public class MorePreferencesFragment extends BasePreferencesFragment { public void onSuccess(final UserInfo result) { // Log.d(TAG, "adding userInfo: " + result); if (result != null) { - Utils.dataBox.addOrUpdateUser(uid, result.getUsername(), cookie, result.getFullName(), result.getProfilePicUrl()); + accountRepository.insertOrUpdateAccount( + uid, + result.getUsername(), + cookie, + result.getFullName(), + result.getProfilePicUrl(), + new RepositoryCallback() { + @Override + public void onSuccess(final Account result) { + final FragmentActivity activity = getActivity(); + if (activity == null) return; + activity.recreate(); + } + + @Override + public void onDataNotAvailable() { + Log.e(TAG, "onDataNotAvailable: insert failed"); + } + }); } - final FragmentActivity activity = getActivity(); - if (activity == null) return; - activity.recreate(); } @Override @@ -181,7 +228,7 @@ public class MorePreferencesFragment extends BasePreferencesFragment { private AccountSwitcherPreference getAccountSwitcherPreference(final String cookie) { final Context context = getContext(); if (context == null) return null; - return new AccountSwitcherPreference(context, cookie, v -> showAccountSwitcherDialog()); + return new AccountSwitcherPreference(context, cookie, accountRepository, v -> showAccountSwitcherDialog()); } private void showAccountSwitcherDialog() { @@ -244,13 +291,16 @@ public class MorePreferencesFragment extends BasePreferencesFragment { public static class AccountSwitcherPreference extends Preference { private final String cookie; + private final AccountRepository accountRepository; private final View.OnClickListener onClickListener; public AccountSwitcherPreference(final Context context, final String cookie, + final AccountRepository accountRepository, final View.OnClickListener onClickListener) { super(context); this.cookie = cookie; + this.accountRepository = accountRepository; this.onClickListener = onClickListener; setLayoutResource(R.layout.pref_account_switcher); } @@ -263,11 +313,20 @@ public class MorePreferencesFragment extends BasePreferencesFragment { final PrefAccountSwitcherBinding binding = PrefAccountSwitcherBinding.bind(root); final String uid = CookieUtils.getUserIdFromCookie(cookie); if (uid == null) return; - final DataBox.CookieModel user = Utils.dataBox.getCookie(uid); - if (user == null) return; - binding.fullName.setText(user.getFullName()); - binding.username.setText("@" + user.getUsername()); - binding.profilePic.setImageURI(user.getProfilePic()); + accountRepository.getAccount(uid, new RepositoryCallback() { + @Override + public void onSuccess(final Account account) { + binding.getRoot().post(() -> { + binding.fullName.setText(account.getFullName()); + binding.username.setText("@" + account.getUsername()); + binding.profilePic.setImageURI(account.getProfilePic()); + binding.getRoot().requestLayout(); + }); + } + + @Override + public void onDataNotAvailable() {} + }); } } } diff --git a/app/src/main/java/awais/instagrabber/utils/AppExecutors.java b/app/src/main/java/awais/instagrabber/utils/AppExecutors.java new file mode 100644 index 00000000..ca5b498b --- /dev/null +++ b/app/src/main/java/awais/instagrabber/utils/AppExecutors.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package awais.instagrabber.utils; + +import android.os.Handler; +import android.os.Looper; + +import androidx.annotation.NonNull; + +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; + +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +/** + * Global executor pools for the whole application. + *

+ * Grouping tasks like this avoids the effects of task starvation (e.g. disk reads don't wait behind + * webservice requests). + */ +public class AppExecutors { + + // private static final int THREAD_COUNT = 3; + + private final Executor diskIO; + // private final Executor networkIO; + private final Executor mainThread; + private final ListeningExecutorService tasksThread; + + private AppExecutors(Executor diskIO, + // Executor networkIO, + Executor mainThread, + ListeningExecutorService tasksThread) { + this.diskIO = diskIO; + // this.networkIO = networkIO; + this.mainThread = mainThread; + this.tasksThread = tasksThread; + } + + public AppExecutors() { + this(Executors.newSingleThreadExecutor(), + // Executors.newFixedThreadPool(THREAD_COUNT), + new MainThreadExecutor(), + MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10))); + } + + public Executor diskIO() { + return diskIO; + } + + // public Executor networkIO() { + // return networkIO; + // } + + + public ListeningExecutorService tasksThread() { + return tasksThread; + } + + public Executor mainThread() { + return mainThread; + } + + private static class MainThreadExecutor implements Executor { + private final Handler mainThreadHandler = new Handler(Looper.getMainLooper()); + + @Override + public void execute(@NonNull Runnable command) { + mainThreadHandler.post(command); + } + } +} diff --git a/app/src/main/java/awais/instagrabber/utils/CookieUtils.java b/app/src/main/java/awais/instagrabber/utils/CookieUtils.java index 918dcf38..5a1a5e12 100644 --- a/app/src/main/java/awais/instagrabber/utils/CookieUtils.java +++ b/app/src/main/java/awais/instagrabber/utils/CookieUtils.java @@ -1,5 +1,6 @@ package awais.instagrabber.utils; +import android.content.Context; import android.util.Log; import android.webkit.CookieManager; @@ -17,9 +18,13 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import awais.instagrabber.BuildConfig; +import awais.instagrabber.db.datasources.AccountDataSource; +import awais.instagrabber.db.repositories.AccountRepository; +import awais.instagrabber.db.repositories.RepositoryCallback; import awaisomereport.LogCollector; public final class CookieUtils { + private static final String TAG = CookieUtils.class.getSimpleName(); public static final CookieManager COOKIE_MANAGER = CookieManager.getInstance(); public static final java.net.CookieManager NET_COOKIE_MANAGER = new java.net.CookieManager(null, CookiePolicy.ACCEPT_ALL); @@ -28,11 +33,7 @@ public final class CookieUtils { if (cookieStore == null || TextUtils.isEmpty(cookieRaw)) { return; } - if (cookieRaw.equals("REMOVE")) { - cookieStore.removeAll(); - Utils.dataBox.deleteAllUserCookies(); - return; - } else if (cookieRaw.equals("LOGOUT")) { + if (cookieRaw.equals("LOGOUT")) { cookieStore.removeAll(); return; } @@ -53,7 +54,19 @@ public final class CookieUtils { } catch (final URISyntaxException e) { if (Utils.logCollector != null) Utils.logCollector.appendException(e, LogCollector.LogFile.UTILS, "setupCookies"); - if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); + if (BuildConfig.DEBUG) Log.e(TAG, "", e); + } + } + + public static void removeAllAccounts(final Context context, final RepositoryCallback callback) { + final CookieStore cookieStore = NET_COOKIE_MANAGER.getCookieStore(); + if (cookieStore == null) return; + cookieStore.removeAll(); + try { + AccountRepository.getInstance(new AppExecutors(), AccountDataSource.getInstance(context)) + .deleteAllAccounts(callback); + } catch (Exception e) { + Log.e(TAG, "setupCookies", e); } } diff --git a/app/src/main/java/awais/instagrabber/utils/DataBox.java b/app/src/main/java/awais/instagrabber/utils/DataBox.java index 73e71b04..3df698f3 100755 --- a/app/src/main/java/awais/instagrabber/utils/DataBox.java +++ b/app/src/main/java/awais/instagrabber/utils/DataBox.java @@ -10,17 +10,13 @@ import android.util.Pair; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.core.util.ObjectsCompat; import java.util.ArrayList; import java.util.Date; import java.util.List; -import awais.instagrabber.BuildConfig; +import awais.instagrabber.db.entities.Favorite; import awais.instagrabber.models.enums.FavoriteType; -import awaisomereport.LogCollector; - -import static awais.instagrabber.utils.Utils.logCollector; public final class DataBox extends SQLiteOpenHelper { private static final String TAG = "DataBox"; @@ -28,22 +24,22 @@ public final class DataBox extends SQLiteOpenHelper { private static DataBox sInstance; private final static int VERSION = 3; - private final static String TABLE_COOKIES = "cookies"; - private final static String TABLE_FAVORITES = "favorites"; - - private final static String KEY_ID = "id"; - private final static String KEY_USERNAME = Constants.EXTRAS_USERNAME; - private final static String KEY_COOKIE = "cookie"; - private final static String KEY_UID = "uid"; - private final static String KEY_FULL_NAME = "full_name"; - private final static String KEY_PROFILE_PIC = "profile_pic"; - - private final static String FAV_COL_ID = "id"; - private final static String FAV_COL_QUERY = "query_text"; - private final static String FAV_COL_TYPE = "type"; - private final static String FAV_COL_DISPLAY_NAME = "display_name"; - private final static String FAV_COL_PIC_URL = "pic_url"; - private final static String FAV_COL_DATE_ADDED = "date_added"; + 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) { if (sInstance == null) sInstance = new DataBox(context.getApplicationContext()); @@ -84,7 +80,7 @@ public final class DataBox extends SQLiteOpenHelper { db.execSQL("ALTER TABLE " + TABLE_COOKIES + " ADD " + KEY_FULL_NAME + " TEXT"); db.execSQL("ALTER TABLE " + TABLE_COOKIES + " ADD " + KEY_PROFILE_PIC + " TEXT"); case 2: - final List oldFavorites = backupOldFavorites(db); + final List 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 + " (" @@ -95,19 +91,41 @@ public final class DataBox extends SQLiteOpenHelper { + FAV_COL_PIC_URL + " TEXT," + FAV_COL_DATE_ADDED + " INTEGER)"); // add the old favorites back - for (final FavoriteModel oldFavorite : oldFavorites) { - addOrUpdateFavorite(db, oldFavorite); + for (final Favorite oldFavorite : oldFavorites) { + insertOrUpdateFavorite(db, oldFavorite); } } 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 backupOldFavorites(@NonNull final SQLiteDatabase db) { + private List 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 oldModels = new ArrayList<>(); + final List oldModels = new ArrayList<>(); final String sql = "SELECT " + "query_text," + "date_added" @@ -122,7 +140,7 @@ public final class DataBox extends SQLiteOpenHelper { if (favoriteTypeQueryPair == null) continue; final FavoriteType type = favoriteTypeQueryPair.first; final String query = favoriteTypeQueryPair.second; - oldModels.add(new FavoriteModel( + oldModels.add(new Favorite( -1, query, type, @@ -162,434 +180,4 @@ public final class DataBox extends SQLiteOpenHelper { } return exists; } - - public final void addOrUpdateFavorite(@NonNull final FavoriteModel model) { - final String query = model.getQuery(); - if (!TextUtils.isEmpty(query)) { - try (final SQLiteDatabase db = getWritableDatabase()) { - db.beginTransaction(); - try { - addOrUpdateFavorite(db, model); - db.setTransactionSuccessful(); - } catch (final Exception e) { - if (logCollector != null) { - logCollector.appendException(e, LogCollector.LogFile.DATA_BOX_FAVORITES, "addOrUpdateFavorite"); - } - if (BuildConfig.DEBUG) { - Log.e(TAG, "Error adding/updating favorite", e); - } - } finally { - db.endTransaction(); - } - } - } - } - - private void addOrUpdateFavorite(@NonNull final SQLiteDatabase db, @NonNull final FavoriteModel 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.insertOrThrow(TABLE_FAVORITES, null, values); - } - } - - public final synchronized void deleteFavorite(@NonNull final String query, @NonNull final FavoriteType type) { - if (!TextUtils.isEmpty(query)) { - try (final SQLiteDatabase db = getWritableDatabase()) { - db.beginTransaction(); - 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(); - } - } - } - } - - @NonNull - public final List getAllFavorites() { - final List favorites = new ArrayList<>(); - final SQLiteDatabase db = getWritableDatabase(); - try (final Cursor cursor = db.rawQuery("SELECT " - + FAV_COL_ID + "," - + FAV_COL_QUERY + "," - + FAV_COL_TYPE + "," - + FAV_COL_DISPLAY_NAME + "," - + FAV_COL_PIC_URL + "," - + FAV_COL_DATE_ADDED - + " FROM " + TABLE_FAVORITES, - null)) { - if (cursor != null && cursor.moveToFirst()) { - db.beginTransaction(); - FavoriteModel tempFav; - do { - FavoriteType type = null; - try { - type = FavoriteType.valueOf(cursor.getString(cursor.getColumnIndex(FAV_COL_TYPE))); - } catch (IllegalArgumentException ignored) {} - tempFav = new FavoriteModel( - 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; - } - - @Nullable - public final FavoriteModel getFavorite(@NonNull final String query, @NonNull final FavoriteType type) { - try (final SQLiteDatabase db = getReadableDatabase(); - final Cursor cursor = db.rawQuery("SELECT " - + FAV_COL_ID + "," - + FAV_COL_QUERY + "," - + FAV_COL_TYPE + "," - + FAV_COL_DISPLAY_NAME + "," - + FAV_COL_PIC_URL + "," - + FAV_COL_DATE_ADDED - + " FROM " + TABLE_FAVORITES - + " WHERE " + FAV_COL_QUERY + "='" + query + "'" - + " AND " + FAV_COL_TYPE + "='" + type.toString() + "'", - 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 FavoriteModel( - 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; - } - - public final void addOrUpdateUser(@NonNull final DataBox.CookieModel cookieModel) { - addOrUpdateUser( - cookieModel.getUid(), - cookieModel.getUsername(), - cookieModel.getCookie(), - cookieModel.getFullName(), - cookieModel.getProfilePic() - ); - } - - public final void addOrUpdateUser(final String uid, - final String username, - final String cookie, - final String fullName, - final String profilePicUrl) { - if (TextUtils.isEmpty(uid)) return; - try (final SQLiteDatabase db = getWritableDatabase()) { - db.beginTransaction(); - try { - final ContentValues values = new ContentValues(); - 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.insertOrThrow(TABLE_COOKIES, null, values); - - db.setTransactionSuccessful(); - } catch (final Exception e) { - if (BuildConfig.DEBUG) Log.e(TAG, "Error", e); - } finally { - db.endTransaction(); - } - } - } - - public final synchronized void delUserCookie(@NonNull final CookieModel cookieModel) { - final String cookieModelUid = cookieModel.getUid(); - if (!TextUtils.isEmpty(cookieModelUid)) { - try (final SQLiteDatabase db = getWritableDatabase()) { - db.beginTransaction(); - try { - final int rowsDeleted = db.delete(TABLE_COOKIES, KEY_UID + "=? AND " + KEY_USERNAME + "=? AND " + KEY_COOKIE + "=?", - new String[]{cookieModelUid, cookieModel.getUsername(), cookieModel.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 deleteAllUserCookies() { - try (final SQLiteDatabase db = getWritableDatabase()) { - 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(); - } - } - } - - @Nullable - public final CookieModel getCookie(final String uid) { - CookieModel cookie = null; - try (final SQLiteDatabase db = getReadableDatabase(); - final Cursor cursor = db.rawQuery( - "SELECT " - + KEY_UID + "," - + KEY_USERNAME + "," - + KEY_COOKIE + "," - + KEY_FULL_NAME + "," - + KEY_PROFILE_PIC - + " FROM " + TABLE_COOKIES - + " WHERE " + KEY_UID + " = ?", - new String[]{uid}) - ) { - if (cursor != null && cursor.moveToFirst()) - cookie = new CookieModel( - 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 - public final List getAllCookies() { - final List cookies = new ArrayList<>(); - try (final SQLiteDatabase db = getReadableDatabase(); - final Cursor cursor = db.rawQuery( - "SELECT " - + KEY_UID + "," - + KEY_USERNAME + "," - + KEY_COOKIE + "," - + KEY_FULL_NAME + "," - + KEY_PROFILE_PIC - + " FROM " + TABLE_COOKIES, null) - ) { - if (cursor != null && cursor.moveToFirst()) { - do { - cookies.add(new CookieModel( - 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 static class CookieModel { - private final String uid; - private final String username; - private final String cookie; - private final String fullName; - private final String profilePic; - private boolean selected; - - public CookieModel(final String uid, - final String username, - final String cookie, - final String fullName, - final String profilePic) { - this.uid = uid; - this.username = username; - this.cookie = cookie; - this.fullName = fullName; - this.profilePic = profilePic; - } - - public String getUid() { - return uid; - } - - public String getUsername() { - return username; - } - - public String getCookie() { - return cookie; - } - - public String getFullName() { - return fullName; - } - - public String getProfilePic() { - return profilePic; - } - - public boolean isSelected() { - return selected; - } - - public void setSelected(final boolean selected) { - this.selected = selected; - } - - public boolean isValid() { - return !TextUtils.isEmpty(uid) - && !TextUtils.isEmpty(username) - && !TextUtils.isEmpty(cookie); - } - - @Override - public boolean equals(final Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - final CookieModel that = (CookieModel) o; - return ObjectsCompat.equals(uid, that.uid) && - ObjectsCompat.equals(username, that.username) && - ObjectsCompat.equals(cookie, that.cookie); - } - - @Override - public int hashCode() { - return ObjectsCompat.hash(uid, username, cookie); - } - - @NonNull - @Override - public String toString() { - return "CookieModel{" + - "uid='" + uid + '\'' + - ", username='" + username + '\'' + - ", cookie='" + cookie + '\'' + - ", fullName='" + fullName + '\'' + - ", profilePic='" + profilePic + '\'' + - ", selected=" + selected + - '}'; - } - } - - public static class FavoriteModel { - private final int id; - private final String query; - private final FavoriteType type; - private final String displayName; - private final String picUrl; - private final Date dateAdded; - - public FavoriteModel(final int id, - final String query, - final FavoriteType type, - final String displayName, - final String picUrl, - final Date dateAdded) { - this.id = id; - this.query = query; - this.type = type; - this.displayName = displayName; - this.picUrl = picUrl; - this.dateAdded = dateAdded; - } - - public int getId() { - return id; - } - - public String getQuery() { - return query; - } - - public FavoriteType getType() { - return type; - } - - public String getDisplayName() { - return displayName; - } - - public String getPicUrl() { - return picUrl; - } - - public Date getDateAdded() { - return dateAdded; - } - - @Override - public boolean equals(final Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - final FavoriteModel that = (FavoriteModel) o; - return id == that.id && - ObjectsCompat.equals(query, that.query) && - type == that.type && - ObjectsCompat.equals(displayName, that.displayName) && - ObjectsCompat.equals(picUrl, that.picUrl) && - ObjectsCompat.equals(dateAdded, that.dateAdded); - } - - @Override - public int hashCode() { - return ObjectsCompat.hash(id, query, type, displayName, picUrl, dateAdded); - } - - @NonNull - @Override - public String toString() { - return "FavoriteModel{" + - "id=" + id + - ", query='" + query + '\'' + - ", type=" + type + - ", displayName='" + displayName + '\'' + - ", picUrl='" + picUrl + '\'' + - ", dateAdded=" + dateAdded + - '}'; - } - } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java b/app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java index 3770bd65..5f195562 100755 --- a/app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java +++ b/app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java @@ -2,6 +2,7 @@ package awais.instagrabber.utils; import android.content.Context; import android.content.SharedPreferences; +import android.os.Handler; import android.util.Base64; import android.util.Log; import android.util.Pair; @@ -18,12 +19,21 @@ import org.json.JSONObject; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.concurrent.CountDownLatch; import awais.instagrabber.BuildConfig; +import awais.instagrabber.db.datasources.AccountDataSource; +import awais.instagrabber.db.datasources.FavoriteDataSource; +import awais.instagrabber.db.entities.Account; +import awais.instagrabber.db.entities.Favorite; +import awais.instagrabber.db.repositories.AccountRepository; +import awais.instagrabber.db.repositories.FavoriteRepository; +import awais.instagrabber.db.repositories.RepositoryCallback; import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.models.enums.FavoriteType; import awais.instagrabber.utils.PasswordUtils.IncorrectPasswordException; @@ -47,37 +57,38 @@ public final class ExportImportUtils { @NonNull final File filePath, final FetchListener fetchListener, @NonNull final Context context) { - final String exportString = getExportString(flags, context); - if (TextUtils.isEmpty(exportString)) return; - final boolean isPass = !TextUtils.isEmpty(password); - byte[] exportBytes = null; - if (isPass) { - final byte[] passwordBytes = password.getBytes(); - final byte[] bytes = new byte[32]; - System.arraycopy(passwordBytes, 0, bytes, 0, Math.min(passwordBytes.length, 32)); - try { - exportBytes = PasswordUtils.enc(exportString, bytes); - } catch (final Exception e) { - if (fetchListener != null) fetchListener.onResult(false); - if (logCollector != null) - logCollector.appendException(e, LogFile.UTILS_EXPORT, "Export::isPass"); - if (BuildConfig.DEBUG) Log.e(TAG, "", e); - } - } else { - exportBytes = Base64.encode(exportString.getBytes(), Base64.DEFAULT | Base64.NO_WRAP | Base64.NO_PADDING); - } - if (exportBytes != null && exportBytes.length > 1) { - try (final FileOutputStream fos = new FileOutputStream(filePath)) { - fos.write(isPass ? 'A' : 'Z'); - fos.write(exportBytes); - if (fetchListener != null) fetchListener.onResult(true); - } catch (final Exception e) { - if (fetchListener != null) fetchListener.onResult(false); - if (logCollector != null) - logCollector.appendException(e, LogFile.UTILS_EXPORT, "Export::notPass"); - if (BuildConfig.DEBUG) Log.e(TAG, "", e); + getExportString(flags, context, exportString -> { + if (TextUtils.isEmpty(exportString)) return; + final boolean isPass = !TextUtils.isEmpty(password); + byte[] exportBytes = null; + if (isPass) { + final byte[] passwordBytes = password.getBytes(); + final byte[] bytes = new byte[32]; + System.arraycopy(passwordBytes, 0, bytes, 0, Math.min(passwordBytes.length, 32)); + try { + exportBytes = PasswordUtils.enc(exportString, bytes); + } catch (final Exception e) { + if (fetchListener != null) fetchListener.onResult(false); + if (logCollector != null) + logCollector.appendException(e, LogFile.UTILS_EXPORT, "Export::isPass"); + if (BuildConfig.DEBUG) Log.e(TAG, "", e); + } + } else { + exportBytes = Base64.encode(exportString.getBytes(), Base64.DEFAULT | Base64.NO_WRAP | Base64.NO_PADDING); } - } else if (fetchListener != null) fetchListener.onResult(false); + if (exportBytes != null && exportBytes.length > 1) { + try (final FileOutputStream fos = new FileOutputStream(filePath)) { + fos.write(isPass ? 'A' : 'Z'); + fos.write(exportBytes); + if (fetchListener != null) fetchListener.onResult(true); + } catch (final Exception e) { + if (fetchListener != null) fetchListener.onResult(false); + if (logCollector != null) + logCollector.appendException(e, LogFile.UTILS_EXPORT, "Export::notPass"); + if (BuildConfig.DEBUG) Log.e(TAG, "", e); + } + } else if (fetchListener != null) fetchListener.onResult(false); + }); } public static void importData(@NonNull final Context context, @@ -99,7 +110,8 @@ public final class ExportImportUtils { final byte[] passwordBytes = password.getBytes(); final byte[] bytes = new byte[32]; System.arraycopy(passwordBytes, 0, bytes, 0, Math.min(passwordBytes.length, 32)); - importJson(new String(PasswordUtils.dec(builder.toString(), bytes)), + importJson(context, + new String(PasswordUtils.dec(builder.toString(), bytes)), flags, fetchListener); } catch (final IncorrectPasswordException e) { @@ -111,7 +123,8 @@ public final class ExportImportUtils { if (BuildConfig.DEBUG) Log.e(TAG, "Error importing backup", e); } } else if (configType == 'Z') { - importJson(new String(Base64.decode(builder.toString(), Base64.DEFAULT | Base64.NO_PADDING | Base64.NO_WRAP)), + importJson(context, + new String(Base64.decode(builder.toString(), Base64.DEFAULT | Base64.NO_PADDING | Base64.NO_WRAP)), flags, fetchListener); @@ -129,7 +142,8 @@ public final class ExportImportUtils { } } - private static void importJson(@NonNull final String json, + private static void importJson(final Context context, + @NonNull final String json, @ExportImportFlags final int flags, final FetchListener fetchListener) { try { @@ -138,10 +152,10 @@ public final class ExportImportUtils { importSettings(jsonObject); } if ((flags & FLAG_COOKIES) == FLAG_COOKIES && jsonObject.has("cookies")) { - importAccounts(jsonObject); + importAccounts(context, jsonObject); } if ((flags & FLAG_FAVORITES) == FLAG_FAVORITES && jsonObject.has("favs")) { - importFavorites(jsonObject); + importFavorites(context, jsonObject); } if (fetchListener != null) fetchListener.onResult(true); } catch (final Exception e) { @@ -151,7 +165,7 @@ public final class ExportImportUtils { } } - private static void importFavorites(final JSONObject jsonObject) throws JSONException { + private static void importFavorites(final Context context, final JSONObject jsonObject) throws JSONException { final JSONArray favs = jsonObject.getJSONArray("favs"); for (int i = 0; i < favs.length(); i++) { final JSONObject favsObject = favs.getJSONObject(i); @@ -175,7 +189,7 @@ public final class ExportImportUtils { if (query == null || favoriteType == null) { continue; } - final DataBox.FavoriteModel favoriteModel = new DataBox.FavoriteModel( + final Favorite favorite = new Favorite( -1, query, favoriteType, @@ -184,41 +198,55 @@ public final class ExportImportUtils { : favsObject.optString("pic_url"), new Date(favsObject.getLong("d"))); // Log.d(TAG, "importJson: favoriteModel: " + favoriteModel); - Utils.dataBox.addOrUpdateFavorite(favoriteModel); + FavoriteRepository.getInstance(new AppExecutors(), FavoriteDataSource.getInstance(context)) + .insertOrUpdateFavorite(favorite, null); } } - private static void importAccounts(final JSONObject jsonObject) throws JSONException { - final JSONArray cookies = jsonObject.getJSONArray("cookies"); - for (int i = 0; i < cookies.length(); i++) { - final JSONObject cookieObject = cookies.getJSONObject(i); - final DataBox.CookieModel cookieModel = new DataBox.CookieModel( - cookieObject.optString("i"), - cookieObject.optString("u"), - cookieObject.optString("c"), - cookieObject.optString("full_name"), - cookieObject.optString("profile_pic") - ); - if (!cookieModel.isValid()) continue; - // Log.d(TAG, "importJson: cookieModel: " + cookieModel); - Utils.dataBox.addOrUpdateUser(cookieModel); + private static void importAccounts(final Context context, + final JSONObject jsonObject) { + final List accounts = new ArrayList<>(); + try { + final JSONArray cookies = jsonObject.getJSONArray("cookies"); + for (int i = 0; i < cookies.length(); i++) { + final JSONObject cookieObject = cookies.getJSONObject(i); + final Account account = new Account( + -1, + cookieObject.optString("i"), + cookieObject.optString("u"), + cookieObject.optString("c"), + cookieObject.optString("full_name"), + cookieObject.optString("profile_pic") + ); + if (!account.isValid()) continue; + accounts.add(account); + } + } catch (Exception e) { + Log.e(TAG, "importAccounts: Error parsing json", e); + return; } + AccountRepository.getInstance(new AppExecutors(), AccountDataSource.getInstance(context)) + .insertOrUpdateAccounts(accounts, null); } - private static void importSettings(final JSONObject jsonObject) throws JSONException { - final JSONObject objSettings = jsonObject.getJSONObject("settings"); - final Iterator keys = objSettings.keys(); - while (keys.hasNext()) { - final String key = keys.next(); - final Object val = objSettings.opt(key); - // Log.d(TAG, "importJson: key: " + key + ", val: " + val); - if (val instanceof String) { - settingsHelper.putString(key, (String) val); - } else if (val instanceof Integer) { - settingsHelper.putInteger(key, (int) val); - } else if (val instanceof Boolean) { - settingsHelper.putBoolean(key, (boolean) val); + private static void importSettings(final JSONObject jsonObject) { + try { + final JSONObject objSettings = jsonObject.getJSONObject("settings"); + final Iterator keys = objSettings.keys(); + while (keys.hasNext()) { + final String key = keys.next(); + final Object val = objSettings.opt(key); + // Log.d(TAG, "importJson: key: " + key + ", val: " + val); + if (val instanceof String) { + settingsHelper.putString(key, (String) val); + } else if (val instanceof Integer) { + settingsHelper.putInteger(key, (int) val); + } else if (val instanceof Boolean) { + settingsHelper.putBoolean(key, (boolean) val); + } } + } catch (Exception e) { + Log.e(TAG, "importSettings error", e); } } @@ -234,28 +262,62 @@ public final class ExportImportUtils { return false; } - @Nullable - private static String getExportString(@ExportImportFlags final int flags, - @NonNull final Context context) { - String result = null; - try { - final JSONObject jsonObject = new JSONObject(); - if ((flags & FLAG_SETTINGS) == FLAG_SETTINGS) { - jsonObject.put("settings", getSettings(context)); - } - if ((flags & FLAG_COOKIES) == FLAG_COOKIES) { - jsonObject.put("cookies", getCookies()); - } - if ((flags & FLAG_FAVORITES) == FLAG_FAVORITES) { - jsonObject.put("favs", getFavorites()); + //todo Need to improve logic + private static void getExportString(@ExportImportFlags final int flags, + @NonNull final Context context, + final OnExportStringCreatedCallback callback) { + final AppExecutors appExecutors = new AppExecutors(); + final Handler innerHandler = new Handler(); + appExecutors.tasksThread().execute(() -> { + final CountDownLatch responseWaiter = new CountDownLatch(3); + try { + final JSONObject jsonObject = new JSONObject(); + innerHandler.post(() -> { + if ((flags & FLAG_SETTINGS) == FLAG_SETTINGS) { + try { + jsonObject.put("settings", getSettings(context)); + } catch (JSONException e) { + Log.e(TAG, "getExportString: ", e); + } + } + responseWaiter.countDown(); + }); + innerHandler.post(() -> { + if ((flags & FLAG_COOKIES) == FLAG_COOKIES) { + getCookies(context, array -> { + try { + jsonObject.put("cookies", array); + } catch (JSONException e) { + Log.e(TAG, "error getting accounts", e); + } + responseWaiter.countDown(); + }); + return; + } + responseWaiter.countDown(); + }); + innerHandler.post(() -> { + if ((flags & FLAG_FAVORITES) == FLAG_FAVORITES) { + getFavorites(context, array -> { + try { + jsonObject.put("favs", array); + } catch (JSONException e) { + Log.e(TAG, "getExportString: ", e); + } + responseWaiter.countDown(); + }); + return; + } + responseWaiter.countDown(); + }); + responseWaiter.await(); + callback.onCreated(jsonObject.toString()); + } catch (final Exception e) { + if (logCollector != null) logCollector.appendException(e, LogFile.UTILS_EXPORT, "getExportString"); + if (BuildConfig.DEBUG) Log.e(TAG, "", e); } - - result = jsonObject.toString(); - } catch (final Exception e) { - if (logCollector != null) logCollector.appendException(e, LogFile.UTILS_EXPORT, "getExportString"); - if (BuildConfig.DEBUG) Log.e(TAG, "", e); - } - return result; + callback.onCreated(null); + }); } @NonNull @@ -277,22 +339,35 @@ public final class ExportImportUtils { return new JSONObject(); } - @NonNull - private static JSONArray getFavorites() { - if (Utils.dataBox == null) return new JSONArray(); + private static void getFavorites(final Context context, final OnFavoritesJsonLoadedCallback callback) { + final FavoriteDataSource dataSource = FavoriteDataSource.getInstance(context); + final FavoriteRepository favoriteRepository = FavoriteRepository.getInstance(new AppExecutors(), dataSource); try { - final List allFavorites = Utils.dataBox.getAllFavorites(); - final JSONArray jsonArray = new JSONArray(); - for (final DataBox.FavoriteModel favorite : allFavorites) { - final JSONObject jsonObject = new JSONObject(); - jsonObject.put("q", favorite.getQuery()); - jsonObject.put("type", favorite.getType().toString()); - jsonObject.put("s", favorite.getDisplayName()); - jsonObject.put("pic_url", favorite.getPicUrl()); - jsonObject.put("d", favorite.getDateAdded().getTime()); - jsonArray.put(jsonObject); - } - return jsonArray; + favoriteRepository.getAllFavorites(new RepositoryCallback>() { + @Override + public void onSuccess(final List favorites) { + final JSONArray jsonArray = new JSONArray(); + try { + for (final Favorite favorite : favorites) { + final JSONObject jsonObject = new JSONObject(); + jsonObject.put("q", favorite.getQuery()); + jsonObject.put("type", favorite.getType().toString()); + jsonObject.put("s", favorite.getDisplayName()); + jsonObject.put("pic_url", favorite.getPicUrl()); + jsonObject.put("d", favorite.getDateAdded().getTime()); + jsonArray.put(jsonObject); + } + } catch (Exception e) { + Log.e(TAG, "onSuccess: Error creating json array", e); + } + callback.onFavoritesJsonLoaded(jsonArray); + } + + @Override + public void onDataNotAvailable() { + callback.onFavoritesJsonLoaded(new JSONArray()); + } + }); } catch (final Exception e) { if (logCollector != null) { logCollector.appendException(e, LogFile.UTILS_EXPORT, "getFavorites"); @@ -301,30 +376,48 @@ public final class ExportImportUtils { Log.e(TAG, "Error exporting favorites", e); } } - return new JSONArray(); } - @NonNull - private static JSONArray getCookies() { - if (Utils.dataBox == null) return new JSONArray(); - try { - final List allCookies = Utils.dataBox.getAllCookies(); - final JSONArray jsonArray = new JSONArray(); - for (final DataBox.CookieModel cookie : allCookies) { - final JSONObject jsonObject = new JSONObject(); - jsonObject.put("i", cookie.getUid()); - jsonObject.put("u", cookie.getUsername()); - jsonObject.put("c", cookie.getCookie()); - jsonObject.put("full_name", cookie.getFullName()); - jsonObject.put("profile_pic", cookie.getProfilePic()); - jsonArray.put(jsonObject); + private static void getCookies(final Context context, final OnAccountJsonLoadedCallback callback) { + final AccountRepository accountRepository = AccountRepository.getInstance(new AppExecutors(), AccountDataSource.getInstance(context)); + accountRepository.getAllAccounts(new RepositoryCallback>() { + @Override + public void onSuccess(final List accounts) { + try { + final JSONArray jsonArray = new JSONArray(); + for (final Account cookie : accounts) { + final JSONObject jsonObject = new JSONObject(); + jsonObject.put("i", cookie.getUid()); + jsonObject.put("u", cookie.getUsername()); + jsonObject.put("c", cookie.getCookie()); + jsonObject.put("full_name", cookie.getFullName()); + jsonObject.put("profile_pic", cookie.getProfilePic()); + jsonArray.put(jsonObject); + } + callback.onAccountsJsonLoaded(jsonArray); + return; + } catch (Exception e) { + Log.e(TAG, "Error exporting accounts", e); + } + callback.onAccountsJsonLoaded(new JSONArray()); } - return jsonArray; - } catch (final Exception e) { - if (BuildConfig.DEBUG) { - Log.e(TAG, "Error exporting accounts", e); + + @Override + public void onDataNotAvailable() { + callback.onAccountsJsonLoaded(new JSONArray()); } - } - return new JSONArray(); + }); + } + + public interface OnExportStringCreatedCallback { + void onCreated(String exportString); + } + + public interface OnAccountJsonLoadedCallback { + void onAccountsJsonLoaded(JSONArray array); + } + + public interface OnFavoritesJsonLoadedCallback { + void onFavoritesJsonLoaded(JSONArray array); } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/utils/Utils.java b/app/src/main/java/awais/instagrabber/utils/Utils.java index 04cba7fc..786475f9 100644 --- a/app/src/main/java/awais/instagrabber/utils/Utils.java +++ b/app/src/main/java/awais/instagrabber/utils/Utils.java @@ -47,7 +47,6 @@ public final class Utils { public static LogCollector logCollector; public static SettingsHelper settingsHelper; - public static DataBox dataBox; public static boolean sessionVolumeFull = false; public static final MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton(); public static ClipboardManager clipboardManager; diff --git a/app/src/main/java/awais/instagrabber/viewmodels/FavoritesViewModel.java b/app/src/main/java/awais/instagrabber/viewmodels/FavoritesViewModel.java index e30d4116..bb865b88 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/FavoritesViewModel.java +++ b/app/src/main/java/awais/instagrabber/viewmodels/FavoritesViewModel.java @@ -5,12 +5,12 @@ import androidx.lifecycle.ViewModel; import java.util.List; -import awais.instagrabber.utils.DataBox; +import awais.instagrabber.db.entities.Favorite; public class FavoritesViewModel extends ViewModel { - private MutableLiveData> list; + private MutableLiveData> list; - public MutableLiveData> getList() { + public MutableLiveData> getList() { if (list == null) { list = new MutableLiveData<>(); } diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index ff4a275d..ec72e8a1 100755 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -22,7 +22,7 @@ android:id="@+id/cookies" style="@style/Widget.AppCompat.Button.ButtonBar.AlertDialog" android:layout_width="0dp" - android:layout_height="match_parent" + android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/get_cookies" /> @@ -30,14 +30,15 @@ android:id="@+id/refresh" style="@style/Widget.AppCompat.Button.ButtonBar.AlertDialog" android:layout_width="0dp" - android:layout_height="match_parent" + android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/refresh" />