From eac0d9a5f707ed1f47c091c2cbf1779ab5455eef Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Mon, 3 Aug 2020 22:28:42 -0400 Subject: [PATCH] story stickers and various bug fixes --- app/build.gradle | 4 +- .../java/awais/instagrabber/InstaApp.java | 3 + .../java/awais/instagrabber/MainHelper.java | 18 +- .../activities/CommentsViewer.java | 13 +- .../activities/DirectMessagesUserInbox.java | 2 +- .../activities/NotificationsViewer.java | 2 +- .../instagrabber/activities/PostViewer.java | 164 +++++++++--------- .../instagrabber/activities/StoryViewer.java | 107 +++++++++++- .../instagrabber/adapters/FeedAdapter.java | 20 +-- .../asyncs/FeedStoriesFetcher.java | 23 ++- .../instagrabber/asyncs/LocationFetcher.java | 2 +- .../instagrabber/asyncs/PostFetcher.java | 2 - .../instagrabber/asyncs/ProfileFetcher.java | 2 +- .../asyncs/ProfilePictureFetcher.java | 48 ++++- .../asyncs/StoryStatusFetcher.java | 121 ------------- .../instagrabber/asyncs/i/iPostFetcher.java | 146 ++++++++++++++++ .../asyncs/i/iStoryStatusFetcher.java | 152 ++++++++++++++++ .../awais/instagrabber/models/PostModel.java | 5 +- .../awais/instagrabber/models/StoryModel.java | 22 ++- .../instagrabber/models/ViewerPostModel.java | 10 +- .../models/{ => stickers}/PollModel.java | 2 +- .../models/stickers/QuestionModel.java | 20 +++ .../awais/instagrabber/utils/Constants.java | 3 + .../instagrabber/utils/SettingsHelper.java | 3 +- .../java/awais/instagrabber/utils/Utils.java | 112 ++++++++++-- .../main/res/layout/activity_story_viewer.xml | 23 ++- app/src/main/res/values/strings.xml | 4 + fastlane/metadata/android/changelogs/37.txt | 5 + 28 files changed, 754 insertions(+), 284 deletions(-) delete mode 100755 app/src/main/java/awais/instagrabber/asyncs/StoryStatusFetcher.java create mode 100755 app/src/main/java/awais/instagrabber/asyncs/i/iPostFetcher.java create mode 100755 app/src/main/java/awais/instagrabber/asyncs/i/iStoryStatusFetcher.java rename app/src/main/java/awais/instagrabber/models/{ => stickers}/PollModel.java (96%) create mode 100755 app/src/main/java/awais/instagrabber/models/stickers/QuestionModel.java create mode 100644 fastlane/metadata/android/changelogs/37.txt diff --git a/app/build.gradle b/app/build.gradle index 3f8e4769..dff3cdcf 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,8 +12,8 @@ android { // REMEMBER TO CHANGE versionCode AS WELL // 16.7 is 32, 16.9 is 35 (34 is public beta) - versionCode 36 - versionName '17.0' + versionCode 37 + versionName '17.1' multiDexEnabled true diff --git a/app/src/main/java/awais/instagrabber/InstaApp.java b/app/src/main/java/awais/instagrabber/InstaApp.java index 64f12df6..a0e10ef7 100755 --- a/app/src/main/java/awais/instagrabber/InstaApp.java +++ b/app/src/main/java/awais/instagrabber/InstaApp.java @@ -8,6 +8,7 @@ import androidx.multidex.MultiDexApplication; import java.net.CookieHandler; import java.text.SimpleDateFormat; +import java.util.UUID; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.DataBox; @@ -61,6 +62,8 @@ public final class InstaApp extends MultiDexApplication { settingsHelper.getString(Constants.CUSTOM_DATE_TIME_FORMAT) : settingsHelper.getString(Constants.DATE_TIME_FORMAT), LocaleUtils.getCurrentLocale()); + settingsHelper.putString(Constants.DEVICE_UUID, UUID.randomUUID().toString()); + changeTheme(); } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/MainHelper.java b/app/src/main/java/awais/instagrabber/MainHelper.java index 914c4132..f32754ac 100755 --- a/app/src/main/java/awais/instagrabber/MainHelper.java +++ b/app/src/main/java/awais/instagrabber/MainHelper.java @@ -60,7 +60,7 @@ import awais.instagrabber.asyncs.HighlightsFetcher; import awais.instagrabber.asyncs.LocationFetcher; import awais.instagrabber.asyncs.PostsFetcher; import awais.instagrabber.asyncs.ProfileFetcher; -import awais.instagrabber.asyncs.StoryStatusFetcher; +import awais.instagrabber.asyncs.i.iStoryStatusFetcher; import awais.instagrabber.customviews.MouseDrawer; import awais.instagrabber.customviews.RamboTextView; import awais.instagrabber.customviews.helpers.GridAutofitLayoutManager; @@ -543,7 +543,7 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener { else main.startActivity(new Intent(main, PostViewer.class) .putExtra(Constants.EXTRAS_INDEX, itemModel.getPosition()) .putExtra(Constants.EXTRAS_TYPE, ItemGetType.DISCOVER_ITEMS) - .putExtra(Constants.EXTRAS_POST, new PostModel(itemModel.getShortCode()))); + .putExtra(Constants.EXTRAS_POST, new PostModel(itemModel.getShortCode(), false))); } }, v -> { final Object tag = v.getTag(); @@ -599,7 +599,7 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener { if (modelType == IntentModelType.POST) { main.startActivityForResult(new Intent(main, PostViewer.class) .putExtra(Constants.EXTRAS_USER, main.userQuery) - .putExtra(Constants.EXTRAS_POST, new PostModel(modelText)), 9629); + .putExtra(Constants.EXTRAS_POST, new PostModel(modelText, false)), 9629); } else { main.addToStack(); main.userQuery = modelType == IntentModelType.HASHTAG ? '#' + modelText : ("@"+modelText); @@ -700,8 +700,6 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener { return; } - final String profileId = hashtagModel.getId(); - final String cookie = Utils.settingsHelper.getString(Constants.COOKIE); final boolean isLoggedIn = !Utils.isEmpty(cookie); @@ -711,7 +709,7 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener { main.mainBinding.btnFollowTag.setVisibility(View.VISIBLE); if (isLoggedIn) { - new StoryStatusFetcher(profileId, hashtagModel.getName(), false, result -> { + new iStoryStatusFetcher(hashtagModel.getName(), null, false, true, result -> { main.storyModels = result; if (result != null && result.length > 0) main.mainBinding.mainHashtagImage.setStoriesBorder(); }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); @@ -774,7 +772,7 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener { final String cookie = Utils.settingsHelper.getString(Constants.COOKIE); final boolean isLoggedIn = !Utils.isEmpty(cookie); if (isLoggedIn) { - new StoryStatusFetcher(profileId, "", false, result -> { + new iStoryStatusFetcher(profileId, profileModel.getUsername(), false, false, result -> { main.storyModels = result; if (result != null && result.length > 0) main.mainBinding.mainProfileImage.setStoriesBorder(); }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); @@ -971,7 +969,7 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener { main.mainBinding.toolbar.toolbar.setTitle(main.userQuery); main.mainBinding.locInfoContainer.setVisibility(View.VISIBLE); - currentlyExecuting = new LocationFetcher(main.userQuery, locationModel -> { + currentlyExecuting = new LocationFetcher(main.userQuery.split("/")[0], locationModel -> { main.locationModel = locationModel; main.mainBinding.toolbar.toolbar.setTitle(locationModel.getName()); @@ -988,7 +986,7 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener { final String cookie = Utils.settingsHelper.getString(Constants.COOKIE); final boolean isLoggedIn = !Utils.isEmpty(cookie); if (isLoggedIn) { - new StoryStatusFetcher(profileId, "", true, result -> { + new iStoryStatusFetcher(profileId.split("/")[0], null, true, false, result -> { main.storyModels = result; if (result != null && result.length > 0) main.mainBinding.mainLocationImage.setStoriesBorder(); }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); @@ -1199,7 +1197,7 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener { private final View.OnClickListener profileActionListener = new View.OnClickListener() { @Override public void onClick(final View v) { - final boolean iamme = isLoggedIn + final boolean iamme = (isLoggedIn && main.profileModel != null) ? Utils.getUserIdFromCookie(Utils.settingsHelper.getString(Constants.COOKIE)).equals(main.profileModel.getId()) : false; if (!isLoggedIn && Utils.dataBox.getFavorite(main.userQuery) != null && v == main.mainBinding.btnFollow) { diff --git a/app/src/main/java/awais/instagrabber/activities/CommentsViewer.java b/app/src/main/java/awais/instagrabber/activities/CommentsViewer.java index bb458a34..99c6b1cb 100755 --- a/app/src/main/java/awais/instagrabber/activities/CommentsViewer.java +++ b/app/src/main/java/awais/instagrabber/activities/CommentsViewer.java @@ -50,6 +50,7 @@ public final class CommentsViewer extends BaseLanguageActivity implements SwipeR private final String cookie = Utils.settingsHelper.getString(Constants.COOKIE); private Resources resources; private InputMethodManager imm; + private View focus; @Override protected void onCreate(@Nullable final Bundle savedInstanceState) { @@ -122,7 +123,8 @@ public final class CommentsViewer extends BaseLanguageActivity implements SwipeR } else if (which == 3) { Utils.copyText(this, commentModel.getText().toString()); } else if (which == 4) { - commentsBinding.rvComments.findViewWithTag(commentModel).setBackgroundColor(0x80888888); + focus = commentsBinding.rvComments.findViewWithTag(commentModel); + focus.setBackgroundColor(0x80888888); commentsBinding.commentCancelParent.setVisibility(View.VISIBLE); String mention = "@"+profileModel.getUsername()+" "; commentsBinding.commentText.setText(mention); @@ -200,10 +202,11 @@ public final class CommentsViewer extends BaseLanguageActivity implements SwipeR Toast.makeText(getApplicationContext(), R.string.comment_send_empty_comment, Toast.LENGTH_SHORT).show(); else if (v == commentsBinding.commentSend) new CommentAction().execute("add"); else if (v == commentsBinding.commentCancelParent) { - commentsBinding.rvComments.findViewWithTag(commentModel).setBackgroundColor(commentModel.getLiked() ? 0x40FF69B4 : 0x00000000); + focus.setBackgroundColor(commentModel.getLiked() ? 0x40FF69B4 : 0x00000000); commentsBinding.commentCancelParent.setVisibility(View.GONE); commentsBinding.commentText.setText(""); commentModel = null; + focus = null; } }; @@ -287,9 +290,11 @@ public final class CommentsViewer extends BaseLanguageActivity implements SwipeR @Override protected void onPostExecute(Void result) { if (ok == true) { - if (commentModel != null) { - commentsBinding.rvComments.findViewWithTag(commentModel).setBackgroundColor(commentModel.getLiked() ? 0x40FF69B4 : 0x00000000); + if (focus != null) { + focus.setBackgroundColor(commentModel.getLiked() ? 0x40FF69B4 : 0x00000000); commentsBinding.commentCancelParent.setVisibility(View.GONE); + commentModel = null; + focus = null; } //imm.hideSoftInputFromWindow(commentsBinding.getView().getRootView().getWindowToken(), 0); diff --git a/app/src/main/java/awais/instagrabber/activities/DirectMessagesUserInbox.java b/app/src/main/java/awais/instagrabber/activities/DirectMessagesUserInbox.java index e27b63d1..0f85399e 100755 --- a/app/src/main/java/awais/instagrabber/activities/DirectMessagesUserInbox.java +++ b/app/src/main/java/awais/instagrabber/activities/DirectMessagesUserInbox.java @@ -110,7 +110,7 @@ public final class DirectMessagesUserInbox extends AppCompatActivity { switch (itemType) { case MEDIA_SHARE: startActivity(new Intent(this, PostViewer.class) - .putExtra(Constants.EXTRAS_POST, new PostModel(directItemModel.getMediaModel().getCode()))); + .putExtra(Constants.EXTRAS_POST, new PostModel(directItemModel.getMediaModel().getCode(), false))); break; case LINK: Intent linkIntent = new Intent(Intent.ACTION_VIEW); diff --git a/app/src/main/java/awais/instagrabber/activities/NotificationsViewer.java b/app/src/main/java/awais/instagrabber/activities/NotificationsViewer.java index d5fda515..744dc8c2 100755 --- a/app/src/main/java/awais/instagrabber/activities/NotificationsViewer.java +++ b/app/src/main/java/awais/instagrabber/activities/NotificationsViewer.java @@ -92,7 +92,7 @@ public final class NotificationsViewer extends BaseLanguageActivity implements S searchUsername(notificationModel.getUsername()); else if (which == 1) startActivity(new Intent(getApplicationContext(), PostViewer.class) - .putExtra(Constants.EXTRAS_POST, new PostModel(notificationModel.getShortcode()))); + .putExtra(Constants.EXTRAS_POST, new PostModel(notificationModel.getShortcode(), false))); }; private final View.OnClickListener clickListener = v -> { diff --git a/app/src/main/java/awais/instagrabber/activities/PostViewer.java b/app/src/main/java/awais/instagrabber/activities/PostViewer.java index 6dc0c4ba..78650450 100755 --- a/app/src/main/java/awais/instagrabber/activities/PostViewer.java +++ b/app/src/main/java/awais/instagrabber/activities/PostViewer.java @@ -57,9 +57,11 @@ import awais.instagrabber.R; import awais.instagrabber.adapters.PostsMediaAdapter; import awais.instagrabber.asyncs.PostFetcher; import awais.instagrabber.asyncs.ProfileFetcher; +import awais.instagrabber.asyncs.i.iPostFetcher; import awais.instagrabber.customviews.CommentMentionClickSpan; import awais.instagrabber.customviews.helpers.SwipeGestureListener; import awais.instagrabber.databinding.ActivityViewerBinding; +import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.interfaces.SwipeEvent; import awais.instagrabber.models.BasePostModel; import awais.instagrabber.models.PostModel; @@ -185,6 +187,83 @@ public final class PostViewer extends BaseLanguageActivity { private final PostsMediaAdapter mediaAdapter = new PostsMediaAdapter(null, onClickListener); private RequestManager glideRequestManager; private LinearLayout.LayoutParams containerLayoutParams; + private final FetchListener pfl = result -> { + if (result == null || result.length < 1) { + Toast.makeText(this, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); + return; + } + + viewerPostModel = result[0]; + + mediaAdapter.setData(result); + if (result.length > 1) { + viewerBinding.mediaList.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, 0, 0.55f + )); + containerLayoutParams.weight = 1.35f; + containerLayoutParams.weight += (Utils.isEmpty(settingsHelper.getString(Constants.COOKIE))) ? 0.3f : 0; + viewerBinding.container.setLayoutParams(containerLayoutParams); + viewerBinding.mediaList.setVisibility(View.VISIBLE); + } + + final View viewStoryPost = findViewById(R.id.viewStoryPost); + if (viewStoryPost != null) { + viewStoryPost.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + if (profileModel.isPrivate()) + Toast.makeText(getApplicationContext(), R.string.share_private_post, Toast.LENGTH_LONG).show(); + Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND); + sharingIntent.setType("text/plain"); + sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, "https://instagram.com/p/"+postModel.getShortCode()); + startActivity(Intent.createChooser(sharingIntent, + (profileModel.isPrivate()) ? getString(R.string.share_private_post) : getString(R.string.share_public_post))); + } + }); + } + + viewerCaptionParent.setOnTouchListener(gestureTouchListener); + viewerBinding.playerView.setOnTouchListener(gestureTouchListener); + viewerBinding.imageViewer.setOnSingleFlingListener((e1, e2, velocityX, velocityY) -> { + final float diffX = e2.getX() - e1.getX(); + if (Math.abs(diffX) > Math.abs(e2.getY() - e1.getY()) && Math.abs(diffX) > SwipeGestureListener.SWIPE_THRESHOLD + && Math.abs(velocityX) > SwipeGestureListener.SWIPE_VELOCITY_THRESHOLD) { + swipeEvent.onSwipe(diffX > 0); + return true; + } + return false; + }); + + final long commentsCount = viewerPostModel.getCommentsCount(); + viewerBinding.bottomPanel.commentsCount.setText(String.valueOf(commentsCount)); + viewerBinding.bottomPanel.btnComments.setVisibility(View.VISIBLE); + + viewerBinding.bottomPanel.btnComments.setOnClickListener(v -> + startActivityForResult(new Intent(this, CommentsViewer.class) + .putExtra(Constants.EXTRAS_SHORTCODE, postShortCode) + .putExtra(Constants.EXTRAS_POST, viewerPostModel.getPostId()) + .putExtra(Constants.EXTRAS_USER, postUserId), 6969)); + viewerBinding.bottomPanel.btnComments.setClickable(true); + viewerBinding.bottomPanel.btnComments.setEnabled(true); + + if (postModel instanceof PostModel) { + final PostModel postModel = (PostModel) this.postModel; + postModel.setPostId(viewerPostModel.getPostId()); + postModel.setTimestamp(viewerPostModel.getTimestamp()); + postModel.setPostCaption(viewerPostModel.getPostCaption()); + postModel.setLike(viewerPostModel.getLike()); + postModel.setBookmark(viewerPostModel.getBookmark()); + } + + setupPostInfoBar("@"+viewerPostModel.getUsername(), viewerPostModel.getItemType(), + viewerPostModel.getLocation() == null ? null : viewerPostModel.getLocation()); + + postCaption = postModel.getPostCaption(); + viewerCaptionParent.setVisibility(View.VISIBLE); + + viewerBinding.bottomPanel.btnDownload.setOnClickListener(downloadClickListener); + + refreshPost(); + }; @Override protected void onCreate(@Nullable final Bundle savedInstanceState) { @@ -341,85 +420,10 @@ public final class PostViewer extends BaseLanguageActivity { viewerBinding.imageViewer.setImageResource(0); viewerBinding.imageViewer.setImageDrawable(null); - new PostFetcher(postModel.getShortCode(), result -> { - if (result == null || result.length < 1) { - Toast.makeText(this, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); - return; - } - - viewerPostModel = result[0]; - commentsEndCursor = viewerPostModel.getCommentsEndCursor(); - - mediaAdapter.setData(result); - if (result.length > 1) { - viewerBinding.mediaList.setLayoutParams(new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, 0, 0.55f - )); - containerLayoutParams.weight = 1.35f; - containerLayoutParams.weight += (Utils.isEmpty(settingsHelper.getString(Constants.COOKIE))) ? 0.3f : 0; - viewerBinding.container.setLayoutParams(containerLayoutParams); - viewerBinding.mediaList.setVisibility(View.VISIBLE); - } - - final View viewStoryPost = findViewById(R.id.viewStoryPost); - if (viewStoryPost != null) { - viewStoryPost.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - if (profileModel.isPrivate()) - Toast.makeText(getApplicationContext(), R.string.share_private_post, Toast.LENGTH_LONG).show(); - Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND); - sharingIntent.setType("text/plain"); - sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, "https://instagram.com/p/"+postModel.getShortCode()); - startActivity(Intent.createChooser(sharingIntent, - (profileModel.isPrivate()) ? getString(R.string.share_private_post) : getString(R.string.share_public_post))); - } - }); - } - - viewerCaptionParent.setOnTouchListener(gestureTouchListener); - viewerBinding.playerView.setOnTouchListener(gestureTouchListener); - viewerBinding.imageViewer.setOnSingleFlingListener((e1, e2, velocityX, velocityY) -> { - final float diffX = e2.getX() - e1.getX(); - if (Math.abs(diffX) > Math.abs(e2.getY() - e1.getY()) && Math.abs(diffX) > SwipeGestureListener.SWIPE_THRESHOLD - && Math.abs(velocityX) > SwipeGestureListener.SWIPE_VELOCITY_THRESHOLD) { - swipeEvent.onSwipe(diffX > 0); - return true; - } - return false; - }); - - final long commentsCount = viewerPostModel.getCommentsCount(); - viewerBinding.bottomPanel.commentsCount.setText(String.valueOf(commentsCount)); - viewerBinding.bottomPanel.btnComments.setVisibility(View.VISIBLE); - - viewerBinding.bottomPanel.btnComments.setOnClickListener(v -> - startActivityForResult(new Intent(this, CommentsViewer.class) - .putExtra(Constants.EXTRAS_END_CURSOR, commentsEndCursor) - .putExtra(Constants.EXTRAS_SHORTCODE, postShortCode) - .putExtra(Constants.EXTRAS_POST, viewerPostModel.getPostId()) - .putExtra(Constants.EXTRAS_USER, postUserId), 6969)); - viewerBinding.bottomPanel.btnComments.setClickable(true); - viewerBinding.bottomPanel.btnComments.setEnabled(true); - - if (postModel instanceof PostModel) { - final PostModel postModel = (PostModel) this.postModel; - postModel.setPostId(viewerPostModel.getPostId()); - postModel.setTimestamp(viewerPostModel.getTimestamp()); - postModel.setPostCaption(viewerPostModel.getPostCaption()); - postModel.setLike(viewerPostModel.getLike()); - postModel.setBookmark(viewerPostModel.getBookmark()); - } - - setupPostInfoBar("@"+viewerPostModel.getUsername(), viewerPostModel.getItemType(), - viewerPostModel.getLocation() == null ? null : viewerPostModel.getLocation()); - - postCaption = postModel.getPostCaption(); - viewerCaptionParent.setVisibility(View.VISIBLE); - - viewerBinding.bottomPanel.btnDownload.setOnClickListener(downloadClickListener); - - refreshPost(); - }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + if (postModel.getShortCode() != null) + new PostFetcher(postModel.getShortCode(), pfl).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + else if (postModel.getPostId() != null) + new iPostFetcher(postModel.getPostId(), pfl).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } private void searchUsername(final String text) { @@ -673,7 +677,7 @@ public final class PostViewer extends BaseLanguageActivity { viewerBinding.topPanel.ivProfilePic.setImageDrawable(null); viewerBinding.topPanel.ivProfilePic.setImageResource(0); - if (from.charAt(0) == '@') + if (!Utils.isEmpty(from) && from.charAt(0) == '@') new ProfileFetcher(from.substring(1), result -> { profileModel = result; diff --git a/app/src/main/java/awais/instagrabber/activities/StoryViewer.java b/app/src/main/java/awais/instagrabber/activities/StoryViewer.java index 78c73cc2..7fce2239 100755 --- a/app/src/main/java/awais/instagrabber/activities/StoryViewer.java +++ b/app/src/main/java/awais/instagrabber/activities/StoryViewer.java @@ -10,12 +10,15 @@ import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.Handler; +import android.text.InputType; import android.util.Log; import android.util.Pair; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.ArrayAdapter; +import android.widget.EditText; +import android.widget.LinearLayout; import android.widget.Toast; import androidx.annotation.NonNull; @@ -43,6 +46,10 @@ import java.io.File; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; +import java.net.URLEncoder; +import java.util.UUID; + +import org.json.JSONObject; import awais.instagrabber.BuildConfig; import awais.instagrabber.R; @@ -52,7 +59,8 @@ import awais.instagrabber.customviews.helpers.SwipeGestureListener; import awais.instagrabber.databinding.ActivityStoryViewerBinding; import awais.instagrabber.interfaces.SwipeEvent; import awais.instagrabber.models.FeedStoryModel; -import awais.instagrabber.models.PollModel; +import awais.instagrabber.models.stickers.PollModel; +import awais.instagrabber.models.stickers.QuestionModel; import awais.instagrabber.models.PostModel; import awais.instagrabber.models.StoryModel; import awais.instagrabber.models.enums.MediaItemType; @@ -86,6 +94,8 @@ public final class StoryViewer extends BaseLanguageActivity { private SwipeEvent swipeEvent; private MenuItem menuDownload; private PollModel poll; + private QuestionModel question; + private String[] mentions; private StoryModel currentStory; private String url, username; private int slidePos = 0, lastSlidePos = 0; @@ -206,10 +216,10 @@ public final class StoryViewer extends BaseLanguageActivity { storyViewerBinding.viewStoryPost.setOnClickListener(v -> { final Object tag = v.getTag(); if (tag instanceof CharSequence) startActivity(new Intent(this, PostViewer.class) - .putExtra(Constants.EXTRAS_POST, new PostModel(tag.toString()))); + .putExtra(Constants.EXTRAS_POST, new PostModel(tag.toString(), tag.toString().matches("^[\\d]+$")))); }); - storyViewerBinding.interactStory.setOnClickListener(v -> { + final View.OnClickListener storyActionListener = v -> { final Object tag = v.getTag(); if (tag instanceof PollModel) { poll = (PollModel) tag; @@ -231,7 +241,32 @@ public final class StoryViewer extends BaseLanguageActivity { .setPositiveButton(R.string.cancel, null) .show(); } - }); + else if (tag instanceof QuestionModel) { + question = (QuestionModel) tag; + final EditText input = new EditText(this); + input.setHint(R.string.answer_hint); + new AlertDialog.Builder(this).setTitle(question.getQuestion()) + .setView(input) + .setPositiveButton(R.string.ok, (d,w) -> { + new RespondAction().execute(input.getText().toString()); + }) + .setNegativeButton(R.string.cancel, null) + .show(); + } + else if (tag instanceof String[]) { + mentions = (String[]) tag; + new AlertDialog.Builder(this).setTitle(R.string.story_mentions) + .setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, mentions), (d,w) -> { + searchUsername(mentions[w]); + }) + .setPositiveButton(R.string.cancel, null) + .show(); + } + }; + + storyViewerBinding.poll.setOnClickListener(storyActionListener); + storyViewerBinding.answer.setOnClickListener(storyActionListener); + storyViewerBinding.mention.setOnClickListener(storyActionListener); storiesAdapter.setData(storyModels); if (storyModels.length > 1) storyViewerBinding.storiesList.setVisibility(View.VISIBLE); @@ -410,10 +445,17 @@ public final class StoryViewer extends BaseLanguageActivity { storyViewerBinding.spotify.setVisibility(spotify != null ? View.VISIBLE : View.GONE); storyViewerBinding.spotify.setTag(spotify); - final PollModel poll = currentStory.getPoll(); - storyViewerBinding.interactStory.setVisibility(poll != null ? View.VISIBLE : View.GONE); - storyViewerBinding.interactStory.setText(R.string.vote_story_poll); - storyViewerBinding.interactStory.setTag(poll); + poll = currentStory.getPoll(); + storyViewerBinding.poll.setVisibility(poll != null ? View.VISIBLE : View.GONE); + storyViewerBinding.poll.setTag(poll); + + question = currentStory.getQuestion(); + storyViewerBinding.answer.setVisibility(question != null ? View.VISIBLE : View.GONE); + storyViewerBinding.answer.setTag(question); + + mentions = currentStory.getMentions(); + storyViewerBinding.mention.setVisibility((mentions != null && mentions.length > 0) ? View.VISIBLE : View.GONE); + storyViewerBinding.mention.setTag(mentions); releasePlayer(); final Intent intent = getIntent(); @@ -497,4 +539,53 @@ public final class StoryViewer extends BaseLanguageActivity { } } } + + class RespondAction extends AsyncTask { + boolean ok = false; + String action; + + protected Void doInBackground(String... rawchoice) { + final String cookie = settingsHelper.getString(Constants.COOKIE); + final String url = "https://i.instagram.com/api/v1/media/" + +currentStory.getStoryMediaId()+"/"+question.getId()+"/story_question_response/"; + try { + JSONObject ogbody = new JSONObject("{\"client_context\":\"" + UUID.randomUUID().toString() + +"\",\"mutation_token\":\"" + UUID.randomUUID().toString() + +"\",\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0] + +"\",\"_uid\":\"" + Utils.getUserIdFromCookie(cookie) + +"\",\"__uuid\":\"" + settingsHelper.getString(Constants.DEVICE_UUID) + +"\"}"); + String choice = rawchoice[0].replaceAll("\"", ("\\\"")); + ogbody.put("response", choice); + String urlParameters = Utils.sign(ogbody.toString()); + final HttpURLConnection 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) { + ok = true; + } + else Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); + urlConnection.disconnect(); + } catch (Throwable ex) { + Log.e("austin_debug", "vote: " + ex); + } + return null; + } + + @Override + protected void onPostExecute(Void result) { + if (ok) { + Toast.makeText(getApplicationContext(), R.string.answered_story, Toast.LENGTH_SHORT).show(); + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/adapters/FeedAdapter.java b/app/src/main/java/awais/instagrabber/adapters/FeedAdapter.java index c223040e..008cdd5c 100755 --- a/app/src/main/java/awais/instagrabber/adapters/FeedAdapter.java +++ b/app/src/main/java/awais/instagrabber/adapters/FeedAdapter.java @@ -93,7 +93,7 @@ public final class FeedAdapter extends RecyclerView.Adapter case R.id.viewStoryPost: activity.startActivity(new Intent(activity, PostViewer.class) .putExtra(Constants.EXTRAS_INDEX, feedModel.getPosition()) - .putExtra(Constants.EXTRAS_POST, new PostModel(feedModel.getShortCode())) + .putExtra(Constants.EXTRAS_POST, new PostModel(feedModel.getShortCode(), false)) .putExtra(Constants.EXTRAS_TYPE, ItemGetType.FEED_ITEMS)); break; @@ -210,15 +210,9 @@ public final class FeedAdapter extends RecyclerView.Adapter final long commentsCount = feedModel.getCommentsCount(); viewHolder.commentsCount.setText(String.valueOf(commentsCount)); - if (commentsCount <= 0) { - viewHolder.btnComments.setTag(null); - viewHolder.btnComments.setOnClickListener(null); - viewHolder.btnComments.setEnabled(false); - } else { - viewHolder.btnComments.setTag(feedModel); - viewHolder.btnComments.setOnClickListener(clickListener); - viewHolder.btnComments.setEnabled(true); - } + viewHolder.btnComments.setTag(feedModel); + viewHolder.btnComments.setOnClickListener(clickListener); + viewHolder.btnComments.setEnabled(true); final JSONObject location = feedModel.getLocation(); @@ -234,6 +228,12 @@ public final class FeedAdapter extends RecyclerView.Adapter viewHolder.username.setLayoutParams(new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT )); + viewHolder.location.setOnClickListener(v -> + new AlertDialog.Builder(v.getContext()).setTitle(location.optString("name")) + .setMessage(R.string.comment_view_mention_location_search) + .setNegativeButton(R.string.cancel, null).setPositiveButton(R.string.ok, + (dialog, which) -> mentionClickListener.onClick(null, location.optString("id")+"/"+location.optString("slug"), false)).show() + ); } final String thumbnailUrl = feedModel.getThumbnailUrl(); diff --git a/app/src/main/java/awais/instagrabber/asyncs/FeedStoriesFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/FeedStoriesFetcher.java index 56b2b634..0202785e 100755 --- a/app/src/main/java/awais/instagrabber/asyncs/FeedStoriesFetcher.java +++ b/app/src/main/java/awais/instagrabber/asyncs/FeedStoriesFetcher.java @@ -68,14 +68,21 @@ public final class FeedStoriesFetcher extends AsyncTask private final String idSlug; public LocationFetcher(String idSlug, FetchListener fetchListener) { - // idSlug = id + "/" + slug + // idSlug = id + "/" + slug UPDATE: slug can be ignored tbh this.idSlug = idSlug; this.fetchListener = fetchListener; } diff --git a/app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java index 7ecc2d5e..9877a035 100755 --- a/app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java +++ b/app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java @@ -97,7 +97,6 @@ public final class PostFetcher extends AsyncTask media.optJSONObject("location")); postModel.setCommentsCount(commentsCount); - postModel.setCommentsEndCursor(endCursor); Utils.checkExistence(downloadDir, customDir, false, postModel); @@ -127,7 +126,6 @@ public final class PostFetcher extends AsyncTask } postModels[0].setCommentsCount(commentsCount); - postModels[0].setCommentsEndCursor(endCursor); result = postModels; } diff --git a/app/src/main/java/awais/instagrabber/asyncs/ProfileFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/ProfileFetcher.java index a063b20f..189f48e8 100755 --- a/app/src/main/java/awais/instagrabber/asyncs/ProfileFetcher.java +++ b/app/src/main/java/awais/instagrabber/asyncs/ProfileFetcher.java @@ -47,7 +47,7 @@ public final class ProfileFetcher extends AsyncTask { final JSONObject timelineMedia = user.getJSONObject("edge_owner_to_timeline_media"); if (timelineMedia.has("edges")) { final JSONArray edges = timelineMedia.getJSONArray("edges"); - if (edges.length() > 0) reallyPrivate = false; + if (edges.length() > 0 || timelineMedia.getLong("count") == 0L) reallyPrivate = false; } String url = user.optString("external_url"); diff --git a/app/src/main/java/awais/instagrabber/asyncs/ProfilePictureFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/ProfilePictureFetcher.java index 802fff03..d2c4331d 100755 --- a/app/src/main/java/awais/instagrabber/asyncs/ProfilePictureFetcher.java +++ b/app/src/main/java/awais/instagrabber/asyncs/ProfilePictureFetcher.java @@ -5,6 +5,10 @@ import android.util.Log; import android.util.Pair; import org.json.JSONObject; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; import java.net.HttpURLConnection; import java.net.URL; @@ -33,11 +37,11 @@ public final class ProfilePictureFetcher extends AsyncTask { @Override protected String doInBackground(final Void... voids) { - String out = picUrl; - if (!isHashtag) try { - final String url = "https://i.instagram.com/api/v1/users/"+userId+"/info/"; - - final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); + 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); @@ -50,13 +54,45 @@ public final class ProfilePictureFetcher extends AsyncTask { if (data.has("hd_profile_pic_url_info")) out = data.getJSONObject("hd_profile_pic_url_info").optString("url"); } + + if (Utils.isEmpty(out)) { + final HttpURLConnection backup = + (HttpURLConnection) new URL("https://instadp.com/fullsize/" + userName).openConnection(); + backup.setUseCaches(false); + backup.setRequestMethod("GET"); + + final String instadp = backup.getResponseCode() == HttpURLConnection.HTTP_OK ? Utils.readFromConnection(backup) : null; + backup.disconnect(); + + if (!Utils.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"); + } + } + } + if (out == null) out = picUrl; + } } catch (final Exception e) { if (logCollector != null) logCollector.appendException(e, LogCollector.LogFile.ASYNC_PROFILE_PICTURE_FETCHER, "doInBackground"); if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); } - if (out == null) out = picUrl; return out; } diff --git a/app/src/main/java/awais/instagrabber/asyncs/StoryStatusFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/StoryStatusFetcher.java deleted file mode 100755 index b9e6cb52..00000000 --- a/app/src/main/java/awais/instagrabber/asyncs/StoryStatusFetcher.java +++ /dev/null @@ -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 { - private final String id, hashtag; - private final boolean location; - private final FetchListener fetchListener; - - public StoryStatusFetcher(final String id, final String hashtag, final boolean location, final FetchListener 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); - } -} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/asyncs/i/iPostFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/i/iPostFetcher.java new file mode 100755 index 00000000..45f59524 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/asyncs/i/iPostFetcher.java @@ -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 { + private final String id; + private final FetchListener fetchListener; + + public iPostFetcher(final String id, final FetchListener 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); + } +} diff --git a/app/src/main/java/awais/instagrabber/asyncs/i/iStoryStatusFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/i/iStoryStatusFetcher.java new file mode 100755 index 00000000..9d6b959d --- /dev/null +++ b/app/src/main/java/awais/instagrabber/asyncs/i/iStoryStatusFetcher.java @@ -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 { + private final String id, username; + private final boolean isLoc, isHashtag; + private final FetchListener fetchListener; + + public iStoryStatusFetcher(final String id, final String username, final boolean isLoc, + final boolean isHashtag, final FetchListener 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); + } +} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/models/PostModel.java b/app/src/main/java/awais/instagrabber/models/PostModel.java index 0bb34f62..8c079a5a 100755 --- a/app/src/main/java/awais/instagrabber/models/PostModel.java +++ b/app/src/main/java/awais/instagrabber/models/PostModel.java @@ -7,8 +7,9 @@ public class PostModel extends BasePostModel { protected String endCursor; protected boolean hasNextPage; - public PostModel(final String shortCode) { - this.shortCode = shortCode; + public PostModel(final String shortCode, final boolean isid) { + if (!isid) this.shortCode = shortCode; + else this.postId = shortCode; this.thumbnailUrl = null; } diff --git a/app/src/main/java/awais/instagrabber/models/StoryModel.java b/app/src/main/java/awais/instagrabber/models/StoryModel.java index 8e367912..a9372697 100755 --- a/app/src/main/java/awais/instagrabber/models/StoryModel.java +++ b/app/src/main/java/awais/instagrabber/models/StoryModel.java @@ -3,13 +3,17 @@ package awais.instagrabber.models; import java.io.Serializable; import awais.instagrabber.models.enums.MediaItemType; +import awais.instagrabber.models.stickers.PollModel; +import awais.instagrabber.models.stickers.QuestionModel; public final class StoryModel implements Serializable { private final String storyMediaId, storyUrl, username; private final MediaItemType itemType; private final long timestamp; - private String videoUrl, tappableShortCode, spotify; + private String videoUrl, tappableShortCode, tappableId, spotify; private PollModel poll; + private QuestionModel question; + private String[] mentions; private int position; private boolean isCurrentSlide = false; @@ -49,6 +53,14 @@ public final class StoryModel implements Serializable { return poll; } + public QuestionModel getQuestion() { + return question; + } + + public String[] getMentions() { + return mentions; + } + public int getPosition() { return position; } @@ -73,6 +85,14 @@ public final class StoryModel implements Serializable { this.poll = poll; } + public void setQuestion(final QuestionModel question) { + this.question = question; + } + + public void setMentions(final String[] mentions) { + this.mentions = mentions; + } + public void setPosition(final int position) { this.position = position; } diff --git a/app/src/main/java/awais/instagrabber/models/ViewerPostModel.java b/app/src/main/java/awais/instagrabber/models/ViewerPostModel.java index bf501364..3ced283e 100755 --- a/app/src/main/java/awais/instagrabber/models/ViewerPostModel.java +++ b/app/src/main/java/awais/instagrabber/models/ViewerPostModel.java @@ -7,7 +7,7 @@ public final class ViewerPostModel extends BasePostModel { protected final String username; protected final JSONObject location; protected final long videoViews; - protected String sliderDisplayUrl, commentsEndCursor; + protected String sliderDisplayUrl; protected long commentsCount, likes; private boolean isCurrentSlide = false; @@ -44,10 +44,6 @@ public final class ViewerPostModel extends BasePostModel { return location; } - public String getCommentsEndCursor() { - return commentsEndCursor; - } - public final long getVideoViews() { return videoViews; } @@ -71,10 +67,6 @@ public final class ViewerPostModel extends BasePostModel { this.commentsCount = commentsCount; } - public void setCommentsEndCursor(final String commentsEndCursor) { - this.commentsEndCursor = commentsEndCursor; - } - public void setCurrentSlide(final boolean currentSlide) { this.isCurrentSlide = currentSlide; } diff --git a/app/src/main/java/awais/instagrabber/models/PollModel.java b/app/src/main/java/awais/instagrabber/models/stickers/PollModel.java similarity index 96% rename from app/src/main/java/awais/instagrabber/models/PollModel.java rename to app/src/main/java/awais/instagrabber/models/stickers/PollModel.java index 3cfe063a..3d0dcda9 100755 --- a/app/src/main/java/awais/instagrabber/models/PollModel.java +++ b/app/src/main/java/awais/instagrabber/models/stickers/PollModel.java @@ -1,4 +1,4 @@ -package awais.instagrabber.models; +package awais.instagrabber.models.stickers; import java.io.Serializable; diff --git a/app/src/main/java/awais/instagrabber/models/stickers/QuestionModel.java b/app/src/main/java/awais/instagrabber/models/stickers/QuestionModel.java new file mode 100755 index 00000000..43c6eac2 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/models/stickers/QuestionModel.java @@ -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; + } +} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/utils/Constants.java b/app/src/main/java/awais/instagrabber/utils/Constants.java index d79441bd..2ade29b7 100755 --- a/app/src/main/java/awais/instagrabber/utils/Constants.java +++ b/app/src/main/java/awais/instagrabber/utils/Constants.java @@ -22,6 +22,7 @@ public final class Constants { // never Export public static final String COOKIE = "cookie"; public static final String SHOW_QUICK_ACCESS_DIALOG = "show_quick_dlg"; + public static final String DEVICE_UUID = "device_uuid"; //////////////////////// EXTRAS //////////////////////// public static final String EXTRAS_USER = "user"; public static final String EXTRAS_HASHTAG = "hashtag"; @@ -45,6 +46,8 @@ public final class Constants { public static final String USER_AGENT = "Mozilla/5.0 (Linux; Android 8.1.0; motorola one Build/OPKS28.63-18-3; wv) " + "AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.80 Mobile Safari/537.36 " + "Instagram 72.0.0.21.98 Android (27/8.1.0; 320dpi; 720x1362; motorola; motorola one; deen_sprout; qcom; pt_BR; 132081645)"; + public static final String I_USER_AGENT = + "Instagram 72.0.0.21.98 Android (27/8.1.0; 320dpi; 720x1362; motorola; motorola one; deen_sprout; qcom; pt_BR; 132081645)"; // see https://github.com/dilame/instagram-private-api/blob/master/src/core/constants.ts public static final String SUPPORTED_CAPABILITIES = "[ { \"name\": \"SUPPORTED_SDK_VERSIONS\", \"value\":" + " \"13.0,14.0,15.0,16.0,17.0,18.0,19.0,20.0,21.0,22.0,23.0,24.0,25.0,26.0,27.0,28.0,29.0,30.0,31.0," + diff --git a/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java b/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java index c638cc51..81b4cbcf 100755 --- a/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java +++ b/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java @@ -18,6 +18,7 @@ import static awais.instagrabber.utils.Constants.CUSTOM_DATE_TIME_FORMAT; import static awais.instagrabber.utils.Constants.CUSTOM_DATE_TIME_FORMAT_ENABLED; import static awais.instagrabber.utils.Constants.DATE_TIME_FORMAT; import static awais.instagrabber.utils.Constants.DATE_TIME_SELECTION; +import static awais.instagrabber.utils.Constants.DEVICE_UUID; 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; @@ -101,7 +102,7 @@ public final class SettingsHelper { if (sharedPreferences != null) sharedPreferences.edit().putBoolean(key, val).apply(); } - @StringDef({COOKIE, FOLDER_PATH, DATE_TIME_FORMAT, DATE_TIME_SELECTION, CUSTOM_DATE_TIME_FORMAT}) + @StringDef({COOKIE, FOLDER_PATH, DATE_TIME_FORMAT, DATE_TIME_SELECTION, CUSTOM_DATE_TIME_FORMAT, DEVICE_UUID}) public @interface StringSettings {} @StringDef({DOWNLOAD_USER_FOLDER, BOTTOM_TOOLBAR, FOLDER_SAVE_TO, AUTOPLAY_VIDEOS, SHOW_QUICK_ACCESS_DIALOG, MUTED_VIDEOS, diff --git a/app/src/main/java/awais/instagrabber/utils/Utils.java b/app/src/main/java/awais/instagrabber/utils/Utils.java index 6a8cb22f..9a3cb05e 100755 --- a/app/src/main/java/awais/instagrabber/utils/Utils.java +++ b/app/src/main/java/awais/instagrabber/utils/Utils.java @@ -53,7 +53,6 @@ import java.net.HttpCookie; import java.net.HttpURLConnection; import java.net.URI; import java.net.URISyntaxException; -import java.security.MessageDigest; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; @@ -75,7 +74,7 @@ import awais.instagrabber.models.BasePostModel; import awais.instagrabber.models.FeedStoryModel; import awais.instagrabber.models.HighlightModel; import awais.instagrabber.models.IntentModel; -import awais.instagrabber.models.PollModel; +import awais.instagrabber.models.stickers.PollModel; import awais.instagrabber.models.ProfileModel; import awais.instagrabber.models.StoryModel; import awais.instagrabber.models.direct_messages.DirectItemModel; @@ -89,6 +88,7 @@ import awais.instagrabber.models.enums.MediaItemType; import awais.instagrabber.models.enums.NotificationType; import awais.instagrabber.models.enums.RavenExpiringMediaType; import awais.instagrabber.models.enums.RavenMediaViewType; +import awais.instagrabber.models.stickers.QuestionModel; import awaisomereport.LogCollector; import static awais.instagrabber.models.direct_messages.DirectItemModel.DirectItemActionLogModel; @@ -260,8 +260,9 @@ public final class Utils { return stringBuilder; } + // isI: true if the content was requested from i.instagram.com instead of graphql @Nullable - public static String getHighQualityPost(final JSONArray resources, final boolean isVideo) { + public static String getHighQualityPost(final JSONArray resources, final boolean isVideo, final boolean isI) { try { final int resourcesLen = resources.length(); @@ -270,18 +271,18 @@ public final class Utils { int lastResBase = 0, lastIndexBase = -1; for (int i = 0; i < resourcesLen; ++i) { final JSONObject item = resources.getJSONObject(i); - if (item != null && (!isVideo || item.has(Constants.EXTRAS_PROFILE))) { - sources[i] = item.getString("src"); - final int currRes = item.getInt("config_width") * item.getInt("config_height"); + if (item != null && (!isVideo || item.has(Constants.EXTRAS_PROFILE) || isI)) { + sources[i] = item.getString(isI ? "url" : "src"); + final int currRes = item.getInt(isI ? "width" : "config_width") * item.getInt(isI ? "height" : "config_height"); - final String profile = isVideo ? item.getString(Constants.EXTRAS_PROFILE) : null; + final String profile = isVideo ? item.optString(Constants.EXTRAS_PROFILE) : null; if (!isVideo || "MAIN".equals(profile)) { if (currRes > lastResMain) { lastResMain = currRes; lastIndexMain = i; } - } else if ("BASELINE".equals(profile)) { + } else { if (currRes > lastResBase) { lastResBase = currRes; lastIndexBase = i; @@ -305,7 +306,9 @@ public final class Utils { public static String getHighQualityImage(final JSONObject resources) { String src = null; try { - if (resources.has("display_resources")) src = getHighQualityPost(resources.getJSONArray("display_resources"), false); + if (resources.has("display_resources")) src = getHighQualityPost(resources.getJSONArray("display_resources"), false, false); + else if (resources.has("image_versions2")) + src = getHighQualityPost(resources.getJSONObject("image_versions2").getJSONArray("candidates"), false, true); if (src == null) return resources.getString("display_url"); } catch (final Exception e) { if (logCollector != null) @@ -1157,6 +1160,23 @@ public final class Utils { return "[]"; } + @NonNull + public static String iHighlightIdsMerger(final String... strings) { + if (strings != null) { + int iMax = strings.length - 1; + if (iMax != -1) { + final StringBuilder builder = new StringBuilder(); + for (int i = 0; ; i++) { + builder.append(strings[i]); + if (i == iMax) return builder.toString(); + builder.append("&reel_ids="); + } + } + + } + return ""; + } + public static void putHighlightModels(final HttpURLConnection conn, final Object[] model) throws Exception { final boolean isHighlightModel = model instanceof HighlightModel[]; final boolean isFeedStoryModel = model instanceof FeedStoryModel[]; @@ -1192,7 +1212,7 @@ public final class Utils { data.getLong("taken_at_timestamp"), data.getJSONObject("owner").getString("username")); if (isVideo && data.has("video_resources")) - storyModels[j].setVideoUrl(Utils.getHighQualityPost(data.getJSONArray("video_resources"), true)); + storyModels[j].setVideoUrl(Utils.getHighQualityPost(data.getJSONArray("video_resources"), true, false)); if (!data.isNull("story_app_attribution")) storyModels[j].setSpotify(data.getJSONObject("story_app_attribution").optString("content_url").split("\\?")[0]); @@ -1226,6 +1246,75 @@ public final class Utils { } } + public static void iPutFeedStoryModels(final HttpURLConnection conn, final FeedStoryModel[] model, final String[] ids) throws Exception { + final JSONObject highlightsMediaReel = new JSONObject(Utils.readFromConnection(conn)).getJSONObject("reels"); + final int mediaLength = highlightsMediaReel.length(); + + for (int i = 0; i < mediaLength; ++i) { + final JSONArray items = highlightsMediaReel.getJSONObject(ids[i]).getJSONArray("items"); + final int itemsLen = items.length(); + + final StoryModel[] storyModels = new StoryModel[itemsLen]; + for (int j = 0; j < itemsLen; ++j) { + final JSONObject data = items.getJSONObject(j); + final boolean isVideo = data.has("has_audio") && data.optBoolean("has_audio"); + + storyModels[j] = 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), + model[i].getProfileModel().getUsername()); + + final JSONArray videoResources = data.optJSONArray("video_versions"); + if (isVideo && videoResources != null) + storyModels[j].setVideoUrl(Utils.getHighQualityPost(videoResources, true, true)); + + if (data.has("story_feed_media")) { + storyModels[j].setTappableShortCode(data.getJSONArray("story_feed_media").getJSONObject(0).optString("media_id")); + } + + if (!data.isNull("story_app_attribution")) + storyModels[j].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) storyModels[j].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) storyModels[j].setQuestion(new QuestionModel( + String.valueOf(tappableObject.getLong("question_id")), + tappableObject.getString("question") + )); + } + JSONArray hashtags = data.optJSONArray("story_hashtags"); + JSONArray atmarks = data.optJSONArray("reel_mentions"); + String[] mentions = new String[(hashtags == null ? 0 : hashtags.length()) + (atmarks == null ? 0 : atmarks.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"); + } + } + storyModels[j].setMentions(mentions); + } + model[i].setStoryModels(storyModels); + } + } + public static String sign(final String message) { try { Mac hasher = Mac.getInstance("HmacSHA256"); @@ -1237,8 +1326,7 @@ public final class Utils { if (hex.length() == 1) hexString.append('0'); hexString.append(hex); } - Log.d("austin_debug", "hash: " + hexString.toString()); - return hexString.toString() + "." + message; + return "ig_sig_key_version="+Constants.SIGNATURE_VERSION+"&signed_body=" + hexString.toString() + "." + message; } catch (Throwable e) { Log.e("austin_debug", "sign: ", e); diff --git a/app/src/main/res/layout/activity_story_viewer.xml b/app/src/main/res/layout/activity_story_viewer.xml index d6a6a7f4..e25f75c2 100755 --- a/app/src/main/res/layout/activity_story_viewer.xml +++ b/app/src/main/res/layout/activity_story_viewer.xml @@ -44,7 +44,6 @@ android:layout_height="wrap_content" android:layout_weight="0.3" android:background="#0000" - android:weightSum="3" android:layout_gravity="bottom"> + + Vote Vote successful! You have already voted! + Answer + Answer... + Answer successful! + Mentions This Account is Private You can log in via Settings on the bottom-right corner. Or, you can view public accounts without login! You can swipe left/right for explore/feed, or search something below! diff --git a/fastlane/metadata/android/changelogs/37.txt b/fastlane/metadata/android/changelogs/37.txt new file mode 100644 index 00000000..312ae726 --- /dev/null +++ b/fastlane/metadata/android/changelogs/37.txt @@ -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 \ No newline at end of file