Add Kotlin and convert some model classes to kotlin

This commit is contained in:
Ammar Githam 2021-05-22 17:58:18 +09:00
parent e726ba3ccf
commit 84e93431ae
54 changed files with 589 additions and 1297 deletions

View File

@ -1,4 +1,5 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: "androidx.navigation.safeargs"
apply from: 'sentry.gradle'

View File

@ -183,7 +183,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
}
getSupportFragmentManager().addOnBackStackChangedListener(this);
// Initialise the internal map
AppExecutors.getInstance().tasksThread().execute(() -> {
AppExecutors.INSTANCE.getTasksThread().execute(() -> {
EmojiParser.setup(this);
EmojiVariantManager.getInstance();
});

View File

@ -4,19 +4,12 @@ import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import awais.instagrabber.adapters.viewholder.FeedStoryViewHolder;
import awais.instagrabber.databinding.ItemHighlightBinding;
import awais.instagrabber.models.FeedStoryModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
public final class FeedStoriesAdapter extends ListAdapter<FeedStoryModel, FeedStoryViewHolder> {
private final OnFeedStoryClickListener listener;
@ -29,7 +22,7 @@ public final class FeedStoriesAdapter extends ListAdapter<FeedStoryModel, FeedSt
@Override
public boolean areContentsTheSame(@NonNull final FeedStoryModel oldItem, @NonNull final FeedStoryModel newItem) {
return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId()) && oldItem.isFullyRead().equals(newItem.isFullyRead());
return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId()) && oldItem.isFullyRead() == newItem.isFullyRead();
}
};

View File

@ -57,7 +57,7 @@ public final class FeedStoriesListAdapter extends ListAdapter<FeedStoryModel, St
@Override
public boolean areContentsTheSame(@NonNull final FeedStoryModel oldItem, @NonNull final FeedStoryModel newItem) {
return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId()) && oldItem.isFullyRead().equals(newItem.isFullyRead());
return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId()) && oldItem.isFullyRead() == newItem.isFullyRead();
}
};

View File

@ -33,7 +33,7 @@ public class FilterViewHolder extends RecyclerView.ViewHolder {
this.binding = binding;
this.tuneFilters = tuneFilters;
this.onFilterClickListener = onFilterClickListener;
appExecutors = AppExecutors.getInstance();
appExecutors = AppExecutors.INSTANCE;
}
public void bind(final int position, final String originalKey, final Bitmap originalBitmap, final Filter<?> item, final boolean isSelected) {
@ -55,13 +55,13 @@ public class FilterViewHolder extends RecyclerView.ViewHolder {
final Bitmap bitmap = BitmapUtils.getBitmapFromMemCache(filterKey);
if (bitmap == null) {
final GPUImageFilter filter = item.getInstance();
appExecutors.tasksThread().submit(() -> {
appExecutors.getTasksThread().submit(() -> {
GPUImage.getBitmapForMultipleFilters(
originalBitmap,
ImmutableList.<GPUImageFilter>builder().add(filter).addAll(tuneFilters).build(),
filteredBitmap -> {
BitmapUtils.addBitmapToMemoryCache(filterKey, filteredBitmap, true);
appExecutors.mainThread().execute(() -> binding.getRoot().post(() -> binding.preview.setImageBitmap(filteredBitmap)));
appExecutors.getMainThread().execute(() -> binding.getRoot().post(() -> binding.preview.setImageBitmap(filteredBitmap)));
}
);
});

View File

@ -22,7 +22,7 @@ public class Tooltip extends AppCompatTextView {
private ViewPropertyAnimator animator;
private boolean showing;
private final AppExecutors appExecutors = AppExecutors.getInstance();
private final AppExecutors appExecutors = AppExecutors.INSTANCE;
private final Runnable dismissRunnable = () -> {
animator = animate().alpha(0).setListener(new AnimatorListenerAdapter() {
@Override
@ -86,8 +86,8 @@ public class Tooltip extends AppCompatTextView {
updateTooltipPosition();
showing = true;
appExecutors.mainThread().cancel(dismissRunnable);
appExecutors.mainThread().execute(dismissRunnable, 2000);
appExecutors.getMainThread().cancel(dismissRunnable);
appExecutors.getMainThread().execute(dismissRunnable, 2000);
if (animator != null) {
animator.setListener(null);
animator.cancel();
@ -109,7 +109,7 @@ public class Tooltip extends AppCompatTextView {
animator = null;
}
appExecutors.mainThread().cancel(dismissRunnable);
appExecutors.getMainThread().cancel(dismissRunnable);
dismissRunnable.run();
}
showing = false;

View File

@ -53,7 +53,7 @@ public class EmojiGridAdapter extends RecyclerView.Adapter<EmojiGridAdapter.Emoj
final EmojiParser emojiParser = EmojiParser.getInstance();
final Map<EmojiCategoryType, EmojiCategory> categoryMap = emojiParser.getCategoryMap();
emojiVariantManager = EmojiVariantManager.getInstance();
appExecutors = AppExecutors.getInstance();
appExecutors = AppExecutors.INSTANCE;
setHasStableIds(true);
if (emojiCategoryType == null) {
// show all if type is null
@ -81,13 +81,13 @@ public class EmojiGridAdapter extends RecyclerView.Adapter<EmojiGridAdapter.Emoj
final Emoji emoji = differ.getCurrentList().get(position);
final String variant = emojiVariantManager.getVariant(emoji.getUnicode());
if (variant != null) {
appExecutors.tasksThread().execute(() -> {
appExecutors.getTasksThread().execute(() -> {
final Optional<Emoji> first = emoji.getVariants()
.stream()
.filter(e -> e.getUnicode().equals(variant))
.findFirst();
if (!first.isPresent()) return;
appExecutors.mainThread().execute(() -> holder.bind(position, first.get(), emoji));
appExecutors.getMainThread().execute(() -> holder.bind(position, first.get(), emoji));
});
return;
}

View File

@ -21,7 +21,7 @@ public class EmojiVariantManager {
private static final String TAG = EmojiVariantManager.class.getSimpleName();
private static final Object LOCK = new Object();
private final AppExecutors appExecutors = AppExecutors.getInstance();
private final AppExecutors appExecutors = AppExecutors.INSTANCE;
private final Map<String, String> selectedVariantMap = new HashMap<>();
private static EmojiVariantManager instance;
@ -57,7 +57,7 @@ public class EmojiVariantManager {
public void setVariant(final String parent, final String variant) {
if (parent == null || variant == null) return;
selectedVariantMap.put(parent, variant);
appExecutors.tasksThread().execute(() -> {
appExecutors.getTasksThread().execute(() -> {
final JSONObject jsonObject = new JSONObject(selectedVariantMap);
final String json = jsonObject.toString();
Utils.settingsHelper.putString(PREF_EMOJI_VARIANTS, json);

View File

@ -57,7 +57,7 @@ public final class EmojiVariantPopup {
this.rootView = rootView;
this.listener = listener;
emojiVariantManager = EmojiVariantManager.getInstance();
appExecutors = AppExecutors.getInstance();
appExecutors = AppExecutors.INSTANCE;
}
public void show(@NonNull final View view, @NonNull final Emoji emoji) {

View File

@ -11,7 +11,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import awais.instagrabber.utils.AppExecutors;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.emoji.EmojiParser;
@ -22,7 +21,7 @@ public class ReactionsManager {
private static final String TAG = ReactionsManager.class.getSimpleName();
private static final Object LOCK = new Object();
private final AppExecutors appExecutors = AppExecutors.getInstance();
// private final AppExecutors appExecutors = AppExecutors.INSTANCE;
private final List<Emoji> reactions = new ArrayList<>();
private static ReactionsManager instance;

View File

@ -23,7 +23,7 @@ public class AccountRepository {
public static AccountRepository getInstance(final AccountDataSource accountDataSource) {
if (instance == null) {
instance = new AccountRepository(AppExecutors.getInstance(), accountDataSource);
instance = new AccountRepository(AppExecutors.INSTANCE, accountDataSource);
}
return instance;
}
@ -31,10 +31,10 @@ public class AccountRepository {
public void getAccount(final long uid,
final RepositoryCallback<Account> callback) {
// request on the I/O thread
appExecutors.diskIO().execute(() -> {
appExecutors.getDiskIO().execute(() -> {
final Account account = accountDataSource.getAccount(String.valueOf(uid));
// notify on the main thread
appExecutors.mainThread().execute(() -> {
appExecutors.getMainThread().execute(() -> {
if (callback == null) return;
if (account == null) {
callback.onDataNotAvailable();
@ -47,10 +47,10 @@ public class AccountRepository {
public void getAllAccounts(final RepositoryCallback<List<Account>> callback) {
// request on the I/O thread
appExecutors.diskIO().execute(() -> {
appExecutors.getDiskIO().execute(() -> {
final List<Account> accounts = accountDataSource.getAllAccounts();
// notify on the main thread
appExecutors.mainThread().execute(() -> {
appExecutors.getMainThread().execute(() -> {
if (callback == null) return;
if (accounts == null) {
callback.onDataNotAvailable();
@ -65,7 +65,7 @@ public class AccountRepository {
public void insertOrUpdateAccounts(final List<Account> accounts,
final RepositoryCallback<Void> callback) {
// request on the I/O thread
appExecutors.diskIO().execute(() -> {
appExecutors.getDiskIO().execute(() -> {
for (final Account account : accounts) {
accountDataSource.insertOrUpdateAccount(account.getUid(),
account.getUsername(),
@ -74,7 +74,7 @@ public class AccountRepository {
account.getProfilePic());
}
// notify on the main thread
appExecutors.mainThread().execute(() -> {
appExecutors.getMainThread().execute(() -> {
if (callback == null) return;
callback.onSuccess(null);
});
@ -88,11 +88,11 @@ public class AccountRepository {
final String profilePicUrl,
final RepositoryCallback<Account> callback) {
// request on the I/O thread
appExecutors.diskIO().execute(() -> {
appExecutors.getDiskIO().execute(() -> {
accountDataSource.insertOrUpdateAccount(String.valueOf(uid), username, cookie, fullName, profilePicUrl);
final Account updated = accountDataSource.getAccount(String.valueOf(uid));
// notify on the main thread
appExecutors.mainThread().execute(() -> {
appExecutors.getMainThread().execute(() -> {
if (callback == null) return;
if (updated == null) {
callback.onDataNotAvailable();
@ -106,10 +106,10 @@ public class AccountRepository {
public void deleteAccount(final Account account,
final RepositoryCallback<Void> callback) {
// request on the I/O thread
appExecutors.diskIO().execute(() -> {
appExecutors.getDiskIO().execute(() -> {
accountDataSource.deleteAccount(account);
// notify on the main thread
appExecutors.mainThread().execute(() -> {
appExecutors.getMainThread().execute(() -> {
if (callback == null) return;
callback.onSuccess(null);
});
@ -118,10 +118,10 @@ public class AccountRepository {
public void deleteAllAccounts(final RepositoryCallback<Void> callback) {
// request on the I/O thread
appExecutors.diskIO().execute(() -> {
appExecutors.getDiskIO().execute(() -> {
accountDataSource.deleteAllAccounts();
// notify on the main thread
appExecutors.mainThread().execute(() -> {
appExecutors.getMainThread().execute(() -> {
if (callback == null) return;
callback.onSuccess(null);
});

View File

@ -22,7 +22,7 @@ public class DMLastNotifiedRepository {
public static DMLastNotifiedRepository getInstance(final DMLastNotifiedDataSource dmLastNotifiedDataSource) {
if (instance == null) {
instance = new DMLastNotifiedRepository(AppExecutors.getInstance(), dmLastNotifiedDataSource);
instance = new DMLastNotifiedRepository(AppExecutors.INSTANCE, dmLastNotifiedDataSource);
}
return instance;
}
@ -30,10 +30,10 @@ public class DMLastNotifiedRepository {
public void getDMLastNotified(final String threadId,
final RepositoryCallback<DMLastNotified> callback) {
// request on the I/O thread
appExecutors.diskIO().execute(() -> {
appExecutors.getDiskIO().execute(() -> {
final DMLastNotified dmLastNotified = dmLastNotifiedDataSource.getDMLastNotified(threadId);
// notify on the main thread
appExecutors.mainThread().execute(() -> {
appExecutors.getMainThread().execute(() -> {
if (callback == null) return;
if (dmLastNotified == null) {
callback.onDataNotAvailable();
@ -46,10 +46,10 @@ public class DMLastNotifiedRepository {
public void getAllDMDmLastNotified(final RepositoryCallback<List<DMLastNotified>> callback) {
// request on the I/O thread
appExecutors.diskIO().execute(() -> {
appExecutors.getDiskIO().execute(() -> {
final List<DMLastNotified> allDMDmLastNotified = dmLastNotifiedDataSource.getAllDMDmLastNotified();
// notify on the main thread
appExecutors.mainThread().execute(() -> {
appExecutors.getMainThread().execute(() -> {
if (callback == null) return;
if (allDMDmLastNotified == null) {
callback.onDataNotAvailable();
@ -64,14 +64,14 @@ public class DMLastNotifiedRepository {
public void insertOrUpdateDMLastNotified(final List<DMLastNotified> dmLastNotifiedList,
final RepositoryCallback<Void> callback) {
// request on the I/O thread
appExecutors.diskIO().execute(() -> {
appExecutors.getDiskIO().execute(() -> {
for (final DMLastNotified dmLastNotified : dmLastNotifiedList) {
dmLastNotifiedDataSource.insertOrUpdateDMLastNotified(dmLastNotified.getThreadId(),
dmLastNotified.getLastNotifiedMsgTs(),
dmLastNotified.getLastNotifiedAt());
}
// notify on the main thread
appExecutors.mainThread().execute(() -> {
appExecutors.getMainThread().execute(() -> {
if (callback == null) return;
callback.onSuccess(null);
});
@ -83,11 +83,11 @@ public class DMLastNotifiedRepository {
final LocalDateTime lastNotifiedAt,
final RepositoryCallback<DMLastNotified> callback) {
// request on the I/O thread
appExecutors.diskIO().execute(() -> {
appExecutors.getDiskIO().execute(() -> {
dmLastNotifiedDataSource.insertOrUpdateDMLastNotified(threadId, lastNotifiedMsgTs, lastNotifiedAt);
final DMLastNotified updated = dmLastNotifiedDataSource.getDMLastNotified(threadId);
// notify on the main thread
appExecutors.mainThread().execute(() -> {
appExecutors.getMainThread().execute(() -> {
if (callback == null) return;
if (updated == null) {
callback.onDataNotAvailable();
@ -101,10 +101,10 @@ public class DMLastNotifiedRepository {
public void deleteDMLastNotified(final DMLastNotified dmLastNotified,
final RepositoryCallback<Void> callback) {
// request on the I/O thread
appExecutors.diskIO().execute(() -> {
appExecutors.getDiskIO().execute(() -> {
dmLastNotifiedDataSource.deleteDMLastNotified(dmLastNotified);
// notify on the main thread
appExecutors.mainThread().execute(() -> {
appExecutors.getMainThread().execute(() -> {
if (callback == null) return;
callback.onSuccess(null);
});
@ -113,10 +113,10 @@ public class DMLastNotifiedRepository {
public void deleteAllDMLastNotified(final RepositoryCallback<Void> callback) {
// request on the I/O thread
appExecutors.diskIO().execute(() -> {
appExecutors.getDiskIO().execute(() -> {
dmLastNotifiedDataSource.deleteAllDMLastNotified();
// notify on the main thread
appExecutors.mainThread().execute(() -> {
appExecutors.getMainThread().execute(() -> {
if (callback == null) return;
callback.onSuccess(null);
});

View File

@ -22,17 +22,17 @@ public class FavoriteRepository {
public static FavoriteRepository getInstance(final FavoriteDataSource favoriteDataSource) {
if (instance == null) {
instance = new FavoriteRepository(AppExecutors.getInstance(), favoriteDataSource);
instance = new FavoriteRepository(AppExecutors.INSTANCE, 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(() -> {
appExecutors.getDiskIO().execute(() -> {
final Favorite favorite = favoriteDataSource.getFavorite(query, type);
// notify on the main thread
appExecutors.mainThread().execute(() -> {
appExecutors.getMainThread().execute(() -> {
if (callback == null) return;
if (favorite == null) {
callback.onDataNotAvailable();
@ -45,10 +45,10 @@ public class FavoriteRepository {
public void getAllFavorites(final RepositoryCallback<List<Favorite>> callback) {
// request on the I/O thread
appExecutors.diskIO().execute(() -> {
appExecutors.getDiskIO().execute(() -> {
final List<Favorite> favorites = favoriteDataSource.getAllFavorites();
// notify on the main thread
appExecutors.mainThread().execute(() -> {
appExecutors.getMainThread().execute(() -> {
if (callback == null) return;
if (favorites == null) {
callback.onDataNotAvailable();
@ -62,10 +62,10 @@ public class FavoriteRepository {
public void insertOrUpdateFavorite(final Favorite favorite,
final RepositoryCallback<Void> callback) {
// request on the I/O thread
appExecutors.diskIO().execute(() -> {
appExecutors.getDiskIO().execute(() -> {
favoriteDataSource.insertOrUpdateFavorite(favorite);
// notify on the main thread
appExecutors.mainThread().execute(() -> {
appExecutors.getMainThread().execute(() -> {
if (callback == null) return;
callback.onSuccess(null);
});
@ -76,10 +76,10 @@ public class FavoriteRepository {
final FavoriteType type,
final RepositoryCallback<Void> callback) {
// request on the I/O thread
appExecutors.diskIO().execute(() -> {
appExecutors.getDiskIO().execute(() -> {
favoriteDataSource.deleteFavorite(query, type);
// notify on the main thread
appExecutors.mainThread().execute(() -> {
appExecutors.getMainThread().execute(() -> {
if (callback == null) return;
callback.onSuccess(null);
});

View File

@ -25,7 +25,7 @@ public class RecentSearchRepository {
public static RecentSearchRepository getInstance(final RecentSearchDataSource recentSearchDataSource) {
if (instance == null) {
instance = new RecentSearchRepository(AppExecutors.getInstance(), recentSearchDataSource);
instance = new RecentSearchRepository(AppExecutors.INSTANCE, recentSearchDataSource);
}
return instance;
}
@ -34,10 +34,10 @@ public class RecentSearchRepository {
@NonNull final FavoriteType type,
final RepositoryCallback<RecentSearch> callback) {
// request on the I/O thread
appExecutors.diskIO().execute(() -> {
appExecutors.getDiskIO().execute(() -> {
final RecentSearch recentSearch = recentSearchDataSource.getRecentSearchByIgIdAndType(igId, type);
// notify on the main thread
appExecutors.mainThread().execute(() -> {
appExecutors.getMainThread().execute(() -> {
if (callback == null) return;
if (recentSearch == null) {
callback.onDataNotAvailable();
@ -50,10 +50,10 @@ public class RecentSearchRepository {
public void getAllRecentSearches(final RepositoryCallback<List<RecentSearch>> callback) {
// request on the I/O thread
appExecutors.diskIO().execute(() -> {
appExecutors.getDiskIO().execute(() -> {
final List<RecentSearch> recentSearches = recentSearchDataSource.getAllRecentSearches();
// notify on the main thread
appExecutors.mainThread().execute(() -> {
appExecutors.getMainThread().execute(() -> {
if (callback == null) return;
callback.onSuccess(recentSearches);
});
@ -73,14 +73,14 @@ public class RecentSearchRepository {
@NonNull final FavoriteType type,
final RepositoryCallback<Void> callback) {
// request on the I/O thread
appExecutors.diskIO().execute(() -> {
appExecutors.getDiskIO().execute(() -> {
RecentSearch recentSearch = recentSearchDataSource.getRecentSearchByIgIdAndType(igId, type);
recentSearch = recentSearch == null
? new RecentSearch(igId, name, username, picUrl, type, LocalDateTime.now())
: new RecentSearch(recentSearch.getId(), igId, name, username, picUrl, type, LocalDateTime.now());
recentSearchDataSource.insertOrUpdateRecentSearch(recentSearch);
// notify on the main thread
appExecutors.mainThread().execute(() -> {
appExecutors.getMainThread().execute(() -> {
if (callback == null) return;
callback.onSuccess(null);
});
@ -91,13 +91,13 @@ public class RecentSearchRepository {
@NonNull final FavoriteType type,
final RepositoryCallback<Void> callback) {
// request on the I/O thread
appExecutors.diskIO().execute(() -> {
appExecutors.getDiskIO().execute(() -> {
final RecentSearch recentSearch = recentSearchDataSource.getRecentSearchByIgIdAndType(igId, type);
if (recentSearch != null) {
recentSearchDataSource.deleteRecentSearch(recentSearch);
}
// notify on the main thread
appExecutors.mainThread().execute(() -> {
appExecutors.getMainThread().execute(() -> {
if (callback == null) return;
if (recentSearch == null) {
callback.onDataNotAvailable();
@ -111,11 +111,11 @@ public class RecentSearchRepository {
public void deleteRecentSearch(@NonNull final RecentSearch recentSearch,
final RepositoryCallback<Void> callback) {
// request on the I/O thread
appExecutors.diskIO().execute(() -> {
appExecutors.getDiskIO().execute(() -> {
recentSearchDataSource.deleteRecentSearch(recentSearch);
// notify on the main thread
appExecutors.mainThread().execute(() -> {
appExecutors.getMainThread().execute(() -> {
if (callback == null) return;
callback.onSuccess(null);
});

View File

@ -59,7 +59,7 @@ public class AccountSwitcherDialogFragment extends DialogFragment {
// final FragmentActivity activity = getActivity();
// if (activity != null) activity.recreate();
// dismiss();
AppExecutors.getInstance().mainThread().execute(() -> {
AppExecutors.INSTANCE.getMainThread().execute(() -> {
final Context context = getContext();
if (context == null) return;
ProcessPhoenix.triggerRebirth(context);

View File

@ -174,7 +174,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
private boolean hasKbOpenedOnce;
private boolean wasToggled;
private final AppExecutors appExecutors = AppExecutors.getInstance();
private final AppExecutors appExecutors = AppExecutors.INSTANCE;
private final Animatable2Compat.AnimationCallback micToSendAnimationCallback = new Animatable2Compat.AnimationCallback() {
@Override
public void onAnimationEnd(final Drawable drawable) {
@ -545,7 +545,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
@SuppressLint("UnsafeOptInUsageError")
private void cleanup() {
if (prevTitleRunnable != null) {
appExecutors.mainThread().cancel(prevTitleRunnable);
appExecutors.getMainThread().cancel(prevTitleRunnable);
}
for (int childCount = binding.chats.getChildCount(), i = 0; i < childCount; ++i) {
final RecyclerView.ViewHolder holder = binding.chats.getChildViewHolder(binding.chats.getChildAt(i));
@ -1379,11 +1379,11 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
private void setTitle(final String title) {
if (actionBar == null) return;
if (prevTitleRunnable != null) {
appExecutors.mainThread().cancel(prevTitleRunnable);
appExecutors.getMainThread().cancel(prevTitleRunnable);
}
prevTitleRunnable = () -> actionBar.setTitle(title);
// set title delayed to avoid title blink if fetch is fast
appExecutors.mainThread().execute(prevTitleRunnable, 1000);
appExecutors.getMainThread().execute(prevTitleRunnable, 1000);
}
private void downloadItem(final DirectItem item) {

View File

@ -116,7 +116,7 @@ public class FiltersFragment extends Fragment {
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
appExecutors = AppExecutors.getInstance();
appExecutors = AppExecutors.INSTANCE;
viewModel = new ViewModelProvider(this).get(FiltersFragmentViewModel.class);
}
@ -194,7 +194,7 @@ public class FiltersFragment extends Fragment {
final Context context = getContext();
if (context == null) return;
binding.preview.setScaleType(GPUImage.ScaleType.CENTER_INSIDE);
appExecutors.tasksThread().execute(() -> {
appExecutors.getTasksThread().execute(() -> {
binding.preview.setImage(sourceUri);
setPreviewBounds();
});
@ -230,7 +230,7 @@ public class FiltersFragment extends Fragment {
binding.apply.setOnClickListener(v -> {
if (callback == null) return;
final List<Filter<?>> appliedTunings = getAppliedTunings();
appExecutors.tasksThread().submit(() -> {
appExecutors.getTasksThread().submit(() -> {
final Bitmap bitmap = binding.preview.getGPUImage().getBitmapWithFilterApplied();
try {
BitmapUtils.convertToJpegAndSaveToUri(context, bitmap, destUri);
@ -287,7 +287,7 @@ public class FiltersFragment extends Fragment {
inputStream = context.getContentResolver().openInputStream(sourceUri);
BitmapFactory.decodeStream(inputStream, null, options);
final float ratio = (float) options.outWidth / options.outHeight;
appExecutors.mainThread().execute(() -> {
appExecutors.getMainThread().execute(() -> {
final ViewGroup.LayoutParams previewLayoutParams = binding.preview.getLayoutParams();
if (options.outHeight > options.outWidth) {
previewLayoutParams.width = (int) (binding.preview.getHeight() * ratio);
@ -472,7 +472,7 @@ public class FiltersFragment extends Fragment {
bitmap,
onFilterClickListener
);
appExecutors.mainThread().execute(() -> {
appExecutors.getMainThread().execute(() -> {
binding.filters.setAdapter(filtersAdapter);
filtersAdapter.submitList(FiltersHelper.getFilters(), () -> {
if (appliedFilter == null) return;

View File

@ -183,7 +183,7 @@ public class ImageEditFragment extends Fragment {
if (context == null) return;
final Uri resultUri = viewModel.getResultUri().getValue();
if (resultUri == null) return;
Utils.mediaScanFile(context, new File(resultUri.toString()), (path, uri) -> AppExecutors.getInstance().mainThread().execute(() -> {
Utils.mediaScanFile(context, new File(resultUri.toString()), (path, uri) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
final NavController navController = NavHostFragment.findNavController(this);
setNavControllerResult(navController, resultUri);
navController.navigateUp();

View File

@ -94,7 +94,7 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
// shouldRecreate();
Toast.makeText(context1, R.string.logout_success, Toast.LENGTH_SHORT).show();
settingsHelper.putString(Constants.COOKIE, "");
AppExecutors.getInstance().mainThread().execute(() -> ProcessPhoenix.triggerRebirth(context1), 200);
AppExecutors.INSTANCE.getMainThread().execute(() -> ProcessPhoenix.triggerRebirth(context1), 200);
return true;
}));
}
@ -139,8 +139,7 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
if (context1 == null) return;
Toast.makeText(context1, R.string.logout_success, Toast.LENGTH_SHORT).show();
settingsHelper.putString(Constants.COOKIE, "");
AppExecutors.getInstance().mainThread()
.execute(() -> ProcessPhoenix.triggerRebirth(context1), 200);
AppExecutors.INSTANCE.getMainThread().execute(() -> ProcessPhoenix.triggerRebirth(context1), 200);
}
@Override
@ -307,7 +306,7 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
// final FragmentActivity activity = getActivity();
// if (activity == null) return;
// activity.recreate();
AppExecutors.getInstance().mainThread().execute(() -> {
AppExecutors.INSTANCE.getMainThread().execute(() -> {
final Context context = getContext();
if (context == null) return;
ProcessPhoenix.triggerRebirth(context);

View File

@ -1,117 +0,0 @@
package awais.instagrabber.models;
import androidx.annotation.NonNull;
import java.io.Serializable;
import java.util.Date;
import java.util.Objects;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.utils.Utils;
public class Comment implements Serializable, Cloneable {
private final User user;
private final String id;
private final String text;
private long likes;
private final long timestamp;
private boolean liked;
private final int replyCount;
private final boolean isChild;
public Comment(final String id,
final String text,
final long timestamp,
final long likes,
final boolean liked,
final User user,
final int replyCount, final boolean isChild) {
this.id = id;
this.text = text;
this.likes = likes;
this.liked = liked;
this.timestamp = timestamp;
this.user = user;
this.replyCount = replyCount;
this.isChild = isChild;
}
public String getId() {
return id;
}
public String getText() {
return text;
}
@NonNull
public String getDateTime() {
return Utils.datetimeParser.format(new Date(timestamp * 1000L));
}
public long getLikes() {
return likes;
}
public boolean getLiked() {
return liked;
}
public void setLiked(boolean liked) {
this.likes = liked ? likes + 1 : likes - 1;
this.liked = liked;
}
public User getUser() {
return user;
}
public int getReplyCount() {
return replyCount;
}
public boolean isChild() {
return isChild;
}
@NonNull
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final Comment comment = (Comment) o;
return likes == comment.likes &&
timestamp == comment.timestamp &&
liked == comment.liked &&
replyCount == comment.replyCount &&
Objects.equals(user, comment.user) &&
Objects.equals(id, comment.id) &&
Objects.equals(text, comment.text) &&
isChild == comment.isChild;
}
@Override
public int hashCode() {
return Objects.hash(user, id, text, likes, timestamp, liked, replyCount, isChild);
}
@NonNull
@Override
public String toString() {
return "Comment{" +
"user=" + user +
", id='" + id + '\'' +
", text='" + text + '\'' +
", likes=" + likes +
", timestamp=" + timestamp +
", liked=" + liked +
", replyCount" + replyCount +
", isChild" + isChild +
'}';
}
}

View File

@ -0,0 +1,69 @@
package awais.instagrabber.models
import awais.instagrabber.repositories.responses.User
import awais.instagrabber.utils.Utils
import java.io.Serializable
import java.util.*
class Comment(
val id: String,
val text: String,
val timestamp: Long,
var likes: Long,
private var liked: Boolean,
val user: User,
val replyCount: Int,
val isChild: Boolean,
) : Serializable, Cloneable {
val dateTime: String
get() = Utils.datetimeParser.format(Date(timestamp * 1000L))
fun getLiked(): Boolean {
return liked
}
fun setLiked(liked: Boolean) {
likes = if (liked) likes + 1 else likes - 1
this.liked = liked
}
@Throws(CloneNotSupportedException::class)
public override fun clone(): Any {
return super.clone()
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Comment
if (id != other.id) return false
if (text != other.text) return false
if (timestamp != other.timestamp) return false
if (likes != other.likes) return false
if (liked != other.liked) return false
if (user != other.user) return false
if (replyCount != other.replyCount) return false
if (isChild != other.isChild) return false
return true
}
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + text.hashCode()
result = 31 * result + timestamp.hashCode()
result = 31 * result + likes.hashCode()
result = 31 * result + liked.hashCode()
result = 31 * result + user.hashCode()
result = 31 * result + replyCount
result = 31 * result + isChild.hashCode()
return result
}
override fun toString(): String {
return "Comment(id='$id', text='$text', timestamp=$timestamp, likes=$likes, liked=$liked, user=$user, replyCount=$replyCount, isChild=$isChild)"
}
}

View File

@ -1,78 +0,0 @@
package awais.instagrabber.models;
import androidx.annotation.NonNull;
import java.io.Serializable;
import java.util.Date;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.utils.Utils;
public final class FeedStoryModel implements Serializable {
private final String storyMediaId;
private final User profileModel;
private final StoryModel firstStoryModel;
private Boolean fullyRead;
private final boolean isLive, isBestie;
private final long timestamp;
private final int mediaCount;
public FeedStoryModel(final String storyMediaId,
final User profileModel,
final boolean fullyRead,
final long timestamp,
final StoryModel firstStoryModel,
final int mediaCount,
final boolean isLive,
final boolean isBestie) {
this.storyMediaId = storyMediaId;
this.profileModel = profileModel;
this.fullyRead = fullyRead;
this.timestamp = timestamp;
this.firstStoryModel = firstStoryModel;
this.mediaCount = mediaCount;
this.isLive = isLive;
this.isBestie = isBestie;
}
public String getStoryMediaId() {
return storyMediaId;
}
public long getTimestamp() {
return timestamp;
}
@NonNull
public String getDateTime() {
return Utils.datetimeParser.format(new Date(timestamp * 1000L));
}
public int getMediaCount() {
return mediaCount;
}
public User getProfileModel() {
return profileModel;
}
public StoryModel getFirstStoryModel() {
return firstStoryModel;
}
public Boolean isFullyRead() {
return fullyRead;
}
public void setFullyRead(final boolean fullyRead) {
this.fullyRead = fullyRead;
}
public boolean isLive() {
return isLive;
}
public boolean isBestie() {
return isBestie;
}
}

View File

@ -0,0 +1,20 @@
package awais.instagrabber.models
import awais.instagrabber.repositories.responses.User
import awais.instagrabber.utils.Utils
import java.io.Serializable
import java.util.*
data class FeedStoryModel(
val storyMediaId: String,
val profileModel: User,
var isFullyRead: Boolean,
val timestamp: Long,
val firstStoryModel: StoryModel,
val mediaCount: Int,
val isLive: Boolean,
val isBestie: Boolean
) : Serializable {
val dateTime: String
get() = Utils.datetimeParser.format(Date(timestamp * 1000L))
}

View File

@ -1,68 +0,0 @@
package awais.instagrabber.models;
import androidx.annotation.Nullable;
import java.io.Serializable;
public final class FollowModel implements Serializable {
private final String id;
private final String username;
private final String fullName;
private final String profilePicUrl;
private String endCursor;
private boolean hasNextPage;
private boolean isShown = true;
public FollowModel(final String id, final String username, final String fullName, final String profilePicUrl) {
this.id = id;
this.username = username;
this.fullName = fullName;
this.profilePicUrl = profilePicUrl;
}
public String getId() {
return id;
}
public String getUsername() {
return username;
}
public String getFullName() {
return fullName;
}
public String getProfilePicUrl() {
return profilePicUrl;
}
public boolean isShown() {
return isShown;
}
public void setShown(final boolean shown) {
isShown = shown;
}
public void setPageCursor(final boolean hasNextPage, final String endCursor) {
this.endCursor = endCursor;
this.hasNextPage = hasNextPage;
}
public boolean hasNextPage() {
return endCursor != null && hasNextPage;
}
public String getEndCursor() {
return endCursor;
}
@Override
public boolean equals(@Nullable final Object obj) {
if (obj instanceof FollowModel) {
final FollowModel model = (FollowModel) obj;
if (model.getId().equals(id) && model.getUsername().equals(username)) return true;
}
return super.equals(obj);
}
}

View File

@ -0,0 +1,49 @@
package awais.instagrabber.models
import java.io.Serializable
class FollowModel(
val id: String,
val username: String,
val fullName: String,
val profilePicUrl: String
) : Serializable {
private var hasNextPage = false
get() = endCursor != null && field
var isShown = true
var endCursor: String? = null
private set
fun setPageCursor(hasNextPage: Boolean, endCursor: String?) {
this.endCursor = endCursor
this.hasNextPage = hasNextPage
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as FollowModel
if (id != other.id) return false
if (username != other.username) return false
if (fullName != other.fullName) return false
if (profilePicUrl != other.profilePicUrl) return false
if (isShown != other.isShown) return false
if (endCursor != other.endCursor) return false
return true
}
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + username.hashCode()
result = 31 * result + fullName.hashCode()
result = 31 * result + profilePicUrl.hashCode()
result = 31 * result + isShown.hashCode()
result = 31 * result + (endCursor?.hashCode() ?: 0)
return result
}
}

View File

@ -1,52 +0,0 @@
package awais.instagrabber.models;
import androidx.annotation.NonNull;
import java.util.Date;
import awais.instagrabber.utils.Utils;
public final class HighlightModel {
private final String title;
private final String id;
private final String thumbnailUrl;
private final long timestamp;
private final int mediaCount;
public HighlightModel(final String title,
final String id,
final String thumbnailUrl,
final long timestamp,
final int mediaCount) {
this.title = title;
this.id = id;
this.thumbnailUrl = thumbnailUrl;
this.timestamp = timestamp;
this.mediaCount = mediaCount;
}
public String getTitle() {
return title;
}
public String getId() {
return id;
}
public String getThumbnailUrl() {
return thumbnailUrl;
}
public long getTimestamp() {
return timestamp;
}
@NonNull
public String getDateTime() {
return Utils.datetimeParser.format(new Date(timestamp * 1000L));
}
public int getMediaCount() {
return mediaCount;
}
}

View File

@ -0,0 +1,15 @@
package awais.instagrabber.models
import awais.instagrabber.utils.Utils
import java.util.*
data class HighlightModel(
val title: String,
val id: String,
val thumbnailUrl: String,
val timestamp: Long,
val mediaCount: Int
) {
val dateTime: String
get() = Utils.datetimeParser.format(Date(timestamp * 1000L))
}

View File

@ -1,21 +0,0 @@
package awais.instagrabber.models;
import awais.instagrabber.models.enums.IntentModelType;
public final class IntentModel {
private final IntentModelType type;
private final String text;
public IntentModel(final IntentModelType type, final String text) {
this.type = type;
this.text = text;
}
public IntentModelType getType() {
return type;
}
public String getText() {
return text;
}
}

View File

@ -0,0 +1,5 @@
package awais.instagrabber.models
import awais.instagrabber.models.enums.IntentModelType
data class IntentModel(val type: IntentModelType, val text: String)

View File

@ -1,64 +0,0 @@
package awais.instagrabber.models;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Objects;
public class Resource<T> {
public final Status status;
public final T data;
public final String message;
public final int resId;
private Resource(@NonNull Status status,
@Nullable T data,
@Nullable String message,
int resId) {
this.status = status;
this.data = data;
this.message = message;
this.resId = resId;
}
@NonNull
public static <T> Resource<T> success(@NonNull T data) {
return new Resource<>(Status.SUCCESS, data, null, 0);
}
@NonNull
public static <T> Resource<T> error(String msg, @Nullable T data) {
return new Resource<>(Status.ERROR, data, msg, 0);
}
@NonNull
public static <T> Resource<T> error(int resId, @Nullable T data) {
return new Resource<>(Status.ERROR, data, null, resId);
}
@NonNull
public static <T> Resource<T> loading(@Nullable T data) {
return new Resource<>(Status.LOADING, data, null, 0);
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final Resource<?> resource = (Resource<?>) o;
return status == resource.status &&
Objects.equals(data, resource.data) &&
Objects.equals(message, resource.message);
}
@Override
public int hashCode() {
return Objects.hash(status, data, message);
}
public enum Status {
SUCCESS,
ERROR,
LOADING
}
}

View File

@ -0,0 +1,36 @@
package awais.instagrabber.models
import androidx.annotation.StringRes
data class Resource<T>(
@JvmField val status: Status,
@JvmField val data: T? = null,
@JvmField val message: String? = null,
@JvmField @StringRes val resId: Int = 0,
) {
enum class Status {
SUCCESS, ERROR, LOADING
}
companion object {
@JvmStatic
fun <T> success(data: T): Resource<T> {
return Resource(Status.SUCCESS, data, null, 0)
}
@JvmStatic
fun <T> error(msg: String?, data: T?): Resource<T?> {
return Resource(Status.ERROR, data, msg, 0)
}
@JvmStatic
fun <T> error(resId: Int, data: T?): Resource<T?> {
return Resource(Status.ERROR, data, null, resId)
}
@JvmStatic
fun <T> loading(data: T?): Resource<T?> {
return Resource(Status.LOADING, data, null, 0)
}
}
}

View File

@ -1,66 +0,0 @@
package awais.instagrabber.models;
import android.graphics.RectF;
import java.util.HashMap;
import java.util.Map;
import awais.instagrabber.fragments.imageedit.filters.FiltersHelper;
import awais.instagrabber.utils.SerializablePair;
public class SavedImageEditState {
private final String sessionId;
private final String originalPath;
private float[] cropImageMatrixValues; // 9 values of matrix
private RectF cropRect;
private HashMap<FiltersHelper.FilterType, Map<Integer, Object>> appliedTuningFilters;
private SerializablePair<FiltersHelper.FilterType, Map<Integer, Object>> appliedFilter;
public SavedImageEditState(final String sessionId, String originalPath) {
this.sessionId = sessionId;
this.originalPath = originalPath;
}
public String getSessionId() {
return sessionId;
}
public String getOriginalPath() {
return originalPath;
}
public float[] getCropImageMatrixValues() {
return cropImageMatrixValues;
}
public void setCropImageMatrixValues(float[] cropImageMatrixValues) {
this.cropImageMatrixValues = cropImageMatrixValues;
}
public RectF getCropRect() {
return cropRect;
}
public void setCropRect(RectF cropRect) {
this.cropRect = cropRect;
}
public HashMap<FiltersHelper.FilterType, Map<Integer, Object>> getAppliedTuningFilters() {
return appliedTuningFilters;
}
public void setAppliedTuningFilters(final HashMap<FiltersHelper.FilterType, Map<Integer, Object>> appliedTuningFilters) {
this.appliedTuningFilters = appliedTuningFilters;
}
public SerializablePair<FiltersHelper.FilterType, Map<Integer, Object>> getAppliedFilter() {
return appliedFilter;
}
public void setAppliedFilter(final SerializablePair<FiltersHelper.FilterType, Map<Integer, Object>> appliedFilter) {
this.appliedFilter = appliedFilter;
}
}

View File

@ -0,0 +1,13 @@
package awais.instagrabber.models
import android.graphics.RectF
import awais.instagrabber.fragments.imageedit.filters.FiltersHelper
import awais.instagrabber.utils.SerializablePair
import java.util.*
data class SavedImageEditState(val sessionId: String, val originalPath: String) {
var cropImageMatrixValues: FloatArray? = null // 9 values of matrix
var cropRect: RectF? = null
var appliedTuningFilters: HashMap<FiltersHelper.FilterType, Map<Int, Any>>? = null
var appliedFilter: SerializablePair<FiltersHelper.FilterType, Map<Int, Any>>? = null
}

View File

@ -1,110 +0,0 @@
package awais.instagrabber.models;
import androidx.annotation.DrawableRes;
import androidx.annotation.IdRes;
import androidx.annotation.NavigationRes;
import androidx.annotation.NonNull;
import java.util.Objects;
public class Tab {
private final int iconResId;
private final String title;
private final boolean removable;
/**
* This is name part of the navigation resource
* eg: @navigation/<b>graphName</b>
*/
private final String graphName;
/**
* This is the actual resource id of the navigation resource (R.navigation.graphName = navigationResId)
*/
private final int navigationResId;
/**
* This is the resource id of the root navigation tag of the navigation resource.
* <p>eg: inside R.navigation.direct_messages_nav_graph, the id of the root tag is R.id.direct_messages_nav_graph.
* <p>So this field would equal to the value of R.id.direct_messages_nav_graph
*/
private final int navigationRootId;
/**
* This is the start destination of the nav graph
*/
private final int startDestinationFragmentId;
public Tab(@DrawableRes final int iconResId,
@NonNull final String title,
final boolean removable,
@NonNull final String graphName,
@NavigationRes final int navigationResId,
@IdRes final int navigationRootId,
@IdRes final int startDestinationFragmentId) {
this.iconResId = iconResId;
this.title = title;
this.removable = removable;
this.graphName = graphName;
this.navigationResId = navigationResId;
this.navigationRootId = navigationRootId;
this.startDestinationFragmentId = startDestinationFragmentId;
}
public int getIconResId() {
return iconResId;
}
public String getTitle() {
return title;
}
public boolean isRemovable() {
return removable;
}
public String getGraphName() {
return graphName;
}
public int getNavigationResId() {
return navigationResId;
}
public int getNavigationRootId() {
return navigationRootId;
}
public int getStartDestinationFragmentId() {
return startDestinationFragmentId;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final Tab tab = (Tab) o;
return iconResId == tab.iconResId &&
removable == tab.removable &&
navigationResId == tab.navigationResId &&
navigationRootId == tab.navigationRootId &&
startDestinationFragmentId == tab.startDestinationFragmentId &&
Objects.equals(title, tab.title) &&
Objects.equals(graphName, tab.graphName);
}
@Override
public int hashCode() {
return Objects.hash(iconResId, title, removable, graphName, navigationResId, navigationRootId, startDestinationFragmentId);
}
@NonNull
@Override
public String toString() {
return "Tab{" +
"title='" + title + '\'' +
", removable=" + removable +
", graphName='" + graphName + '\'' +
'}';
}
}

View File

@ -0,0 +1,36 @@
package awais.instagrabber.models
import androidx.annotation.DrawableRes
import androidx.annotation.IdRes
import androidx.annotation.NavigationRes
data class Tab(
@param:DrawableRes val iconResId: Int,
val title: String,
val isRemovable: Boolean,
/**
* This is name part of the navigation resource
* eg: @navigation/ **graphName**
*/
val graphName: String,
/**
* This is the actual resource id of the navigation resource (R.navigation.graphName = navigationResId)
*/
@param:NavigationRes val navigationResId: Int,
/**
* This is the resource id of the root navigation tag of the navigation resource.
*
* eg: inside R.navigation.direct_messages_nav_graph, the id of the root tag is R.id.direct_messages_nav_graph.
*
* So this field would equal to the value of R.id.direct_messages_nav_graph
*/
@param:IdRes val navigationRootId: Int,
/**
* This is the start destination of the nav graph
*/
@param:IdRes val startDestinationFragmentId: Int,
)

View File

@ -1,97 +0,0 @@
package awais.instagrabber.models;
public class UploadPhotoOptions {
private final String uploadId;
private final String name;
// private final Uri uri;
private final long byteLength;
private final boolean isSideCar;
private final String waterfallId;
public static class Builder {
private String uploadId;
private String name;
// private Uri uri;
private boolean isSideCar;
private String waterfallId;
private long byteLength;
private Builder() {}
public Builder setUploadId(final String uploadId) {
this.uploadId = uploadId;
return this;
}
public Builder setName(final String name) {
this.name = name;
return this;
}
// public Builder setUri(final Uri uri) {
// this.uri = uri;
// return this;
// }
public Builder setByteLength(final long byteLength) {
this.byteLength = byteLength;
return this;
}
public Builder setIsSideCar(final boolean isSideCar) {
this.isSideCar = isSideCar;
return this;
}
public Builder setWaterfallId(final String waterfallId) {
this.waterfallId = waterfallId;
return this;
}
public UploadPhotoOptions build() {
return new UploadPhotoOptions(uploadId, name, /*uri,*/ byteLength, isSideCar, waterfallId);
}
}
public static Builder builder() {
return new Builder();
}
public UploadPhotoOptions(final String uploadId,
final String name,
// final Uri uri,
final long byteLength,
final boolean isSideCar,
final String waterfallId) {
this.uploadId = uploadId;
this.name = name;
// this.uri = uri;
this.byteLength = byteLength;
this.isSideCar = isSideCar;
this.waterfallId = waterfallId;
}
public String getUploadId() {
return uploadId;
}
public String getName() {
return name;
}
// public Uri getUri() {
// return uri;
// }
public long getByteLength() {
return byteLength;
}
public boolean isSideCar() {
return isSideCar;
}
public String getWaterfallId() {
return waterfallId;
}
}

View File

@ -0,0 +1,9 @@
package awais.instagrabber.models
data class UploadPhotoOptions(
val uploadId: String? = null,
val name: String,
val byteLength: Long = 0,
val isSideCar: Boolean = false,
val waterfallId: String? = null,
)

View File

@ -1,217 +0,0 @@
package awais.instagrabber.models;
import awais.instagrabber.models.enums.MediaItemType;
public class UploadVideoOptions {
private final String uploadId;
private final String name;
private final long byteLength;
private final long duration;
private final int width;
private final int height;
private final boolean isSideCar;
private final boolean forAlbum; // Stories
private final boolean isDirect;
private final boolean isDirectVoice;
private final boolean forDirectStory;
private final boolean isIgtvVideo;
private final String waterfallId;
private final long offset;
private final MediaItemType mediaType;
public static class Builder {
private String uploadId;
private String name;
private long byteLength;
private long duration;
private int width;
private int height;
private boolean isSideCar;
private boolean forAlbum; // Stories
private boolean isDirect;
private boolean isDirectVoice;
private boolean forDirectStory;
private boolean isIgtvVideo;
private String waterfallId;
private long offset;
private MediaItemType mediaType;
private Builder() {}
public Builder setUploadId(final String uploadId) {
this.uploadId = uploadId;
return this;
}
public Builder setName(final String name) {
this.name = name;
return this;
}
public Builder setByteLength(final long byteLength) {
this.byteLength = byteLength;
return this;
}
public Builder setDuration(final long duration) {
this.duration = duration;
return this;
}
public Builder setWidth(final int width) {
this.width = width;
return this;
}
public Builder setHeight(final int height) {
this.height = height;
return this;
}
public Builder setIsSideCar(final boolean isSideCar) {
this.isSideCar = isSideCar;
return this;
}
public Builder setForAlbum(final boolean forAlbum) {
this.forAlbum = forAlbum;
return this;
}
public Builder setIsDirect(final boolean isDirect) {
this.isDirect = isDirect;
return this;
}
public Builder setIsDirectVoice(final boolean isDirectVoice) {
this.isDirectVoice = isDirectVoice;
return this;
}
public Builder setForDirectStory(final boolean forDirectStory) {
this.forDirectStory = forDirectStory;
return this;
}
public Builder setIsIgtvVideo(final boolean isIgtvVideo) {
this.isIgtvVideo = isIgtvVideo;
return this;
}
public Builder setWaterfallId(final String waterfallId) {
this.waterfallId = waterfallId;
return this;
}
public Builder setOffset(final long offset) {
this.offset = offset;
return this;
}
public Builder setMediaType(final MediaItemType mediaType) {
this.mediaType = mediaType;
return this;
}
public UploadVideoOptions build() {
return new UploadVideoOptions(uploadId, name, byteLength, duration, width, height, isSideCar, forAlbum, isDirect, isDirectVoice,
forDirectStory, isIgtvVideo, waterfallId, offset, mediaType);
}
}
public static Builder builder() {
return new Builder();
}
private UploadVideoOptions(final String uploadId,
final String name,
final long byteLength,
final long duration,
final int width,
final int height,
final boolean isSideCar,
final boolean forAlbum,
final boolean isDirect,
final boolean isDirectVoice,
final boolean forDirectStory,
final boolean isIgtvVideo,
final String waterfallId,
final long offset,
final MediaItemType mediaType) {
this.uploadId = uploadId;
this.name = name;
this.byteLength = byteLength;
this.duration = duration;
this.width = width;
this.height = height;
this.isSideCar = isSideCar;
this.forAlbum = forAlbum;
this.isDirect = isDirect;
this.isDirectVoice = isDirectVoice;
this.forDirectStory = forDirectStory;
this.isIgtvVideo = isIgtvVideo;
this.waterfallId = waterfallId;
this.offset = offset;
this.mediaType = mediaType;
}
public String getUploadId() {
return uploadId;
}
public String getName() {
return name;
}
public long getByteLength() {
return byteLength;
}
public long getDuration() {
return duration;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public boolean isSideCar() {
return isSideCar;
}
public boolean isForAlbum() {
return forAlbum;
}
public boolean isDirect() {
return isDirect;
}
public boolean isDirectVoice() {
return isDirectVoice;
}
public boolean isForDirectStory() {
return forDirectStory;
}
public boolean isIgtvVideo() {
return isIgtvVideo;
}
public String getWaterfallId() {
return waterfallId;
}
public long getOffset() {
return offset;
}
public MediaItemType getMediaType() {
return mediaType;
}
}

View File

@ -0,0 +1,22 @@
package awais.instagrabber.models
import awais.instagrabber.models.enums.MediaItemType
data class UploadVideoOptions(
val uploadId: String,
val name: String,
val byteLength: Long = 0,
val duration: Long = 0,
val width: Int = 0,
val height: Int = 0,
val isSideCar: Boolean = false,
// Stories
val isForAlbum: Boolean = false,
val isDirect: Boolean = false,
val isDirectVoice: Boolean = false,
val isForDirectStory: Boolean = false,
val isIgtvVideo: Boolean = false,
val waterfallId: String? = null,
val offset: Long = 0,
val mediaType: MediaItemType? = null,
)

View File

@ -1,106 +0,0 @@
/*
* 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 MainThreadExecutor mainThread;
private final ListeningExecutorService tasksThread;
private AppExecutors(Executor diskIO,
Executor networkIO,
MainThreadExecutor 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 MainThreadExecutor mainThread() {
return mainThread;
}
public static class MainThreadExecutor implements Executor {
private final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
@Override
public void execute(@NonNull Runnable command) {
mainThreadHandler.post(command);
}
public void execute(final Runnable command, final int delay) {
mainThreadHandler.postDelayed(command, delay);
}
public void cancel(@NonNull Runnable command) {
mainThreadHandler.removeCallbacks(command);
}
}
}

View File

@ -0,0 +1,52 @@
/*
* 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 com.google.common.util.concurrent.ListeningExecutorService
import com.google.common.util.concurrent.MoreExecutors
import java.util.concurrent.Executor
import java.util.concurrent.Executors
/**
* Global executor pools for the whole application.
*
*
* Grouping tasks like this avoids the effects of task starvation (e.g. disk reads don't wait behind
* webservice requests).
*/
object AppExecutors {
val diskIO: Executor = Executors.newSingleThreadExecutor()
val networkIO: Executor = Executors.newFixedThreadPool(3)
val mainThread: MainThreadExecutor = MainThreadExecutor()
val tasksThread: ListeningExecutorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10))
class MainThreadExecutor : Executor {
private val mainThreadHandler = Handler(Looper.getMainLooper())
override fun execute(command: Runnable) {
mainThreadHandler.post(command)
}
fun execute(command: Runnable?, delay: Int) {
mainThreadHandler.postDelayed(command!!, delay.toLong())
}
fun cancel(command: Runnable) {
mainThreadHandler.removeCallbacks(command)
}
}
}

View File

@ -28,7 +28,7 @@ import java.util.concurrent.Executors;
public final class BitmapUtils {
private static final String TAG = BitmapUtils.class.getSimpleName();
private static final LruCache<String, Bitmap> bitmapMemoryCache;
private static final AppExecutors appExecutors = AppExecutors.getInstance();
private static final AppExecutors appExecutors = AppExecutors.INSTANCE;
private static final ExecutorService callbackHandlers = Executors
.newCachedThreadPool(r -> new Thread(r, "bm-load-callback-handler#" + NumberUtils.random(0, 100)));
public static final float THUMBNAIL_SIZE = 200f;
@ -128,7 +128,7 @@ public final class BitmapUtils {
final ThumbnailLoadCallback callback) {
if (contentResolver == null || uri == null || callback == null) return;
final ListenableFuture<BitmapResult> future = appExecutors
.tasksThread()
.getTasksThread()
.submit(() -> getBitmapResult(contentResolver, uri, reqWidth, reqHeight, maxDimenSize, addToCache));
Futures.addCallback(future, new FutureCallback<BitmapResult>() {
@Override
@ -149,11 +149,11 @@ public final class BitmapUtils {
@Nullable
public static BitmapResult getBitmapResult(final ContentResolver contentResolver,
final Uri uri,
final float reqWidth,
final float reqHeight,
final float maxDimenSize,
final boolean addToCache) {
final Uri uri,
final float reqWidth,
final float reqHeight,
final float maxDimenSize,
final boolean addToCache) {
BitmapFactory.Options bitmapOptions;
float actualReqWidth = reqWidth;
float actualReqHeight = reqHeight;

View File

@ -80,8 +80,6 @@ public final class ExportImportUtils {
throw e;
} catch (final Exception e) {
if (fetchListener != null) fetchListener.onResult(false);
// if (logCollector != null)
// logCollector.appendException(e, LogFile.UTILS_IMPORT, "Import::pass");
if (BuildConfig.DEBUG) Log.e(TAG, "Error importing backup", e);
}
} else if (configType == 'Z') {
@ -99,7 +97,6 @@ public final class ExportImportUtils {
throw e;
} catch (final Exception e) {
if (fetchListener != null) fetchListener.onResult(false);
// if (logCollector != null) logCollector.appendException(e, LogFile.UTILS_IMPORT, "Import");
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
}
}
@ -122,7 +119,6 @@ public final class ExportImportUtils {
if (fetchListener != null) fetchListener.onResult(true);
} catch (final Exception e) {
if (fetchListener != null) fetchListener.onResult(false);
// if (logCollector != null) logCollector.appendException(e, LogFile.UTILS_IMPORT, "importJson");
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
}
}
@ -250,8 +246,6 @@ public final class ExportImportUtils {
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 {
@ -264,8 +258,6 @@ public final class ExportImportUtils {
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);
@ -330,10 +322,10 @@ public final class ExportImportUtils {
Log.e(TAG, "onFailure: ", t);
callback.onCreated(null);
}
}, AppExecutors.getInstance().tasksThread());
}, AppExecutors.INSTANCE.getTasksThread());
return;
} catch (final Exception e) {
// if (logCollector != null) logCollector.appendException(e, LogFile.UTILS_EXPORT, "getExportString");
// if (logCollector != null) logCollector.appendException(e, LogFile.UTILS_EXPORT, "getExportString");
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
}
callback.onCreated(null);
@ -342,7 +334,7 @@ public final class ExportImportUtils {
@NonNull
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(() -> {
return AppExecutors.INSTANCE.getTasksThread().submit(() -> {
final Map<String, ?> allPrefs = sharedPreferences.getAll();
if (allPrefs == null) {
return new JSONObject();
@ -382,9 +374,6 @@ public final class ExportImportUtils {
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);
}
@ -418,9 +407,6 @@ public final class ExportImportUtils {
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);
}

View File

@ -32,14 +32,14 @@ public final class FlavorTown {
final boolean force) {
if (checking) return;
checking = true;
AppExecutors.getInstance().networkIO().execute(() -> {
AppExecutors.INSTANCE.getNetworkIO().execute(() -> {
final String onlineVersionName = UPDATE_CHECKER.getLatestVersion();
if (onlineVersionName == null) return;
final String onlineVersion = getVersion(onlineVersionName);
final String localVersion = getVersion(BuildConfig.VERSION_NAME);
if (Objects.equals(onlineVersion, localVersion)) {
if (force) {
AppExecutors.getInstance().mainThread().execute(() -> {
AppExecutors.INSTANCE.getMainThread().execute(() -> {
final Context applicationContext = context.getApplicationContext();
// Check if app was closed or crashed before reaching here
if (applicationContext == null) return;

View File

@ -60,7 +60,7 @@ public class MediaController {
public MediaController(final Context context, final OnLoadListener onLoadListener) {
this.context = context;
this.onLoadListener = onLoadListener;
appExecutors = AppExecutors.getInstance();
appExecutors = AppExecutors.INSTANCE;
}
public void load() {
@ -285,9 +285,9 @@ public class MediaController {
final AlbumEntry allVideosAlbumFinal,
int delay) {
if (broadcastPhotosRunnable != null) {
appExecutors.mainThread().cancel(broadcastPhotosRunnable);
appExecutors.getMainThread().cancel(broadcastPhotosRunnable);
}
appExecutors.mainThread().execute(broadcastPhotosRunnable = () -> {
appExecutors.getMainThread().execute(broadcastPhotosRunnable = () -> {
allMediaAlbums = mediaAlbumsSorted;
allPhotoAlbums = photoAlbumsSorted;
broadcastPhotosRunnable = null;

View File

@ -1,173 +0,0 @@
package awais.instagrabber.utils;
import androidx.annotation.NonNull;
import com.google.common.collect.ImmutableMap;
import org.json.JSONObject;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import awais.instagrabber.models.UploadPhotoOptions;
import awais.instagrabber.models.UploadVideoOptions;
import awais.instagrabber.models.enums.MediaItemType;
public final class MediaUploadHelper {
private static final long LOWER = 1000000000L;
private static final long UPPER = 9999999999L;
private static Map<String, String> createPhotoRuploadParams(final UploadPhotoOptions options) {
final String retryContextString = getRetryContextString();
final Map<String, String> params = new HashMap<>();
params.put("retry_context", retryContextString);
params.put("media_type", "1");
params.put("upload_id", options.getUploadId());
params.put("xsharing_user_ids", "[]");
final Map<String, String> imageCompression = new HashMap<>();
imageCompression.put("lib_name", "moz");
imageCompression.put("lib_version", "3.1.m");
imageCompression.put("quality", "80");
params.put("image_compression", new JSONObject(imageCompression).toString());
if (options.isSideCar()) {
params.put("is_sidecar", "1");
}
return params;
}
private static Map<String, String> createVideoRuploadParams(final UploadVideoOptions options) {
final String retryContextString = getRetryContextString();
final Map<String, String> ruploadParams = new HashMap<>();
ruploadParams.put("retry_context", retryContextString);
ruploadParams.put("media_type", "2");
ruploadParams.put("xsharing_user_ids", "[]");
ruploadParams.put("upload_id", options.getUploadId());
ruploadParams.put("upload_media_width", String.valueOf(options.getWidth()));
ruploadParams.put("upload_media_height", String.valueOf(options.getHeight()));
ruploadParams.put("upload_media_duration_ms", String.valueOf(options.getDuration()));
if (options.isSideCar()) {
ruploadParams.put("is_sidecar", "1");
}
if (options.isForAlbum()) {
ruploadParams.put("for_album", "1");
}
if (options.isDirect()) {
ruploadParams.put("direct_v2", "1");
}
if (options.isForDirectStory()) {
ruploadParams.put("for_direct_story", "1");
ruploadParams.put("content_tags", "");
}
if (options.isIgtvVideo()) {
ruploadParams.put("is_igtv_video", "1");
}
if (options.isDirectVoice()) {
ruploadParams.put("is_direct_voice", "1");
}
return ruploadParams;
}
@NonNull
public static String getRetryContextString() {
final Map<String, Integer> retryContext = new HashMap<>();
retryContext.put("num_step_auto_retry", 0);
retryContext.put("num_reupload", 0);
retryContext.put("num_step_manual_retry", 0);
return new JSONObject(retryContext).toString();
}
public static UploadPhotoOptions createUploadPhotoOptions(final long byteLength) {
final String uploadId = generateUploadId();
return UploadPhotoOptions.builder()
.setUploadId(uploadId)
.setName(generateName(uploadId))
.setByteLength(byteLength)
.build();
}
public static UploadVideoOptions createUploadDmVideoOptions(final long byteLength,
final long duration,
final int width,
final int height) {
final String uploadId = generateUploadId();
return UploadVideoOptions.builder()
.setUploadId(uploadId)
.setName(generateName(uploadId))
.setByteLength(byteLength)
.setDuration(duration)
.setWidth(width)
.setHeight(height)
.setIsDirect(true)
.setMediaType(MediaItemType.MEDIA_TYPE_VIDEO)
.build();
}
public static UploadVideoOptions createUploadDmVoiceOptions(final long byteLength,
final long duration) {
final String uploadId = generateUploadId();
return UploadVideoOptions.builder()
.setUploadId(uploadId)
.setName(generateName(uploadId))
.setDuration(duration)
.setIsDirectVoice(true)
.setByteLength(byteLength)
.setMediaType(MediaItemType.MEDIA_TYPE_VOICE)
.build();
}
@NonNull
public static String generateUploadId() {
return String.valueOf(new Date().getTime() / 1000);
}
@NonNull
public static String generateName(final String uploadId) {
final long random = NumberUtils.random(LOWER, UPPER + 1);
return String.format("%s_0_%s", uploadId, random);
}
@NonNull
public static Map<String, String> getUploadPhotoHeaders(@NonNull final UploadPhotoOptions options) {
final String waterfallId = TextUtils.isEmpty(options.getWaterfallId()) ? UUID.randomUUID().toString() : options.getWaterfallId();
final String contentLength = String.valueOf(options.getByteLength());
final Map<String, String> headers = new HashMap<>();
headers.put("X_FB_PHOTO_WATERFALL_ID", waterfallId);
headers.put("X-Entity-Type", "image/jpeg");
headers.put("Offset", "0");
headers.put("X-Instagram-Rupload-Params", new JSONObject(createPhotoRuploadParams(options)).toString());
headers.put("X-Entity-Name", options.getName());
headers.put("X-Entity-Length", contentLength);
headers.put("Content-Type", "application/octet-stream");
headers.put("Content-Length", contentLength);
headers.put("Accept-Encoding", "gzip");
return headers;
}
@NonNull
public static Map<String, String> getUploadVideoHeaders(@NonNull final UploadVideoOptions options) {
final Map<String, String> ruploadParams = createVideoRuploadParams(options);
final String waterfallId = TextUtils.isEmpty(options.getWaterfallId()) ? UUID.randomUUID().toString() : options.getWaterfallId();
final String contentLength = String.valueOf(options.getByteLength());
return ImmutableMap.<String, String>builder()
.putAll(getBaseUploadVideoHeaders(ruploadParams))
.put("X_FB_PHOTO_WATERFALL_ID", waterfallId)
.put("X-Entity-Type", "video/mp4")
.put("Offset", String.valueOf(options.getOffset() > 0 ? options.getOffset() : 0))
.put("X-Entity-Name", options.getName())
.put("X-Entity-Length", contentLength)
.put("Content-Type", "application/octet-stream")
.put("Content-Length", contentLength)
.build();
}
private static Map<String, String> getBaseUploadVideoHeaders(@NonNull final Map<String, String> ruploadParams) {
return ImmutableMap.of(
"X-IG-Connection-Type", "WIFI",
"X-IG-Capabilities", "3brTvwE=",
"Accept-Encoding", "gzip",
"X-Instagram-Rupload-Params", new JSONObject(ruploadParams).toString()
);
}
}

View File

@ -0,0 +1,154 @@
@file:JvmName("MediaUploadHelper")
package awais.instagrabber.utils
import awais.instagrabber.models.UploadPhotoOptions
import awais.instagrabber.models.UploadVideoOptions
import awais.instagrabber.models.enums.MediaItemType
import org.json.JSONObject
import java.util.*
private const val LOWER = 1000000000L
private const val UPPER = 9999999999L
private fun createPhotoRuploadParams(options: UploadPhotoOptions): Map<String, String> {
val imageCompression = mapOf(
"lib_name" to "moz",
"lib_version" to "3.1.m",
"quality" to "80",
)
return listOfNotNull(
"retry_context" to retryContextString,
"media_type" to "1",
"upload_id" to (options.uploadId ?: ""),
"xsharing_user_ids" to "[]",
"image_compression" to JSONObject(imageCompression).toString(),
if (options.isSideCar) "is_sidecar" to "1" else null,
).toMap()
}
private fun createVideoRuploadParams(options: UploadVideoOptions): Map<String, String> {
val retryContextString = retryContextString
return listOfNotNull(
"retry_context" to retryContextString,
"media_type" to "2",
"xsharing_user_ids" to "[]",
"upload_id" to options.uploadId,
"upload_media_width" to options.width.toString(),
"upload_media_height" to options.height.toString(),
"upload_media_duration_ms" to options.duration.toString(),
if (options.isSideCar) "is_sidecar" to "1" else null,
if (options.isForAlbum) "for_album" to "1" else null,
if (options.isDirect) "direct_v2" to "1" else null,
*(if (options.isForDirectStory) arrayOf(
"for_direct_story" to "1",
"content_tags" to ""
) else emptyArray()),
if (options.isIgtvVideo) "is_igtv_video" to "1" else null,
if (options.isDirectVoice) "is_direct_voice" to "1" else null,
).toMap()
}
val retryContextString: String
get() {
return JSONObject(
mapOf(
"num_step_auto_retry" to 0,
"num_reupload" to 0,
"num_step_manual_retry" to 0,
)
).toString()
}
fun createUploadPhotoOptions(byteLength: Long): UploadPhotoOptions {
val uploadId = generateUploadId()
return UploadPhotoOptions(
uploadId,
generateName(uploadId),
byteLength,
)
}
fun createUploadDmVideoOptions(
byteLength: Long,
duration: Long,
width: Int,
height: Int
): UploadVideoOptions {
val uploadId = generateUploadId()
return UploadVideoOptions(
uploadId,
generateName(uploadId),
byteLength,
duration,
width,
height,
true,
mediaType = MediaItemType.MEDIA_TYPE_VIDEO,
)
}
fun createUploadDmVoiceOptions(
byteLength: Long,
duration: Long
): UploadVideoOptions {
val uploadId = generateUploadId()
return UploadVideoOptions(
uploadId,
generateName(uploadId),
byteLength,
duration,
isDirectVoice = true,
mediaType = MediaItemType.MEDIA_TYPE_VOICE,
)
}
fun generateUploadId(): String {
return (Date().time / 1000).toString()
}
fun generateName(uploadId: String): String {
val random = NumberUtils.random(LOWER, UPPER + 1)
return "${uploadId}_0_$random"
}
fun getUploadPhotoHeaders(options: UploadPhotoOptions): Map<String, String> {
val waterfallId = options.waterfallId ?: UUID.randomUUID().toString()
val contentLength = options.byteLength.toString()
return mapOf(
"X_FB_PHOTO_WATERFALL_ID" to waterfallId,
"X-Entity-Type" to "image/jpeg",
"Offset" to "0",
"X-Instagram-Rupload-Params" to JSONObject(createPhotoRuploadParams(options)).toString(),
"X-Entity-Name" to options.name,
"X-Entity-Length" to contentLength,
"Content-Type" to "application/octet-stream",
"Content-Length" to contentLength,
"Accept-Encoding" to "gzip",
)
}
fun getUploadVideoHeaders(options: UploadVideoOptions): Map<String, String> {
val ruploadParams = createVideoRuploadParams(options)
val waterfallId = options.waterfallId ?: UUID.randomUUID().toString()
val contentLength = options.byteLength.toString()
return getBaseUploadVideoHeaders(ruploadParams) + mapOf(
"X_FB_PHOTO_WATERFALL_ID" to waterfallId,
"X-Entity-Type" to "video/mp4",
"Offset" to (if (options.offset > 0) options.offset else 0).toString(),
"X-Entity-Name" to options.name,
"X-Entity-Length" to contentLength,
"Content-Type" to "application/octet-stream",
"Content-Length" to contentLength,
)
}
private fun getBaseUploadVideoHeaders(ruploadParams: Map<String, String>): Map<String, String> {
return mapOf(
"X-IG-Connection-Type" to "WIFI",
"X-IG-Capabilities" to "3brTvwE=",
"Accept-Encoding" to "gzip",
"X-Instagram-Rupload-Params" to JSONObject(ruploadParams).toString()
)
}

View File

@ -33,7 +33,7 @@ import okio.Source;
public final class MediaUploader {
private static final String TAG = MediaUploader.class.getSimpleName();
private static final String HOST = "https://i.instagram.com";
private static final AppExecutors appExecutors = AppExecutors.getInstance();
private static final AppExecutors appExecutors = AppExecutors.INSTANCE;
public static void uploadPhoto(@NonNull final Uri uri,
@NonNull final ContentResolver contentResolver,
@ -57,7 +57,7 @@ public final class MediaUploader {
private static void uploadPhoto(@NonNull final Bitmap bitmap,
@NonNull final OnMediaUploadCompleteListener listener) {
appExecutors.tasksThread().submit(() -> {
appExecutors.getTasksThread().submit(() -> {
final File file;
final long byteLength;
try {
@ -70,7 +70,7 @@ public final class MediaUploader {
final UploadPhotoOptions options = MediaUploadHelper.createUploadPhotoOptions(byteLength);
final Map<String, String> headers = MediaUploadHelper.getUploadPhotoHeaders(options);
final String url = HOST + "/rupload_igphoto/" + options.getName() + "/";
appExecutors.networkIO().execute(() -> {
appExecutors.getNetworkIO().execute(() -> {
try (FileInputStream input = new FileInputStream(file)) {
upload(input, url, headers, listener);
} catch (IOException e) {
@ -87,10 +87,10 @@ public final class MediaUploader {
final ContentResolver contentResolver,
final UploadVideoOptions options,
final OnMediaUploadCompleteListener listener) {
appExecutors.tasksThread().submit(() -> {
appExecutors.getTasksThread().submit(() -> {
final Map<String, String> headers = MediaUploadHelper.getUploadVideoHeaders(options);
final String url = HOST + "/rupload_igvideo/" + options.getName() + "/";
appExecutors.networkIO().execute(() -> {
appExecutors.getNetworkIO().execute(() -> {
try (InputStream input = contentResolver.openInputStream(uri)) {
if (input == null) {
listener.onFailure(new RuntimeException("InputStream was null"));

View File

@ -25,7 +25,7 @@ public final class MediaUtils {
public static void getVideoInfo(@NonNull final ContentResolver contentResolver,
@NonNull final Uri uri,
@NonNull final OnInfoLoadListener<VideoInfo> listener) {
AppExecutors.getInstance().tasksThread().submit(() -> {
AppExecutors.INSTANCE.getTasksThread().submit(() -> {
try (Cursor cursor = MediaStore.Video.query(contentResolver, uri, PROJECTION_VIDEO)) {
if (cursor == null) {
if (listener != null) {
@ -63,7 +63,7 @@ public final class MediaUtils {
public static void getVoiceInfo(@NonNull final ContentResolver contentResolver,
@NonNull final Uri uri,
@NonNull final OnInfoLoadListener<VideoInfo> listener) {
AppExecutors.getInstance().tasksThread().submit(() -> {
AppExecutors.INSTANCE.getTasksThread().submit(() -> {
try (Cursor cursor = MediaStore.Video.query(contentResolver, uri, PROJECTION_AUDIO)) {
if (cursor == null) {
if (listener != null) {

View File

@ -23,7 +23,7 @@ public final class UpdateCheckCommon {
public static void showUpdateDialog(@NonNull final Context context,
@NonNull final String version,
@NonNull final DialogInterface.OnClickListener onDownloadClickListener) {
AppExecutors.getInstance().mainThread().execute(() -> {
AppExecutors.INSTANCE.getMainThread().execute(() -> {
new MaterialAlertDialogBuilder(context)
.setTitle(context.getString(R.string.update_available, version))
.setNeutralButton(R.string.skip_update, (dialog, which) -> {

View File

@ -135,7 +135,7 @@ public class ImageEditViewModel extends AndroidViewModel {
private void applyFilters() {
final GPUImage gpuImage = new GPUImage(getApplication());
if ((tuningFilters != null && !tuningFilters.isEmpty()) || appliedFilter != null) {
AppExecutors.getInstance().tasksThread().submit(() -> {
AppExecutors.INSTANCE.getTasksThread().submit(() -> {
final List<GPUImageFilter> list = new ArrayList<>();
if (tuningFilters != null) {
for (Filter<? extends GPUImageFilter> tuningFilter : tuningFilters) {

View File

@ -232,7 +232,7 @@ public class SearchFragmentViewModel extends AppStateViewModel {
topResults.postValue(Resource.success(Collections.emptyList()));
Log.e(TAG, "onFailure: ", t);
}
}, AppExecutors.getInstance().mainThread());
}, AppExecutors.INSTANCE.getMainThread());
}
private void sendErrorResponse(@NonNull final FavoriteType type) {

View File

@ -108,9 +108,9 @@ public class IgErrorsInterceptor implements Interceptor {
// final View view = mainActivity.getRootView();
// if (view == null) return;
try {
AppExecutors.getInstance()
.mainThread()
.execute(() -> Toast.makeText(mainActivity.getApplicationContext(), message, Toast.LENGTH_LONG).show());
AppExecutors.INSTANCE
.getMainThread()
.execute(() -> Toast.makeText(mainActivity.getApplicationContext(), message, Toast.LENGTH_LONG).show());
} catch (Exception e) {
Log.e(TAG, "showSnackbar: ", e);
}

View File

@ -1,4 +1,6 @@
buildscript {
ext.kotlin_version = '1.5.0'
repositories {
google()
mavenCentral()
@ -6,6 +8,7 @@ buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:4.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
def nav_version = "2.3.5"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
}