diff --git a/.all-contributorsrc b/.all-contributorsrc
index 1bc26b73..3e8bb4a7 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -61,6 +61,15 @@
"translation"
]
},
+ {
+ "login": "CrazyMarvin",
+ "name": "CrazyMarvin",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/15004217?v=4",
+ "profile": "https://github.com/CrazyMarvin",
+ "contributions": [
+ "financial"
+ ]
+ },
{
"login": "KevinNThomas",
"name": "Kevin Thomas",
@@ -82,16 +91,6 @@
"question"
]
},
- {
- "login": "e-edgren",
- "name": "Airikr",
- "avatar_url": "https://avatars0.githubusercontent.com/u/53869451",
- "profile": "https://airikr.me/",
- "contributions": [
- "ideas",
- "question"
- ]
- },
{
"login": "alin-1",
"name": "ALIN",
@@ -102,6 +101,26 @@
"ideas"
]
},
+ {
+ "login": "RickyM7",
+ "name": "Ricardo",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/24703825?v=4",
+ "profile": "https://github.com/RickyM7",
+ "contributions": [
+ "bug",
+ "translation"
+ ]
+ },
+ {
+ "login": "e-edgren",
+ "name": "Airikr",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/53869451",
+ "profile": "https://airikr.me/",
+ "contributions": [
+ "ideas",
+ "question"
+ ]
+ },
{
"login": "Akrai",
"name": "Akrai",
@@ -112,6 +131,15 @@
"translation"
]
},
+ {
+ "login": "cizordj",
+ "name": "Cézar Augusto",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/32869222?v=4",
+ "profile": "https://github.com/cizordj",
+ "contributions": [
+ "translation"
+ ]
+ },
{
"login": "farzadx",
"name": "farzadx",
@@ -149,19 +177,28 @@
]
},
{
- "login": "kernoeb",
- "name": "kernoeb",
- "avatar_url": "https://avatars3.githubusercontent.com/u/24623168",
- "profile": "https://becauseofprog.fr/",
+ "login": "initdebugs",
+ "name": "Initdebugs",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/75781464?v=4",
+ "profile": "https://github.com/initdebugs",
"contributions": [
"translation"
]
},
{
- "login": "Lego8486",
- "name": "Ten_Lego",
- "avatar_url": "https://avatars1.githubusercontent.com/u/47414485",
- "profile": "https://github.com/Lego8486",
+ "login": "CrafterSvK",
+ "name": "Jakub Janek",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/8365659?v=4",
+ "profile": "https://janek.xyz/",
+ "contributions": [
+ "translation"
+ ]
+ },
+ {
+ "login": "kernoeb",
+ "name": "kernoeb",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/24623168",
+ "profile": "https://becauseofprog.fr/",
"contributions": [
"translation"
]
@@ -229,16 +266,6 @@
"translation"
]
},
- {
- "login": "RickyM7",
- "name": "Ricardo",
- "avatar_url": "https://avatars3.githubusercontent.com/u/24703825?v=4",
- "profile": "https://github.com/RickyM7",
- "contributions": [
- "bug",
- "translation"
- ]
- },
{
"login": "rikishi0071",
"name": "rikishi0071",
@@ -257,6 +284,15 @@
"translation"
]
},
+ {
+ "login": "Lego8486",
+ "name": "Ten_Lego",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/47414485",
+ "profile": "https://github.com/Lego8486",
+ "contributions": [
+ "translation"
+ ]
+ },
{
"login": "wagnim",
"name": "wagnim",
@@ -265,6 +301,15 @@
"contributions": [
"translation"
]
+ },
+ {
+ "login": "ysakamoto",
+ "name": "ysakamoto",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/1331642?v=4",
+ "profile": "https://github.com/ysakamoto",
+ "contributions": [
+ "translation"
+ ]
}
],
"contributorsPerLine": 6,
diff --git a/README.md b/README.md
index 3e0e5552..1fe4a522 100755
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](http://makeapullrequest.com)
[![GPLv3 license](https://img.shields.io/badge/License-GPLv3-blue.svg)](./LICENSE)
[![GitHub stars](https://img.shields.io/github/stars/austinhuang0131/instagrabber.svg?style=social&label=Star)](https://GitHub.com/austinhuang0131/barinsta/stargazers/)
-[![All Contributors](https://img.shields.io/badge/all_contributors-27-orange.svg)](#contributors)
+[![All Contributors](https://img.shields.io/badge/all_contributors-32-orange.svg)](#contributors)
We're previously known as InstaGrabber.
@@ -32,11 +32,15 @@ Version status: ![F-Droid](https://img.shields.io/f-droid/v/me.austinhuang.insta
+## We need maintainers!
+
+To speed up development, we need more hands on deck. If you are proficient in Java and Android development, and are willing to perform such a public service, please [contact us](https://t.me/austinhuang).
+
## Contact us
* Use [GitHub issues](https://github.com/austinhuang0131/instagrabber/issues) when possible.
* Email: [Barinsta@AustinHuang.me](mailto:barinsta@austinhuang.me?body=Please%20note%20that%20your%20email%20address%20and%20the%20entire%20content%20will%20be%20published%20onto%20GitHub%20issues.%20If%20you%20do%20not%20wish%20to%20do%20that%2C%20use%20other%20contact%20methods%20instead.) (Synced to GitHub issues)
-* Reddit: [r/Barinsta](https://reddit.com/r/barinsta)
+* Reddit: [![r/Barinsta](https://img.shields.io/reddit/subreddit-subscribers/Barinsta?style=social)](https://reddit.com/r/barinsta)
* Chat (Bridged to each other): [![Matrix](https://img.shields.io/badge/Matrix-%23Barinsta:matrix.org-000000?logo=matrix)](https://matrix.to/#/#barinsta:matrix.org) [![Telegram](https://img.shields.io/badge/Telegram-@Grabber__App-2CA5E0?logo=telegram)](https://t.me/grabber_app) [![Discord](https://img.shields.io/badge/Discord-YtEDzN2-7289da?logo=discord&logoColor=white)](https://discord.gg/YtEDzN2) [![Gitter](https://img.shields.io/badge/Gitter-InstaGrabber/General-ed1965?logo=gitter)](https://gitter.im/instagrabber/general)
## Contributors
@@ -53,36 +57,43 @@ Prominent contributors are listed here in the [all-contributors](https://allcont
Anderson Mesquita 💻 🐛 |
AWAiS 💻 |
Stefan Najdovski 🎨 🌍 |
- Kevin Thomas 💵 |
+ CrazyMarvin 💵 |
+ Kevin Thomas 💵 |
Shadowspear123 📝 🐛 🤔 💬 |
- Airikr 🤔 💬 |
ALIN 🐛 🤔 |
+ Ricardo 🐛 🌍 |
+ Airikr 🤔 💬 |
Akrai 🤔 🌍 |
+
+
+ Cézar Augusto 🌍 |
farzadx 🌍 |
Fatih Aydın 🌍 |
-
-
fouze555 🌍 |
Galang23 🌍 |
- kernoeb 🌍 |
- Ten_Lego 🌍 |
- MoaufmKlo 🌍 |
- nalinalini 🌍 |
+ Initdebugs 🌍 |
+ Jakub Janek 🌍 |
+ kernoeb 🌍 |
+ MoaufmKlo 🌍 |
+ nalinalini 🌍 |
peterge1998 🌍 |
PierreM0 🌍 |
+
+
RAMAR-RAR 🌍 |
rohang02 🌍 |
retiolus 🌍 |
- Ricardo 🐛 🌍 |
-
-
rikishi0071 🌍 |
Still Hsu 🌍 |
+ Ten_Lego 🌍 |
+
+
wagnim 🌍 |
+ ysakamoto 🌍 |
@@ -92,12 +103,11 @@ Prominent contributors are listed here in the [all-contributors](https://allcont
## License
-This app is originally made by [@AwaisKing](https://github.com/AwaisKing) on [GitLab](https://gitlab.com/AwaisKing/instagrabber).
+This app's predecessor, InstaGrabber, is originally made by [@AwaisKing](https://github.com/AwaisKing) on [GitLab](https://gitlab.com/AwaisKing/instagrabber).
- Barinsta (ex-InstaGrabber)
- Copyright (C) 2019 AWAiS
- Copyright (C) 2020 Austin Huang
- Ammar Githam
+ Barinsta
+ Copyright (C) 2020-2021 Austin Huang
+ Ammar Githam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/app/build.gradle b/app/build.gradle
index 01d39a3a..8705ac87 100755
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -10,8 +10,8 @@ android {
minSdkVersion 21
targetSdkVersion 29
- versionCode 54
- versionName '19.0.2'
+ versionCode 56
+ versionName '19.0.4'
multiDexEnabled true
@@ -98,6 +98,9 @@ dependencies {
implementation "androidx.emoji:emoji:$emoji_compat_version"
implementation "androidx.emoji:emoji-appcompat:$emoji_compat_version"
+ // implementation 'com.github.hendrawd:StorageUtil:1.1.0'
+ implementation 'me.austinhuang:AutoLinkTextViewV2:-SNAPSHOT'
+
implementation 'org.jsoup:jsoup:1.13.1'
implementation 'com.facebook.fresco:fresco:2.3.0'
implementation 'com.facebook.fresco:animated-webp:2.3.0'
@@ -109,7 +112,6 @@ dependencies {
implementation 'org.apache.commons:commons-imaging:1.0-alpha2'
implementation 'com.ibm.icu:icu4j:68.1'
- implementation 'com.github.ammargitham:AutoLinkTextViewV2:master-SNAPSHOT'
implementation 'com.github.ammargitham:uCrop:2.3-native-beta-2'
implementation 'com.github.ammargitham:android-gpuimage:2.1.1-beta4'
diff --git a/app/src/main/java/awais/instagrabber/adapters/CommentsAdapter.java b/app/src/main/java/awais/instagrabber/adapters/CommentsAdapter.java
index e0ccb44e..d88e5f34 100755
--- a/app/src/main/java/awais/instagrabber/adapters/CommentsAdapter.java
+++ b/app/src/main/java/awais/instagrabber/adapters/CommentsAdapter.java
@@ -83,8 +83,8 @@ public final class CommentsAdapter extends ListAdapter {
private final OnFeedStoryClickListener listener;
@@ -22,7 +29,7 @@ public final class FeedStoriesAdapter extends ListAdapter {
+ private final OnFeedStoryClickListener listener;
+
+ private static final DiffUtil.ItemCallback diffCallback = new DiffUtil.ItemCallback() {
+ @Override
+ public boolean areItemsTheSame(@NonNull final FeedStoryModel oldItem, @NonNull final FeedStoryModel newItem) {
+ return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId());
+ }
+
+ @Override
+ public boolean areContentsTheSame(@NonNull final FeedStoryModel oldItem, @NonNull final FeedStoryModel newItem) {
+ return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId()) && oldItem.isFullyRead().equals(newItem.isFullyRead());
+ }
+ };
+
+ public FeedStoriesListAdapter(final OnFeedStoryClickListener listener) {
+ super(diffCallback);
+ this.listener = listener;
+ }
+
+ @NonNull
+ @Override
+ public StoryListViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
+ final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
+ final ItemNotificationBinding binding = ItemNotificationBinding.inflate(layoutInflater, parent, false);
+ return new StoryListViewHolder(binding);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull final StoryListViewHolder holder, final int position) {
+ final FeedStoryModel model = getItem(position);
+ holder.bind(model, position, listener);
+ }
+
+ public interface OnFeedStoryClickListener {
+ void onFeedStoryClick(final FeedStoryModel model, final int position);
+
+ void onProfileClick(final String username);
+ }
+}
diff --git a/app/src/main/java/awais/instagrabber/adapters/HighlightStoriesListAdapter.java b/app/src/main/java/awais/instagrabber/adapters/HighlightStoriesListAdapter.java
new file mode 100755
index 00000000..9547b9c1
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/adapters/HighlightStoriesListAdapter.java
@@ -0,0 +1,53 @@
+package awais.instagrabber.adapters;
+
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.DiffUtil;
+import androidx.recyclerview.widget.ListAdapter;
+
+import awais.instagrabber.adapters.viewholder.StoryListViewHolder;
+import awais.instagrabber.databinding.ItemNotificationBinding;
+import awais.instagrabber.models.HighlightModel;
+
+public final class HighlightStoriesListAdapter extends ListAdapter {
+ private final OnHighlightStoryClickListener listener;
+
+ private static final DiffUtil.ItemCallback diffCallback = new DiffUtil.ItemCallback() {
+ @Override
+ public boolean areItemsTheSame(@NonNull final HighlightModel oldItem, @NonNull final HighlightModel newItem) {
+ return oldItem.getId().equals(newItem.getId());
+ }
+
+ @Override
+ public boolean areContentsTheSame(@NonNull final HighlightModel oldItem, @NonNull final HighlightModel newItem) {
+ return oldItem.getId().equals(newItem.getId());
+ }
+ };
+
+ public HighlightStoriesListAdapter(final OnHighlightStoryClickListener listener) {
+ super(diffCallback);
+ this.listener = listener;
+ }
+
+ @NonNull
+ @Override
+ public StoryListViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
+ final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
+ final ItemNotificationBinding binding = ItemNotificationBinding.inflate(layoutInflater, parent, false);
+ return new StoryListViewHolder(binding);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull final StoryListViewHolder holder, final int position) {
+ final HighlightModel model = getItem(position);
+ holder.bind(model, position, listener);
+ }
+
+ public interface OnHighlightStoryClickListener {
+ void onHighlightClick(final HighlightModel model, final int position);
+
+ void onProfileClick(final String username);
+ }
+}
diff --git a/app/src/main/java/awais/instagrabber/adapters/LikesAdapter.java b/app/src/main/java/awais/instagrabber/adapters/LikesAdapter.java
new file mode 100755
index 00000000..5592ee6d
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/adapters/LikesAdapter.java
@@ -0,0 +1,45 @@
+package awais.instagrabber.adapters;
+
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.List;
+
+import awais.instagrabber.adapters.viewholder.FollowsViewHolder;
+import awais.instagrabber.databinding.ItemFollowBinding;
+import awais.instagrabber.models.ProfileModel;
+
+public final class LikesAdapter extends RecyclerView.Adapter {
+ private final List profileModels;
+ private final View.OnClickListener onClickListener;
+
+ public LikesAdapter(final List profileModels,
+ final View.OnClickListener onClickListener) {
+ this.profileModels = profileModels;
+ this.onClickListener = onClickListener;
+ }
+
+ @NonNull
+ @Override
+ public FollowsViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
+ final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
+ final ItemFollowBinding binding = ItemFollowBinding.inflate(layoutInflater, parent, false);
+ return new FollowsViewHolder(binding);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull final FollowsViewHolder holder, final int position) {
+ final ProfileModel model = profileModels.get(position);
+ holder.bind(model, null, onClickListener);
+ }
+
+ @Override
+ public int getItemCount() {
+ return profileModels.size();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java b/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java
index 10e5867a..124abe73 100644
--- a/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java
+++ b/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java
@@ -76,16 +76,23 @@ public final class NotificationsAdapter extends ListAdapter sort(final List list) {
final List listCopy = new ArrayList<>(list);
Collections.sort(listCopy, (o1, o2) -> {
- if (o1.getType() == o2.getType()) return 0;
// keep requests at top
- if (o1.getType() == NotificationType.REQUEST) return -1;
- if (o2.getType() == NotificationType.REQUEST) return 1;
- return 0;
+ if (o1.getType() == o2.getType()
+ && o1.getType() == NotificationType.REQUEST
+ && o2.getType() == NotificationType.REQUEST) return 0;
+ else if (o1.getType() == NotificationType.REQUEST) return -1;
+ else if (o2.getType() == NotificationType.REQUEST) return 1;
+ // timestamp
+ return o1.getTimestamp() > o2.getTimestamp() ? -1 : (o1.getTimestamp() == o2.getTimestamp() ? 0 : 1);
});
return listCopy;
}
public interface OnNotificationClickListener {
void onNotificationClick(final NotificationModel model);
+
+ void onProfileClick(final String username);
+
+ void onPreviewClick(final NotificationModel model);
}
}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/adapters/PostViewAdapter.java b/app/src/main/java/awais/instagrabber/adapters/PostViewAdapter.java
deleted file mode 100644
index f49b8974..00000000
--- a/app/src/main/java/awais/instagrabber/adapters/PostViewAdapter.java
+++ /dev/null
@@ -1,75 +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 androidx.recyclerview.widget.ListAdapter;
-//
-// import awais.instagrabber.adapters.viewholder.PostViewerViewHolder;
-// import awais.instagrabber.databinding.ItemFullPostViewBinding;
-// import awais.instagrabber.interfaces.MentionClickListener;
-// import awais.instagrabber.models.ViewerPostModelWrapper;
-//
-// public class PostViewAdapter extends ListAdapter {
-// private final OnPostViewChildViewClickListener clickListener;
-// private final OnPostCaptionLongClickListener longClickListener;
-// private final MentionClickListener mentionClickListener;
-//
-// private static final DiffUtil.ItemCallback diffCallback = new DiffUtil.ItemCallback() {
-// @Override
-// public boolean areItemsTheSame(@NonNull final ViewerPostModelWrapper oldItem,
-// @NonNull final ViewerPostModelWrapper newItem) {
-// return oldItem.getPosition() == newItem.getPosition();
-// }
-//
-// @Override
-// public boolean areContentsTheSame(@NonNull final ViewerPostModelWrapper oldItem,
-// @NonNull final ViewerPostModelWrapper newItem) {
-// return oldItem.getViewerPostModels().equals(newItem.getViewerPostModels());
-// }
-// };
-//
-// public PostViewAdapter(final OnPostViewChildViewClickListener clickListener,
-// final OnPostCaptionLongClickListener longClickListener,
-// final MentionClickListener mentionClickListener) {
-// super(diffCallback);
-// this.clickListener = clickListener;
-// this.longClickListener = longClickListener;
-// this.mentionClickListener = mentionClickListener;
-// }
-//
-// @Override
-// public void onViewDetachedFromWindow(@NonNull final PostViewerViewHolder holder) {
-// holder.stopPlayingVideo();
-// }
-//
-// @NonNull
-// @Override
-// public PostViewerViewHolder onCreateViewHolder(@NonNull final ViewGroup parent,
-// final int viewType) {
-// final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
-// final ItemFullPostViewBinding binding = ItemFullPostViewBinding
-// .inflate(layoutInflater, parent, false);
-// return new PostViewerViewHolder(binding);
-// }
-//
-// @Override
-// public void onBindViewHolder(@NonNull final PostViewerViewHolder holder, final int position) {
-// final ViewerPostModelWrapper item = getItem(position);
-// holder.bind(item, position, clickListener, longClickListener, mentionClickListener);
-// }
-//
-// public interface OnPostViewChildViewClickListener {
-// void onClick(View v,
-// ViewerPostModelWrapper viewerPostModelWrapper,
-// int postPosition,
-// int childPosition);
-// }
-//
-// public interface OnPostCaptionLongClickListener {
-// void onLongClick(String text);
-// }
-// }
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/FeedStoryViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/FeedStoryViewHolder.java
index 10c00e69..76b309e9 100644
--- a/app/src/main/java/awais/instagrabber/adapters/viewholder/FeedStoryViewHolder.java
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/FeedStoryViewHolder.java
@@ -24,10 +24,14 @@ public final class FeedStoryViewHolder extends RecyclerView.ViewHolder {
if (listener == null) return;
listener.onFeedStoryClick(model, position);
});
+ binding.getRoot().setOnLongClickListener(v -> {
+ if (listener != null) listener.onFeedStoryLongClick(model, position);
+ return true;
+ });
final ProfileModel profileModel = model.getProfileModel();
binding.title.setText(profileModel.getUsername());
- binding.title.setAlpha(model.getFullyRead() ? 0.5F : 1.0F);
+ binding.title.setAlpha(model.isFullyRead() ? 0.5F : 1.0F);
binding.icon.setImageURI(profileModel.getSdProfilePic());
- binding.icon.setAlpha(model.getFullyRead() ? 0.5F : 1.0F);
+ binding.icon.setAlpha(model.isFullyRead() ? 0.5F : 1.0F);
}
}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/FollowsViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/FollowsViewHolder.java
index a9b09de0..1f38cecb 100755
--- a/app/src/main/java/awais/instagrabber/adapters/viewholder/FollowsViewHolder.java
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/FollowsViewHolder.java
@@ -1,5 +1,6 @@
package awais.instagrabber.adapters.viewholder;
+import android.util.Log;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java
index ac1ef257..1c49dbee 100644
--- a/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java
@@ -24,10 +24,6 @@ public final class NotificationViewHolder extends RecyclerView.ViewHolder {
public void bind(final NotificationModel model,
final OnNotificationClickListener notificationClickListener) {
if (model == null) return;
- itemView.setOnClickListener(v -> {
- if (notificationClickListener == null) return;
- notificationClickListener.onNotificationClick(model);
- });
int text = -1;
CharSequence subtext = null;
switch (model.getType()) {
@@ -52,20 +48,54 @@ public final class NotificationViewHolder extends RecyclerView.ViewHolder {
text = R.string.request_notif;
subtext = model.getText();
break;
+ case COMMENT_LIKE:
+ case TAGGED_COMMENT:
+ case RESPONDED_STORY:
+ subtext = model.getText();
+ break;
+ case AYML:
+ subtext = model.getPostId();
+ break;
}
- binding.tvUsername.setText(model.getUsername());
- binding.tvComment.setText(text);
- binding.tvSubComment.setText(subtext, subtext instanceof Spannable ? TextView.BufferType.SPANNABLE : TextView.BufferType.NORMAL);
- // binding.tvSubComment.setMentionClickListener(mentionClickListener);
- if (model.getType() != NotificationType.REQUEST) {
+ binding.tvSubComment.setText(model.getType() == NotificationType.AYML ? model.getText() : subtext);
+ if (text == -1 && subtext != null) {
+ binding.tvComment.setText(subtext);
+ binding.tvComment.setVisibility(TextUtils.isEmpty(subtext) ? View.GONE : View.VISIBLE);
+ binding.tvSubComment.setVisibility(model.getType() == NotificationType.AYML ? View.VISIBLE : View.GONE);
+ }
+ else if (text != -1) {
+ binding.tvComment.setText(text);
+ binding.tvSubComment.setVisibility(subtext == null ? View.GONE : View.VISIBLE);
+ }
+
+ if (model.getType() != NotificationType.REQUEST && model.getType() != NotificationType.AYML) {
binding.tvDate.setText(model.getDateTime());
}
+
+ binding.tvUsername.setText(model.getUsername());
binding.ivProfilePic.setImageURI(model.getProfilePic());
- if (TextUtils.isEmpty(model.getPreviewPic())) {
+ binding.ivProfilePic.setOnClickListener(v -> {
+ if (notificationClickListener == null) return;
+ notificationClickListener.onProfileClick(model.getUsername());
+ });
+
+ if (model.getType() == NotificationType.AYML) {
binding.ivPreviewPic.setVisibility(View.GONE);
+ }
+ else if (TextUtils.isEmpty(model.getPreviewPic())) {
+ binding.ivPreviewPic.setVisibility(View.INVISIBLE);
} else {
binding.ivPreviewPic.setVisibility(View.VISIBLE);
binding.ivPreviewPic.setImageURI(model.getPreviewPic());
+ binding.ivPreviewPic.setOnClickListener(v -> {
+ if (notificationClickListener == null) return;
+ notificationClickListener.onPreviewClick(model);
+ });
}
+
+ itemView.setOnClickListener(v -> {
+ if (notificationClickListener == null) return;
+ notificationClickListener.onNotificationClick(model);
+ });
}
}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/PostViewerViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/PostViewerViewHolder.java
deleted file mode 100644
index dcb2f9fc..00000000
--- a/app/src/main/java/awais/instagrabber/adapters/viewholder/PostViewerViewHolder.java
+++ /dev/null
@@ -1,240 +0,0 @@
-// package awais.instagrabber.adapters.viewholder;
-//
-// import android.content.res.ColorStateList;
-// import android.content.res.Resources;
-// import android.util.Log;
-// import android.view.View;
-// import android.widget.TextView;
-//
-// import androidx.annotation.NonNull;
-// import androidx.core.content.ContextCompat;
-// import androidx.core.view.ViewCompat;
-// import androidx.recyclerview.widget.RecyclerView;
-// import androidx.viewpager2.widget.ViewPager2;
-//
-// import com.google.android.exoplayer2.Player;
-// import com.google.android.exoplayer2.SimpleExoPlayer;
-// import com.google.android.exoplayer2.ui.PlayerView;
-//
-// import java.util.ArrayList;
-// import java.util.List;
-//
-// import awais.instagrabber.R;
-// import awais.instagrabber.adapters.PostViewAdapter.OnPostCaptionLongClickListener;
-// import awais.instagrabber.adapters.PostViewAdapter.OnPostViewChildViewClickListener;
-// import awais.instagrabber.adapters.PostViewerChildAdapter;
-// import awais.instagrabber.databinding.ItemFullPostViewBinding;
-// import awais.instagrabber.interfaces.MentionClickListener;
-// import awais.instagrabber.models.PostChild;
-// import awais.instagrabber.models.ProfileModel;
-// import awais.instagrabber.models.ViewerPostModel;
-// import awais.instagrabber.models.ViewerPostModelWrapper;
-// import awais.instagrabber.models.enums.MediaItemType;
-// import awais.instagrabber.utils.TextUtils;
-// import awais.instagrabber.utils.Utils;
-//
-// public class PostViewerViewHolder extends RecyclerView.ViewHolder {
-// private static final String TAG = "PostViewerViewHolder";
-//
-// private final ItemFullPostViewBinding binding;
-// private int currentChildPosition;
-//
-// public PostViewerViewHolder(@NonNull final ItemFullPostViewBinding binding) {
-// super(binding.getRoot());
-// this.binding = binding;
-// binding.topPanel.viewStoryPost.setVisibility(View.GONE);
-// }
-//
-// public void bind(final ViewerPostModelWrapper wrapper,
-// final int position,
-// final OnPostViewChildViewClickListener clickListener,
-// final OnPostCaptionLongClickListener longClickListener,
-// final MentionClickListener mentionClickListener) {
-// if (wrapper == null) return;
-// final List items = wrapper.getViewerPostModels();
-// if (items == null || items.size() == 0) return;
-// if (items.get(0) == null) return;
-// final PostViewerChildAdapter adapter = new PostViewerChildAdapter();
-// binding.mediaViewPager.setAdapter(adapter);
-// final PostChild firstPost = items.get(0);
-// setPostInfo(firstPost, mentionClickListener);
-// setMediaItems(items, adapter);
-// setupListeners(wrapper,
-// position,
-// clickListener,
-// longClickListener,
-// mentionClickListener,
-// firstPost.getLocation());
-// }
-//
-// private void setPostInfo(final PostChild firstPost,
-// final MentionClickListener mentionClickListener) {
-// final ProfileModel profileModel = firstPost.getProfileModel();
-// if (profileModel == null) return;
-// binding.topPanel.title.setText(profileModel.getUsername());
-// final String locationName = firstPost.getLocationName();
-// if (!TextUtils.isEmpty(locationName)) {
-// binding.topPanel.location.setVisibility(View.VISIBLE);
-// binding.topPanel.location.setText(locationName);
-// } else binding.topPanel.location.setVisibility(View.GONE);
-// binding.topPanel.ivProfilePic.setImageURI(profileModel.getSdProfilePic());
-// binding.bottomPanel.commentsCount.setText(String.valueOf(firstPost.getCommentsCount()));
-// final CharSequence postCaption = firstPost.getPostCaption();
-// if (TextUtils.hasMentions(postCaption)) {
-// binding.bottomPanel.viewerCaption.setMentionClickListener(mentionClickListener);
-// binding.bottomPanel.viewerCaption
-// .setText(TextUtils.getMentionText(postCaption), TextView.BufferType.SPANNABLE);
-// } else {
-// binding.bottomPanel.viewerCaption.setMentionClickListener(null);
-// binding.bottomPanel.viewerCaption.setText(postCaption);
-// }
-// binding.bottomPanel.tvPostDate.setText(firstPost.getPostDate());
-// setupLikes(firstPost);
-// setupSave(firstPost);
-// }
-//
-// private void setupLikes(final ViewerPostModel firstPost) {
-// final boolean liked = firstPost.getLike();
-// final long likeCount = firstPost.getLikes();
-// final Resources resources = itemView.getContext().getResources();
-// if (liked) {
-// final String unlikeString = resources.getString(R.string.unlike, String.valueOf(likeCount));
-// binding.btnLike.setText(unlikeString);
-// ViewCompat.setBackgroundTintList(binding.btnLike,
-// ColorStateList.valueOf(ContextCompat.getColor(itemView.getContext(), R.color.btn_pink_background)));
-// } else {
-// final String likeString = resources.getString(R.string.like, String.valueOf(likeCount));
-// binding.btnLike.setText(likeString);
-// ViewCompat.setBackgroundTintList(binding.btnLike,
-// ColorStateList.valueOf(ContextCompat.getColor(itemView.getContext(), R.color.btn_lightpink_background)));
-// }
-// }
-//
-// private void setupSave(final ViewerPostModel firstPost) {
-// final boolean saved = firstPost.isSaved();
-// if (saved) {
-// binding.btnBookmark.setText(R.string.unbookmark);
-// ViewCompat.setBackgroundTintList(binding.btnBookmark,
-// ColorStateList.valueOf(ContextCompat.getColor(itemView.getContext(), R.color.btn_orange_background)));
-// } else {
-// binding.btnBookmark.setText(R.string.bookmark);
-// ViewCompat.setBackgroundTintList(
-// binding.btnBookmark,
-// ColorStateList.valueOf(ContextCompat.getColor(itemView.getContext(), R.color.btn_lightorange_background)));
-// }
-// }
-//
-// private void setupListeners(final ViewerPostModelWrapper wrapper,
-// final int position,
-// final OnPostViewChildViewClickListener clickListener,
-// final OnPostCaptionLongClickListener longClickListener,
-// final MentionClickListener mentionClickListener,
-// final String location) {
-// final View.OnClickListener onClickListener = v -> clickListener
-// .onClick(v, wrapper, position, currentChildPosition);
-// binding.bottomPanel.btnComments.setOnClickListener(onClickListener);
-// binding.topPanel.title.setOnClickListener(onClickListener);
-// binding.topPanel.ivProfilePic.setOnClickListener(onClickListener);
-// binding.bottomPanel.btnDownload.setOnClickListener(onClickListener);
-// binding.bottomPanel.viewerCaption.setOnClickListener(onClickListener);
-// binding.btnLike.setOnClickListener(onClickListener);
-// binding.btnBookmark.setOnClickListener(onClickListener);
-// binding.bottomPanel.viewerCaption.setOnLongClickListener(v -> {
-// longClickListener.onLongClick(binding.bottomPanel.viewerCaption.getText().toString());
-// return true;
-// });
-// if (!TextUtils.isEmpty(location)) {
-// binding.topPanel.location.setOnClickListener(v -> mentionClickListener
-// .onClick(binding.topPanel.location, location, false, true));
-// }
-// }
-//
-// private void setMediaItems(final List items,
-// final PostViewerChildAdapter adapter) {
-// final List filteredList = new ArrayList<>();
-// for (final ViewerPostModel model : items) {
-// final MediaItemType itemType = model.getItemType();
-// if (itemType == MediaItemType.MEDIA_TYPE_VIDEO || itemType == MediaItemType.MEDIA_TYPE_IMAGE) {
-// filteredList.add(model);
-// }
-// }
-// binding.mediaCounter.setVisibility(filteredList.size() > 1 ? View.VISIBLE : View.GONE);
-// final String counter = "1/" + filteredList.size();
-// binding.mediaCounter.setText(counter);
-// adapter.submitList(filteredList);
-// binding.mediaViewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
-// @Override
-// public void onPageSelected(final int position) {
-// if (filteredList.size() <= 0 || position >= filteredList.size()) return;
-// currentChildPosition = position;
-// final String counter = (position + 1) + "/" + filteredList.size();
-// binding.mediaCounter.setText(counter);
-// final ViewerPostModel viewerPostModel = filteredList.get(position);
-// if (viewerPostModel.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO) {
-// setVideoDetails(viewerPostModel);
-// setVolumeListener(position);
-// return;
-// }
-// setImageDetails();
-// }
-// });
-// }
-//
-// private void setVolumeListener(final int position) {
-// binding.bottomPanel.btnMute.setOnClickListener(v -> {
-// try {
-// final RecyclerView.ViewHolder viewHolder = ((RecyclerView) binding.mediaViewPager
-// .getChildAt(0)).findViewHolderForAdapterPosition(position);
-// if (viewHolder != null) {
-// final View itemView = viewHolder.itemView;
-// if (itemView instanceof PlayerView) {
-// final SimpleExoPlayer player = (SimpleExoPlayer) ((PlayerView) itemView)
-// .getPlayer();
-// if (player == null) return;
-// final float vol = player.getVolume() == 0f ? 1f : 0f;
-// player.setVolume(vol);
-// binding.bottomPanel.btnMute.setImageResource(vol == 0f ? R.drawable.ic_volume_up_24
-// : R.drawable.ic_volume_off_24);
-// Utils.sessionVolumeFull = vol == 1f;
-// }
-// }
-// } catch (Exception e) {
-// Log.e(TAG, "Error", e);
-// }
-// });
-// }
-//
-// private void setImageDetails() {
-// binding.bottomPanel.btnMute.setVisibility(View.GONE);
-// binding.bottomPanel.videoViewsContainer.setVisibility(View.GONE);
-// }
-//
-// private void setVideoDetails(final ViewerPostModel viewerPostModel) {
-// binding.bottomPanel.btnMute.setVisibility(View.VISIBLE);
-// final long videoViews = viewerPostModel.getVideoViews();
-// if (videoViews < 0) {
-// binding.bottomPanel.videoViewsContainer.setVisibility(View.GONE);
-// return;
-// }
-// binding.bottomPanel.tvVideoViews.setText(String.valueOf(videoViews));
-// binding.bottomPanel.videoViewsContainer.setVisibility(View.VISIBLE);
-// }
-//
-// public void stopPlayingVideo() {
-// try {
-// final RecyclerView.ViewHolder viewHolder = ((RecyclerView) binding.mediaViewPager
-// .getChildAt(0)).findViewHolderForAdapterPosition(currentChildPosition);
-// if (viewHolder != null) {
-// final View itemView = viewHolder.itemView;
-// if (itemView instanceof PlayerView) {
-// final Player player = ((PlayerView) itemView).getPlayer();
-// if (player != null) {
-// player.setPlayWhenReady(false);
-// }
-// }
-// }
-// } catch (Exception e) {
-// Log.e(TAG, "Error", e);
-// }
-// }
-// }
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/StoryListViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/StoryListViewHolder.java
new file mode 100644
index 00000000..b2bb5d17
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/StoryListViewHolder.java
@@ -0,0 +1,85 @@
+package awais.instagrabber.adapters.viewholder;
+
+import android.text.TextUtils;
+import android.view.View;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import awais.instagrabber.R;
+import awais.instagrabber.adapters.FeedStoriesListAdapter.OnFeedStoryClickListener;
+import awais.instagrabber.adapters.HighlightStoriesListAdapter.OnHighlightStoryClickListener;
+import awais.instagrabber.databinding.ItemNotificationBinding;
+import awais.instagrabber.models.FeedStoryModel;
+import awais.instagrabber.models.HighlightModel;
+
+public final class StoryListViewHolder extends RecyclerView.ViewHolder {
+ private final ItemNotificationBinding binding;
+
+ public StoryListViewHolder(final ItemNotificationBinding binding) {
+ super(binding.getRoot());
+ this.binding = binding;
+ }
+
+ public void bind(final FeedStoryModel model,
+ final int position,
+ final OnFeedStoryClickListener notificationClickListener) {
+ if (model == null) return;
+
+ final int storiesCount = model.getMediaCount();
+ binding.tvComment.setVisibility(View.VISIBLE);
+ binding.tvComment.setText(itemView.getResources().getQuantityString(R.plurals.stories_count, storiesCount, storiesCount));
+
+ binding.tvSubComment.setVisibility(View.GONE);
+
+ binding.tvDate.setText(model.getDateTime());
+
+ binding.tvUsername.setText(model.getProfileModel().getUsername());
+ binding.ivProfilePic.setImageURI(model.getProfileModel().getSdProfilePic());
+ binding.ivProfilePic.setOnClickListener(v -> {
+ if (notificationClickListener == null) return;
+ notificationClickListener.onProfileClick(model.getProfileModel().getUsername());
+ });
+
+ if (model.getFirstStoryModel() != null) {
+ binding.ivPreviewPic.setVisibility(View.VISIBLE);
+ binding.ivPreviewPic.setImageURI(model.getFirstStoryModel().getThumbnail());
+ }
+ else binding.ivPreviewPic.setVisibility(View.INVISIBLE);
+
+ float alpha = model.isFullyRead() ? 0.5F : 1.0F;
+ binding.ivProfilePic.setAlpha(alpha);
+ binding.ivPreviewPic.setAlpha(alpha);
+ binding.tvUsername.setAlpha(alpha);
+ binding.tvComment.setAlpha(alpha);
+ binding.tvDate.setAlpha(alpha);
+
+ itemView.setOnClickListener(v -> {
+ if (notificationClickListener == null) return;
+ notificationClickListener.onFeedStoryClick(model, position);
+ });
+ }
+
+ public void bind(final HighlightModel model,
+ final int position,
+ final OnHighlightStoryClickListener notificationClickListener) {
+ if (model == null) return;
+
+ final int storiesCount = model.getMediaCount();
+ binding.tvComment.setVisibility(View.VISIBLE);
+ binding.tvComment.setText(itemView.getResources().getQuantityString(R.plurals.stories_count, storiesCount, storiesCount));
+
+ binding.tvSubComment.setVisibility(View.GONE);
+
+ binding.tvUsername.setText(model.getDateTime());
+
+ binding.ivProfilePic.setVisibility(View.GONE);
+
+ binding.ivPreviewPic.setVisibility(View.VISIBLE);
+ binding.ivPreviewPic.setImageURI(model.getThumbnailUrl());
+
+ itemView.setOnClickListener(v -> {
+ if (notificationClickListener == null) return;
+ notificationClickListener.onHighlightClick(model, position);
+ });
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/comments/ChildCommentViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/comments/ChildCommentViewHolder.java
index bc5aecd8..8e9a7941 100644
--- a/app/src/main/java/awais/instagrabber/adapters/viewholder/comments/ChildCommentViewHolder.java
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/comments/ChildCommentViewHolder.java
@@ -89,8 +89,7 @@ public final class ChildCommentViewHolder extends RecyclerView.ViewHolder {
public final void setLiked(final boolean liked) {
if (liked) {
- // container.setBackgroundColor(0x40FF69B4);
- return;
+ itemView.setBackgroundColor(itemView.getResources().getColor(R.color.comment_liked));
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/comments/ParentCommentViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/comments/ParentCommentViewHolder.java
index 2b595d9e..20637801 100644
--- a/app/src/main/java/awais/instagrabber/adapters/viewholder/comments/ParentCommentViewHolder.java
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/comments/ParentCommentViewHolder.java
@@ -89,8 +89,7 @@ public final class ParentCommentViewHolder extends RecyclerView.ViewHolder {
public final void setLiked(final boolean liked) {
if (liked) {
- // container.setBackgroundColor(0x40FF69B4);
- return;
+ itemView.setBackgroundColor(itemView.getResources().getColor(R.color.comment_liked));
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/asyncs/CommentsFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/CommentsFetcher.java
index 7c1a5c43..d9f3cb65 100755
--- a/app/src/main/java/awais/instagrabber/asyncs/CommentsFetcher.java
+++ b/app/src/main/java/awais/instagrabber/asyncs/CommentsFetcher.java
@@ -28,11 +28,12 @@ import static awais.instagrabber.utils.Utils.logCollector;
public final class CommentsFetcher extends AsyncTask> {
private static final String TAG = "CommentsFetcher";
- private final String shortCode;
+ private final String shortCode, endCursor;
private final FetchListener> fetchListener;
- public CommentsFetcher(final String shortCode, final FetchListener> fetchListener) {
+ public CommentsFetcher(final String shortCode, final String endCursor, final FetchListener> fetchListener) {
this.shortCode = shortCode;
+ this.endCursor = endCursor;
this.fetchListener = fetchListener;
}
@@ -48,15 +49,17 @@ public final class CommentsFetcher extends AsyncTask commentModels = getParentComments();
- for (final CommentModel commentModel : commentModels) {
- final List childCommentModels = commentModel.getChildCommentModels();
- if (childCommentModels != null) {
- final int childCommentsLen = childCommentModels.size();
- final CommentModel lastChild = childCommentModels.get(childCommentsLen - 1);
- if (lastChild != null && lastChild.hasNextPage() && !TextUtils.isEmpty(lastChild.getEndCursor())) {
- final List remoteChildComments = getChildComments(commentModel.getId());
- commentModel.setChildCommentModels(remoteChildComments);
- lastChild.setPageCursor(false, null);
+ if (commentModels != null) {
+ for (final CommentModel commentModel : commentModels) {
+ final List childCommentModels = commentModel.getChildCommentModels();
+ if (childCommentModels != null) {
+ final int childCommentsLen = childCommentModels.size();
+ final CommentModel lastChild = childCommentModels.get(childCommentsLen - 1);
+ if (lastChild != null && lastChild.hasNextPage() && !TextUtils.isEmpty(lastChild.getEndCursor())) {
+ final List remoteChildComments = getChildComments(commentModel.getId());
+ commentModel.setChildCommentModels(remoteChildComments);
+ lastChild.setPageCursor(false, null);
+ }
}
}
}
@@ -76,11 +79,10 @@ public final class CommentsFetcher extends AsyncTask getChildComments(final String commentId) {
final List commentModels = new ArrayList<>();
- String endCursor = "";
- while (endCursor != null) {
+ String childEndCursor = "";
+ while (childEndCursor != null) {
final String url = "https://www.instagram.com/graphql/query/?query_hash=51fdd02b67508306ad4484ff574a0b62&variables=" +
- "{\"comment_id\":\"" + commentId + "\",\"first\":50,\"after\":\"" + endCursor + "\"}";
-
+ "{\"comment_id\":\"" + commentId + "\",\"first\":50,\"after\":\"" + childEndCursor + "\"}";
try {
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setUseCaches(false);
@@ -93,8 +95,8 @@ public final class CommentsFetcher extends AsyncTask("commentModels.size", commentModels.size()));
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
+ if (fetchListener != null) fetchListener.onFailure(e);
break;
}
}
@@ -152,17 +156,14 @@ public final class CommentsFetcher extends AsyncTask getParentComments() {
final List commentModels = new ArrayList<>();
- String endCursor = "";
- while (endCursor != null) {
final String url = "https://www.instagram.com/graphql/query/?query_hash=bc3296d1ce80a24b1b6e40b1e72903f5&variables=" +
- "{\"shortcode\":\"" + shortCode + "\",\"first\":50,\"after\":\"" + endCursor + "\"}";
-
- try {
+ "{\"shortcode\":\"" + shortCode + "\",\"first\":50,\"after\":\"" + endCursor.replace("\"", "\\\"") + "\"}";
+ try {
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setUseCaches(false);
conn.connect();
- if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) break;
+ if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) return null;
else {
final JSONObject parentComments = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("data")
.getJSONObject("shortcode_media")
@@ -170,8 +171,8 @@ public final class CommentsFetcher extends AsyncTask 0) {
final String childEndCursor;
- final boolean hasNextPage;
+ final boolean childHasNextPage;
if ((tempJsonObject = tempJsonObject.optJSONObject("page_info")) != null) {
childEndCursor = tempJsonObject.optString("end_cursor");
- hasNextPage = tempJsonObject.optBoolean("has_next_page", !TextUtils.isEmpty(childEndCursor));
+ childHasNextPage = tempJsonObject.optBoolean("has_next_page", !TextUtils.isEmpty(childEndCursor));
} else {
childEndCursor = null;
- hasNextPage = false;
+ childHasNextPage = false;
}
final List childCommentModels = new ArrayList<>();
@@ -257,6 +261,7 @@ public final class CommentsFetcher extends AsyncTask("commentModelsList.size", commentModels.size()));
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
- break;
+ if (fetchListener != null) fetchListener.onFailure(e);
+ return null;
}
- }
return commentModels;
}
}
diff --git a/app/src/main/java/awais/instagrabber/asyncs/FeedPostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/FeedPostFetchService.java
index d2bba0ce..b45a224a 100644
--- a/app/src/main/java/awais/instagrabber/asyncs/FeedPostFetchService.java
+++ b/app/src/main/java/awais/instagrabber/asyncs/FeedPostFetchService.java
@@ -1,34 +1,61 @@
package awais.instagrabber.asyncs;
+//import android.os.Handler;
+//import android.util.Log;
+
+import java.util.ArrayList;
import java.util.List;
import awais.instagrabber.customviews.helpers.PostFetcher;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.repositories.responses.PostsFetchResponse;
+import awais.instagrabber.utils.Constants;
+import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.webservices.FeedService;
import awais.instagrabber.webservices.ServiceCallback;
+import static awais.instagrabber.utils.Utils.settingsHelper;
+
public class FeedPostFetchService implements PostFetcher.PostFetchService {
private static final String TAG = "FeedPostFetchService";
private final FeedService feedService;
private String nextCursor;
+// private final Handler handler;
private boolean hasNextPage;
+// private static final int DELAY_MILLIS = 500;
public FeedPostFetchService() {
feedService = FeedService.getInstance();
+// handler = new Handler();
}
@Override
public void fetch(final FetchListener> fetchListener) {
- feedService.fetch(25, nextCursor, new ServiceCallback() {
+ final List feedModels = new ArrayList<>();
+ final String cookie = settingsHelper.getString(Constants.COOKIE);
+ final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
+ feedModels.clear();
+ feedService.fetch(csrfToken, nextCursor, new ServiceCallback() {
@Override
public void onSuccess(final PostsFetchResponse result) {
- if (result == null) return;
+ if (result == null && feedModels.size() > 0) {
+ fetchListener.onResult(feedModels);
+ return;
+ }
+ else if (result == null) return;
nextCursor = result.getNextCursor();
hasNextPage = result.hasNextPage();
+ feedModels.addAll(result.getFeedModels());
if (fetchListener != null) {
- fetchListener.onResult(result.getFeedModels());
+ if (feedModels.size() < 15 && hasNextPage) {
+// handler.postDelayed(() -> {
+ feedService.fetch(csrfToken, nextCursor, this);
+// }, DELAY_MILLIS);
+ }
+ else {
+ fetchListener.onResult(feedModels);
+ }
}
}
diff --git a/app/src/main/java/awais/instagrabber/asyncs/FeedStoriesFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/FeedStoriesFetcher.java
deleted file mode 100755
index c975dc5d..00000000
--- a/app/src/main/java/awais/instagrabber/asyncs/FeedStoriesFetcher.java
+++ /dev/null
@@ -1,93 +0,0 @@
-package awais.instagrabber.asyncs;
-
-import android.os.AsyncTask;
-import android.util.Log;
-
-import org.json.JSONArray;
-import org.json.JSONObject;
-
-import java.net.HttpURLConnection;
-import java.net.URL;
-
-import awais.instagrabber.BuildConfig;
-import awais.instagrabber.interfaces.FetchListener;
-import awais.instagrabber.models.FeedStoryModel;
-import awais.instagrabber.models.ProfileModel;
-import awais.instagrabber.utils.Constants;
-import awais.instagrabber.utils.NetworkUtils;
-import awaisomereport.LogCollector.LogFile;
-
-import static awais.instagrabber.utils.Utils.logCollector;
-
-public final class FeedStoriesFetcher extends AsyncTask {
- private final FetchListener fetchListener;
-
- public FeedStoriesFetcher(final FetchListener fetchListener) {
- this.fetchListener = fetchListener;
- }
-
- @Override
- protected FeedStoryModel[] doInBackground(final Void... voids) {
- FeedStoryModel[] result = null;
- String url = "https://www.instagram.com/graphql/query/?query_hash=b7b84d884400bc5aa7cfe12ae843a091&variables=" +
- "{\"only_stories\":true,\"stories_prefetch\":false,\"stories_video_dash_manifest\":false}";
-
- try {
- HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
- conn.setInstanceFollowRedirects(false);
- conn.setUseCaches(false);
- conn.connect();
-
- if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
- final JSONArray feedStoriesReel = new JSONObject(NetworkUtils.readFromConnection(conn))
- .getJSONObject("data")
- .getJSONObject(Constants.EXTRAS_USER)
- .getJSONObject("feed_reels_tray")
- .getJSONObject("edge_reels_tray_to_reel")
- .getJSONArray("edges");
-
- conn.disconnect();
-
- final int storiesLen = feedStoriesReel.length();
- final FeedStoryModel[] feedStoryModels = new FeedStoryModel[storiesLen];
- final String[] feedStoryIDs = new String[storiesLen];
-
- for (int i = 0; i < storiesLen; ++i) {
- final JSONObject node = feedStoriesReel.getJSONObject(i).getJSONObject("node");
-
- final JSONObject user = node.getJSONObject(node.has("user") ? "user" : "owner");
- final ProfileModel profileModel = new ProfileModel(false, false, false,
- user.getString("id"),
- user.getString("username"),
- null, null, null,
- user.getString("profile_pic_url"),
- null, 0, 0, 0, false, false, false, false);
-
- final String id = node.getString("id");
- final boolean fullyRead = !node.isNull("seen") && node.getLong("seen") == node.getLong("latest_reel_media");
- feedStoryIDs[i] = id;
- feedStoryModels[i] = new FeedStoryModel(id, profileModel, fullyRead);
- }
- result = feedStoryModels;
- }
-
- conn.disconnect();
- } catch (final Exception e) {
- if (logCollector != null)
- logCollector.appendException(e, LogFile.ASYNC_FEED_STORY_FETCHER, "doInBackground");
- if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
- }
-
- return result;
- }
-
- @Override
- protected void onPreExecute() {
- if (fetchListener != null) fetchListener.doBefore();
- }
-
- @Override
- protected void onPostExecute(final FeedStoryModel[] result) {
- if (fetchListener != null) fetchListener.onResult(result);
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/asyncs/GetActivityAsyncTask.java b/app/src/main/java/awais/instagrabber/asyncs/GetActivityAsyncTask.java
index ff65a56b..1987443b 100644
--- a/app/src/main/java/awais/instagrabber/asyncs/GetActivityAsyncTask.java
+++ b/app/src/main/java/awais/instagrabber/asyncs/GetActivityAsyncTask.java
@@ -16,12 +16,21 @@ import awais.instagrabber.utils.TextUtils;
public class GetActivityAsyncTask extends AsyncTask {
private static final String TAG = "GetActivityAsyncTask";
- private OnTaskCompleteListener onTaskCompleteListener;
+ private final OnTaskCompleteListener onTaskCompleteListener;
public GetActivityAsyncTask(final OnTaskCompleteListener onTaskCompleteListener) {
this.onTaskCompleteListener = onTaskCompleteListener;
}
+ /*
+ This needs to be redone to fetch i inbox instead
+ Within inbox, data is (body JSON => counts)
+ Then we have these counts:
+ new_posts, activity_feed_dot_badge, relationships, campaign_notification
+ usertags, likes, comment_likes, shopping_notification, comments
+ photos_of_you (not sure about difference to usertags), requests
+ */
+
protected NotificationCounts doInBackground(final String... cookiesArray) {
if (cookiesArray == null) return null;
final String cookie = cookiesArray[0];
@@ -70,11 +79,11 @@ public class GetActivityAsyncTask extends AsyncTask {
if (logCollector != null)
logCollector.appendException(e, LogCollector.LogFile.ASYNC_HASHTAG_FETCHER, "doInBackground");
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
+ if (fetchListener != null) fetchListener.onFailure(e);
}
return result;
diff --git a/app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java
index 4c12f16e..55f1bc40 100644
--- a/app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java
+++ b/app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java
@@ -6,12 +6,14 @@ import awais.instagrabber.customviews.helpers.PostFetcher;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.HashtagModel;
+import awais.instagrabber.repositories.responses.PostsFetchResponse;
+import awais.instagrabber.webservices.GraphQLService;
import awais.instagrabber.webservices.ServiceCallback;
import awais.instagrabber.webservices.TagsService;
-import awais.instagrabber.webservices.TagsService.TagPostsFetchResponse;
public class HashtagPostFetchService implements PostFetcher.PostFetchService {
private final TagsService tagsService;
+ private final GraphQLService graphQLService;
private final HashtagModel hashtagModel;
private String nextMaxId;
private boolean moreAvailable;
@@ -20,19 +22,20 @@ public class HashtagPostFetchService implements PostFetcher.PostFetchService {
public HashtagPostFetchService(final HashtagModel hashtagModel, final boolean isLoggedIn) {
this.hashtagModel = hashtagModel;
this.isLoggedIn = isLoggedIn;
- tagsService = TagsService.getInstance();
+ tagsService = isLoggedIn ? TagsService.getInstance() : null;
+ graphQLService = isLoggedIn ? null : GraphQLService.getInstance();
}
@Override
public void fetch(final FetchListener> fetchListener) {
- final ServiceCallback cb = new ServiceCallback() {
+ final ServiceCallback cb = new ServiceCallback() {
@Override
- public void onSuccess(final TagPostsFetchResponse result) {
+ public void onSuccess(final PostsFetchResponse result) {
if (result == null) return;
- nextMaxId = result.getNextMaxId();
- moreAvailable = result.isMoreAvailable();
+ nextMaxId = result.getNextCursor();
+ moreAvailable = result.hasNextPage();
if (fetchListener != null) {
- fetchListener.onResult(result.getItems());
+ fetchListener.onResult(result.getFeedModels());
}
}
@@ -45,7 +48,7 @@ public class HashtagPostFetchService implements PostFetcher.PostFetchService {
}
};
if (isLoggedIn) tagsService.fetchPosts(hashtagModel.getName().toLowerCase(), nextMaxId, cb);
- else tagsService.fetchGraphQLPosts(hashtagModel.getName().toLowerCase(), nextMaxId, cb);
+ else graphQLService.fetchHashtagPosts(hashtagModel.getName().toLowerCase(), nextMaxId, cb);
}
@Override
diff --git a/app/src/main/java/awais/instagrabber/asyncs/HighlightsFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/HighlightsFetcher.java
deleted file mode 100755
index acb7b7c1..00000000
--- a/app/src/main/java/awais/instagrabber/asyncs/HighlightsFetcher.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package awais.instagrabber.asyncs;
-
-import android.os.AsyncTask;
-import android.util.Log;
-
-import org.json.JSONArray;
-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.HighlightModel;
-import awais.instagrabber.utils.Constants;
-import awais.instagrabber.utils.NetworkUtils;
-
-public final class HighlightsFetcher extends AsyncTask> {
- private final String id;
- private final FetchListener> fetchListener;
-
- public HighlightsFetcher(final String id, final FetchListener> fetchListener) {
- this.id = id;
- this.fetchListener = fetchListener;
- }
-
- @Override
- protected List doInBackground(final Void... voids) {
- List result = null;
- String url = "https://i.instagram.com/api/v1/highlights/" + id + "/highlights_tray/";
-
- try {
- HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
- conn.setInstanceFollowRedirects(false);
- conn.setUseCaches(false);
- conn.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
- conn.connect();
-
- if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
- final JSONArray highlightsReel = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONArray("tray");
-
- final int length = highlightsReel.length();
- final List highlightModels = new ArrayList<>();
- // final String[] highlightIds = new String[length];
- for (int i = 0; i < length; ++i) {
- final JSONObject highlightNode = highlightsReel.getJSONObject(i);
- highlightModels.add(new HighlightModel(
- highlightNode.getString("title"),
- highlightNode.getString(Constants.EXTRAS_ID),
- highlightNode.getJSONObject("cover_media")
- .getJSONObject("cropped_image_version")
- .getString("url")
- ));
- }
- conn.disconnect();
- result = highlightModels;
- }
-
- conn.disconnect();
- } catch (Exception e) {
- if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
- }
-
- return result;
- }
-
- @Override
- protected void onPostExecute(final List result) {
- if (fetchListener != null) fetchListener.onResult(result);
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java
index 2b504c50..7c7a12ce 100644
--- a/app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java
+++ b/app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java
@@ -6,12 +6,14 @@ import awais.instagrabber.customviews.helpers.PostFetcher;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.LocationModel;
+import awais.instagrabber.repositories.responses.PostsFetchResponse;
+import awais.instagrabber.webservices.GraphQLService;
import awais.instagrabber.webservices.LocationService;
-import awais.instagrabber.webservices.LocationService.LocationPostsFetchResponse;
import awais.instagrabber.webservices.ServiceCallback;
public class LocationPostFetchService implements PostFetcher.PostFetchService {
private final LocationService locationService;
+ private final GraphQLService graphQLService;
private final LocationModel locationModel;
private String nextMaxId;
private boolean moreAvailable;
@@ -20,19 +22,20 @@ public class LocationPostFetchService implements PostFetcher.PostFetchService {
public LocationPostFetchService(final LocationModel locationModel, final boolean isLoggedIn) {
this.locationModel = locationModel;
this.isLoggedIn = isLoggedIn;
- locationService = LocationService.getInstance();
+ locationService = isLoggedIn ? LocationService.getInstance() : null;
+ graphQLService = isLoggedIn ? null : GraphQLService.getInstance();
}
@Override
public void fetch(final FetchListener> fetchListener) {
- final ServiceCallback cb = new ServiceCallback() {
+ final ServiceCallback cb = new ServiceCallback() {
@Override
- public void onSuccess(final LocationPostsFetchResponse result) {
+ public void onSuccess(final PostsFetchResponse result) {
if (result == null) return;
- nextMaxId = result.getNextMaxId();
- moreAvailable = result.isMoreAvailable();
+ nextMaxId = result.getNextCursor();
+ moreAvailable = result.hasNextPage();
if (fetchListener != null) {
- fetchListener.onResult(result.getItems());
+ fetchListener.onResult(result.getFeedModels());
}
}
@@ -45,7 +48,7 @@ public class LocationPostFetchService implements PostFetcher.PostFetchService {
}
};
if (isLoggedIn) locationService.fetchPosts(locationModel.getId(), nextMaxId, cb);
- else locationService.fetchGraphQLPosts(locationModel.getId(), nextMaxId, cb);
+ else graphQLService.fetchLocationPosts(locationModel.getId(), nextMaxId, cb);
}
@Override
diff --git a/app/src/main/java/awais/instagrabber/asyncs/NotificationsFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/NotificationsFetcher.java
index 16e355df..714a2793 100644
--- a/app/src/main/java/awais/instagrabber/asyncs/NotificationsFetcher.java
+++ b/app/src/main/java/awais/instagrabber/asyncs/NotificationsFetcher.java
@@ -3,21 +3,14 @@ package awais.instagrabber.asyncs;
import android.os.AsyncTask;
import android.util.Log;
-import org.json.JSONArray;
-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.NotificationModel;
-import awais.instagrabber.models.enums.NotificationType;
-import awais.instagrabber.utils.Constants;
-import awais.instagrabber.utils.LocaleUtils;
-import awais.instagrabber.utils.NetworkUtils;
+import awais.instagrabber.webservices.NewsService;
+import awais.instagrabber.webservices.ServiceCallback;
import awaisomereport.LogCollector;
import static awais.instagrabber.utils.Utils.logCollector;
@@ -26,89 +19,48 @@ public final class NotificationsFetcher extends AsyncTask> fetchListener;
+ private final NewsService newsService;
+ private final boolean markAsSeen;
+ private boolean fetchedWeb = false;
- public NotificationsFetcher(final FetchListener> fetchListener) {
+ public NotificationsFetcher(final boolean markAsSeen,
+ final FetchListener> fetchListener) {
+ this.markAsSeen = markAsSeen;
this.fetchListener = fetchListener;
+ newsService = NewsService.getInstance();
}
@Override
protected List doInBackground(final Void... voids) {
- List result = new ArrayList<>();
- final String url = "https://www.instagram.com/accounts/activity/?__a=1";
- try {
- final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
- conn.setInstanceFollowRedirects(false);
- conn.setUseCaches(false);
- conn.setRequestProperty("Accept-Language", LocaleUtils.getCurrentLocale().getLanguage() + ",en-US;q=0.8");
- conn.connect();
+ List notificationModels = new ArrayList<>();
- if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
- final JSONObject page = new JSONObject(NetworkUtils.readFromConnection(conn))
- .getJSONObject("graphql")
- .getJSONObject("user");
- final JSONObject ewaf = page.getJSONObject("activity_feed")
- .optJSONObject("edge_web_activity_feed");
- final JSONObject efr = page.optJSONObject("edge_follow_requests");
- JSONObject data;
- JSONArray media;
- if (ewaf != null
- && (media = ewaf.optJSONArray("edges")) != null
- && media.length() > 0
- && media.optJSONObject(0).optJSONObject("node") != null) {
- for (int i = 0; i < media.length(); ++i) {
- data = media.optJSONObject(i).optJSONObject("node");
- if (data == null) continue;
- final String type = data.getString("__typename");
- final NotificationType notificationType = NotificationType.valueOfType(type);
- if (notificationType == null) continue;
- final JSONObject user = data.getJSONObject("user");
- result.add(new NotificationModel(
- data.getString(Constants.EXTRAS_ID),
- data.optString("text"), // comments or mentions
- data.getLong("timestamp"),
- user.getString("id"),
- user.getString("username"),
- user.getString("profile_pic_url"),
- !data.isNull("media") ? data.getJSONObject("media").getString("shortcode") : null,
- !data.isNull("media") ? data.getJSONObject("media").getString("thumbnail_src") : null, notificationType));
- }
+ newsService.fetchAppInbox(markAsSeen, new ServiceCallback>() {
+ @Override
+ public void onSuccess(final List result) {
+ if (result == null) return;
+ notificationModels.addAll(result);
+ if (fetchedWeb) {
+ fetchListener.onResult(notificationModels);
}
-
- if (efr != null
- && (media = efr.optJSONArray("edges")) != null
- && media.length() > 0
- && media.optJSONObject(0).optJSONObject("node") != null) {
- for (int i = 0; i < media.length(); ++i) {
- data = media.optJSONObject(i).optJSONObject("node");
- if (data == null) continue;
- result.add(new NotificationModel(
- data.getString(Constants.EXTRAS_ID),
- data.optString("full_name"),
- 0L,
- data.getString(Constants.EXTRAS_ID),
- data.getString("username"),
- data.getString("profile_pic_url"),
- null,
- null, NotificationType.REQUEST));
- }
+ else {
+ fetchedWeb = true;
+ newsService.fetchWebInbox(markAsSeen, this);
}
}
- conn.disconnect();
- } catch (final Exception e) {
- if (logCollector != null)
- logCollector.appendException(e, LogCollector.LogFile.ASYNC_NOTIFICATION_FETCHER, "doInBackground");
- if (BuildConfig.DEBUG) Log.e(TAG, "", e);
- }
- return result;
+
+ @Override
+ public void onFailure(final Throwable t) {
+ // Log.e(TAG, "onFailure: ", t);
+ if (fetchListener != null) {
+ fetchListener.onFailure(t);
+ }
+ }
+ });
+ return notificationModels;
}
@Override
protected void onPreExecute() {
if (fetchListener != null) fetchListener.doBefore();
}
-
- @Override
- protected void onPostExecute(final List result) {
- if (fetchListener != null) fetchListener.onResult(result);
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java
index f87e5103..2a721a61 100755
--- a/app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java
+++ b/app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java
@@ -65,6 +65,7 @@ public final class PostFetcher extends AsyncTask {
owner.optInt("edge_followed_by"),
-1,
owner.optBoolean("followed_by_viewer"),
+ false,
owner.optBoolean("restricted_by_viewer"),
owner.optBoolean("blocked_by_viewer"),
owner.optBoolean("requested_by_viewer")
diff --git a/app/src/main/java/awais/instagrabber/asyncs/ProfileFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/ProfileFetcher.java
index e2e4fe2c..7e78b0b2 100755
--- a/app/src/main/java/awais/instagrabber/asyncs/ProfileFetcher.java
+++ b/app/src/main/java/awais/instagrabber/asyncs/ProfileFetcher.java
@@ -72,6 +72,7 @@ public final class ProfileFetcher extends AsyncTask {
user.getJSONObject("edge_followed_by").getLong("count"),
user.getJSONObject("edge_follow").getLong("count"),
user.optBoolean("followed_by_viewer"),
+ user.optBoolean("follows_viewer"),
user.optBoolean("restricted_by_viewer"),
user.optBoolean("blocked_by_viewer"),
user.optBoolean("requested_by_viewer"));
diff --git a/app/src/main/java/awais/instagrabber/asyncs/ProfilePictureFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/ProfilePictureFetcher.java
index 6fe750a1..433697de 100755
--- a/app/src/main/java/awais/instagrabber/asyncs/ProfilePictureFetcher.java
+++ b/app/src/main/java/awais/instagrabber/asyncs/ProfilePictureFetcher.java
@@ -40,51 +40,34 @@ public final class ProfilePictureFetcher extends AsyncTask {
protected String doInBackground(final Void... voids) {
String out = null;
if (isHashtag) out = picUrl;
- else try {
- final HttpURLConnection conn =
- (HttpURLConnection) new URL("https://i.instagram.com/api/v1/users/"+userId+"/info/").openConnection();
- conn.setUseCaches(false);
- conn.setRequestMethod("GET");
- conn.setRequestProperty("User-Agent", Constants.USER_AGENT);
+ else if (Utils.settingsHelper.getBoolean(Constants.INSTADP)) try {
+ final HttpURLConnection backup =
+ (HttpURLConnection) new URL("https://instadp.com/fullsize/" + userName).openConnection();
+ backup.setUseCaches(false);
+ backup.setRequestMethod("GET");
+ backup.setRequestProperty("User-Agent", Constants.A_USER_AGENT);
- final String result = conn.getResponseCode() == HttpURLConnection.HTTP_OK ? NetworkUtils.readFromConnection(conn) : null;
- conn.disconnect();
+ final String instadp = backup.getResponseCode() == HttpURLConnection.HTTP_OK ? NetworkUtils.readFromConnection(backup) : null;
+ backup.disconnect();
- if (!TextUtils.isEmpty(result)) {
- JSONObject data = new JSONObject(result).getJSONObject("user");
- if (data.has("hd_profile_pic_url_info"))
- out = data.getJSONObject("hd_profile_pic_url_info").optString("url");
- }
+ if (!TextUtils.isEmpty(instadp)) {
+ final Document doc = Jsoup.parse(instadp);
+ boolean fallback = false;
- if (TextUtils.isEmpty(out) && Utils.settingsHelper.getBoolean(Constants.INSTADP)) {
- final HttpURLConnection backup =
- (HttpURLConnection) new URL("https://instadp.com/fullsize/" + userName).openConnection();
- backup.setUseCaches(false);
- backup.setRequestMethod("GET");
- backup.setRequestProperty("User-Agent", Constants.A_USER_AGENT);
+ final int imgIndex = instadp.indexOf("preloadImg('"), lastIndex;
- final String instadp = backup.getResponseCode() == HttpURLConnection.HTTP_OK ? NetworkUtils.readFromConnection(backup) : null;
- backup.disconnect();
-
- if (!TextUtils.isEmpty(instadp)) {
- final Document doc = Jsoup.parse(instadp);
- boolean fallback = false;
-
- final int imgIndex = instadp.indexOf("preloadImg('"), lastIndex;
-
- Element element = doc.selectFirst(".instadp");
- if (element != null && (element = element.selectFirst(".picture")) != null)
- out = element.attr("src");
- else if ((element = doc.selectFirst(".download-btn")) != null)
- out = element.attr("href");
- else if (imgIndex != -1 && (lastIndex = instadp.indexOf("')", imgIndex)) != -1)
- out = instadp.substring(imgIndex + 12, lastIndex);
- else {
- final Elements imgs = doc.getElementsByTag("img");
- for (final Element img : imgs) {
- final String imgStr = img.toString();
- if (imgStr.contains("cdninstagram.com")) out = img.attr("src");
- }
+ Element element = doc.selectFirst(".instadp");
+ if (element != null && (element = element.selectFirst(".picture")) != null)
+ out = element.attr("src");
+ else if ((element = doc.selectFirst(".download-btn")) != null)
+ out = element.attr("href");
+ else if (imgIndex != -1 && (lastIndex = instadp.indexOf("')", imgIndex)) != -1)
+ out = instadp.substring(imgIndex + 12, lastIndex);
+ else {
+ final Elements imgs = doc.getElementsByTag("img");
+ for (final Element img : imgs) {
+ final String imgStr = img.toString();
+ if (imgStr.contains("cdninstagram.com")) out = img.attr("src");
}
}
}
diff --git a/app/src/main/java/awais/instagrabber/asyncs/ProfilePostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/ProfilePostFetchService.java
index 3617cbd2..649d28d8 100644
--- a/app/src/main/java/awais/instagrabber/asyncs/ProfilePostFetchService.java
+++ b/app/src/main/java/awais/instagrabber/asyncs/ProfilePostFetchService.java
@@ -7,29 +7,34 @@ import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.repositories.responses.PostsFetchResponse;
+import awais.instagrabber.webservices.GraphQLService;
import awais.instagrabber.webservices.ProfileService;
import awais.instagrabber.webservices.ServiceCallback;
public class ProfilePostFetchService implements PostFetcher.PostFetchService {
private static final String TAG = "ProfilePostFetchService";
private final ProfileService profileService;
+ private final GraphQLService graphQLService;
private final ProfileModel profileModel;
- private String nextCursor;
- private boolean hasNextPage;
+ private final boolean isLoggedIn;
+ private String nextMaxId;
+ private boolean moreAvailable;
- public ProfilePostFetchService(final ProfileModel profileModel) {
+ public ProfilePostFetchService(final ProfileModel profileModel, final boolean isLoggedIn) {
this.profileModel = profileModel;
- profileService = ProfileService.getInstance();
+ this.isLoggedIn = isLoggedIn;
+ graphQLService = isLoggedIn ? null : GraphQLService.getInstance();
+ profileService = isLoggedIn ? ProfileService.getInstance() : null;
}
@Override
public void fetch(final FetchListener> fetchListener) {
- profileService.fetchPosts(profileModel, 30, nextCursor, new ServiceCallback() {
+ final ServiceCallback cb = new ServiceCallback() {
@Override
public void onSuccess(final PostsFetchResponse result) {
if (result == null) return;
- nextCursor = result.getNextCursor();
- hasNextPage = result.hasNextPage();
+ nextMaxId = result.getNextCursor();
+ moreAvailable = result.hasNextPage();
if (fetchListener != null) {
fetchListener.onResult(result.getFeedModels());
}
@@ -42,16 +47,18 @@ public class ProfilePostFetchService implements PostFetcher.PostFetchService {
fetchListener.onFailure(t);
}
}
- });
+ };
+ if (isLoggedIn) profileService.fetchPosts(profileModel.getId(), nextMaxId, cb);
+ else graphQLService.fetchProfilePosts(profileModel.getId(), 30, nextMaxId, cb);
}
@Override
public void reset() {
- nextCursor = null;
+ nextMaxId = null;
}
@Override
public boolean hasNextPage() {
- return hasNextPage;
+ return moreAvailable;
}
}
diff --git a/app/src/main/java/awais/instagrabber/asyncs/QuizAction.java b/app/src/main/java/awais/instagrabber/asyncs/QuizAction.java
deleted file mode 100644
index 4042b875..00000000
--- a/app/src/main/java/awais/instagrabber/asyncs/QuizAction.java
+++ /dev/null
@@ -1,89 +0,0 @@
-package awais.instagrabber.asyncs;
-
-import android.os.AsyncTask;
-import android.util.Log;
-
-import org.json.JSONObject;
-
-import java.io.DataOutputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.UUID;
-
-import awais.instagrabber.models.StoryModel;
-import awais.instagrabber.models.stickers.QuizModel;
-import awais.instagrabber.utils.Constants;
-import awais.instagrabber.utils.CookieUtils;
-import awais.instagrabber.utils.Utils;
-
-import static awais.instagrabber.utils.Utils.settingsHelper;
-
-public class QuizAction extends AsyncTask {
- private static final String TAG = "QuizAction";
-
- private final StoryModel storyModel;
- private final QuizModel quizModel;
- private final String cookie;
- private final OnTaskCompleteListener onTaskCompleteListener;
-
- public QuizAction(final StoryModel storyModel,
- final QuizModel quizModel,
- final String cookie,
- final OnTaskCompleteListener onTaskCompleteListener) {
- this.storyModel = storyModel;
- this.quizModel = quizModel;
- this.cookie = cookie;
- this.onTaskCompleteListener = onTaskCompleteListener;
- }
-
- protected Integer doInBackground(Integer... rawChoice) {
- int choice = rawChoice[0];
- final String url = "https://i.instagram.com/api/v1/media/" + storyModel.getStoryMediaId().split("_")[0] + "/" + quizModel.getId() + "/story_quiz_answer/";
- HttpURLConnection urlConnection = null;
- try {
- JSONObject ogBody = new JSONObject("{\"client_context\":\"" + UUID.randomUUID().toString()
- + "\",\"mutation_token\":\"" + UUID.randomUUID().toString()
- + "\",\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0]
- + "\",\"_uid\":\"" + CookieUtils.getUserIdFromCookie(cookie)
- + "\",\"__uuid\":\"" + settingsHelper.getString(Constants.DEVICE_UUID)
- + "\"}");
- ogBody.put("answer", String.valueOf(choice));
- String urlParameters = Utils.sign(ogBody.toString());
- urlConnection = (HttpURLConnection) new URL(url).openConnection();
- urlConnection.setRequestMethod("POST");
- urlConnection.setUseCaches(false);
- urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
- urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
- if (urlParameters != null) {
- urlConnection.setRequestProperty("Content-Length", Integer.toString(urlParameters.getBytes().length));
- }
- urlConnection.setDoOutput(true);
- DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
- wr.writeBytes(urlParameters);
- wr.flush();
- wr.close();
- Log.d(TAG, "quiz: " + url + " " + cookie + " " + urlParameters);
- urlConnection.connect();
- if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
- return choice;
- }
- } catch (Throwable ex) {
- Log.e(TAG, "quiz: " + ex);
- } finally {
- if (urlConnection != null) {
- urlConnection.disconnect();
- }
- }
- return -1;
- }
-
- @Override
- protected void onPostExecute(final Integer choice) {
- if (onTaskCompleteListener == null || choice == null) return;
- onTaskCompleteListener.onTaskComplete(choice);
- }
-
- public interface OnTaskCompleteListener {
- void onTaskComplete(final int choice);
- }
-}
diff --git a/app/src/main/java/awais/instagrabber/asyncs/RespondAction.java b/app/src/main/java/awais/instagrabber/asyncs/RespondAction.java
deleted file mode 100644
index 836d6b76..00000000
--- a/app/src/main/java/awais/instagrabber/asyncs/RespondAction.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package awais.instagrabber.asyncs;
-
-import android.os.AsyncTask;
-import android.util.Log;
-
-import org.json.JSONObject;
-
-import java.io.DataOutputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.UUID;
-
-import awais.instagrabber.models.StoryModel;
-import awais.instagrabber.models.stickers.QuestionModel;
-import awais.instagrabber.utils.Constants;
-import awais.instagrabber.utils.CookieUtils;
-import awais.instagrabber.utils.Utils;
-
-import static awais.instagrabber.utils.Utils.settingsHelper;
-
-public class RespondAction extends AsyncTask {
-
- private final StoryModel storyModel;
- private final QuestionModel questionModel;
- private final String cookie;
- private final OnTaskCompleteListener onTaskCompleteListener;
-
- public RespondAction(final StoryModel storyModel,
- final QuestionModel questionModel,
- final String cookie,
- final OnTaskCompleteListener onTaskCompleteListener) {
- this.storyModel = storyModel;
- this.questionModel = questionModel;
- this.cookie = cookie;
- this.onTaskCompleteListener = onTaskCompleteListener;
- }
-
- protected Boolean doInBackground(String... rawChoice) {
- final String url = "https://i.instagram.com/api/v1/media/"
- + storyModel.getStoryMediaId().split("_")[0] + "/" + questionModel.getId() + "/story_question_response/";
- HttpURLConnection urlConnection = null;
- try {
- final JSONObject ogbody = new JSONObject("{\"client_context\":\"" + UUID.randomUUID().toString()
- + "\",\"mutation_token\":\"" + UUID.randomUUID().toString()
- + "\",\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0]
- + "\",\"_uid\":\"" + CookieUtils.getUserIdFromCookie(cookie)
- + "\",\"__uuid\":\"" + settingsHelper.getString(Constants.DEVICE_UUID)
- + "\"}");
- String choice = rawChoice[0].replaceAll("\"", ("\\\""));
- ogbody.put("response", choice);
- String urlParameters = Utils.sign(ogbody.toString());
- urlConnection = (HttpURLConnection) new URL(url).openConnection();
- urlConnection.setRequestMethod("POST");
- urlConnection.setUseCaches(false);
- urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
- urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
- urlConnection.setRequestProperty("Content-Length", Integer.toString(urlParameters.getBytes().length));
- urlConnection.setDoOutput(true);
- DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
- wr.writeBytes(urlParameters);
- wr.flush();
- wr.close();
- urlConnection.connect();
- if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
- return true;
- }
-
- } catch (Throwable ex) {
- Log.e("austin_debug", "respond: " + ex);
- } finally {
- if (urlConnection != null) {
- urlConnection.disconnect();
- }
- }
- return null;
- }
-
- @Override
- protected void onPostExecute(final Boolean ok) {
- if (onTaskCompleteListener == null) return;
- onTaskCompleteListener.onTaskComplete(ok);
-
- }
-
- public interface OnTaskCompleteListener {
- void onTaskComplete(final boolean result);
- }
-}
diff --git a/app/src/main/java/awais/instagrabber/asyncs/SavedPostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/SavedPostFetchService.java
index 36c668d1..b13d842d 100644
--- a/app/src/main/java/awais/instagrabber/asyncs/SavedPostFetchService.java
+++ b/app/src/main/java/awais/instagrabber/asyncs/SavedPostFetchService.java
@@ -6,34 +6,39 @@ import awais.instagrabber.customviews.helpers.PostFetcher;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.enums.PostItemType;
+import awais.instagrabber.repositories.responses.PostsFetchResponse;
+import awais.instagrabber.webservices.GraphQLService;
import awais.instagrabber.webservices.ProfileService;
-import awais.instagrabber.webservices.ProfileService.SavedPostsFetchResponse;
import awais.instagrabber.webservices.ServiceCallback;
public class SavedPostFetchService implements PostFetcher.PostFetchService {
private final ProfileService profileService;
+ private final GraphQLService graphQLService;
private final String profileId;
private final PostItemType type;
+ private final boolean isLoggedIn;
private String nextMaxId;
private boolean moreAvailable;
- public SavedPostFetchService(final String profileId, final PostItemType type) {
+ public SavedPostFetchService(final String profileId, final PostItemType type, final boolean isLoggedIn) {
this.profileId = profileId;
this.type = type;
- profileService = ProfileService.getInstance();
+ this.isLoggedIn = isLoggedIn;
+ graphQLService = isLoggedIn ? null : GraphQLService.getInstance();
+ profileService = isLoggedIn ? ProfileService.getInstance() : null;
}
@Override
public void fetch(final FetchListener> fetchListener) {
- final ServiceCallback callback = new ServiceCallback() {
+ final ServiceCallback callback = new ServiceCallback() {
@Override
- public void onSuccess(final SavedPostsFetchResponse result) {
+ public void onSuccess(final PostsFetchResponse result) {
if (result == null) return;
- nextMaxId = result.getNextMaxId();
- moreAvailable = result.isMoreAvailable();
+ nextMaxId = result.getNextCursor();
+ moreAvailable = result.hasNextPage();
if (fetchListener != null) {
- fetchListener.onResult(result.getItems());
+ fetchListener.onResult(result.getFeedModels());
}
}
@@ -50,7 +55,8 @@ public class SavedPostFetchService implements PostFetcher.PostFetchService {
profileService.fetchLiked(nextMaxId, callback);
break;
case TAGGED:
- profileService.fetchTagged(profileId, nextMaxId, callback);
+ if (isLoggedIn) profileService.fetchTagged(profileId, nextMaxId, callback);
+ else graphQLService.fetchTaggedPosts(profileId, 30, nextMaxId, callback);
break;
case SAVED:
default:
diff --git a/app/src/main/java/awais/instagrabber/asyncs/VoteAction.java b/app/src/main/java/awais/instagrabber/asyncs/VoteAction.java
deleted file mode 100644
index cfa91c77..00000000
--- a/app/src/main/java/awais/instagrabber/asyncs/VoteAction.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package awais.instagrabber.asyncs;
-
-import android.os.AsyncTask;
-import android.util.Log;
-
-import java.io.DataOutputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-
-import awais.instagrabber.models.StoryModel;
-import awais.instagrabber.models.stickers.PollModel;
-import awais.instagrabber.utils.Constants;
-
-public class VoteAction extends AsyncTask {
-
- private static final String TAG = "VoteAction";
-
- private final StoryModel storyModel;
- private final PollModel pollModel;
- private final String cookie;
- private final OnTaskCompleteListener onTaskCompleteListener;
-
- public VoteAction(final StoryModel storyModel,
- final PollModel pollModel,
- final String cookie,
- final OnTaskCompleteListener onTaskCompleteListener) {
- this.storyModel = storyModel;
- this.pollModel = pollModel;
- this.cookie = cookie;
- this.onTaskCompleteListener = onTaskCompleteListener;
- }
-
- protected Integer doInBackground(Integer... rawChoice) {
- int choice = rawChoice[0];
- final String url = "https://www.instagram.com/media/" + storyModel.getStoryMediaId().split("_")[0] + "/" + pollModel.getId() + "/story_poll_vote/";
- try {
- final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
- urlConnection.setRequestMethod("POST");
- urlConnection.setUseCaches(false);
- urlConnection.setRequestProperty("User-Agent", Constants.USER_AGENT);
- urlConnection.setRequestProperty("x-csrftoken", cookie.split("csrftoken=")[1].split(";")[0]);
- urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
- urlConnection.setRequestProperty("Content-Length", "6");
- urlConnection.setDoOutput(true);
- DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
- wr.writeBytes("vote=" + choice);
- wr.flush();
- wr.close();
- urlConnection.connect();
- if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
- return choice;
- }
- urlConnection.disconnect();
- } catch (Exception ex) {
- Log.e(TAG, "Error", ex);
- }
- return -1;
- }
-
- @Override
- protected void onPostExecute(final Integer result) {
- if (result == null || onTaskCompleteListener == null) return;
- onTaskCompleteListener.onTaskComplete(result);
- }
-
- public interface OnTaskCompleteListener {
- void onTaskComplete(final int choice);
- }
-}
diff --git a/app/src/main/java/awais/instagrabber/asyncs/direct_messages/DirectThreadBroadcaster.java b/app/src/main/java/awais/instagrabber/asyncs/direct_messages/DirectThreadBroadcaster.java
index 56f696e8..6a99a190 100644
--- a/app/src/main/java/awais/instagrabber/asyncs/direct_messages/DirectThreadBroadcaster.java
+++ b/app/src/main/java/awais/instagrabber/asyncs/direct_messages/DirectThreadBroadcaster.java
@@ -122,4 +122,131 @@
// public interface OnBroadcastCompleteListener {
// void onTaskComplete(DirectThreadBroadcastResponse response);
// }
+//
+// public enum ItemType {
+// TEXT("text"),
+// REACTION("reaction"),
+// REELSHARE("reel_share"),
+// IMAGE("configure_photo");
+//
+// private final String value;
+//
+// ItemType(final String value) {
+// this.value = value;
+// }
+//
+// public String getValue() {
+// return value;
+// }
+// }
+//
+// public static abstract class BroadcastOptions {
+// private final ItemType itemType;
+//
+// public BroadcastOptions(final ItemType itemType) {
+// this.itemType = itemType;
+// }
+//
+// public ItemType getItemType() {
+// return itemType;
+// }
+//
+// abstract Map getFormMap();
+// }
+//
+// public static class TextBroadcastOptions extends BroadcastOptions {
+// private final String text;
+//
+// public TextBroadcastOptions(String text) throws UnsupportedEncodingException {
+// super(ItemType.TEXT);
+// this.text = URLEncoder.encode(text, "UTF-8")
+// .replaceAll("\\+", "%20").replaceAll("%21", "!").replaceAll("%27", "'").replaceAll("%22", "\\\"")
+// .replaceAll("%28", "(").replaceAll("%29", ")").replaceAll("%7E", "~").replaceAll("%0A", "\n");
+// }
+//
+// @Override
+// Map getFormMap() {
+// return Collections.singletonMap("text", text);
+// }
+// }
+//
+// public static class ReactionBroadcastOptions extends BroadcastOptions {
+// private final String itemId;
+// private final boolean delete;
+//
+// public ReactionBroadcastOptions(String itemId, boolean delete) {
+// super(ItemType.REACTION);
+// this.itemId = itemId;
+// this.delete = delete;
+// }
+//
+// @Override
+// Map getFormMap() {
+// final Map form = new HashMap<>();
+// form.put("item_id", itemId);
+// form.put("reaction_status", delete ? "deleted" : "created");
+// form.put("reaction_type", "like");
+// return form;
+// }
+// }
+//
+// public static class StoryReplyBroadcastOptions extends BroadcastOptions {
+// private final String text, mediaId, reelId;
+//
+// public StoryReplyBroadcastOptions(String text, String mediaId, String reelId) throws UnsupportedEncodingException {
+// super(ItemType.REELSHARE);
+// this.text = URLEncoder.encode(text, "UTF-8")
+// .replaceAll("\\+", "%20").replaceAll("%21", "!").replaceAll("%27", "'")
+// .replaceAll("%28", "(").replaceAll("%29", ")").replaceAll("%7E", "~").replaceAll("%0A", "\n");
+// this.mediaId = mediaId;
+// this.reelId = reelId; // or user id, usually same
+// }
+//
+// @Override
+// Map getFormMap() {
+// final Map form = new HashMap<>();
+// form.put("text", text);
+// form.put("media_id", mediaId);
+// form.put("reel_id", reelId);
+// form.put("entry", "reel");
+// return form;
+// }
+// }
+//
+// public static class ImageBroadcastOptions extends BroadcastOptions {
+// final boolean allowFullAspectRatio;
+// final String uploadId;
+//
+// public ImageBroadcastOptions(final boolean allowFullAspectRatio, final String uploadId) {
+// super(ItemType.IMAGE);
+// this.allowFullAspectRatio = allowFullAspectRatio;
+// this.uploadId = uploadId;
+// }
+//
+// @Override
+// Map getFormMap() {
+// final Map form = new HashMap<>();
+// form.put("allow_full_aspect_ratio", String.valueOf(allowFullAspectRatio));
+// form.put("upload_id", uploadId);
+// return form;
+// }
+// }
+//
+// public static class DirectThreadBroadcastResponse {
+// private final int responseCode;
+// private final JSONObject response;
+//
+// public DirectThreadBroadcastResponse(int responseCode, JSONObject response) {
+// this.responseCode = responseCode;
+// this.response = response;
+// }
+//
+// public int getResponseCode() {
+// return responseCode;
+// }
+//
+// public JSONObject getResponse() {
+// return response;
+// }
+// }
// }
diff --git a/app/src/main/java/awais/instagrabber/customviews/PostsRecyclerView.java b/app/src/main/java/awais/instagrabber/customviews/PostsRecyclerView.java
index 53f222e0..3e0d8e14 100644
--- a/app/src/main/java/awais/instagrabber/customviews/PostsRecyclerView.java
+++ b/app/src/main/java/awais/instagrabber/customviews/PostsRecyclerView.java
@@ -183,11 +183,13 @@ public class PostsRecyclerView extends RecyclerView {
private void initSelf() {
feedViewModel = new ViewModelProvider(viewModelStoreOwner).get(FeedViewModel.class);
- feedViewModel.getList().observe(lifeCycleOwner, list -> feedAdapter.submitList(list, () -> {
- if (!shouldScrollToTop) return;
- smoothScrollToPosition(0);
- shouldScrollToTop = false;
- }));
+ feedViewModel.getList().observe(lifeCycleOwner, list -> {
+ if (list.size() > 0) feedAdapter.submitList(list, () -> {
+ if (!shouldScrollToTop) return;
+ smoothScrollToPosition(0);
+ shouldScrollToTop = false;
+ });
+ });
postFetcher = new PostFetcher(postFetchService, fetchListener);
if (layoutPreferences.getHasGap()) {
addItemDecoration(gridSpacingItemDecoration);
diff --git a/app/src/main/java/awais/instagrabber/db/AppDatabase.java b/app/src/main/java/awais/instagrabber/db/AppDatabase.java
index 6190575a..11547601 100644
--- a/app/src/main/java/awais/instagrabber/db/AppDatabase.java
+++ b/app/src/main/java/awais/instagrabber/db/AppDatabase.java
@@ -161,7 +161,7 @@ public abstract class AppDatabase extends RoomDatabase {
final FavoriteType type = favoriteTypeQueryPair.first;
final String query = favoriteTypeQueryPair.second;
oldModels.add(new Favorite(
- -1,
+ 0,
query,
type,
queryDisplayExists ? cursor.getString(cursor.getColumnIndex("query_display"))
diff --git a/app/src/main/java/awais/instagrabber/db/dao/FavoriteDao.java b/app/src/main/java/awais/instagrabber/db/dao/FavoriteDao.java
index 50dc3129..4bf0f2ad 100644
--- a/app/src/main/java/awais/instagrabber/db/dao/FavoriteDao.java
+++ b/app/src/main/java/awais/instagrabber/db/dao/FavoriteDao.java
@@ -21,7 +21,7 @@ public interface FavoriteDao {
@Query("SELECT * FROM favorites WHERE query_text = :query and type = :type")
Favorite findFavoriteByQueryAndType(String query, FavoriteType type);
- @Insert(onConflict = OnConflictStrategy.REPLACE)
+ @Insert
List insertFavorites(Favorite... favorites);
@Update
diff --git a/app/src/main/java/awais/instagrabber/dialogs/ProfilePicDialogFragment.java b/app/src/main/java/awais/instagrabber/dialogs/ProfilePicDialogFragment.java
index 6a468cf3..91779710 100644
--- a/app/src/main/java/awais/instagrabber/dialogs/ProfilePicDialogFragment.java
+++ b/app/src/main/java/awais/instagrabber/dialogs/ProfilePicDialogFragment.java
@@ -9,6 +9,7 @@ import android.graphics.drawable.ColorDrawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -19,6 +20,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.DialogFragment;
+import androidx.fragment.app.FragmentActivity;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.controller.BaseControllerListener;
@@ -30,9 +32,19 @@ import java.io.File;
import awais.instagrabber.R;
import awais.instagrabber.asyncs.ProfilePictureFetcher;
import awais.instagrabber.databinding.DialogProfilepicBinding;
+import awais.instagrabber.db.entities.Account;
+import awais.instagrabber.db.repositories.RepositoryCallback;
import awais.instagrabber.interfaces.FetchListener;
+import awais.instagrabber.models.StoryModel;
+import awais.instagrabber.repositories.responses.UserInfo;
+import awais.instagrabber.utils.Constants;
+import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.DownloadUtils;
import awais.instagrabber.utils.TextUtils;
+import awais.instagrabber.webservices.ProfileService;
+import awais.instagrabber.webservices.ServiceCallback;
+
+import static awais.instagrabber.utils.Utils.settingsHelper;
public class ProfilePicDialogFragment extends DialogFragment {
private static final String TAG = "ProfilePicDlgFragment";
@@ -41,9 +53,15 @@ public class ProfilePicDialogFragment extends DialogFragment {
private final String name;
private final String fallbackUrl;
+ private boolean isLoggedIn;
private DialogProfilepicBinding binding;
private String url;
+ private final FetchListener fetchListener = profileUrl -> {
+ url = profileUrl;
+ setupPhoto();
+ };
+
public ProfilePicDialogFragment(final String id, final String name, final String fallbackUrl) {
this.id = id;
this.name = name;
@@ -55,6 +73,8 @@ public class ProfilePicDialogFragment extends DialogFragment {
final ViewGroup container,
final Bundle savedInstanceState) {
binding = DialogProfilepicBinding.inflate(inflater, container, false);
+ final String cookie = settingsHelper.getString(Constants.COOKIE);
+ isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != null;
return binding.getRoot();
}
@@ -83,7 +103,7 @@ public class ProfilePicDialogFragment extends DialogFragment {
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
init();
- fetchPhoto();
+ fetchAvatar();
}
private void init() {
@@ -106,37 +126,55 @@ public class ProfilePicDialogFragment extends DialogFragment {
}
}
- private void fetchPhoto() {
- final FetchListener fetchListener = profileUrl -> {
- url = profileUrl;
- if (TextUtils.isEmpty(url)) {
- url = fallbackUrl;
- }
- final DraweeController controller = Fresco
- .newDraweeControllerBuilder()
- .setUri(url)
- .setOldController(binding.imageViewer.getController())
- .setControllerListener(new BaseControllerListener() {
- @Override
- public void onFailure(final String id, final Throwable throwable) {
- super.onFailure(id, throwable);
- binding.download.setVisibility(View.GONE);
- binding.progressView.setVisibility(View.GONE);
- }
+ private void fetchAvatar() {
+ if (isLoggedIn) {
+ final ProfileService profileService = ProfileService.getInstance();
+ profileService.getUserInfo(id, new ServiceCallback() {
+ @Override
+ public void onSuccess(final UserInfo result) {
+ if (result != null) {
+ fetchListener.onResult(result.getHDProfilePicUrl());
+ }
+ }
- @Override
- public void onFinalImageSet(final String id,
- final ImageInfo imageInfo,
- final Animatable animatable) {
- super.onFinalImageSet(id, imageInfo, animatable);
- binding.download.setVisibility(View.VISIBLE);
- binding.progressView.setVisibility(View.GONE);
- }
- })
- .build();
- binding.imageViewer.setController(controller);
- };
- new ProfilePictureFetcher(name, id, fetchListener, url, false).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ @Override
+ public void onFailure(final Throwable t) {
+ final Context context = getContext();
+ Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
+ getDialog().dismiss();
+ }
+ });
+ }
+ else new ProfilePictureFetcher(name, id, fetchListener, fallbackUrl, false).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+
+ private void setupPhoto() {
+ if (TextUtils.isEmpty(url)) {
+ url = fallbackUrl;
+ }
+ final DraweeController controller = Fresco
+ .newDraweeControllerBuilder()
+ .setUri(url)
+ .setOldController(binding.imageViewer.getController())
+ .setControllerListener(new BaseControllerListener() {
+ @Override
+ public void onFailure(final String id, final Throwable throwable) {
+ super.onFailure(id, throwable);
+ binding.download.setVisibility(View.GONE);
+ binding.progressView.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onFinalImageSet(final String id,
+ final ImageInfo imageInfo,
+ final Animatable animatable) {
+ super.onFinalImageSet(id, imageInfo, animatable);
+ binding.download.setVisibility(View.VISIBLE);
+ binding.progressView.setVisibility(View.GONE);
+ }
+ })
+ .build();
+ binding.imageViewer.setController(controller);
}
private void downloadProfilePicture() {
diff --git a/app/src/main/java/awais/instagrabber/fragments/CommentsViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/CommentsViewerFragment.java
index c716c989..500ad96b 100644
--- a/app/src/main/java/awais/instagrabber/fragments/CommentsViewerFragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/CommentsViewerFragment.java
@@ -25,6 +25,7 @@ import androidx.appcompat.widget.LinearLayoutCompat;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.ViewModelProvider;
+import androidx.navigation.NavController;
import androidx.navigation.NavDirections;
import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.LinearLayoutManager;
@@ -32,11 +33,18 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import awais.instagrabber.BuildConfig;
import awais.instagrabber.R;
import awais.instagrabber.adapters.CommentsAdapter;
import awais.instagrabber.asyncs.CommentsFetcher;
+import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
import awais.instagrabber.databinding.FragmentCommentsBinding;
import awais.instagrabber.dialogs.ProfilePicDialogFragment;
+import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.CommentModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.utils.Constants;
@@ -56,17 +64,48 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
private CommentsAdapter commentsAdapter;
private FragmentCommentsBinding binding;
- private String shortCode;
- private String userId;
+ private LinearLayoutManager layoutManager;
+ private RecyclerLazyLoader lazyLoader;
+ private String shortCode, userId, endCursor = null;
private Resources resources;
private InputMethodManager imm;
private AppCompatActivity fragmentActivity;
private LinearLayoutCompat root;
- private boolean shouldRefresh = true;
+ private boolean shouldRefresh = true, hasNextPage = false;
private MediaService mediaService;
private String postId;
+ private AsyncTask> currentlyRunning;
private CommentsViewModel commentsViewModel;
+ private final FetchListener> fetchListener = new FetchListener>() {
+ @Override
+ public void doBefore() {
+ binding.swipeRefreshLayout.setRefreshing(true);
+ }
+
+ @Override
+ public void onResult(final List commentModels) {
+ if (commentModels != null && commentModels.size() > 0) {
+ endCursor = commentModels.get(0).getEndCursor();
+ hasNextPage = commentModels.get(0).hasNextPage();
+ List list = commentsViewModel.getList().getValue();
+ list = list != null ? new LinkedList<>(list) : new LinkedList<>();
+ // final int oldSize = list != null ? list.size() : 0;
+ list.addAll(commentModels);
+ commentsViewModel.getList().postValue(list);
+ }
+ binding.swipeRefreshLayout.setRefreshing(false);
+ stopCurrentExecutor();
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
+ binding.swipeRefreshLayout.setRefreshing(false);
+ stopCurrentExecutor();
+ }
+ };
+
private final CommentsAdapter.CommentCallback commentCallback = new CommentsAdapter.CommentCallback() {
@Override
public void onClick(final CommentModel comment) {
@@ -181,11 +220,11 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
@Override
public void onRefresh() {
- binding.swipeRefreshLayout.setRefreshing(true);
- new CommentsFetcher(shortCode, commentModels -> {
- commentsViewModel.getList().postValue(commentModels);
- binding.swipeRefreshLayout.setRefreshing(false);
- }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ endCursor = null;
+ lazyLoader.resetState();
+ commentsViewModel.getList().postValue(Collections.emptyList());
+ stopCurrentExecutor();
+ currentlyRunning = new CommentsFetcher(shortCode, "", fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private void init() {
@@ -198,7 +237,8 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
binding.swipeRefreshLayout.setOnRefreshListener(this);
binding.swipeRefreshLayout.setRefreshing(true);
commentsViewModel = new ViewModelProvider(this).get(CommentsViewModel.class);
- binding.rvComments.setLayoutManager(new LinearLayoutManager(getContext()));
+ layoutManager = new LinearLayoutManager(getContext());
+ binding.rvComments.setLayoutManager(layoutManager);
commentsAdapter = new CommentsAdapter(commentCallback);
binding.rvComments.setAdapter(commentsAdapter);
commentsViewModel.getList().observe(getViewLifecycleOwner(), commentsAdapter::submitList);
@@ -226,6 +266,13 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
});
binding.commentField.setEndIconOnClickListener(newCommentListener);
}
+ lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
+ if (hasNextPage && !TextUtils.isEmpty(endCursor))
+ currentlyRunning = new CommentsFetcher(shortCode, endCursor, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ endCursor = null;
+ });
+ binding.rvComments.addOnScrollListener(lazyLoader);
+ stopCurrentExecutor();
onRefresh();
}
@@ -249,30 +296,29 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
&& (userIdFromCookie.equals(commentModel.getProfileModel().getId()) || userIdFromCookie.equals(userId))) {
commentDialogList = new String[]{
resources.getString(R.string.open_profile),
- resources.getString(R.string.view_pfp),
-// resources.getString(R.string.comment_viewer_copy_user),
resources.getString(R.string.comment_viewer_copy_comment),
+ resources.getString(R.string.comment_viewer_see_likers),
resources.getString(R.string.comment_viewer_reply_comment),
commentModel.getLiked() ? resources.getString(R.string.comment_viewer_unlike_comment)
: resources.getString(R.string.comment_viewer_like_comment),
+ resources.getString(R.string.comment_viewer_translate_comment),
resources.getString(R.string.comment_viewer_delete_comment)
};
} else if (!TextUtils.isEmpty(cookie)) {
commentDialogList = new String[]{
resources.getString(R.string.open_profile),
- resources.getString(R.string.view_pfp),
-// resources.getString(R.string.comment_viewer_copy_user),
resources.getString(R.string.comment_viewer_copy_comment),
+ resources.getString(R.string.comment_viewer_see_likers),
resources.getString(R.string.comment_viewer_reply_comment),
commentModel.getLiked() ? resources.getString(R.string.comment_viewer_unlike_comment)
: resources.getString(R.string.comment_viewer_like_comment),
+ resources.getString(R.string.comment_viewer_translate_comment)
};
} else {
commentDialogList = new String[]{
resources.getString(R.string.open_profile),
- resources.getString(R.string.view_pfp),
-// resources.getString(R.string.comment_viewer_copy_user),
- resources.getString(R.string.comment_viewer_copy_comment)
+ resources.getString(R.string.comment_viewer_copy_comment),
+ resources.getString(R.string.comment_viewer_see_likers)
};
}
final Context context = getContext();
@@ -284,25 +330,20 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
case 0: // open profile
openProfile("@" + profileModel.getUsername());
break;
- case 1: // view profile pic
- final FragmentManager fragmentManager = getParentFragmentManager();
- final ProfilePicDialogFragment fragment = new ProfilePicDialogFragment(profileModel.getId(),
- profileModel.getUsername(),
- profileModel.getHdProfilePic());
- final FragmentTransaction ft = fragmentManager.beginTransaction();
- ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
- .add(fragment, "profilePicDialog")
- .commit();
- break;
-// case 2: // copy username
-// Utils.copyText(context, profileModel.getUsername());
-// break;
- case 2: // copy comment
+ case 1: // copy comment
Utils.copyText(context, "@" + profileModel.getUsername() + ": " + commentModel.getText());
break;
+ case 2: // see comment likers, this is surprisingly available to anons
+ final NavController navController = getNavController();
+ if (navController != null) {
+ final Bundle bundle = new Bundle();
+ bundle.putString("postId", commentModel.getId());
+ bundle.putBoolean("isComment", true);
+ navController.navigate(R.id.action_global_likesViewerFragment, bundle);
+ }
+ else Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
+ break;
case 3: // reply to comment
- // final View focus = binding.rvComments.findViewWithTag(commentModel);
- // focus.setBackgroundColor(0x80888888);
commentsAdapter.setSelected(commentModel);
String mention = "@" + profileModel.getUsername() + " ";
binding.commentText.setText(mention);
@@ -326,7 +367,7 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
return;
}
- onRefresh();
+ commentsAdapter.setLiked(commentModel, true);
}
@Override
@@ -344,7 +385,7 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
return;
}
- onRefresh();
+ commentsAdapter.setLiked(commentModel, false);
}
@Override
@@ -354,7 +395,29 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
}
});
break;
- case 5: // delete comment
+ case 5: // translate comment
+ mediaService.translate(commentModel.getId(), "2", new ServiceCallback() {
+ @Override
+ public void onSuccess(final String result) {
+ if (TextUtils.isEmpty(result)) {
+ Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ new AlertDialog.Builder(context)
+ .setTitle(username)
+ .setMessage(result)
+ .setPositiveButton(R.string.ok, null)
+ .show();
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ Log.e(TAG, "Error translating comment", t);
+ Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
+ }
+ });
+ break;
+ case 6: // delete comment
final String userId = CookieUtils.getUserIdFromCookie(cookie);
if (userId == null) return;
mediaService.deleteComment(
@@ -389,4 +452,25 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
final NavDirections action = CommentsViewerFragmentDirections.actionGlobalProfileFragment(username);
NavHostFragment.findNavController(this).navigate(action);
}
+
+ private void stopCurrentExecutor() {
+ if (currentlyRunning != null) {
+ try {
+ currentlyRunning.cancel(true);
+ } catch (final Exception e) {
+ if (BuildConfig.DEBUG) Log.e(TAG, "", e);
+ }
+ }
+ }
+
+ @Nullable
+ private NavController getNavController() {
+ NavController navController = null;
+ try {
+ navController = NavHostFragment.findNavController(this);
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "navigateToProfile", e);
+ }
+ return navController;
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/fragments/FavoritesFragment.java b/app/src/main/java/awais/instagrabber/fragments/FavoritesFragment.java
index 190e9a59..34c81322 100644
--- a/app/src/main/java/awais/instagrabber/fragments/FavoritesFragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/FavoritesFragment.java
@@ -81,7 +81,6 @@ public class FavoritesFragment extends Fragment {
@Override
public void onSuccess(final List favorites) {
favoritesViewModel.getList().postValue(favorites);
- fetchMissingInfo(favorites);
}
@Override
@@ -156,109 +155,4 @@ public class FavoritesFragment extends Fragment {
binding.favoriteList.setAdapter(adapter);
}
-
- private void fetchMissingInfo(final List allFavorites) {
- final Runnable runnable = () -> {
- final List updatedList = new ArrayList<>(allFavorites);
- // cyclic barrier is to make the async calls synchronous
- final CyclicBarrier cyclicBarrier = new CyclicBarrier(2, () -> {
- // Log.d(TAG, "fetchMissingInfo: barrier action");
- favoritesViewModel.getList().postValue(new ArrayList<>(updatedList));
- });
- try {
- for (final Favorite model : allFavorites) {
- cyclicBarrier.reset();
- // if the model has missing pic or display name (for user and location), fetch those details
- switch (model.getType()) {
- case LOCATION:
- if (TextUtils.isEmpty(model.getDisplayName())
- || TextUtils.isEmpty(model.getPicUrl())) {
- new LocationFetcher(model.getQuery(), result -> {
- if (result == null) return;
- final int i = updatedList.indexOf(model);
- updatedList.remove(i);
- final Favorite updated = new Favorite(
- model.getId(),
- model.getQuery(),
- model.getType(),
- result.getName(),
- result.getSdProfilePic(),
- model.getDateAdded()
- );
- favoriteRepository.insertOrUpdateFavorite(updated, new RepositoryCallback() {
- @Override
- public void onSuccess(final Void result) {
- updatedList.add(i, updated);
- try {
- cyclicBarrier.await();
- } catch (BrokenBarrierException | InterruptedException e) {
- Log.e(TAG, "fetchMissingInfo: ", e);
- }
- }
-
- @Override
- public void onDataNotAvailable() {
- try {
- cyclicBarrier.await();
- } catch (BrokenBarrierException | InterruptedException e) {
- Log.e(TAG, "fetchMissingInfo: ", e);
- }
- }
- });
- }).execute();
- cyclicBarrier.await();
- }
- break;
- case USER:
- if (TextUtils.isEmpty(model.getDisplayName())
- || TextUtils.isEmpty(model.getPicUrl())) {
- new ProfileFetcher(model.getQuery(), result -> {
- if (result == null) return;
- final int i = updatedList.indexOf(model);
- updatedList.remove(i);
- final Favorite updated = new Favorite(
- model.getId(),
- model.getQuery(),
- model.getType(),
- result.getName(),
- result.getSdProfilePic(),
- model.getDateAdded()
- );
- favoriteRepository.insertOrUpdateFavorite(updated, new RepositoryCallback() {
- @Override
- public void onSuccess(final Void result) {
- try {
- cyclicBarrier.await();
- } catch (BrokenBarrierException | InterruptedException e) {
- Log.e(TAG, "fetchMissingInfo: ", e);
- }
- }
-
- @Override
- public void onDataNotAvailable() {
- try {
- cyclicBarrier.await();
- } catch (BrokenBarrierException | InterruptedException e) {
- Log.e(TAG, "fetchMissingInfo: ", e);
- }
- }
- });
- updatedList.add(i, updated);
- }).execute();
- cyclicBarrier.await();
- }
- break;
- case HASHTAG:
- default:
- // hashtags don't require displayName or pic
- // updatedList.add(model);
- }
- }
- } catch (Exception e) {
- Log.e(TAG, "fetchMissingInfo: ", e);
- }
- favoritesViewModel.getList().postValue(updatedList);
- };
- new Thread(runnable).start();
- }
}
diff --git a/app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.java
index f27d2c91..2512cca0 100644
--- a/app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.java
@@ -20,29 +20,22 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SearchView;
import androidx.fragment.app.Fragment;
import androidx.navigation.fragment.NavHostFragment;
+import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import java.util.ArrayList;
-import java.util.Arrays;
-import awais.instagrabber.BuildConfig;
import awais.instagrabber.R;
import awais.instagrabber.adapters.FollowAdapter;
+import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
import awais.instagrabber.databinding.FragmentFollowersViewerBinding;
import awais.instagrabber.models.FollowModel;
-import awais.instagrabber.repositories.responses.FriendshipRepoChangeRootResponse;
import awais.instagrabber.repositories.responses.FriendshipRepoListFetchResponse;
-import awais.instagrabber.utils.Constants;
-import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.webservices.FriendshipService;
import awais.instagrabber.webservices.ServiceCallback;
-import awaisomereport.LogCollector;
import thoughtbot.expandableadapter.ExpandableGroup;
-import static awais.instagrabber.utils.Utils.logCollector;
-import static awais.instagrabber.utils.Utils.settingsHelper;
-
public final class FollowViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
private static final String TAG = "FollowViewerFragment";
@@ -51,9 +44,11 @@ public final class FollowViewerFragment extends Fragment implements SwipeRefresh
private final ArrayList followersModels = new ArrayList<>();
private final ArrayList allFollowing = new ArrayList<>();
- private boolean isFollowersList, isCompare = false, loading = false;
- private String profileId, username, namePost, type;
+ private boolean moreAvailable = true, isFollowersList, isCompare = false, loading = false, shouldRefresh = true;
+ private String profileId, username, namePost, type, endCursor;
private Resources resources;
+ private LinearLayoutManager layoutManager;
+ private RecyclerLazyLoader lazyLoader;
private FollowModel model;
private FollowAdapter adapter;
private View.OnClickListener clickListener;
@@ -61,9 +56,61 @@ public final class FollowViewerFragment extends Fragment implements SwipeRefresh
private AsyncTask currentlyExecuting;
private SwipeRefreshLayout root;
private FriendshipService friendshipService;
- private boolean shouldRefresh = true;
private AppCompatActivity fragmentActivity;
+ final ServiceCallback followingFetchCb = new ServiceCallback() {
+ @Override
+ public void onSuccess(final FriendshipRepoListFetchResponse result) {
+ if (result != null) {
+ followingModels.addAll(result.getItems());
+ if (!isFollowersList) followModels.addAll(result.getItems());
+ if (result.isMoreAvailable()) {
+ endCursor = result.getNextMaxId();
+ friendshipService.getList(false, profileId, endCursor, this);
+ } else if (followersModels.size() == 0) {
+ if (!isFollowersList) moreAvailable = false;
+ friendshipService.getList(true, profileId, null, followingFetchCb);
+ } else {
+ if (!isFollowersList) moreAvailable = false;
+ showCompare();
+ }
+ } else binding.swipeRefreshLayout.setRefreshing(false);
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ binding.swipeRefreshLayout.setRefreshing(false);
+ Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
+ Log.e(TAG, "Error fetching list (double, following)", t);
+ }
+ };
+ final ServiceCallback followersFetchCb = new ServiceCallback() {
+ @Override
+ public void onSuccess(final FriendshipRepoListFetchResponse result) {
+ if (result != null) {
+ followersModels.addAll(result.getItems());
+ if (isFollowersList) followModels.addAll(result.getItems());
+ if (result.isMoreAvailable()) {
+ endCursor = result.getNextMaxId();
+ friendshipService.getList(true, profileId, endCursor, this);
+ } else if (followingModels.size() == 0) {
+ if (isFollowersList) moreAvailable = false;
+ friendshipService.getList(false, profileId, null, followingFetchCb);
+ } else {
+ if (isFollowersList) moreAvailable = false;
+ showCompare();
+ }
+ }
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ binding.swipeRefreshLayout.setRefreshing(false);
+ Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
+ Log.e(TAG, "Error fetching list (double, follower)", t);
+ }
+ };
+
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -141,13 +188,13 @@ public final class FollowViewerFragment extends Fragment implements SwipeRefresh
public void onRefresh() {
if (isCompare) listCompare();
else listFollows();
+ endCursor = null;
+ lazyLoader.resetState();
}
private void listFollows() {
- loading = true;
type = resources.getString(isFollowersList ? R.string.followers_type_followers : R.string.followers_type_following);
setSubtitle(type);
- followModels.clear();
final ServiceCallback cb = new ServiceCallback() {
@Override
public void onSuccess(final FriendshipRepoListFetchResponse result) {
@@ -156,82 +203,70 @@ public final class FollowViewerFragment extends Fragment implements SwipeRefresh
return;
}
else {
+ int oldSize = followModels.size() == 0 ? 0 : followModels.size() - 1;
followModels.addAll(result.getItems());
if (result.isMoreAvailable()) {
- friendshipService.getList(isFollowersList, profileId, result.getNextMaxId(), this);
- }
- else {
- binding.swipeRefreshLayout.setRefreshing(false);
- if (isFollowersList) followersModels.addAll(followModels);
- else followingModels.addAll(followModels);
- refreshAdapter(followModels, null, null, null);
+ moreAvailable = true;
+ endCursor = result.getNextMaxId();
}
+ else moreAvailable = false;
+ binding.swipeRefreshLayout.setRefreshing(false);
+ if (isFollowersList) followersModels.addAll(result.getItems());
+ else followingModels.addAll(result.getItems());
+ refreshAdapter(followModels, null, null, null);
+ layoutManager.scrollToPosition(oldSize);
}
}
@Override
public void onFailure(final Throwable t) {
binding.swipeRefreshLayout.setRefreshing(false);
+ Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
Log.e(TAG, "Error fetching list (single)", t);
}
};
- binding.swipeRefreshLayout.setRefreshing(true);
- friendshipService.getList(isFollowersList, profileId, null, cb);
+ layoutManager = new LinearLayoutManager(getContext());
+ lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
+ if (!TextUtils.isEmpty(endCursor)) {
+ binding.swipeRefreshLayout.setRefreshing(true);
+ layoutManager.setStackFromEnd(true);
+ friendshipService.getList(isFollowersList, profileId, endCursor, cb);
+ }
+ endCursor = null;
+ });
+ binding.rvFollow.addOnScrollListener(lazyLoader);
+ binding.rvFollow.setLayoutManager(layoutManager);
+ if (moreAvailable) {
+ binding.swipeRefreshLayout.setRefreshing(true);
+ friendshipService.getList(isFollowersList, profileId, endCursor, cb);
+ }
+ else {
+ refreshAdapter(followModels, null, null, null);
+ layoutManager.scrollToPosition(0);
+ }
}
private void listCompare() {
+ layoutManager.setStackFromEnd(false);
+ binding.rvFollow.clearOnScrollListeners();
loading = true;
setSubtitle(R.string.followers_compare);
allFollowing.clear();
- followersModels.clear();
- followingModels.clear();
- final ServiceCallback followingFetchCb = new ServiceCallback() {
- @Override
- public void onSuccess(final FriendshipRepoListFetchResponse result) {
- if (result != null) {
- followingModels.addAll(result.getItems());
-
- if (result.isMoreAvailable()) {
- friendshipService.getList(false, profileId, result.getNextMaxId(), this);
- } else {
- showCompare();
- }
- } else binding.swipeRefreshLayout.setRefreshing(false);
- }
-
- @Override
- public void onFailure(final Throwable t) {
- binding.swipeRefreshLayout.setRefreshing(false);
- Log.e(TAG, "Error fetching list (double, following)", t);
- }
- };
- final ServiceCallback followersFetchCb = new ServiceCallback() {
- @Override
- public void onSuccess(final FriendshipRepoListFetchResponse result) {
- if (result != null) {
- followersModels.addAll(result.getItems());
- if (result.isMoreAvailable()) {
- friendshipService.getList(true, profileId, result.getNextMaxId(), this);
- } else if (followingModels.size() == 0) {
- friendshipService.getList(false, profileId, null, followingFetchCb);
- } else {
- showCompare();
- }
- }
- }
-
- @Override
- public void onFailure(final Throwable t) {
- binding.swipeRefreshLayout.setRefreshing(false);
- Log.e(TAG, "Error fetching list (double, follower)", t);
- }
- };
- binding.swipeRefreshLayout.setRefreshing(true);
- if (followersModels.size() == 0) {
- friendshipService.getList(true, profileId, null, followersFetchCb);
+ if (moreAvailable) {
+ binding.swipeRefreshLayout.setRefreshing(true);
+ Toast.makeText(getContext(), R.string.follower_start_compare, Toast.LENGTH_LONG).show();
+ friendshipService.getList(isFollowersList,
+ profileId,
+ endCursor,
+ isFollowersList ? followersFetchCb : followingFetchCb);
}
- else if (followingModels.size() == 0) {
- friendshipService.getList(false, profileId, null, followingFetchCb);
+ else if (followersModels.size() == 0 || followingModels.size() == 0) {
+ binding.swipeRefreshLayout.setRefreshing(true);
+ Toast.makeText(getContext(), R.string.follower_start_compare, Toast.LENGTH_LONG).show();
+ friendshipService.getList(!isFollowersList,
+ profileId,
+ null,
+ isFollowersList ? followingFetchCb : followersFetchCb);
}
else showCompare();
}
@@ -346,12 +381,12 @@ public final class FollowViewerFragment extends Fragment implements SwipeRefresh
final Context context = getContext();
if (loading) Toast.makeText(context, R.string.follower_wait_to_load, Toast.LENGTH_LONG).show();
else if (isCompare) {
- listFollows();
isCompare = !isCompare;
+ listFollows();
}
else {
- listCompare();
isCompare = !isCompare;
+ listCompare();
}
return true;
}
@@ -371,8 +406,7 @@ public final class FollowViewerFragment extends Fragment implements SwipeRefresh
if (allFollowing != null && allFollowing.size() > 0)
groups.add(new ExpandableGroup(resources.getString(R.string.followers_both_following), allFollowing));
} else {
- final ExpandableGroup group = new ExpandableGroup(type, followModels);
- groups.add(group);
+ groups.add(new ExpandableGroup(type, followModels));
}
adapter = new FollowAdapter(clickListener, groups);
adapter.toggleGroup(0);
diff --git a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java
index 26733e12..10fc0482 100644
--- a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java
@@ -54,6 +54,7 @@ import awais.instagrabber.db.entities.Favorite;
import awais.instagrabber.db.repositories.FavoriteRepository;
import awais.instagrabber.db.repositories.RepositoryCallback;
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
+import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.HashtagModel;
import awais.instagrabber.models.PostsLayoutPreferences;
@@ -363,19 +364,27 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
private void fetchHashtagModel() {
stopCurrentExecutor();
binding.swipeRefreshLayout.setRefreshing(true);
- currentlyExecuting = new HashtagFetcher(hashtag.substring(1), result -> {
- hashtagModel = result;
- binding.swipeRefreshLayout.setRefreshing(false);
- final Context context = getContext();
- if (context == null) return;
- if (hashtagModel == null) {
- Toast.makeText(context, R.string.error_loading_profile, Toast.LENGTH_SHORT).show();
- return;
+ currentlyExecuting = new HashtagFetcher(hashtag.substring(1), new FetchListener() {
+ @Override
+ public void onResult(final HashtagModel result) {
+ hashtagModel = result;
+ binding.swipeRefreshLayout.setRefreshing(false);
+ final Context context = getContext();
+ if (context == null) return;
+ if (hashtagModel == null) {
+ Toast.makeText(context, R.string.error_loading_profile, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ setTitle();
+ setHashtagDetails();
+ setupPosts();
+ fetchStories();
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
}
- setTitle();
- setHashtagDetails();
- setupPosts();
- fetchStories();
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
@@ -410,9 +419,12 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
hashtagDetailsBinding.btnFollowTag.setClickable(true);
if (!result) {
Log.e(TAG, "onSuccess: result is false");
+ Snackbar.make(root, R.string.downloader_unknown_error, BaseTransientBottomBar.LENGTH_LONG)
+ .show();
return;
}
- onRefresh();
+ hashtagDetailsBinding.btnFollowTag.setText(R.string.unfollow);
+ hashtagDetailsBinding.btnFollowTag.setChipIconResource(R.drawable.ic_outline_person_add_disabled_24);
}
@Override
@@ -435,9 +447,12 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
hashtagDetailsBinding.btnFollowTag.setClickable(true);
if (!result) {
Log.e(TAG, "onSuccess: result is false");
+ Snackbar.make(root, R.string.downloader_unknown_error, BaseTransientBottomBar.LENGTH_LONG)
+ .show();
return;
}
- onRefresh();
+ hashtagDetailsBinding.btnFollowTag.setText(R.string.follow);
+ hashtagDetailsBinding.btnFollowTag.setChipIconResource(R.drawable.ic_outline_person_add_24);
}
@Override
@@ -462,8 +477,23 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
favoriteRepository.getFavorite(hashtag.substring(1), FavoriteType.HASHTAG, new RepositoryCallback() {
@Override
public void onSuccess(final Favorite result) {
- hashtagDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24);
- hashtagDetailsBinding.favChip.setText(R.string.favorite_short);
+ favoriteRepository.insertOrUpdateFavorite(new Favorite(
+ result.getId(),
+ hashtag.substring(1),
+ FavoriteType.HASHTAG,
+ hashtagModel.getName(),
+ hashtagModel.getSdProfilePic(),
+ result.getDateAdded()
+ ), new RepositoryCallback() {
+ @Override
+ public void onSuccess(final Void result) {
+ hashtagDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24);
+ hashtagDetailsBinding.favChip.setText(R.string.favorite_short);
+ }
+
+ @Override
+ public void onDataNotAvailable() {}
+ });
}
@Override
@@ -492,18 +522,18 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
@Override
public void onDataNotAvailable() {
favoriteRepository.insertOrUpdateFavorite(new Favorite(
- -1,
+ 0,
hashtag.substring(1),
FavoriteType.HASHTAG,
hashtagModel.getName(),
- null,
+ hashtagModel.getSdProfilePic(),
new Date()
), new RepositoryCallback() {
@Override
public void onSuccess(final Void result) {
hashtagDetailsBinding.favChip.setText(R.string.favorite_short);
hashtagDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24);
- showSnackbar(getString(R.string.added_to_favs));
+ showSnackbar(getString(R.string.added_to_favs_short));
}
@Override
@@ -513,7 +543,9 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
}));
hashtagDetailsBinding.mainHashtagImage.setImageURI(hashtagModel.getSdProfilePic());
final String postCount = String.valueOf(hashtagModel.getPostCount());
- final SpannableStringBuilder span = new SpannableStringBuilder(getString(R.string.main_posts_count_inline, postCount));
+ final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline,
+ hashtagModel.getPostCount() > 2000000000L ? 2000000000 : hashtagModel.getPostCount().intValue(),
+ postCount));
span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0);
span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0);
hashtagDetailsBinding.mainTagPostCount.setText(span);
@@ -522,7 +554,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
if (!hasStories) return;
// show stories
final NavDirections action = HashTagFragmentDirections
- .actionHashtagFragmentToStoryViewerFragment(-1, null, true, false, hashtagModel.getName(), hashtagModel.getName());
+ .actionHashtagFragmentToStoryViewerFragment(-1, null, true, false, hashtagModel.getName(), hashtagModel.getName(), false, false);
NavHostFragment.findNavController(this).navigate(action);
});
}
diff --git a/app/src/main/java/awais/instagrabber/fragments/LikesViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/LikesViewerFragment.java
new file mode 100644
index 00000000..157222b8
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/fragments/LikesViewerFragment.java
@@ -0,0 +1,170 @@
+package awais.instagrabber.fragments;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.LinearLayoutCompat;
+import androidx.navigation.fragment.NavHostFragment;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+
+import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
+
+import java.util.List;
+
+import awais.instagrabber.R;
+import awais.instagrabber.adapters.LikesAdapter;
+import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
+import awais.instagrabber.databinding.FragmentLikesBinding;
+import awais.instagrabber.models.ProfileModel;
+import awais.instagrabber.repositories.responses.GraphQLUserListFetchResponse;
+import awais.instagrabber.utils.Constants;
+import awais.instagrabber.utils.CookieUtils;
+import awais.instagrabber.utils.TextUtils;
+import awais.instagrabber.utils.Utils;
+import awais.instagrabber.webservices.GraphQLService;
+import awais.instagrabber.webservices.MediaService;
+import awais.instagrabber.webservices.ServiceCallback;
+
+import static awais.instagrabber.utils.Utils.settingsHelper;
+
+public final class LikesViewerFragment extends BottomSheetDialogFragment implements SwipeRefreshLayout.OnRefreshListener {
+ private static final String TAG = "LikesViewerFragment";
+
+ private final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);
+
+ private LikesAdapter likesAdapter;
+ private FragmentLikesBinding binding;
+ private LinearLayoutManager layoutManager;
+ private Resources resources;
+ private AppCompatActivity fragmentActivity;
+ private LinearLayoutCompat root;
+ private RecyclerLazyLoader lazyLoader;
+ private MediaService mediaService;
+ private GraphQLService graphQLService;
+ private boolean isLoggedIn;
+ private String postId, endCursor;
+ private boolean isComment;
+
+ private final ServiceCallback> cb = new ServiceCallback>() {
+ @Override
+ public void onSuccess(final List result) {
+ final LikesAdapter likesAdapter = new LikesAdapter(result, v -> {
+ final Object tag = v.getTag();
+ if (tag instanceof ProfileModel) {
+ ProfileModel model = (ProfileModel) tag;
+ final Bundle bundle = new Bundle();
+ bundle.putString("username", "@" + model.getUsername());
+ NavHostFragment.findNavController(LikesViewerFragment.this).navigate(R.id.action_global_profileFragment, bundle);
+ }
+ });
+ binding.rvLikes.setAdapter(likesAdapter);
+ binding.rvLikes.setLayoutManager(new LinearLayoutManager(getContext()));
+ binding.swipeRefreshLayout.setRefreshing(false);
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ Log.e(TAG, "Error", t);
+ try {
+ final Context context = getContext();
+ Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
+ }
+ catch (Exception e) {}
+ }
+ };
+
+ private final ServiceCallback acb = new ServiceCallback() {
+ @Override
+ public void onSuccess(final GraphQLUserListFetchResponse result) {
+ endCursor = result.getNextMaxId();
+ final LikesAdapter likesAdapter = new LikesAdapter(result.getItems(), v -> {
+ final Object tag = v.getTag();
+ if (tag instanceof ProfileModel) {
+ ProfileModel model = (ProfileModel) tag;
+ final Bundle bundle = new Bundle();
+ bundle.putString("username", "@" + model.getUsername());
+ NavHostFragment.findNavController(LikesViewerFragment.this).navigate(R.id.action_global_profileFragment, bundle);
+ }
+ });
+ binding.rvLikes.setAdapter(likesAdapter);
+ binding.swipeRefreshLayout.setRefreshing(false);
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ Log.e(TAG, "Error", t);
+ try {
+ final Context context = getContext();
+ Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
+ }
+ catch (Exception e) {}
+ }
+ };
+
+ @Override
+ public void onCreate(@Nullable final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final String cookie = settingsHelper.getString(Constants.COOKIE);
+ isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != null;
+ fragmentActivity = (AppCompatActivity) getActivity();
+ mediaService = isLoggedIn ? MediaService.getInstance() : null;
+ graphQLService = isLoggedIn ? null : GraphQLService.getInstance();
+ // setHasOptionsMenu(true);
+ }
+
+ @NonNull
+ @Override
+ public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {
+ binding = FragmentLikesBinding.inflate(getLayoutInflater());
+ binding.swipeRefreshLayout.setEnabled(false);
+ binding.swipeRefreshLayout.setNestedScrollingEnabled(false);
+ root = binding.getRoot();
+ return root;
+ }
+
+ @Override
+ public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
+ init();
+ }
+
+ @Override
+ public void onRefresh() {
+ if (isComment && !isLoggedIn) {
+ lazyLoader.resetState();
+ graphQLService.fetchCommentLikers(postId, null, acb);
+ }
+ else mediaService.fetchLikes(postId, isComment, cb);
+ }
+
+ private void init() {
+ if (getArguments() == null) return;
+ final LikesViewerFragmentArgs fragmentArgs = LikesViewerFragmentArgs.fromBundle(getArguments());
+ postId = fragmentArgs.getPostId();
+ isComment = fragmentArgs.getIsComment();
+ binding.swipeRefreshLayout.setOnRefreshListener(this);
+ binding.swipeRefreshLayout.setRefreshing(true);
+ resources = getResources();
+ if (isComment && !isLoggedIn) {
+ layoutManager = new LinearLayoutManager(getContext());
+ binding.rvLikes.setLayoutManager(layoutManager);
+ lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
+ if (!TextUtils.isEmpty(endCursor))
+ graphQLService.fetchCommentLikers(postId, endCursor, acb);
+ endCursor = null;
+ });
+ binding.rvLikes.addOnScrollListener(lazyLoader);
+ }
+ onRefresh();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java
index acccc0b9..c48d4939 100644
--- a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java
@@ -400,8 +400,9 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
// binding.swipeRefreshLayout.setRefreshing(true);
locationDetailsBinding.mainLocationImage.setImageURI(locationModel.getSdProfilePic());
final String postCount = String.valueOf(locationModel.getPostCount());
- final SpannableStringBuilder span = new SpannableStringBuilder(getString(R.string.main_posts_count_inline,
- postCount));
+ final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline,
+ locationModel.getPostCount() > 2000000000L ? 2000000000 : locationModel.getPostCount().intValue(),
+ postCount));
span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0);
span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0);
locationDetailsBinding.mainLocPostCount.setText(span);
@@ -455,6 +456,20 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
locationDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24);
locationDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24);
locationDetailsBinding.favChip.setText(R.string.favorite_short);
+ favoriteRepository.insertOrUpdateFavorite(new Favorite(
+ result.getId(),
+ locationId,
+ FavoriteType.LOCATION,
+ locationModel.getName(),
+ locationModel.getSdProfilePic(),
+ result.getDateAdded()
+ ), new RepositoryCallback() {
+ @Override
+ public void onSuccess(final Void result) {}
+
+ @Override
+ public void onDataNotAvailable() {}
+ });
}
@Override
@@ -484,7 +499,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
@Override
public void onDataNotAvailable() {
favoriteRepository.insertOrUpdateFavorite(new Favorite(
- -1,
+ 0,
locationId,
FavoriteType.LOCATION,
locationModel.getName(),
@@ -495,7 +510,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
public void onSuccess(final Void result) {
locationDetailsBinding.favChip.setText(R.string.favorite_short);
locationDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24);
- showSnackbar(getString(R.string.added_to_favs));
+ showSnackbar(getString(R.string.added_to_favs_short));
}
@Override
@@ -508,7 +523,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
if (hasStories) {
// show stories
final NavDirections action = LocationFragmentDirections
- .actionLocationFragmentToStoryViewerFragment(-1, null, false, true, locationId, locationModel.getName());
+ .actionLocationFragmentToStoryViewerFragment(-1, null, false, true, locationId, locationModel.getName(), false, false);
NavHostFragment.findNavController(this).navigate(action);
}
});
diff --git a/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java
index f8733382..34559f0e 100644
--- a/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java
@@ -18,20 +18,28 @@ import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.core.app.NotificationManagerCompat;
import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavDirections;
import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+import java.util.List;
+
import awais.instagrabber.R;
import awais.instagrabber.adapters.NotificationsAdapter;
import awais.instagrabber.adapters.NotificationsAdapter.OnNotificationClickListener;
import awais.instagrabber.asyncs.NotificationsFetcher;
import awais.instagrabber.asyncs.PostFetcher;
import awais.instagrabber.databinding.FragmentNotificationsViewerBinding;
+import awais.instagrabber.dialogs.ProfilePicDialogFragment;
import awais.instagrabber.fragments.settings.MorePreferencesFragmentDirections;
+import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.interfaces.MentionClickListener;
+import awais.instagrabber.models.FeedModel;
+import awais.instagrabber.models.NotificationModel;
import awais.instagrabber.models.enums.NotificationType;
import awais.instagrabber.repositories.responses.FriendshipRepoChangeRootResponse;
import awais.instagrabber.utils.Constants;
@@ -40,6 +48,7 @@ import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.viewmodels.NotificationViewModel;
import awais.instagrabber.webservices.FriendshipService;
+import awais.instagrabber.webservices.MediaService;
import awais.instagrabber.webservices.NewsService;
import awais.instagrabber.webservices.ServiceCallback;
@@ -53,96 +62,146 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
private boolean shouldRefresh = true;
private NotificationViewModel notificationViewModel;
private FriendshipService friendshipService;
- private String userId;
- private String csrfToken;
+ private MediaService mediaService;
private NewsService newsService;
+ private String userId, csrfToken, type;
+ private Context context;
- private final OnNotificationClickListener clickListener = model -> {
- if (model == null) return;
- final String username = model.getUsername();
- final SpannableString title = new SpannableString(username + (TextUtils.isEmpty(model.getText()) ? "" : (":\n" + model.getText())));
- title.setSpan(new RelativeSizeSpan(1.23f), 0, username.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
-
- String[] commentDialogList;
- if (model.getShortCode() != null) {
- commentDialogList = new String[]{
- getString(R.string.open_profile),
- getString(R.string.view_post)
- };
- } else if (model.getType() == NotificationType.REQUEST) {
- commentDialogList = new String[]{
- getString(R.string.open_profile),
- getString(R.string.request_approve),
- getString(R.string.request_reject)
- };
- } else {
- commentDialogList = new String[]{getString(R.string.open_profile)};
+ private final OnNotificationClickListener clickListener = new OnNotificationClickListener() {
+ @Override
+ public void onProfileClick(final String username) {
+ openProfile(username);
}
- final Context context = getContext();
- if (context == null) return;
- final DialogInterface.OnClickListener profileDialogListener = (dialog, which) -> {
- switch (which) {
- case 0:
- openProfile(model.getUsername());
- break;
- case 1:
- if (model.getType() == NotificationType.REQUEST) {
- friendshipService.approve(userId, model.getUserId(), csrfToken, new ServiceCallback() {
- @Override
- public void onSuccess(final FriendshipRepoChangeRootResponse result) {
- // Log.d(TAG, "onSuccess: " + result);
- if (result.getStatus().equals("ok")) {
- onRefresh();
- return;
- }
- Log.e(TAG, "approve: status was not ok!");
- }
- @Override
- public void onFailure(final Throwable t) {
- Log.e(TAG, "approve: onFailure: ", t);
- }
- });
- return;
- }
- final AlertDialog alertDialog = new AlertDialog.Builder(context)
- .setCancelable(false)
- .setView(R.layout.dialog_opening_post)
- .create();
- alertDialog.show();
- new PostFetcher(model.getShortCode(), feedModel -> {
+ @Override
+ public void onPreviewClick(final NotificationModel model) {
+ if (model.getType() == NotificationType.RESPONDED_STORY) {
+ final NavDirections action = NotificationsViewerFragmentDirections.actionNotificationsViewerFragmentToStoryViewerFragment(
+ -1, null, false, false, model.getPostId(), model.getUsername(), false, true);
+ NavHostFragment.findNavController(NotificationsViewerFragment.this).navigate(action);
+ }
+ else {
+ mediaService.fetch(model.getPostId(), new ServiceCallback() {
+ @Override
+ public void onSuccess(final FeedModel feedModel) {
final PostViewV2Fragment fragment = PostViewV2Fragment
.builder(feedModel)
.build();
- fragment.setOnShowListener(dialog1 -> alertDialog.dismiss());
fragment.show(getChildFragmentManager(), "post_view");
- }).execute();
- break;
- case 2:
- friendshipService.ignore(userId, model.getUserId(), csrfToken, new ServiceCallback() {
- @Override
- public void onSuccess(final FriendshipRepoChangeRootResponse result) {
- // Log.d(TAG, "onSuccess: " + result);
- if (result.getStatus().equals("ok")) {
- onRefresh();
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onNotificationClick(final NotificationModel model) {
+ if (model == null) return;
+ final String username = model.getUsername();
+ if (model.getType() == NotificationType.FOLLOW || model.getType() == NotificationType.AYML) {
+ openProfile(username);
+ }
+ else {
+ final SpannableString title = new SpannableString(username + (TextUtils.isEmpty(model.getText()) ? "" : (":\n" + model.getText())));
+ title.setSpan(new RelativeSizeSpan(1.23f), 0, username.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+
+ String[] commentDialogList;
+ if (model.getType() == NotificationType.RESPONDED_STORY) {
+ commentDialogList = new String[]{
+ getString(R.string.open_profile),
+ getString(R.string.view_story)
+ };
+ }
+ else if (model.getPostId() != null) {
+ commentDialogList = new String[]{
+ getString(R.string.open_profile),
+ getString(R.string.view_post)
+ };
+ }
+ else if (model.getType() == NotificationType.REQUEST) {
+ commentDialogList = new String[]{
+ getString(R.string.open_profile),
+ getString(R.string.request_approve),
+ getString(R.string.request_reject)
+ };
+ }
+ else commentDialogList = null; // shouldn't happen
+ final Context context = getContext();
+ if (context == null) return;
+ final DialogInterface.OnClickListener profileDialogListener = (dialog, which) -> {
+ switch (which) {
+ case 0:
+ openProfile(username);
+ break;
+ case 1:
+ if (model.getType() == NotificationType.REQUEST) {
+ friendshipService.approve(userId, model.getUserId(), csrfToken, new ServiceCallback() {
+ @Override
+ public void onSuccess(final FriendshipRepoChangeRootResponse result) {
+ onRefresh();
+ Log.e(TAG, "approve: status was not ok!");
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ Log.e(TAG, "approve: onFailure: ", t);
+ }
+ });
return;
}
- Log.e(TAG, "ignore: status was not ok!");
- }
+ else if (model.getType() == NotificationType.RESPONDED_STORY) {
+ final NavDirections action = NotificationsViewerFragmentDirections.actionNotificationsViewerFragmentToStoryViewerFragment(
+ -1, null, false, false, model.getPostId(), model.getUsername(), false, true);
+ NavHostFragment.findNavController(NotificationsViewerFragment.this).navigate(action);
+ return;
+ }
+ final AlertDialog alertDialog = new AlertDialog.Builder(context)
+ .setCancelable(false)
+ .setView(R.layout.dialog_opening_post)
+ .create();
+ alertDialog.show();
+ mediaService.fetch(model.getPostId(), new ServiceCallback() {
+ @Override
+ public void onSuccess(final FeedModel feedModel) {
+ final PostViewV2Fragment fragment = PostViewV2Fragment
+ .builder(feedModel)
+ .build();
+ fragment.setOnShowListener(dialog1 -> alertDialog.dismiss());
+ fragment.show(getChildFragmentManager(), "post_view");
+ }
- @Override
- public void onFailure(final Throwable t) {
- Log.e(TAG, "ignore: onFailure: ", t);
- }
- });
- break;
+ @Override
+ public void onFailure(final Throwable t) {
+ Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
+ }
+ });
+ break;
+ case 2:
+ friendshipService.ignore(userId, model.getUserId(), csrfToken, new ServiceCallback() {
+ @Override
+ public void onSuccess(final FriendshipRepoChangeRootResponse result) {
+ onRefresh();
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ Log.e(TAG, "ignore: onFailure: ", t);
+ }
+ });
+ break;
+ }
+ };
+ new AlertDialog.Builder(context)
+ .setTitle(title)
+ .setItems(commentDialogList, profileDialogListener)
+ .setNegativeButton(R.string.cancel, null)
+ .show();
}
- };
- new AlertDialog.Builder(context)
- .setTitle(title)
- .setItems(commentDialogList, profileDialogListener)
- .setNegativeButton(R.string.cancel, null)
- .show();
+ }
};
private final MentionClickListener mentionClickListener = (view, text, isHashtag, isLocation) -> {
if (getContext() == null) return;
@@ -158,7 +217,7 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- final Context context = getContext();
+ context = getContext();
if (context == null) return;
NotificationManagerCompat.from(context.getApplicationContext()).cancel(Constants.ACTIVITY_NOTIFICATION_ID);
final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);
@@ -166,7 +225,7 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
Toast.makeText(context, R.string.activity_notloggedin, Toast.LENGTH_SHORT).show();
}
friendshipService = FriendshipService.getInstance();
- newsService = NewsService.getInstance();
+ mediaService = MediaService.getInstance();
userId = CookieUtils.getUserIdFromCookie(cookie);
csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
}
@@ -191,6 +250,8 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
}
private void init() {
+ final NotificationsViewerFragmentArgs fragmentArgs = NotificationsViewerFragmentArgs.fromBundle(getArguments());
+ type = fragmentArgs.getType();
final Context context = getContext();
CookieUtils.setupCookies(settingsHelper.getString(Constants.COOKIE));
binding.swipeRefreshLayout.setOnRefreshListener(this);
@@ -205,23 +266,39 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
@Override
public void onRefresh() {
binding.swipeRefreshLayout.setRefreshing(true);
- new NotificationsFetcher(notificationModels -> {
- binding.swipeRefreshLayout.setRefreshing(false);
- notificationViewModel.getList().postValue(notificationModels);
- final String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
- newsService.markChecked(timestamp, csrfToken, new ServiceCallback() {
- @Override
- public void onSuccess(@NonNull final Boolean result) {
- // Log.d(TAG, "onResponse: body: " + result);
- if (!result) Log.e(TAG, "onSuccess: Error marking activity checked, response is false");
- }
+ switch (type) {
+ case "notif":
+ new NotificationsFetcher(true, new FetchListener>() {
+ @Override
+ public void onResult(final List notificationModels) {
+ binding.swipeRefreshLayout.setRefreshing(false);
+ notificationViewModel.getList().postValue(notificationModels);
+ }
- @Override
- public void onFailure(final Throwable t) {
- Log.e(TAG, "onFailure: Error marking activity checked", t);
- }
- });
- }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ @Override
+ public void onFailure(Throwable t) {
+ binding.swipeRefreshLayout.setRefreshing(false);
+ Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
+ }
+ }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ break;
+ case "ayml":
+ newsService = NewsService.getInstance();
+ newsService.fetchSuggestions(csrfToken, new ServiceCallback>() {
+ @Override
+ public void onSuccess(final List notificationModels) {
+ binding.swipeRefreshLayout.setRefreshing(false);
+ notificationViewModel.getList().postValue(notificationModels);
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ binding.swipeRefreshLayout.setRefreshing(false);
+ Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
+ }
+ });
+ break;
+ }
}
private void openProfile(final String username) {
diff --git a/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java b/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java
index 4b77b6e3..da96177c 100644
--- a/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java
@@ -26,6 +26,7 @@ import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowManager;
+import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ScrollView;
import android.widget.Toast;
@@ -33,6 +34,7 @@ import android.widget.ViewSwitcher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.content.PermissionChecker;
import androidx.core.view.ViewCompat;
@@ -70,6 +72,7 @@ import awais.instagrabber.customviews.VideoPlayerCallbackAdapter;
import awais.instagrabber.customviews.VideoPlayerViewHelper;
import awais.instagrabber.customviews.drawee.AnimatedZoomableController;
import awais.instagrabber.databinding.DialogPostViewBinding;
+import awais.instagrabber.fragments.main.ProfileFragment;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.PostChild;
import awais.instagrabber.models.ProfileModel;
@@ -114,6 +117,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
private DialogInterface.OnShowListener onShowListener;
private boolean isLoggedIn;
private boolean hasBeenToggled = false;
+ private CharSequence postCaption = null;
private final VerticalDragHelper.OnVerticalDragListener onVerticalDragListener = new VerticalDragHelper.OnVerticalDragListener() {
@@ -306,6 +310,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
@Override
public void onDestroyView() {
super.onDestroyView();
+ if (feedModel == null) return;
switch (feedModel.getItemType()) {
case MEDIA_TYPE_VIDEO:
if (videoPlayerViewHelper != null) {
@@ -414,8 +419,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
private void init() {
if (feedModel == null) return;
- final String cookie = settingsHelper.getString(Constants.COOKIE);
- isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != null;
+ isLoggedIn = !TextUtils.isEmpty(COOKIE) && CookieUtils.getUserIdFromCookie(COOKIE) != null;
if (!wasPaused && (sharedProfilePicElement != null || sharedMainPostElement != null)) {
binding.getRoot().getBackground().mutate().setAlpha(0);
}
@@ -534,7 +538,16 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
}
});
binding.like.setOnLongClickListener(v -> {
- Utils.displayToastAboveView(context, v, getString(R.string.like_without_count));
+ final NavController navController = getNavController();
+ if (navController != null && isLoggedIn) {
+ final Bundle bundle = new Bundle();
+ bundle.putString("postId", feedModel.getPostId());
+ bundle.putBoolean("isComment", false);
+ navController.navigate(R.id.action_global_likesViewerFragment, bundle);
+ }
+ else {
+ Utils.displayToastAboveView(context, v, getString(R.string.like_without_count));
+ }
return true;
});
}
@@ -701,13 +714,52 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
}
private void setupCaption() {
- final CharSequence postCaption = feedModel.getPostCaption();
+ postCaption = feedModel.getPostCaption();
binding.date.setText(Utils.datetimeParser.format(new Date(feedModel.getTimestamp() * 1000L)));
- if (TextUtils.isEmpty(postCaption)) {
+ if (!feedModel.getProfileModel().getId().equals(CookieUtils.getUserIdFromCookie(COOKIE)) && TextUtils.isEmpty(postCaption)) {
binding.caption.setVisibility(View.GONE);
+ binding.translateTitle.setVisibility(View.GONE);
binding.captionToggle.setVisibility(View.GONE);
return;
}
+ if (feedModel.getProfileModel().getId().equals(CookieUtils.getUserIdFromCookie(COOKIE))) {
+ binding.editCaption.setVisibility(View.VISIBLE);
+ binding.editCaption.setOnClickListener(v -> {
+ final EditText input = new EditText(context);
+ input.setText(postCaption);
+ new AlertDialog.Builder(context)
+ .setTitle(R.string.edit_caption)
+ .setView(input)
+ .setPositiveButton(R.string.confirm, (d, w) -> {
+ binding.editCaption.setVisibility(View.GONE);
+ mediaService.editCaption(
+ feedModel.getPostId(),
+ CookieUtils.getUserIdFromCookie(COOKIE),
+ input.getText().toString(),
+ CookieUtils.getCsrfTokenFromCookie(COOKIE),
+ new ServiceCallback() {
+ @Override
+ public void onSuccess(final Boolean result) {
+ binding.editCaption.setVisibility(View.VISIBLE);
+ if (result) {
+ feedModel.setPostCaption(input.getText().toString());
+ binding.caption.setText(input.getText().toString());
+ }
+ else Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ Log.e(TAG, "Error editing caption", t);
+ Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
+ binding.editCaption.setVisibility(View.VISIBLE);
+ }
+ });
+ })
+ .setNegativeButton(R.string.cancel, null)
+ .show();
+ });
+ }
binding.caption.addOnHashtagListener(autoLinkItem -> {
final NavController navController = NavHostFragment.findNavController(this);
final Bundle bundle = new Bundle();
@@ -737,11 +789,33 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
binding.captionParent.getBackground().mutate().setAlpha((int) (128 + (128 * (slideOffset < 0 ? 0 : slideOffset))));
}
});
- binding.caption.setOnClickListener(v -> {
+ binding.captionFrame.setOnClickListener(v -> {
if (bottomSheetBehavior == null) return;
if (bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) return;
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
});
+ if (TextUtils.isEmpty(feedModel.getCaptionId()))
+ binding.translateTitle.setVisibility(View.GONE);
+ else binding.translateTitle.setOnClickListener(v -> {
+ mediaService.translate(feedModel.getCaptionId(), "1", new ServiceCallback() {
+ @Override
+ public void onSuccess(final String result) {
+ if (TextUtils.isEmpty(result)) {
+ Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ binding.translateTitle.setOnClickListener(null);
+ binding.translatedCaption.setVisibility(View.VISIBLE);
+ binding.translatedCaption.setText(result);
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ Log.e(TAG, "Error translating comment", t);
+ Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
+ }
+ });
+ });
binding.captionToggle.setOnClickListener(v -> {
if (bottomSheetBehavior == null) return;
switch (bottomSheetBehavior.getState()) {
@@ -894,6 +968,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
@Override
public void onItemClicked(final int position) {
+ toggleDetails();
}
@Override
diff --git a/app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java
index fbb9f8f7..ed16f43c 100644
--- a/app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java
@@ -40,24 +40,26 @@ import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.PostsLayoutPreferences;
import awais.instagrabber.models.enums.PostItemType;
import awais.instagrabber.utils.Constants;
+import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.DownloadUtils;
+import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
import static androidx.core.content.PermissionChecker.checkSelfPermission;
import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION;
+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;
+ private String username, cookie, profileId;
private ActionMode actionMode;
private SwipeRefreshLayout root;
private AppCompatActivity fragmentActivity;
- private boolean shouldRefresh = true;
+ private boolean isLoggedIn, shouldRefresh = true;
private PostItemType type;
- private String profileId;
private Set selectedFeedModels;
private FeedModel downloadFeedModel;
private int downloadChildPosition = -1;
@@ -225,6 +227,8 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
@Override
public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {
+ cookie = settingsHelper.getString(Constants.COOKIE);
+ isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != null;
if (root != null) {
shouldRefresh = false;
return root;
@@ -281,7 +285,7 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
private void setupPosts() {
binding.posts.setViewModelStoreOwner(this)
.setLifeCycleOwner(this)
- .setPostFetchService(new SavedPostFetchService(profileId, type))
+ .setPostFetchService(new SavedPostFetchService(profileId, type, isLoggedIn))
.setLayoutPreferences(layoutPreferences)
.addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
.setFeedItemCallback(feedItemCallback)
diff --git a/app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java
new file mode 100644
index 00000000..dd851198
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java
@@ -0,0 +1,220 @@
+package awais.instagrabber.fragments;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.fragment.app.Fragment;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.navigation.NavDirections;
+import androidx.navigation.fragment.NavHostFragment;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import awais.instagrabber.R;
+import awais.instagrabber.adapters.FeedStoriesListAdapter;
+import awais.instagrabber.adapters.FeedStoriesListAdapter.OnFeedStoryClickListener;
+import awais.instagrabber.adapters.HighlightStoriesListAdapter;
+import awais.instagrabber.adapters.HighlightStoriesListAdapter.OnHighlightStoryClickListener;
+import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
+import awais.instagrabber.databinding.FragmentStoryListViewerBinding;
+import awais.instagrabber.fragments.main.FeedFragment;
+import awais.instagrabber.fragments.settings.MorePreferencesFragmentDirections;
+import awais.instagrabber.models.FeedStoryModel;
+import awais.instagrabber.models.HighlightModel;
+import awais.instagrabber.utils.Constants;
+import awais.instagrabber.utils.CookieUtils;
+import awais.instagrabber.utils.TextUtils;
+import awais.instagrabber.viewmodels.FeedStoriesViewModel;
+import awais.instagrabber.viewmodels.ArchivesViewModel;
+import awais.instagrabber.webservices.ServiceCallback;
+import awais.instagrabber.webservices.StoriesService;
+import awais.instagrabber.webservices.StoriesService.ArchiveFetchResponse;
+
+import static awais.instagrabber.utils.Utils.settingsHelper;
+
+public final class StoryListViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
+ private static final String TAG = "StoryListViewerFragment";
+
+ private AppCompatActivity fragmentActivity;
+ private FragmentStoryListViewerBinding binding;
+ private SwipeRefreshLayout root;
+ private boolean shouldRefresh = true, firstRefresh = true;
+ private FeedStoriesViewModel feedStoriesViewModel;
+ private ArchivesViewModel archivesViewModel;
+ private StoriesService storiesService;
+ private Context context;
+ private String type, endCursor = null;
+ private RecyclerLazyLoader lazyLoader;
+ private FeedStoriesListAdapter adapter;
+
+ private final OnFeedStoryClickListener clickListener = new OnFeedStoryClickListener() {
+ @Override
+ public void onFeedStoryClick(final FeedStoryModel model, final int position) {
+ if (model == null) return;
+ final NavDirections action = StoryListViewerFragmentDirections.actionStoryListFragmentToStoryViewerFragment(position, null, false, false, null, null, false, false);
+ NavHostFragment.findNavController(StoryListViewerFragment.this).navigate(action);
+ }
+
+ @Override
+ public void onProfileClick(final String username) {
+ openProfile(username);
+ }
+ };
+
+ private final OnHighlightStoryClickListener archiveClickListener = new OnHighlightStoryClickListener() {
+ @Override
+ public void onHighlightClick(final HighlightModel model, final int position) {
+ if (model == null) return;
+ final NavDirections action = StoryListViewerFragmentDirections.actionStoryListFragmentToStoryViewerFragment(
+ position, getString(R.string.action_archive), false, false, null, null, true, false);
+ NavHostFragment.findNavController(StoryListViewerFragment.this).navigate(action);
+ }
+
+ @Override
+ public void onProfileClick(final String username) {
+ openProfile(username);
+ }
+ };
+
+ private final ServiceCallback cb = new ServiceCallback() {
+ @Override
+ public void onSuccess(final ArchiveFetchResponse result) {
+ endCursor = result.getNextCursor();
+ final List models = archivesViewModel.getList().getValue();
+ final List modelsCopy = models == null ? new ArrayList<>() : new ArrayList<>(models);
+ modelsCopy.addAll(result.getResult());
+ archivesViewModel.getList().postValue(modelsCopy);
+ binding.swipeRefreshLayout.setRefreshing(false);
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ Log.e(TAG, "Error", t);
+ try {
+ final Context context = getContext();
+ Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
+ }
+ catch (Exception e) {}
+ }
+ };
+
+ @Override
+ public void onCreate(@Nullable final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ fragmentActivity = (AppCompatActivity) requireActivity();
+ context = getContext();
+ if (context == null) return;
+ storiesService = StoriesService.getInstance();
+ }
+
+ @NonNull
+ @Override
+ public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {
+ if (root != null) {
+ shouldRefresh = false;
+ return root;
+ }
+ binding = FragmentStoryListViewerBinding.inflate(getLayoutInflater());
+ root = binding.getRoot();
+ return root;
+ }
+
+ @Override
+ public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
+ if (!shouldRefresh) return;
+ init();
+ shouldRefresh = false;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ final ActionBar actionBar = fragmentActivity.getSupportActionBar();
+ if (actionBar != null) actionBar.setTitle(type == "feed" ? R.string.feed_stories : R.string.action_archive);
+ }
+
+ @Override
+ public void onDestroy() {
+ if (archivesViewModel != null) archivesViewModel.getList().postValue(null);
+ super.onDestroy();
+ }
+
+ private void init() {
+ final Context context = getContext();
+ if (getArguments() == null) return;
+ final StoryListViewerFragmentArgs fragmentArgs = StoryListViewerFragmentArgs.fromBundle(getArguments());
+ type = fragmentArgs.getType();
+ binding.swipeRefreshLayout.setOnRefreshListener(this);
+ final LinearLayoutManager layoutManager = new LinearLayoutManager(context);
+ final ActionBar actionBar = fragmentActivity.getSupportActionBar();
+ if (type == "feed") {
+ if (actionBar != null) actionBar.setTitle(R.string.feed_stories);
+ feedStoriesViewModel = new ViewModelProvider(fragmentActivity).get(FeedStoriesViewModel.class);
+ adapter = new FeedStoriesListAdapter(clickListener);
+ binding.rvStories.setLayoutManager(layoutManager);
+ binding.rvStories.setAdapter(adapter);
+ feedStoriesViewModel.getList().observe(getViewLifecycleOwner(), adapter::submitList);
+ }
+ else {
+ if (actionBar != null) actionBar.setTitle(R.string.action_archive);
+ lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
+ if (!TextUtils.isEmpty(endCursor)) onRefresh();
+ endCursor = null;
+ });
+ binding.rvStories.addOnScrollListener(lazyLoader);
+ archivesViewModel = new ViewModelProvider(fragmentActivity).get(ArchivesViewModel.class);
+ final HighlightStoriesListAdapter adapter = new HighlightStoriesListAdapter(archiveClickListener);
+ binding.rvStories.setLayoutManager(layoutManager);
+ binding.rvStories.setAdapter(adapter);
+ archivesViewModel.getList().observe(getViewLifecycleOwner(), adapter::submitList);
+ }
+ onRefresh();
+ }
+
+ @Override
+ public void onRefresh() {
+ binding.swipeRefreshLayout.setRefreshing(true);
+ if (type == "feed" && firstRefresh) {
+ binding.swipeRefreshLayout.setRefreshing(false);
+ adapter.submitList(feedStoriesViewModel.getList().getValue());
+ firstRefresh = false;
+ }
+ else if (type == "feed") {
+ final String cookie = settingsHelper.getString(Constants.COOKIE);
+ storiesService.getFeedStories(CookieUtils.getCsrfTokenFromCookie(cookie), new ServiceCallback>() {
+ @Override
+ public void onSuccess(final List result) {
+ feedStoriesViewModel.getList().postValue(result);
+ binding.swipeRefreshLayout.setRefreshing(false);
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ Log.e(TAG, "failed", t);
+ Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+ else if (type == "archive") {
+ storiesService.fetchArchive(endCursor, cb);
+ }
+ }
+
+ private void openProfile(final String username) {
+ final NavDirections action = MorePreferencesFragmentDirections
+ .actionGlobalProfileFragment("@" + username);
+ NavHostFragment.findNavController(this).navigate(action);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java
index c0e1e119..946034a4 100644
--- a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java
@@ -11,6 +11,7 @@ import android.os.Handler;
import android.util.Log;
import android.util.Pair;
import android.view.GestureDetector;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -20,6 +21,9 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
@@ -54,6 +58,9 @@ import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.text.NumberFormat;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
@@ -62,10 +69,7 @@ import awais.instagrabber.BuildConfig;
import awais.instagrabber.R;
import awais.instagrabber.adapters.StoriesAdapter;
import awais.instagrabber.asyncs.PostFetcher;
-import awais.instagrabber.asyncs.QuizAction;
-import awais.instagrabber.asyncs.RespondAction;
import awais.instagrabber.asyncs.SeenAction;
-import awais.instagrabber.asyncs.VoteAction;
import awais.instagrabber.asyncs.direct_messages.CreateThreadAction;
import awais.instagrabber.customviews.helpers.SwipeGestureListener;
import awais.instagrabber.databinding.FragmentStoryViewerBinding;
@@ -78,16 +82,27 @@ import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.models.stickers.PollModel;
import awais.instagrabber.models.stickers.QuestionModel;
import awais.instagrabber.models.stickers.QuizModel;
+import awais.instagrabber.models.stickers.SliderModel;
+import awais.instagrabber.models.stickers.SwipeUpModel;
+import awais.instagrabber.repositories.requests.directmessages.BroadcastOptions;
+import awais.instagrabber.repositories.responses.StoryStickerResponse;
+import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponse;
import awais.instagrabber.utils.Constants;
+import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.DownloadUtils;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
+import awais.instagrabber.viewmodels.ArchivesViewModel;
import awais.instagrabber.viewmodels.FeedStoriesViewModel;
import awais.instagrabber.viewmodels.HighlightsViewModel;
import awais.instagrabber.viewmodels.StoriesViewModel;
+import awais.instagrabber.webservices.DirectMessagesService;
import awais.instagrabber.webservices.ServiceCallback;
import awais.instagrabber.webservices.StoriesService;
import awaisomereport.LogCollector;
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
import static awais.instagrabber.customviews.helpers.SwipeGestureListener.SWIPE_THRESHOLD;
import static awais.instagrabber.customviews.helpers.SwipeGestureListener.SWIPE_VELOCITY_THRESHOLD;
@@ -100,8 +115,7 @@ public class StoryViewerFragment extends Fragment {
private AppCompatActivity fragmentActivity;
private View root;
- private @NonNull
- FragmentStoryViewerBinding binding;
+ private FragmentStoryViewerBinding binding;
private String currentStoryUsername;
private StoriesAdapter storiesAdapter;
private SwipeEvent swipeEvent;
@@ -115,26 +129,32 @@ public class StoryViewerFragment extends Fragment {
private QuestionModel question;
private String[] mentions;
private QuizModel quiz;
+ private SliderModel slider;
private MenuItem menuDownload;
private MenuItem menuDm;
private SimpleExoPlayer player;
private boolean isHashtag, isLoc;
private String highlight;
- private boolean fetching = false;
+ private boolean fetching = false, sticking = false, shouldRefresh = true;
private int currentFeedStoryIndex;
+ private double sliderValue;
private StoriesViewModel storiesViewModel;
- private boolean shouldRefresh = true;
private StoryViewerFragmentArgs fragmentArgs;
private ViewModel viewModel;
- private boolean isHighlight;
+ private boolean isHighlight, isArchive, isNotification;
+ private DirectMessagesService directMessagesService;
private final String cookie = settingsHelper.getString(Constants.COOKIE);
+ private final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
+ private final String userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
+ private final String deviceId = settingsHelper.getString(Constants.DEVICE_UUID);
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fragmentActivity = (AppCompatActivity) requireActivity();
storiesService = StoriesService.getInstance();
+ directMessagesService = DirectMessagesService.getInstance(csrfToken, userIdFromCookie, deviceId);
setHasOptionsMenu(true);
}
@@ -188,24 +208,33 @@ public class StoryViewerFragment extends Fragment {
new AlertDialog.Builder(context)
.setTitle(R.string.reply_story)
.setView(input)
- .setPositiveButton(R.string.ok, (d, w) -> new CreateThreadAction(cookie, currentStory.getUserId(), threadId -> {
- // try {
- // final StoryReplyBroadcastOptions options = new StoryReplyBroadcastOptions(
- // threadId,
- // input.getText().toString(),
- // currentStory.getStoryMediaId(),
- // currentStory.getUserId()
- // );
- // final DirectThreadBroadcaster broadcast = new DirectThreadBroadcaster(threadId);
- // broadcast.setOnTaskCompleteListener(result -> Toast.makeText(
- // context,
- // result != null ? R.string.answered_story : R.string.downloader_unknown_error,
- // Toast.LENGTH_SHORT
- // ).show());
- // broadcast.execute(options);
- // } catch (UnsupportedEncodingException e) {
- // Log.e(TAG, "Error", e);
- // }
+ .setPositiveButton(R.string.confirm, (d, w) -> new CreateThreadAction(cookie, currentStory.getUserId(), threadId -> {
+ try {
+ final Call request = directMessagesService
+ .broadcastStoryReply(BroadcastOptions.ThreadIdOrUserIds.of(threadId),
+ input.getText().toString(),
+ currentStory.getStoryMediaId(),
+ currentStory.getUserId());
+ request.enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull final Call call,
+ @NonNull final Response response) {
+ if (!response.isSuccessful()) {
+ Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onFailure(@NonNull final Call call, @NonNull final Throwable t) {
+ Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
+ Log.e(TAG, "onFailure: ", t);
+ }
+ });
+ } catch (UnsupportedEncodingException e) {
+ Log.e(TAG, "Error", e);
+ }
}).execute())
.setNegativeButton(R.string.cancel, null)
.show();
@@ -244,9 +273,13 @@ public class StoryViewerFragment extends Fragment {
currentFeedStoryIndex = fragmentArgs.getFeedStoryIndex();
highlight = fragmentArgs.getHighlight();
isHighlight = !TextUtils.isEmpty(highlight);
+ isArchive = fragmentArgs.getIsArchive();
+ isNotification = fragmentArgs.getIsNotification();
if (currentFeedStoryIndex >= 0) {
viewModel = isHighlight
- ? new ViewModelProvider(fragmentActivity).get(HighlightsViewModel.class)
+ ? isArchive
+ ? new ViewModelProvider(fragmentActivity).get(ArchivesViewModel.class)
+ : new ViewModelProvider(fragmentActivity).get(HighlightsViewModel.class)
: new ViewModelProvider(fragmentActivity).get(FeedStoriesViewModel.class);
}
// feedStoryModels = feedStoriesViewModel.getList().getValue();
@@ -275,7 +308,10 @@ public class StoryViewerFragment extends Fragment {
final boolean hasFeedStories;
List> models = null;
if (currentFeedStoryIndex >= 0) {
- if (isHighlight) {
+ if (isArchive) {
+ final ArchivesViewModel archivesViewModel = (ArchivesViewModel) viewModel;
+ models = archivesViewModel.getList().getValue();
+ } else if (isHighlight) {
final HighlightsViewModel highlightsViewModel = (HighlightsViewModel) viewModel;
models = highlightsViewModel.getList().getValue();
// final HighlightModel model = models.get(currentFeedStoryIndex);
@@ -296,15 +332,16 @@ public class StoryViewerFragment extends Fragment {
swipeEvent = isRightSwipe -> {
final List storyModels = storiesViewModel.getList().getValue();
final int storiesLen = storyModels == null ? 0 : storyModels.size();
+ if (sticking) {
+ Toast.makeText(context, R.string.follower_wait_to_load, Toast.LENGTH_SHORT).show();
+ return;
+ }
if (storiesLen <= 0) return;
final boolean isLeftSwipe = !isRightSwipe;
final boolean endOfCurrentStories = slidePos + 1 >= storiesLen;
final boolean swipingBeyondCurrentStories = (endOfCurrentStories && isLeftSwipe) || (slidePos == 0 && isRightSwipe);
if (swipingBeyondCurrentStories && hasFeedStories) {
final int index = currentFeedStoryIndex;
- if (settingsHelper.getBoolean(MARK_AS_SEEN)) {
- new SeenAction(cookie, currentStory).execute();
- }
if ((isRightSwipe && index == 0) || (isLeftSwipe && index == finalModels.size() - 1)) {
Toast.makeText(context, R.string.no_more_stories, Toast.LENGTH_SHORT).show();
return;
@@ -312,7 +349,7 @@ public class StoryViewerFragment extends Fragment {
final Object feedStoryModel = isRightSwipe
? finalModels.get(index - 1)
: finalModels.size() == index + 1 ? null : finalModels.get(index + 1);
- paginateStories(feedStoryModel, context, isRightSwipe, currentFeedStoryIndex == finalModels.size() - 2);
+ paginateStories(feedStoryModel, finalModels.get(index), context, isRightSwipe, currentFeedStoryIndex == finalModels.size() - 2);
return;
}
if (isRightSwipe) {
@@ -351,8 +388,12 @@ public class StoryViewerFragment extends Fragment {
if (hasFeedStories) {
binding.btnBackward.setVisibility(currentFeedStoryIndex == 0 ? View.INVISIBLE : View.VISIBLE);
binding.btnForward.setVisibility(currentFeedStoryIndex == finalModels.size() - 1 ? View.INVISIBLE : View.VISIBLE);
- binding.btnBackward.setOnClickListener(v -> paginateStories(finalModels.get(currentFeedStoryIndex - 1), context, true, false));
- binding.btnForward.setOnClickListener(v -> paginateStories(finalModels.get(currentFeedStoryIndex + 1), context, false,
+ binding.btnBackward.setOnClickListener(v -> paginateStories(finalModels.get(currentFeedStoryIndex - 1),
+ finalModels.get(currentFeedStoryIndex),
+ context, true, false));
+ binding.btnForward.setOnClickListener(v -> paginateStories(finalModels.get(currentFeedStoryIndex + 1),
+ finalModels.get(currentFeedStoryIndex),
+ context, false,
currentFeedStoryIndex == finalModels.size() - 2));
}
@@ -365,6 +406,14 @@ public class StoryViewerFragment extends Fragment {
startActivity(intent);
}
});
+ binding.swipeUp.setOnClickListener(v -> {
+ final Object tag = v.getTag();
+ if (tag instanceof CharSequence) {
+ final Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse(tag.toString()));
+ startActivity(intent);
+ }
+ });
binding.viewStoryPost.setOnClickListener(v -> {
final Object tag = v.getTag();
if (!(tag instanceof CharSequence)) return;
@@ -406,15 +455,30 @@ public class StoryViewerFragment extends Fragment {
poll.getLeftChoice() + " (" + poll.getLeftCount() + ")",
poll.getRightChoice() + " (" + poll.getRightCount() + ")"
}), (d, w) -> {
- if (!TextUtils.isEmpty(cookie))
- new VoteAction(currentStory, poll, cookie, choice -> {
- if (choice > -1) {
- poll.setMyChoice(choice);
- Toast.makeText(context, R.string.votef_story_poll, Toast.LENGTH_SHORT).show();
- return;
- }
- Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
- }).execute(w);
+ if (!TextUtils.isEmpty(cookie)) {
+ sticking = true;
+ storiesService.respondToPoll(
+ currentStory.getStoryMediaId().split("_")[0],
+ poll.getId(),
+ w,
+ userIdFromCookie,
+ csrfToken,
+ new ServiceCallback() {
+ @Override
+ public void onSuccess(final StoryStickerResponse result) {
+ sticking = false;
+ poll.setMyChoice(w);
+ Toast.makeText(context, R.string.votef_story_poll, Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ sticking = false;
+ Log.e(TAG, "Error responding", t);
+ Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
})
.setPositiveButton(R.string.cancel, null)
.show();
@@ -426,21 +490,36 @@ public class StoryViewerFragment extends Fragment {
new AlertDialog.Builder(context)
.setTitle(question.getQuestion())
.setView(input)
- .setPositiveButton(R.string.ok, (d, w) -> new RespondAction(currentStory, question, cookie, result -> {
- if (result) {
- Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT).show();
- } else
- Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
- }).execute(input.getText().toString()))
+ .setPositiveButton(R.string.confirm, (d, w) -> {
+ sticking = true;
+ storiesService.respondToQuestion(
+ currentStory.getStoryMediaId().split("_")[0],
+ question.getId(),
+ input.getText().toString(),
+ userIdFromCookie,
+ csrfToken,
+ new ServiceCallback() {
+ @Override
+ public void onSuccess(final StoryStickerResponse result) {
+ sticking = false;
+ Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ sticking = false;
+ Log.e(TAG, "Error responding", t);
+ Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
+ }
+ });
+ })
.setNegativeButton(R.string.cancel, null)
.show();
} else if (tag instanceof String[]) {
mentions = (String[]) tag;
new AlertDialog.Builder(context)
.setTitle(R.string.story_mentions)
- .setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, mentions), (d, w) -> {
- openProfile(mentions[w]);
- })
+ .setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, mentions), (d, w) -> openProfile(mentions[w]))
.setPositiveButton(R.string.cancel, null)
.show();
} else if (tag instanceof QuizModel) {
@@ -451,27 +530,122 @@ public class StoryViewerFragment extends Fragment {
new AlertDialog.Builder(context)
.setTitle(quiz.getMyChoice() > -1 ? getString(R.string.story_quizzed) : quiz.getQuestion())
.setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, choices), (d, w) -> {
- if (quiz.getMyChoice() == -1 && !TextUtils.isEmpty(cookie))
- new QuizAction(currentStory, quiz, cookie, choice -> {
- if (choice > -1) {
- quiz.setMyChoice(choice);
- Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT).show();
- return;
- }
- Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
- }).execute(w);
+ if (quiz.getMyChoice() == -1 && !TextUtils.isEmpty(cookie)) {
+ sticking = true;
+ storiesService.respondToQuiz(
+ currentStory.getStoryMediaId().split("_")[0],
+ quiz.getId(),
+ w,
+ userIdFromCookie,
+ csrfToken,
+ new ServiceCallback() {
+ @Override
+ public void onSuccess(final StoryStickerResponse result) {
+ sticking = false;
+ quiz.setMyChoice(w);
+ Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ sticking = false;
+ Log.e(TAG, "Error responding", t);
+ Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
})
.setPositiveButton(R.string.cancel, null)
.show();
+ } else if (tag instanceof SliderModel) {
+ slider = (SliderModel) tag;
+ NumberFormat percentage = NumberFormat.getPercentInstance();
+ percentage.setMaximumFractionDigits(2);
+ LinearLayout sliderView = new LinearLayout(context);
+ sliderView.setLayoutParams(new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT));
+ sliderView.setOrientation(LinearLayout.VERTICAL);
+ TextView tv = new TextView(context);
+ tv.setGravity(Gravity.CENTER_HORIZONTAL);
+ final SeekBar input = new SeekBar(context);
+ double avg = slider.getAverage() * 100;
+ input.setProgress((int) avg);
+ sliderView.addView(input);
+ sliderView.addView(tv);
+ if (slider.getMyChoice().isNaN() && slider.canVote()) {
+ input.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ sliderValue = progress / 100.0;
+ tv.setText(percentage.format(sliderValue));
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+ });
+ new AlertDialog.Builder(context)
+ .setTitle(TextUtils.isEmpty(slider.getQuestion()) ? slider.getEmoji() : slider.getQuestion())
+ .setMessage(getResources().getQuantityString(R.plurals.slider_info,
+ slider.getVoteCount(),
+ slider.getVoteCount(),
+ percentage.format(slider.getAverage())))
+ .setView(sliderView)
+ .setPositiveButton(R.string.confirm, (d, w) -> {
+ sticking = true;
+ storiesService.respondToSlider(
+ currentStory.getStoryMediaId().split("_")[0],
+ slider.getId(),
+ sliderValue,
+ userIdFromCookie,
+ csrfToken,
+ new ServiceCallback() {
+ @Override
+ public void onSuccess(final StoryStickerResponse result) {
+ sticking = false;
+ slider.setMyChoice(sliderValue);
+ Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ sticking = false;
+ Log.e(TAG, "Error responding", t);
+ Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
+ }
+ });
+ })
+ .setNegativeButton(R.string.cancel, null)
+ .show();
+ } else {
+ input.setEnabled(false);
+ tv.setText(getString(R.string.slider_answer, percentage.format(slider.getMyChoice())));
+ new AlertDialog.Builder(context)
+ .setTitle(TextUtils.isEmpty(slider.getQuestion()) ? slider.getEmoji() : slider.getQuestion())
+ .setMessage(getResources().getQuantityString(R.plurals.slider_info,
+ slider.getVoteCount(),
+ slider.getVoteCount(),
+ percentage.format(slider.getAverage())))
+ .setView(sliderView)
+ .setPositiveButton(R.string.ok, null)
+ .show();
+ }
}
};
binding.poll.setOnClickListener(storyActionListener);
binding.answer.setOnClickListener(storyActionListener);
binding.mention.setOnClickListener(storyActionListener);
binding.quiz.setOnClickListener(storyActionListener);
+ binding.slider.setOnClickListener(storyActionListener);
}
private void resetView() {
+ final Context context = getContext();
slidePos = 0;
lastSlidePos = 0;
if (menuDownload != null) menuDownload.setVisible(false);
@@ -480,10 +654,23 @@ public class StoryViewerFragment extends Fragment {
releasePlayer();
String currentStoryMediaId = null;
if (currentFeedStoryIndex >= 0) {
- if (isHighlight) {
+ if (isArchive) {
+ final ArchivesViewModel archivesViewModel = (ArchivesViewModel) viewModel;
+ final List models = archivesViewModel.getList().getValue();
+ if (models == null || models.isEmpty() || currentFeedStoryIndex >= models.size()) {
+ Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ final HighlightModel model = models.get(currentFeedStoryIndex);
+ currentStoryMediaId = model.getId();
+ currentStoryUsername = model.getTitle();
+ } else if (isHighlight) {
final HighlightsViewModel highlightsViewModel = (HighlightsViewModel) viewModel;
final List models = highlightsViewModel.getList().getValue();
- if (models == null || models.isEmpty() || currentFeedStoryIndex >= models.size()) return;
+ if (models == null || models.isEmpty() || currentFeedStoryIndex >= models.size()) {
+ Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
+ return;
+ }
final HighlightModel model = models.get(currentFeedStoryIndex);
currentStoryMediaId = model.getId();
currentStoryUsername = model.getTitle();
@@ -502,7 +689,12 @@ public class StoryViewerFragment extends Fragment {
isHashtag = fragmentArgs.getIsHashtag();
isLoc = fragmentArgs.getIsLoc();
final boolean hasUsername = !TextUtils.isEmpty(currentStoryUsername);
- if (hasUsername) {
+ if (isHighlight) {
+ final ActionBar actionBar = fragmentActivity.getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setTitle(highlight);
+ }
+ } else if (hasUsername) {
currentStoryUsername = currentStoryUsername.replace("@", "");
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
if (actionBar != null) {
@@ -511,6 +703,31 @@ public class StoryViewerFragment extends Fragment {
}
storiesViewModel.getList().setValue(Collections.emptyList());
if (currentStoryMediaId == null) return;
+ if (isNotification) {
+ storiesService.fetch(currentStoryMediaId, new ServiceCallback() {
+ @Override
+ public void onSuccess(final StoryModel storyModel) {
+ fetching = false;
+ binding.storiesList.setVisibility(View.GONE);
+ if (storyModel == null) {
+ storiesViewModel.getList().setValue(Collections.emptyList());
+ currentStory = null;
+ return;
+ }
+ storiesViewModel.getList().setValue(Collections.singletonList(storyModel));
+ currentStory = storyModel;
+ refreshStory();
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ final Context context = getContext();
+ Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
+ Log.e(TAG, "Error", t);
+ }
+ });
+ return;
+ }
final ServiceCallback> storyCallback = new ServiceCallback>() {
@Override
public void onSuccess(final List storyModels) {
@@ -521,7 +738,9 @@ public class StoryViewerFragment extends Fragment {
binding.storiesList.setVisibility(View.GONE);
return;
}
- binding.storiesList.setVisibility(View.VISIBLE);
+ binding.storiesList.setVisibility((storyModels.size() == 1 && currentFeedStoryIndex == -1) ? View.GONE : View.VISIBLE);
+ binding.btnBackward.setVisibility(currentFeedStoryIndex == -1 ? View.GONE : View.VISIBLE);
+ binding.btnForward.setVisibility(currentFeedStoryIndex == -1 ? View.GONE : View.VISIBLE);
storiesViewModel.getList().setValue(storyModels);
currentStory = storyModels.get(0);
refreshStory();
@@ -529,6 +748,8 @@ public class StoryViewerFragment extends Fragment {
@Override
public void onFailure(final Throwable t) {
+ final Context context = getContext();
+ Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
Log.e(TAG, "Error", t);
}
};
@@ -543,7 +764,7 @@ public class StoryViewerFragment extends Fragment {
private void refreshStory() {
if (binding.storiesList.getVisibility() == View.VISIBLE) {
final List storyModels = storiesViewModel.getList().getValue();
- if (storyModels != null) {
+ if (storyModels != null && storyModels.size() > 0) {
StoryModel item = storyModels.get(lastSlidePos);
if (item != null) {
item.setCurrentSlide(false);
@@ -587,6 +808,17 @@ public class StoryViewerFragment extends Fragment {
binding.quiz.setVisibility(quiz != null ? View.VISIBLE : View.GONE);
binding.quiz.setTag(quiz);
+ slider = currentStory.getSlider();
+ binding.slider.setVisibility(slider != null ? View.VISIBLE : View.GONE);
+ binding.slider.setTag(slider);
+
+ final SwipeUpModel swipeUp = currentStory.getSwipeUp();
+ if (swipeUp != null) {
+ binding.swipeUp.setVisibility(View.VISIBLE);
+ binding.swipeUp.setText(swipeUp.getText());
+ binding.swipeUp.setTag(swipeUp.getUrl());
+ }
+
releasePlayer();
if (isHashtag || isLoc) {
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
@@ -670,8 +902,8 @@ public class StoryViewerFragment extends Fragment {
@Override
public void onLoadCompleted(final int windowIndex,
@Nullable final MediaSource.MediaPeriodId mediaPeriodId,
- final LoadEventInfo loadEventInfo,
- final MediaLoadData mediaLoadData) {
+ @NonNull final LoadEventInfo loadEventInfo,
+ @NonNull final MediaLoadData mediaLoadData) {
if (menuDownload != null) menuDownload.setVisible(true);
if (currentStory.canReply() && menuDm != null && !TextUtils.isEmpty(cookie))
menuDm.setVisible(true);
@@ -681,8 +913,8 @@ public class StoryViewerFragment extends Fragment {
@Override
public void onLoadStarted(final int windowIndex,
@Nullable final MediaSource.MediaPeriodId mediaPeriodId,
- final LoadEventInfo loadEventInfo,
- final MediaLoadData mediaLoadData) {
+ @NonNull final LoadEventInfo loadEventInfo,
+ @NonNull final MediaLoadData mediaLoadData) {
if (menuDownload != null) menuDownload.setVisible(true);
if (currentStory.canReply() && menuDm != null && !TextUtils.isEmpty(cookie))
menuDm.setVisible(true);
@@ -692,17 +924,17 @@ public class StoryViewerFragment extends Fragment {
@Override
public void onLoadCanceled(final int windowIndex,
@Nullable final MediaSource.MediaPeriodId mediaPeriodId,
- final LoadEventInfo loadEventInfo,
- final MediaLoadData mediaLoadData) {
+ @NonNull final LoadEventInfo loadEventInfo,
+ @NonNull final MediaLoadData mediaLoadData) {
binding.progressView.setVisibility(View.GONE);
}
@Override
public void onLoadError(final int windowIndex,
@Nullable final MediaSource.MediaPeriodId mediaPeriodId,
- final LoadEventInfo loadEventInfo,
- final MediaLoadData mediaLoadData,
- final IOException error,
+ @NonNull final LoadEventInfo loadEventInfo,
+ @NonNull final MediaLoadData mediaLoadData,
+ @NonNull final IOException error,
final boolean wasCanceled) {
if (menuDownload != null) menuDownload.setVisible(false);
if (menuDm != null) menuDm.setVisible(false);
@@ -745,12 +977,25 @@ public class StoryViewerFragment extends Fragment {
player = null;
}
- private void paginateStories(Object feedStory, Context context, boolean backward, boolean last) {
- if (feedStory != null) {
+ private void paginateStories(Object newFeedStory, Object oldFeedStory, Context context, boolean backward, boolean last) {
+ if (newFeedStory != null) {
if (fetching) {
Toast.makeText(context, R.string.be_patient, Toast.LENGTH_SHORT).show();
return;
}
+ if (settingsHelper.getBoolean(MARK_AS_SEEN)
+ && oldFeedStory instanceof FeedStoryModel
+ && viewModel instanceof FeedStoriesViewModel) {
+ final FeedStoriesViewModel feedStoriesViewModel = (FeedStoriesViewModel) viewModel;
+ final FeedStoryModel oldFeedStoryModel = (FeedStoryModel) oldFeedStory;
+ if (!oldFeedStoryModel.isFullyRead()) {
+ oldFeedStoryModel.setFullyRead(true);
+ final List models = feedStoriesViewModel.getList().getValue();
+ final List modelsCopy = models == null ? new ArrayList<>() : new ArrayList<>(models);
+ modelsCopy.set(currentFeedStoryIndex, oldFeedStoryModel);
+ feedStoriesViewModel.getList().postValue(models);
+ }
+ }
fetching = true;
binding.btnBackward.setVisibility(currentFeedStoryIndex == 1 && backward ? View.INVISIBLE : View.VISIBLE);
binding.btnForward.setVisibility(last ? View.INVISIBLE : View.VISIBLE);
diff --git a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java
index 6b6cfadf..55ea42eb 100644
--- a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java
@@ -18,7 +18,9 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.AppCompatButton;
import androidx.appcompat.widget.AppCompatImageView;
import androidx.fragment.app.Fragment;
@@ -42,6 +44,7 @@ import awais.instagrabber.utils.Utils;
public class DirectMessageSettingsFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
private static final String TAG = "DirectMsgsSettingsFrag";
+ private AppCompatActivity fragmentActivity;
private RecyclerView userList;
private RecyclerView leftUserList;
private EditText titleText;
@@ -82,6 +85,7 @@ public class DirectMessageSettingsFragment extends Fragment implements SwipeRefr
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ fragmentActivity = (AppCompatActivity) requireActivity();
basicClickListener = v -> {
final Object tag = v.getTag();
if (tag instanceof ProfileModel) {
@@ -147,6 +151,11 @@ public class DirectMessageSettingsFragment extends Fragment implements SwipeRefr
threadTitle = DirectMessageSettingsFragmentArgs.fromBundle(getArguments()).getTitle();
binding.swipeRefreshLayout.setEnabled(false);
+ final ActionBar actionBar = fragmentActivity.getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setTitle(threadTitle);
+ }
+
userList = binding.userList;
userList.setHasFixedSize(true);
userList.setLayoutManager(layoutManager);
@@ -209,7 +218,7 @@ public class DirectMessageSettingsFragment extends Fragment implements SwipeRefr
class ChangeSettings extends AsyncTask {
String action, argument;
boolean ok = false;
- private String text;
+ private final String text;
public ChangeSettings(final String text) {
this.text = text;
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 afc14848..832977c8 100644
--- a/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java
@@ -12,6 +12,7 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.Toast;
import androidx.activity.OnBackPressedCallback;
import androidx.activity.OnBackPressedDispatcher;
@@ -47,6 +48,7 @@ import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.FeedStoryModel;
import awais.instagrabber.models.PostsLayoutPreferences;
import awais.instagrabber.utils.Constants;
+import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.DownloadUtils;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.viewmodels.FeedStoriesViewModel;
@@ -55,6 +57,7 @@ import awais.instagrabber.webservices.StoriesService;
import static androidx.core.content.PermissionChecker.checkSelfPermission;
import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION;
+import static awais.instagrabber.utils.Utils.settingsHelper;
public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
private static final String TAG = "FeedFragment";
@@ -74,6 +77,7 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
private int downloadChildPosition = -1;
private PostsLayoutPreferences layoutPreferences = Utils.getPostsLayoutPreferences(Constants.PREF_POSTS_LAYOUT);
private RecyclerView storiesRecyclerView;
+ private MenuItem storyListMenu;
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
@Override
@@ -272,26 +276,23 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
@Override
public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
inflater.inflate(R.menu.feed_menu, menu);
+ storyListMenu = menu.findItem(R.id.storyList);
+ storyListMenu.setVisible(!storiesFetching);
}
@Override
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
- if (item.getItemId() == R.id.layout) {
+ if (item.getItemId() == R.id.storyList) {
+ final NavDirections action = FeedFragmentDirections.actionGlobalStoryListViewerFragment("feed");
+ NavHostFragment.findNavController(FeedFragment.this).navigate(action);
+ }
+ else if (item.getItemId() == R.id.layout) {
showPostsLayoutPreferences();
return true;
}
return super.onOptionsItemSelected(item);
}
- @Override
- public void onResume() {
- super.onResume();
- updateSwipeRefreshState();
- // if (videoAwareRecyclerScroller != null && shouldAutoPlay) {
- // videoAwareRecyclerScroller.startPlaying();
- // }
- }
-
@Override
public void onRefresh() {
binding.feedRecyclerView.refresh();
@@ -309,9 +310,13 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
@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;
+ final boolean granted = grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED;
final Context context = getContext();
if (context == null) return;
+ if (!granted) {
+ Toast.makeText(context, R.string.download_permission, Toast.LENGTH_SHORT).show();
+ return;
+ }
if (requestCode == STORAGE_PERM_REQUEST_CODE && granted) {
if (downloadFeedModel == null) return;
DownloadUtils.showDownloadDialog(context, downloadFeedModel, downloadChildPosition);
@@ -346,11 +351,22 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
}
private void setupFeedStories() {
+ if (storyListMenu != null) storyListMenu.setVisible(false);
feedStoriesViewModel = new ViewModelProvider(fragmentActivity).get(FeedStoriesViewModel.class);
- final FeedStoriesAdapter feedStoriesAdapter = new FeedStoriesAdapter((model, position) -> {
- final NavDirections action = FeedFragmentDirections.actionFeedFragmentToStoryViewerFragment(position, null, false, false, null, null);
- NavHostFragment.findNavController(this).navigate(action);
- });
+ final FeedStoriesAdapter feedStoriesAdapter = new FeedStoriesAdapter(
+ new FeedStoriesAdapter.OnFeedStoryClickListener() {
+ @Override
+ public void onFeedStoryClick(FeedStoryModel model, int position) {
+ final NavDirections action = FeedFragmentDirections.actionFeedFragmentToStoryViewerFragment(position, null, false, false, null, null, false, false);
+ NavHostFragment.findNavController(FeedFragment.this).navigate(action);
+ }
+
+ @Override
+ public void onFeedStoryLongClick(FeedStoryModel model, int position) {
+ navigateToProfile("@" + model.getProfileModel().getUsername());
+ }
+ }
+ );
final Context context = getContext();
if (context == null) return;
storiesRecyclerView = new RecyclerView(context);
@@ -362,18 +378,20 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
storiesRecyclerView.setLayoutManager(new LinearLayoutManager(context, RecyclerView.HORIZONTAL, false));
storiesRecyclerView.setAdapter(feedStoriesAdapter);
fragmentActivity.setCollapsingView(storiesRecyclerView);
- feedStoriesViewModel.getList().observe(fragmentActivity, feedStoriesAdapter::submitList);
+ feedStoriesViewModel.getList().observe(getViewLifecycleOwner(), feedStoriesAdapter::submitList);
fetchStories();
}
private void fetchStories() {
+ final String cookie = settingsHelper.getString(Constants.COOKIE);
storiesFetching = true;
updateSwipeRefreshState();
- storiesService.getFeedStories(new ServiceCallback>() {
+ storiesService.getFeedStories(CookieUtils.getCsrfTokenFromCookie(cookie), new ServiceCallback>() {
@Override
public void onSuccess(final List result) {
feedStoriesViewModel.getList().postValue(result);
storiesFetching = false;
+ if (storyListMenu != null) storyListMenu.setVisible(true);
updateSwipeRefreshState();
}
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 4c12b4e3..d5ab821d 100644
--- a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java
@@ -54,7 +54,6 @@ import awais.instagrabber.R;
import awais.instagrabber.activities.MainActivity;
import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.adapters.HighlightsAdapter;
-import awais.instagrabber.asyncs.HighlightsFetcher;
import awais.instagrabber.asyncs.ProfileFetcher;
import awais.instagrabber.asyncs.ProfilePostFetchService;
import awais.instagrabber.asyncs.UsernameFetcher;
@@ -75,6 +74,7 @@ import awais.instagrabber.dialogs.ProfilePicDialogFragment;
import awais.instagrabber.fragments.PostViewV2Fragment;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.FeedModel;
+import awais.instagrabber.models.HighlightModel;
import awais.instagrabber.models.PostsLayoutPreferences;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.StoryModel;
@@ -89,6 +89,7 @@ import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.viewmodels.HighlightsViewModel;
import awais.instagrabber.webservices.FriendshipService;
+import awais.instagrabber.webservices.MediaService;
import awais.instagrabber.webservices.ServiceCallback;
import awais.instagrabber.webservices.StoriesService;
@@ -113,6 +114,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
private Handler usernameSettingHandler;
private FriendshipService friendshipService;
private StoriesService storiesService;
+ private MediaService mediaService;
private boolean shouldRefresh = true;
private boolean hasStories = false;
private HighlightsAdapter highlightsAdapter;
@@ -298,6 +300,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
fragmentActivity = (MainActivity) requireActivity();
friendshipService = FriendshipService.getInstance();
storiesService = StoriesService.getInstance();
+ mediaService = MediaService.getInstance();
accountRepository = AccountRepository.getInstance(AccountDataSource.getInstance(getContext()));
favoriteRepository = FavoriteRepository.getInstance(FavoriteDataSource.getInstance(getContext()));
setHasOptionsMenu(true);
@@ -370,10 +373,10 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
}
if (item.getItemId() == R.id.restrict) {
if (!isLoggedIn) return false;
- final String action = profileModel.getRestricted() ? "Unrestrict" : "Restrict";
+ final String action = profileModel.isRestricted() ? "Unrestrict" : "Restrict";
friendshipService.toggleRestrict(
profileModel.getId(),
- !profileModel.getRestricted(),
+ !profileModel.isRestricted(),
CookieUtils.getCsrfTokenFromCookie(cookie),
new ServiceCallback() {
@Override
@@ -392,7 +395,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
if (item.getItemId() == R.id.block) {
final String userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
if (!isLoggedIn) return false;
- if (profileModel.getBlocked()) {
+ if (profileModel.isBlocked()) {
friendshipService.unblock(
userIdFromCookie,
profileModel.getId(),
@@ -434,6 +437,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
@Override
public void onRefresh() {
+ profileDetailsBinding.mainProfileImage.setVisibility(View.INVISIBLE);
fetchProfileDetails();
}
@@ -571,44 +575,99 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
fetchStoryAndHighlights(profileId);
}
setupButtons(profileId, myId);
- if (!profileId.equals(myId)) {
- profileDetailsBinding.favCb.setVisibility(View.VISIBLE);
- favoriteRepository.getFavorite(username.substring(1), FavoriteType.USER, new RepositoryCallback() {
- @Override
- public void onSuccess(final Favorite result) {
- profileDetailsBinding.favCb.setChecked(true);
- profileDetailsBinding.favCb.setButtonDrawable(R.drawable.ic_star_check_24);
- }
+ profileDetailsBinding.favChip.setVisibility(View.VISIBLE);
+ final FavoriteRepository favoriteRepository = FavoriteRepository.getInstance(FavoriteDataSource.getInstance(getContext()));
+ favoriteRepository.getFavorite(profileModel.getUsername(), FavoriteType.USER, new RepositoryCallback() {
+ @Override
+ public void onSuccess(final Favorite result) {
+ profileDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24);
+ profileDetailsBinding.favChip.setText(R.string.added_to_favs_short);
+ favoriteRepository.insertOrUpdateFavorite(new Favorite(
+ result.getId(),
+ profileModel.getUsername(),
+ FavoriteType.USER,
+ profileModel.getName(),
+ profileModel.getSdProfilePic(),
+ result.getDateAdded()
+ ), new RepositoryCallback() {
+ @Override
+ public void onSuccess(final Void result) {}
- @Override
- public void onDataNotAvailable() {
- profileDetailsBinding.favCb.setChecked(false);
- profileDetailsBinding.favCb.setButtonDrawable(R.drawable.ic_outline_star_plus_24);
- }
- });
- } else {
- profileDetailsBinding.favCb.setVisibility(View.GONE);
- }
+ @Override
+ public void onDataNotAvailable() {}
+ });
+ }
+
+ @Override
+ public void onDataNotAvailable() {
+ profileDetailsBinding.favChip.setChipIconResource(R.drawable.ic_outline_star_plus_24);
+ profileDetailsBinding.favChip.setText(R.string.add_to_favorites);
+ }
+ });
+ profileDetailsBinding.favChip.setOnClickListener(
+ v -> favoriteRepository.getFavorite(profileModel.getUsername(), FavoriteType.USER, new RepositoryCallback() {
+ @Override
+ public void onSuccess(final Favorite result) {
+ favoriteRepository.deleteFavorite(profileModel.getUsername(), FavoriteType.USER, new RepositoryCallback() {
+ @Override
+ public void onSuccess(final Void result) {
+ profileDetailsBinding.favChip.setText(R.string.add_to_favorites);
+ profileDetailsBinding.favChip.setChipIconResource(R.drawable.ic_outline_star_plus_24);
+ showSnackbar(getString(R.string.removed_from_favs));
+ }
+
+ @Override
+ public void onDataNotAvailable() {}
+ });
+ }
+
+ @Override
+ public void onDataNotAvailable() {
+ favoriteRepository.insertOrUpdateFavorite(new Favorite(
+ 0,
+ profileModel.getUsername(),
+ FavoriteType.USER,
+ profileModel.getName(),
+ profileModel.getSdProfilePic(),
+ new Date()
+ ), new RepositoryCallback() {
+ @Override
+ public void onSuccess(final Void result) {
+ profileDetailsBinding.favChip.setText(R.string.added_to_favs);
+ profileDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24);
+ showSnackbar(getString(R.string.added_to_favs));
+ }
+
+ @Override
+ public void onDataNotAvailable() {}
+ });
+ }
+ }));
profileDetailsBinding.mainProfileImage.setImageURI(profileModel.getHdProfilePic());
+ profileDetailsBinding.mainProfileImage.setVisibility(View.VISIBLE);
- final long followersCount = profileModel.getFollowersCount();
- final long followingCount = profileModel.getFollowingCount();
+ final Long followersCount = profileModel.getFollowersCount();
+ final Long followingCount = profileModel.getFollowingCount();
final String postCount = String.valueOf(profileModel.getPostCount());
- SpannableStringBuilder span = new SpannableStringBuilder(getString(R.string.main_posts_count,
- postCount));
+ SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline,
+ profileModel.getPostCount() > 2000000000L ? 2000000000 : profileModel.getPostCount().intValue(),
+ postCount));
span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0);
span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0);
profileDetailsBinding.mainPostCount.setText(span);
+ profileDetailsBinding.mainPostCount.setVisibility(View.VISIBLE);
final String followersCountStr = String.valueOf(followersCount);
final int followersCountStrLen = followersCountStr.length();
- span = new SpannableStringBuilder(getString(R.string.main_posts_followers,
- followersCountStr));
+ span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_followers,
+ followersCount > 2000000000L ? 2000000000 : followersCount.intValue(),
+ followersCountStr));
span.setSpan(new RelativeSizeSpan(1.2f), 0, followersCountStrLen, 0);
span.setSpan(new StyleSpan(Typeface.BOLD), 0, followersCountStrLen, 0);
profileDetailsBinding.mainFollowers.setText(span);
+ profileDetailsBinding.mainFollowers.setVisibility(View.VISIBLE);
final String followingCountStr = String.valueOf(followingCount);
final int followingCountStrLen = followingCountStr.length();
@@ -617,6 +676,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
span.setSpan(new RelativeSizeSpan(1.2f), 0, followingCountStrLen, 0);
span.setSpan(new StyleSpan(Typeface.BOLD), 0, followingCountStrLen, 0);
profileDetailsBinding.mainFollowing.setText(span);
+ profileDetailsBinding.mainFollowing.setVisibility(View.VISIBLE);
profileDetailsBinding.mainFullName.setText(TextUtils.isEmpty(profileModel.getName()) ? profileModel.getUsername()
: profileModel.getName());
@@ -640,6 +700,51 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
.trim()));
profileDetailsBinding.mainBiography
.addOnURLClickListener(autoLinkItem -> Utils.openURL(getContext(), autoLinkItem.getOriginalText().trim()));
+ profileDetailsBinding.mainBiography.setOnClickListener(v -> {
+ String[] commentDialogList;
+ if (!TextUtils.isEmpty(cookie)) {
+ commentDialogList = new String[]{
+ getResources().getString(R.string.bio_copy),
+ getResources().getString(R.string.bio_translate)
+ };
+ } else {
+ commentDialogList = new String[]{
+ getResources().getString(R.string.bio_copy)
+ };
+ }
+ new AlertDialog.Builder(context)
+ .setItems(commentDialogList, (d,w) -> {
+ switch (w) {
+ case 0:
+ Utils.copyText(context, biography);
+ break;
+ case 1:
+ mediaService.translate(profileModel.getId(), "3", new ServiceCallback() {
+ @Override
+ public void onSuccess(final String result) {
+ if (TextUtils.isEmpty(result)) {
+ Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ new AlertDialog.Builder(context)
+ .setTitle(profileModel.getUsername())
+ .setMessage(result)
+ .setPositiveButton(R.string.ok, null)
+ .show();
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ Log.e(TAG, "Error translating bio", t);
+ Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
+ }
+ });
+ break;
+ }
+ })
+ .setNegativeButton(R.string.cancel, null)
+ .show();
+ });
profileDetailsBinding.mainBiography.setOnLongClickListener(v -> {
Utils.copyText(context, biography);
return true;
@@ -687,6 +792,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
}
private void setupButtons(final String profileId, final String myId) {
+ profileDetailsBinding.btnTagged.setVisibility(profileModel.isReallyPrivate() ? View.GONE : View.VISIBLE);
if (isLoggedIn) {
if (profileId.equals(myId)) {
profileDetailsBinding.btnTagged.setVisibility(View.VISIBLE);
@@ -696,15 +802,29 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
profileDetailsBinding.btnSaved.setText(R.string.saved);
return;
}
- profileDetailsBinding.btnTagged.setVisibility(View.GONE);
profileDetailsBinding.btnSaved.setVisibility(View.GONE);
profileDetailsBinding.btnLiked.setVisibility(View.GONE);
profileDetailsBinding.btnDM.setVisibility(View.VISIBLE); // maybe there is a judgment mechanism?
profileDetailsBinding.btnFollow.setVisibility(View.VISIBLE);
- if (profileModel.getFollowing()) {
+ if (profileModel.isFollowing() || profileModel.isFollower()) {
+ profileDetailsBinding.mainStatus.setVisibility(View.VISIBLE);
+ if (!profileModel.isFollowing()) {
+ profileDetailsBinding.mainStatus.setChipBackgroundColor(getResources().getColorStateList(R.color.blue_800));
+ profileDetailsBinding.mainStatus.setText(R.string.status_follower);
+ }
+ else if (!profileModel.isFollower()) {
+ profileDetailsBinding.mainStatus.setChipBackgroundColor(getResources().getColorStateList(R.color.deep_orange_800));
+ profileDetailsBinding.mainStatus.setText(R.string.status_following);
+ }
+ else {
+ profileDetailsBinding.mainStatus.setChipBackgroundColor(getResources().getColorStateList(R.color.green_800));
+ profileDetailsBinding.mainStatus.setText(R.string.status_mutual);
+ }
+ }
+ if (profileModel.isFollowing()) {
profileDetailsBinding.btnFollow.setText(R.string.unfollow);
profileDetailsBinding.btnFollow.setIconResource(R.drawable.ic_outline_person_add_disabled_24);
- } else if (profileModel.getRequested()) {
+ } else if (profileModel.isRequested()) {
profileDetailsBinding.btnFollow.setText(R.string.cancel);
profileDetailsBinding.btnFollow.setIconResource(R.drawable.ic_outline_person_add_disabled_24);
} else {
@@ -713,16 +833,15 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
}
if (restrictMenuItem != null) {
restrictMenuItem.setVisible(true);
- if (profileModel.getRestricted()) {
+ if (profileModel.isRestricted()) {
restrictMenuItem.setTitle(R.string.unrestrict);
} else {
restrictMenuItem.setTitle(R.string.restrict);
}
}
- profileDetailsBinding.btnTagged.setVisibility(profileModel.isReallyPrivate() ? View.GONE : View.VISIBLE);
if (blockMenuItem != null) {
blockMenuItem.setVisible(true);
- if (profileModel.getBlocked()) {
+ if (profileModel.isBlocked()) {
blockMenuItem.setTitle(R.string.unblock);
} else {
blockMenuItem.setTitle(R.string.block);
@@ -732,7 +851,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
}
if (!profileModel.isReallyPrivate() && restrictMenuItem != null) {
restrictMenuItem.setVisible(true);
- if (profileModel.getRestricted()) {
+ if (profileModel.isRestricted()) {
restrictMenuItem.setTitle(R.string.unrestrict);
} else {
restrictMenuItem.setTitle(R.string.restrict);
@@ -760,20 +879,55 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
Log.e(TAG, "Error", t);
}
});
- new HighlightsFetcher(profileId,
- result -> {
- highlightsFetching = false;
- if (result != null) {
- profileDetailsBinding.highlightsList.setVisibility(View.VISIBLE);
- highlightsViewModel.getList().postValue(result);
- } else profileDetailsBinding.highlightsList.setVisibility(View.GONE);
- }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ storiesService.fetchHighlights(profileId,
+ new ServiceCallback>() {
+ @Override
+ public void onSuccess(final List result) {
+ highlightsFetching = false;
+ if (result != null) {
+ profileDetailsBinding.highlightsList.setVisibility(View.VISIBLE);
+ highlightsViewModel.getList().postValue(result);
+ }
+ else profileDetailsBinding.highlightsList.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ profileDetailsBinding.highlightsList.setVisibility(View.GONE);
+ Log.e(TAG, "Error", t);
+ }
+ });
}
private void setupCommonListeners() {
+ final Context context = getContext();
final String userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
profileDetailsBinding.btnFollow.setOnClickListener(v -> {
- if (profileModel.getFollowing() || profileModel.getRequested()) {
+ if (profileModel.isFollowing() && profileModel.isPrivate()) {
+ new AlertDialog.Builder(context)
+ .setTitle(R.string.priv_acc)
+ .setMessage(R.string.priv_acc_confirm)
+ .setPositiveButton(R.string.confirm, (d, w) ->
+ friendshipService.unfollow(
+ userIdFromCookie,
+ profileModel.getId(),
+ CookieUtils.getCsrfTokenFromCookie(cookie),
+ new ServiceCallback() {
+ @Override
+ public void onSuccess(final FriendshipRepoChangeRootResponse result) {
+ // Log.d(TAG, "Unfollow success: " + result);
+ onRefresh();
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ Log.e(TAG, "Error unfollowing", t);
+ }
+ }))
+ .setNegativeButton(R.string.cancel, null)
+ .show();
+ }
+ else if (profileModel.isFollowing() || profileModel.isRequested()) {
friendshipService.unfollow(
userIdFromCookie,
profileModel.getId(),
@@ -854,72 +1008,18 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
if (which == 1) {
// show stories
final NavDirections action = ProfileFragmentDirections
- .actionProfileFragmentToStoryViewerFragment(-1, null, false, false, profileModel.getId(), username);
+ .actionProfileFragmentToStoryViewerFragment(-1, null, false, false, profileModel.getId(), username, false, false);
NavHostFragment.findNavController(this).navigate(action);
return;
}
showProfilePicDialog();
};
- final Context context = getContext();
if (context == null) return;
new AlertDialog.Builder(context)
.setItems(options, profileDialogListener)
.setNegativeButton(R.string.cancel, null)
.show();
});
- profileDetailsBinding.favCb.setOnCheckedChangeListener((buttonView, isChecked) -> {
- // do not do anything if state matches the db, as listener is set before profile details are set
- final Context context = getContext();
- if (context == null) return;
- final String finalUsername = username.startsWith("@") ? username.substring(1) : username;
- favoriteRepository.getFavorite(finalUsername, FavoriteType.USER, new RepositoryCallback() {
- @Override
- public void onSuccess(final Favorite result) {
- if (isChecked) return; // already a fav
- buttonView.setVisibility(View.GONE);
- profileDetailsBinding.favProgress.setVisibility(View.VISIBLE);
- favoriteRepository.deleteFavorite(finalUsername, FavoriteType.USER, new RepositoryCallback() {
- @Override
- public void onSuccess(final Void result) {
- profileDetailsBinding.favCb.setButtonDrawable(R.drawable.ic_outline_star_plus_24);
- profileDetailsBinding.favProgress.setVisibility(View.GONE);
- profileDetailsBinding.favCb.setVisibility(View.VISIBLE);
- showSnackbar(getString(R.string.removed_from_favs));
- }
-
- @Override
- public void onDataNotAvailable() {}
- });
- }
-
- @Override
- public void onDataNotAvailable() {
- if (!isChecked) return; // not in fav already
- buttonView.setVisibility(View.GONE);
- profileDetailsBinding.favProgress.setVisibility(View.VISIBLE);
- final Favorite model = new Favorite(
- -1,
- finalUsername,
- FavoriteType.USER,
- profileModel.getName(),
- profileModel.getSdProfilePic(),
- new Date()
- );
- favoriteRepository.insertOrUpdateFavorite(model, new RepositoryCallback() {
- @Override
- public void onSuccess(final Void result) {
- profileDetailsBinding.favCb.setButtonDrawable(R.drawable.ic_star_check_24);
- profileDetailsBinding.favProgress.setVisibility(View.GONE);
- profileDetailsBinding.favCb.setVisibility(View.VISIBLE);
- showSnackbar(getString(R.string.added_to_favs));
- }
-
- @Override
- public void onDataNotAvailable() {}
- });
- }
- });
- });
}
private void showSnackbar(final String message) {
@@ -951,7 +1051,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
private void setupPosts() {
binding.postsRecyclerView.setViewModelStoreOwner(this)
.setLifeCycleOwner(this)
- .setPostFetchService(new ProfilePostFetchService(profileModel))
+ .setPostFetchService(new ProfilePostFetchService(profileModel, isLoggedIn))
.setLayoutPreferences(layoutPreferences)
.addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
.setFeedItemCallback(feedItemCallback)
@@ -969,7 +1069,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
highlightsViewModel = new ViewModelProvider(fragmentActivity).get(HighlightsViewModel.class);
highlightsAdapter = new HighlightsAdapter((model, position) -> {
final NavDirections action = ProfileFragmentDirections
- .actionProfileFragmentToStoryViewerFragment(position, model.getTitle(), false, false, null, null);
+ .actionProfileFragmentToStoryViewerFragment(position, model.getTitle(), false, false, null, null, false, false);
NavHostFragment.findNavController(this).navigate(action);
});
final Context context = getContext();
diff --git a/app/src/main/java/awais/instagrabber/fragments/settings/AboutFragment.java b/app/src/main/java/awais/instagrabber/fragments/settings/AboutFragment.java
index 682de804..51158a3b 100644
--- a/app/src/main/java/awais/instagrabber/fragments/settings/AboutFragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/settings/AboutFragment.java
@@ -88,9 +88,10 @@ public class AboutFragment extends BasePreferencesFragment {
preference.setSummary(R.string.about_feedback_summary);
preference.setIconSpaceReserved(false);
preference.setOnPreferenceClickListener(p -> {
- final Intent intent = new Intent(Intent.ACTION_SENDTO);
- intent.setData(Uri.parse(getString(R.string.about_feedback_summary)
- + "?body=Please%20note%20that%20your%20email%20address%20and%20the%20entire%20content%20will%20be%20published%20onto%20GitHub%20issues.%20If%20you%20do%20not%20wish%20to%20do%20that%2C%20use%20other%20contact%20methods%20instead."));
+ final Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.setType("message/rfc822")
+ .putExtra(Intent.EXTRA_EMAIL, getString(R.string.about_feedback_summary))
+ .putExtra(Intent.EXTRA_TEXT, "Please note that your email address and the entire content will be published onto GitHub issues. If you do not wish to do that, use other contact methods instead.");
if (intent.resolveActivity(context.getPackageManager()) != null) startActivity(intent);
return true;
});
diff --git a/app/src/main/java/awais/instagrabber/fragments/settings/BackupPreferencesFragment.java b/app/src/main/java/awais/instagrabber/fragments/settings/BackupPreferencesFragment.java
index 62d59258..0214eabc 100644
--- a/app/src/main/java/awais/instagrabber/fragments/settings/BackupPreferencesFragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/settings/BackupPreferencesFragment.java
@@ -26,10 +26,30 @@ public class BackupPreferencesFragment extends BasePreferencesFragment {
if (context == null) {
return;
}
+ screen.addPreference(getAboutPreference(context));
+ screen.addPreference(getWarningPreference(context));
screen.addPreference(getCreatePreference(context));
screen.addPreference(getRestorePreference(context));
}
+ private Preference getAboutPreference(@NonNull final Context context) {
+ final Preference preference = new Preference(context);
+ preference.setSummary(R.string.backup_summary);
+ preference.setEnabled(false);
+ preference.setIcon(R.drawable.ic_outline_info_24);
+ preference.setIconSpaceReserved(true);
+ return preference;
+ }
+
+ private Preference getWarningPreference(@NonNull final Context context) {
+ final Preference preference = new Preference(context);
+ preference.setSummary(R.string.backup_warning);
+ preference.setEnabled(false);
+ preference.setIcon(R.drawable.ic_warning);
+ preference.setIconSpaceReserved(true);
+ return preference;
+ }
+
private Preference getCreatePreference(@NonNull final Context context) {
final Preference preference = new Preference(context);
preference.setTitle(R.string.create_backup);
diff --git a/app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java b/app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java
index 5dcdde7a..6e2b88b8 100644
--- a/app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java
@@ -134,7 +134,18 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
screen.addPreference(getDivider(context));
if (isLoggedIn) {
screen.addPreference(getPreference(R.string.action_notif, R.drawable.ic_not_liked, preference -> {
- NavHostFragment.findNavController(this).navigate(R.id.action_global_notificationsViewerFragment);
+ final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalNotificationsViewerFragment("notif");
+ NavHostFragment.findNavController(this).navigate(navDirections);
+ return true;
+ }));
+ screen.addPreference(getPreference(R.string.action_ayml, R.drawable.ic_suggested_users, preference -> {
+ final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalNotificationsViewerFragment("ayml");
+ NavHostFragment.findNavController(this).navigate(navDirections);
+ return true;
+ }));
+ screen.addPreference(getPreference(R.string.action_archive, R.drawable.ic_archive, preference -> {
+ final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalStoryListViewerFragment("archive");
+ NavHostFragment.findNavController(this).navigate(navDirections);
return true;
}));
}
diff --git a/app/src/main/java/awais/instagrabber/fragments/settings/SettingsPreferencesFragment.java b/app/src/main/java/awais/instagrabber/fragments/settings/SettingsPreferencesFragment.java
index 0f567583..24e935cf 100644
--- a/app/src/main/java/awais/instagrabber/fragments/settings/SettingsPreferencesFragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/settings/SettingsPreferencesFragment.java
@@ -79,6 +79,7 @@ public class SettingsPreferencesFragment extends BasePreferencesFragment {
screen.addPreference(loggedInUsersPreferenceCategory);
loggedInUsersPreferenceCategory.setIconSpaceReserved(false);
loggedInUsersPreferenceCategory.setTitle(R.string.login_settings);
+ loggedInUsersPreferenceCategory.addPreference(getStorySortPreference());
loggedInUsersPreferenceCategory.addPreference(getMarkStoriesSeenPreference());
loggedInUsersPreferenceCategory.addPreference(getMarkDMSeenPreference());
loggedInUsersPreferenceCategory.addPreference(getEnableActivityNotificationsPreference());
@@ -204,6 +205,25 @@ public class SettingsPreferencesFragment extends BasePreferencesFragment {
return preference;
}
+ private Preference getStorySortPreference() {
+ final Context context = getContext();
+ if (context == null) return null;
+ final ListPreference preference = new ListPreference(context);
+ preference.setSummaryProvider(ListPreference.SimpleSummaryProvider.getInstance());
+ final int length = getResources().getStringArray(R.array.story_sorts).length;
+ final String[] values = new String[length];
+ for (int i = 0; i < length; i++) {
+ values[i] = String.valueOf(i);
+ }
+ preference.setKey(Constants.STORY_SORT);
+ preference.setTitle(R.string.story_sort_setting);
+ preference.setDialogTitle(R.string.story_sort_setting);
+ preference.setEntries(R.array.story_sorts);
+ preference.setIconSpaceReserved(false);
+ preference.setEntryValues(values);
+ return preference;
+ }
+
private Preference getMarkStoriesSeenPreference() {
final Context context = getContext();
if (context == null) return null;
diff --git a/app/src/main/java/awais/instagrabber/models/BasePostModel.java b/app/src/main/java/awais/instagrabber/models/BasePostModel.java
index 38f5f8fd..4f8486d3 100755
--- a/app/src/main/java/awais/instagrabber/models/BasePostModel.java
+++ b/app/src/main/java/awais/instagrabber/models/BasePostModel.java
@@ -11,16 +11,12 @@ import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.utils.Utils;
public abstract class BasePostModel implements Serializable, Selectable {
- protected String postId;
- protected String displayUrl;
- protected String shortCode;
+ protected String postId, displayUrl, shortCode, captionId;
protected CharSequence postCaption;
protected MediaItemType itemType;
- protected boolean isSelected;
- protected boolean isDownloaded;
+ protected boolean isSelected, isDownloaded;
protected long timestamp;
- boolean liked;
- boolean saved;
+ boolean liked, saved;
public boolean getLike() {
return liked;
@@ -46,6 +42,10 @@ public abstract class BasePostModel implements Serializable, Selectable {
return postCaption;
}
+ public final String getCaptionId() {
+ return captionId;
+ }
+
public final String getShortCode() {
return shortCode;
}
diff --git a/app/src/main/java/awais/instagrabber/models/CommentModel.java b/app/src/main/java/awais/instagrabber/models/CommentModel.java
index 9ce02bb0..c66f6916 100755
--- a/app/src/main/java/awais/instagrabber/models/CommentModel.java
+++ b/app/src/main/java/awais/instagrabber/models/CommentModel.java
@@ -11,11 +11,10 @@ public class CommentModel {
private final ProfileModel profileModel;
private final String id;
private final String text;
- private final long likes;
+ private long likes;
private final long timestamp;
private List childCommentModels;
- private final boolean liked;
- private boolean hasNextPage;
+ private boolean liked, hasNextPage;
private String endCursor;
public CommentModel(final String id,
@@ -53,6 +52,11 @@ public class CommentModel {
return liked;
}
+ public void setLiked(boolean liked) {
+ this.likes = liked ? likes + 1 : likes - 1;
+ this.liked = liked;
+ }
+
public ProfileModel getProfileModel() {
return profileModel;
}
diff --git a/app/src/main/java/awais/instagrabber/models/FeedModel.java b/app/src/main/java/awais/instagrabber/models/FeedModel.java
index d9178640..f4e51664 100755
--- a/app/src/main/java/awais/instagrabber/models/FeedModel.java
+++ b/app/src/main/java/awais/instagrabber/models/FeedModel.java
@@ -24,7 +24,7 @@ public final class FeedModel extends PostModel {
private String displayUrl;
private String thumbnailUrl;
private String shortCode;
- private String postCaption;
+ private String postCaption, captionId;
private long commentsCount;
private long timestamp;
private boolean liked;
@@ -76,6 +76,11 @@ public final class FeedModel extends PostModel {
return this;
}
+ public Builder setCaptionId(final String captionId) {
+ this.captionId = captionId;
+ return this;
+ }
+
public Builder setCommentsCount(final long commentsCount) {
this.commentsCount = commentsCount;
return this;
@@ -127,8 +132,8 @@ public final class FeedModel extends PostModel {
}
public FeedModel build() {
- return new FeedModel(profileModel, itemType, viewCount, postId, displayUrl, thumbnailUrl, shortCode, postCaption, commentsCount,
- timestamp, liked, bookmarked, likesCount, locationName, locationId, sliderItems, imageHeight, imageWidth);
+ return new FeedModel(profileModel, itemType, viewCount, postId, displayUrl, thumbnailUrl, shortCode, postCaption, captionId,
+ commentsCount, timestamp, liked, bookmarked, likesCount, locationName, locationId, sliderItems, imageHeight, imageWidth);
}
}
@@ -140,6 +145,7 @@ public final class FeedModel extends PostModel {
final String thumbnailUrl,
final String shortCode,
final String postCaption,
+ final String captionId,
final long commentsCount,
final long timestamp,
final boolean liked,
@@ -150,7 +156,7 @@ public final class FeedModel extends PostModel {
final List sliderItems,
final int imageHeight,
final int imageWidth) {
- super(itemType, postId, displayUrl, thumbnailUrl, shortCode, postCaption, timestamp, liked, bookmarked);
+ super(itemType, postId, displayUrl, thumbnailUrl, shortCode, postCaption, captionId, timestamp, liked, bookmarked);
this.profileModel = profileModel;
this.commentsCount = commentsCount;
this.likesCount = likesCount;
diff --git a/app/src/main/java/awais/instagrabber/models/FeedStoryModel.java b/app/src/main/java/awais/instagrabber/models/FeedStoryModel.java
index e2d9fc77..01ffac76 100755
--- a/app/src/main/java/awais/instagrabber/models/FeedStoryModel.java
+++ b/app/src/main/java/awais/instagrabber/models/FeedStoryModel.java
@@ -1,36 +1,66 @@
package awais.instagrabber.models;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
import java.io.Serializable;
+import java.util.Date;
+
+import awais.instagrabber.utils.Utils;
public final class FeedStoryModel implements Serializable {
private final String storyMediaId;
private final ProfileModel profileModel;
- private StoryModel[] storyModels;
- private boolean fullyRead;
+ private final StoryModel firstStoryModel;
+ private Boolean fullyRead;
+ private final long timestamp;
+ private final int mediaCount;
- public FeedStoryModel(final String storyMediaId, final ProfileModel profileModel, final boolean fullyRead) {
+ public FeedStoryModel(final String storyMediaId, final ProfileModel profileModel, final boolean fullyRead,
+ final long timestamp, final StoryModel firstStoryModel, final int mediaCount) {
this.storyMediaId = storyMediaId;
this.profileModel = profileModel;
this.fullyRead = fullyRead;
+ this.timestamp = timestamp;
+ this.firstStoryModel = firstStoryModel;
+ this.mediaCount = mediaCount;
}
public String getStoryMediaId() {
return storyMediaId;
}
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ @NonNull
+ public String getDateTime() {
+ return Utils.datetimeParser.format(new Date(timestamp * 1000L));
+ }
+
+ public int getMediaCount() {
+ return mediaCount;
+ }
+
public ProfileModel getProfileModel() {
return profileModel;
}
- public void setStoryModels(final StoryModel[] storyModels) {
- this.storyModels = storyModels;
+// public void setFirstStoryModel(final StoryModel firstStoryModel) {
+// this.firstStoryModel = firstStoryModel;
+// }
+
+ public StoryModel getFirstStoryModel() {
+ return firstStoryModel;
}
- public StoryModel[] getStoryModels() {
- return storyModels;
- }
-
- public boolean getFullyRead() {
+ public Boolean isFullyRead() {
return fullyRead;
}
+
+ public void setFullyRead(final boolean fullyRead) {
+ this.fullyRead = fullyRead;
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/models/HashtagModel.java b/app/src/main/java/awais/instagrabber/models/HashtagModel.java
index fbcac9db..58ff7932 100755
--- a/app/src/main/java/awais/instagrabber/models/HashtagModel.java
+++ b/app/src/main/java/awais/instagrabber/models/HashtagModel.java
@@ -29,7 +29,7 @@ public final class HashtagModel implements Serializable {
return sdProfilePic;
}
- public long getPostCount() { return postCount; }
+ public Long getPostCount() { return postCount; }
public boolean getFollowing() { return following; }
}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/models/HighlightModel.java b/app/src/main/java/awais/instagrabber/models/HighlightModel.java
index 70e4032a..86d0327d 100755
--- a/app/src/main/java/awais/instagrabber/models/HighlightModel.java
+++ b/app/src/main/java/awais/instagrabber/models/HighlightModel.java
@@ -1,16 +1,28 @@
package awais.instagrabber.models;
+import androidx.annotation.NonNull;
+
+import java.util.Date;
+
+import awais.instagrabber.utils.Utils;
+
public final class HighlightModel {
private final String title;
private final String id;
private final String thumbnailUrl;
+ private final long timestamp;
+ private final int mediaCount;
public HighlightModel(final String title,
final String id,
- final String thumbnailUrl) {
+ final String thumbnailUrl,
+ final long timestamp,
+ final int mediaCount) {
this.title = title;
this.id = id;
this.thumbnailUrl = thumbnailUrl;
+ this.timestamp = timestamp;
+ this.mediaCount = mediaCount;
}
public String getTitle() {
@@ -24,4 +36,17 @@ public final class HighlightModel {
public String getThumbnailUrl() {
return thumbnailUrl;
}
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ @NonNull
+ public String getDateTime() {
+ return Utils.datetimeParser.format(new Date(timestamp * 1000L));
+ }
+
+ public int getMediaCount() {
+ return mediaCount;
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/models/LocationModel.java b/app/src/main/java/awais/instagrabber/models/LocationModel.java
index fedaa773..b237d4e4 100755
--- a/app/src/main/java/awais/instagrabber/models/LocationModel.java
+++ b/app/src/main/java/awais/instagrabber/models/LocationModel.java
@@ -59,5 +59,5 @@ public final class LocationModel implements Serializable {
return sdProfilePic;
}
- public long getPostCount() { return postCount; }
+ public Long getPostCount() { return postCount; }
}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/models/NotificationModel.java b/app/src/main/java/awais/instagrabber/models/NotificationModel.java
index d461a033..a7b78b47 100755
--- a/app/src/main/java/awais/instagrabber/models/NotificationModel.java
+++ b/app/src/main/java/awais/instagrabber/models/NotificationModel.java
@@ -13,7 +13,7 @@ public final class NotificationModel {
private final String userId;
private final String username;
private final String profilePicUrl;
- private final String shortCode;
+ private final String postId;
private final String previewUrl;
private final NotificationType type;
private final CharSequence text;
@@ -25,7 +25,7 @@ public final class NotificationModel {
final String userId,
final String username,
final String profilePicUrl,
- final String shortCode,
+ final String postId,
final String previewUrl,
final NotificationType type) {
this.id = id;
@@ -34,7 +34,7 @@ public final class NotificationModel {
this.userId = userId;
this.username = username;
this.profilePicUrl = profilePicUrl;
- this.shortCode = shortCode;
+ this.postId = postId;
this.previewUrl = previewUrl;
this.type = type;
}
@@ -47,6 +47,10 @@ public final class NotificationModel {
return text;
}
+ public long getTimestamp() {
+ return timestamp;
+ }
+
@NonNull
public String getDateTime() {
return Utils.datetimeParser.format(new Date(timestamp * 1000L));
@@ -64,8 +68,8 @@ public final class NotificationModel {
return profilePicUrl;
}
- public String getShortCode() {
- return shortCode;
+ public String getPostId() {
+ return postId;
}
public String getPreviewPic() {
diff --git a/app/src/main/java/awais/instagrabber/models/PostModel.java b/app/src/main/java/awais/instagrabber/models/PostModel.java
index e74924d7..75bb6753 100755
--- a/app/src/main/java/awais/instagrabber/models/PostModel.java
+++ b/app/src/main/java/awais/instagrabber/models/PostModel.java
@@ -19,6 +19,7 @@ public class PostModel extends BasePostModel {
final String thumbnailUrl,
final String shortCode,
final CharSequence postCaption,
+ final String captionId,
long timestamp,
boolean liked,
boolean bookmarked) {
@@ -28,6 +29,7 @@ public class PostModel extends BasePostModel {
this.thumbnailUrl = thumbnailUrl;
this.shortCode = shortCode;
this.postCaption = postCaption;
+ this.captionId = captionId;
this.timestamp = timestamp;
this.liked = liked;
this.saved = bookmarked;
diff --git a/app/src/main/java/awais/instagrabber/models/ProfileModel.java b/app/src/main/java/awais/instagrabber/models/ProfileModel.java
index e6367ef6..ffed64f7 100755
--- a/app/src/main/java/awais/instagrabber/models/ProfileModel.java
+++ b/app/src/main/java/awais/instagrabber/models/ProfileModel.java
@@ -3,15 +3,15 @@ package awais.instagrabber.models;
import java.io.Serializable;
public final class ProfileModel implements Serializable {
- private final boolean isPrivate, reallyPrivate, isVerified, following, restricted, blocked, requested;
+ private final boolean isPrivate, reallyPrivate, isVerified, following, follower, restricted, blocked, requested;
private final long postCount, followersCount, followingCount;
private final String id, username, name, biography, url, sdProfilePic, hdProfilePic;
public ProfileModel(final boolean isPrivate, final boolean reallyPrivate,
final boolean isVerified, final String id, final String username, final String name, final String biography,
final String url, final String sdProfilePic, final String hdProfilePic, final long postCount,
- final long followersCount, final long followingCount, final boolean following, final boolean restricted,
- final boolean blocked, final boolean requested) {
+ final long followersCount, final long followingCount, final boolean following, final boolean follower,
+ final boolean restricted, final boolean blocked, final boolean requested) {
this.isPrivate = isPrivate;
this.reallyPrivate = reallyPrivate;
this.isVerified = isVerified;
@@ -26,21 +26,22 @@ public final class ProfileModel implements Serializable {
this.followersCount = followersCount;
this.followingCount = followingCount;
this.following = following;
+ this.follower = follower;
this.restricted = restricted;
this.blocked = blocked;
this.requested = requested;
}
public static ProfileModel getDefaultProfileModel() {
- return new ProfileModel(false, false, false, null, null, null, null, null, null, null, 0, 0, 0, false, false, false, false);
+ return new ProfileModel(false, false, false, null, null, null, null, null, null, null, 0, 0, 0, false, false, false, false, false);
}
public static ProfileModel getDefaultProfileModel(final String userId) {
- return new ProfileModel(false, false, false, userId, null, null, null, null, null, null, 0, 0, 0, false, false, false, false);
+ return new ProfileModel(false, false, false, userId, null, null, null, null, null, null, 0, 0, 0, false, false, false, false, false);
}
public static ProfileModel getDefaultProfileModel(final String userId, final String username) {
- return new ProfileModel(false, false, false, userId, username, null, null, null, null, null, 0, 0, 0, false, false, false, false);
+ return new ProfileModel(false, false, false, userId, username, null, null, null, null, null, 0, 0, 0, false, false, false, false, false);
}
public boolean isPrivate() {
@@ -83,31 +84,35 @@ public final class ProfileModel implements Serializable {
return hdProfilePic;
}
- public long getPostCount() {
+ public Long getPostCount() {
return postCount;
}
- public long getFollowersCount() {
+ public Long getFollowersCount() {
return followersCount;
}
- public long getFollowingCount() {
+ public Long getFollowingCount() {
return followingCount;
}
- public boolean getFollowing() {
+ public boolean isFollowing() {
return following;
}
- public boolean getRestricted() {
+ public boolean isFollower() {
+ return follower;
+ }
+
+ public boolean isRestricted() {
return restricted;
}
- public boolean getBlocked() {
+ public boolean isBlocked() {
return blocked;
}
- public boolean getRequested() {
+ public boolean isRequested() {
return requested;
}
}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/models/StoryModel.java b/app/src/main/java/awais/instagrabber/models/StoryModel.java
index 9d3959c8..80b451c8 100755
--- a/app/src/main/java/awais/instagrabber/models/StoryModel.java
+++ b/app/src/main/java/awais/instagrabber/models/StoryModel.java
@@ -6,11 +6,13 @@ import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.models.stickers.PollModel;
import awais.instagrabber.models.stickers.QuestionModel;
import awais.instagrabber.models.stickers.QuizModel;
+import awais.instagrabber.models.stickers.SliderModel;
import awais.instagrabber.models.stickers.SwipeUpModel;
public final class StoryModel implements Serializable {
private final String storyMediaId;
private final String storyUrl;
+ private String thumbnail;
private final String username;
private final String userId;
private final MediaItemType itemType;
@@ -21,6 +23,7 @@ public final class StoryModel implements Serializable {
private String spotify;
private PollModel poll;
private QuestionModel question;
+ private SliderModel slider;
private QuizModel quiz;
private SwipeUpModel swipeUp;
private String[] mentions;
@@ -28,10 +31,11 @@ public final class StoryModel implements Serializable {
private boolean isCurrentSlide = false;
private final boolean canReply;
- public StoryModel(final String storyMediaId, final String storyUrl, final MediaItemType itemType,
+ public StoryModel(final String storyMediaId, final String storyUrl, final String thumbnail, final MediaItemType itemType,
final long timestamp, final String username, final String userId, final boolean canReply) {
this.storyMediaId = storyMediaId;
this.storyUrl = storyUrl;
+ this.thumbnail = thumbnail;
this.itemType = itemType;
this.timestamp = timestamp;
this.username = username;
@@ -43,6 +47,10 @@ public final class StoryModel implements Serializable {
return storyUrl;
}
+ public String getThumbnail() {
+ return thumbnail;
+ }
+
public String getStoryMediaId() {
return storyMediaId;
}
@@ -71,6 +79,10 @@ public final class StoryModel implements Serializable {
return question;
}
+ public SliderModel getSlider() {
+ return slider;
+ }
+
public QuizModel getQuiz() {
return quiz;
}
@@ -85,6 +97,10 @@ public final class StoryModel implements Serializable {
return position;
}
+ public void setThumbnail(final String thumbnail) {
+ this.thumbnail = thumbnail;
+ }
+
public void setVideoUrl(final String videoUrl) {
this.videoUrl = videoUrl;
}
@@ -109,6 +125,10 @@ public final class StoryModel implements Serializable {
this.question = question;
}
+ public void setSlider(final SliderModel slider) {
+ this.slider = slider;
+ }
+
public void setQuiz(final QuizModel quiz) {
this.quiz = quiz;
}
diff --git a/app/src/main/java/awais/instagrabber/models/enums/NotificationType.java b/app/src/main/java/awais/instagrabber/models/enums/NotificationType.java
index acdadfbd..7e39443b 100755
--- a/app/src/main/java/awais/instagrabber/models/enums/NotificationType.java
+++ b/app/src/main/java/awais/instagrabber/models/enums/NotificationType.java
@@ -5,12 +5,20 @@ import java.util.HashMap;
import java.util.Map;
public enum NotificationType implements Serializable {
+ // web
LIKE("GraphLikeAggregatedStory"),
FOLLOW("GraphFollowAggregatedStory"),
COMMENT("GraphCommentMediaStory"),
MENTION("GraphMentionStory"),
TAGGED("GraphUserTaggedStory"),
- REQUEST("REQUEST");
+ // app story_type
+ COMMENT_LIKE("13"),
+ TAGGED_COMMENT("14"),
+ RESPONDED_STORY("213"),
+ // efr - random value
+ REQUEST("REQUEST"),
+ // ayml - random value
+ AYML("AYML");
private final String itemType;
private static final Map map = new HashMap<>();
diff --git a/app/src/main/java/awais/instagrabber/models/stickers/SliderModel.java b/app/src/main/java/awais/instagrabber/models/stickers/SliderModel.java
new file mode 100755
index 00000000..ff5c0f20
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/models/stickers/SliderModel.java
@@ -0,0 +1,53 @@
+package awais.instagrabber.models.stickers;
+
+import java.io.Serializable;
+
+public final class SliderModel implements Serializable {
+ private final int voteCount;
+ private final Double average;
+ private Double myChoice;
+ private final boolean canVote;
+ private final String id, question, emoji;
+
+ public SliderModel(final String id, final String question, final String emoji, final boolean canVote,
+ final Double average, final int voteCount, final Double myChoice) {
+ this.id = id;
+ this.question = question;
+ this.emoji = emoji;
+ this.canVote = canVote;
+ this.average = average;
+ this.voteCount = voteCount;
+ this.myChoice = myChoice;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getQuestion() {
+ return question;
+ }
+
+ public String getEmoji() {
+ return emoji;
+ }
+
+ public boolean canVote() {
+ return canVote;
+ }
+
+ public int getVoteCount() {
+ return voteCount;
+ }
+
+ public Double getAverage() {
+ return average;
+ }
+
+ public Double getMyChoice() { return myChoice; }
+
+ public Double setMyChoice(final Double choice) {
+ this.myChoice = choice;
+ return choice;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/repositories/FeedRepository.java b/app/src/main/java/awais/instagrabber/repositories/FeedRepository.java
index 3672fb90..24ac5e01 100644
--- a/app/src/main/java/awais/instagrabber/repositories/FeedRepository.java
+++ b/app/src/main/java/awais/instagrabber/repositories/FeedRepository.java
@@ -3,10 +3,12 @@ package awais.instagrabber.repositories;
import java.util.Map;
import retrofit2.Call;
-import retrofit2.http.GET;
-import retrofit2.http.QueryMap;
+import retrofit2.http.FieldMap;
+import retrofit2.http.FormUrlEncoded;
+import retrofit2.http.POST;
public interface FeedRepository {
- @GET("/graphql/query/")
- Call fetch(@QueryMap(encoded = true) Map queryParams);
+ @FormUrlEncoded
+ @POST("/api/v1/feed/timeline/")
+ Call fetch(@FieldMap final Map signedForm);
}
diff --git a/app/src/main/java/awais/instagrabber/repositories/GraphQLRepository.java b/app/src/main/java/awais/instagrabber/repositories/GraphQLRepository.java
new file mode 100644
index 00000000..23020035
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/repositories/GraphQLRepository.java
@@ -0,0 +1,12 @@
+package awais.instagrabber.repositories;
+
+import java.util.Map;
+
+import retrofit2.Call;
+import retrofit2.http.GET;
+import retrofit2.http.QueryMap;
+
+public interface GraphQLRepository {
+ @GET("/graphql/query/")
+ Call fetch(@QueryMap(encoded = true) Map queryParams);
+}
diff --git a/app/src/main/java/awais/instagrabber/repositories/LocationRepository.java b/app/src/main/java/awais/instagrabber/repositories/LocationRepository.java
index 2237254d..7cb5ca41 100644
--- a/app/src/main/java/awais/instagrabber/repositories/LocationRepository.java
+++ b/app/src/main/java/awais/instagrabber/repositories/LocationRepository.java
@@ -12,7 +12,4 @@ public interface LocationRepository {
@GET("/api/v1/feed/location/{location}/")
Call fetchPosts(@Path("location") final String locationId,
@QueryMap Map queryParams);
-
- @GET("/graphql/query/")
- Call fetchGraphQLPosts(@QueryMap(encoded = true) Map queryParams);
}
diff --git a/app/src/main/java/awais/instagrabber/repositories/MediaRepository.java b/app/src/main/java/awais/instagrabber/repositories/MediaRepository.java
index 73633b24..cff9cac3 100644
--- a/app/src/main/java/awais/instagrabber/repositories/MediaRepository.java
+++ b/app/src/main/java/awais/instagrabber/repositories/MediaRepository.java
@@ -5,48 +5,57 @@ import java.util.Map;
import retrofit2.Call;
import retrofit2.http.FieldMap;
import retrofit2.http.FormUrlEncoded;
+import retrofit2.http.GET;
import retrofit2.http.Header;
import retrofit2.http.POST;
import retrofit2.http.Path;
import retrofit2.http.QueryMap;
public interface MediaRepository {
+ @GET("/api/v1/media/{mediaId}/info/")
+ Call fetch(@Path("mediaId") final String mediaId);
+
+ @GET("/api/v1/media/{mediaId}/{action}/")
+ Call fetchLikes(@Path("mediaId") final String mediaId,
+ @Path("action") final String action); // one of "likers" or "comment_likers"
@FormUrlEncoded
@POST("/api/v1/media/{mediaId}/{action}/")
- Call action(@Header("User-Agent") final String userAgent,
- @Path("action") final String action,
+ Call action(@Path("action") final String action,
@Path("mediaId") final String mediaId,
@FieldMap final Map signedForm);
@FormUrlEncoded
@POST("/api/v1/media/{mediaId}/comment/")
- Call comment(@Header("User-Agent") final String userAgent,
- @Path("mediaId") final String mediaId,
+ Call comment(@Path("mediaId") final String mediaId,
@FieldMap final Map signedForm);
@FormUrlEncoded
@POST("/api/v1/media/{mediaId}/comment/bulk_delete/")
- Call commentsBulkDelete(@Header("User-Agent") final String userAgent,
- @Path("mediaId") final String mediaId,
+ Call commentsBulkDelete(@Path("mediaId") final String mediaId,
@FieldMap final Map signedForm);
@FormUrlEncoded
@POST("/api/v1/media/{commentId}/comment_like/")
- Call commentLike(@Header("User-Agent") final String userAgent,
- @Path("commentId") final String commentId,
+ Call commentLike(@Path("commentId") final String commentId,
@FieldMap final Map signedForm);
@FormUrlEncoded
@POST("/api/v1/media/{commentId}/comment_unlike/")
- Call commentUnlike(@Header("User-Agent") final String userAgent,
- @Path("commentId") final String commentId,
+ Call commentUnlike(@Path("commentId") final String commentId,
@FieldMap final Map signedForm);
+ @FormUrlEncoded
+ @POST("/api/v1/media/{mediaId}/edit_media/")
+ Call editCaption(@Path("mediaId") final String mediaId,
+ @FieldMap final Map signedForm);
+
+ @GET("/api/v1/language/translate/")
+ Call translate(@QueryMap final Map form);
+
@FormUrlEncoded
@POST("/api/v1/media/upload_finish/")
- Call uploadFinish(// @Header("User-Agent") final String userAgent,
- @Header("retry_context") final String retryContext,
+ Call uploadFinish(@Header("retry_context") final String retryContext,
@QueryMap Map queryParams,
@FieldMap final Map signedForm);
}
diff --git a/app/src/main/java/awais/instagrabber/repositories/NewsRepository.java b/app/src/main/java/awais/instagrabber/repositories/NewsRepository.java
index e8e71116..20b50934 100644
--- a/app/src/main/java/awais/instagrabber/repositories/NewsRepository.java
+++ b/app/src/main/java/awais/instagrabber/repositories/NewsRepository.java
@@ -6,16 +6,24 @@ import awais.instagrabber.utils.Constants;
import retrofit2.Call;
import retrofit2.http.FieldMap;
import retrofit2.http.FormUrlEncoded;
+import retrofit2.http.GET;
import retrofit2.http.Header;
import retrofit2.http.Headers;
import retrofit2.http.POST;
+import retrofit2.http.Query;
public interface NewsRepository {
- Call inbox();
+ @Headers("User-Agent: " + Constants.USER_AGENT)
+ @GET("https://www.instagram.com/accounts/activity/?__a=1")
+ Call webInbox();
+
+ @Headers("User-Agent: " + Constants.I_USER_AGENT)
+ @GET("/api/v1/news/inbox/")
+ Call appInbox(@Query(value = "mark_as_seen", encoded = true) boolean markAsSeen);
@FormUrlEncoded
- @Headers("User-Agent: " + Constants.USER_AGENT)
- @POST("https://www.instagram.com/web/activity/mark_checked/")
- Call markChecked(@Header("x-csrftoken") String csrfToken, @FieldMap Map map);
+ @Headers("User-Agent: " + Constants.I_USER_AGENT)
+ @POST("/api/v1/discover/ayml/")
+ Call getAyml(@FieldMap final Map form);
}
diff --git a/app/src/main/java/awais/instagrabber/repositories/ProfileRepository.java b/app/src/main/java/awais/instagrabber/repositories/ProfileRepository.java
index 8643b682..916dd094 100644
--- a/app/src/main/java/awais/instagrabber/repositories/ProfileRepository.java
+++ b/app/src/main/java/awais/instagrabber/repositories/ProfileRepository.java
@@ -9,11 +9,11 @@ import retrofit2.http.QueryMap;
public interface ProfileRepository {
- @GET("api/v1/users/{uid}/info/")
+ @GET("/api/v1/users/{uid}/info/")
Call getUserInfo(@Path("uid") final String uid);
- @GET("/graphql/query/")
- Call fetch(@QueryMap Map queryMap);
+ @GET("/api/v1/feed/user/{uid}/")
+ Call fetch(@Path("uid") final String uid, @QueryMap Map queryParams);
@GET("/api/v1/feed/saved/")
Call fetchSaved(@QueryMap Map queryParams);
diff --git a/app/src/main/java/awais/instagrabber/repositories/StoriesRepository.java b/app/src/main/java/awais/instagrabber/repositories/StoriesRepository.java
index 42f262d6..139ba352 100644
--- a/app/src/main/java/awais/instagrabber/repositories/StoriesRepository.java
+++ b/app/src/main/java/awais/instagrabber/repositories/StoriesRepository.java
@@ -2,17 +2,40 @@ package awais.instagrabber.repositories;
import java.util.Map;
+import awais.instagrabber.repositories.responses.StoryStickerResponse;
import retrofit2.Call;
+import retrofit2.http.FieldMap;
+import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.Header;
+import retrofit2.http.Path;
+import retrofit2.http.POST;
import retrofit2.http.QueryMap;
import retrofit2.http.Url;
public interface StoriesRepository {
+ @GET("/api/v1/media/{mediaId}/info/")
+ Call fetch(@Path("mediaId") final String mediaId);
+ // this one is the same as MediaRepository.fetch BUT you need to make sure it's a story
- @GET("graphql/query/")
- Call getStories(@QueryMap(encoded = true) Map variables);
+ @GET("/api/v1/feed/reels_tray/")
+ Call getFeedStories();
+
+ @GET("/api/v1/highlights/{uid}/highlights_tray/")
+ Call fetchHighlights(@Path("uid") final String uid);
+
+ @GET("/api/v1/archive/reel/day_shells/")
+ Call fetchArchive(@QueryMap Map queryParams);
@GET
Call getUserStory(@Header("User-Agent") String userAgent, @Url String url);
+
+ @FormUrlEncoded
+ @POST("/api/v1/media/{storyId}/{stickerId}/{action}/")
+ Call respondToSticker(@Header("User-Agent") String userAgent,
+ @Path("storyId") String storyId,
+ @Path("stickerId") String stickerId,
+ @Path("action") String action,
+ // story_poll_vote, story_question_response, story_slider_vote, story_quiz_answer
+ @FieldMap Map form);
}
diff --git a/app/src/main/java/awais/instagrabber/repositories/TagsRepository.java b/app/src/main/java/awais/instagrabber/repositories/TagsRepository.java
index b646a8a3..4db3efbd 100644
--- a/app/src/main/java/awais/instagrabber/repositories/TagsRepository.java
+++ b/app/src/main/java/awais/instagrabber/repositories/TagsRepository.java
@@ -24,7 +24,4 @@ public interface TagsRepository {
@GET("/api/v1/feed/tag/{tag}/")
Call