mirror of
				https://github.com/KokaKiwi/BarInsta
				synced 2025-10-31 03:25:34 +00:00 
			
		
		
		
	Update export backup logic to use futures instead of CountDownLatch. Change backup file name to use date and time instead of milliseconds.
This commit is contained in:
		
							parent
							
								
									6b8df5fee2
								
							
						
					
					
						commit
						cea61eae6c
					
				| @ -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); | ||||
|  | ||||
| @ -2,7 +2,6 @@ package awais.instagrabber.utils; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.content.SharedPreferences; | ||||
| import android.os.Handler; | ||||
| import android.util.Base64; | ||||
| import android.util.Log; | ||||
| import android.util.Pair; | ||||
| @ -10,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; | ||||
| @ -24,7 +29,6 @@ import java.util.Date; | ||||
| import java.util.Iterator; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.concurrent.CountDownLatch; | ||||
| 
 | ||||
| import awais.instagrabber.BuildConfig; | ||||
| import awais.instagrabber.db.datasources.AccountDataSource; | ||||
| @ -49,48 +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) { | ||||
|         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); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     public static void importData(@NonNull final Context context, | ||||
|                                   @ExportImportFlags final int flags, | ||||
|                                   @NonNull final File file, | ||||
| @ -262,66 +224,116 @@ public final class ExportImportUtils { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     //todo Need to improve logic | ||||
|     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); | ||||
|         }); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private static void getExportString(@ExportImportFlags final int flags, | ||||
|                                         @NonNull final Context context, | ||||
|                                         final OnExportStringCreatedCallback callback) { | ||||
|         final Handler innerHandler = new Handler(); | ||||
|         AppExecutors.getInstance().tasksThread().execute(() -> { | ||||
|             final CountDownLatch responseWaiter = new CountDownLatch(3); | ||||
|         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(); | ||||
|                 innerHandler.post(() -> { | ||||
|                     if ((flags & FLAG_SETTINGS) == FLAG_SETTINGS) { | ||||
|                         try { | ||||
|                             jsonObject.put("settings", getSettings(context)); | ||||
|                         } catch (JSONException e) { | ||||
|                             Log.e(TAG, "getExportString: ", e); | ||||
|                     if (result == null) { | ||||
|                         callback.onCreated(jsonObject.toString()); | ||||
|                         return; | ||||
|                     } | ||||
|                     } | ||||
|                     responseWaiter.countDown(); | ||||
|                 }); | ||||
|                 innerHandler.post(() -> { | ||||
|                     if ((flags & FLAG_COOKIES) == FLAG_COOKIES) { | ||||
|                         getCookies(context, array -> { | ||||
|                     try { | ||||
|                                 jsonObject.put("cookies", array); | ||||
|                             } catch (JSONException e) { | ||||
|                         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); | ||||
|                     } | ||||
|                             responseWaiter.countDown(); | ||||
|                         }); | ||||
|                         return; | ||||
|                     } | ||||
|                     responseWaiter.countDown(); | ||||
|                 }); | ||||
|                 innerHandler.post(() -> { | ||||
|                     if ((flags & FLAG_FAVORITES) == FLAG_FAVORITES) { | ||||
|                         getFavorites(context, array -> { | ||||
|                     try { | ||||
|                                 jsonObject.put("favs", array); | ||||
|                             } catch (JSONException e) { | ||||
|                                 Log.e(TAG, "getExportString: ", e); | ||||
|                         final JSONArray favorites = (JSONArray) result.get(2); | ||||
|                         if (favorites != null) { | ||||
|                             jsonObject.put("favs", favorites); | ||||
|                         } | ||||
|                             responseWaiter.countDown(); | ||||
|                         }); | ||||
|                         return; | ||||
|                     } catch (Exception e) { | ||||
|                         Log.e(TAG, "error getting favorites: ", e); | ||||
|                     } | ||||
|                     responseWaiter.countDown(); | ||||
|                 }); | ||||
|                 responseWaiter.await(); | ||||
|                     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); | ||||
|         } | ||||
|         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); | ||||
|         return AppExecutors.getInstance().tasksThread().submit(() -> { | ||||
|             final Map<String, ?> allPrefs = sharedPreferences.getAll(); | ||||
|             if (allPrefs == null) { | ||||
|                 return new JSONObject(); | ||||
| @ -336,12 +348,12 @@ public final class ExportImportUtils { | ||||
|                 Log.e(TAG, "Error exporting settings", e); | ||||
|             } | ||||
|             return new JSONObject(); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     private static void getFavorites(final Context context, final OnFavoritesJsonLoadedCallback callback) { | ||||
|         final FavoriteDataSource dataSource = FavoriteDataSource.getInstance(context); | ||||
|         final FavoriteRepository favoriteRepository = FavoriteRepository.getInstance(dataSource); | ||||
|         try { | ||||
|     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) { | ||||
| @ -357,17 +369,6 @@ public final class ExportImportUtils { | ||||
|                         jsonArray.put(jsonObject); | ||||
|                     } | ||||
|                 } catch (Exception e) { | ||||
|                         Log.e(TAG, "onSuccess: Error creating json array", e); | ||||
|                     } | ||||
|                     callback.onFavoritesJsonLoaded(jsonArray); | ||||
|                 } | ||||
| 
 | ||||
|                 @Override | ||||
|                 public void onDataNotAvailable() { | ||||
|                     callback.onFavoritesJsonLoaded(new JSONArray()); | ||||
|                 } | ||||
|             }); | ||||
|         } catch (final Exception e) { | ||||
|                     if (logCollector != null) { | ||||
|                         logCollector.appendException(e, LogFile.UTILS_EXPORT, "getFavorites"); | ||||
|                     } | ||||
| @ -375,15 +376,25 @@ public final class ExportImportUtils { | ||||
|                         Log.e(TAG, "Error exporting favorites", e); | ||||
|                     } | ||||
|                 } | ||||
|                 future.set(jsonArray); | ||||
|             } | ||||
| 
 | ||||
|     private static void getCookies(final Context context, final OnAccountJsonLoadedCallback callback) { | ||||
|             @Override | ||||
|             public void onDataNotAvailable() { | ||||
|                 future.set(new JSONArray()); | ||||
|             } | ||||
|         }); | ||||
|         return future; | ||||
|     } | ||||
| 
 | ||||
|     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) { | ||||
|                 try { | ||||
|                 final JSONArray jsonArray = new JSONArray(); | ||||
|                 try { | ||||
|                     for (final Account cookie : accounts) { | ||||
|                         final JSONObject jsonObject = new JSONObject(); | ||||
|                         jsonObject.put("i", cookie.getUid()); | ||||
| @ -393,30 +404,29 @@ public final class ExportImportUtils { | ||||
|                         jsonObject.put("profile_pic", cookie.getProfilePic()); | ||||
|                         jsonArray.put(jsonObject); | ||||
|                     } | ||||
|                     callback.onAccountsJsonLoaded(jsonArray); | ||||
|                     return; | ||||
|                 } catch (Exception e) { | ||||
|                     if (logCollector != null) { | ||||
|                         logCollector.appendException(e, LogFile.UTILS_EXPORT, "getCookies"); | ||||
|                     } | ||||
|                     if (BuildConfig.DEBUG) { | ||||
|                         Log.e(TAG, "Error exporting accounts", e); | ||||
|                     } | ||||
|                 callback.onAccountsJsonLoaded(new JSONArray()); | ||||
|                 } | ||||
|                 future.set(jsonArray); | ||||
|             } | ||||
| 
 | ||||
|             @Override | ||||
|             public void onDataNotAvailable() { | ||||
|                 callback.onAccountsJsonLoaded(new JSONArray()); | ||||
|                 future.set(new JSONArray()); | ||||
|             } | ||||
|         }); | ||||
|         return future; | ||||
|     } | ||||
| 
 | ||||
|     @IntDef(value = {FLAG_COOKIES, FLAG_FAVORITES, FLAG_SETTINGS}, flag = true) | ||||
|     @interface ExportImportFlags {} | ||||
| 
 | ||||
|     public interface OnExportStringCreatedCallback { | ||||
|         void onCreated(String exportString); | ||||
|     } | ||||
| 
 | ||||
|     public interface OnAccountJsonLoadedCallback { | ||||
|         void onAccountsJsonLoaded(JSONArray array); | ||||
|     } | ||||
| 
 | ||||
|     public interface OnFavoritesJsonLoadedCallback { | ||||
|         void onFavoritesJsonLoaded(JSONArray array); | ||||
|     } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user