Austin Huang
5 years ago
No known key found for this signature in database
GPG Key ID: 84C23AA04587A91F
28 changed files with 754 additions and 284 deletions
-
4app/build.gradle
-
3app/src/main/java/awais/instagrabber/InstaApp.java
-
18app/src/main/java/awais/instagrabber/MainHelper.java
-
13app/src/main/java/awais/instagrabber/activities/CommentsViewer.java
-
2app/src/main/java/awais/instagrabber/activities/DirectMessagesUserInbox.java
-
2app/src/main/java/awais/instagrabber/activities/NotificationsViewer.java
-
164app/src/main/java/awais/instagrabber/activities/PostViewer.java
-
107app/src/main/java/awais/instagrabber/activities/StoryViewer.java
-
20app/src/main/java/awais/instagrabber/adapters/FeedAdapter.java
-
23app/src/main/java/awais/instagrabber/asyncs/FeedStoriesFetcher.java
-
2app/src/main/java/awais/instagrabber/asyncs/LocationFetcher.java
-
2app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java
-
2app/src/main/java/awais/instagrabber/asyncs/ProfileFetcher.java
-
48app/src/main/java/awais/instagrabber/asyncs/ProfilePictureFetcher.java
-
121app/src/main/java/awais/instagrabber/asyncs/StoryStatusFetcher.java
-
146app/src/main/java/awais/instagrabber/asyncs/i/iPostFetcher.java
-
152app/src/main/java/awais/instagrabber/asyncs/i/iStoryStatusFetcher.java
-
5app/src/main/java/awais/instagrabber/models/PostModel.java
-
22app/src/main/java/awais/instagrabber/models/StoryModel.java
-
10app/src/main/java/awais/instagrabber/models/ViewerPostModel.java
-
2app/src/main/java/awais/instagrabber/models/stickers/PollModel.java
-
20app/src/main/java/awais/instagrabber/models/stickers/QuestionModel.java
-
3app/src/main/java/awais/instagrabber/utils/Constants.java
-
3app/src/main/java/awais/instagrabber/utils/SettingsHelper.java
-
112app/src/main/java/awais/instagrabber/utils/Utils.java
-
23app/src/main/res/layout/activity_story_viewer.xml
-
4app/src/main/res/values/strings.xml
-
5fastlane/metadata/android/changelogs/37.txt
@ -1,121 +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.enums.MediaItemType; |
|||
import awais.instagrabber.models.PollModel; |
|||
import awais.instagrabber.models.StoryModel; |
|||
import awais.instagrabber.utils.Constants; |
|||
import awais.instagrabber.utils.Utils; |
|||
import awaisomereport.LogCollector; |
|||
|
|||
import static awais.instagrabber.utils.Utils.logCollector; |
|||
|
|||
public final class StoryStatusFetcher extends AsyncTask<Void, Void, StoryModel[]> { |
|||
private final String id, hashtag; |
|||
private final boolean location; |
|||
private final FetchListener<StoryModel[]> fetchListener; |
|||
|
|||
public StoryStatusFetcher(final String id, final String hashtag, final boolean location, final FetchListener<StoryModel[]> fetchListener) { |
|||
this.id = id; |
|||
this.hashtag = hashtag; |
|||
this.location = location; |
|||
this.fetchListener = fetchListener; |
|||
} |
|||
|
|||
@Override |
|||
protected StoryModel[] doInBackground(final Void... voids) { |
|||
StoryModel[] result = null; |
|||
final String url = "https://www.instagram.com/graphql/query/?query_hash=90709b530ea0969f002c86a89b4f2b8d&variables=" + |
|||
"{\"precomposed_overlay\":false,\"show_story_viewer_list\":false,\"stories_video_dash_manifest\":false," |
|||
+(!Utils.isEmpty(hashtag) ? ("\"tag_names\":\""+hashtag+"\"}") : ( |
|||
location ? "\"location_ids\":[\""+id.split("/")[0]+"\"]}" : "\"reel_ids\":[\"" + id + "\"]}")); |
|||
|
|||
try { |
|||
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); |
|||
conn.setInstanceFollowRedirects(false); |
|||
conn.setUseCaches(false); |
|||
conn.connect(); |
|||
|
|||
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { |
|||
JSONObject data = new JSONObject(Utils.readFromConnection(conn)).getJSONObject("data"); |
|||
|
|||
JSONArray media; |
|||
if ((media = data.optJSONArray("reels_media")) != null && media.length() > 0 && |
|||
(data = media.optJSONObject(0)) != null && |
|||
(media = data.optJSONArray("items")) != null) { |
|||
|
|||
final int mediaLen = media.length(); |
|||
|
|||
final StoryModel[] models = new StoryModel[mediaLen]; |
|||
for (int i = 0; i < mediaLen; ++i) { |
|||
data = media.getJSONObject(i); |
|||
final boolean isVideo = data.getBoolean("is_video"); |
|||
|
|||
final JSONArray tappableObjects = data.optJSONArray("tappable_objects"); |
|||
final int tappableLength = tappableObjects != null ? tappableObjects.length() : 0; |
|||
|
|||
models[i] = new StoryModel(data.getString(Constants.EXTRAS_ID), |
|||
data.getString("display_url"), |
|||
isVideo ? MediaItemType.MEDIA_TYPE_VIDEO : MediaItemType.MEDIA_TYPE_IMAGE, |
|||
data.optLong("taken_at_timestamp", 0), |
|||
data.getJSONObject("owner").getString("username")); |
|||
|
|||
final JSONArray videoResources = data.optJSONArray("video_resources"); |
|||
if (isVideo && videoResources != null) |
|||
models[i].setVideoUrl(Utils.getHighQualityPost(videoResources, true)); |
|||
|
|||
if (!data.isNull("story_app_attribution")) |
|||
models[i].setSpotify(data.getJSONObject("story_app_attribution").optString("content_url").split("\\?")[0]); |
|||
|
|||
for (int j = 0; j < tappableLength; ++j) { |
|||
JSONObject tappableObject = tappableObjects.getJSONObject(j); |
|||
if (tappableObject.optString("__typename").equals("GraphTappableFeedMedia")) { |
|||
models[i].setTappableShortCode(tappableObject.getJSONObject("media").getString(Constants.EXTRAS_SHORTCODE)); |
|||
} |
|||
else if (tappableObject.optString("__typename").equals("GraphTappableStoryPoll")) { |
|||
models[i].setPoll(new PollModel( |
|||
tappableObject.getString("id"), |
|||
tappableObject.getString("question"), |
|||
tappableObject.getJSONArray("tallies").getJSONObject(0).getString("text"), |
|||
tappableObject.getJSONArray("tallies").getJSONObject(0).getInt("count"), |
|||
tappableObject.getJSONArray("tallies").getJSONObject(1).getString("text"), |
|||
tappableObject.getJSONArray("tallies").getJSONObject(1).getInt("count"), |
|||
tappableObject.optInt("viewer_vote", -1) |
|||
)); |
|||
} |
|||
} |
|||
} |
|||
result = models; |
|||
} |
|||
} |
|||
|
|||
conn.disconnect(); |
|||
} catch (final Exception e) { |
|||
if (logCollector != null) |
|||
logCollector.appendException(e, LogCollector.LogFile.ASYNC_STORY_STATUS_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 StoryModel[] result) { |
|||
if (fetchListener != null) fetchListener.onResult(result); |
|||
} |
|||
} |
@ -0,0 +1,146 @@ |
|||
package awais.instagrabber.asyncs.i; |
|||
|
|||
import android.os.AsyncTask; |
|||
import android.os.Environment; |
|||
import android.util.Log; |
|||
|
|||
import org.json.JSONArray; |
|||
import org.json.JSONObject; |
|||
|
|||
import java.io.File; |
|||
import java.net.HttpURLConnection; |
|||
import java.net.URL; |
|||
|
|||
import awais.instagrabber.BuildConfig; |
|||
import awais.instagrabber.interfaces.FetchListener; |
|||
import awais.instagrabber.models.ViewerPostModel; |
|||
import awais.instagrabber.models.enums.MediaItemType; |
|||
import awais.instagrabber.utils.Constants; |
|||
import awais.instagrabber.utils.Utils; |
|||
import awaisomereport.LogCollector; |
|||
|
|||
import static awais.instagrabber.utils.Constants.DOWNLOAD_USER_FOLDER; |
|||
import static awais.instagrabber.utils.Constants.FOLDER_PATH; |
|||
import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO; |
|||
import static awais.instagrabber.utils.Utils.logCollector; |
|||
|
|||
public final class iPostFetcher extends AsyncTask<Void, Void, ViewerPostModel[]> { |
|||
private final String id; |
|||
private final FetchListener<ViewerPostModel[]> fetchListener; |
|||
|
|||
public iPostFetcher(final String id, final FetchListener<ViewerPostModel[]> fetchListener) { |
|||
this.id = id; |
|||
this.fetchListener = fetchListener; |
|||
} |
|||
|
|||
@Override |
|||
protected ViewerPostModel[] doInBackground(final Void... voids) { |
|||
ViewerPostModel[] result = null; |
|||
try { |
|||
final HttpURLConnection conn = (HttpURLConnection) new URL("https://i.instagram.com/api/v1/media/" + id + "/info").openConnection(); |
|||
conn.setUseCaches(false); |
|||
conn.setRequestProperty("User-Agent", Constants.USER_AGENT); |
|||
conn.connect(); |
|||
|
|||
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { |
|||
|
|||
final JSONObject media = new JSONObject(Utils.readFromConnection(conn)).getJSONArray("items").getJSONObject(0); |
|||
|
|||
final String username = media.has("user") ? media.getJSONObject("user").getString(Constants.EXTRAS_USERNAME) : null; |
|||
|
|||
// to check if file exists |
|||
final File downloadDir = new File(Environment.getExternalStorageDirectory(), "Download" + |
|||
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER) ? ("/"+username) : "")); |
|||
File customDir = null; |
|||
if (Utils.settingsHelper.getBoolean(FOLDER_SAVE_TO)) { |
|||
final String customPath = Utils.settingsHelper.getString(FOLDER_PATH + |
|||
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER) ? ("/"+username) : "")); |
|||
if (!Utils.isEmpty(customPath)) customDir = new File(customPath); |
|||
} |
|||
|
|||
final long timestamp = media.getLong("taken_at"); |
|||
|
|||
final boolean isVideo = media.has("has_audio") && media.optBoolean("has_audio"); |
|||
final boolean isSlider = !media.isNull("carousel_media_count"); |
|||
|
|||
final MediaItemType mediaItemType; |
|||
if (isSlider) mediaItemType = MediaItemType.MEDIA_TYPE_SLIDER; |
|||
else if (isVideo) mediaItemType = MediaItemType.MEDIA_TYPE_VIDEO; |
|||
else mediaItemType = MediaItemType.MEDIA_TYPE_IMAGE; |
|||
|
|||
final String postCaption; |
|||
final JSONObject mediaToCaption = media.optJSONObject("caption"); |
|||
if (mediaToCaption == null) postCaption = null; |
|||
else postCaption = mediaToCaption.optString("text"); |
|||
|
|||
final long commentsCount = media.optLong("comment_count"); |
|||
|
|||
if (mediaItemType != MediaItemType.MEDIA_TYPE_SLIDER) { |
|||
final ViewerPostModel postModel = new ViewerPostModel(mediaItemType, |
|||
media.getString(Constants.EXTRAS_ID), |
|||
isVideo |
|||
? Utils.getHighQualityPost(media.optJSONArray("video_versions"), true, true) |
|||
: Utils.getHighQualityImage(media), |
|||
media.getString("code"), |
|||
Utils.isEmpty(postCaption) ? null : postCaption, |
|||
username, |
|||
isVideo && media.has("view_count") ? media.getLong("view_count") : -1, |
|||
timestamp, media.optBoolean("has_liked"), media.optBoolean("has_viewer_saved"), |
|||
media.getLong("like_count"), |
|||
media.optJSONObject("location")); |
|||
|
|||
postModel.setCommentsCount(commentsCount); |
|||
|
|||
Utils.checkExistence(downloadDir, customDir, false, postModel); |
|||
|
|||
result = new ViewerPostModel[]{postModel}; |
|||
|
|||
} else { |
|||
final JSONArray children = media.getJSONArray("carousel_media"); |
|||
final ViewerPostModel[] postModels = new ViewerPostModel[children.length()]; |
|||
|
|||
for (int i = 0; i < postModels.length; ++i) { |
|||
final JSONObject node = children.getJSONObject(i); |
|||
final boolean isChildVideo = node.has("video_duration"); |
|||
|
|||
postModels[i] = new ViewerPostModel(isChildVideo ? MediaItemType.MEDIA_TYPE_VIDEO : MediaItemType.MEDIA_TYPE_IMAGE, |
|||
media.getString(Constants.EXTRAS_ID), |
|||
isChildVideo |
|||
? Utils.getHighQualityPost(node.optJSONArray("video_versions"), true, true) |
|||
: Utils.getHighQualityImage(node), |
|||
media.getString("code"), |
|||
postCaption, |
|||
username, |
|||
-1, |
|||
timestamp, media.optBoolean("has_liked"), media.optBoolean("has_viewer_saved"), |
|||
media.getLong("like_count"), |
|||
media.optJSONObject("location")); |
|||
postModels[i].setSliderDisplayUrl(Utils.getHighQualityImage(node)); |
|||
|
|||
Utils.checkExistence(downloadDir, customDir, true, postModels[i]); |
|||
} |
|||
|
|||
postModels[0].setCommentsCount(commentsCount); |
|||
result = postModels; |
|||
} |
|||
} |
|||
|
|||
conn.disconnect(); |
|||
} catch (Exception e) { |
|||
if (logCollector != null) |
|||
logCollector.appendException(e, LogCollector.LogFile.ASYNC_POST_FETCHER, "doInBackground (i)"); |
|||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
@Override |
|||
protected void onPreExecute() { |
|||
if (fetchListener != null) fetchListener.doBefore(); |
|||
} |
|||
|
|||
@Override |
|||
protected void onPostExecute(final ViewerPostModel[] postModels) { |
|||
if (fetchListener != null) fetchListener.onResult(postModels); |
|||
} |
|||
} |
@ -0,0 +1,152 @@ |
|||
package awais.instagrabber.asyncs.i; |
|||
|
|||
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.stickers.PollModel; |
|||
import awais.instagrabber.models.stickers.QuestionModel; |
|||
import awais.instagrabber.models.StoryModel; |
|||
import awais.instagrabber.models.enums.MediaItemType; |
|||
import awais.instagrabber.utils.Constants; |
|||
import awais.instagrabber.utils.Utils; |
|||
import awaisomereport.LogCollector; |
|||
|
|||
import static awais.instagrabber.utils.Utils.logCollector; |
|||
|
|||
public final class iStoryStatusFetcher extends AsyncTask<Void, Void, StoryModel[]> { |
|||
private final String id, username; |
|||
private final boolean isLoc, isHashtag; |
|||
private final FetchListener<StoryModel[]> fetchListener; |
|||
|
|||
public iStoryStatusFetcher(final String id, final String username, final boolean isLoc, |
|||
final boolean isHashtag, final FetchListener<StoryModel[]> fetchListener) { |
|||
this.id = id; |
|||
this.username = username; |
|||
this.isLoc = isLoc; |
|||
this.isHashtag = isHashtag; |
|||
this.fetchListener = fetchListener; |
|||
} |
|||
|
|||
@Override |
|||
protected StoryModel[] doInBackground(final Void... voids) { |
|||
StoryModel[] result = null; |
|||
final String url = "https://i.instagram.com/api/v1/" + (isLoc ? "locations/" : (isHashtag ? "tags/" : "feed/reels_media/?reel_ids=")) |
|||
+ id + ((isLoc || isHashtag) ? "/story/" : ""); |
|||
|
|||
try { |
|||
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); |
|||
conn.setInstanceFollowRedirects(false); |
|||
conn.setUseCaches(false); |
|||
conn.setRequestProperty("User-Agent", Constants.USER_AGENT); |
|||
conn.connect(); |
|||
|
|||
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { |
|||
JSONObject data = (isLoc || isHashtag) |
|||
? new JSONObject(Utils.readFromConnection(conn)).getJSONObject("story") |
|||
: new JSONObject(Utils.readFromConnection(conn)).getJSONObject("reels").getJSONObject(id); |
|||
|
|||
JSONArray media; |
|||
if ((media = data.optJSONArray("items")) != null && media.length() > 0 && |
|||
(data = media.optJSONObject(0)) != null) { |
|||
|
|||
final int mediaLen = media.length(); |
|||
|
|||
final StoryModel[] models = new StoryModel[mediaLen]; |
|||
for (int i = 0; i < mediaLen; ++i) { |
|||
data = media.getJSONObject(i); |
|||
final boolean isVideo = data.has("has_audio") && data.optBoolean("has_audio"); |
|||
|
|||
models[i] = new StoryModel(data.getString("pk"), |
|||
data.getJSONObject("image_versions2").getJSONArray("candidates").getJSONObject(0).getString("url"), |
|||
isVideo ? MediaItemType.MEDIA_TYPE_VIDEO : MediaItemType.MEDIA_TYPE_IMAGE, |
|||
data.optLong("taken_at", 0), |
|||
(isLoc || isHashtag) ? data.getJSONObject("user").getString("username") : username); |
|||
|
|||
final JSONArray videoResources = data.optJSONArray("video_versions"); |
|||
if (isVideo && videoResources != null) |
|||
models[i].setVideoUrl(Utils.getHighQualityPost(videoResources, true, true)); |
|||
|
|||
if (data.has("story_feed_media")) { |
|||
models[i].setTappableShortCode(data.getJSONArray("story_feed_media").getJSONObject(0).optString("media_id")); |
|||
} |
|||
|
|||
if (!data.isNull("story_app_attribution")) |
|||
models[i].setSpotify(data.getJSONObject("story_app_attribution").optString("content_url").split("\\?")[0]); |
|||
|
|||
if (data.has("story_polls")) { |
|||
JSONObject tappableObject = data.optJSONArray("story_polls").getJSONObject(0).optJSONObject("poll_sticker"); |
|||
if (tappableObject != null) models[i].setPoll(new PollModel( |
|||
String.valueOf(tappableObject.getLong("poll_id")), |
|||
tappableObject.getString("question"), |
|||
tappableObject.getJSONArray("tallies").getJSONObject(0).getString("text"), |
|||
tappableObject.getJSONArray("tallies").getJSONObject(0).getInt("count"), |
|||
tappableObject.getJSONArray("tallies").getJSONObject(1).getString("text"), |
|||
tappableObject.getJSONArray("tallies").getJSONObject(1).getInt("count"), |
|||
tappableObject.optInt("viewer_vote", -1) |
|||
)); |
|||
} |
|||
if (data.has("story_questions")) { |
|||
JSONObject tappableObject = data.getJSONArray("story_questions").getJSONObject(0).optJSONObject("question_sticker"); |
|||
if (tappableObject != null) models[i].setQuestion(new QuestionModel( |
|||
String.valueOf(tappableObject.getLong("question_id")), |
|||
tappableObject.getString("question") |
|||
)); |
|||
} |
|||
JSONArray hashtags = data.optJSONArray("story_hashtags"); |
|||
JSONArray locations = data.optJSONArray("story_locations"); |
|||
JSONArray atmarks = data.optJSONArray("reel_mentions"); |
|||
String[] mentions = new String[(hashtags == null ? 0 : hashtags.length()) |
|||
+ (atmarks == null ? 0 : atmarks.length()) |
|||
+ (locations == null ? 0 : locations.length())]; |
|||
if (hashtags != null) { |
|||
for (int h = 0; h < hashtags.length(); ++h) { |
|||
mentions[h] = "#"+hashtags.getJSONObject(h).getJSONObject("hashtag").getString("name"); |
|||
} |
|||
} |
|||
if (atmarks != null) { |
|||
for (int h = 0; h < atmarks.length(); ++h) { |
|||
mentions[h + (hashtags == null ? 0 : hashtags.length())] = |
|||
"@"+atmarks.getJSONObject(h).getJSONObject("user").getString("username"); |
|||
} |
|||
} |
|||
if (locations != null) { |
|||
for (int h = 0; h < locations.length(); ++h) { |
|||
mentions[h + (hashtags == null ? 0 : hashtags.length()) + (atmarks == null ? 0 : atmarks.length())] = |
|||
String.valueOf(locations.getJSONObject(h).getJSONObject("location").getLong("pk")) |
|||
+"/ ("+locations.getJSONObject(h).getJSONObject("location").getString("short_name")+")"; |
|||
} |
|||
} |
|||
if (mentions.length != 0) models[i].setMentions(mentions); |
|||
} |
|||
result = models; |
|||
} |
|||
} |
|||
|
|||
conn.disconnect(); |
|||
} catch (final Exception e) { |
|||
if (logCollector != null) |
|||
logCollector.appendException(e, LogCollector.LogFile.ASYNC_STORY_STATUS_FETCHER, "doInBackground (i)"); |
|||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
@Override |
|||
protected void onPreExecute() { |
|||
if (fetchListener != null) fetchListener.doBefore(); |
|||
} |
|||
|
|||
@Override |
|||
protected void onPostExecute(final StoryModel[] result) { |
|||
if (fetchListener != null) fetchListener.onResult(result); |
|||
} |
|||
} |
@ -1,4 +1,4 @@ |
|||
package awais.instagrabber.models; |
|||
package awais.instagrabber.models.stickers; |
|||
|
|||
import java.io.Serializable; |
|||
|
@ -0,0 +1,20 @@ |
|||
package awais.instagrabber.models.stickers; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
public final class QuestionModel implements Serializable { |
|||
private final String id, question; |
|||
|
|||
public QuestionModel(final String id, final String question) { |
|||
this.id = id; // only the poll id |
|||
this.question = question; |
|||
} |
|||
|
|||
public String getId() { |
|||
return id; |
|||
} |
|||
|
|||
public String getQuestion() { |
|||
return question; |
|||
} |
|||
} |
@ -0,0 +1,5 @@ |
|||
* You can now respond to question boxes in stories |
|||
* You can now check out story mentions (except non-Spotify music stickers, but you can hear them by playing the story) |
|||
* Above 2 does not apply to highlights |
|||
* You can now click on locations from feed |
|||
* Fixed a bug where you cannot access your own follower/following list when you are private and have no posts |
Write
Preview
Loading…
Cancel
Save
Reference in new issue