mirror of
				https://github.com/KokaKiwi/BarInsta
				synced 2025-10-31 03:25:34 +00:00 
			
		
		
		
	Merge pull request #400 from ammargitham/add-room
Add Room Persistence Library
This commit is contained in:
		
						commit
						7964df976d
					
				| @ -17,6 +17,12 @@ android { | ||||
| 
 | ||||
|         vectorDrawables.useSupportLibrary = true | ||||
|         vectorDrawables.generatedDensities = [] | ||||
| 
 | ||||
|         javaCompileOptions { | ||||
|             annotationProcessorOptions { | ||||
|                 arguments = ["room.schemaLocation": "$projectDir/schemas".toString()] | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     compileOptions { | ||||
| @ -32,8 +38,13 @@ android { | ||||
|     aaptOptions { additionalParameters '--no-version-vectors' } | ||||
| 
 | ||||
|     buildTypes { | ||||
|         debug { | ||||
|             minifyEnabled true | ||||
|             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' | ||||
|         } | ||||
| 
 | ||||
|         release { | ||||
|             minifyEnabled false | ||||
|             minifyEnabled true | ||||
|             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' | ||||
|         } | ||||
|     } | ||||
| @ -45,19 +56,19 @@ configurations.all { | ||||
| } | ||||
| 
 | ||||
| dependencies { | ||||
|     coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.0' | ||||
|     coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.1' | ||||
| 
 | ||||
|     def appcompat_version = "1.2.0" | ||||
|     def nav_version = '2.3.1' | ||||
|     def nav_version = '2.3.2' | ||||
| 
 | ||||
|     implementation 'com.google.android.material:material:1.3.0-alpha03' | ||||
|     implementation 'com.google.android.material:material:1.3.0-alpha04' | ||||
| 
 | ||||
|     implementation 'com.google.android.exoplayer:exoplayer-core:2.12.0' | ||||
|     implementation 'com.google.android.exoplayer:exoplayer-ui:2.12.0' | ||||
| 
 | ||||
|     implementation "androidx.appcompat:appcompat:$appcompat_version" | ||||
|     implementation "androidx.appcompat:appcompat-resources:$appcompat_version" | ||||
|     implementation "androidx.recyclerview:recyclerview:1.2.0-alpha06" | ||||
|     implementation "androidx.recyclerview:recyclerview:1.2.0-beta01" | ||||
|     implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' | ||||
|     implementation "androidx.viewpager2:viewpager2:1.0.0" | ||||
|     implementation "androidx.navigation:navigation-fragment:$nav_version" | ||||
| @ -69,6 +80,12 @@ dependencies { | ||||
| 
 | ||||
|     implementation 'com.google.guava:guava:27.0.1-android' | ||||
| 
 | ||||
|     // Room | ||||
|     def room_version = "2.2.5" | ||||
|     implementation "androidx.room:room-runtime:$room_version" | ||||
|     implementation "androidx.room:room-guava:$room_version" | ||||
|     annotationProcessor "androidx.room:room-compiler:$room_version" | ||||
| 
 | ||||
|     // implementation 'com.github.hendrawd:StorageUtil:1.1.0' | ||||
|     implementation 'com.github.ammargitham:AutoLinkTextViewV2:master-SNAPSHOT' | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										7
									
								
								app/proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								app/proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							| @ -19,3 +19,10 @@ | ||||
| # If you keep the line number information, uncomment this to | ||||
| # hide the original source file name. | ||||
| #-renamesourcefileattribute SourceFile | ||||
| 
 | ||||
| #noinspection ShrinkerUnresolvedReference | ||||
| #-keep class !com.google.android.exoplayer2.**, ** { *; } | ||||
| 
 | ||||
| #-keep class !awais.instagrabber.** { *; } | ||||
| 
 | ||||
| -dontobfuscate | ||||
							
								
								
									
										114
									
								
								app/schemas/awais.instagrabber.db.AppDatabase/4.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								app/schemas/awais.instagrabber.db.AppDatabase/4.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,114 @@ | ||||
| { | ||||
|   "formatVersion": 1, | ||||
|   "database": { | ||||
|     "version": 4, | ||||
|     "identityHash": "538d64adaeb8c3a98db9204955932e59", | ||||
|     "entities": [ | ||||
|       { | ||||
|         "tableName": "accounts", | ||||
|         "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `uid` TEXT, `username` TEXT, `cookie` TEXT, `full_name` TEXT, `profile_pic` TEXT)", | ||||
|         "fields": [ | ||||
|           { | ||||
|             "fieldPath": "id", | ||||
|             "columnName": "id", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "uid", | ||||
|             "columnName": "uid", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "username", | ||||
|             "columnName": "username", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "cookie", | ||||
|             "columnName": "cookie", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "fullName", | ||||
|             "columnName": "full_name", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "profilePic", | ||||
|             "columnName": "profile_pic", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": false | ||||
|           } | ||||
|         ], | ||||
|         "primaryKey": { | ||||
|           "columnNames": [ | ||||
|             "id" | ||||
|           ], | ||||
|           "autoGenerate": true | ||||
|         }, | ||||
|         "indices": [], | ||||
|         "foreignKeys": [] | ||||
|       }, | ||||
|       { | ||||
|         "tableName": "favorites", | ||||
|         "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `query_text` TEXT, `type` TEXT, `display_name` TEXT, `pic_url` TEXT, `date_added` INTEGER)", | ||||
|         "fields": [ | ||||
|           { | ||||
|             "fieldPath": "id", | ||||
|             "columnName": "id", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": true | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "query", | ||||
|             "columnName": "query_text", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "type", | ||||
|             "columnName": "type", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "displayName", | ||||
|             "columnName": "display_name", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "picUrl", | ||||
|             "columnName": "pic_url", | ||||
|             "affinity": "TEXT", | ||||
|             "notNull": false | ||||
|           }, | ||||
|           { | ||||
|             "fieldPath": "dateAdded", | ||||
|             "columnName": "date_added", | ||||
|             "affinity": "INTEGER", | ||||
|             "notNull": false | ||||
|           } | ||||
|         ], | ||||
|         "primaryKey": { | ||||
|           "columnNames": [ | ||||
|             "id" | ||||
|           ], | ||||
|           "autoGenerate": true | ||||
|         }, | ||||
|         "indices": [], | ||||
|         "foreignKeys": [] | ||||
|       } | ||||
|     ], | ||||
|     "views": [], | ||||
|     "setupQueries": [ | ||||
|       "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", | ||||
|       "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '538d64adaeb8c3a98db9204955932e59')" | ||||
|     ] | ||||
|   } | ||||
| } | ||||
| @ -108,7 +108,8 @@ | ||||
|         <activity | ||||
|             android:name=".activities.Login" | ||||
|             android:label="@string/login" | ||||
|             android:parentActivityName=".activities.MainActivity"> | ||||
|             android:parentActivityName=".activities.MainActivity" | ||||
|             android:theme="@style/AppTheme.Light.White"> | ||||
|             <meta-data | ||||
|                 android:name="android.support.PARENT_ACTIVITY" | ||||
|                 android:value=".activities.MainActivity" /> | ||||
|  | ||||
| @ -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); | ||||
| 
 | ||||
|  | ||||
| @ -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(); | ||||
|  | ||||
| @ -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<DataBox.CookieModel, AccountSwitcherAdapter.ViewHolder> { | ||||
| public class AccountSwitcherAdapter extends ListAdapter<Account, AccountSwitcherAdapter.ViewHolder> { | ||||
|     private static final String TAG = "AccountSwitcherAdapter"; | ||||
|     private static final DiffUtil.ItemCallback<DataBox.CookieModel> DIFF_CALLBACK = new DiffUtil.ItemCallback<DataBox.CookieModel>() { | ||||
|     private static final DiffUtil.ItemCallback<Account> DIFF_CALLBACK = new DiffUtil.ItemCallback<Account>() { | ||||
|         @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<DataBox.CookieModel, Acc | ||||
| 
 | ||||
|     @Override | ||||
|     public void onBindViewHolder(@NonNull final ViewHolder holder, final int position) { | ||||
|         final DataBox.CookieModel model = getItem(position); | ||||
|         final Account model = getItem(position); | ||||
|         if (model == null) return; | ||||
|         final String cookie = settingsHelper.getString(Constants.COOKIE); | ||||
|         final boolean isCurrent = model.getCookie().equals(cookie); | ||||
| @ -61,11 +61,11 @@ public class AccountSwitcherAdapter extends ListAdapter<DataBox.CookieModel, Acc | ||||
|     } | ||||
| 
 | ||||
|     public interface OnAccountClickListener { | ||||
|         void onAccountClick(final DataBox.CookieModel model, final boolean isCurrent); | ||||
|         void onAccountClick(final Account model, final boolean isCurrent); | ||||
|     } | ||||
| 
 | ||||
|     public interface OnAccountLongClickListener { | ||||
|         boolean onAccountLongClick(final DataBox.CookieModel model, final boolean isCurrent); | ||||
|         boolean onAccountLongClick(final Account model, final boolean isCurrent); | ||||
|     } | ||||
| 
 | ||||
|     public static class ViewHolder extends RecyclerView.ViewHolder { | ||||
| @ -78,7 +78,7 @@ public class AccountSwitcherAdapter extends ListAdapter<DataBox.CookieModel, Acc | ||||
|         } | ||||
| 
 | ||||
|         @SuppressLint("SetTextI18n") | ||||
|         public void bind(final DataBox.CookieModel model, | ||||
|         public void bind(final Account model, | ||||
|                          final boolean isCurrent, | ||||
|                          final OnAccountClickListener clickListener, | ||||
|                          final OnAccountLongClickListener longClickListener) { | ||||
|  | ||||
| @ -20,8 +20,8 @@ import awais.instagrabber.R; | ||||
| import awais.instagrabber.adapters.viewholder.FavoriteViewHolder; | ||||
| import awais.instagrabber.databinding.ItemFavSectionHeaderBinding; | ||||
| import awais.instagrabber.databinding.ItemSuggestionBinding; | ||||
| import awais.instagrabber.db.entities.Favorite; | ||||
| import awais.instagrabber.models.enums.FavoriteType; | ||||
| import awais.instagrabber.utils.DataBox; | ||||
| 
 | ||||
| public class FavoritesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { | ||||
| 
 | ||||
| @ -102,7 +102,7 @@ public class FavoritesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold | ||||
|         return getItem(position).isHeader() ? 0 : 1; | ||||
|     } | ||||
| 
 | ||||
|     public void submitList(@Nullable final List<DataBox.FavoriteModel> list) { | ||||
|     public void submitList(@Nullable final List<Favorite> list) { | ||||
|         if (list == null) { | ||||
|             differ.submitList(null); | ||||
|             return; | ||||
| @ -110,7 +110,7 @@ public class FavoritesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold | ||||
|         differ.submitList(sectionAndSort(list)); | ||||
|     } | ||||
| 
 | ||||
|     public void submitList(@Nullable final List<DataBox.FavoriteModel> list, @Nullable final Runnable commitCallback) { | ||||
|     public void submitList(@Nullable final List<Favorite> list, @Nullable final Runnable commitCallback) { | ||||
|         if (list == null) { | ||||
|             differ.submitList(null, commitCallback); | ||||
|             return; | ||||
| @ -119,8 +119,8 @@ public class FavoritesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     private List<FavoriteModelOrHeader> sectionAndSort(@NonNull final List<DataBox.FavoriteModel> list) { | ||||
|         final List<DataBox.FavoriteModel> listCopy = new ArrayList<>(list); | ||||
|     private List<FavoriteModelOrHeader> sectionAndSort(@NonNull final List<Favorite> list) { | ||||
|         final List<Favorite> 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<RecyclerView.ViewHold | ||||
|         }); | ||||
|         final List<FavoriteModelOrHeader> 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<RecyclerView.ViewHold | ||||
| 
 | ||||
|     private static class FavoriteModelOrHeader { | ||||
|         FavoriteType header; | ||||
|         DataBox.FavoriteModel model; | ||||
|         Favorite model; | ||||
| 
 | ||||
|         boolean isHeader() { | ||||
|             return header != null; | ||||
| @ -164,11 +164,11 @@ public class FavoritesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold | ||||
|     } | ||||
| 
 | ||||
|     public interface OnFavoriteClickListener { | ||||
|         void onClick(final DataBox.FavoriteModel model); | ||||
|         void onClick(final Favorite model); | ||||
|     } | ||||
| 
 | ||||
|     public interface OnFavoriteLongClickListener { | ||||
|         boolean onLongClick(final DataBox.FavoriteModel model); | ||||
|         boolean onLongClick(final Favorite model); | ||||
|     } | ||||
| 
 | ||||
|     public static class FavSectionViewHolder extends RecyclerView.ViewHolder { | ||||
|  | ||||
| @ -7,9 +7,9 @@ import androidx.recyclerview.widget.RecyclerView; | ||||
| 
 | ||||
| import awais.instagrabber.adapters.FavoritesAdapter; | ||||
| import awais.instagrabber.databinding.ItemSuggestionBinding; | ||||
| import awais.instagrabber.db.entities.Favorite; | ||||
| import awais.instagrabber.models.enums.FavoriteType; | ||||
| import awais.instagrabber.utils.Constants; | ||||
| import awais.instagrabber.utils.DataBox; | ||||
| 
 | ||||
| public class FavoriteViewHolder extends RecyclerView.ViewHolder { | ||||
|     private static final String TAG = "FavoriteViewHolder"; | ||||
| @ -22,7 +22,7 @@ public class FavoriteViewHolder extends RecyclerView.ViewHolder { | ||||
|         binding.isVerified.setVisibility(View.GONE); | ||||
|     } | ||||
| 
 | ||||
|     public void bind(final DataBox.FavoriteModel model, | ||||
|     public void bind(final Favorite model, | ||||
|                      final FavoritesAdapter.OnFavoriteClickListener clickListener, | ||||
|                      final FavoritesAdapter.OnFavoriteLongClickListener longClickListener) { | ||||
|         // Log.d(TAG, "bind: " + model); | ||||
|  | ||||
| @ -16,13 +16,11 @@ import awais.instagrabber.interfaces.FetchListener; | ||||
| import awais.instagrabber.models.NotificationModel; | ||||
| import awais.instagrabber.models.enums.NotificationType; | ||||
| import awais.instagrabber.utils.Constants; | ||||
| import awais.instagrabber.utils.CookieUtils; | ||||
| import awais.instagrabber.utils.LocaleUtils; | ||||
| import awais.instagrabber.utils.NetworkUtils; | ||||
| import awaisomereport.LogCollector; | ||||
| 
 | ||||
| import static awais.instagrabber.utils.Utils.logCollector; | ||||
| import static awais.instagrabber.utils.Utils.settingsHelper; | ||||
| 
 | ||||
| public final class NotificationsFetcher extends AsyncTask<Void, Void, List<NotificationModel>> { | ||||
|     private static final String TAG = "NotificationsFetcher"; | ||||
| @ -37,8 +35,6 @@ public final class NotificationsFetcher extends AsyncTask<Void, Void, List<Notif | ||||
|     protected List<NotificationModel> doInBackground(final Void... voids) { | ||||
|         List<NotificationModel> 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); | ||||
|  | ||||
| @ -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<Void, Void, FeedModel> { | ||||
| 
 | ||||
|     @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(); | ||||
|  | ||||
							
								
								
									
										229
									
								
								app/src/main/java/awais/instagrabber/db/AppDatabase.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								app/src/main/java/awais/instagrabber/db/AppDatabase.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,229 @@ | ||||
| package awais.instagrabber.db; | ||||
| 
 | ||||
| import android.content.ContentValues; | ||||
| import android.content.Context; | ||||
| import android.database.Cursor; | ||||
| import android.database.sqlite.SQLiteDatabase; | ||||
| import android.util.Log; | ||||
| import android.util.Pair; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.room.Database; | ||||
| import androidx.room.Room; | ||||
| import androidx.room.RoomDatabase; | ||||
| import androidx.room.TypeConverters; | ||||
| import androidx.room.migration.Migration; | ||||
| import androidx.sqlite.db.SupportSQLiteDatabase; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.Date; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import awais.instagrabber.db.dao.AccountDao; | ||||
| import awais.instagrabber.db.dao.FavoriteDao; | ||||
| import awais.instagrabber.db.entities.Account; | ||||
| import awais.instagrabber.db.entities.Favorite; | ||||
| import awais.instagrabber.models.enums.FavoriteType; | ||||
| import awais.instagrabber.utils.Utils; | ||||
| 
 | ||||
| @Database(entities = {Account.class, Favorite.class}, | ||||
|           version = 4) | ||||
| @TypeConverters({Converters.class}) | ||||
| public abstract class AppDatabase extends RoomDatabase { | ||||
|     private static final String TAG = AppDatabase.class.getSimpleName(); | ||||
| 
 | ||||
|     private static AppDatabase INSTANCE; | ||||
| 
 | ||||
|     public abstract AccountDao accountDao(); | ||||
| 
 | ||||
|     public abstract FavoriteDao favoriteDao(); | ||||
| 
 | ||||
|     public static AppDatabase getDatabase(final Context context) { | ||||
|         if (INSTANCE == null) { | ||||
|             synchronized (AppDatabase.class) { | ||||
|                 if (INSTANCE == null) { | ||||
|                     INSTANCE = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "cookiebox.db") | ||||
|                                    .addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4) | ||||
|                                    .build(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return INSTANCE; | ||||
|     } | ||||
| 
 | ||||
|     static final Migration MIGRATION_1_2 = new Migration(1, 2) { | ||||
|         @Override | ||||
|         public void migrate(@NonNull SupportSQLiteDatabase db) { | ||||
|             db.execSQL("ALTER TABLE cookies ADD " + Account.COL_FULL_NAME + " TEXT"); | ||||
|             db.execSQL("ALTER TABLE cookies ADD " + Account.COL_PROFILE_PIC + " TEXT"); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     static final Migration MIGRATION_2_3 = new Migration(2, 3) { | ||||
|         @Override | ||||
|         public void migrate(@NonNull SupportSQLiteDatabase db) { | ||||
|             final List<Favorite> oldFavorites = backupOldFavorites(db); | ||||
|             // recreate with new columns (as there will be no doubt about the `query_display` column being present or not in the future versions) | ||||
|             db.execSQL("DROP TABLE " + Favorite.TABLE_NAME); | ||||
|             db.execSQL("CREATE TABLE " + Favorite.TABLE_NAME + " (" | ||||
|                                + Favorite.COL_ID + " INTEGER PRIMARY KEY," | ||||
|                                + Favorite.COL_QUERY + " TEXT," | ||||
|                                + Favorite.COL_TYPE + " TEXT," | ||||
|                                + Favorite.COL_DISPLAY_NAME + " TEXT," | ||||
|                                + Favorite.COL_PIC_URL + " TEXT," | ||||
|                                + Favorite.COL_DATE_ADDED + " INTEGER)"); | ||||
|             // add the old favorites back | ||||
|             for (final Favorite oldFavorite : oldFavorites) { | ||||
|                 insertOrUpdateFavorite(db, oldFavorite); | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     static final Migration MIGRATION_3_4 = new Migration(3, 4) { | ||||
|         @Override | ||||
|         public void migrate(@NonNull SupportSQLiteDatabase db) { | ||||
|             // Required when migrating to Room. | ||||
|             // The original table primary keys were not 'NOT NULL', so the migration to Room were failing without the below migration. | ||||
|             // Taking this opportunity to rename cookies table to accounts | ||||
| 
 | ||||
|             // Create new table with name 'accounts' | ||||
|             db.execSQL("CREATE TABLE " + Account.TABLE_NAME + " (" | ||||
|                                + Account.COL_ID + " INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," | ||||
|                                + Account.COL_UID + " TEXT," | ||||
|                                + Account.COL_USERNAME + " TEXT," | ||||
|                                + Account.COL_COOKIE + " TEXT," | ||||
|                                + Account.COL_FULL_NAME + " TEXT," | ||||
|                                + Account.COL_PROFILE_PIC + " TEXT)"); | ||||
|             // Insert all data from table 'cookies' to 'accounts' | ||||
|             db.execSQL("INSERT INTO " + Account.TABLE_NAME + " (" | ||||
|                                + Account.COL_UID + "," | ||||
|                                + Account.COL_USERNAME + "," | ||||
|                                + Account.COL_COOKIE + "," | ||||
|                                + Account.COL_FULL_NAME + "," | ||||
|                                + Account.COL_PROFILE_PIC + ") " | ||||
|                                + "SELECT " | ||||
|                                + Account.COL_UID + "," | ||||
|                                + Account.COL_USERNAME + "," | ||||
|                                + Account.COL_COOKIE + "," | ||||
|                                + Account.COL_FULL_NAME + "," | ||||
|                                + Account.COL_PROFILE_PIC | ||||
|                                + " FROM cookies"); | ||||
|             // Drop old cookies table | ||||
|             db.execSQL("DROP TABLE cookies"); | ||||
| 
 | ||||
|             // Create favorite backup table | ||||
|             db.execSQL("CREATE TABLE " + Favorite.TABLE_NAME + "_backup (" | ||||
|                                + Favorite.COL_ID + " INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," | ||||
|                                + Favorite.COL_QUERY + " TEXT," | ||||
|                                + Favorite.COL_TYPE + " TEXT," | ||||
|                                + Favorite.COL_DISPLAY_NAME + " TEXT," | ||||
|                                + Favorite.COL_PIC_URL + " TEXT," | ||||
|                                + Favorite.COL_DATE_ADDED + " INTEGER)"); | ||||
|             // Insert all data from table 'favorite' to 'favorite_backup' | ||||
|             db.execSQL("INSERT INTO " + Favorite.TABLE_NAME + "_backup (" | ||||
|                                + Favorite.COL_QUERY + "," | ||||
|                                + Favorite.COL_TYPE + "," | ||||
|                                + Favorite.COL_DISPLAY_NAME + "," | ||||
|                                + Favorite.COL_PIC_URL + "," | ||||
|                                + Favorite.COL_DATE_ADDED + ") " | ||||
|                                + "SELECT " | ||||
|                                + Favorite.COL_QUERY + "," | ||||
|                                + Favorite.COL_TYPE + "," | ||||
|                                + Favorite.COL_DISPLAY_NAME + "," | ||||
|                                + Favorite.COL_PIC_URL + "," | ||||
|                                + Favorite.COL_DATE_ADDED | ||||
|                                + " FROM " + Favorite.TABLE_NAME); | ||||
|             // Drop favorites | ||||
|             db.execSQL("DROP TABLE " + Favorite.TABLE_NAME); | ||||
|             // Rename favorite_backup to favorites | ||||
|             db.execSQL("ALTER TABLE " + Favorite.TABLE_NAME + "_backup RENAME TO " + Favorite.TABLE_NAME); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     @NonNull | ||||
|     private static List<Favorite> backupOldFavorites(@NonNull final SupportSQLiteDatabase db) { | ||||
|         // check if old favorites table had the column query_display | ||||
|         final boolean queryDisplayExists = checkColumnExists(db, Favorite.TABLE_NAME, "query_display"); | ||||
|         Log.d(TAG, "backupOldFavorites: queryDisplayExists: " + queryDisplayExists); | ||||
|         final List<Favorite> oldModels = new ArrayList<>(); | ||||
|         final String sql = "SELECT " | ||||
|                 + "query_text," | ||||
|                 + "date_added" | ||||
|                 + (queryDisplayExists ? ",query_display" : "") | ||||
|                 + " FROM " + Favorite.TABLE_NAME; | ||||
|         try (final Cursor cursor = db.query(sql)) { | ||||
|             if (cursor != null && cursor.moveToFirst()) { | ||||
|                 do { | ||||
|                     try { | ||||
|                         final String queryText = cursor.getString(cursor.getColumnIndex("query_text")); | ||||
|                         final Pair<FavoriteType, String> favoriteTypeQueryPair = Utils.migrateOldFavQuery(queryText); | ||||
|                         if (favoriteTypeQueryPair == null) continue; | ||||
|                         final FavoriteType type = favoriteTypeQueryPair.first; | ||||
|                         final String query = favoriteTypeQueryPair.second; | ||||
|                         oldModels.add(new Favorite( | ||||
|                                 -1, | ||||
|                                 query, | ||||
|                                 type, | ||||
|                                 queryDisplayExists ? cursor.getString(cursor.getColumnIndex("query_display")) | ||||
|                                                    : null, | ||||
|                                 null, | ||||
|                                 new Date(cursor.getLong(cursor.getColumnIndex("date_added"))) | ||||
|                         )); | ||||
|                     } catch (Exception e) { | ||||
|                         Log.e(TAG, "onUpgrade", e); | ||||
|                     } | ||||
|                 } while (cursor.moveToNext()); | ||||
|             } | ||||
|         } catch (Exception e) { | ||||
|             Log.e(TAG, "onUpgrade", e); | ||||
|         } | ||||
|         Log.d(TAG, "backupOldFavorites: oldModels:" + oldModels); | ||||
|         return oldModels; | ||||
|     } | ||||
| 
 | ||||
|     private static synchronized void insertOrUpdateFavorite(@NonNull final SupportSQLiteDatabase db, @NonNull final Favorite model) { | ||||
|         final ContentValues values = new ContentValues(); | ||||
|         values.put(Favorite.COL_QUERY, model.getQuery()); | ||||
|         values.put(Favorite.COL_TYPE, model.getType().toString()); | ||||
|         values.put(Favorite.COL_DISPLAY_NAME, model.getDisplayName()); | ||||
|         values.put(Favorite.COL_PIC_URL, model.getPicUrl()); | ||||
|         values.put(Favorite.COL_DATE_ADDED, model.getDateAdded().getTime()); | ||||
|         int rows; | ||||
|         if (model.getId() >= 1) { | ||||
|             rows = db.update(Favorite.TABLE_NAME, | ||||
|                              SQLiteDatabase.CONFLICT_IGNORE, | ||||
|                              values, | ||||
|                              Favorite.COL_ID + "=?", | ||||
|                              new String[]{String.valueOf(model.getId())}); | ||||
|         } else { | ||||
|             rows = db.update(Favorite.TABLE_NAME, | ||||
|                              SQLiteDatabase.CONFLICT_IGNORE, | ||||
|                              values, | ||||
|                              Favorite.COL_QUERY + "=?" + " AND " + Favorite.COL_TYPE + "=?", | ||||
|                              new String[]{model.getQuery(), model.getType().toString()}); | ||||
|         } | ||||
|         if (rows != 1) { | ||||
|             db.insert(Favorite.TABLE_NAME, SQLiteDatabase.CONFLICT_IGNORE, values); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static boolean checkColumnExists(@NonNull final SupportSQLiteDatabase db, | ||||
|                                              @NonNull final String tableName, | ||||
|                                              @NonNull final String columnName) { | ||||
|         boolean exists = false; | ||||
|         try (Cursor cursor = db.query("PRAGMA table_info(" + tableName + ")")) { | ||||
|             if (cursor.moveToFirst()) { | ||||
|                 do { | ||||
|                     final String currentColumn = cursor.getString(cursor.getColumnIndex("name")); | ||||
|                     if (currentColumn.equals(columnName)) { | ||||
|                         exists = true; | ||||
|                     } | ||||
|                 } while (cursor.moveToNext()); | ||||
| 
 | ||||
|             } | ||||
|         } catch (Exception ex) { | ||||
|             Log.e(TAG, "checkColumnExists", ex); | ||||
|         } | ||||
|         return exists; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										33
									
								
								app/src/main/java/awais/instagrabber/db/Converters.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								app/src/main/java/awais/instagrabber/db/Converters.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | ||||
| package awais.instagrabber.db; | ||||
| 
 | ||||
| import androidx.room.TypeConverter; | ||||
| 
 | ||||
| import java.util.Date; | ||||
| 
 | ||||
| import awais.instagrabber.models.enums.FavoriteType; | ||||
| 
 | ||||
| public class Converters { | ||||
|     @TypeConverter | ||||
|     public static Date fromTimestamp(Long value) { | ||||
|         return value == null ? null : new Date(value); | ||||
|     } | ||||
| 
 | ||||
|     @TypeConverter | ||||
|     public static Long dateToTimestamp(Date date) { | ||||
|         return date == null ? null : date.getTime(); | ||||
|     } | ||||
| 
 | ||||
|     @TypeConverter | ||||
|     public static FavoriteType fromFavoriteTypeString(String value) { | ||||
|         try { | ||||
|             return FavoriteType.valueOf(value); | ||||
|         } catch (Exception e) { | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @TypeConverter | ||||
|     public static String favoriteTypeToString(FavoriteType favoriteType) { | ||||
|         return favoriteType == null ? null : favoriteType.toString(); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										34
									
								
								app/src/main/java/awais/instagrabber/db/dao/AccountDao.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								app/src/main/java/awais/instagrabber/db/dao/AccountDao.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | ||||
| package awais.instagrabber.db.dao; | ||||
| 
 | ||||
| import androidx.room.Dao; | ||||
| import androidx.room.Delete; | ||||
| import androidx.room.Insert; | ||||
| import androidx.room.OnConflictStrategy; | ||||
| import androidx.room.Query; | ||||
| import androidx.room.Update; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| import awais.instagrabber.db.entities.Account; | ||||
| 
 | ||||
| @Dao | ||||
| public interface AccountDao { | ||||
| 
 | ||||
|     @Query("SELECT * FROM accounts") | ||||
|     List<Account> getAllAccounts(); | ||||
| 
 | ||||
|     @Query("SELECT * FROM accounts WHERE uid = :uid") | ||||
|     Account findAccountByUid(String uid); | ||||
| 
 | ||||
|     @Insert(onConflict = OnConflictStrategy.REPLACE) | ||||
|     List<Long> insertAccounts(Account... accounts); | ||||
| 
 | ||||
|     @Update | ||||
|     void updateAccounts(Account... accounts); | ||||
| 
 | ||||
|     @Delete | ||||
|     void deleteAccounts(Account... accounts); | ||||
| 
 | ||||
|     @Query("DELETE from accounts") | ||||
|     void deleteAllAccounts(); | ||||
| } | ||||
							
								
								
									
										35
									
								
								app/src/main/java/awais/instagrabber/db/dao/FavoriteDao.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								app/src/main/java/awais/instagrabber/db/dao/FavoriteDao.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | ||||
| package awais.instagrabber.db.dao; | ||||
| 
 | ||||
| import androidx.room.Dao; | ||||
| import androidx.room.Delete; | ||||
| import androidx.room.Insert; | ||||
| import androidx.room.OnConflictStrategy; | ||||
| import androidx.room.Query; | ||||
| import androidx.room.Update; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| import awais.instagrabber.db.entities.Favorite; | ||||
| import awais.instagrabber.models.enums.FavoriteType; | ||||
| 
 | ||||
| @Dao | ||||
| public interface FavoriteDao { | ||||
| 
 | ||||
|     @Query("SELECT * FROM favorites") | ||||
|     List<Favorite> getAllFavorites(); | ||||
| 
 | ||||
|     @Query("SELECT * FROM favorites WHERE query_text = :query and type = :type") | ||||
|     Favorite findFavoriteByQueryAndType(String query, FavoriteType type); | ||||
| 
 | ||||
|     @Insert(onConflict = OnConflictStrategy.REPLACE) | ||||
|     List<Long> insertFavorites(Favorite... favorites); | ||||
| 
 | ||||
|     @Update | ||||
|     void updateFavorites(Favorite... favorites); | ||||
| 
 | ||||
|     @Delete | ||||
|     void deleteFavorites(Favorite... favorites); | ||||
| 
 | ||||
|     @Query("DELETE from favorites") | ||||
|     void deleteAllFavorites(); | ||||
| } | ||||
| @ -0,0 +1,68 @@ | ||||
| package awais.instagrabber.db.datasources; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| import awais.instagrabber.db.AppDatabase; | ||||
| import awais.instagrabber.db.dao.AccountDao; | ||||
| import awais.instagrabber.db.entities.Account; | ||||
| 
 | ||||
| public class AccountDataSource { | ||||
|     private static final String TAG = AccountDataSource.class.getSimpleName(); | ||||
| 
 | ||||
|     private static AccountDataSource INSTANCE; | ||||
| 
 | ||||
|     private final AccountDao accountDao; | ||||
| 
 | ||||
|     private AccountDataSource(final AccountDao accountDao) { | ||||
|         this.accountDao = accountDao; | ||||
|     } | ||||
| 
 | ||||
|     public static AccountDataSource getInstance(@NonNull Context context) { | ||||
|         if (INSTANCE == null) { | ||||
|             synchronized (AccountDataSource.class) { | ||||
|                 if (INSTANCE == null) { | ||||
|                     final AppDatabase database = AppDatabase.getDatabase(context); | ||||
|                     INSTANCE = new AccountDataSource(database.accountDao()); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return INSTANCE; | ||||
|     } | ||||
| 
 | ||||
|     @Nullable | ||||
|     public final Account getAccount(final String uid) { | ||||
|         return accountDao.findAccountByUid(uid); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     public final List<Account> getAllAccounts() { | ||||
|         return accountDao.getAllAccounts(); | ||||
|     } | ||||
| 
 | ||||
|     public final void insertOrUpdateAccount(final String uid, | ||||
|                                             final String username, | ||||
|                                             final String cookie, | ||||
|                                             final String fullName, | ||||
|                                             final String profilePicUrl) { | ||||
|         final Account account = getAccount(uid); | ||||
|         final Account toUpdate = new Account(account == null ? 0 : account.getId(), uid, username, cookie, fullName, profilePicUrl); | ||||
|         if (account != null) { | ||||
|             accountDao.updateAccounts(toUpdate); | ||||
|             return; | ||||
|         } | ||||
|         accountDao.insertAccounts(toUpdate); | ||||
|     } | ||||
| 
 | ||||
|     public final void deleteAccount(@NonNull final Account account) { | ||||
|         accountDao.deleteAccounts(account); | ||||
|     } | ||||
| 
 | ||||
|     public final void deleteAllAccounts() { | ||||
|         accountDao.deleteAllAccounts(); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,62 @@ | ||||
| package awais.instagrabber.db.datasources; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| import awais.instagrabber.db.AppDatabase; | ||||
| import awais.instagrabber.db.dao.FavoriteDao; | ||||
| import awais.instagrabber.db.entities.Favorite; | ||||
| import awais.instagrabber.models.enums.FavoriteType; | ||||
| 
 | ||||
| public class FavoriteDataSource { | ||||
|     private static final String TAG = FavoriteDataSource.class.getSimpleName(); | ||||
| 
 | ||||
|     private static FavoriteDataSource INSTANCE; | ||||
| 
 | ||||
|     private final FavoriteDao favoriteDao; | ||||
| 
 | ||||
|     private FavoriteDataSource(final FavoriteDao favoriteDao) { | ||||
|         this.favoriteDao = favoriteDao; | ||||
|     } | ||||
| 
 | ||||
|     public static synchronized FavoriteDataSource getInstance(@NonNull Context context) { | ||||
|         if (INSTANCE == null) { | ||||
|             synchronized (FavoriteDataSource.class) { | ||||
|                 if (INSTANCE == null) { | ||||
|                     final AppDatabase database = AppDatabase.getDatabase(context); | ||||
|                     INSTANCE = new FavoriteDataSource(database.favoriteDao()); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return INSTANCE; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     @Nullable | ||||
|     public final Favorite getFavorite(@NonNull final String query, @NonNull final FavoriteType type) { | ||||
|         return favoriteDao.findFavoriteByQueryAndType(query, type); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     public final List<Favorite> getAllFavorites() { | ||||
|         return favoriteDao.getAllFavorites(); | ||||
|     } | ||||
| 
 | ||||
|     public final void insertOrUpdateFavorite(@NonNull final Favorite favorite) { | ||||
|         if (favorite.getId() > 0) { | ||||
|             favoriteDao.updateFavorites(favorite); | ||||
|             return; | ||||
|         } | ||||
|         favoriteDao.insertFavorites(favorite); | ||||
|     } | ||||
| 
 | ||||
|     public final void deleteFavorite(@NonNull final String query, @NonNull final FavoriteType type) { | ||||
|         final Favorite favorite = getFavorite(query, type); | ||||
|         if (favorite == null) return; | ||||
|         favoriteDao.deleteFavorites(favorite); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										124
									
								
								app/src/main/java/awais/instagrabber/db/entities/Account.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								app/src/main/java/awais/instagrabber/db/entities/Account.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,124 @@ | ||||
| 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.Ignore; | ||||
| import androidx.room.PrimaryKey; | ||||
| 
 | ||||
| import awais.instagrabber.utils.Constants; | ||||
| import awais.instagrabber.utils.TextUtils; | ||||
| 
 | ||||
| @Entity(tableName = Account.TABLE_NAME) | ||||
| public class Account { | ||||
|     public final static String TABLE_NAME = "accounts"; | ||||
|     public final static String COL_ID = "id"; | ||||
|     public final static String COL_USERNAME = Constants.EXTRAS_USERNAME; | ||||
|     public final static String COL_COOKIE = "cookie"; | ||||
|     public final static String COL_UID = "uid"; | ||||
|     public final static String COL_FULL_NAME = "full_name"; | ||||
|     public final static String COL_PROFILE_PIC = "profile_pic"; | ||||
| 
 | ||||
|     @PrimaryKey(autoGenerate = true) | ||||
|     @ColumnInfo(name = COL_ID) | ||||
|     private final int id; | ||||
| 
 | ||||
|     @ColumnInfo(name = COL_UID) | ||||
|     private final String uid; | ||||
| 
 | ||||
|     @ColumnInfo(name = COL_USERNAME) | ||||
|     private final String username; | ||||
| 
 | ||||
|     @ColumnInfo(name = COL_COOKIE) | ||||
|     private final String cookie; | ||||
| 
 | ||||
|     @ColumnInfo(name = COL_FULL_NAME) | ||||
|     private final String fullName; | ||||
| 
 | ||||
|     @ColumnInfo(name = COL_PROFILE_PIC) | ||||
|     private final String profilePic; | ||||
| 
 | ||||
|     @Ignore | ||||
|     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 + | ||||
|                 '}'; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										110
									
								
								app/src/main/java/awais/instagrabber/db/entities/Favorite.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								app/src/main/java/awais/instagrabber/db/entities/Favorite.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,110 @@ | ||||
| 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 = Favorite.TABLE_NAME) | ||||
| public class Favorite { | ||||
|     public final static String TABLE_NAME = "favorites"; | ||||
|     public final static String COL_ID = "id"; | ||||
|     public final static String COL_QUERY = "query_text"; | ||||
|     public final static String COL_TYPE = "type"; | ||||
|     public final static String COL_DISPLAY_NAME = "display_name"; | ||||
|     public final static String COL_PIC_URL = "pic_url"; | ||||
|     public final static String COL_DATE_ADDED = "date_added"; | ||||
| 
 | ||||
|     @PrimaryKey(autoGenerate = true) | ||||
|     @ColumnInfo(name = COL_ID) | ||||
|     private final int id; | ||||
| 
 | ||||
|     @ColumnInfo(name = COL_QUERY) | ||||
|     private final String query; | ||||
| 
 | ||||
|     @ColumnInfo(name = COL_TYPE) | ||||
|     private final FavoriteType type; | ||||
| 
 | ||||
|     @ColumnInfo(name = COL_DISPLAY_NAME) | ||||
|     private final String displayName; | ||||
| 
 | ||||
|     @ColumnInfo(name = COL_PIC_URL) | ||||
|     private final String picUrl; | ||||
| 
 | ||||
|     @ColumnInfo(name = COL_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 + | ||||
|                 '}'; | ||||
|     } | ||||
| } | ||||
| @ -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<Account> cachedAccounts; | ||||
| 
 | ||||
|     private AccountRepository(final AppExecutors appExecutors, final AccountDataSource accountDataSource) { | ||||
|         this.appExecutors = appExecutors; | ||||
|         this.accountDataSource = accountDataSource; | ||||
|     } | ||||
| 
 | ||||
|     public static AccountRepository getInstance(final AccountDataSource accountDataSource) { | ||||
|         if (instance == null) { | ||||
|             instance = new AccountRepository(AppExecutors.getInstance(), accountDataSource); | ||||
|         } | ||||
|         return instance; | ||||
|     } | ||||
| 
 | ||||
|     public void getAccount(final String uid, | ||||
|                            final RepositoryCallback<Account> 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<List<Account>> callback) { | ||||
|         // request on the I/O thread | ||||
|         appExecutors.diskIO().execute(() -> { | ||||
|             final List<Account> 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<Account> accounts, | ||||
|                                        final RepositoryCallback<Void> 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<Account> 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<Void> 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<Void> 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); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,88 @@ | ||||
| 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 FavoriteDataSource favoriteDataSource) { | ||||
|         if (instance == null) { | ||||
|             instance = new FavoriteRepository(AppExecutors.getInstance(), favoriteDataSource); | ||||
|         } | ||||
|         return instance; | ||||
|     } | ||||
| 
 | ||||
|     public void getFavorite(final String query, final FavoriteType type, final RepositoryCallback<Favorite> 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<List<Favorite>> callback) { | ||||
|         // request on the I/O thread | ||||
|         appExecutors.diskIO().execute(() -> { | ||||
|             final List<Favorite> 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<Void> callback) { | ||||
|         // request on the I/O thread | ||||
|         appExecutors.diskIO().execute(() -> { | ||||
|             favoriteDataSource.insertOrUpdateFavorite(favorite); | ||||
|             // notify on the main thread | ||||
|             appExecutors.mainThread().execute(() -> { | ||||
|                 if (callback == null) return; | ||||
|                 callback.onSuccess(null); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     public void deleteFavorite(final String query, | ||||
|                                final FavoriteType type, | ||||
|                                final RepositoryCallback<Void> 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); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,11 @@ | ||||
| package awais.instagrabber.db.repositories; | ||||
| 
 | ||||
| import androidx.annotation.MainThread; | ||||
| 
 | ||||
| public interface RepositoryCallback<T> { | ||||
|     @MainThread | ||||
|     void onSuccess(T result); | ||||
| 
 | ||||
|     @MainThread | ||||
|     void onDataNotAvailable(); | ||||
| } | ||||
| @ -21,9 +21,12 @@ 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.Constants; | ||||
| import awais.instagrabber.utils.CookieUtils; | ||||
| import awais.instagrabber.utils.DataBox; | ||||
| import awais.instagrabber.utils.TextUtils; | ||||
| import awais.instagrabber.utils.Utils; | ||||
| 
 | ||||
| @ -31,9 +34,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(AccountDataSource.getInstance(getContext())); | ||||
|     } | ||||
| 
 | ||||
|     public AccountSwitcherDialogFragment(final OnAddAccountClickListener onAddAccountClickListener) { | ||||
|         this.onAddAccountClickListener = onAddAccountClickListener; | ||||
|         accountRepository = AccountRepository.getInstance(AccountDataSource.getInstance(getContext())); | ||||
|     } | ||||
| 
 | ||||
|     private final AccountSwitcherAdapter.OnAccountClickListener accountClickListener = (model, isCurrent) -> { | ||||
|         if (isCurrent) { | ||||
|             dismiss(); | ||||
| @ -59,8 +73,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<Void>() { | ||||
|                         @Override | ||||
|                         public void onSuccess(final Void result) { | ||||
|                             dismiss(); | ||||
|                         } | ||||
| 
 | ||||
|                         @Override | ||||
|                         public void onDataNotAvailable() { | ||||
|                             dismiss(); | ||||
|                         } | ||||
|                     }); | ||||
|                 }) | ||||
|                 .setNegativeButton(R.string.cancel, null) | ||||
|                 .show(); | ||||
| @ -68,10 +92,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 +122,19 @@ public class AccountSwitcherDialogFragment extends DialogFragment { | ||||
|     private void init() { | ||||
|         final AccountSwitcherAdapter adapter = new AccountSwitcherAdapter(accountClickListener, accountLongClickListener); | ||||
|         binding.accounts.setAdapter(adapter); | ||||
|         final List<DataBox.CookieModel> 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<List<Account>>() { | ||||
|             @Override | ||||
|             public void onSuccess(final List<Account> 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 +153,9 @@ public class AccountSwitcherDialogFragment extends DialogFragment { | ||||
|      * @param cookie   active cookie | ||||
|      * @param allUsers list of users | ||||
|      */ | ||||
|     private void sortUserList(final String cookie, final List<DataBox.CookieModel> allUsers) { | ||||
|     private void sortUserList(final String cookie, final List<Account> allUsers) { | ||||
|         boolean sortByName = true; | ||||
|         for (final DataBox.CookieModel user : allUsers) { | ||||
|         for (final Account user : allUsers) { | ||||
|             if (TextUtils.isEmpty(user.getFullName())) { | ||||
|                 sortByName = false; | ||||
|                 break; | ||||
|  | ||||
| @ -19,6 +19,8 @@ import androidx.fragment.app.DialogFragment; | ||||
| import androidx.fragment.app.FragmentTransaction; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.text.SimpleDateFormat; | ||||
| import java.util.Date; | ||||
| import java.util.Locale; | ||||
| 
 | ||||
| import awais.instagrabber.databinding.DialogCreateBackupBinding; | ||||
| @ -32,6 +34,7 @@ import static awais.instagrabber.utils.DownloadUtils.PERMS; | ||||
| 
 | ||||
| public class CreateBackupDialogFragment extends DialogFragment { | ||||
|     private static final int STORAGE_PERM_REQUEST_CODE = 8020; | ||||
|     private static final SimpleDateFormat BACKUP_FILE_DATE_TIME_FORMAT = new SimpleDateFormat("yyyyMMddHHmmss", Locale.US); | ||||
| 
 | ||||
|     private final OnResultListener onResultListener; | ||||
|     private DialogCreateBackupBinding binding; | ||||
| @ -139,7 +142,8 @@ public class CreateBackupDialogFragment extends DialogFragment { | ||||
|         final DirectoryChooser directoryChooser = new DirectoryChooser() | ||||
|                 .setInitialDirectory(folderPath) | ||||
|                 .setInteractionListener(path -> { | ||||
|                     final File file = new File(path, String.format(Locale.ENGLISH, "barinsta_%d.backup", System.currentTimeMillis())); | ||||
|                     final Date now = new Date(); | ||||
|                     final File file = new File(path, String.format("barinsta_%s.backup", BACKUP_FILE_DATE_TIME_FORMAT.format(now))); | ||||
|                     int flags = 0; | ||||
|                     if (binding.cbExportFavorites.isChecked()) { | ||||
|                         flags |= ExportImportUtils.FLAG_FAVORITES; | ||||
| @ -150,12 +154,12 @@ public class CreateBackupDialogFragment extends DialogFragment { | ||||
|                     if (binding.cbExportLogins.isChecked()) { | ||||
|                         flags |= ExportImportUtils.FLAG_COOKIES; | ||||
|                     } | ||||
|                     ExportImportUtils.exportData(password, flags, file, result -> { | ||||
|                     ExportImportUtils.exportData(context, flags, file, password, result -> { | ||||
|                         if (onResultListener != null) { | ||||
|                             onResultListener.onResult(result); | ||||
|                         } | ||||
|                         dismiss(); | ||||
|                     }, context); | ||||
|                     }); | ||||
| 
 | ||||
|                 }); | ||||
|         directoryChooser.setEnterTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); | ||||
|  | ||||
| @ -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) {} | ||||
|  | ||||
| @ -28,9 +28,11 @@ 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.TextUtils; | ||||
| import awais.instagrabber.utils.Utils; | ||||
| import awais.instagrabber.viewmodels.FavoritesViewModel; | ||||
| 
 | ||||
| public class FavoritesFragment extends Fragment { | ||||
| @ -41,6 +43,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(FavoriteDataSource.getInstance(getContext())); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
| @ -68,9 +77,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<DataBox.FavoriteModel> allFavorites = Utils.dataBox.getAllFavorites(); | ||||
|         favoritesViewModel.getList().postValue(allFavorites); | ||||
|         fetchMissingInfo(allFavorites); | ||||
|         favoriteRepository.getAllFavorites(new RepositoryCallback<List<Favorite>>() { | ||||
|             @Override | ||||
|             public void onSuccess(final List<Favorite> favorites) { | ||||
|                 favoritesViewModel.getList().postValue(favorites); | ||||
|                 fetchMissingInfo(favorites); | ||||
|             } | ||||
| 
 | ||||
|             @Override | ||||
|             public void onDataNotAvailable() {} | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     private void init() { | ||||
| @ -114,30 +130,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<Void>() { | ||||
|                                 @Override | ||||
|                                 public void onSuccess(final Void result) { | ||||
|                                     d.dismiss(); | ||||
|                                     favoriteRepository.getAllFavorites(new RepositoryCallback<List<Favorite>>() { | ||||
|                                         @Override | ||||
|                                         public void onSuccess(final List<Favorite> 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<DataBox.FavoriteModel> allFavorites) { | ||||
|     private void fetchMissingInfo(final List<Favorite> allFavorites) { | ||||
|         final Runnable runnable = () -> { | ||||
|             final List<DataBox.FavoriteModel> updatedList = new ArrayList<>(allFavorites); | ||||
|             final List<Favorite> 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 +174,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<Void>() { | ||||
|                                         @Override | ||||
|                                         public void onSuccess(final Void 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 +213,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<Void>() { | ||||
|                                         @Override | ||||
|                                         public void onSuccess(final Void 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(); | ||||
|                             } | ||||
|  | ||||
| @ -49,6 +49,10 @@ 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; | ||||
| @ -57,7 +61,6 @@ import awais.instagrabber.models.StoryModel; | ||||
| import awais.instagrabber.models.enums.FavoriteType; | ||||
| 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 +457,60 @@ 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(FavoriteDataSource.getInstance(getContext())); | ||||
|         favoriteRepository.getFavorite(hashtag.substring(1), FavoriteType.HASHTAG, new RepositoryCallback<Favorite>() { | ||||
|             @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<Favorite>() { | ||||
|                     @Override | ||||
|                     public void onSuccess(final Favorite result) { | ||||
|                         favoriteRepository.deleteFavorite(hashtag.substring(1), FavoriteType.HASHTAG, new RepositoryCallback<Void>() { | ||||
|                             @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<Void>() { | ||||
|                             @Override | ||||
|                             public void onSuccess(final Void 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 +527,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; | ||||
|  | ||||
| @ -52,6 +52,10 @@ 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; | ||||
| @ -60,7 +64,6 @@ import awais.instagrabber.models.StoryModel; | ||||
| import awais.instagrabber.models.enums.FavoriteType; | ||||
| 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 +446,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(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<Favorite>() { | ||||
|             @Override | ||||
|             public void onSuccess(final Favorite result) { | ||||
|                 locationDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24); | ||||
|                 message = getString(R.string.added_to_favs); | ||||
|                 locationDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24); | ||||
|                 locationDetailsBinding.favChip.setText(R.string.favorite_short); | ||||
|             } | ||||
|             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(); | ||||
| 
 | ||||
|             @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); | ||||
|             } | ||||
|         }); | ||||
|         locationDetailsBinding.favChip.setOnClickListener(v -> { | ||||
|             favoriteRepository.getFavorite(locationId, FavoriteType.LOCATION, new RepositoryCallback<Favorite>() { | ||||
|                 @Override | ||||
|                 public void onSuccess(final Favorite result) { | ||||
|                     favoriteRepository.deleteFavorite(locationId, FavoriteType.LOCATION, new RepositoryCallback<Void>() { | ||||
|                         @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<Void>() { | ||||
|                         @Override | ||||
|                         public void onSuccess(final Void 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 +514,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; | ||||
|  | ||||
| @ -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(); | ||||
|  | ||||
| @ -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; | ||||
| @ -77,7 +84,6 @@ import awais.instagrabber.repositories.responses.FriendshipRepoChangeRootRespons | ||||
| import awais.instagrabber.repositories.responses.FriendshipRepoRestrictRootResponse; | ||||
| 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 +289,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 +298,8 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe | ||||
|         fragmentActivity = (MainActivity) requireActivity(); | ||||
|         friendshipService = FriendshipService.getInstance(); | ||||
|         storiesService = StoriesService.getInstance(); | ||||
|         accountRepository = AccountRepository.getInstance(AccountDataSource.getInstance(getContext())); | ||||
|         favoriteRepository = FavoriteRepository.getInstance(FavoriteDataSource.getInstance(getContext())); | ||||
|         setHasOptionsMenu(true); | ||||
|     } | ||||
| 
 | ||||
| @ -498,19 +508,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<Account>() { | ||||
|                 @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 +573,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<Favorite>() { | ||||
|                 @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 +869,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<Favorite>() { | ||||
|                 @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<Void>() { | ||||
|                         @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<Void>() { | ||||
|                         @Override | ||||
|                         public void onSuccess(final Void 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(); | ||||
|  | ||||
| @ -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,16 @@ 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.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 +45,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(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<DataBox.CookieModel> allCookies = Utils.dataBox.getAllCookies(); | ||||
|         if (isLoggedIn) { | ||||
|             accountCategory.setSummary(R.string.account_hint); | ||||
|             accountCategory.addPreference(getAccountSwitcherPreference(cookie)); | ||||
| @ -67,34 +73,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<List<Account>>() { | ||||
|             @Override | ||||
|             public void onSuccess(@NonNull final List<Account> 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<Void>() { | ||||
|                                                 @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 +194,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<Account>() { | ||||
|                                     @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 +227,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 +290,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 +312,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<Account>() { | ||||
|                 @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() {} | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										98
									
								
								app/src/main/java/awais/instagrabber/utils/AppExecutors.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								app/src/main/java/awais/instagrabber/utils/AppExecutors.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,98 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  * <p> | ||||
|  * 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 static final Object LOCK = new Object(); | ||||
| 
 | ||||
|     private static AppExecutors instance; | ||||
| 
 | ||||
|     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 static AppExecutors getInstance() { | ||||
|         if (instance == null) { | ||||
|             synchronized (LOCK) { | ||||
|                 if (instance == null) { | ||||
|                     instance = new AppExecutors(Executors.newSingleThreadExecutor(), | ||||
|                                                 Executors.newFixedThreadPool(THREAD_COUNT), | ||||
|                                                 new MainThreadExecutor(), | ||||
|                                                 MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10))); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return instance; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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<Void> callback) { | ||||
|         final CookieStore cookieStore = NET_COOKIE_MANAGER.getCookieStore(); | ||||
|         if (cookieStore == null) return; | ||||
|         cookieStore.removeAll(); | ||||
|         try { | ||||
|             AccountRepository.getInstance(AccountDataSource.getInstance(context)) | ||||
|                              .deleteAllAccounts(callback); | ||||
|         } catch (Exception e) { | ||||
|             Log.e(TAG, "setupCookies", e); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -1,26 +1,12 @@ | ||||
| package awais.instagrabber.utils; | ||||
| 
 | ||||
| import android.content.ContentValues; | ||||
| import android.content.Context; | ||||
| import android.database.Cursor; | ||||
| import android.database.sqlite.SQLiteDatabase; | ||||
| import android.database.sqlite.SQLiteOpenHelper; | ||||
| import android.util.Log; | ||||
| 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.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 +14,6 @@ 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 static synchronized DataBox getInstance(final Context context) { | ||||
|         if (sInstance == null) sInstance = new DataBox(context.getApplicationContext()); | ||||
| @ -57,21 +27,6 @@ public final class DataBox extends SQLiteOpenHelper { | ||||
|     @Override | ||||
|     public void onCreate(@NonNull final SQLiteDatabase db) { | ||||
|         Log.i(TAG, "Creating tables..."); | ||||
|         db.execSQL("CREATE TABLE " + TABLE_COOKIES + " (" | ||||
|                            + KEY_ID + " INTEGER PRIMARY KEY," | ||||
|                            + KEY_UID + " TEXT," | ||||
|                            + KEY_USERNAME + " TEXT," | ||||
|                            + KEY_COOKIE + " TEXT," | ||||
|                            + KEY_FULL_NAME + " TEXT," | ||||
|                            + KEY_PROFILE_PIC + " TEXT)"); | ||||
|         // db.execSQL("CREATE TABLE favorites (id INTEGER PRIMARY KEY, query_text TEXT, date_added INTEGER, query_display TEXT)"); | ||||
|         db.execSQL("CREATE TABLE " + TABLE_FAVORITES + " (" | ||||
|                            + FAV_COL_ID + " INTEGER PRIMARY KEY," | ||||
|                            + FAV_COL_QUERY + " TEXT," | ||||
|                            + FAV_COL_TYPE + " TEXT," | ||||
|                            + FAV_COL_DISPLAY_NAME + " TEXT," | ||||
|                            + FAV_COL_PIC_URL + " TEXT," | ||||
|                            + FAV_COL_DATE_ADDED + " INTEGER)"); | ||||
|         Log.i(TAG, "Tables created!"); | ||||
|     } | ||||
| 
 | ||||
| @ -81,515 +36,8 @@ public final class DataBox extends SQLiteOpenHelper { | ||||
|         // switch without break, so that all migrations from a previous version to new are run | ||||
|         switch (oldVersion) { | ||||
|             case 1: | ||||
|                 db.execSQL("ALTER TABLE " + TABLE_COOKIES + " ADD " + KEY_FULL_NAME + " TEXT"); | ||||
|                 db.execSQL("ALTER TABLE " + TABLE_COOKIES + " ADD " + KEY_PROFILE_PIC + " TEXT"); | ||||
|             case 2: | ||||
|                 final List<FavoriteModel> oldFavorites = backupOldFavorites(db); | ||||
|                 // recreate with new columns (as there will be no doubt about the `query_display` column being present or not in the future versions) | ||||
|                 db.execSQL("DROP TABLE " + TABLE_FAVORITES); | ||||
|                 db.execSQL("CREATE TABLE " + TABLE_FAVORITES + " (" | ||||
|                                    + FAV_COL_ID + " INTEGER PRIMARY KEY," | ||||
|                                    + FAV_COL_QUERY + " TEXT," | ||||
|                                    + FAV_COL_TYPE + " TEXT," | ||||
|                                    + FAV_COL_DISPLAY_NAME + " TEXT," | ||||
|                                    + FAV_COL_PIC_URL + " TEXT," | ||||
|                                    + FAV_COL_DATE_ADDED + " INTEGER)"); | ||||
|                 // add the old favorites back | ||||
|                 for (final FavoriteModel oldFavorite : oldFavorites) { | ||||
|                     addOrUpdateFavorite(db, oldFavorite); | ||||
|                 } | ||||
|         } | ||||
|         Log.i(TAG, String.format("DB update from v%d to v%d completed!", oldVersion, newVersion)); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     private List<FavoriteModel> 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<FavoriteModel> oldModels = new ArrayList<>(); | ||||
|         final String sql = "SELECT " | ||||
|                 + "query_text," | ||||
|                 + "date_added" | ||||
|                 + (queryDisplayExists ? ",query_display" : "") | ||||
|                 + " FROM " + TABLE_FAVORITES; | ||||
|         try (final Cursor cursor = db.rawQuery(sql, null)) { | ||||
|             if (cursor != null && cursor.moveToFirst()) { | ||||
|                 do { | ||||
|                     try { | ||||
|                         final String queryText = cursor.getString(cursor.getColumnIndex("query_text")); | ||||
|                         final Pair<FavoriteType, String> favoriteTypeQueryPair = Utils.migrateOldFavQuery(queryText); | ||||
|                         if (favoriteTypeQueryPair == null) continue; | ||||
|                         final FavoriteType type = favoriteTypeQueryPair.first; | ||||
|                         final String query = favoriteTypeQueryPair.second; | ||||
|                         oldModels.add(new FavoriteModel( | ||||
|                                 -1, | ||||
|                                 query, | ||||
|                                 type, | ||||
|                                 queryDisplayExists ? cursor.getString(cursor.getColumnIndex("query_display")) | ||||
|                                                    : null, | ||||
|                                 null, | ||||
|                                 new Date(cursor.getLong(cursor.getColumnIndex("date_added"))) | ||||
|                         )); | ||||
|                     } catch (Exception e) { | ||||
|                         Log.e(TAG, "onUpgrade", e); | ||||
|                     } | ||||
|                 } while (cursor.moveToNext()); | ||||
|             } | ||||
|         } catch (Exception e) { | ||||
|             Log.e(TAG, "onUpgrade", e); | ||||
|         } | ||||
|         Log.d(TAG, "backupOldFavorites: oldModels:" + oldModels); | ||||
|         return oldModels; | ||||
|     } | ||||
| 
 | ||||
|     public boolean checkColumnExists(@NonNull final SQLiteDatabase db, | ||||
|                                      @NonNull final String tableName, | ||||
|                                      @NonNull final String columnName) { | ||||
|         boolean exists = false; | ||||
|         try (Cursor cursor = db.rawQuery("PRAGMA table_info(" + tableName + ")", null)) { | ||||
|             if (cursor.moveToFirst()) { | ||||
|                 do { | ||||
|                     final String currentColumn = cursor.getString(cursor.getColumnIndex("name")); | ||||
|                     if (currentColumn.equals(columnName)) { | ||||
|                         exists = true; | ||||
|                     } | ||||
|                 } while (cursor.moveToNext()); | ||||
| 
 | ||||
|             } | ||||
|         } catch (Exception ex) { | ||||
|             Log.e(TAG, "checkColumnExists", ex); | ||||
|         } | ||||
|         return exists; | ||||
|     } | ||||
| 
 | ||||
|     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<FavoriteModel> getAllFavorites() { | ||||
|         final List<FavoriteModel> 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<CookieModel> getAllCookies() { | ||||
|         final List<CookieModel> 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 + | ||||
|                     '}'; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -9,8 +9,14 @@ import android.widget.Toast; | ||||
| 
 | ||||
| import androidx.annotation.IntDef; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| import com.google.common.collect.ImmutableList; | ||||
| import com.google.common.util.concurrent.FutureCallback; | ||||
| import com.google.common.util.concurrent.Futures; | ||||
| import com.google.common.util.concurrent.ListenableFuture; | ||||
| import com.google.common.util.concurrent.SettableFuture; | ||||
| 
 | ||||
| import org.checkerframework.checker.nullness.compatqual.NullableDecl; | ||||
| import org.json.JSONArray; | ||||
| import org.json.JSONException; | ||||
| import org.json.JSONObject; | ||||
| @ -18,12 +24,20 @@ 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 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; | ||||
| @ -39,47 +53,6 @@ public final class ExportImportUtils { | ||||
|     public static final int FLAG_FAVORITES = 1 << 1; | ||||
|     public static final int FLAG_SETTINGS = 1 << 2; | ||||
| 
 | ||||
|     @IntDef(value = {FLAG_COOKIES, FLAG_FAVORITES, FLAG_SETTINGS}, flag = true) | ||||
|     @interface ExportImportFlags {} | ||||
| 
 | ||||
|     public static void exportData(@Nullable final String password, | ||||
|                                   @ExportImportFlags final int flags, | ||||
|                                   @NonNull final File filePath, | ||||
|                                   final FetchListener<Boolean> 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); | ||||
|             } | ||||
|         } else if (fetchListener != null) fetchListener.onResult(false); | ||||
|     } | ||||
| 
 | ||||
|     public static void importData(@NonNull final Context context, | ||||
|                                   @ExportImportFlags final int flags, | ||||
|                                   @NonNull final File file, | ||||
| @ -99,7 +72,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 +85,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 +104,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<Boolean> fetchListener) { | ||||
|         try { | ||||
| @ -138,10 +114,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 +127,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 +151,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 +160,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(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 importSettings(final JSONObject jsonObject) throws JSONException { | ||||
|         final JSONObject objSettings = jsonObject.getJSONObject("settings"); | ||||
|         final Iterator<String> 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 importAccounts(final Context context, | ||||
|                                        final JSONObject jsonObject) { | ||||
|         final List<Account> 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(AccountDataSource.getInstance(context)) | ||||
|                          .insertOrUpdateAccounts(accounts, null); | ||||
|     } | ||||
| 
 | ||||
|     private static void importSettings(final JSONObject jsonObject) { | ||||
|         try { | ||||
|             final JSONObject objSettings = jsonObject.getJSONObject("settings"); | ||||
|             final Iterator<String> 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,97 +224,209 @@ 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()); | ||||
|     public static void exportData(@NonNull final Context context, | ||||
|                                   @ExportImportFlags final int flags, | ||||
|                                   @NonNull final File filePath, | ||||
|                                   final String password, | ||||
|                                   final FetchListener<Boolean> fetchListener) { | ||||
|         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); | ||||
|             } | ||||
|             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); | ||||
|         }); | ||||
| 
 | ||||
|             result = jsonObject.toString(); | ||||
|     } | ||||
| 
 | ||||
|     private static void getExportString(@ExportImportFlags final int flags, | ||||
|                                         @NonNull final Context context, | ||||
|                                         final OnExportStringCreatedCallback callback) { | ||||
|         if (callback == null) return; | ||||
|         try { | ||||
|             final ImmutableList.Builder<ListenableFuture<?>> futures = ImmutableList.builder(); | ||||
|             futures.add((flags & FLAG_SETTINGS) == FLAG_SETTINGS | ||||
|                         ? getSettings(context) | ||||
|                         : Futures.immediateFuture(null)); | ||||
|             futures.add((flags & FLAG_COOKIES) == FLAG_COOKIES | ||||
|                         ? getCookies(context) | ||||
|                         : Futures.immediateFuture(null)); | ||||
|             futures.add((flags & FLAG_FAVORITES) == FLAG_FAVORITES | ||||
|                         ? getFavorites(context) | ||||
|                         : Futures.immediateFuture(null)); | ||||
|             //noinspection UnstableApiUsage | ||||
|             final ListenableFuture<List<Object>> allFutures = Futures.allAsList(futures.build()); | ||||
|             Futures.addCallback(allFutures, new FutureCallback<List<Object>>() { | ||||
|                 @Override | ||||
|                 public void onSuccess(@NullableDecl final List<Object> result) { | ||||
|                     final JSONObject jsonObject = new JSONObject(); | ||||
|                     if (result == null) { | ||||
|                         callback.onCreated(jsonObject.toString()); | ||||
|                         return; | ||||
|                     } | ||||
|                     try { | ||||
|                         final JSONObject settings = (JSONObject) result.get(0); | ||||
|                         if (settings != null) { | ||||
|                             jsonObject.put("settings", settings); | ||||
|                         } | ||||
|                     } catch (Exception e) { | ||||
|                         Log.e(TAG, "error getting settings: ", e); | ||||
|                     } | ||||
|                     try { | ||||
|                         final JSONArray accounts = (JSONArray) result.get(1); | ||||
|                         if (accounts != null) { | ||||
|                             jsonObject.put("cookies", accounts); | ||||
|                         } | ||||
|                     } catch (Exception e) { | ||||
|                         Log.e(TAG, "error getting accounts", e); | ||||
|                     } | ||||
|                     try { | ||||
|                         final JSONArray favorites = (JSONArray) result.get(2); | ||||
|                         if (favorites != null) { | ||||
|                             jsonObject.put("favs", favorites); | ||||
|                         } | ||||
|                     } catch (Exception e) { | ||||
|                         Log.e(TAG, "error getting favorites: ", e); | ||||
|                     } | ||||
|                     callback.onCreated(jsonObject.toString()); | ||||
|                 } | ||||
| 
 | ||||
|                 @Override | ||||
|                 public void onFailure(@NonNull final Throwable t) { | ||||
|                     Log.e(TAG, "onFailure: ", t); | ||||
|                     callback.onCreated(null); | ||||
|                 } | ||||
|             }, AppExecutors.getInstance().tasksThread()); | ||||
|             return; | ||||
|         } 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 | ||||
|     private static JSONObject getSettings(@NonNull final Context context) { | ||||
|     private static ListenableFuture<JSONObject> getSettings(@NonNull final Context context) { | ||||
|         final SharedPreferences sharedPreferences = context.getSharedPreferences(Constants.SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); | ||||
|         final Map<String, ?> allPrefs = sharedPreferences.getAll(); | ||||
|         if (allPrefs == null) { | ||||
|         return AppExecutors.getInstance().tasksThread().submit(() -> { | ||||
|             final Map<String, ?> allPrefs = sharedPreferences.getAll(); | ||||
|             if (allPrefs == null) { | ||||
|                 return new JSONObject(); | ||||
|             } | ||||
|             try { | ||||
|                 final JSONObject jsonObject = new JSONObject(allPrefs); | ||||
|                 jsonObject.remove(Constants.COOKIE); | ||||
|                 jsonObject.remove(Constants.DEVICE_UUID); | ||||
|                 jsonObject.remove(Constants.PREV_INSTALL_VERSION); | ||||
|                 return jsonObject; | ||||
|             } catch (Exception e) { | ||||
|                 Log.e(TAG, "Error exporting settings", e); | ||||
|             } | ||||
|             return new JSONObject(); | ||||
|         } | ||||
|         try { | ||||
|             final JSONObject jsonObject = new JSONObject(allPrefs); | ||||
|             jsonObject.remove(Constants.COOKIE); | ||||
|             jsonObject.remove(Constants.DEVICE_UUID); | ||||
|             jsonObject.remove(Constants.PREV_INSTALL_VERSION); | ||||
|             return jsonObject; | ||||
|         } catch (Exception e) { | ||||
|             Log.e(TAG, "Error exporting settings", e); | ||||
|         } | ||||
|         return new JSONObject(); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     private static JSONArray getFavorites() { | ||||
|         if (Utils.dataBox == null) return new JSONArray(); | ||||
|         try { | ||||
|             final List<DataBox.FavoriteModel> 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); | ||||
|     private static ListenableFuture<JSONArray> getFavorites(final Context context) { | ||||
|         final SettableFuture<JSONArray> future = SettableFuture.create(); | ||||
|         final FavoriteRepository favoriteRepository = FavoriteRepository.getInstance(FavoriteDataSource.getInstance(context)); | ||||
|         favoriteRepository.getAllFavorites(new RepositoryCallback<List<Favorite>>() { | ||||
|             @Override | ||||
|             public void onSuccess(final List<Favorite> 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) { | ||||
|                     if (logCollector != null) { | ||||
|                         logCollector.appendException(e, LogFile.UTILS_EXPORT, "getFavorites"); | ||||
|                     } | ||||
|                     if (BuildConfig.DEBUG) { | ||||
|                         Log.e(TAG, "Error exporting favorites", e); | ||||
|                     } | ||||
|                 } | ||||
|                 future.set(jsonArray); | ||||
|             } | ||||
|             return jsonArray; | ||||
|         } catch (final Exception e) { | ||||
|             if (logCollector != null) { | ||||
|                 logCollector.appendException(e, LogFile.UTILS_EXPORT, "getFavorites"); | ||||
| 
 | ||||
|             @Override | ||||
|             public void onDataNotAvailable() { | ||||
|                 future.set(new JSONArray()); | ||||
|             } | ||||
|             if (BuildConfig.DEBUG) { | ||||
|                 Log.e(TAG, "Error exporting favorites", e); | ||||
|             } | ||||
|         } | ||||
|         return new JSONArray(); | ||||
|         }); | ||||
|         return future; | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     private static JSONArray getCookies() { | ||||
|         if (Utils.dataBox == null) return new JSONArray(); | ||||
|         try { | ||||
|             final List<DataBox.CookieModel> 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 ListenableFuture<JSONArray> getCookies(final Context context) { | ||||
|         final SettableFuture<JSONArray> future = SettableFuture.create(); | ||||
|         final AccountRepository accountRepository = AccountRepository.getInstance(AccountDataSource.getInstance(context)); | ||||
|         accountRepository.getAllAccounts(new RepositoryCallback<List<Account>>() { | ||||
|             @Override | ||||
|             public void onSuccess(final List<Account> accounts) { | ||||
|                 final JSONArray jsonArray = new JSONArray(); | ||||
|                 try { | ||||
|                     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); | ||||
|                     } | ||||
|                 } catch (Exception e) { | ||||
|                     if (logCollector != null) { | ||||
|                         logCollector.appendException(e, LogFile.UTILS_EXPORT, "getCookies"); | ||||
|                     } | ||||
|                     if (BuildConfig.DEBUG) { | ||||
|                         Log.e(TAG, "Error exporting accounts", e); | ||||
|                     } | ||||
|                 } | ||||
|                 future.set(jsonArray); | ||||
|             } | ||||
|             return jsonArray; | ||||
|         } catch (final Exception e) { | ||||
|             if (BuildConfig.DEBUG) { | ||||
|                 Log.e(TAG, "Error exporting accounts", e); | ||||
| 
 | ||||
|             @Override | ||||
|             public void onDataNotAvailable() { | ||||
|                 future.set(new JSONArray()); | ||||
|             } | ||||
|         } | ||||
|         return new JSONArray(); | ||||
|         }); | ||||
|         return future; | ||||
|     } | ||||
| 
 | ||||
|     @IntDef(value = {FLAG_COOKIES, FLAG_FAVORITES, FLAG_SETTINGS}, flag = true) | ||||
|     @interface ExportImportFlags {} | ||||
| 
 | ||||
|     public interface OnExportStringCreatedCallback { | ||||
|         void onCreated(String exportString); | ||||
|     } | ||||
| } | ||||
| @ -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; | ||||
|  | ||||
| @ -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<DataBox.FavoriteModel>> list; | ||||
|     private MutableLiveData<List<Favorite>> list; | ||||
| 
 | ||||
|     public MutableLiveData<List<DataBox.FavoriteModel>> getList() { | ||||
|     public MutableLiveData<List<Favorite>> getList() { | ||||
|         if (list == null) { | ||||
|             list = new MutableLiveData<>(); | ||||
|         } | ||||
|  | ||||
| @ -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" /> | ||||
| 
 | ||||
|         <androidx.appcompat.widget.AppCompatCheckBox | ||||
|             android:id="@+id/desktop_mode" | ||||
|             android:layout_width="wrap_content" | ||||
|             android:layout_height="match_parent" | ||||
|             android:layout_width="0dp" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:layout_weight="1" | ||||
|             android:gravity="center" | ||||
|             android:text="@string/desktop_2fa" /> | ||||
|     </androidx.appcompat.widget.LinearLayoutCompat> | ||||
|  | ||||
| @ -101,7 +101,7 @@ | ||||
| 
 | ||||
|     <ProgressBar | ||||
|         android:id="@+id/fav_progress" | ||||
|         style="@style/Widget.MaterialComponents.ProgressIndicator.Circular.Indeterminate" | ||||
|         style="@style/Widget.MaterialComponents.CircularProgressIndicator" | ||||
|         android:layout_width="24dp" | ||||
|         android:layout_height="24dp" | ||||
|         android:visibility="gone" | ||||
|  | ||||
| @ -6,7 +6,7 @@ buildscript { | ||||
| 
 | ||||
|     dependencies { | ||||
|         classpath 'com.android.tools.build:gradle:4.1.1' | ||||
|         def nav_version = "2.3.0" | ||||
|         def nav_version = "2.3.2" | ||||
|         classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version" | ||||
|     } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user