diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d0847f2f..58a12069 100755
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -138,6 +138,8 @@
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
+
+
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/adapters/DiscoverAdapter.java b/app/src/main/java/awais/instagrabber/adapters/DiscoverAdapter.java
deleted file mode 100755
index 5a1af57e..00000000
--- a/app/src/main/java/awais/instagrabber/adapters/DiscoverAdapter.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package awais.instagrabber.adapters;
-
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.DiffUtil;
-
-import awais.instagrabber.R;
-import awais.instagrabber.adapters.viewholder.DiscoverViewHolder;
-import awais.instagrabber.models.DiscoverItemModel;
-import awais.instagrabber.models.enums.MediaItemType;
-
-public final class DiscoverAdapter extends MultiSelectListAdapter {
-
- private static final DiffUtil.ItemCallback diffCallback = new DiffUtil.ItemCallback() {
- @Override
- public boolean areItemsTheSame(@NonNull final DiscoverItemModel oldItem, @NonNull final DiscoverItemModel newItem) {
- return oldItem.getPostId().equals(newItem.getPostId());
- }
-
- @Override
- public boolean areContentsTheSame(@NonNull final DiscoverItemModel oldItem, @NonNull final DiscoverItemModel newItem) {
- return oldItem.getPostId().equals(newItem.getPostId());
- }
- };
-
- public DiscoverAdapter(final OnItemClickListener clickListener,
- final OnItemLongClickListener longClickListener) {
- super(diffCallback, clickListener, longClickListener);
- }
-
- @NonNull
- @Override
- public DiscoverViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
- final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
- return new DiscoverViewHolder(layoutInflater.inflate(R.layout.item_post, parent, false));
- }
-
- @Override
- public void onBindViewHolder(@NonNull final DiscoverViewHolder holder, final int position) {
- final DiscoverItemModel itemModel = getItem(position);
- if (itemModel != null) {
- itemModel.setPosition(position);
- holder.itemView.setTag(itemModel);
- holder.itemView.setOnClickListener(v -> getInternalOnItemClickListener().onItemClick(itemModel, position));
- holder.itemView.setOnLongClickListener(v -> getInternalOnLongItemClickListener().onItemLongClick(itemModel, position));
- final MediaItemType mediaType = itemModel.getItemType();
- holder.typeIcon.setVisibility(
- mediaType == MediaItemType.MEDIA_TYPE_VIDEO || mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE);
- holder.typeIcon.setImageResource(mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? R.drawable.ic_slider_24 : R.drawable.ic_video_24);
- holder.selectedView.setVisibility(itemModel.isSelected() ? View.VISIBLE : View.GONE);
- holder.postImage.setImageURI(itemModel.getDisplayUrl());
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/adapters/FeedAdapterV2.java b/app/src/main/java/awais/instagrabber/adapters/FeedAdapterV2.java
index 30baeda0..0d17f17a 100644
--- a/app/src/main/java/awais/instagrabber/adapters/FeedAdapterV2.java
+++ b/app/src/main/java/awais/instagrabber/adapters/FeedAdapterV2.java
@@ -10,6 +10,9 @@ import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView;
+import java.util.HashSet;
+import java.util.Set;
+
import awais.instagrabber.adapters.viewholder.FeedGridItemViewHolder;
import awais.instagrabber.adapters.viewholder.feed.FeedItemViewHolder;
import awais.instagrabber.adapters.viewholder.feed.FeedPhotoViewHolder;
@@ -26,15 +29,13 @@ import awais.instagrabber.models.enums.MediaItemType;
public final class FeedAdapterV2 extends ListAdapter {
private static final String TAG = "FeedAdapterV2";
- private PostsLayoutPreferences layoutPreferences;
private final FeedItemCallback feedItemCallback;
+ private final SelectionModeCallback selectionModeCallback;
+ private final Set selectedPositions = new HashSet<>();
+ private final Set selectedFeedModels = new HashSet<>();
- // private final View.OnLongClickListener longClickListener = v -> {
- // final Object tag;
- // if (v instanceof RamboTextView && (tag = v.getTag()) instanceof FeedModel)
- // Utils.copyText(v.getContext(), ((FeedModel) tag).getPostCaption());
- // return true;
- // };
+ private PostsLayoutPreferences layoutPreferences;
+ private boolean selectionModeActive = false;
private static final DiffUtil.ItemCallback DIFF_CALLBACK = new DiffUtil.ItemCallback() {
@@ -48,12 +49,56 @@ public final class FeedAdapterV2 extends ListAdapter selectedFeedModels);
+
+ void onSelectionEnd();
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/FeedGridItemViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/FeedGridItemViewHolder.java
index 0f26bb86..db0545ac 100644
--- a/app/src/main/java/awais/instagrabber/adapters/viewholder/FeedGridItemViewHolder.java
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/FeedGridItemViewHolder.java
@@ -31,16 +31,29 @@ public class FeedGridItemViewHolder extends RecyclerView.ViewHolder {
public FeedGridItemViewHolder(@NonNull final ItemFeedGridBinding binding) {
super(binding.getRoot());
this.binding = binding;
- // for rounded borders (clip view to background shape)
- //
}
- public void bind(@NonNull final FeedModel feedModel,
+ public void bind(final int position,
+ @NonNull final FeedModel feedModel,
@NonNull final PostsLayoutPreferences layoutPreferences,
- final FeedAdapterV2.FeedItemCallback feedItemCallback) {
- if (feedItemCallback != null) {
- itemView.setOnClickListener(v -> feedItemCallback.onPostClick(feedModel, binding.profilePic, binding.postImage));
+ final FeedAdapterV2.FeedItemCallback feedItemCallback,
+ final FeedAdapterV2.AdapterSelectionCallback adapterSelectionCallback,
+ final boolean selectionModeActive,
+ final boolean selected) {
+ itemView.setOnClickListener(v -> {
+ if (!selectionModeActive && feedItemCallback != null) {
+ feedItemCallback.onPostClick(feedModel, binding.profilePic, binding.postImage);
+ return;
+ }
+ if (selectionModeActive && adapterSelectionCallback != null) {
+ adapterSelectionCallback.onPostClick(position, feedModel);
+ }
+ });
+ if (adapterSelectionCallback != null) {
+ itemView.setOnLongClickListener(v -> adapterSelectionCallback.onPostLongClick(position, feedModel));
}
+ binding.selectedView.setVisibility(selected ? View.VISIBLE : View.GONE);
+ // for rounded borders (clip view to background shape)
itemView.setClipToOutline(layoutPreferences.getHasRoundedCorners());
if (layoutPreferences.getType() == STAGGERED_GRID) {
final float aspectRatio = (float) feedModel.getImageWidth() / feedModel.getImageHeight();
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/PostMediaViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/PostMediaViewHolder.java
index 8d247dcf..d2d265a8 100755
--- a/app/src/main/java/awais/instagrabber/adapters/viewholder/PostMediaViewHolder.java
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/PostMediaViewHolder.java
@@ -19,7 +19,7 @@ public final class PostMediaViewHolder extends RecyclerView.ViewHolder {
public void bind(final ViewerPostModel model, final int position, final View.OnClickListener clickListener) {
if (model == null) return;
- model.setPosition(position);
+ // model.setPosition(position);
itemView.setTag(model);
itemView.setOnClickListener(clickListener);
binding.selectedView.setVisibility(model.isCurrentSlide() ? View.VISIBLE : View.GONE);
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/PostViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/PostViewHolder.java
index 39c1d61a..5900ebc1 100755
--- a/app/src/main/java/awais/instagrabber/adapters/viewholder/PostViewHolder.java
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/PostViewHolder.java
@@ -25,7 +25,7 @@ public final class PostViewHolder extends RecyclerView.ViewHolder {
final OnItemClickListener clickListener,
final OnItemLongClickListener longClickListener) {
if (postModel == null) return;
- postModel.setPosition(position);
+ // postModel.setPosition(position);
itemView.setOnClickListener(v -> clickListener.onItemClick(postModel, position));
itemView.setOnLongClickListener(v -> longClickListener.onItemLongClick(postModel, position));
diff --git a/app/src/main/java/awais/instagrabber/asyncs/DiscoverFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/DiscoverFetcher.java
deleted file mode 100755
index f86958aa..00000000
--- a/app/src/main/java/awais/instagrabber/asyncs/DiscoverFetcher.java
+++ /dev/null
@@ -1,202 +0,0 @@
-package awais.instagrabber.asyncs;
-
-import android.os.AsyncTask;
-import android.os.Environment;
-import android.util.Log;
-import android.util.Pair;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import org.json.JSONArray;
-import org.json.JSONObject;
-
-import java.io.File;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.ArrayList;
-
-import awais.instagrabber.BuildConfig;
-import awais.instagrabber.interfaces.FetchListener;
-import awais.instagrabber.models.DiscoverItemModel;
-import awais.instagrabber.models.enums.MediaItemType;
-import awais.instagrabber.utils.Constants;
-import awais.instagrabber.utils.DownloadUtils;
-import awais.instagrabber.utils.NetworkUtils;
-import awais.instagrabber.utils.ResponseBodyUtils;
-import awais.instagrabber.utils.TextUtils;
-import awais.instagrabber.utils.Utils;
-import awaisomereport.LogCollector;
-
-import static awais.instagrabber.utils.Constants.DOWNLOAD_USER_FOLDER;
-import static awais.instagrabber.utils.Constants.FOLDER_PATH;
-import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
-import static awais.instagrabber.utils.Utils.logCollector;
-import static awais.instagrabber.utils.Utils.settingsHelper;
-
-public final class DiscoverFetcher extends AsyncTask {
- private final String cluster, maxId, rankToken;
- private final FetchListener fetchListener;
- private int lastId = 0;
- private boolean isFirst, moreAvailable;
- private String nextMaxId;
-
- public DiscoverFetcher(final String cluster, final String maxId, final String rankToken,
- final FetchListener fetchListener, final boolean isFirst) {
- this.cluster = cluster == null ? "explore_all%3A0" : cluster.replace(":", "%3A");
- this.maxId = maxId == null ? "" : "&max_id=" + maxId;
- this.rankToken = rankToken;
- this.fetchListener = fetchListener;
- this.isFirst = isFirst;
- }
-
- @Nullable
- @Override
- protected final DiscoverItemModel[] doInBackground(final Void... voids) {
-
- DiscoverItemModel[] result = null;
-
- final ArrayList discoverItemModels = fetchItems(null, maxId);
- if (discoverItemModels != null) {
- result = discoverItemModels.toArray(new DiscoverItemModel[0]);
- if (result.length > 0) {
- final DiscoverItemModel lastModel = result[result.length - 1];
- if (lastModel != null && nextMaxId != null) lastModel.setMore(moreAvailable, nextMaxId);
- }
- }
-
- return result;
- }
-
- private ArrayList fetchItems(ArrayList discoverItemModels, final String maxId) {
- try {
- final String url = "https://www.instagram.com/explore/grid/?is_prefetch=false&omit_cover_media=true&module=explore_popular" +
- "&use_sectional_payload=false&cluster_id=" + cluster + "&include_fixed_destinations=true&session_id=" + rankToken + maxId;
-
- final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
-
- urlConnection.setUseCaches(false);
- urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
-
- if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
- final JSONObject discoverResponse = new JSONObject(NetworkUtils.readFromConnection(urlConnection));
-
- moreAvailable = discoverResponse.getBoolean("more_available");
- nextMaxId = discoverResponse.optString("next_max_id");
-
- final JSONArray sectionalItems = discoverResponse.getJSONArray("sectional_items");
- if (discoverItemModels == null) discoverItemModels = new ArrayList<>(sectionalItems.length() * 2);
-
- for (int i = 0; i < sectionalItems.length(); ++i) {
- final JSONObject sectionItem = sectionalItems.getJSONObject(i);
-
- final String feedType = sectionItem.getString("feed_type");
- final String layoutType = sectionItem.getString("layout_type");
-
- if (sectionItem.has("layout_content") && feedType.equals("media")) {
- final JSONObject layoutContent = sectionItem.getJSONObject("layout_content");
-
- if ("media_grid".equals(layoutType)) {
- final JSONArray medias = layoutContent.getJSONArray("medias");
- for (int j = 0; j < medias.length(); ++j)
- discoverItemModels.add(makeDiscoverModel(medias.getJSONObject(j).getJSONObject("media")));
-
- } else {
- final boolean isOneSide = "one_by_two_left".equals(layoutType);
- if (isOneSide || "two_by_two_right".equals(layoutType)) {
-
- final JSONObject layoutItem = layoutContent.getJSONObject(isOneSide ? "one_by_two_item" : "two_by_two_item");
- if (layoutItem.has("media"))
- discoverItemModels.add(makeDiscoverModel(layoutItem.getJSONObject("media")));
-
- if (layoutContent.has("fill_items")) {
- final JSONArray fillItems = layoutContent.getJSONArray("fill_items");
- for (int j = 0; j < fillItems.length(); ++j)
- discoverItemModels.add(makeDiscoverModel(fillItems.getJSONObject(j).getJSONObject("media")));
- }
- }
- }
- }
- }
-
- discoverItemModels.trimToSize();
- urlConnection.disconnect();
-
- // hack to fetch 50+ items
- if (this.isFirst) {
- final int size = discoverItemModels.size();
- if (size > 50) this.isFirst = false;
- discoverItemModels = fetchItems(discoverItemModels, "&max_id=" + (lastId++));
- }
- } else {
- urlConnection.disconnect();
- }
- } catch (final Exception e) {
- if (logCollector != null)
- logCollector.appendException(e, LogCollector.LogFile.ASYNC_DISCOVER_FETCHER, "fetchItems",
- new Pair<>("maxId", maxId),
- new Pair<>("lastId", lastId),
- new Pair<>("isFirst", isFirst),
- new Pair<>("nextMaxId", nextMaxId));
- if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
- }
-
- return discoverItemModels;
- }
-
- @NonNull
- private DiscoverItemModel makeDiscoverModel(@NonNull final JSONObject media) throws Exception {
- final JSONObject user = media.getJSONObject(Constants.EXTRAS_USER);
- final String username = user.getString(Constants.EXTRAS_USERNAME);
- // final ProfileModel userProfileModel = new ProfileModel(user.getBoolean("is_private"),
- // user.getBoolean("is_verified"),
- // String.valueOf(user.get("pk")),
- // username,
- // user.getString("full_name"),
- // null,
- // user.getString("profile_pic_url"), null,
- // 0, 0, 0);
-
- // final String comment;
- // if (!media.has("caption")) comment = null;
- // else {
- // final Object caption = media.get("caption");
- // comment = caption instanceof JSONObject ? ((JSONObject) caption).getString("text") : null;
- // }
-
- final MediaItemType mediaType = ResponseBodyUtils.getMediaItemType(media.getInt("media_type"));
-
- final ResponseBodyUtils.ThumbnailDetails thumbnailUrl = ResponseBodyUtils.getThumbnailUrl(media, mediaType);
- final DiscoverItemModel model = new DiscoverItemModel(mediaType,
- media.getString("pk"),
- media.getString("code"),
- thumbnailUrl != null ? thumbnailUrl.url : null);
-
- final File downloadDir = new File(Environment.getExternalStorageDirectory(), "Download" +
- (Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER) ? ("/" + username) : ""));
-
- // to check if file exists
- File customDir = null;
- if (settingsHelper.getBoolean(FOLDER_SAVE_TO)) {
- final String customPath = settingsHelper.getString(FOLDER_PATH);
- if (!TextUtils.isEmpty(customPath)) customDir = new File(customPath +
- (Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER)
- ? "/" + username
- : ""));
- }
-
- DownloadUtils.checkExistence(downloadDir, customDir, mediaType == MediaItemType.MEDIA_TYPE_SLIDER, model);
-
- return model;
- }
-
- @Override
- protected void onPreExecute() {
- if (fetchListener != null) fetchListener.doBefore();
- }
-
- @Override
- protected void onPostExecute(final DiscoverItemModel[] discoverItemModels) {
- if (fetchListener != null) fetchListener.onResult(discoverItemModels);
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/asyncs/FeedFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/FeedFetcher.java
deleted file mode 100755
index 4ae5ad35..00000000
--- a/app/src/main/java/awais/instagrabber/asyncs/FeedFetcher.java
+++ /dev/null
@@ -1,270 +0,0 @@
-package awais.instagrabber.asyncs;
-
-import android.os.AsyncTask;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.List;
-
-import awais.instagrabber.BuildConfig;
-import awais.instagrabber.interfaces.FetchListener;
-import awais.instagrabber.models.FeedModel;
-import awais.instagrabber.models.PostChild;
-import awais.instagrabber.models.ProfileModel;
-import awais.instagrabber.models.enums.MediaItemType;
-import awais.instagrabber.utils.Constants;
-import awais.instagrabber.utils.NetworkUtils;
-import awais.instagrabber.utils.ResponseBodyUtils;
-import awais.instagrabber.utils.TextUtils;
-import awaisomereport.LogCollector;
-
-import static awais.instagrabber.utils.Utils.logCollector;
-
-public final class FeedFetcher extends AsyncTask> {
- private static final String TAG = "FeedFetcher";
-
- private static final int maxItemsToLoad = 25; // max is 50, but that's too many posts
- private final String endCursor;
- private final FetchListener> fetchListener;
-
- public FeedFetcher(final FetchListener> fetchListener) {
- this.endCursor = "";
- this.fetchListener = fetchListener;
- }
-
- public FeedFetcher(final String endCursor, final FetchListener> fetchListener) {
- this.endCursor = endCursor == null ? "" : endCursor;
- this.fetchListener = fetchListener;
- }
-
- @Override
- protected final List doInBackground(final Void... voids) {
- final List result = new ArrayList<>();
- HttpURLConnection urlConnection = null;
- try {
- //
- // stories: 04334405dbdef91f2c4e207b84c204d7 && https://i.instagram.com/api/v1/feed/reels_tray/
- // https://www.instagram.com/graphql/query/?query_hash=04334405dbdef91f2c4e207b84c204d7&variables={"only_stories":true,"stories_prefetch":false,"stories_video_dash_manifest":false}
- // ///////////////////////////////////////////////
- // feed:
- // https://www.instagram.com/graphql/query/?query_hash=6b838488258d7a4820e48d209ef79eb1&variables=
- // {"cached_feed_item_ids":[],"fetch_media_item_count":12,"fetch_media_item_cursor":"","fetch_comment_count":4,"fetch_like":3,"has_stories":false,"has_threaded_comments":true}
- // only used: fetch_media_item_cursor, fetch_media_item_count: 100 (max 50), has_threaded_comments = true
- // //////////////////////////////////////////////
- // more unknowns: https://github.com/qsniyg/rssit/blob/master/rssit/generators/instagram.py
- //
-
- final String url = "https://www.instagram.com/graphql/query/?query_hash=6b838488258d7a4820e48d209ef79eb1&variables=" +
- "{\"fetch_media_item_count\":" + maxItemsToLoad + ",\"has_threaded_comments\":true,\"fetch_media_item_cursor\":\"" + endCursor + "\"}";
- urlConnection = (HttpURLConnection) new URL(url).openConnection();
-
- if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
- final String json = NetworkUtils.readFromConnection(urlConnection);
- // Log.d(TAG, json);
- final JSONObject timelineFeed = new JSONObject(json).getJSONObject("data")
- .getJSONObject(Constants.EXTRAS_USER)
- .getJSONObject("edge_web_feed_timeline");
-
- final String endCursor;
- final boolean hasNextPage;
-
- final JSONObject pageInfo = timelineFeed.getJSONObject("page_info");
- if (pageInfo.has("has_next_page")) {
- hasNextPage = pageInfo.getBoolean("has_next_page");
- endCursor = hasNextPage ? pageInfo.getString("end_cursor") : null;
- } else {
- hasNextPage = false;
- endCursor = null;
- }
-
- final JSONArray feedItems = timelineFeed.getJSONArray("edges");
-
- for (int i = 0; i < feedItems.length(); ++i) {
- final JSONObject feedItem = feedItems.getJSONObject(i).getJSONObject("node");
- final String mediaType = feedItem.optString("__typename");
- if (mediaType.isEmpty() || "GraphSuggestedUserFeedUnit".equals(mediaType))
- continue;
-
- final boolean isVideo = feedItem.optBoolean("is_video");
- final long videoViews = feedItem.optLong("video_view_count", 0);
-
- final String displayUrl = feedItem.optString("display_url");
- if (TextUtils.isEmpty(displayUrl)) continue;
- final String resourceUrl;
-
- if (isVideo) {
- resourceUrl = feedItem.getString("video_url");
- } else {
- resourceUrl = feedItem.has("display_resources") ? ResponseBodyUtils.getHighQualityImage(feedItem) : displayUrl;
- }
-
- ProfileModel profileModel = null;
- if (feedItem.has("owner")) {
- final JSONObject owner = feedItem.getJSONObject("owner");
- profileModel = new ProfileModel(
- owner.optBoolean("is_private"),
- false, // if you can see it then you def follow
- owner.optBoolean("is_verified"),
- owner.getString(Constants.EXTRAS_ID),
- owner.getString(Constants.EXTRAS_USERNAME),
- owner.optString("full_name"),
- null,
- null,
- owner.getString("profile_pic_url"),
- null,
- 0,
- 0,
- 0,
- false,
- false,
- false,
- false);
- }
- JSONObject tempJsonObject = feedItem.optJSONObject("edge_media_preview_comment");
- final long commentsCount = tempJsonObject != null ? tempJsonObject.optLong("count") : 0;
- tempJsonObject = feedItem.optJSONObject("edge_media_to_caption");
- final JSONArray captions = tempJsonObject != null ? tempJsonObject.getJSONArray("edges") : null;
- String captionText = null;
- if (captions != null && captions.length() > 0) {
- if ((tempJsonObject = captions.optJSONObject(0)) != null &&
- (tempJsonObject = tempJsonObject.optJSONObject("node")) != null) {
- captionText = tempJsonObject.getString("text");
- }
- }
- final JSONObject location = feedItem.optJSONObject("location");
- // Log.d(TAG, "location: " + (location == null ? null : location.toString()));
- String locationId = null;
- String locationName = null;
- if (location != null) {
- locationName = location.optString("name");
- if (location.has("id")) {
- locationId = location.getString("id");
- } else if (location.has("pk")) {
- locationId = location.getString("pk");
- }
- // Log.d(TAG, "locationId: " + locationId);
- }
- int height = 0;
- int width = 0;
- final JSONObject dimensions = feedItem.optJSONObject("dimensions");
- if (dimensions != null) {
- height = dimensions.optInt("height");
- width = dimensions.optInt("width");
- }
- String thumbnailUrl = null;
- try {
- thumbnailUrl = feedItem.getJSONArray("display_resources")
- .getJSONObject(0)
- .getString("src");
- } catch (JSONException ignored) {}
- final FeedModel.Builder feedModelBuilder = new FeedModel.Builder()
- .setProfileModel(profileModel)
- .setItemType(isVideo ? MediaItemType.MEDIA_TYPE_VIDEO
- : MediaItemType.MEDIA_TYPE_IMAGE)
- .setViewCount(videoViews)
- .setPostId(feedItem.getString(Constants.EXTRAS_ID))
- .setDisplayUrl(resourceUrl)
- .setThumbnailUrl(thumbnailUrl != null ? thumbnailUrl : displayUrl)
- .setShortCode(feedItem.getString(Constants.EXTRAS_SHORTCODE))
- .setPostCaption(captionText)
- .setCommentsCount(commentsCount)
- .setTimestamp(feedItem.optLong("taken_at_timestamp", -1))
- .setLiked(feedItem.getBoolean("viewer_has_liked"))
- .setBookmarked(feedItem.getBoolean("viewer_has_saved"))
- .setLikesCount(feedItem.getJSONObject("edge_media_preview_like")
- .getLong("count"))
- .setLocationName(locationName)
- .setLocationId(locationId)
- .setImageHeight(height)
- .setImageWidth(width);
-
- final boolean isSlider = "GraphSidecar".equals(mediaType) && feedItem.has("edge_sidecar_to_children");
-
- if (isSlider) {
- feedModelBuilder.setItemType(MediaItemType.MEDIA_TYPE_SLIDER);
- final JSONObject sidecar = feedItem.optJSONObject("edge_sidecar_to_children");
- if (sidecar != null) {
- final JSONArray children = sidecar.optJSONArray("edges");
- if (children != null) {
- final List sliderItems = getSliderItems(children);
- feedModelBuilder.setSliderItems(sliderItems);
- }
- }
- }
- final FeedModel feedModel = feedModelBuilder.build();
- result.add(feedModel);
- }
- if (!result.isEmpty() && result.get(result.size() - 1) != null) {
- result.get(result.size() - 1).setPageCursor(hasNextPage, endCursor);
- }
- }
- } catch (final Exception e) {
- if (logCollector != null)
- logCollector.appendException(e, LogCollector.LogFile.ASYNC_FEED_FETCHER, "doInBackground");
- if (BuildConfig.DEBUG) {
- Log.e(TAG, "", e);
- }
- } finally {
- if (urlConnection != null) {
- urlConnection.disconnect();
- }
- }
-
- return result;
- }
-
- @NonNull
- private List getSliderItems(final JSONArray children) throws JSONException {
- final List sliderItems = new ArrayList<>();
- for (int j = 0; j < children.length(); ++j) {
- final JSONObject childNode = children.optJSONObject(j).getJSONObject("node");
- final boolean isChildVideo = childNode.optBoolean("is_video");
- int height = 0;
- int width = 0;
- final JSONObject dimensions = childNode.optJSONObject("dimensions");
- if (dimensions != null) {
- height = dimensions.optInt("height");
- width = dimensions.optInt("width");
- }
- String thumbnailUrl = null;
- try {
- thumbnailUrl = childNode.getJSONArray("display_resources")
- .getJSONObject(0)
- .getString("src");
- } catch (JSONException ignored) {}
- final PostChild sliderItem = new PostChild.Builder()
- .setItemType(isChildVideo ? MediaItemType.MEDIA_TYPE_VIDEO
- : MediaItemType.MEDIA_TYPE_IMAGE)
- .setPostId(childNode.getString(Constants.EXTRAS_ID))
- .setDisplayUrl(isChildVideo ? childNode.getString("video_url")
- : childNode.getString("display_url"))
- .setThumbnailUrl(thumbnailUrl != null ? thumbnailUrl
- : childNode.getString("display_url"))
- .setVideoViews(childNode.optLong("video_view_count", -1))
- .setHeight(height)
- .setWidth(width)
- .build();
- // Log.d(TAG, "getSliderItems: sliderItem: " + sliderItem);
- sliderItems.add(sliderItem);
- }
- return sliderItems;
- }
-
- @Override
- protected void onPreExecute() {
- if (fetchListener != null) fetchListener.doBefore();
- }
-
- @Override
- protected void onPostExecute(final List postModels) {
- if (fetchListener != null) fetchListener.onResult(postModels);
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/asyncs/PostsFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/PostsFetcher.java
deleted file mode 100755
index ad0d8710..00000000
--- a/app/src/main/java/awais/instagrabber/asyncs/PostsFetcher.java
+++ /dev/null
@@ -1,182 +0,0 @@
-package awais.instagrabber.asyncs;
-
-import android.os.AsyncTask;
-import android.os.Environment;
-import android.util.Log;
-
-import org.json.JSONArray;
-import org.json.JSONObject;
-
-import java.io.File;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.List;
-
-import awais.instagrabber.BuildConfig;
-import awais.instagrabber.interfaces.FetchListener;
-import awais.instagrabber.models.PostModel;
-import awais.instagrabber.models.enums.MediaItemType;
-import awais.instagrabber.models.enums.PostItemType;
-import awais.instagrabber.utils.Constants;
-import awais.instagrabber.utils.DownloadUtils;
-import awais.instagrabber.utils.NetworkUtils;
-import awais.instagrabber.utils.TextUtils;
-import awais.instagrabber.utils.Utils;
-import awaisomereport.LogCollector;
-
-import static awais.instagrabber.utils.Constants.DOWNLOAD_USER_FOLDER;
-import static awais.instagrabber.utils.Constants.FOLDER_PATH;
-import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
-import static awais.instagrabber.utils.Utils.logCollector;
-
-public final class PostsFetcher extends AsyncTask> {
- private static final String TAG = "PostsFetcher";
- private final PostItemType type;
- private final String endCursor;
- private final String id;
- private final FetchListener> fetchListener;
- private String username = null;
-
- public PostsFetcher(final String id,
- final PostItemType type,
- final String endCursor,
- final FetchListener> fetchListener) {
- this.id = id;
- this.type = type;
- this.endCursor = endCursor == null ? "" : endCursor;
- this.fetchListener = fetchListener;
- }
-
- public PostsFetcher setUsername(final String username) {
- this.username = username;
- return this;
- }
-
- @Override
- protected List doInBackground(final Void... voids) {
- // final boolean isHashTag = id.charAt(0) == '#';
- // final boolean isSaved = id.charAt(0) == '$';
- // final boolean isTagged = id.charAt(0) == '%';
- // final boolean isLocation = id.contains("/");
-
- final String url;
- switch (type) {
- case HASHTAG:
- url = "https://www.instagram.com/graphql/query/?query_hash=9b498c08113f1e09617a1703c22b2f32&variables=" +
- "{\"tag_name\":\"" + id.toLowerCase() + "\",\"first\":150,\"after\":\"" + endCursor + "\"}";
- break;
- case LOCATION:
- url = "https://www.instagram.com/graphql/query/?query_hash=36bd0f2bf5911908de389b8ceaa3be6d&variables=" +
- "{\"id\":\"" + id + "\",\"first\":150,\"after\":\"" + endCursor + "\"}";
- break;
- case SAVED:
- url = "https://www.instagram.com/graphql/query/?query_hash=8c86fed24fa03a8a2eea2a70a80c7b6b&variables=" +
- "{\"id\":\"" + id + "\",\"first\":150,\"after\":\"" + endCursor + "\"}";
- break;
- case TAGGED:
- url = "https://www.instagram.com/graphql/query/?query_hash=31fe64d9463cbbe58319dced405c6206&variables=" +
- "{\"id\":\"" + id + "\",\"first\":150,\"after\":\"" + endCursor + "\"}";
- break;
- default:
- url = "https://www.instagram.com/graphql/query/?query_hash=18a7b935ab438c4514b1f742d8fa07a7&variables=" +
- "{\"id\":\"" + id + "\",\"first\":150,\"after\":\"" + endCursor + "\"}";
- }
- List result = new ArrayList<>();
- try {
- final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
- conn.setUseCaches(false);
- conn.connect();
-
- if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
- // to check if file exists
- final File downloadDir = new File(Environment.getExternalStorageDirectory(), "Download" +
- (Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER) ? ("/" + username) : ""));
- File customDir = null;
- if (Utils.settingsHelper.getBoolean(FOLDER_SAVE_TO)) {
- final String customPath = Utils.settingsHelper.getString(FOLDER_PATH +
- (Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER)
- ? ("/" + username)
- : ""));
- if (!TextUtils.isEmpty(customPath)) customDir = new File(customPath);
- }
-
- final boolean isHashtag = type == PostItemType.HASHTAG;
- final boolean isLocation = type == PostItemType.LOCATION;
- final boolean isSaved = type == PostItemType.SAVED;
- final boolean isTagged = type == PostItemType.TAGGED;
- final JSONObject mediaPosts = new JSONObject(NetworkUtils.readFromConnection(conn))
- .getJSONObject("data")
- .getJSONObject(isHashtag
- ? Constants.EXTRAS_HASHTAG
- : (isLocation ? Constants.EXTRAS_LOCATION
- : Constants.EXTRAS_USER))
- .getJSONObject(isHashtag ? "edge_hashtag_to_media" :
- isLocation ? "edge_location_to_media" : isSaved ? "edge_saved_media"
- : isTagged ? "edge_user_to_photos_of_you"
- : "edge_owner_to_timeline_media");
-
- final String endCursor;
- final boolean hasNextPage;
-
- final JSONObject pageInfo = mediaPosts.getJSONObject("page_info");
- if (pageInfo.has("has_next_page")) {
- hasNextPage = pageInfo.getBoolean("has_next_page");
- endCursor = hasNextPage ? pageInfo.getString("end_cursor") : null;
- } else {
- hasNextPage = false;
- endCursor = null;
- }
-
- final JSONArray edges = mediaPosts.getJSONArray("edges");
- for (int i = 0; i < edges.length(); ++i) {
- final JSONObject mediaNode = edges.getJSONObject(i).getJSONObject("node");
- final JSONArray captions = mediaNode.getJSONObject("edge_media_to_caption").getJSONArray("edges");
-
- final boolean isSlider = mediaNode.has("__typename") && mediaNode.getString("__typename").equals("GraphSidecar");
- final boolean isVideo = mediaNode.getBoolean("is_video");
-
- final MediaItemType itemType;
- if (isSlider) itemType = MediaItemType.MEDIA_TYPE_SLIDER;
- else if (isVideo) itemType = MediaItemType.MEDIA_TYPE_VIDEO;
- else itemType = MediaItemType.MEDIA_TYPE_IMAGE;
-
- final PostModel model = new PostModel(
- itemType,
- mediaNode.getString(Constants.EXTRAS_ID),
- mediaNode.getString("display_url"),
- mediaNode.getString("thumbnail_src"),
- mediaNode.getString(Constants.EXTRAS_SHORTCODE),
- captions.length() > 0 ? captions.getJSONObject(0)
- .getJSONObject("node")
- .getString("text")
- : null,
- mediaNode.getLong("taken_at_timestamp"),
- mediaNode.optBoolean("viewer_has_liked"),
- mediaNode.optBoolean("viewer_has_saved")
- // , mediaNode.isNull("edge_liked_by") ? 0 : mediaNode.getJSONObject("edge_liked_by").getLong("count")
- );
- result.add(model);
- DownloadUtils.checkExistence(downloadDir, customDir, isSlider, model);
- }
-
- if (!result.isEmpty() && result.get(result.size() - 1) != null)
- result.get(result.size() - 1).setPageCursor(hasNextPage, endCursor);
- }
- conn.disconnect();
- } catch (Exception e) {
- if (logCollector != null) {
- logCollector.appendException(e, LogCollector.LogFile.ASYNC_MAIN_POSTS_FETCHER, "doInBackground");
- }
- if (BuildConfig.DEBUG) {
- Log.e(TAG, "Error fetching posts", e);
- }
- }
- return result;
- }
-
- @Override
- protected void onPostExecute(final List postModels) {
- if (fetchListener != null) fetchListener.onResult(postModels);
- }
-}
diff --git a/app/src/main/java/awais/instagrabber/customviews/PostsRecyclerView.java b/app/src/main/java/awais/instagrabber/customviews/PostsRecyclerView.java
index 113914ab..a3e909ce 100644
--- a/app/src/main/java/awais/instagrabber/customviews/PostsRecyclerView.java
+++ b/app/src/main/java/awais/instagrabber/customviews/PostsRecyclerView.java
@@ -45,6 +45,7 @@ public class PostsRecyclerView extends RecyclerView {
private RecyclerLazyLoaderAtBottom lazyLoader;
private FeedAdapterV2.FeedItemCallback feedItemCallback;
private boolean shouldScrollToTop;
+ private FeedAdapterV2.SelectionModeCallback selectionModeCallback;
private final List fetchStatusChangeListeners = new ArrayList<>();
@@ -112,6 +113,11 @@ public class PostsRecyclerView extends RecyclerView {
return this;
}
+ public PostsRecyclerView setSelectionModeCallback(@NonNull final FeedAdapterV2.SelectionModeCallback selectionModeCallback) {
+ this.selectionModeCallback = selectionModeCallback;
+ return this;
+ }
+
public PostsRecyclerView setLayoutPreferences(final PostsLayoutPreferences layoutPreferences) {
this.layoutPreferences = layoutPreferences;
if (initCalled) {
@@ -154,7 +160,7 @@ public class PostsRecyclerView extends RecyclerView {
}
private void initAdapter() {
- feedAdapter = new FeedAdapterV2(layoutPreferences, feedItemCallback);
+ feedAdapter = new FeedAdapterV2(layoutPreferences, feedItemCallback, selectionModeCallback);
feedAdapter.setStateRestorationPolicy(RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY);
setAdapter(feedAdapter);
}
@@ -241,6 +247,10 @@ public class PostsRecyclerView extends RecyclerView {
return layoutPreferences;
}
+ public void endSelection() {
+ feedAdapter.endSelection();
+ }
+
public interface FetchStatusChangeListener {
void onFetchStatusChange(boolean fetching);
}
diff --git a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java
index 1e81025d..05015e3a 100644
--- a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java
@@ -1,6 +1,7 @@
package awais.instagrabber.fragments;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.graphics.Typeface;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -19,6 +20,7 @@ import android.view.ViewGroup;
import android.widget.Toast;
import androidx.activity.OnBackPressedCallback;
+import androidx.activity.OnBackPressedDispatcher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
@@ -31,9 +33,11 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.google.android.material.snackbar.BaseTransientBottomBar;
import com.google.android.material.snackbar.Snackbar;
+import com.google.common.collect.ImmutableList;
import java.util.Date;
import java.util.List;
+import java.util.Set;
import awais.instagrabber.R;
import awais.instagrabber.activities.MainActivity;
@@ -68,6 +72,7 @@ import static awais.instagrabber.utils.Utils.settingsHelper;
public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
private static final String TAG = "HashTagFragment";
private static final int STORAGE_PERM_REQUEST_CODE = 8020;
+ private static final int STORAGE_PERM_REQUEST_CODE_FOR_SELECTION = 8030;
public static final String ARG_HASHTAG = "hashtag";
@@ -84,14 +89,13 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
private boolean isLoggedIn;
private TagsService tagsService;
private boolean storiesFetching;
+ private Set selectedFeedModels;
+ private FeedModel downloadFeedModel;
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
@Override
public void handleOnBackPressed() {
- setEnabled(false);
- remove();
- // if (postsAdapter == null) return;
- // postsAdapter.clearSelection();
+ binding.posts.endSelection();
}
};
private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(
@@ -99,55 +103,26 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
new PrimaryActionModeCallback.CallbacksHelper() {
@Override
public void onDestroy(final ActionMode mode) {
- onBackPressedCallback.handleOnBackPressed();
+ binding.posts.endSelection();
}
@Override
public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
if (item.getItemId() == R.id.action_download) {
- // if (postsAdapter == null || hashtag == null) {
- // return false;
- // }
- // final Context context = getContext();
- // if (context == null) return false;
- // DownloadUtils.batchDownload(context,
- // hashtag,
- // DownloadMethod.DOWNLOAD_MAIN,
- // postsAdapter.getSelectedModels());
- // checkAndResetAction();
+ if (HashTagFragment.this.selectedFeedModels == null) return false;
+ final Context context = getContext();
+ if (context == null) return false;
+ if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
+ DownloadUtils.download(context, ImmutableList.copyOf(HashTagFragment.this.selectedFeedModels));
+ binding.posts.endSelection();
+ return true;
+ }
+ requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE_FOR_SELECTION);
return true;
}
return false;
}
});
- // private final FetchListener> postsFetchListener = new FetchListener>() {
- // @Override
- // public void onResult(final List result) {
- // binding.swipeRefreshLayout.setRefreshing(false);
- // if (result == null) return;
- // binding.mainPosts.post(() -> binding.mainPosts.setVisibility(View.VISIBLE));
- // final List postModels = postsViewModel.getList().getValue();
- // List finalList = postModels == null || postModels.isEmpty()
- // ? new ArrayList<>()
- // : new ArrayList<>(postModels);
- // if (isPullToRefresh) {
- // finalList = result;
- // isPullToRefresh = false;
- // } else {
- // finalList.addAll(result);
- // }
- // finalList.addAll(result);
- // postsViewModel.getList().postValue(finalList);
- // PostModel model = null;
- // if (!result.isEmpty()) {
- // model = result.get(result.size() - 1);
- // }
- // if (model == null) return;
- // endCursor = model.getEndCursor();
- // hasNextPage = model.hasNextPage();
- // model.setPageCursor(false, null);
- // }
- // };
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
@Override
public void onPostClick(final FeedModel feedModel, final View profilePicView, final View mainPostImage) {
@@ -177,6 +152,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
showDownloadDialog(feedModel);
return;
}
+ downloadFeedModel = feedModel;
requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE);
}
@@ -233,6 +209,41 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
fragment.show(getChildFragmentManager(), "post_view");
}
};
+ private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() {
+
+ @Override
+ public void onSelectionStart() {
+ if (!onBackPressedCallback.isEnabled()) {
+ final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
+ onBackPressedCallback.setEnabled(true);
+ onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
+ }
+ if (actionMode == null) {
+ actionMode = fragmentActivity.startActionMode(multiSelectAction);
+ }
+ }
+
+ @Override
+ public void onSelectionChange(final Set selectedFeedModels) {
+ final String title = getString(R.string.number_selected, selectedFeedModels.size());
+ if (actionMode != null) {
+ actionMode.setTitle(title);
+ }
+ HashTagFragment.this.selectedFeedModels = selectedFeedModels;
+ }
+
+ @Override
+ public void onSelectionEnd() {
+ if (onBackPressedCallback.isEnabled()) {
+ onBackPressedCallback.setEnabled(false);
+ onBackPressedCallback.remove();
+ }
+ if (actionMode != null) {
+ actionMode.finish();
+ actionMode = null;
+ }
+ }
+ };
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
@@ -289,6 +300,24 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
return super.onOptionsItemSelected(item);
}
+ @Override
+ public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ final boolean granted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
+ if (requestCode == STORAGE_PERM_REQUEST_CODE && granted) {
+ if (downloadFeedModel == null) return;
+ showDownloadDialog(downloadFeedModel);
+ downloadFeedModel = null;
+ return;
+ }
+ if (requestCode == STORAGE_PERM_REQUEST_CODE_FOR_SELECTION && granted) {
+ final Context context = getContext();
+ if (context == null) return;
+ DownloadUtils.download(context, ImmutableList.copyOf(selectedFeedModels));
+ binding.posts.endSelection();
+ }
+ }
+
private void init() {
if (getArguments() == null) return;
final String cookie = settingsHelper.getString(Constants.COOKIE);
@@ -324,63 +353,9 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
.setLayoutPreferences(PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_HASHTAG_POSTS_LAYOUT)))
.addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
.setFeedItemCallback(feedItemCallback)
+ .setSelectionModeCallback(selectionModeCallback)
.init();
binding.swipeRefreshLayout.setRefreshing(true);
- // postsViewModel = new ViewModelProvider(this).get(PostsViewModel.class);
- // final Context context = getContext();
- // if (context == null) return;
- // final GridAutofitLayoutManager layoutManager = new GridAutofitLayoutManager(context, Utils.convertDpToPx(110));
- // binding.mainPosts.setLayoutManager(layoutManager);
- // binding.mainPosts.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(4)));
- // postsAdapter = new PostsAdapter((postModel, position) -> {
- // if (postsAdapter.isSelecting()) {
- // if (actionMode == null) return;
- // final String title = getString(R.string.number_selected, postsAdapter.getSelectedModels().size());
- // actionMode.setTitle(title);
- // return;
- // }
- // if (checkAndResetAction()) return;
- // final List postModels = postsViewModel.getList().getValue();
- // if (postModels == null || postModels.size() == 0) return;
- // if (postModels.get(0) == null) return;
- // final String postId = postModels.get(0).getPostId();
- // final boolean isId = postId != null && isLoggedIn;
- // final String[] idsOrShortCodes = new String[postModels.size()];
- // for (int i = 0; i < postModels.size(); i++) {
- // idsOrShortCodes[i] = isId ? postModels.get(i).getPostId()
- // : postModels.get(i).getShortCode();
- // }
- // final NavDirections action = HashTagFragmentDirections.actionGlobalPostViewFragment(
- // position,
- // idsOrShortCodes,
- // isId);
- // NavHostFragment.findNavController(this).navigate(action);
- //
- // }, (model, position) -> {
- // if (!postsAdapter.isSelecting()) {
- // checkAndResetAction();
- // return true;
- // }
- // if (onBackPressedCallback.isEnabled()) {
- // return true;
- // }
- // final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
- // onBackPressedCallback.setEnabled(true);
- // actionMode = fragmentActivity.startActionMode(multiSelectAction);
- // final String title = getString(R.string.number_selected, 1);
- // actionMode.setTitle(title);
- // onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
- // return true;
- // });
- // postsViewModel.getList().observe(fragmentActivity, postsAdapter::submitList);
- // binding.mainPosts.setAdapter(postsAdapter);
- // final RecyclerLazyLoader lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
- // if (!hasNextPage || getContext() == null) return;
- // binding.swipeRefreshLayout.setRefreshing(true);
- // fetchPosts();
- // endCursor = null;
- // });
- // binding.mainPosts.addOnScrollListener(lazyLoader);
}
private void setHashtagDetails() {
@@ -603,21 +578,6 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
navController.navigate(R.id.action_global_profileFragment, bundle);
}
- private boolean checkAndResetAction() {
- if (!onBackPressedCallback.isEnabled() && actionMode == null) {
- return false;
- }
- if (onBackPressedCallback.isEnabled()) {
- onBackPressedCallback.setEnabled(false);
- onBackPressedCallback.remove();
- }
- if (actionMode != null) {
- actionMode.finish();
- actionMode = null;
- }
- return true;
- }
-
private void showPostsLayoutPreferences() {
final PostsLayoutPreferencesDialogFragment fragment = new PostsLayoutPreferencesDialogFragment(
Constants.PREF_HASHTAG_POSTS_LAYOUT,
diff --git a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java
index a6a18370..f0ac0554 100644
--- a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java
@@ -2,6 +2,7 @@ package awais.instagrabber.fragments;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.AsyncTask;
@@ -22,6 +23,7 @@ import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.OnBackPressedCallback;
+import androidx.activity.OnBackPressedDispatcher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
@@ -34,9 +36,11 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.google.android.material.snackbar.BaseTransientBottomBar;
import com.google.android.material.snackbar.Snackbar;
+import com.google.common.collect.ImmutableList;
import java.util.Date;
import java.util.List;
+import java.util.Set;
import awais.instagrabber.R;
import awais.instagrabber.activities.MainActivity;
@@ -70,6 +74,7 @@ import static awais.instagrabber.utils.Utils.settingsHelper;
public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
private static final String TAG = "LocationFragment";
private static final int STORAGE_PERM_REQUEST_CODE = 8020;
+ private static final int STORAGE_PERM_REQUEST_CODE_FOR_SELECTION = 8030;
private MainActivity fragmentActivity;
private FragmentLocationBinding binding;
@@ -83,72 +88,39 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
private AsyncTask, ?, ?> currentlyExecuting;
private boolean isLoggedIn;
private boolean storiesFetching;
+ private Set selectedFeedModels;
+ private FeedModel downloadFeedModel;
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
@Override
public void handleOnBackPressed() {
- // if (postsAdapter == null) {
- // setEnabled(false);
- // remove();
- // return;
- // }
- // postsAdapter.clearSelection();
- setEnabled(false);
- remove();
+ binding.posts.endSelection();
}
};
private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(
R.menu.multi_select_download_menu, new PrimaryActionModeCallback.CallbacksHelper() {
@Override
public void onDestroy(final ActionMode mode) {
- onBackPressedCallback.handleOnBackPressed();
+ binding.posts.endSelection();
}
@Override
public boolean onActionItemClicked(final ActionMode mode,
final MenuItem item) {
if (item.getItemId() == R.id.action_download) {
- // if (postsAdapter == null || locationId == null) {
- // return false;
- // }
- // final Context context = getContext();
- // if (context == null) return false;
- // DownloadUtils.batchDownload(context,
- // locationId,
- // DownloadMethod.DOWNLOAD_MAIN,
- // postsAdapter.getSelectedModels());
- // checkAndResetAction();
- return true;
+ if (LocationFragment.this.selectedFeedModels == null) return false;
+ final Context context = getContext();
+ if (context == null) return false;
+ if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
+ DownloadUtils.download(context, ImmutableList.copyOf(LocationFragment.this.selectedFeedModels));
+ binding.posts.endSelection();
+ return true;
+ }
+ requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE_FOR_SELECTION);
}
return false;
}
});
- // private final FetchListener> postsFetchListener = new FetchListener>() {
- // @Override
- // public void onResult(final List result) {
- // binding.swipeRefreshLayout.setRefreshing(false);
- // if (result == null) return;
- // binding.mainPosts.post(() -> binding.mainPosts.setVisibility(View.VISIBLE));
- // final List postModels = postsViewModel.getList().getValue();
- // List finalList = postModels == null || postModels.isEmpty() ? new ArrayList<>()
- // : new ArrayList<>(postModels);
- // if (isPullToRefresh) {
- // finalList = result;
- // isPullToRefresh = false;
- // } else {
- // finalList.addAll(result);
- // }
- // postsViewModel.getList().postValue(finalList);
- // PostModel model = null;
- // if (!result.isEmpty()) {
- // model = result.get(result.size() - 1);
- // }
- // if (model == null) return;
- // endCursor = model.getEndCursor();
- // hasNextPage = model.hasNextPage();
- // model.setPageCursor(false, null);
- // }
- // };
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
@Override
public void onPostClick(final FeedModel feedModel, final View profilePicView, final View mainPostImage) {
@@ -234,6 +206,41 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
fragment.show(getChildFragmentManager(), "post_view");
}
};
+ private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() {
+
+ @Override
+ public void onSelectionStart() {
+ if (!onBackPressedCallback.isEnabled()) {
+ final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
+ onBackPressedCallback.setEnabled(true);
+ onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
+ }
+ if (actionMode == null) {
+ actionMode = fragmentActivity.startActionMode(multiSelectAction);
+ }
+ }
+
+ @Override
+ public void onSelectionChange(final Set selectedFeedModels) {
+ final String title = getString(R.string.number_selected, selectedFeedModels.size());
+ if (actionMode != null) {
+ actionMode.setTitle(title);
+ }
+ LocationFragment.this.selectedFeedModels = selectedFeedModels;
+ }
+
+ @Override
+ public void onSelectionEnd() {
+ if (onBackPressedCallback.isEnabled()) {
+ onBackPressedCallback.setEnabled(false);
+ onBackPressedCallback.remove();
+ }
+ if (actionMode != null) {
+ actionMode.finish();
+ actionMode = null;
+ }
+ }
+ };
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
@@ -291,13 +298,23 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
return super.onOptionsItemSelected(item);
}
- // @Override
- // public void onDestroy() {
- // super.onDestroy();
- // if (postsViewModel != null) {
- // postsViewModel.getList().postValue(Collections.emptyList());
- // }
- // }
+ @Override
+ public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ final boolean granted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
+ if (requestCode == STORAGE_PERM_REQUEST_CODE && granted) {
+ if (downloadFeedModel == null) return;
+ showDownloadDialog(downloadFeedModel);
+ downloadFeedModel = null;
+ return;
+ }
+ if (requestCode == STORAGE_PERM_REQUEST_CODE_FOR_SELECTION && granted) {
+ final Context context = getContext();
+ if (context == null) return;
+ DownloadUtils.download(context, ImmutableList.copyOf(selectedFeedModels));
+ binding.posts.endSelection();
+ }
+ }
private void init() {
if (getArguments() == null) return;
@@ -318,63 +335,9 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
.setLayoutPreferences(PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_LOCATION_POSTS_LAYOUT)))
.addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
.setFeedItemCallback(feedItemCallback)
+ .setSelectionModeCallback(selectionModeCallback)
.init();
binding.swipeRefreshLayout.setRefreshing(true);
- // postsViewModel = new ViewModelProvider(this).get(PostsViewModel.class);
- // final Context context = getContext();
- // if (context == null) return;
- // final GridAutofitLayoutManager layoutManager = new GridAutofitLayoutManager(context, Utils.convertDpToPx(110));
- // binding.mainPosts.setLayoutManager(layoutManager);
- // binding.mainPosts.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(4)));
- // postsAdapter = new PostsAdapter((postModel, position) -> {
- // if (postsAdapter.isSelecting()) {
- // if (actionMode == null) return;
- // final String title = getString(R.string.number_selected, postsAdapter.getSelectedModels().size());
- // actionMode.setTitle(title);
- // return;
- // }
- // if (checkAndResetAction()) return;
- // final List postModels = postsViewModel.getList().getValue();
- // if (postModels == null || postModels.size() == 0) return;
- // if (postModels.get(0) == null) return;
- // final String postId = postModels.get(0).getPostId();
- // final boolean isId = postId != null && isLoggedIn;
- // final String[] idsOrShortCodes = new String[postModels.size()];
- // for (int i = 0; i < postModels.size(); i++) {
- // idsOrShortCodes[i] = isId ? postModels.get(i).getPostId()
- // : postModels.get(i).getShortCode();
- // }
- // final NavDirections action = LocationFragmentDirections.actionGlobalPostViewFragment(
- // position,
- // idsOrShortCodes,
- // isId);
- // NavHostFragment.findNavController(this).navigate(action);
- // }, (model, position) -> {
- // if (!postsAdapter.isSelecting()) {
- // checkAndResetAction();
- // return true;
- // }
- // if (onBackPressedCallback.isEnabled()) {
- // return true;
- // }
- // final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity
- // .getOnBackPressedDispatcher();
- // onBackPressedCallback.setEnabled(true);
- // actionMode = fragmentActivity.startActionMode(multiSelectAction);
- // final String title = getString(R.string.number_selected, 1);
- // actionMode.setTitle(title);
- // onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
- // return true;
- // });
- // postsViewModel.getList().observe(fragmentActivity, postsAdapter::submitList);
- // binding.mainPosts.setAdapter(postsAdapter);
- // final RecyclerLazyLoader lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
- // if (!hasNextPage) return;
- // binding.swipeRefreshLayout.setRefreshing(true);
- // fetchPosts();
- // endCursor = null;
- // });
- // binding.mainPosts.addOnScrollListener(lazyLoader);
}
private void fetchLocationModel() {
@@ -519,12 +482,6 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
}
}
- // private void fetchPosts() {
- // stopCurrentExecutor();
- // currentlyExecuting = new PostsFetcher(locationModel.getId(), PostItemType.LOCATION, endCursor, postsFetchListener)
- // .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- // }
-
private void stopCurrentExecutor() {
if (currentlyExecuting != null) {
try {
@@ -597,21 +554,6 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
navController.navigate(R.id.action_global_profileFragment, bundle);
}
- private boolean checkAndResetAction() {
- if (!onBackPressedCallback.isEnabled() && actionMode == null) {
- return false;
- }
- if (onBackPressedCallback.isEnabled()) {
- onBackPressedCallback.setEnabled(false);
- onBackPressedCallback.remove();
- }
- if (actionMode != null) {
- actionMode.finish();
- actionMode = null;
- }
- return true;
- }
-
private void showPostsLayoutPreferences() {
final PostsLayoutPreferencesDialogFragment fragment = new PostsLayoutPreferencesDialogFragment(
Constants.PREF_LOCATION_POSTS_LAYOUT,
diff --git a/app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java
index 112e0fd4..f446cacb 100644
--- a/app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java
@@ -13,6 +13,7 @@ import android.view.View;
import android.view.ViewGroup;
import androidx.activity.OnBackPressedCallback;
+import androidx.activity.OnBackPressedDispatcher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
@@ -24,7 +25,9 @@ import androidx.navigation.NavDirections;
import androidx.navigation.fragment.NavHostFragment;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
-import java.util.ArrayList;
+import com.google.common.collect.ImmutableList;
+
+import java.util.Set;
import awais.instagrabber.R;
import awais.instagrabber.adapters.FeedAdapterV2;
@@ -34,7 +37,6 @@ import awais.instagrabber.databinding.FragmentSavedBinding;
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
import awais.instagrabber.fragments.main.ProfileFragmentDirections;
import awais.instagrabber.models.FeedModel;
-import awais.instagrabber.models.PostModel;
import awais.instagrabber.models.PostsLayoutPreferences;
import awais.instagrabber.models.enums.PostItemType;
import awais.instagrabber.utils.Constants;
@@ -47,6 +49,7 @@ import static awais.instagrabber.utils.Utils.settingsHelper;
public final class SavedViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
private static final int STORAGE_PERM_REQUEST_CODE = 8020;
+ private static final int STORAGE_PERM_REQUEST_CODE_FOR_SELECTION = 8030;
private FragmentSavedBinding binding;
private String username;
@@ -56,15 +59,13 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
private boolean shouldRefresh = true;
private PostItemType type;
private String profileId;
+ private Set selectedFeedModels;
+ private FeedModel downloadFeedModel;
- private final ArrayList selectedItems = new ArrayList<>();
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
@Override
public void handleOnBackPressed() {
- setEnabled(false);
- remove();
- // if (postsAdapter == null) return;
- // postsAdapter.clearSelection();
+ binding.posts.endSelection();
}
};
private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(
@@ -72,23 +73,21 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
new PrimaryActionModeCallback.CallbacksHelper() {
@Override
public void onDestroy(final ActionMode mode) {
- onBackPressedCallback.handleOnBackPressed();
+ binding.posts.endSelection();
}
@Override
public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
if (item.getItemId() == R.id.action_download) {
- // if (postsAdapter == null || username == null) {
- // return false;
- // }
- // final Context context = getContext();
- // if (context == null) return false;
- // DownloadUtils.batchDownload(context,
- // username,
- // DownloadMethod.DOWNLOAD_SAVED,
- // postsAdapter.getSelectedModels());
- // checkAndResetAction();
- return true;
+ if (SavedViewerFragment.this.selectedFeedModels == null) return false;
+ final Context context = getContext();
+ if (context == null) return false;
+ if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
+ DownloadUtils.download(context, ImmutableList.copyOf(SavedViewerFragment.this.selectedFeedModels));
+ binding.posts.endSelection();
+ return true;
+ }
+ requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE_FOR_SELECTION);
}
return false;
}
@@ -178,6 +177,41 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
fragment.show(getChildFragmentManager(), "post_view");
}
};
+ private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() {
+
+ @Override
+ public void onSelectionStart() {
+ if (!onBackPressedCallback.isEnabled()) {
+ final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
+ onBackPressedCallback.setEnabled(true);
+ onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
+ }
+ if (actionMode == null) {
+ actionMode = fragmentActivity.startActionMode(multiSelectAction);
+ }
+ }
+
+ @Override
+ public void onSelectionChange(final Set selectedFeedModels) {
+ final String title = getString(R.string.number_selected, selectedFeedModels.size());
+ if (actionMode != null) {
+ actionMode.setTitle(title);
+ }
+ SavedViewerFragment.this.selectedFeedModels = selectedFeedModels;
+ }
+
+ @Override
+ public void onSelectionEnd() {
+ if (onBackPressedCallback.isEnabled()) {
+ onBackPressedCallback.setEnabled(false);
+ onBackPressedCallback.remove();
+ }
+ if (actionMode != null) {
+ actionMode.finish();
+ actionMode = null;
+ }
+ }
+ };
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
@@ -286,6 +320,7 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
.setLayoutPreferences(PostsLayoutPreferences.fromJson(settingsHelper.getString(getPostsLayoutPreferenceKey())))
.addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
.setFeedItemCallback(feedItemCallback)
+ .setSelectionModeCallback(selectionModeCallback)
.init();
binding.swipeRefreshLayout.setRefreshing(true);
}
@@ -306,10 +341,18 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
@Override
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
- if (requestCode == 8020 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- // final Context context = getContext();
- // if (context == null) return;
- // DownloadUtils.batchDownload(context, null, DownloadMethod.DOWNLOAD_SAVED, selectedItems);
+ final boolean granted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
+ if (requestCode == STORAGE_PERM_REQUEST_CODE && granted) {
+ if (downloadFeedModel == null) return;
+ showDownloadDialog(downloadFeedModel);
+ downloadFeedModel = null;
+ return;
+ }
+ if (requestCode == STORAGE_PERM_REQUEST_CODE_FOR_SELECTION && granted) {
+ final Context context = getContext();
+ if (context == null) return;
+ DownloadUtils.download(context, ImmutableList.copyOf(selectedFeedModels));
+ binding.posts.endSelection();
}
}
@@ -392,19 +435,4 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
preferences -> new Handler().postDelayed(() -> binding.posts.setLayoutPreferences(preferences), 200));
fragment.show(getChildFragmentManager(), "posts_layout_preferences");
}
-
- private boolean checkAndResetAction() {
- if (!onBackPressedCallback.isEnabled() && actionMode == null) {
- return false;
- }
- if (onBackPressedCallback.isEnabled()) {
- onBackPressedCallback.setEnabled(false);
- onBackPressedCallback.remove();
- }
- if (actionMode != null) {
- actionMode.finish();
- actionMode = null;
- }
- return true;
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/fragments/TopicPostsFragment.java b/app/src/main/java/awais/instagrabber/fragments/TopicPostsFragment.java
index c7130647..92af3d4d 100644
--- a/app/src/main/java/awais/instagrabber/fragments/TopicPostsFragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/TopicPostsFragment.java
@@ -2,6 +2,7 @@ package awais.instagrabber.fragments;
import android.animation.ArgbEvaluator;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.Animatable;
@@ -9,6 +10,7 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
import android.os.Handler;
+import android.view.ActionMode;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -16,6 +18,8 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import androidx.activity.OnBackPressedCallback;
+import androidx.activity.OnBackPressedDispatcher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.PermissionChecker;
@@ -33,11 +37,15 @@ import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.controller.BaseControllerListener;
import com.facebook.drawee.interfaces.DraweeController;
import com.facebook.imagepipeline.image.ImageInfo;
+import com.google.common.collect.ImmutableList;
+
+import java.util.Set;
import awais.instagrabber.R;
import awais.instagrabber.activities.MainActivity;
import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.asyncs.DiscoverPostFetchService;
+import awais.instagrabber.customviews.PrimaryActionModeCallback;
import awais.instagrabber.customviews.helpers.NestedCoordinatorLayout;
import awais.instagrabber.databinding.FragmentTopicPostsBinding;
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
@@ -56,12 +64,47 @@ import static awais.instagrabber.utils.Utils.settingsHelper;
public class TopicPostsFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
private static final int STORAGE_PERM_REQUEST_CODE = 8020;
+ private static final int STORAGE_PERM_REQUEST_CODE_FOR_SELECTION = 8030;
+
private MainActivity fragmentActivity;
private FragmentTopicPostsBinding binding;
private NestedCoordinatorLayout root;
private boolean shouldRefresh = true;
private TopicCluster topicCluster;
+ private ActionMode actionMode;
+ private Set selectedFeedModels;
+ private FeedModel downloadFeedModel;
+ private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
+ @Override
+ public void handleOnBackPressed() {
+ binding.posts.endSelection();
+ }
+ };
+ private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(
+ R.menu.multi_select_download_menu, new PrimaryActionModeCallback.CallbacksHelper() {
+ @Override
+ public void onDestroy(final ActionMode mode) {
+ binding.posts.endSelection();
+ }
+
+ @Override
+ public boolean onActionItemClicked(final ActionMode mode,
+ final MenuItem item) {
+ if (item.getItemId() == R.id.action_download) {
+ if (TopicPostsFragment.this.selectedFeedModels == null) return false;
+ final Context context = getContext();
+ if (context == null) return false;
+ if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
+ DownloadUtils.download(context, ImmutableList.copyOf(TopicPostsFragment.this.selectedFeedModels));
+ binding.posts.endSelection();
+ return true;
+ }
+ requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE_FOR_SELECTION);
+ }
+ return false;
+ }
+ });
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
@Override
public void onPostClick(final FeedModel feedModel, final View profilePicView, final View mainPostImage) {
@@ -147,6 +190,41 @@ public class TopicPostsFragment extends Fragment implements SwipeRefreshLayout.O
fragment.show(getChildFragmentManager(), "post_view");
}
};
+ private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() {
+
+ @Override
+ public void onSelectionStart() {
+ if (!onBackPressedCallback.isEnabled()) {
+ final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
+ onBackPressedCallback.setEnabled(true);
+ onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
+ }
+ if (actionMode == null) {
+ actionMode = fragmentActivity.startActionMode(multiSelectAction);
+ }
+ }
+
+ @Override
+ public void onSelectionChange(final Set selectedFeedModels) {
+ final String title = getString(R.string.number_selected, selectedFeedModels.size());
+ if (actionMode != null) {
+ actionMode.setTitle(title);
+ }
+ TopicPostsFragment.this.selectedFeedModels = selectedFeedModels;
+ }
+
+ @Override
+ public void onSelectionEnd() {
+ if (onBackPressedCallback.isEnabled()) {
+ onBackPressedCallback.setEnabled(false);
+ onBackPressedCallback.remove();
+ }
+ if (actionMode != null) {
+ actionMode.finish();
+ actionMode = null;
+ }
+ }
+ };
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
@@ -220,6 +298,24 @@ public class TopicPostsFragment extends Fragment implements SwipeRefreshLayout.O
resetToolbar();
}
+ @Override
+ public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ final boolean granted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
+ if (requestCode == STORAGE_PERM_REQUEST_CODE && granted) {
+ if (downloadFeedModel == null) return;
+ showDownloadDialog(downloadFeedModel);
+ downloadFeedModel = null;
+ return;
+ }
+ if (requestCode == STORAGE_PERM_REQUEST_CODE_FOR_SELECTION && granted) {
+ final Context context = getContext();
+ if (context == null) return;
+ DownloadUtils.download(context, ImmutableList.copyOf(selectedFeedModels));
+ binding.posts.endSelection();
+ }
+ }
+
private void resetToolbar() {
fragmentActivity.resetToolbar();
}
@@ -303,6 +399,7 @@ public class TopicPostsFragment extends Fragment implements SwipeRefreshLayout.O
.setLayoutPreferences(PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_TOPIC_POSTS_LAYOUT)))
.addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
.setFeedItemCallback(feedItemCallback)
+ .setSelectionModeCallback(selectionModeCallback)
.init();
binding.swipeRefreshLayout.setRefreshing(true);
}
diff --git a/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java b/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java
index 055c36c1..bb2e693e 100644
--- a/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java
@@ -1,9 +1,11 @@
package awais.instagrabber.fragments.main;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
+import android.view.ActionMode;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -11,6 +13,8 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import androidx.activity.OnBackPressedCallback;
+import androidx.activity.OnBackPressedDispatcher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
@@ -24,13 +28,17 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+import com.google.common.collect.ImmutableList;
+
import java.util.List;
+import java.util.Set;
import awais.instagrabber.R;
import awais.instagrabber.activities.MainActivity;
import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.adapters.FeedStoriesAdapter;
import awais.instagrabber.asyncs.FeedPostFetchService;
+import awais.instagrabber.customviews.PrimaryActionModeCallback;
import awais.instagrabber.databinding.FragmentFeedBinding;
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
import awais.instagrabber.fragments.PostViewV2Fragment;
@@ -51,8 +59,7 @@ import static awais.instagrabber.utils.Utils.settingsHelper;
public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
private static final String TAG = "FeedFragment";
private static final int STORAGE_PERM_REQUEST_CODE = 8020;
- // private static final double MAX_VIDEO_HEIGHT = 0.9 * Utils.displayMetrics.heightPixels;
- // private static final int RESIZED_VIDEO_HEIGHT = (int) (0.8 * Utils.displayMetrics.heightPixels);
+ private static final int STORAGE_PERM_REQUEST_CODE_FOR_SELECTION = 8030;
private MainActivity fragmentActivity;
private CoordinatorLayout root;
@@ -61,6 +68,9 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
private boolean shouldRefresh = true;
private FeedStoriesViewModel feedStoriesViewModel;
private boolean storiesFetching;
+ private ActionMode actionMode;
+ private Set selectedFeedModels;
+ private FeedModel downloadFeedModel;
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
@Override
@@ -91,6 +101,7 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
showDownloadDialog(feedModel);
return;
}
+ downloadFeedModel = feedModel;
requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE);
}
@@ -147,6 +158,72 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
fragment.show(getChildFragmentManager(), "post_view");
}
};
+ private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
+ @Override
+ public void handleOnBackPressed() {
+ binding.feedRecyclerView.endSelection();
+ }
+ };
+ private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(
+ R.menu.multi_select_download_menu,
+ new PrimaryActionModeCallback.CallbacksHelper() {
+ @Override
+ public void onDestroy(final ActionMode mode) {
+ binding.feedRecyclerView.endSelection();
+ }
+
+ @Override
+ public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
+ if (item.getItemId() == R.id.action_download) {
+ if (FeedFragment.this.selectedFeedModels == null) return false;
+ final Context context = getContext();
+ if (context == null) return false;
+ if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
+ DownloadUtils.download(context, ImmutableList.copyOf(FeedFragment.this.selectedFeedModels));
+ binding.feedRecyclerView.endSelection();
+ return true;
+ }
+ requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE_FOR_SELECTION);
+ return true;
+ }
+ return false;
+ }
+ });
+ private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() {
+
+ @Override
+ public void onSelectionStart() {
+ if (!onBackPressedCallback.isEnabled()) {
+ final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
+ onBackPressedCallback.setEnabled(true);
+ onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
+ }
+ if (actionMode == null) {
+ actionMode = fragmentActivity.startActionMode(multiSelectAction);
+ }
+ }
+
+ @Override
+ public void onSelectionChange(final Set selectedFeedModels) {
+ final String title = getString(R.string.number_selected, selectedFeedModels.size());
+ if (actionMode != null) {
+ actionMode.setTitle(title);
+ }
+ FeedFragment.this.selectedFeedModels = selectedFeedModels;
+ }
+
+ @Override
+ public void onSelectionEnd() {
+ if (onBackPressedCallback.isEnabled()) {
+ onBackPressedCallback.setEnabled(false);
+ onBackPressedCallback.remove();
+ }
+ if (actionMode != null) {
+ actionMode.finish();
+ actionMode = null;
+ }
+ }
+ };
private void navigateToProfile(final String username) {
final NavController navController = NavHostFragment.findNavController(this);
@@ -183,7 +260,6 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
setupFeedStories();
setupFeed();
shouldRefresh = false;
- // showPostsLayoutPreferences();
}
@Override
@@ -223,6 +299,23 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
fetchStories();
}
+ @Override
+ public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ final boolean granted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
+ if (requestCode == STORAGE_PERM_REQUEST_CODE && granted) {
+ if (downloadFeedModel == null) return;
+ showDownloadDialog(downloadFeedModel);
+ return;
+ }
+ if (requestCode == STORAGE_PERM_REQUEST_CODE_FOR_SELECTION && granted) {
+ final Context context = getContext();
+ if (context == null) return;
+ DownloadUtils.download(context, ImmutableList.copyOf(selectedFeedModels));
+ binding.feedRecyclerView.endSelection();
+ }
+ }
+
private void setupFeed() {
binding.feedRecyclerView.setViewModelStoreOwner(this)
.setLifeCycleOwner(this)
@@ -230,6 +323,7 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
.setLayoutPreferences(PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_POSTS_LAYOUT)))
.addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
.setFeedItemCallback(feedItemCallback)
+ .setSelectionModeCallback(selectionModeCallback)
.init();
binding.feedSwipeRefreshLayout.setRefreshing(true);
// if (shouldAutoPlay) {
@@ -276,7 +370,7 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
});
}
- private void showDownloadDialog(final FeedModel feedModel) {
+ private void showDownloadDialog(@NonNull final FeedModel feedModel) {
final Context context = getContext();
if (context == null) return;
DownloadUtils.download(context, feedModel);
diff --git a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java
index 32486499..87e917e6 100644
--- a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java
@@ -2,6 +2,7 @@ package awais.instagrabber.fragments.main;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.pm.PackageManager;
import android.graphics.Typeface;
import android.graphics.drawable.Animatable;
import android.os.AsyncTask;
@@ -24,6 +25,7 @@ import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.OnBackPressedCallback;
+import androidx.activity.OnBackPressedDispatcher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
@@ -46,17 +48,18 @@ import com.facebook.drawee.controller.ControllerListener;
import com.facebook.imagepipeline.image.ImageInfo;
import com.google.android.material.snackbar.BaseTransientBottomBar;
import com.google.android.material.snackbar.Snackbar;
+import com.google.common.collect.ImmutableList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
+import java.util.Set;
import awais.instagrabber.ProfileNavGraphDirections;
import awais.instagrabber.R;
import awais.instagrabber.activities.MainActivity;
import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.adapters.HighlightsAdapter;
-import awais.instagrabber.adapters.PostsAdapter;
import awais.instagrabber.asyncs.HighlightsFetcher;
import awais.instagrabber.asyncs.ProfileFetcher;
import awais.instagrabber.asyncs.ProfilePostFetchService;
@@ -75,7 +78,6 @@ import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.PostsLayoutPreferences;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.StoryModel;
-import awais.instagrabber.models.enums.DownloadMethod;
import awais.instagrabber.models.enums.FavoriteType;
import awais.instagrabber.models.enums.PostItemType;
import awais.instagrabber.repositories.responses.FriendshipRepoChangeRootResponse;
@@ -98,6 +100,7 @@ import static awais.instagrabber.utils.Utils.settingsHelper;
public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
private static final String TAG = "ProfileFragment";
private static final int STORAGE_PERM_REQUEST_CODE = 8020;
+ private static final int STORAGE_PERM_REQUEST_CODE_FOR_SELECTION = 8030;
private MainActivity fragmentActivity;
private CoordinatorLayout root;
@@ -106,7 +109,6 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
private String cookie;
private String username;
private ProfileModel profileModel;
- private PostsAdapter postsAdapter;
private ActionMode actionMode;
private Handler usernameSettingHandler;
private FriendshipService friendshipService;
@@ -118,6 +120,9 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
private MenuItem blockMenuItem;
private MenuItem restrictMenuItem;
private boolean highlightsFetching;
+ private boolean postsSetupDone = false;
+ private Set selectedFeedModels;
+ private FeedModel downloadFeedModel;
private final Runnable usernameSettingRunnable = () -> {
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
@@ -131,10 +136,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
@Override
public void handleOnBackPressed() {
- setEnabled(false);
- remove();
- if (postsAdapter == null) return;
- postsAdapter.clearSelection();
+ binding.postsRecyclerView.endSelection();
}
};
private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(
@@ -142,22 +144,21 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
new CallbacksHelper() {
@Override
public void onDestroy(final ActionMode mode) {
- onBackPressedCallback.handleOnBackPressed();
+ binding.postsRecyclerView.endSelection();
}
@Override
public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
if (item.getItemId() == R.id.action_download) {
- if (postsAdapter == null || username == null) {
- return false;
- }
+ if (ProfileFragment.this.selectedFeedModels == null) return false;
final Context context = getContext();
if (context == null) return false;
- DownloadUtils.batchDownload(context,
- username,
- DownloadMethod.DOWNLOAD_MAIN,
- postsAdapter.getSelectedModels());
- checkAndResetAction();
+ if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
+ DownloadUtils.download(context, ImmutableList.copyOf(ProfileFragment.this.selectedFeedModels));
+ binding.postsRecyclerView.endSelection();
+ return true;
+ }
+ requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE_FOR_SELECTION);
return true;
}
return false;
@@ -210,6 +211,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
showDownloadDialog(feedModel);
return;
}
+ downloadFeedModel = feedModel;
requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE);
}
@@ -266,7 +268,41 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
fragment.show(getChildFragmentManager(), "post_view");
}
};
- private boolean postsSetupDone = false;
+ private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() {
+
+ @Override
+ public void onSelectionStart() {
+ if (!onBackPressedCallback.isEnabled()) {
+ final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
+ onBackPressedCallback.setEnabled(true);
+ onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
+ }
+ if (actionMode == null) {
+ actionMode = fragmentActivity.startActionMode(multiSelectAction);
+ }
+ }
+
+ @Override
+ public void onSelectionChange(final Set selectedFeedModels) {
+ final String title = getString(R.string.number_selected, selectedFeedModels.size());
+ if (actionMode != null) {
+ actionMode.setTitle(title);
+ }
+ ProfileFragment.this.selectedFeedModels = selectedFeedModels;
+ }
+
+ @Override
+ public void onSelectionEnd() {
+ if (onBackPressedCallback.isEnabled()) {
+ onBackPressedCallback.setEnabled(false);
+ onBackPressedCallback.remove();
+ }
+ if (actionMode != null) {
+ actionMode.finish();
+ actionMode = null;
+ }
+ }
+ };
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
@@ -409,18 +445,32 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
@Override
public void onDestroy() {
super.onDestroy();
- postsAdapter = null;
if (usernameSettingHandler != null) {
usernameSettingHandler.removeCallbacks(usernameSettingRunnable);
}
- // if (postsViewModel != null) {
- // postsViewModel.getList().postValue(Collections.emptyList());
- // }
if (highlightsViewModel != null) {
highlightsViewModel.getList().postValue(Collections.emptyList());
}
}
+ @Override
+ public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ final boolean granted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
+ if (requestCode == STORAGE_PERM_REQUEST_CODE && granted) {
+ if (downloadFeedModel == null) return;
+ showDownloadDialog(downloadFeedModel);
+ downloadFeedModel = null;
+ return;
+ }
+ if (requestCode == STORAGE_PERM_REQUEST_CODE_FOR_SELECTION && granted) {
+ final Context context = getContext();
+ if (context == null) return;
+ DownloadUtils.download(context, ImmutableList.copyOf(selectedFeedModels));
+ binding.postsRecyclerView.endSelection();
+ }
+ }
+
private void init() {
if (getArguments() != null) {
final ProfileFragmentArgs fragmentArgs = ProfileFragmentArgs.fromBundle(getArguments());
@@ -836,8 +886,8 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
final ProfilePicDialogFragment fragment = new ProfilePicDialogFragment(profileModel.getId(), username, profileModel.getHdProfilePic());
final FragmentTransaction ft = fragmentManager.beginTransaction();
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
- .add(fragment, "profilePicDialog")
- .commit();
+ .add(fragment, "profilePicDialog")
+ .commit();
}
}
@@ -855,6 +905,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
.setLayoutPreferences(PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_PROFILE_POSTS_LAYOUT)))
.addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
.setFeedItemCallback(feedItemCallback)
+ .setSelectionModeCallback(selectionModeCallback)
.init();
binding.swipeRefreshLayout.setRefreshing(true);
postsSetupDone = true;
@@ -928,21 +979,6 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
// .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
- private boolean checkAndResetAction() {
- if (!onBackPressedCallback.isEnabled() && actionMode == null) {
- return false;
- }
- if (onBackPressedCallback.isEnabled()) {
- onBackPressedCallback.setEnabled(false);
- onBackPressedCallback.remove();
- }
- if (actionMode != null) {
- actionMode.finish();
- actionMode = null;
- }
- return true;
- }
-
private void navigateToProfile(final String username) {
final NavController navController = NavHostFragment.findNavController(this);
final Bundle bundle = new Bundle();
diff --git a/app/src/main/java/awais/instagrabber/models/BasePostModel.java b/app/src/main/java/awais/instagrabber/models/BasePostModel.java
index 8c124926..38f5f8fd 100755
--- a/app/src/main/java/awais/instagrabber/models/BasePostModel.java
+++ b/app/src/main/java/awais/instagrabber/models/BasePostModel.java
@@ -19,7 +19,6 @@ public abstract class BasePostModel implements Serializable, Selectable {
protected boolean isSelected;
protected boolean isDownloaded;
protected long timestamp;
- protected int position;
boolean liked;
boolean saved;
@@ -55,10 +54,6 @@ public abstract class BasePostModel implements Serializable, Selectable {
return timestamp;
}
- public int getPosition() {
- return this.position;
- }
-
public boolean isSelected() {
return isSelected;
}
@@ -75,10 +70,6 @@ public abstract class BasePostModel implements Serializable, Selectable {
this.postId = postId;
}
- public void setPosition(final int position) {
- this.position = position;
- }
-
public void setSelected(final boolean selected) {
this.isSelected = selected;
}
diff --git a/app/src/main/java/awais/instagrabber/models/FeedModel.java b/app/src/main/java/awais/instagrabber/models/FeedModel.java
index b41323fe..d9178640 100755
--- a/app/src/main/java/awais/instagrabber/models/FeedModel.java
+++ b/app/src/main/java/awais/instagrabber/models/FeedModel.java
@@ -9,13 +9,11 @@ public final class FeedModel extends PostModel {
private final long commentsCount;
private long likesCount;
private final long viewCount;
- private boolean captionExpanded = false;
- private boolean mentionClicked = false;
- private List sliderItems;
- private int imageWidth;
- private int imageHeight;
- private String locationName;
- private String locationId;
+ private final List sliderItems;
+ private final int imageWidth;
+ private final int imageHeight;
+ private final String locationName;
+ private final String locationId;
public static class Builder {
@@ -184,39 +182,10 @@ public final class FeedModel extends PostModel {
return likesCount;
}
- public boolean isCaptionExpanded() {
- return captionExpanded;
- }
-
- public boolean isMentionClicked() {
- return !mentionClicked;
- }
-
- // public void setMentionClicked(final boolean mentionClicked) {
- // this.mentionClicked = mentionClicked;
- // }
- //
- // public void setSliderItems(final ViewerPostModel[] sliderItems) {
- // this.sliderItems = sliderItems;
- // setItemType(MediaItemType.MEDIA_TYPE_SLIDER);
- // }
-
- public void toggleCaption() {
- captionExpanded = !captionExpanded;
- }
-
public int getImageWidth() {
return imageWidth;
}
- // public void setImageWidth(final int imageWidth) {
- // this.imageWidth = imageWidth;
- // }
-
- // public void setImageHeight(final int imageHeight) {
- // this.imageHeight = imageHeight;
- // }
-
public int getImageHeight() {
return imageHeight;
}
@@ -225,18 +194,10 @@ public final class FeedModel extends PostModel {
return locationName;
}
- // public void setLocationName(final String locationName) {
- // this.locationName = locationName;
- // }
-
public String getLocationId() {
return locationId;
}
- // public void setLocationId(final String locationId) {
- // this.locationId = locationId;
- // }
-
public void setLiked(final boolean liked) {
this.liked = liked;
}
@@ -257,8 +218,6 @@ public final class FeedModel extends PostModel {
", thumbnailUrl=" + thumbnailUrl +
", commentsCount=" + commentsCount +
", viewCount=" + viewCount +
- ", captionExpanded=" + captionExpanded +
- ", mentionClicked=" + mentionClicked +
// ", sliderItems=" + sliderItems +
", imageWidth=" + imageWidth +
", imageHeight=" + imageHeight +
diff --git a/app/src/main/java/awais/instagrabber/services/DeleteImageIntentService.java b/app/src/main/java/awais/instagrabber/services/DeleteImageIntentService.java
new file mode 100644
index 00000000..fa6b5d78
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/services/DeleteImageIntentService.java
@@ -0,0 +1,64 @@
+package awais.instagrabber.services;
+
+import android.app.IntentService;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.app.NotificationManagerCompat;
+
+import java.io.File;
+
+public class DeleteImageIntentService extends IntentService {
+ private final static String TAG = "DeleteImageIntent";
+ private static final int DELETE_IMAGE_SERVICE_REQUEST_CODE = 9010;
+
+ public static final String EXTRA_IMAGE_PATH = "extra_image_path";
+ public static final String EXTRA_NOTIFICATION_ID = "extra_notification_id";
+ public static final String DELETE_IMAGE_SERVICE = "delete_image_service";
+
+ public DeleteImageIntentService() {
+ super(DELETE_IMAGE_SERVICE);
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ startService(new Intent(this, DeleteImageIntentService.class));
+ }
+
+ @Override
+ protected void onHandleIntent(@Nullable Intent intent) {
+ if (intent != null && Intent.ACTION_DELETE.equals(intent.getAction()) && intent.hasExtra(EXTRA_IMAGE_PATH)) {
+ final String path = intent.getStringExtra(EXTRA_IMAGE_PATH);
+ final File file = new File(path);
+ boolean deleted;
+ if (file.exists()) {
+ deleted = file.delete();
+ if (!deleted) {
+ Log.w(TAG, "onHandleIntent: file not delete!");
+ }
+ } else {
+ deleted = true;
+ }
+ if (deleted) {
+ final int notificationId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1);
+ NotificationManagerCompat.from(this).cancel(notificationId);
+ }
+ }
+ }
+
+ @NonNull
+ public static PendingIntent pendingIntent(@NonNull final Context context,
+ @NonNull final String imagePath,
+ final int notificationId) {
+ final Intent intent = new Intent(context, DeleteImageIntentService.class);
+ intent.setAction(Intent.ACTION_DELETE);
+ intent.putExtra(EXTRA_IMAGE_PATH, imagePath);
+ intent.putExtra(EXTRA_NOTIFICATION_ID, notificationId);
+ return PendingIntent.getService(context, DELETE_IMAGE_SERVICE_REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+}
diff --git a/app/src/main/java/awais/instagrabber/utils/DownloadUtils.java b/app/src/main/java/awais/instagrabber/utils/DownloadUtils.java
index b9bea336..f9c37f80 100644
--- a/app/src/main/java/awais/instagrabber/utils/DownloadUtils.java
+++ b/app/src/main/java/awais/instagrabber/utils/DownloadUtils.java
@@ -319,37 +319,42 @@ public final class DownloadUtils {
public static void download(@NonNull final Context context,
@NonNull final FeedModel feedModel,
final int position) {
- final File downloadDir = getDownloadDir(context, "@" + feedModel.getProfileModel().getUsername());
- if (downloadDir == null) return;
- switch (feedModel.getItemType()) {
- case MEDIA_TYPE_IMAGE:
- case MEDIA_TYPE_VIDEO: {
- final String url = feedModel.getDisplayUrl();
- final File file = getDownloadSaveFile(downloadDir, feedModel.getPostId(), url);
- download(context, url, file.getAbsolutePath());
- break;
- }
- case MEDIA_TYPE_SLIDER:
- final List sliderItems = feedModel.getSliderItems();
- final Map map = new HashMap<>();
- for (int i = 0; i < sliderItems.size(); i++) {
- final PostChild child = sliderItems.get(i);
- final String url = child.getDisplayUrl();
- final File file = getDownloadChildSaveFile(downloadDir, feedModel.getPostId(), i + 1, url);
- map.put(url, file.getAbsolutePath());
- }
- download(context, map);
- break;
- default:
- }
-
+ download(context, Collections.singletonList(feedModel), position);
}
- private static void download(final Context context,
- final String url,
- final String filePath) {
- if (context == null || url == null || filePath == null) return;
- download(context, Collections.singletonMap(url, filePath));
+ public static void download(@NonNull final Context context,
+ @NonNull final List feedModels) {
+ download(context, feedModels, -1);
+ }
+
+ private static void download(@NonNull final Context context,
+ @NonNull final List feedModels,
+ final int childPositionIfSingle) {
+ final Map map = new HashMap<>();
+ for (final FeedModel feedModel : feedModels) {
+ final File downloadDir = getDownloadDir(context, "@" + feedModel.getProfileModel().getUsername());
+ if (downloadDir == null) return;
+ switch (feedModel.getItemType()) {
+ case MEDIA_TYPE_IMAGE:
+ case MEDIA_TYPE_VIDEO: {
+ final String url = feedModel.getDisplayUrl();
+ final File file = getDownloadSaveFile(downloadDir, feedModel.getPostId(), url);
+ map.put(url, file.getAbsolutePath());
+ break;
+ }
+ case MEDIA_TYPE_SLIDER:
+ final List sliderItems = feedModel.getSliderItems();
+ for (int i = 0; i < sliderItems.size(); i++) {
+ final PostChild child = sliderItems.get(i);
+ final String url = child.getDisplayUrl();
+ final File file = getDownloadChildSaveFile(downloadDir, feedModel.getPostId(), i + 1, url);
+ map.put(url, file.getAbsolutePath());
+ }
+ break;
+ default:
+ }
+ }
+ download(context, map);
}
private static void download(final Context context, final Map urlFilePathMap) {
diff --git a/app/src/main/java/awais/instagrabber/workers/DownloadWorker.java b/app/src/main/java/awais/instagrabber/workers/DownloadWorker.java
index 9785a1ec..7d63e4c6 100644
--- a/app/src/main/java/awais/instagrabber/workers/DownloadWorker.java
+++ b/app/src/main/java/awais/instagrabber/workers/DownloadWorker.java
@@ -35,15 +35,15 @@ import java.net.URL;
import java.net.URLConnection;
import java.util.Collection;
import java.util.HashMap;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.R;
-import awais.instagrabber.activities.MainActivity;
+import awais.instagrabber.services.DeleteImageIntentService;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
@@ -236,85 +236,85 @@ public class DownloadWorker extends Worker {
private void showSummary(final Map urlToFilePathMap) {
final Context context = getApplicationContext();
final Collection filePaths = urlToFilePathMap.values();
- final List notifications = filePaths
- .stream()
- .map(filePath -> {
- final File file = new File(filePath);
- context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file)));
- MediaScannerConnection.scanFile(context, new String[]{file.getAbsolutePath()}, null, null);
- final Uri uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", file);
- final ContentResolver contentResolver = context.getContentResolver();
- Bitmap bitmap = null;
- if (Utils.isImage(uri, contentResolver)) {
- try (final InputStream inputStream = contentResolver.openInputStream(uri)) {
- bitmap = BitmapFactory.decodeStream(inputStream);
- } catch (final Exception e) {
- if (logCollector != null)
- logCollector.appendException(e, LogCollector.LogFile.ASYNC_DOWNLOADER, "onPostExecute::bitmap_1");
- if (BuildConfig.DEBUG) Log.e(TAG, "", e);
- }
+ final List notifications = new LinkedList<>();
+ final List notificationIds = new LinkedList<>();
+ int count = 1;
+ for (final String filePath : filePaths) {
+ final File file = new File(filePath);
+ context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file)));
+ MediaScannerConnection.scanFile(context, new String[]{file.getAbsolutePath()}, null, null);
+ final Uri uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", file);
+ final ContentResolver contentResolver = context.getContentResolver();
+ Bitmap bitmap = null;
+ if (Utils.isImage(uri, contentResolver)) {
+ try (final InputStream inputStream = contentResolver.openInputStream(uri)) {
+ bitmap = BitmapFactory.decodeStream(inputStream);
+ } catch (final Exception e) {
+ if (logCollector != null)
+ logCollector.appendException(e, LogCollector.LogFile.ASYNC_DOWNLOADER, "onPostExecute::bitmap_1");
+ if (BuildConfig.DEBUG) Log.e(TAG, "", e);
+ }
+ }
+ if (bitmap == null) {
+ final MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+ try {
+ try {
+ retriever.setDataSource(context, uri);
+ } catch (final Exception e) {
+ retriever.setDataSource(file.getAbsolutePath());
}
- if (bitmap == null) {
- final MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+ bitmap = retriever.getFrameAtTime();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
try {
- try {
- retriever.setDataSource(context, uri);
- } catch (final Exception e) {
- retriever.setDataSource(file.getAbsolutePath());
- }
- bitmap = retriever.getFrameAtTime();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
- try {
- retriever.close();
- } catch (final Exception e) {
- if (logCollector != null)
- logCollector.appendException(e, LogCollector.LogFile.ASYNC_DOWNLOADER, "onPostExecute::bitmap_2");
- }
+ retriever.close();
} catch (final Exception e) {
- if (BuildConfig.DEBUG) Log.e(TAG, "", e);
if (logCollector != null)
- logCollector.appendException(e, LogCollector.LogFile.ASYNC_DOWNLOADER, "onPostExecute::bitmap_3");
+ logCollector.appendException(e, LogCollector.LogFile.ASYNC_DOWNLOADER, "onPostExecute::bitmap_2");
}
- }
- final String downloadComplete = context.getString(R.string.downloader_complete);
- final Intent intent = new Intent(Intent.ACTION_VIEW, uri)
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_FROM_BACKGROUND
- | Intent.FLAG_GRANT_READ_URI_PERMISSION
- | Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
- .putExtra(Intent.EXTRA_STREAM, uri);
- final PendingIntent pendingIntent = PendingIntent.getActivity(
- context,
- DOWNLOAD_NOTIFICATION_INTENT_REQUEST_CODE,
- intent,
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT
- );
- final Intent deleteIntent = new Intent(getApplicationContext(), MainActivity.class)
- .setAction(Constants.ACTION_SHOW_ACTIVITY)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
- final PendingIntent deleteItemIntent = PendingIntent
- .getActivity(getApplicationContext(), DELETE_IMAGE_REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
- final NotificationCompat.Builder builder = new NotificationCompat.Builder(context, DOWNLOAD_CHANNEL_ID)
- .setSmallIcon(R.drawable.ic_download)
- .setContentText(null)
- .setContentTitle(downloadComplete)
- .setWhen(System.currentTimeMillis())
- .setOnlyAlertOnce(true)
- .setAutoCancel(true)
- .setGroup(NOTIF_GROUP_NAME + "_" + getId())
- .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
- .setContentIntent(pendingIntent)
- .addAction(R.drawable.ic_delete, context.getString(R.string.delete), deleteItemIntent);
- if (bitmap != null) {
- builder.setLargeIcon(bitmap)
- .setStyle(new NotificationCompat.BigPictureStyle()
- .bigPicture(bitmap)
- .bigLargeIcon(null))
- .setBadgeIconType(NotificationCompat.BADGE_ICON_SMALL);
- }
- return builder;
- })
- .collect(Collectors.toList());
+ } catch (final Exception e) {
+ if (BuildConfig.DEBUG) Log.e(TAG, "", e);
+ if (logCollector != null)
+ logCollector.appendException(e, LogCollector.LogFile.ASYNC_DOWNLOADER, "onPostExecute::bitmap_3");
+ }
+ }
+ final String downloadComplete = context.getString(R.string.downloader_complete);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, uri)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_FROM_BACKGROUND
+ | Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
+ .putExtra(Intent.EXTRA_STREAM, uri);
+ final PendingIntent pendingIntent = PendingIntent.getActivity(
+ context,
+ DOWNLOAD_NOTIFICATION_INTENT_REQUEST_CODE,
+ intent,
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT
+ );
+ final int notificationId = getNotificationId() + count;
+ notificationIds.add(notificationId);
+ count++;
+ final NotificationCompat.Builder builder = new NotificationCompat.Builder(context, DOWNLOAD_CHANNEL_ID)
+ .setSmallIcon(R.drawable.ic_download)
+ .setContentText(null)
+ .setContentTitle(downloadComplete)
+ .setWhen(System.currentTimeMillis())
+ .setOnlyAlertOnce(true)
+ .setAutoCancel(true)
+ .setGroup(NOTIF_GROUP_NAME + "_" + getId())
+ .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
+ .setContentIntent(pendingIntent)
+ .addAction(R.drawable.ic_delete,
+ context.getString(R.string.delete),
+ DeleteImageIntentService.pendingIntent(context, filePath, notificationId));
+ if (bitmap != null) {
+ builder.setLargeIcon(bitmap)
+ .setStyle(new NotificationCompat.BigPictureStyle()
+ .bigPicture(bitmap)
+ .bigLargeIcon(null))
+ .setBadgeIconType(NotificationCompat.BADGE_ICON_SMALL);
+ }
+ notifications.add(builder);
+ }
Notification summaryNotification = null;
if (urlToFilePathMap.size() != 1) {
final String text = "Downloaded " + urlToFilePathMap.size() + " items";
@@ -327,20 +327,18 @@ public class DownloadWorker extends Worker {
.setGroupSummary(true)
.build();
}
- int count = 1;
- for (final NotificationCompat.Builder builder : notifications) {
+ for (int i = 0; i < notifications.size(); i++) {
+ final NotificationCompat.Builder builder = notifications.get(i);
// only make sound and vibrate for the last notification
- if (count != notifications.size()) {
+ if (i != notifications.size() - 1) {
builder.setSound(null)
.setVibrate(null);
}
- notificationManager.notify(getNotificationId() + count, builder.build());
- count++;
+ notificationManager.notify(notificationIds.get(i), builder.build());
}
if (summaryNotification != null) {
notificationManager.notify(getNotificationId() + count, summaryNotification);
}
-
}
public static class DownloadRequest {
diff --git a/app/src/main/res/drawable/ic_baseline_check_circle_24.xml b/app/src/main/res/drawable/ic_baseline_check_circle_24.xml
new file mode 100644
index 00000000..5e111ca7
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_check_circle_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/layout/item_feed_grid.xml b/app/src/main/res/layout/item_feed_grid.xml
index 0ed1d34b..d456d916 100644
--- a/app/src/main/res/layout/item_feed_grid.xml
+++ b/app/src/main/res/layout/item_feed_grid.xml
@@ -92,17 +92,17 @@
android:id="@+id/selectedView"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="#8A000000"
+ android:background="@color/black_a50"
android:visibility="gone"
- tools:visibility="gone">
+ tools:visibility="visible">
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_post.xml b/app/src/main/res/layout/item_post.xml
index 1df80ac9..2a420115 100755
--- a/app/src/main/res/layout/item_post.xml
+++ b/app/src/main/res/layout/item_post.xml
@@ -47,7 +47,7 @@
android:layout_height="match_parent"
android:background="#8A000000"
android:visibility="gone"
- tools:visibility="gone">
+ tools:visibility="visible">