1
0
mirror of https://github.com/KokaKiwi/BarInsta synced 2024-11-22 06:37:30 +00:00

story stickers and various bug fixes

This commit is contained in:
Austin Huang 2020-08-03 22:28:42 -04:00
parent 90daf3069c
commit eac0d9a5f7
No known key found for this signature in database
GPG Key ID: 84C23AA04587A91F
28 changed files with 754 additions and 284 deletions

View File

@ -12,8 +12,8 @@ android {
// REMEMBER TO CHANGE versionCode AS WELL // REMEMBER TO CHANGE versionCode AS WELL
// 16.7 is 32, 16.9 is 35 (34 is public beta) // 16.7 is 32, 16.9 is 35 (34 is public beta)
versionCode 36 versionCode 37
versionName '17.0' versionName '17.1'
multiDexEnabled true multiDexEnabled true

View File

@ -8,6 +8,7 @@ import androidx.multidex.MultiDexApplication;
import java.net.CookieHandler; import java.net.CookieHandler;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.UUID;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.DataBox; 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.CUSTOM_DATE_TIME_FORMAT) :
settingsHelper.getString(Constants.DATE_TIME_FORMAT), LocaleUtils.getCurrentLocale()); settingsHelper.getString(Constants.DATE_TIME_FORMAT), LocaleUtils.getCurrentLocale());
settingsHelper.putString(Constants.DEVICE_UUID, UUID.randomUUID().toString());
changeTheme(); changeTheme();
} }
} }

View File

@ -60,7 +60,7 @@ import awais.instagrabber.asyncs.HighlightsFetcher;
import awais.instagrabber.asyncs.LocationFetcher; import awais.instagrabber.asyncs.LocationFetcher;
import awais.instagrabber.asyncs.PostsFetcher; import awais.instagrabber.asyncs.PostsFetcher;
import awais.instagrabber.asyncs.ProfileFetcher; 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.MouseDrawer;
import awais.instagrabber.customviews.RamboTextView; import awais.instagrabber.customviews.RamboTextView;
import awais.instagrabber.customviews.helpers.GridAutofitLayoutManager; 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) else main.startActivity(new Intent(main, PostViewer.class)
.putExtra(Constants.EXTRAS_INDEX, itemModel.getPosition()) .putExtra(Constants.EXTRAS_INDEX, itemModel.getPosition())
.putExtra(Constants.EXTRAS_TYPE, ItemGetType.DISCOVER_ITEMS) .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 -> { }, v -> {
final Object tag = v.getTag(); final Object tag = v.getTag();
@ -599,7 +599,7 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
if (modelType == IntentModelType.POST) { if (modelType == IntentModelType.POST) {
main.startActivityForResult(new Intent(main, PostViewer.class) main.startActivityForResult(new Intent(main, PostViewer.class)
.putExtra(Constants.EXTRAS_USER, main.userQuery) .putExtra(Constants.EXTRAS_USER, main.userQuery)
.putExtra(Constants.EXTRAS_POST, new PostModel(modelText)), 9629); .putExtra(Constants.EXTRAS_POST, new PostModel(modelText, false)), 9629);
} else { } else {
main.addToStack(); main.addToStack();
main.userQuery = modelType == IntentModelType.HASHTAG ? '#' + modelText : ("@"+modelText); main.userQuery = modelType == IntentModelType.HASHTAG ? '#' + modelText : ("@"+modelText);
@ -700,8 +700,6 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
return; return;
} }
final String profileId = hashtagModel.getId();
final String cookie = Utils.settingsHelper.getString(Constants.COOKIE); final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);
final boolean isLoggedIn = !Utils.isEmpty(cookie); final boolean isLoggedIn = !Utils.isEmpty(cookie);
@ -711,7 +709,7 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
main.mainBinding.btnFollowTag.setVisibility(View.VISIBLE); main.mainBinding.btnFollowTag.setVisibility(View.VISIBLE);
if (isLoggedIn) { if (isLoggedIn) {
new StoryStatusFetcher(profileId, hashtagModel.getName(), false, result -> { new iStoryStatusFetcher(hashtagModel.getName(), null, false, true, result -> {
main.storyModels = result; main.storyModels = result;
if (result != null && result.length > 0) main.mainBinding.mainHashtagImage.setStoriesBorder(); if (result != null && result.length > 0) main.mainBinding.mainHashtagImage.setStoriesBorder();
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }).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 String cookie = Utils.settingsHelper.getString(Constants.COOKIE);
final boolean isLoggedIn = !Utils.isEmpty(cookie); final boolean isLoggedIn = !Utils.isEmpty(cookie);
if (isLoggedIn) { if (isLoggedIn) {
new StoryStatusFetcher(profileId, "", false, result -> { new iStoryStatusFetcher(profileId, profileModel.getUsername(), false, false, result -> {
main.storyModels = result; main.storyModels = result;
if (result != null && result.length > 0) main.mainBinding.mainProfileImage.setStoriesBorder(); if (result != null && result.length > 0) main.mainBinding.mainProfileImage.setStoriesBorder();
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }).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.toolbar.toolbar.setTitle(main.userQuery);
main.mainBinding.locInfoContainer.setVisibility(View.VISIBLE); main.mainBinding.locInfoContainer.setVisibility(View.VISIBLE);
currentlyExecuting = new LocationFetcher(main.userQuery, locationModel -> { currentlyExecuting = new LocationFetcher(main.userQuery.split("/")[0], locationModel -> {
main.locationModel = locationModel; main.locationModel = locationModel;
main.mainBinding.toolbar.toolbar.setTitle(locationModel.getName()); 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 String cookie = Utils.settingsHelper.getString(Constants.COOKIE);
final boolean isLoggedIn = !Utils.isEmpty(cookie); final boolean isLoggedIn = !Utils.isEmpty(cookie);
if (isLoggedIn) { if (isLoggedIn) {
new StoryStatusFetcher(profileId, "", true, result -> { new iStoryStatusFetcher(profileId.split("/")[0], null, true, false, result -> {
main.storyModels = result; main.storyModels = result;
if (result != null && result.length > 0) main.mainBinding.mainLocationImage.setStoriesBorder(); if (result != null && result.length > 0) main.mainBinding.mainLocationImage.setStoriesBorder();
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
@ -1199,7 +1197,7 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
private final View.OnClickListener profileActionListener = new View.OnClickListener() { private final View.OnClickListener profileActionListener = new View.OnClickListener() {
@Override @Override
public void onClick(final View v) { 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()) ? Utils.getUserIdFromCookie(Utils.settingsHelper.getString(Constants.COOKIE)).equals(main.profileModel.getId())
: false; : false;
if (!isLoggedIn && Utils.dataBox.getFavorite(main.userQuery) != null && v == main.mainBinding.btnFollow) { if (!isLoggedIn && Utils.dataBox.getFavorite(main.userQuery) != null && v == main.mainBinding.btnFollow) {

View File

@ -50,6 +50,7 @@ public final class CommentsViewer extends BaseLanguageActivity implements SwipeR
private final String cookie = Utils.settingsHelper.getString(Constants.COOKIE); private final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);
private Resources resources; private Resources resources;
private InputMethodManager imm; private InputMethodManager imm;
private View focus;
@Override @Override
protected void onCreate(@Nullable final Bundle savedInstanceState) { protected void onCreate(@Nullable final Bundle savedInstanceState) {
@ -122,7 +123,8 @@ public final class CommentsViewer extends BaseLanguageActivity implements SwipeR
} else if (which == 3) { } else if (which == 3) {
Utils.copyText(this, commentModel.getText().toString()); Utils.copyText(this, commentModel.getText().toString());
} else if (which == 4) { } else if (which == 4) {
commentsBinding.rvComments.findViewWithTag(commentModel).setBackgroundColor(0x80888888); focus = commentsBinding.rvComments.findViewWithTag(commentModel);
focus.setBackgroundColor(0x80888888);
commentsBinding.commentCancelParent.setVisibility(View.VISIBLE); commentsBinding.commentCancelParent.setVisibility(View.VISIBLE);
String mention = "@"+profileModel.getUsername()+" "; String mention = "@"+profileModel.getUsername()+" ";
commentsBinding.commentText.setText(mention); 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(); 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.commentSend) new CommentAction().execute("add");
else if (v == commentsBinding.commentCancelParent) { 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.commentCancelParent.setVisibility(View.GONE);
commentsBinding.commentText.setText(""); commentsBinding.commentText.setText("");
commentModel = null; commentModel = null;
focus = null;
} }
}; };
@ -287,9 +290,11 @@ public final class CommentsViewer extends BaseLanguageActivity implements SwipeR
@Override @Override
protected void onPostExecute(Void result) { protected void onPostExecute(Void result) {
if (ok == true) { if (ok == true) {
if (commentModel != null) { if (focus != null) {
commentsBinding.rvComments.findViewWithTag(commentModel).setBackgroundColor(commentModel.getLiked() ? 0x40FF69B4 : 0x00000000); focus.setBackgroundColor(commentModel.getLiked() ? 0x40FF69B4 : 0x00000000);
commentsBinding.commentCancelParent.setVisibility(View.GONE); commentsBinding.commentCancelParent.setVisibility(View.GONE);
commentModel = null;
focus = null;
} }
//imm.hideSoftInputFromWindow(commentsBinding.getView().getRootView().getWindowToken(), 0); //imm.hideSoftInputFromWindow(commentsBinding.getView().getRootView().getWindowToken(), 0);

View File

@ -110,7 +110,7 @@ public final class DirectMessagesUserInbox extends AppCompatActivity {
switch (itemType) { switch (itemType) {
case MEDIA_SHARE: case MEDIA_SHARE:
startActivity(new Intent(this, PostViewer.class) 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; break;
case LINK: case LINK:
Intent linkIntent = new Intent(Intent.ACTION_VIEW); Intent linkIntent = new Intent(Intent.ACTION_VIEW);

View File

@ -92,7 +92,7 @@ public final class NotificationsViewer extends BaseLanguageActivity implements S
searchUsername(notificationModel.getUsername()); searchUsername(notificationModel.getUsername());
else if (which == 1) else if (which == 1)
startActivity(new Intent(getApplicationContext(), PostViewer.class) 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 -> { private final View.OnClickListener clickListener = v -> {

View File

@ -57,9 +57,11 @@ import awais.instagrabber.R;
import awais.instagrabber.adapters.PostsMediaAdapter; import awais.instagrabber.adapters.PostsMediaAdapter;
import awais.instagrabber.asyncs.PostFetcher; import awais.instagrabber.asyncs.PostFetcher;
import awais.instagrabber.asyncs.ProfileFetcher; import awais.instagrabber.asyncs.ProfileFetcher;
import awais.instagrabber.asyncs.i.iPostFetcher;
import awais.instagrabber.customviews.CommentMentionClickSpan; import awais.instagrabber.customviews.CommentMentionClickSpan;
import awais.instagrabber.customviews.helpers.SwipeGestureListener; import awais.instagrabber.customviews.helpers.SwipeGestureListener;
import awais.instagrabber.databinding.ActivityViewerBinding; import awais.instagrabber.databinding.ActivityViewerBinding;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.interfaces.SwipeEvent; import awais.instagrabber.interfaces.SwipeEvent;
import awais.instagrabber.models.BasePostModel; import awais.instagrabber.models.BasePostModel;
import awais.instagrabber.models.PostModel; import awais.instagrabber.models.PostModel;
@ -185,6 +187,83 @@ public final class PostViewer extends BaseLanguageActivity {
private final PostsMediaAdapter mediaAdapter = new PostsMediaAdapter(null, onClickListener); private final PostsMediaAdapter mediaAdapter = new PostsMediaAdapter(null, onClickListener);
private RequestManager glideRequestManager; private RequestManager glideRequestManager;
private LinearLayout.LayoutParams containerLayoutParams; private LinearLayout.LayoutParams containerLayoutParams;
private final FetchListener<ViewerPostModel[]> 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 @Override
protected void onCreate(@Nullable final Bundle savedInstanceState) { protected void onCreate(@Nullable final Bundle savedInstanceState) {
@ -341,85 +420,10 @@ public final class PostViewer extends BaseLanguageActivity {
viewerBinding.imageViewer.setImageResource(0); viewerBinding.imageViewer.setImageResource(0);
viewerBinding.imageViewer.setImageDrawable(null); viewerBinding.imageViewer.setImageDrawable(null);
new PostFetcher(postModel.getShortCode(), result -> { if (postModel.getShortCode() != null)
if (result == null || result.length < 1) { new PostFetcher(postModel.getShortCode(), pfl).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
Toast.makeText(this, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); else if (postModel.getPostId() != null)
return; new iPostFetcher(postModel.getPostId(), pfl).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
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);
} }
private void searchUsername(final String text) { private void searchUsername(final String text) {
@ -673,7 +677,7 @@ public final class PostViewer extends BaseLanguageActivity {
viewerBinding.topPanel.ivProfilePic.setImageDrawable(null); viewerBinding.topPanel.ivProfilePic.setImageDrawable(null);
viewerBinding.topPanel.ivProfilePic.setImageResource(0); viewerBinding.topPanel.ivProfilePic.setImageResource(0);
if (from.charAt(0) == '@') if (!Utils.isEmpty(from) && from.charAt(0) == '@')
new ProfileFetcher(from.substring(1), result -> { new ProfileFetcher(from.substring(1), result -> {
profileModel = result; profileModel = result;

View File

@ -10,12 +10,15 @@ import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment; import android.os.Environment;
import android.os.Handler; import android.os.Handler;
import android.text.InputType;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -43,6 +46,10 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import java.net.URLEncoder;
import java.util.UUID;
import org.json.JSONObject;
import awais.instagrabber.BuildConfig; import awais.instagrabber.BuildConfig;
import awais.instagrabber.R; import awais.instagrabber.R;
@ -52,7 +59,8 @@ import awais.instagrabber.customviews.helpers.SwipeGestureListener;
import awais.instagrabber.databinding.ActivityStoryViewerBinding; import awais.instagrabber.databinding.ActivityStoryViewerBinding;
import awais.instagrabber.interfaces.SwipeEvent; import awais.instagrabber.interfaces.SwipeEvent;
import awais.instagrabber.models.FeedStoryModel; 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.PostModel;
import awais.instagrabber.models.StoryModel; import awais.instagrabber.models.StoryModel;
import awais.instagrabber.models.enums.MediaItemType; import awais.instagrabber.models.enums.MediaItemType;
@ -86,6 +94,8 @@ public final class StoryViewer extends BaseLanguageActivity {
private SwipeEvent swipeEvent; private SwipeEvent swipeEvent;
private MenuItem menuDownload; private MenuItem menuDownload;
private PollModel poll; private PollModel poll;
private QuestionModel question;
private String[] mentions;
private StoryModel currentStory; private StoryModel currentStory;
private String url, username; private String url, username;
private int slidePos = 0, lastSlidePos = 0; private int slidePos = 0, lastSlidePos = 0;
@ -206,10 +216,10 @@ public final class StoryViewer extends BaseLanguageActivity {
storyViewerBinding.viewStoryPost.setOnClickListener(v -> { storyViewerBinding.viewStoryPost.setOnClickListener(v -> {
final Object tag = v.getTag(); final Object tag = v.getTag();
if (tag instanceof CharSequence) startActivity(new Intent(this, PostViewer.class) 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(); final Object tag = v.getTag();
if (tag instanceof PollModel) { if (tag instanceof PollModel) {
poll = (PollModel) tag; poll = (PollModel) tag;
@ -231,7 +241,32 @@ public final class StoryViewer extends BaseLanguageActivity {
.setPositiveButton(R.string.cancel, null) .setPositiveButton(R.string.cancel, null)
.show(); .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); storiesAdapter.setData(storyModels);
if (storyModels.length > 1) storyViewerBinding.storiesList.setVisibility(View.VISIBLE); 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.setVisibility(spotify != null ? View.VISIBLE : View.GONE);
storyViewerBinding.spotify.setTag(spotify); storyViewerBinding.spotify.setTag(spotify);
final PollModel poll = currentStory.getPoll(); poll = currentStory.getPoll();
storyViewerBinding.interactStory.setVisibility(poll != null ? View.VISIBLE : View.GONE); storyViewerBinding.poll.setVisibility(poll != null ? View.VISIBLE : View.GONE);
storyViewerBinding.interactStory.setText(R.string.vote_story_poll); storyViewerBinding.poll.setTag(poll);
storyViewerBinding.interactStory.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(); releasePlayer();
final Intent intent = getIntent(); final Intent intent = getIntent();
@ -497,4 +539,53 @@ public final class StoryViewer extends BaseLanguageActivity {
} }
} }
} }
class RespondAction extends AsyncTask<String, Void, Void> {
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();
}
}
}
} }

View File

@ -93,7 +93,7 @@ public final class FeedAdapter extends RecyclerView.Adapter<FeedItemViewHolder>
case R.id.viewStoryPost: case R.id.viewStoryPost:
activity.startActivity(new Intent(activity, PostViewer.class) activity.startActivity(new Intent(activity, PostViewer.class)
.putExtra(Constants.EXTRAS_INDEX, feedModel.getPosition()) .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)); .putExtra(Constants.EXTRAS_TYPE, ItemGetType.FEED_ITEMS));
break; break;
@ -210,15 +210,9 @@ public final class FeedAdapter extends RecyclerView.Adapter<FeedItemViewHolder>
final long commentsCount = feedModel.getCommentsCount(); final long commentsCount = feedModel.getCommentsCount();
viewHolder.commentsCount.setText(String.valueOf(commentsCount)); viewHolder.commentsCount.setText(String.valueOf(commentsCount));
if (commentsCount <= 0) { viewHolder.btnComments.setTag(feedModel);
viewHolder.btnComments.setTag(null); viewHolder.btnComments.setOnClickListener(clickListener);
viewHolder.btnComments.setOnClickListener(null); viewHolder.btnComments.setEnabled(true);
viewHolder.btnComments.setEnabled(false);
} else {
viewHolder.btnComments.setTag(feedModel);
viewHolder.btnComments.setOnClickListener(clickListener);
viewHolder.btnComments.setEnabled(true);
}
final JSONObject location = feedModel.getLocation(); final JSONObject location = feedModel.getLocation();
@ -234,6 +228,12 @@ public final class FeedAdapter extends RecyclerView.Adapter<FeedItemViewHolder>
viewHolder.username.setLayoutParams(new RelativeLayout.LayoutParams( viewHolder.username.setLayoutParams(new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT 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(); final String thumbnailUrl = feedModel.getThumbnailUrl();

View File

@ -68,14 +68,21 @@ public final class FeedStoriesFetcher extends AsyncTask<Void, Void, FeedStoryMod
feedStoryModels[i] = new FeedStoryModel(id, profileModel); feedStoryModels[i] = new FeedStoryModel(id, profileModel);
} }
url = "https://www.instagram.com/graphql/query/?query_hash=0a85e6ea60a4c99edc58ab2f3d17cfdf&variables=" + for (int s = 0; s <= Math.ceil(feedStoryIDs.length / 20); ++s) {
"{\"reel_ids\":" + Utils.highlightIdsMerger(feedStoryIDs) + ",\"precomposed_overlay\":false}"; String[] shard = new String[Math.min(20, (feedStoryIDs.length - s*20))];
conn = (HttpURLConnection) new URL(url).openConnection(); FeedStoryModel[] feedStoryShard = new FeedStoryModel[Math.min(20, (feedStoryIDs.length - s*20))];
conn.setInstanceFollowRedirects(false); System.arraycopy(feedStoryIDs, s*20, shard, 0, shard.length);
conn.setUseCaches(false); System.arraycopy(feedStoryModels, s*20, feedStoryShard, 0, shard.length);
conn.connect(); url = "https://i.instagram.com/api/v1/feed/reels_media/?reel_ids=" + Utils.iHighlightIdsMerger(shard);
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { conn = (HttpURLConnection) new URL(url).openConnection();
Utils.putHighlightModels(conn, feedStoryModels); conn.setRequestProperty("User-Agent", Constants.USER_AGENT);
conn.setInstanceFollowRedirects(false);
conn.setUseCaches(false);
conn.setReadTimeout(2000);
conn.connect();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
Utils.iPutFeedStoryModels(conn, feedStoryShard, shard);
}
} }
result = feedStoryModels; result = feedStoryModels;

View File

@ -26,7 +26,7 @@ public final class LocationFetcher extends AsyncTask<Void, Void, LocationModel>
private final String idSlug; private final String idSlug;
public LocationFetcher(String idSlug, FetchListener<LocationModel> fetchListener) { public LocationFetcher(String idSlug, FetchListener<LocationModel> fetchListener) {
// idSlug = id + "/" + slug // idSlug = id + "/" + slug UPDATE: slug can be ignored tbh
this.idSlug = idSlug; this.idSlug = idSlug;
this.fetchListener = fetchListener; this.fetchListener = fetchListener;
} }

View File

@ -97,7 +97,6 @@ public final class PostFetcher extends AsyncTask<Void, Void, ViewerPostModel[]>
media.optJSONObject("location")); media.optJSONObject("location"));
postModel.setCommentsCount(commentsCount); postModel.setCommentsCount(commentsCount);
postModel.setCommentsEndCursor(endCursor);
Utils.checkExistence(downloadDir, customDir, false, postModel); Utils.checkExistence(downloadDir, customDir, false, postModel);
@ -127,7 +126,6 @@ public final class PostFetcher extends AsyncTask<Void, Void, ViewerPostModel[]>
} }
postModels[0].setCommentsCount(commentsCount); postModels[0].setCommentsCount(commentsCount);
postModels[0].setCommentsEndCursor(endCursor);
result = postModels; result = postModels;
} }

View File

@ -47,7 +47,7 @@ public final class ProfileFetcher extends AsyncTask<Void, Void, ProfileModel> {
final JSONObject timelineMedia = user.getJSONObject("edge_owner_to_timeline_media"); final JSONObject timelineMedia = user.getJSONObject("edge_owner_to_timeline_media");
if (timelineMedia.has("edges")) { if (timelineMedia.has("edges")) {
final JSONArray edges = timelineMedia.getJSONArray("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"); String url = user.optString("external_url");

View File

@ -5,6 +5,10 @@ import android.util.Log;
import android.util.Pair; import android.util.Pair;
import org.json.JSONObject; 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.HttpURLConnection;
import java.net.URL; import java.net.URL;
@ -33,11 +37,11 @@ public final class ProfilePictureFetcher extends AsyncTask<Void, Void, String> {
@Override @Override
protected String doInBackground(final Void... voids) { protected String doInBackground(final Void... voids) {
String out = picUrl; String out = null;
if (!isHashtag) try { if (isHashtag) out = picUrl;
final String url = "https://i.instagram.com/api/v1/users/"+userId+"/info/"; else try {
final HttpURLConnection conn =
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); (HttpURLConnection) new URL("https://i.instagram.com/api/v1/users/"+userId+"/info/").openConnection();
conn.setUseCaches(false); conn.setUseCaches(false);
conn.setRequestMethod("GET"); conn.setRequestMethod("GET");
conn.setRequestProperty("User-Agent", Constants.USER_AGENT); conn.setRequestProperty("User-Agent", Constants.USER_AGENT);
@ -50,13 +54,45 @@ public final class ProfilePictureFetcher extends AsyncTask<Void, Void, String> {
if (data.has("hd_profile_pic_url_info")) if (data.has("hd_profile_pic_url_info"))
out = data.getJSONObject("hd_profile_pic_url_info").optString("url"); 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) { } catch (final Exception e) {
if (logCollector != null) if (logCollector != null)
logCollector.appendException(e, LogCollector.LogFile.ASYNC_PROFILE_PICTURE_FETCHER, "doInBackground"); logCollector.appendException(e, LogCollector.LogFile.ASYNC_PROFILE_PICTURE_FETCHER, "doInBackground");
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
} }
if (out == null) out = picUrl;
return out; return out;
} }

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -7,8 +7,9 @@ public class PostModel extends BasePostModel {
protected String endCursor; protected String endCursor;
protected boolean hasNextPage; protected boolean hasNextPage;
public PostModel(final String shortCode) { public PostModel(final String shortCode, final boolean isid) {
this.shortCode = shortCode; if (!isid) this.shortCode = shortCode;
else this.postId = shortCode;
this.thumbnailUrl = null; this.thumbnailUrl = null;
} }

View File

@ -3,13 +3,17 @@ package awais.instagrabber.models;
import java.io.Serializable; import java.io.Serializable;
import awais.instagrabber.models.enums.MediaItemType; import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.models.stickers.PollModel;
import awais.instagrabber.models.stickers.QuestionModel;
public final class StoryModel implements Serializable { public final class StoryModel implements Serializable {
private final String storyMediaId, storyUrl, username; private final String storyMediaId, storyUrl, username;
private final MediaItemType itemType; private final MediaItemType itemType;
private final long timestamp; private final long timestamp;
private String videoUrl, tappableShortCode, spotify; private String videoUrl, tappableShortCode, tappableId, spotify;
private PollModel poll; private PollModel poll;
private QuestionModel question;
private String[] mentions;
private int position; private int position;
private boolean isCurrentSlide = false; private boolean isCurrentSlide = false;
@ -49,6 +53,14 @@ public final class StoryModel implements Serializable {
return poll; return poll;
} }
public QuestionModel getQuestion() {
return question;
}
public String[] getMentions() {
return mentions;
}
public int getPosition() { public int getPosition() {
return position; return position;
} }
@ -73,6 +85,14 @@ public final class StoryModel implements Serializable {
this.poll = poll; 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) { public void setPosition(final int position) {
this.position = position; this.position = position;
} }

View File

@ -7,7 +7,7 @@ public final class ViewerPostModel extends BasePostModel {
protected final String username; protected final String username;
protected final JSONObject location; protected final JSONObject location;
protected final long videoViews; protected final long videoViews;
protected String sliderDisplayUrl, commentsEndCursor; protected String sliderDisplayUrl;
protected long commentsCount, likes; protected long commentsCount, likes;
private boolean isCurrentSlide = false; private boolean isCurrentSlide = false;
@ -44,10 +44,6 @@ public final class ViewerPostModel extends BasePostModel {
return location; return location;
} }
public String getCommentsEndCursor() {
return commentsEndCursor;
}
public final long getVideoViews() { public final long getVideoViews() {
return videoViews; return videoViews;
} }
@ -71,10 +67,6 @@ public final class ViewerPostModel extends BasePostModel {
this.commentsCount = commentsCount; this.commentsCount = commentsCount;
} }
public void setCommentsEndCursor(final String commentsEndCursor) {
this.commentsEndCursor = commentsEndCursor;
}
public void setCurrentSlide(final boolean currentSlide) { public void setCurrentSlide(final boolean currentSlide) {
this.isCurrentSlide = currentSlide; this.isCurrentSlide = currentSlide;
} }

View File

@ -1,4 +1,4 @@
package awais.instagrabber.models; package awais.instagrabber.models.stickers;
import java.io.Serializable; import java.io.Serializable;

View File

@ -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;
}
}

View File

@ -22,6 +22,7 @@ public final class Constants {
// never Export // never Export
public static final String COOKIE = "cookie"; public static final String COOKIE = "cookie";
public static final String SHOW_QUICK_ACCESS_DIALOG = "show_quick_dlg"; public static final String SHOW_QUICK_ACCESS_DIALOG = "show_quick_dlg";
public static final String DEVICE_UUID = "device_uuid";
//////////////////////// EXTRAS //////////////////////// //////////////////////// EXTRAS ////////////////////////
public static final String EXTRAS_USER = "user"; public static final String EXTRAS_USER = "user";
public static final String EXTRAS_HASHTAG = "hashtag"; 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) " + 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 " + "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)"; "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 // 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\":" + 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," + " \"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," +

View File

@ -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.CUSTOM_DATE_TIME_FORMAT_ENABLED;
import static awais.instagrabber.utils.Constants.DATE_TIME_FORMAT; import static awais.instagrabber.utils.Constants.DATE_TIME_FORMAT;
import static awais.instagrabber.utils.Constants.DATE_TIME_SELECTION; 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.DOWNLOAD_USER_FOLDER;
import static awais.instagrabber.utils.Constants.FOLDER_PATH; import static awais.instagrabber.utils.Constants.FOLDER_PATH;
import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO; 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(); 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 {} public @interface StringSettings {}
@StringDef({DOWNLOAD_USER_FOLDER, BOTTOM_TOOLBAR, FOLDER_SAVE_TO, AUTOPLAY_VIDEOS, SHOW_QUICK_ACCESS_DIALOG, MUTED_VIDEOS, @StringDef({DOWNLOAD_USER_FOLDER, BOTTOM_TOOLBAR, FOLDER_SAVE_TO, AUTOPLAY_VIDEOS, SHOW_QUICK_ACCESS_DIALOG, MUTED_VIDEOS,

View File

@ -53,7 +53,6 @@ import java.net.HttpCookie;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.security.MessageDigest;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -75,7 +74,7 @@ import awais.instagrabber.models.BasePostModel;
import awais.instagrabber.models.FeedStoryModel; import awais.instagrabber.models.FeedStoryModel;
import awais.instagrabber.models.HighlightModel; import awais.instagrabber.models.HighlightModel;
import awais.instagrabber.models.IntentModel; 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.ProfileModel;
import awais.instagrabber.models.StoryModel; import awais.instagrabber.models.StoryModel;
import awais.instagrabber.models.direct_messages.DirectItemModel; 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.NotificationType;
import awais.instagrabber.models.enums.RavenExpiringMediaType; import awais.instagrabber.models.enums.RavenExpiringMediaType;
import awais.instagrabber.models.enums.RavenMediaViewType; import awais.instagrabber.models.enums.RavenMediaViewType;
import awais.instagrabber.models.stickers.QuestionModel;
import awaisomereport.LogCollector; import awaisomereport.LogCollector;
import static awais.instagrabber.models.direct_messages.DirectItemModel.DirectItemActionLogModel; import static awais.instagrabber.models.direct_messages.DirectItemModel.DirectItemActionLogModel;
@ -260,8 +260,9 @@ public final class Utils {
return stringBuilder; return stringBuilder;
} }
// isI: true if the content was requested from i.instagram.com instead of graphql
@Nullable @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 { try {
final int resourcesLen = resources.length(); final int resourcesLen = resources.length();
@ -270,18 +271,18 @@ public final class Utils {
int lastResBase = 0, lastIndexBase = -1; int lastResBase = 0, lastIndexBase = -1;
for (int i = 0; i < resourcesLen; ++i) { for (int i = 0; i < resourcesLen; ++i) {
final JSONObject item = resources.getJSONObject(i); final JSONObject item = resources.getJSONObject(i);
if (item != null && (!isVideo || item.has(Constants.EXTRAS_PROFILE))) { if (item != null && (!isVideo || item.has(Constants.EXTRAS_PROFILE) || isI)) {
sources[i] = item.getString("src"); sources[i] = item.getString(isI ? "url" : "src");
final int currRes = item.getInt("config_width") * item.getInt("config_height"); 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 (!isVideo || "MAIN".equals(profile)) {
if (currRes > lastResMain) { if (currRes > lastResMain) {
lastResMain = currRes; lastResMain = currRes;
lastIndexMain = i; lastIndexMain = i;
} }
} else if ("BASELINE".equals(profile)) { } else {
if (currRes > lastResBase) { if (currRes > lastResBase) {
lastResBase = currRes; lastResBase = currRes;
lastIndexBase = i; lastIndexBase = i;
@ -305,7 +306,9 @@ public final class Utils {
public static String getHighQualityImage(final JSONObject resources) { public static String getHighQualityImage(final JSONObject resources) {
String src = null; String src = null;
try { 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"); if (src == null) return resources.getString("display_url");
} catch (final Exception e) { } catch (final Exception e) {
if (logCollector != null) if (logCollector != null)
@ -1157,6 +1160,23 @@ public final class Utils {
return "[]"; 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 { public static void putHighlightModels(final HttpURLConnection conn, final Object[] model) throws Exception {
final boolean isHighlightModel = model instanceof HighlightModel[]; final boolean isHighlightModel = model instanceof HighlightModel[];
final boolean isFeedStoryModel = model instanceof FeedStoryModel[]; final boolean isFeedStoryModel = model instanceof FeedStoryModel[];
@ -1192,7 +1212,7 @@ public final class Utils {
data.getLong("taken_at_timestamp"), data.getJSONObject("owner").getString("username")); data.getLong("taken_at_timestamp"), data.getJSONObject("owner").getString("username"));
if (isVideo && data.has("video_resources")) 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")) if (!data.isNull("story_app_attribution"))
storyModels[j].setSpotify(data.getJSONObject("story_app_attribution").optString("content_url").split("\\?")[0]); 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) { public static String sign(final String message) {
try { try {
Mac hasher = Mac.getInstance("HmacSHA256"); Mac hasher = Mac.getInstance("HmacSHA256");
@ -1237,8 +1326,7 @@ public final class Utils {
if (hex.length() == 1) hexString.append('0'); if (hex.length() == 1) hexString.append('0');
hexString.append(hex); hexString.append(hex);
} }
Log.d("austin_debug", "hash: " + hexString.toString()); return "ig_sig_key_version="+Constants.SIGNATURE_VERSION+"&signed_body=" + hexString.toString() + "." + message;
return hexString.toString() + "." + message;
} }
catch (Throwable e) { catch (Throwable e) {
Log.e("austin_debug", "sign: ", e); Log.e("austin_debug", "sign: ", e);

View File

@ -44,7 +44,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="0.3" android:layout_weight="0.3"
android:background="#0000" android:background="#0000"
android:weightSum="3"
android:layout_gravity="bottom"> android:layout_gravity="bottom">
<androidx.appcompat.widget.AppCompatButton <androidx.appcompat.widget.AppCompatButton
android:id="@+id/viewStoryPost" android:id="@+id/viewStoryPost"
@ -56,14 +55,32 @@
android:visibility="gone" android:visibility="gone"
app:backgroundTint="@color/btn_green_background" /> app:backgroundTint="@color/btn_green_background" />
<androidx.appcompat.widget.AppCompatButton <androidx.appcompat.widget.AppCompatButton
android:id="@+id/interactStory" android:id="@+id/poll"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="@string/view_story_post" android:text="@string/vote_story_poll"
android:textColor="@color/btn_blue_text_color" android:textColor="@color/btn_blue_text_color"
android:visibility="gone" android:visibility="gone"
app:backgroundTint="@color/btn_blue_background" /> app:backgroundTint="@color/btn_blue_background" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/answer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/answer_story"
android:textColor="@color/btn_blue_text_color"
android:visibility="gone"
app:backgroundTint="@color/btn_blue_background" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/mention"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/story_mentions"
android:textColor="@color/btn_orange_text_color"
android:visibility="gone"
app:backgroundTint="@color/btn_orange_background" />
<androidx.appcompat.widget.AppCompatButton <androidx.appcompat.widget.AppCompatButton
android:id="@+id/spotify" android:id="@+id/spotify"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -67,6 +67,10 @@
<string name="vote_story_poll">Vote</string> <string name="vote_story_poll">Vote</string>
<string name="votef_story_poll">Vote successful!</string> <string name="votef_story_poll">Vote successful!</string>
<string name="voted_story_poll">You have already voted!</string> <string name="voted_story_poll">You have already voted!</string>
<string name="answer_story">Answer</string>
<string name="answer_hint">Answer...</string>
<string name="answered_story">Answer successful!</string>
<string name="story_mentions">Mentions</string>
<string name="priv_acc">This Account is Private</string> <string name="priv_acc">This Account is Private</string>
<string name="no_acc">You can log in via Settings on the bottom-right corner. Or, you can view public accounts without login!</string> <string name="no_acc">You can log in via Settings on the bottom-right corner. Or, you can view public accounts without login!</string>
<string name="no_acc_logged_in">You can swipe left/right for explore/feed, or search something below!</string> <string name="no_acc_logged_in">You can swipe left/right for explore/feed, or search something below!</string>

View File

@ -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