mirror of
https://github.com/KokaKiwi/BarInsta
synced 2024-12-23 05:16:58 +00:00
Add Kotlin and convert some model classes to kotlin
This commit is contained in:
parent
e726ba3ccf
commit
84e93431ae
@ -1,4 +1,5 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: "androidx.navigation.safeargs"
|
||||
apply from: 'sentry.gradle'
|
||||
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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)));
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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 +
|
||||
'}';
|
||||
}
|
||||
}
|
69
app/src/main/java/awais/instagrabber/models/Comment.kt
Normal file
69
app/src/main/java/awais/instagrabber/models/Comment.kt
Normal 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)"
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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))
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
49
app/src/main/java/awais/instagrabber/models/FollowModel.kt
Normal file
49
app/src/main/java/awais/instagrabber/models/FollowModel.kt
Normal 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
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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))
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package awais.instagrabber.models
|
||||
|
||||
import awais.instagrabber.models.enums.IntentModelType
|
||||
|
||||
data class IntentModel(val type: IntentModelType, val text: String)
|
@ -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
|
||||
}
|
||||
}
|
36
app/src/main/java/awais/instagrabber/models/Resource.kt
Normal file
36
app/src/main/java/awais/instagrabber/models/Resource.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
@ -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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
36
app/src/main/java/awais/instagrabber/models/Tab.kt
Normal file
36
app/src/main/java/awais/instagrabber/models/Tab.kt
Normal 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,
|
||||
)
|
@ -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;
|
||||
}
|
||||
}
|
@ -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,
|
||||
)
|
@ -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;
|
||||
}
|
||||
}
|
@ -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,
|
||||
)
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
52
app/src/main/java/awais/instagrabber/utils/AppExecutors.kt
Normal file
52
app/src/main/java/awais/instagrabber/utils/AppExecutors.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
154
app/src/main/java/awais/instagrabber/utils/MediaUploadHelper.kt
Normal file
154
app/src/main/java/awais/instagrabber/utils/MediaUploadHelper.kt
Normal 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()
|
||||
)
|
||||
}
|
@ -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"));
|
||||
|
@ -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) {
|
||||
|
@ -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) -> {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user