diff --git a/app/build.gradle b/app/build.gradle index 66aab6cf..8083b4e2 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,8 +10,8 @@ android { minSdkVersion 21 targetSdkVersion 29 - versionCode 57 - versionName '19.0.5' + versionCode 58 + versionName '19.1.0' multiDexEnabled true diff --git a/app/src/main/java/awais/instagrabber/InstaGrabberApplication.java b/app/src/main/java/awais/instagrabber/InstaGrabberApplication.java index c5cd0d69..6d3d4f7d 100644 --- a/app/src/main/java/awais/instagrabber/InstaGrabberApplication.java +++ b/app/src/main/java/awais/instagrabber/InstaGrabberApplication.java @@ -12,11 +12,13 @@ import com.facebook.imagepipeline.core.ImagePipelineConfig; import java.net.CookieHandler; import java.text.SimpleDateFormat; import java.util.UUID; +import java.util.concurrent.ThreadLocalRandom; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.LocaleUtils; import awais.instagrabber.utils.SettingsHelper; import awais.instagrabber.utils.TextUtils; +import awais.instagrabber.utils.UserAgentUtils; import awaisomereport.CrashReporter; import awaisomereport.LogCollector; @@ -85,5 +87,15 @@ public final class InstaGrabberApplication extends Application { if (TextUtils.isEmpty(settingsHelper.getString(Constants.DEVICE_UUID))) { settingsHelper.putString(Constants.DEVICE_UUID, UUID.randomUUID().toString()); } + + if (settingsHelper.getInteger(Constants.BROWSER_UA_CODE) == -1) { + int randomNum = ThreadLocalRandom.current().nextInt(0, UserAgentUtils.browsers.length); + settingsHelper.putInteger(Constants.BROWSER_UA_CODE, randomNum); + } + + if (settingsHelper.getInteger(Constants.APP_UA_CODE) == -1) { + int randomNum = ThreadLocalRandom.current().nextInt(0, UserAgentUtils.devices.length); + settingsHelper.putInteger(Constants.APP_UA_CODE, randomNum); + } } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/animations/FabAnimation.java b/app/src/main/java/awais/instagrabber/animations/FabAnimation.java new file mode 100644 index 00000000..1df54dcc --- /dev/null +++ b/app/src/main/java/awais/instagrabber/animations/FabAnimation.java @@ -0,0 +1,61 @@ +package awais.instagrabber.animations; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.view.View; + +// https://medium.com/better-programming/animated-fab-button-with-more-options-2dcf7118fff6 + +public class FabAnimation { + public static boolean rotateFab(final View v, boolean rotate) { + v.animate().setDuration(200) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + } + }) + .rotation(rotate ? 135f : 0f); + return rotate; + } + + public static void showIn(final View v) { + v.setVisibility(View.VISIBLE); + v.setAlpha(0f); + v.setTranslationY(v.getHeight()); + v.animate() + .setDuration(200) + .translationY(0) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + } + }) + .alpha(1f) + .start(); + } + + public static void showOut(final View v) { + v.setVisibility(View.VISIBLE); + v.setAlpha(1f); + v.setTranslationY(0); + v.animate() + .setDuration(200) + .translationY(v.getHeight()) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + v.setVisibility(View.GONE); + super.onAnimationEnd(animation); + } + }).alpha(0f) + .start(); + } + + public static void init(final View v) { + v.setVisibility(View.GONE); + v.setTranslationY(v.getHeight()); + v.setAlpha(0f); + } +} diff --git a/app/src/main/java/awais/instagrabber/asyncs/CreateThreadAction.java b/app/src/main/java/awais/instagrabber/asyncs/CreateThreadAction.java index 74a24496..a38eee76 100644 --- a/app/src/main/java/awais/instagrabber/asyncs/CreateThreadAction.java +++ b/app/src/main/java/awais/instagrabber/asyncs/CreateThreadAction.java @@ -35,7 +35,7 @@ public class CreateThreadAction extends AsyncTask { try { urlConnection = (HttpURLConnection) new URL(url).openConnection(); urlConnection.setRequestMethod("POST"); - urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT); + urlConnection.setRequestProperty("User-Agent", Utils.settingsHelper.getString(Constants.APP_UA)); urlConnection.setUseCaches(false); final String urlParameters = Utils.sign("{\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0] + "\",\"_uid\":\"" + CookieUtils.getUserIdFromCookie(cookie) diff --git a/app/src/main/java/awais/instagrabber/asyncs/FeedPostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/FeedPostFetchService.java index b4b5d03c..b07b7b45 100644 --- a/app/src/main/java/awais/instagrabber/asyncs/FeedPostFetchService.java +++ b/app/src/main/java/awais/instagrabber/asyncs/FeedPostFetchService.java @@ -29,8 +29,9 @@ public class FeedPostFetchService implements PostFetcher.PostFetchService { final List feedModels = new ArrayList<>(); final String cookie = settingsHelper.getString(Constants.COOKIE); final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie); + final String deviceUuid = settingsHelper.getString(Constants.DEVICE_UUID); feedModels.clear(); - feedService.fetch(csrfToken, nextCursor, new ServiceCallback() { + feedService.fetch(csrfToken, deviceUuid, nextCursor, new ServiceCallback() { @Override public void onSuccess(final PostsFetchResponse result) { if (result == null && feedModels.size() > 0) { diff --git a/app/src/main/java/awais/instagrabber/asyncs/GetActivityAsyncTask.java b/app/src/main/java/awais/instagrabber/asyncs/GetActivityAsyncTask.java index 9d8ca49c..13a88ee4 100644 --- a/app/src/main/java/awais/instagrabber/asyncs/GetActivityAsyncTask.java +++ b/app/src/main/java/awais/instagrabber/asyncs/GetActivityAsyncTask.java @@ -14,6 +14,7 @@ import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.NetworkUtils; import awais.instagrabber.utils.TextUtils; +import awais.instagrabber.utils.Utils; public class GetActivityAsyncTask extends AsyncTask { private static final String TAG = "GetActivityAsyncTask"; @@ -44,7 +45,7 @@ public class GetActivityAsyncTask extends AsyncTask { userJson.optBoolean("blocked_by_viewer"), false, isPrivate, - false, - userJson.optBoolean("restricted_by_viewer"), + userJson.optBoolean("has_requested_viewer"), + userJson.optBoolean("requested_by_viewer"), false, userJson.optBoolean("restricted_by_viewer"), false diff --git a/app/src/main/java/awais/instagrabber/asyncs/UsernameFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/UsernameFetcher.java index 9ae937d4..d0feb459 100755 --- a/app/src/main/java/awais/instagrabber/asyncs/UsernameFetcher.java +++ b/app/src/main/java/awais/instagrabber/asyncs/UsernameFetcher.java @@ -14,6 +14,7 @@ import awais.instagrabber.BuildConfig; import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.NetworkUtils; +import awais.instagrabber.utils.Utils; public final class UsernameFetcher extends AsyncTask { private final FetchListener fetchListener; @@ -31,7 +32,7 @@ public final class UsernameFetcher extends AsyncTask { try { final HttpURLConnection conn = (HttpURLConnection) new URL("https://i.instagram.com/api/v1/users/" + uid + "/info/").openConnection(); - conn.setRequestProperty("User-Agent", Constants.USER_AGENT); + conn.setRequestProperty("User-Agent", Utils.settingsHelper.getString(Constants.BROWSER_UA)); conn.setUseCaches(true); final JSONObject user; diff --git a/app/src/main/java/awais/instagrabber/fragments/CommentsViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/CommentsViewerFragment.java index e438523e..338300dd 100644 --- a/app/src/main/java/awais/instagrabber/fragments/CommentsViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/CommentsViewerFragment.java @@ -64,7 +64,7 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl private LinearLayoutManager layoutManager; private RecyclerLazyLoader lazyLoader; private String shortCode; - private long userId; + private long authorUserId, userIdFromCookie; private String endCursor = null; private Resources resources; private InputMethodManager imm; @@ -140,14 +140,13 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl Toast.makeText(context, R.string.comment_send_empty_comment, Toast.LENGTH_SHORT).show(); return; } - final long userId = CookieUtils.getUserIdFromCookie(cookie); - if (userId == 0) return; + if (userIdFromCookie == 0) return; String replyToId = null; final CommentModel commentModel = commentsAdapter.getSelected(); if (commentModel != null) { replyToId = commentModel.getId(); } - mediaService.comment(postId, text.toString(), userId, replyToId, CookieUtils.getCsrfTokenFromCookie(cookie), new ServiceCallback() { + mediaService.comment(postId, text.toString(), replyToId, new ServiceCallback() { @Override public void onSuccess(final Boolean result) { commentsAdapter.clearSelection(); @@ -171,7 +170,10 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl public void onCreate(@Nullable final Bundle savedInstanceState) { super.onCreate(savedInstanceState); fragmentActivity = (AppCompatActivity) getActivity(); - mediaService = MediaService.getInstance(); + final String deviceUuid = Utils.settingsHelper.getString(Constants.DEVICE_UUID); + final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie); + userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie); + mediaService = MediaService.getInstance(deviceUuid, csrfToken, userIdFromCookie); // setHasOptionsMenu(true); } @@ -231,7 +233,7 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl final CommentsViewerFragmentArgs fragmentArgs = CommentsViewerFragmentArgs.fromBundle(getArguments()); shortCode = fragmentArgs.getShortCode(); postId = fragmentArgs.getPostId(); - userId = fragmentArgs.getPostUserId(); + authorUserId = fragmentArgs.getPostUserId(); // setTitle(); binding.swipeRefreshLayout.setOnRefreshListener(this); binding.swipeRefreshLayout.setRefreshing(true); @@ -289,10 +291,9 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl String[] commentDialogList; - final long userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie); if (!TextUtils.isEmpty(cookie) && userIdFromCookie != 0 - && (userIdFromCookie == commentModel.getProfileModel().getPk() || userIdFromCookie == userId)) { + && (userIdFromCookie == commentModel.getProfileModel().getPk() || userIdFromCookie == authorUserId)) { commentDialogList = new String[]{ resources.getString(R.string.open_profile), resources.getString(R.string.comment_viewer_copy_comment), @@ -324,7 +325,6 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl if (context == null) return; final DialogInterface.OnClickListener profileDialogListener = (dialog, which) -> { final User profileModel = commentModel.getProfileModel(); - final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie); switch (which) { case 0: // open profile openProfile("@" + profileModel.getUsername()); @@ -354,11 +354,8 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl }, 200); break; case 4: // like/unlike comment - if (csrfToken == null) { - return; - } if (!commentModel.getLiked()) { - mediaService.commentLike(commentModel.getId(), csrfToken, new ServiceCallback() { + mediaService.commentLike(commentModel.getId(), new ServiceCallback() { @Override public void onSuccess(final Boolean result) { if (!result) { @@ -376,7 +373,7 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl }); return; } - mediaService.commentUnlike(commentModel.getId(), csrfToken, new ServiceCallback() { + mediaService.commentUnlike(commentModel.getId(), new ServiceCallback() { @Override public void onSuccess(final Boolean result) { if (!result) { @@ -416,10 +413,9 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl }); break; case 6: // delete comment - final long userId = CookieUtils.getUserIdFromCookie(cookie); - if (userId == 0) return; + if (userIdFromCookie == 0) return; mediaService.deleteComment( - postId, userId, commentModel.getId(), csrfToken, + postId, commentModel.getId(), new ServiceCallback() { @Override public void onSuccess(final Boolean result) { diff --git a/app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.java index 631d1ebe..11ee076c 100644 --- a/app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.java @@ -118,7 +118,7 @@ public final class FollowViewerFragment extends Fragment implements SwipeRefresh @Override public void onCreate(@Nullable final Bundle savedInstanceState) { super.onCreate(savedInstanceState); - friendshipService = FriendshipService.getInstance(); + friendshipService = FriendshipService.getInstance(null, null, 0); fragmentActivity = (AppCompatActivity) getActivity(); setHasOptionsMenu(true); } diff --git a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java index 366eaf35..ce3b1cf9 100644 --- a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java @@ -414,10 +414,11 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe hashtagDetailsBinding.btnFollowTag.setOnClickListener(v -> { final String cookie = settingsHelper.getString(Constants.COOKIE); final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie); + final String ua = settingsHelper.getString(Constants.BROWSER_UA); if (csrfToken != null) { hashtagDetailsBinding.btnFollowTag.setClickable(false); if (!hashtagModel.getFollowing()) { - tagsService.follow(hashtag.substring(1), csrfToken, new ServiceCallback() { + tagsService.follow(ua, hashtag.substring(1), csrfToken, new ServiceCallback() { @Override public void onSuccess(final Boolean result) { hashtagDetailsBinding.btnFollowTag.setClickable(true); @@ -445,7 +446,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe }); return; } - tagsService.unfollow(hashtag.substring(1), csrfToken, new ServiceCallback() { + tagsService.unfollow(ua, hashtag.substring(1), csrfToken, new ServiceCallback() { @Override public void onSuccess(final Boolean result) { hashtagDetailsBinding.btnFollowTag.setClickable(true); diff --git a/app/src/main/java/awais/instagrabber/fragments/LikesViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/LikesViewerFragment.java index 3bf8eccf..f2c12d91 100644 --- a/app/src/main/java/awais/instagrabber/fragments/LikesViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/LikesViewerFragment.java @@ -104,7 +104,7 @@ public final class LikesViewerFragment extends BottomSheetDialogFragment impleme final String cookie = settingsHelper.getString(Constants.COOKIE); isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0; // final AppCompatActivity fragmentActivity = (AppCompatActivity) getActivity(); - mediaService = isLoggedIn ? MediaService.getInstance() : null; + mediaService = isLoggedIn ? MediaService.getInstance(null, null, 0) : null; graphQLService = isLoggedIn ? null : GraphQLService.getInstance(); // setHasOptionsMenu(true); } diff --git a/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java index d0b745b6..1b372ea1 100644 --- a/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java @@ -60,7 +60,6 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe private NotificationViewModel notificationViewModel; private FriendshipService friendshipService; private MediaService mediaService; - private long userId; private String csrfToken; private String type; private Context context; @@ -133,7 +132,7 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe break; case 1: if (model.getType() == NotificationType.REQUEST) { - friendshipService.approve(userId, model.getUserId(), csrfToken, new ServiceCallback() { + friendshipService.approve(model.getUserId(), new ServiceCallback() { @Override public void onSuccess(final FriendshipChangeResponse result) { onRefresh(); @@ -175,7 +174,7 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe }); break; case 2: - friendshipService.ignore(userId, model.getUserId(), csrfToken, new ServiceCallback() { + friendshipService.ignore(model.getUserId(), new ServiceCallback() { @Override public void onSuccess(final FriendshipChangeResponse result) { onRefresh(); @@ -218,10 +217,11 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe if (TextUtils.isEmpty(cookie)) { Toast.makeText(context, R.string.activity_notloggedin, Toast.LENGTH_SHORT).show(); } - friendshipService = FriendshipService.getInstance(); - mediaService = MediaService.getInstance(); - userId = CookieUtils.getUserIdFromCookie(cookie); + mediaService = MediaService.getInstance(null, null, 0); + final long userId = CookieUtils.getUserIdFromCookie(cookie); + final String deviceUuid = Utils.settingsHelper.getString(Constants.DEVICE_UUID); csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie); + friendshipService = FriendshipService.getInstance(deviceUuid, csrfToken, userId); } @NonNull diff --git a/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java b/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java index 60a3b8f6..40f96c93 100644 --- a/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java @@ -465,16 +465,16 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment im binding.date.setVisibility(View.VISIBLE); binding.date.setText(date); })); - if (viewModel.getMedia().isCommentLikesEnabled()) { - viewModel.getLikeCount().observe(getViewLifecycleOwner(), count -> { - final long safeCount = getSafeCount(count); - final String likesString = getResources().getQuantityString(R.plurals.likes_count, (int) safeCount, safeCount); - binding.likesCount.setText(likesString); - }); + viewModel.getLikeCount().observe(getViewLifecycleOwner(), count -> { + final long safeCount = getSafeCount(count); + final String likesString = getResources().getQuantityString(R.plurals.likes_count, (int) safeCount, safeCount); + binding.likesCount.setText(likesString); + }); + if (!viewModel.getMedia().isCommentsDisabled()) { viewModel.getCommentCount().observe(getViewLifecycleOwner(), count -> { final long safeCount = getSafeCount(count); final String likesString = getResources().getQuantityString(R.plurals.comments_count, (int) safeCount, safeCount); - binding.likesCount.setText(likesString); + binding.commentsCount.setText(likesString); }); } viewModel.getViewCount().observe(getViewLifecycleOwner(), count -> { @@ -538,7 +538,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment im } private void setupComment() { - if (!viewModel.hasPk() || !viewModel.getMedia().isCommentLikesEnabled()) { + if (!viewModel.hasPk() || viewModel.getMedia().isCommentsDisabled()) { binding.comment.setVisibility(View.GONE); binding.commentsCount.setVisibility(View.GONE); return; @@ -577,7 +577,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment im } private void setupLike() { - final boolean likableMedia = viewModel.hasPk() && viewModel.getMedia().isCommentLikesEnabled(); + final boolean likableMedia = viewModel.hasPk() /*&& viewModel.getMedia().isCommentLikesEnabled()*/; if (!likableMedia) { binding.like.setVisibility(View.GONE); binding.likesCount.setVisibility(View.GONE); @@ -1402,20 +1402,24 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment im if (media.getLocation() != null) { binding.location.setVisibility(View.VISIBLE); } - binding.captionParent.setVisibility(View.VISIBLE); binding.bottomBg.setVisibility(View.VISIBLE); - binding.likesCount.setVisibility(View.VISIBLE); - binding.commentsCount.setVisibility(View.VISIBLE); - binding.date.setVisibility(View.VISIBLE); - binding.captionToggle.setVisibility(View.VISIBLE); + if (viewModel.hasPk()) { + binding.likesCount.setVisibility(View.VISIBLE); + binding.date.setVisibility(View.VISIBLE); + binding.captionParent.setVisibility(View.VISIBLE); + binding.captionToggle.setVisibility(View.VISIBLE); + binding.share.setVisibility(View.VISIBLE); + } + if (viewModel.hasPk() && !viewModel.getMedia().isCommentsDisabled()) { + binding.comment.setVisibility(View.VISIBLE); + binding.commentsCount.setVisibility(View.VISIBLE); + } binding.download.setVisibility(View.VISIBLE); - binding.share.setVisibility(View.VISIBLE); - binding.comment.setVisibility(View.VISIBLE); final List options = viewModel.getOptions().getValue(); if (options != null && !options.isEmpty()) { binding.options.setVisibility(View.VISIBLE); } - if (viewModel.isLoggedIn()) { + if (viewModel.isLoggedIn() && viewModel.hasPk()) { binding.like.setVisibility(View.VISIBLE); binding.save.setVisibility(View.VISIBLE); } diff --git a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java index 2b062b8d..57296c2b 100644 --- a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java @@ -172,6 +172,21 @@ public class DirectMessageThreadFragment extends Fragment { @Override public void onMediaClick(final Media media) { + if (media.isReelMedia()) { + final String pk = media.getPk(); + try { + final long mediaId = Long.parseLong(pk); + final User user = media.getUser(); + if (user == null) return; + final String username = user.getUsername(); + final NavDirections action = DirectMessageThreadFragmentDirections + .actionThreadToStory(StoryViewerOptions.forStory(mediaId, username)); + NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(action); + } catch (NumberFormatException e) { + Log.e(TAG, "onMediaClick (story): ", e); + } + return; + } final PostViewV2Fragment.Builder builder = PostViewV2Fragment.builder(media); builder.build().show(getChildFragmentManager(), "post_view"); } diff --git a/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java b/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java index b9c6d465..54f6ea25 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java @@ -68,6 +68,7 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre private FragmentFeedBinding binding; private StoriesService storiesService; private boolean shouldRefresh = true; + private final boolean isRotate = false; private FeedStoriesViewModel feedStoriesViewModel; private boolean storiesFetching; private ActionMode actionMode; @@ -282,6 +283,24 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) { if (!shouldRefresh) return; binding.feedSwipeRefreshLayout.setOnRefreshListener(this); + /* + FabAnimation.init(binding.fabCamera); + FabAnimation.init(binding.fabStory); + binding.fabAdd.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + isRotate = FabAnimation.rotateFab(v, !isRotate); + if (isRotate) { + FabAnimation.showIn(binding.fabCamera); + FabAnimation.showIn(binding.fabStory); + } + else { + FabAnimation.showOut(binding.fabCamera); + FabAnimation.showOut(binding.fabStory); + } + } + }); + */ setupFeedStories(); setupFeed(); shouldRefresh = false; diff --git a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java index e72d3bae..1b2a9a5e 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java @@ -99,7 +99,6 @@ import awais.instagrabber.webservices.StoriesService; import static androidx.core.content.PermissionChecker.checkSelfPermission; import static awais.instagrabber.fragments.HashTagFragment.ARG_HASHTAG; import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION; -import static awais.instagrabber.utils.Utils.settingsHelper; public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener { private static final String TAG = "ProfileFragment"; @@ -300,10 +299,15 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe @Override public void onCreate(@Nullable final Bundle savedInstanceState) { super.onCreate(savedInstanceState); + cookie = Utils.settingsHelper.getString(Constants.COOKIE); + isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0; + final long userId = CookieUtils.getUserIdFromCookie(cookie); + final String deviceUuid = Utils.settingsHelper.getString(Constants.DEVICE_UUID); + final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie); fragmentActivity = (MainActivity) requireActivity(); - friendshipService = FriendshipService.getInstance(); + friendshipService = FriendshipService.getInstance(deviceUuid, csrfToken, userId); storiesService = StoriesService.getInstance(); - mediaService = MediaService.getInstance(); + mediaService = MediaService.getInstance(null, null, 0); accountRepository = AccountRepository.getInstance(AccountDataSource.getInstance(getContext())); favoriteRepository = FavoriteRepository.getInstance(FavoriteDataSource.getInstance(getContext())); setHasOptionsMenu(true); @@ -313,8 +317,6 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { - cookie = settingsHelper.getString(Constants.COOKIE); - isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0; if (root != null) { if (getArguments() != null) { final ProfileFragmentArgs fragmentArgs = ProfileFragmentArgs.fromBundle(getArguments()); @@ -380,7 +382,6 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe friendshipService.toggleRestrict( profileModel.getPk(), !profileModel.getFriendshipStatus().isRestricted(), - CookieUtils.getCsrfTokenFromCookie(cookie), new ServiceCallback() { @Override public void onSuccess(final FriendshipRestrictResponse result) { @@ -396,13 +397,10 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe return true; } if (item.getItemId() == R.id.block) { - final long userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie); if (!isLoggedIn) return false; if (profileModel.getFriendshipStatus().isBlocking()) { friendshipService.unblock( - userIdFromCookie, profileModel.getPk(), - CookieUtils.getCsrfTokenFromCookie(cookie), new ServiceCallback() { @Override public void onSuccess(final FriendshipChangeResponse result) { @@ -418,9 +416,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe return true; } friendshipService.block( - userIdFromCookie, profileModel.getPk(), - CookieUtils.getCsrfTokenFromCookie(cookie), new ServiceCallback() { @Override public void onSuccess(final FriendshipChangeResponse result) { @@ -628,9 +624,9 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe ), new RepositoryCallback() { @Override public void onSuccess(final Void result) { - profileDetailsBinding.favChip.setText(R.string.added_to_favs); + profileDetailsBinding.favChip.setText(R.string.added_to_favs_short); profileDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24); - showSnackbar(getString(R.string.added_to_favs)); + showSnackbar(getString(R.string.added_to_favs_short)); } @Override @@ -894,7 +890,6 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe private void setupCommonListeners() { final Context context = getContext(); - final long userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie); profileDetailsBinding.btnFollow.setOnClickListener(v -> { if (profileModel.getFriendshipStatus().isFollowing() && profileModel.isPrivate()) { new AlertDialog.Builder(context) @@ -902,9 +897,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe .setMessage(R.string.priv_acc_confirm) .setPositiveButton(R.string.confirm, (d, w) -> friendshipService.unfollow( - userIdFromCookie, profileModel.getPk(), - CookieUtils.getCsrfTokenFromCookie(cookie), new ServiceCallback() { @Override public void onSuccess(final FriendshipChangeResponse result) { @@ -919,11 +912,10 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe })) .setNegativeButton(R.string.cancel, null) .show(); - } else if (profileModel.getFriendshipStatus().isFollowing() || profileModel.getFriendshipStatus().isOutgoingRequest()) { + } + else if (profileModel.getFriendshipStatus().isFollowing() || profileModel.getFriendshipStatus().isOutgoingRequest()) { friendshipService.unfollow( - userIdFromCookie, profileModel.getPk(), - CookieUtils.getCsrfTokenFromCookie(cookie), new ServiceCallback() { @Override public void onSuccess(final FriendshipChangeResponse result) { @@ -938,9 +930,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe }); } else { friendshipService.follow( - userIdFromCookie, profileModel.getPk(), - CookieUtils.getCsrfTokenFromCookie(cookie), new ServiceCallback() { @Override public void onSuccess(final FriendshipChangeResponse result) { @@ -1102,6 +1092,6 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe private boolean isReallyPrivate() { final long myId = CookieUtils.getUserIdFromCookie(cookie); final FriendshipStatus friendshipStatus = profileModel.getFriendshipStatus(); - return !friendshipStatus.isFollowedBy() && (profileModel.getPk() != myId) && profileModel.isPrivate(); + return !friendshipStatus.isFollowing() && (profileModel.getPk() != myId) && profileModel.isPrivate(); } } diff --git a/app/src/main/java/awais/instagrabber/repositories/FriendshipRepository.java b/app/src/main/java/awais/instagrabber/repositories/FriendshipRepository.java index 919efaaa..123a7a4d 100644 --- a/app/src/main/java/awais/instagrabber/repositories/FriendshipRepository.java +++ b/app/src/main/java/awais/instagrabber/repositories/FriendshipRepository.java @@ -8,7 +8,6 @@ import retrofit2.Call; import retrofit2.http.FieldMap; import retrofit2.http.FormUrlEncoded; import retrofit2.http.GET; -import retrofit2.http.Header; import retrofit2.http.POST; import retrofit2.http.Path; import retrofit2.http.QueryMap; @@ -17,20 +16,17 @@ public interface FriendshipRepository { @FormUrlEncoded @POST("/api/v1/friendships/{action}/{id}/") - Call change(@Header("User-Agent") String userAgent, - @Path("action") String action, + Call change(@Path("action") String action, @Path("id") long id, @FieldMap Map form); @FormUrlEncoded @POST("/api/v1/restrict_action/{action}/") - Call toggleRestrict(@Header("User-Agent") String userAgent, - @Path("action") String action, + Call toggleRestrict(@Path("action") String action, @FieldMap Map form); @GET("/api/v1/friendships/{userId}/{type}/") - Call getList(@Header("User-Agent") String userAgent, - @Path("userId") long userId, + Call getList(@Path("userId") long userId, @Path("type") String type, // following or followers @QueryMap(encoded = true) Map queryParams); } diff --git a/app/src/main/java/awais/instagrabber/repositories/NewsRepository.java b/app/src/main/java/awais/instagrabber/repositories/NewsRepository.java index 20b50934..edc1e149 100644 --- a/app/src/main/java/awais/instagrabber/repositories/NewsRepository.java +++ b/app/src/main/java/awais/instagrabber/repositories/NewsRepository.java @@ -2,28 +2,23 @@ package awais.instagrabber.repositories; import java.util.Map; -import awais.instagrabber.utils.Constants; import retrofit2.Call; import retrofit2.http.FieldMap; import retrofit2.http.FormUrlEncoded; import retrofit2.http.GET; import retrofit2.http.Header; -import retrofit2.http.Headers; import retrofit2.http.POST; import retrofit2.http.Query; public interface NewsRepository { - @Headers("User-Agent: " + Constants.USER_AGENT) @GET("https://www.instagram.com/accounts/activity/?__a=1") - Call webInbox(); + Call webInbox(@Header("User-Agent") String userAgent); - @Headers("User-Agent: " + Constants.I_USER_AGENT) @GET("/api/v1/news/inbox/") - Call appInbox(@Query(value = "mark_as_seen", encoded = true) boolean markAsSeen); + Call appInbox(@Header("User-Agent") String userAgent, @Query(value = "mark_as_seen", encoded = true) boolean markAsSeen); @FormUrlEncoded - @Headers("User-Agent: " + Constants.I_USER_AGENT) @POST("/api/v1/discover/ayml/") - Call getAyml(@FieldMap final Map form); + Call getAyml(@Header("User-Agent") String userAgent, @FieldMap final Map form); } diff --git a/app/src/main/java/awais/instagrabber/repositories/StoriesRepository.java b/app/src/main/java/awais/instagrabber/repositories/StoriesRepository.java index 17af1b86..6eaa83ba 100644 --- a/app/src/main/java/awais/instagrabber/repositories/StoriesRepository.java +++ b/app/src/main/java/awais/instagrabber/repositories/StoriesRepository.java @@ -7,7 +7,6 @@ import retrofit2.Call; import retrofit2.http.FieldMap; import retrofit2.http.FormUrlEncoded; import retrofit2.http.GET; -import retrofit2.http.Header; import retrofit2.http.POST; import retrofit2.http.Path; import retrofit2.http.QueryMap; @@ -28,12 +27,11 @@ public interface StoriesRepository { Call fetchArchive(@QueryMap Map queryParams); @GET - Call getUserStory(@Header("User-Agent") String userAgent, @Url String url); + Call getUserStory(@Url String url); @FormUrlEncoded @POST("/api/v1/media/{storyId}/{stickerId}/{action}/") - Call respondToSticker(@Header("User-Agent") String userAgent, - @Path("storyId") String storyId, + Call respondToSticker(@Path("storyId") String storyId, @Path("stickerId") String stickerId, @Path("action") String action, // story_poll_vote, story_question_response, story_slider_vote, story_quiz_answer diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/Media.java b/app/src/main/java/awais/instagrabber/repositories/responses/Media.java index 6667fd1e..f864a434 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/Media.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/Media.java @@ -19,6 +19,7 @@ public class Media implements Serializable { private final MediaItemType mediaType; private final boolean canViewerReshare; private final boolean commentLikesEnabled; + private final boolean commentsDisabled; private final long nextMaxId; private final long commentCount; private final ImageVersions2 imageVersions2; @@ -56,6 +57,7 @@ public class Media implements Serializable { final int originalHeight, final MediaItemType mediaType, final boolean commentLikesEnabled, + final boolean commentsDisabled, final long nextMaxId, final long commentCount, final long likeCount, @@ -87,6 +89,7 @@ public class Media implements Serializable { this.originalHeight = originalHeight; this.mediaType = mediaType; this.commentLikesEnabled = commentLikesEnabled; + this.commentsDisabled = commentsDisabled; this.nextMaxId = nextMaxId; this.commentCount = commentCount; this.likeCount = likeCount; @@ -182,6 +185,10 @@ public class Media implements Serializable { return commentLikesEnabled; } + public boolean isCommentsDisabled() { + return commentsDisabled; + } + public long getNextMaxId() { return nextMaxId; } diff --git a/app/src/main/java/awais/instagrabber/utils/Constants.java b/app/src/main/java/awais/instagrabber/utils/Constants.java index 7dcdf37b..5d59a0cd 100644 --- a/app/src/main/java/awais/instagrabber/utils/Constants.java +++ b/app/src/main/java/awais/instagrabber/utils/Constants.java @@ -9,8 +9,10 @@ public final class Constants { public static final String APP_THEME = "app_theme_v19"; public static final String APP_LANGUAGE = "app_language_v19"; public static final String STORY_SORT = "story_sort"; - // int prefs + // int prefs, do not export public static final String PREV_INSTALL_VERSION = "prevVersion"; + public static final String BROWSER_UA_CODE = "browser_ua_code"; + public static final String APP_UA_CODE = "app_ua_code"; // boolean prefs public static final String DOWNLOAD_USER_FOLDER = "download_user_folder"; // deprecated: public static final String BOTTOM_TOOLBAR = "bottom_toolbar"; @@ -31,6 +33,8 @@ public final class Constants { 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"; + public static final String BROWSER_UA = "browser_ua"; + public static final String APP_UA = "app_ua"; //////////////////////// EXTRAS //////////////////////// public static final String EXTRAS_USER = "user"; public static final String EXTRAS_HASHTAG = "hashtag"; @@ -54,13 +58,6 @@ public final class Constants { // Notification ids public static final int ACTIVITY_NOTIFICATION_ID = 10; - // spoof - 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 169.1.0.29.135 Android (27/8.1.0; 320dpi; 720x1362; motorola; motorola one; deen_sprout; qcom; pt_BR; 262886998)"; - public static final String I_USER_AGENT = - "Instagram 169.1.0.29.135 Android (27/8.1.0; 320dpi; 720x1362; motorola; motorola one; deen_sprout; qcom; pt_BR; 262886998)"; - public static final String A_USER_AGENT = "https://Barinsta.AustinHuang.me / mailto:Barinsta@AustinHuang.me"; // 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/DirectItemFactory.java b/app/src/main/java/awais/instagrabber/utils/DirectItemFactory.java index 44f28942..31efa897 100644 --- a/app/src/main/java/awais/instagrabber/utils/DirectItemFactory.java +++ b/app/src/main/java/awais/instagrabber/utils/DirectItemFactory.java @@ -81,6 +81,7 @@ public class DirectItemFactory { height, isVideo ? MediaItemType.MEDIA_TYPE_VIDEO : MediaItemType.MEDIA_TYPE_IMAGE, false, + false, -1, -1, -1, @@ -156,6 +157,7 @@ public class DirectItemFactory { 0, MediaItemType.MEDIA_TYPE_VOICE, false, + false, -1, 0, 0, diff --git a/app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java b/app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java index 03003ce0..e94f71c7 100755 --- a/app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java +++ b/app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java @@ -343,6 +343,10 @@ public final class ExportImportUtils { jsonObject.remove(Constants.COOKIE); jsonObject.remove(Constants.DEVICE_UUID); jsonObject.remove(Constants.PREV_INSTALL_VERSION); + jsonObject.remove(Constants.BROWSER_UA_CODE); + jsonObject.remove(Constants.BROWSER_UA); + jsonObject.remove(Constants.APP_UA_CODE); + jsonObject.remove(Constants.APP_UA); return jsonObject; } catch (Exception e) { Log.e(TAG, "Error exporting settings", e); diff --git a/app/src/main/java/awais/instagrabber/utils/FlavorTown.java b/app/src/main/java/awais/instagrabber/utils/FlavorTown.java index 1559afcd..b63620ae 100755 --- a/app/src/main/java/awais/instagrabber/utils/FlavorTown.java +++ b/app/src/main/java/awais/instagrabber/utils/FlavorTown.java @@ -102,6 +102,14 @@ public final class FlavorTown { public static void changelogCheck(@NonNull final Context context) { if (settingsHelper.getInteger(Constants.PREV_INSTALL_VERSION) < BuildConfig.VERSION_CODE) { + final String langCode = settingsHelper.getString(Constants.APP_LANGUAGE); + final String lang = LocaleUtils.getCorrespondingLanguageCode(langCode); + final int appUaCode = settingsHelper.getInteger(Constants.APP_UA_CODE); + final String appUa = UserAgentUtils.generateAppUA(appUaCode, lang); + settingsHelper.putString(Constants.APP_UA, appUa); + final int browserUaCode = settingsHelper.getInteger(Constants.BROWSER_UA_CODE); + final String browserUa = UserAgentUtils.generateBrowserUA(browserUaCode); + settingsHelper.putString(Constants.BROWSER_UA, browserUa); Toast.makeText(context, R.string.updated, Toast.LENGTH_SHORT).show(); settingsHelper.putInteger(Constants.PREV_INSTALL_VERSION, BuildConfig.VERSION_CODE); } diff --git a/app/src/main/java/awais/instagrabber/utils/LocaleUtils.java b/app/src/main/java/awais/instagrabber/utils/LocaleUtils.java index 0ea69a55..c48b3341 100755 --- a/app/src/main/java/awais/instagrabber/utils/LocaleUtils.java +++ b/app/src/main/java/awais/instagrabber/utils/LocaleUtils.java @@ -19,7 +19,11 @@ public final class LocaleUtils { if (baseContext instanceof ContextThemeWrapper) baseContext = ((ContextThemeWrapper) baseContext).getBaseContext(); - final String lang = LocaleUtils.getCorrespondingLanguageCode(baseContext); + if (Utils.settingsHelper == null) + Utils.settingsHelper = new SettingsHelper(baseContext); + + final String appLanguageSettings = Utils.settingsHelper.getString(Constants.APP_LANGUAGE); + final String lang = TextUtils.isEmpty(appLanguageSettings) ? null : LocaleUtils.getCorrespondingLanguageCode(appLanguageSettings); currentLocale = TextUtils.isEmpty(lang) ? defaultLocale : (lang.contains("_") ? new Locale(lang.split("_")[0], lang.split("_")[1]) : new Locale(lang)); @@ -49,13 +53,7 @@ public final class LocaleUtils { } @Nullable - private static String getCorrespondingLanguageCode(final Context baseContext) { - if (Utils.settingsHelper == null) - Utils.settingsHelper = new SettingsHelper(baseContext); - - final String appLanguageSettings = Utils.settingsHelper.getString(Constants.APP_LANGUAGE); - if (TextUtils.isEmpty(appLanguageSettings)) return null; - + public static String getCorrespondingLanguageCode(final String appLanguageSettings) { final int appLanguageIndex = Integer.parseInt(appLanguageSettings); if (appLanguageIndex == 1) return "en"; if (appLanguageIndex == 2) return "fr"; diff --git a/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java b/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java index 0fd2753f..853f262c 100644 --- a/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java +++ b/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java @@ -857,6 +857,7 @@ public final class ResponseBodyUtils { height, mediaItemType, false, + feedItem.optBoolean("comments_disabled"), -1, commentsCount, likesCount, diff --git a/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java b/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java index 204f81cd..c6c01d29 100755 --- a/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java +++ b/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java @@ -10,7 +10,11 @@ import androidx.appcompat.app.AppCompatDelegate; import static awais.instagrabber.utils.Constants.APP_LANGUAGE; import static awais.instagrabber.utils.Constants.APP_THEME; +import static awais.instagrabber.utils.Constants.APP_UA; +import static awais.instagrabber.utils.Constants.APP_UA_CODE; import static awais.instagrabber.utils.Constants.AUTOPLAY_VIDEOS; +import static awais.instagrabber.utils.Constants.BROWSER_UA; +import static awais.instagrabber.utils.Constants.BROWSER_UA_CODE; import static awais.instagrabber.utils.Constants.CHECK_ACTIVITY; import static awais.instagrabber.utils.Constants.CHECK_UPDATES; import static awais.instagrabber.utils.Constants.COOKIE; @@ -80,7 +84,7 @@ public final class SettingsHelper { private int getIntegerDefault(@IntegerSettings final String key) { if (APP_THEME.equals(key)) return getThemeCode(true); - if (PREV_INSTALL_VERSION.equals(key)) return -1; + if (PREV_INSTALL_VERSION.equals(key) || APP_UA_CODE.equals(key) || BROWSER_UA_CODE.equals(key)) return -1; return 0; } @@ -121,10 +125,11 @@ public final class SettingsHelper { } @StringDef( - {APP_LANGUAGE, APP_THEME, COOKIE, FOLDER_PATH, DATE_TIME_FORMAT, DATE_TIME_SELECTION, CUSTOM_DATE_TIME_FORMAT, - DEVICE_UUID, SKIPPED_VERSION, DEFAULT_TAB, PREF_DARK_THEME, PREF_LIGHT_THEME, PREF_POSTS_LAYOUT, - PREF_PROFILE_POSTS_LAYOUT, PREF_TOPIC_POSTS_LAYOUT, PREF_HASHTAG_POSTS_LAYOUT, PREF_LOCATION_POSTS_LAYOUT, - PREF_LIKED_POSTS_LAYOUT, PREF_TAGGED_POSTS_LAYOUT, PREF_SAVED_POSTS_LAYOUT, STORY_SORT, PREF_EMOJI_VARIANTS, PREF_REACTIONS}) + {APP_LANGUAGE, APP_THEME, APP_UA, BROWSER_UA, COOKIE, FOLDER_PATH, DATE_TIME_FORMAT, DATE_TIME_SELECTION, + CUSTOM_DATE_TIME_FORMAT, DEVICE_UUID, SKIPPED_VERSION, DEFAULT_TAB, PREF_DARK_THEME, PREF_LIGHT_THEME, + PREF_POSTS_LAYOUT, PREF_PROFILE_POSTS_LAYOUT, PREF_TOPIC_POSTS_LAYOUT, PREF_HASHTAG_POSTS_LAYOUT, + PREF_LOCATION_POSTS_LAYOUT, PREF_LIKED_POSTS_LAYOUT, PREF_TAGGED_POSTS_LAYOUT, PREF_SAVED_POSTS_LAYOUT, + STORY_SORT, PREF_EMOJI_VARIANTS, PREF_REACTIONS}) public @interface StringSettings {} @StringDef({DOWNLOAD_USER_FOLDER, FOLDER_SAVE_TO, AUTOPLAY_VIDEOS, SHOW_QUICK_ACCESS_DIALOG, MUTED_VIDEOS, @@ -132,6 +137,6 @@ public final class SettingsHelper { CHECK_UPDATES, SWAP_DATE_TIME_FORMAT_ENABLED}) public @interface BooleanSettings {} - @StringDef({PREV_INSTALL_VERSION}) + @StringDef({PREV_INSTALL_VERSION, BROWSER_UA_CODE, APP_UA_CODE}) public @interface IntegerSettings {} } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/utils/UpdateChecker.java b/app/src/main/java/awais/instagrabber/utils/UpdateChecker.java index ea39e370..3ea67ba1 100755 --- a/app/src/main/java/awais/instagrabber/utils/UpdateChecker.java +++ b/app/src/main/java/awais/instagrabber/utils/UpdateChecker.java @@ -30,7 +30,7 @@ public final class UpdateChecker extends AsyncTask { HttpURLConnection conn = (HttpURLConnection) new URL("https://f-droid.org/api/v1/packages/me.austinhuang.instagrabber").openConnection(); conn.setUseCaches(false); - conn.setRequestProperty("User-Agent", Constants.A_USER_AGENT); + conn.setRequestProperty("User-Agent", "https://Barinsta.AustinHuang.me / mailto:Barinsta@AustinHuang.me"); conn.connect(); final int responseCode = conn.getResponseCode(); diff --git a/app/src/main/java/awais/instagrabber/utils/UserAgentUtils.java b/app/src/main/java/awais/instagrabber/utils/UserAgentUtils.java new file mode 100644 index 00000000..36944cce --- /dev/null +++ b/app/src/main/java/awais/instagrabber/utils/UserAgentUtils.java @@ -0,0 +1,77 @@ +package awais.instagrabber.utils; + +import androidx.annotation.NonNull; + +public class UserAgentUtils { + + /* GraphQL user agents (which are just standard browser UA's). + * Go to https://www.whatismybrowser.com/guides/the-latest-user-agent/ to update it + * Windows first (Assume win64 not wow64): Chrome, Firefox, Edge + * Then macOS: Chrome, Firefox, Safari + */ + public static final String[] browsers = { + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36 Edg/87.0.664.75", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 11.1; rv:84.0) Gecko/20100101 Firefox/84.0", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.2 Safari/605.1.15" + }; + // use APKpure, assume x86 + private static final String igVersion = "169.3.0.30.135"; + private static final String igVersionCode = "264009054"; + // https://github.com/dilame/instagram-private-api/blob/master/src/samples/devices.json + // presumed constant, so no need to update + public static final String[] devices = { + "25/7.1.1; 440dpi; 1080x1920; Xiaomi; Mi Note 3; jason; qcom", + "23/6.0.1; 480dpi; 1080x1920; Xiaomi; Redmi Note 3; kenzo; qcom", + "23/6.0; 480dpi; 1080x1920; Xiaomi; Redmi Note 4; nikel; mt6797", + "24/7.0; 480dpi; 1080x1920; Xiaomi/xiaomi; Redmi Note 4; mido; qcom", + "23/6.0; 480dpi; 1080x1920; Xiaomi; Redmi Note 4X; nikel; mt6797", + "27/8.1.0; 440dpi; 1080x2030; Xiaomi/xiaomi; Redmi Note 5; whyred; qcom", + "23/6.0.1; 480dpi; 1080x1920; Xiaomi; Redmi 4; markw; qcom", + "27/8.1.0; 440dpi; 1080x2030; Xiaomi/xiaomi; Redmi 5 Plus; vince; qcom", + "25/7.1.2; 440dpi; 1080x2030; Xiaomi/xiaomi; Redmi 5 Plus; vince; qcom", + "26/8.0.0; 480dpi; 1080x1920; Xiaomi; MI 5; gemini; qcom", + "27/8.1.0; 480dpi; 1080x1920; Xiaomi/xiaomi; Mi A1; tissot_sprout; qcom", + "26/8.0.0; 480dpi; 1080x1920; Xiaomi; MI 6; sagit; qcom", + "25/7.1.1; 440dpi; 1080x1920; Xiaomi; MI MAX 2; oxygen; qcom", + "24/7.0; 480dpi; 1080x1920; Xiaomi; MI 5s; capricorn; qcom", + "26/8.0.0; 480dpi; 1080x1920; samsung; SM-A520F; a5y17lte; samsungexynos7880", + "26/8.0.0; 480dpi; 1080x2076; samsung; SM-G950F; dreamlte; samsungexynos8895", + "26/8.0.0; 640dpi; 1440x2768; samsung; SM-G950F; dreamlte; samsungexynos8895", + "26/8.0.0; 420dpi; 1080x2094; samsung; SM-G955F; dream2lte; samsungexynos8895", + "26/8.0.0; 560dpi; 1440x2792; samsung; SM-G955F; dream2lte; samsungexynos8895", + "24/7.0; 480dpi; 1080x1920; samsung; SM-A510F; a5xelte; samsungexynos7580", + "26/8.0.0; 480dpi; 1080x1920; samsung; SM-G930F; herolte; samsungexynos8890", + "26/8.0.0; 480dpi; 1080x1920; samsung; SM-G935F; hero2lte; samsungexynos8890", + "26/8.0.0; 420dpi; 1080x2094; samsung; SM-G965F; star2lte; samsungexynos9810", + "26/8.0.0; 480dpi; 1080x2076; samsung; SM-A530F; jackpotlte; samsungexynos7885", + "24/7.0; 640dpi; 1440x2560; samsung; SM-G925F; zerolte; samsungexynos7420", + "26/8.0.0; 420dpi; 1080x1920; samsung; SM-A720F; a7y17lte; samsungexynos7880", + "24/7.0; 640dpi; 1440x2560; samsung; SM-G920F; zeroflte; samsungexynos7420", + "24/7.0; 420dpi; 1080x1920; samsung; SM-J730FM; j7y17lte; samsungexynos7870", + "26/8.0.0; 480dpi; 1080x2076; samsung; SM-G960F; starlte; samsungexynos9810", + "26/8.0.0; 420dpi; 1080x2094; samsung; SM-N950F; greatlte; samsungexynos8895", + "26/8.0.0; 420dpi; 1080x2094; samsung; SM-A730F; jackpot2lte; samsungexynos7885", + "26/8.0.0; 420dpi; 1080x2094; samsung; SM-A605FN; a6plte; qcom", + "26/8.0.0; 480dpi; 1080x1920; HUAWEI/HONOR; STF-L09; HWSTF; hi3660", + "27/8.1.0; 480dpi; 1080x2280; HUAWEI/HONOR; COL-L29; HWCOL; kirin970", + "26/8.0.0; 480dpi; 1080x2032; HUAWEI/HONOR; LLD-L31; HWLLD-H; hi6250", + "26/8.0.0; 480dpi; 1080x2150; HUAWEI; ANE-LX1; HWANE; hi6250", + "26/8.0.0; 480dpi; 1080x2032; HUAWEI; FIG-LX1; HWFIG-H; hi6250", + "27/8.1.0; 480dpi; 1080x2150; HUAWEI/HONOR; COL-L29; HWCOL; kirin970", + "26/8.0.0; 480dpi; 1080x2038; HUAWEI/HONOR; BND-L21; HWBND-H; hi6250", + "23/6.0.1; 420dpi; 1080x1920; LeMobile/LeEco; Le X527; le_s2_ww; qcom" + }; + + @NonNull + public static String generateBrowserUA(final int code) { + return browsers[code - 1]; + } + + @NonNull + public static String generateAppUA(final int code, final String lang) { + return "Instagram " + igVersion + " Android (" + devices[code] + "; " + lang + "; " + igVersionCode + ")"; + } +} diff --git a/app/src/main/java/awais/instagrabber/viewmodels/DirectSettingsViewModel.java b/app/src/main/java/awais/instagrabber/viewmodels/DirectSettingsViewModel.java index 14ae8c8a..2b282340 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/DirectSettingsViewModel.java +++ b/app/src/main/java/awais/instagrabber/viewmodels/DirectSettingsViewModel.java @@ -79,7 +79,7 @@ public class DirectSettingsViewModel extends AndroidViewModel { throw new IllegalArgumentException("User is not logged in!"); } directMessagesService = DirectMessagesService.getInstance(csrfToken, userId, deviceUuid); - friendshipService = FriendshipService.getInstance(); + friendshipService = FriendshipService.getInstance(deviceUuid, csrfToken, userId); resources = getApplication().getResources(); } @@ -264,7 +264,7 @@ public class DirectSettingsViewModel extends AndroidViewModel { private LiveData> blockUser(final User user) { final MutableLiveData> data = new MutableLiveData<>(); - friendshipService.block(userId, user.getPk(), csrfToken, new ServiceCallback() { + friendshipService.block(user.getPk(), new ServiceCallback() { @Override public void onSuccess(final FriendshipChangeResponse result) { // refresh thread @@ -281,7 +281,7 @@ public class DirectSettingsViewModel extends AndroidViewModel { private LiveData> unblockUser(final User user) { final MutableLiveData> data = new MutableLiveData<>(); - friendshipService.unblock(userId, user.getPk(), csrfToken, new ServiceCallback() { + friendshipService.unblock(user.getPk(), new ServiceCallback() { @Override public void onSuccess(final FriendshipChangeResponse result) { // refresh thread @@ -298,7 +298,7 @@ public class DirectSettingsViewModel extends AndroidViewModel { private LiveData> restrictUser(final User user) { final MutableLiveData> data = new MutableLiveData<>(); - friendshipService.toggleRestrict(user.getPk(), true, csrfToken, new ServiceCallback() { + friendshipService.toggleRestrict(user.getPk(), true, new ServiceCallback() { @Override public void onSuccess(final FriendshipRestrictResponse result) { // refresh thread @@ -315,7 +315,7 @@ public class DirectSettingsViewModel extends AndroidViewModel { private LiveData> unRestrictUser(final User user) { final MutableLiveData> data = new MutableLiveData<>(); - friendshipService.toggleRestrict(user.getPk(), false, csrfToken, new ServiceCallback() { + friendshipService.toggleRestrict(user.getPk(), false, new ServiceCallback() { @Override public void onSuccess(final FriendshipRestrictResponse result) { // refresh thread diff --git a/app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.java b/app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.java index 14742b99..514ec57a 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.java +++ b/app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.java @@ -103,7 +103,7 @@ public class DirectThreadViewModel extends AndroidViewModel { throw new IllegalArgumentException("User is not logged in!"); } service = DirectMessagesService.getInstance(csrfToken, userId, deviceUuid); - mediaService = MediaService.getInstance(); + mediaService = MediaService.getInstance(deviceUuid, csrfToken, userId); contentResolver = application.getContentResolver(); recordingsDir = DirectoryUtils.getOutputMediaDirectory(application, "Recordings"); this.application = application; @@ -467,7 +467,7 @@ public class DirectThreadViewModel extends AndroidViewModel { .setUploadId(uploadDmVideoOptions.getUploadId()) .setSourceType("2") .setVideoOptions(new UploadFinishOptions.VideoOptions().setLength(duration / 1000f)); - final Call uploadFinishRequest = mediaService.uploadFinish(userId, csrfToken, uploadFinishOptions); + final Call uploadFinishRequest = mediaService.uploadFinish(uploadFinishOptions); uploadFinishRequest.enqueue(new Callback() { @Override public void onResponse(@NonNull final Call call, @NonNull final Response response) { @@ -584,7 +584,7 @@ public class DirectThreadViewModel extends AndroidViewModel { final UploadFinishOptions uploadFinishOptions = new UploadFinishOptions() .setUploadId(uploadDmVoiceOptions.getUploadId()) .setSourceType("4"); - final Call uploadFinishRequest = mediaService.uploadFinish(userId, csrfToken, uploadFinishOptions); + final Call uploadFinishRequest = mediaService.uploadFinish(uploadFinishOptions); uploadFinishRequest.enqueue(new Callback() { @Override public void onResponse(@NonNull final Call call, @NonNull final Response response) { diff --git a/app/src/main/java/awais/instagrabber/viewmodels/PostViewV2ViewModel.java b/app/src/main/java/awais/instagrabber/viewmodels/PostViewV2ViewModel.java index 4575a344..b17a8d75 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/PostViewV2ViewModel.java +++ b/app/src/main/java/awais/instagrabber/viewmodels/PostViewV2ViewModel.java @@ -43,16 +43,16 @@ public class PostViewV2ViewModel extends ViewModel { private final MutableLiveData> options = new MutableLiveData<>(new ArrayList<>()); private final MediaService mediaService; private final long viewerId; - private final String csrfToken; private final boolean isLoggedIn; private Media media; public PostViewV2ViewModel() { - mediaService = MediaService.getInstance(); final String cookie = settingsHelper.getString(Constants.COOKIE); + final String deviceUuid = settingsHelper.getString(Constants.DEVICE_UUID); + final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie); viewerId = CookieUtils.getUserIdFromCookie(cookie); - csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie); + mediaService = MediaService.getInstance(deviceUuid, csrfToken, viewerId); isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0; } @@ -142,14 +142,14 @@ public class PostViewV2ViewModel extends ViewModel { public LiveData> like() { final MutableLiveData> data = new MutableLiveData<>(); data.postValue(Resource.loading(null)); - mediaService.like(media.getPk(), viewerId, csrfToken, getLikeUnlikeCallback(data)); + mediaService.like(media.getPk(), getLikeUnlikeCallback(data)); return data; } public LiveData> unlike() { final MutableLiveData> data = new MutableLiveData<>(); data.postValue(Resource.loading(null)); - mediaService.unlike(media.getPk(), viewerId, csrfToken, getLikeUnlikeCallback(data)); + mediaService.unlike(media.getPk(), getLikeUnlikeCallback(data)); return data; } @@ -196,14 +196,14 @@ public class PostViewV2ViewModel extends ViewModel { public LiveData> save() { final MutableLiveData> data = new MutableLiveData<>(); data.postValue(Resource.loading(null)); - mediaService.save(media.getPk(), viewerId, csrfToken, getSaveUnsaveCallback(data)); + mediaService.save(media.getPk(), getSaveUnsaveCallback(data)); return data; } public LiveData> unsave() { final MutableLiveData> data = new MutableLiveData<>(); data.postValue(Resource.loading(null)); - mediaService.unsave(media.getPk(), viewerId, csrfToken, getSaveUnsaveCallback(data)); + mediaService.unsave(media.getPk(), getSaveUnsaveCallback(data)); return data; } @@ -232,7 +232,7 @@ public class PostViewV2ViewModel extends ViewModel { public LiveData> updateCaption(final String caption) { final MutableLiveData> data = new MutableLiveData<>(); data.postValue(Resource.loading(null)); - mediaService.editCaption(media.getPk(), viewerId, caption, csrfToken, new ServiceCallback() { + mediaService.editCaption(media.getPk(), caption, new ServiceCallback() { @Override public void onSuccess(final Boolean result) { if (result) { diff --git a/app/src/main/java/awais/instagrabber/webservices/AddCookiesInterceptor.java b/app/src/main/java/awais/instagrabber/webservices/AddCookiesInterceptor.java index 37d123b5..3b5f709f 100644 --- a/app/src/main/java/awais/instagrabber/webservices/AddCookiesInterceptor.java +++ b/app/src/main/java/awais/instagrabber/webservices/AddCookiesInterceptor.java @@ -25,7 +25,7 @@ public class AddCookiesInterceptor implements Interceptor { } final String userAgentHeader = "User-Agent"; if (request.header(userAgentHeader) == null) { - builder.addHeader(userAgentHeader, hasCookie ? Constants.I_USER_AGENT : Constants.USER_AGENT); + builder.addHeader(userAgentHeader, Utils.settingsHelper.getString(hasCookie ? Constants.APP_UA : Constants.BROWSER_UA)); } final String languageHeader = "Accept-Language"; if (request.header(languageHeader) == null) { diff --git a/app/src/main/java/awais/instagrabber/webservices/FeedService.java b/app/src/main/java/awais/instagrabber/webservices/FeedService.java index 6ec814c4..72e75adc 100644 --- a/app/src/main/java/awais/instagrabber/webservices/FeedService.java +++ b/app/src/main/java/awais/instagrabber/webservices/FeedService.java @@ -9,7 +9,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.TimeZone; import java.util.UUID; import awais.instagrabber.repositories.FeedRepository; @@ -47,16 +46,16 @@ public class FeedService extends BaseService { } public void fetch(final String csrfToken, + final String deviceUuid, final String cursor, final ServiceCallback callback) { final Map form = new HashMap<>(); - form.put("_uuid", UUID.randomUUID().toString()); + form.put("_uuid", deviceUuid); form.put("_csrftoken", csrfToken); form.put("phone_id", UUID.randomUUID().toString()); form.put("device_id", UUID.randomUUID().toString()); form.put("client_session_id", UUID.randomUUID().toString()); form.put("is_prefetch", "0"); - form.put("timezone_offset", String.valueOf(TimeZone.getDefault().getRawOffset() / 1000)); if (!TextUtils.isEmpty(cursor)) { form.put("max_id", cursor); form.put("reason", "pagination"); @@ -110,7 +109,7 @@ public class FeedService extends BaseService { final List allPosts = new ArrayList<>(); final List items = feedFetchResponse.getItems(); for (final Media media : items) { - if (media.isInjected() || media.getMediaType() == null) continue; + if (media == null || media.isInjected() || (media.getMediaType() == null && media.getEndOfFeedDemarcator() == null)) continue; if (needNewMaxId && media.getEndOfFeedDemarcator() != null) { final EndOfFeedDemarcator endOfFeedDemarcator = media.getEndOfFeedDemarcator(); final EndOfFeedGroupSet groupSet = endOfFeedDemarcator.getGroupSet(); @@ -123,7 +122,7 @@ public class FeedService extends BaseService { nextMaxId = group.getNextMaxId(); final List feedItems = group.getFeedItems(); for (final Media feedItem : feedItems) { - if (feedItem == null || feedItem.isInjected()) continue; + if (feedItem == null || feedItem.isInjected() || feedItem.getMediaType() == null) continue; allPosts.add(feedItem); } } diff --git a/app/src/main/java/awais/instagrabber/webservices/FriendshipService.java b/app/src/main/java/awais/instagrabber/webservices/FriendshipService.java index 5b6ec369..ec69ff54 100644 --- a/app/src/main/java/awais/instagrabber/webservices/FriendshipService.java +++ b/app/src/main/java/awais/instagrabber/webservices/FriendshipService.java @@ -13,14 +13,13 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.UUID; +import java.util.Objects; import awais.instagrabber.models.FollowModel; import awais.instagrabber.repositories.FriendshipRepository; import awais.instagrabber.repositories.responses.FriendshipChangeResponse; import awais.instagrabber.repositories.responses.FriendshipListFetchResponse; import awais.instagrabber.repositories.responses.FriendshipRestrictResponse; -import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.Utils; import retrofit2.Call; @@ -32,61 +31,74 @@ public class FriendshipService extends BaseService { private static final String TAG = "FriendshipService"; private final FriendshipRepository repository; + private final String deviceUuid, csrfToken; + private final long userId; private static FriendshipService instance; - private FriendshipService() { + private FriendshipService(final String deviceUuid, + final String csrfToken, + final long userId) { + this.deviceUuid = deviceUuid; + this.csrfToken = csrfToken; + this.userId = userId; final Retrofit retrofit = getRetrofitBuilder() .baseUrl("https://i.instagram.com") .build(); repository = retrofit.create(FriendshipRepository.class); } - public static FriendshipService getInstance() { - if (instance == null) { - instance = new FriendshipService(); + public String getCsrfToken() { + return csrfToken; + } + + public String getDeviceUuid() { + return deviceUuid; + } + + public long getUserId() { + return userId; + } + + public static FriendshipService getInstance(final String deviceUuid, final String csrfToken, final long userId) { + if (instance == null + || !Objects.equals(instance.getCsrfToken(), csrfToken) + || !Objects.equals(instance.getDeviceUuid(), deviceUuid) + || !Objects.equals(instance.getUserId(), userId)) { + instance = new FriendshipService(deviceUuid, csrfToken, userId); } return instance; } - public void follow(final long userId, - final long targetUserId, - final String csrfToken, + public void follow(final long targetUserId, final ServiceCallback callback) { - change("create", userId, targetUserId, csrfToken, callback); + change("create", targetUserId, callback); } - public void unfollow(final long userId, - final long targetUserId, - final String csrfToken, + public void unfollow(final long targetUserId, final ServiceCallback callback) { - change("destroy", userId, targetUserId, csrfToken, callback); + change("destroy", targetUserId, callback); } - public void block(final long userId, - final long targetUserId, - final String csrfToken, + public void block(final long targetUserId, final ServiceCallback callback) { - change("block", userId, targetUserId, csrfToken, callback); + change("block", targetUserId, callback); } - public void unblock(final long userId, - final long targetUserId, - final String csrfToken, + public void unblock(final long targetUserId, final ServiceCallback callback) { - change("unblock", userId, targetUserId, csrfToken, callback); + change("unblock", targetUserId, callback); } public void toggleRestrict(final long targetUserId, final boolean restrict, - final String csrfToken, final ServiceCallback callback) { final Map form = new HashMap<>(3); form.put("_csrftoken", csrfToken); - form.put("_uuid", UUID.randomUUID().toString()); + form.put("_uuid", deviceUuid); form.put("target_user_id", String.valueOf(targetUserId)); final String action = restrict ? "restrict" : "unrestrict"; - final Call request = repository.toggleRestrict(Constants.I_USER_AGENT, action, form); + final Call request = repository.toggleRestrict(action, form); request.enqueue(new Callback() { @Override public void onResponse(@NonNull final Call call, @@ -106,33 +118,27 @@ public class FriendshipService extends BaseService { }); } - public void approve(final long userId, - final long targetUserId, - final String csrfToken, + public void approve(final long targetUserId, final ServiceCallback callback) { - change("approve", userId, targetUserId, csrfToken, callback); + change("approve", targetUserId, callback); } - public void ignore(final long userId, - final long targetUserId, - final String csrfToken, + public void ignore(final long targetUserId, final ServiceCallback callback) { - change("ignore", userId, targetUserId, csrfToken, callback); + change("ignore", targetUserId, callback); } private void change(final String action, - final long userId, final long targetUserId, - final String csrfToken, final ServiceCallback callback) { final Map form = new HashMap<>(5); form.put("_csrftoken", csrfToken); form.put("_uid", userId); - form.put("_uuid", UUID.randomUUID().toString()); + form.put("_uuid", deviceUuid); form.put("radio_type", "wifi-none"); form.put("user_id", targetUserId); final Map signedForm = Utils.sign(form); - final Call request = repository.change(Constants.I_USER_AGENT, action, targetUserId, signedForm); + final Call request = repository.change(action, targetUserId, signedForm); request.enqueue(new Callback() { @Override public void onResponse(@NonNull final Call call, @@ -158,8 +164,8 @@ public class FriendshipService extends BaseService { final ServiceCallback callback) { final Map queryMap = new HashMap<>(); if (maxId != null) queryMap.put("max_id", maxId); - final Call request = repository.getList(Constants.I_USER_AGENT, - targetUserId, + final Call request = repository.getList( + targetUserId, follower ? "followers" : "following", queryMap); request.enqueue(new Callback() { diff --git a/app/src/main/java/awais/instagrabber/webservices/MediaService.java b/app/src/main/java/awais/instagrabber/webservices/MediaService.java index 81354535..1c24f95d 100644 --- a/app/src/main/java/awais/instagrabber/webservices/MediaService.java +++ b/app/src/main/java/awais/instagrabber/webservices/MediaService.java @@ -14,6 +14,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.UUID; import awais.instagrabber.repositories.MediaRepository; @@ -34,19 +35,41 @@ public class MediaService extends BaseService { private static final String TAG = "MediaService"; private final MediaRepository repository; + private final String deviceUuid, csrfToken; + private final long userId; private static MediaService instance; - private MediaService() { + private MediaService(final String deviceUuid, + final String csrfToken, + final long userId) { + this.deviceUuid = deviceUuid; + this.csrfToken = csrfToken; + this.userId = userId; final Retrofit retrofit = getRetrofitBuilder() .baseUrl("https://i.instagram.com") .build(); repository = retrofit.create(MediaRepository.class); } - public static MediaService getInstance() { - if (instance == null) { - instance = new MediaService(); + public String getCsrfToken() { + return csrfToken; + } + + public String getDeviceUuid() { + return deviceUuid; + } + + public long getUserId() { + return userId; + } + + public static MediaService getInstance(final String deviceUuid, final String csrfToken, final long userId) { + if (instance == null + || !Objects.equals(instance.getCsrfToken(), csrfToken) + || !Objects.equals(instance.getDeviceUuid(), deviceUuid) + || !Objects.equals(instance.getUserId(), userId)) { + instance = new MediaService(deviceUuid, csrfToken, userId); } return instance; } @@ -78,43 +101,33 @@ public class MediaService extends BaseService { } public void like(final String mediaId, - final long userId, - final String csrfToken, final ServiceCallback callback) { - action(mediaId, userId, "like", csrfToken, callback); + action(mediaId, "like", callback); } public void unlike(final String mediaId, - final long userId, - final String csrfToken, final ServiceCallback callback) { - action(mediaId, userId, "unlike", csrfToken, callback); + action(mediaId, "unlike", callback); } public void save(final String mediaId, - final long userId, - final String csrfToken, final ServiceCallback callback) { - action(mediaId, userId, "save", csrfToken, callback); + action(mediaId, "save", callback); } public void unsave(final String mediaId, - final long userId, - final String csrfToken, final ServiceCallback callback) { - action(mediaId, userId, "unsave", csrfToken, callback); + action(mediaId, "unsave", callback); } private void action(final String mediaId, - final long userId, final String action, - final String csrfToken, final ServiceCallback callback) { final Map form = new HashMap<>(4); form.put("media_id", mediaId); form.put("_csrftoken", csrfToken); form.put("_uid", userId); - form.put("_uuid", UUID.randomUUID().toString()); + form.put("_uuid", deviceUuid); // form.put("radio_type", "wifi-none"); final Map signedForm = Utils.sign(form); final Call request = repository.action(action, mediaId, signedForm); @@ -149,9 +162,7 @@ public class MediaService extends BaseService { public void comment(@NonNull final String mediaId, @NonNull final String comment, - final long userId, final String replyToCommentId, - final String csrfToken, @NonNull final ServiceCallback callback) { final String module = "self_comments_v2"; final Map form = new HashMap<>(); @@ -159,7 +170,7 @@ public class MediaService extends BaseService { form.put("idempotence_token", UUID.randomUUID().toString()); form.put("_csrftoken", csrfToken); form.put("_uid", userId); - form.put("_uuid", UUID.randomUUID().toString()); + form.put("_uuid", deviceUuid); form.put("comment_text", comment); form.put("containermodule", module); if (!TextUtils.isEmpty(replyToCommentId)) { @@ -194,23 +205,19 @@ public class MediaService extends BaseService { } public void deleteComment(final String mediaId, - final long userId, final String commentId, - final String csrfToken, @NonNull final ServiceCallback callback) { - deleteComments(mediaId, userId, Collections.singletonList(commentId), csrfToken, callback); + deleteComments(mediaId, Collections.singletonList(commentId), callback); } public void deleteComments(final String mediaId, - final long userId, final List commentIds, - final String csrfToken, @NonNull final ServiceCallback callback) { final Map form = new HashMap<>(); form.put("comment_ids_to_delete", TextUtils.join(",", commentIds)); form.put("_csrftoken", csrfToken); form.put("_uid", userId); - form.put("_uuid", UUID.randomUUID().toString()); + form.put("_uuid", deviceUuid); final Map signedForm = Utils.sign(form); final Call bulkDeleteRequest = repository.commentsBulkDelete(mediaId, signedForm); bulkDeleteRequest.enqueue(new Callback() { @@ -241,12 +248,11 @@ public class MediaService extends BaseService { } public void commentLike(@NonNull final String commentId, - @NonNull final String csrfToken, @NonNull final ServiceCallback callback) { final Map form = new HashMap<>(); form.put("_csrftoken", csrfToken); // form.put("_uid", userId); - // form.put("_uuid", UUID.randomUUID().toString()); + // form.put("_uuid", deviceUuid); final Map signedForm = Utils.sign(form); final Call commentLikeRequest = repository.commentLike(commentId, signedForm); commentLikeRequest.enqueue(new Callback() { @@ -277,12 +283,11 @@ public class MediaService extends BaseService { } public void commentUnlike(final String commentId, - @NonNull final String csrfToken, @NonNull final ServiceCallback callback) { final Map form = new HashMap<>(); form.put("_csrftoken", csrfToken); // form.put("_uid", userId); - // form.put("_uuid", UUID.randomUUID().toString()); + // form.put("_uuid", deviceUuid); final Map signedForm = Utils.sign(form); final Call commentUnlikeRequest = repository.commentUnlike(commentId, signedForm); commentUnlikeRequest.enqueue(new Callback() { @@ -313,14 +318,12 @@ public class MediaService extends BaseService { } public void editCaption(final String postId, - final long userId, final String newCaption, - @NonNull final String csrfToken, @NonNull final ServiceCallback callback) { final Map form = new HashMap<>(); form.put("_csrftoken", csrfToken); form.put("_uid", userId); - form.put("_uuid", UUID.randomUUID().toString()); + form.put("_uuid", deviceUuid); form.put("igtv_feed_preview", "false"); form.put("media_id", postId); form.put("caption_text", newCaption); @@ -411,9 +414,7 @@ public class MediaService extends BaseService { }); } - public Call uploadFinish(final long userId, - @NonNull final String csrfToken, - @NonNull final UploadFinishOptions options) { + public Call uploadFinish(@NonNull final UploadFinishOptions options) { if (options.getVideoOptions() != null) { final UploadFinishOptions.VideoOptions videoOptions = options.getVideoOptions(); if (videoOptions.getClips() == null) { @@ -430,7 +431,7 @@ public class MediaService extends BaseService { .put("_csrftoken", csrfToken) .put("source_type", options.getSourceType()) .put("_uid", String.valueOf(userId)) - .put("_uuid", UUID.randomUUID().toString()) + .put("_uuid", deviceUuid) .put("upload_id", options.getUploadId()); if (options.getVideoOptions() != null) { formBuilder.putAll(options.getVideoOptions().getMap()); diff --git a/app/src/main/java/awais/instagrabber/webservices/NewsService.java b/app/src/main/java/awais/instagrabber/webservices/NewsService.java index 945b1f17..d8c83313 100644 --- a/app/src/main/java/awais/instagrabber/webservices/NewsService.java +++ b/app/src/main/java/awais/instagrabber/webservices/NewsService.java @@ -21,6 +21,7 @@ import awais.instagrabber.models.NotificationModel; import awais.instagrabber.models.enums.NotificationType; import awais.instagrabber.repositories.NewsRepository; import awais.instagrabber.utils.Constants; +import awais.instagrabber.utils.Utils; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; @@ -32,6 +33,7 @@ public class NewsService extends BaseService { private final NewsRepository repository; private static NewsService instance; + private static String browserUa, appUa; private NewsService() { final Retrofit retrofit = getRetrofitBuilder() @@ -44,13 +46,15 @@ public class NewsService extends BaseService { if (instance == null) { instance = new NewsService(); } + appUa = Utils.settingsHelper.getString(Constants.APP_UA); + browserUa = Utils.settingsHelper.getString(Constants.BROWSER_UA); return instance; } public void fetchAppInbox(final boolean markAsSeen, final ServiceCallback> callback) { final List result = new ArrayList<>(); - final Call request = repository.appInbox(markAsSeen); + final Call request = repository.appInbox(appUa, markAsSeen); request.enqueue(new Callback() { @Override public void onResponse(@NonNull final Call call, @NonNull final Response response) { @@ -90,7 +94,7 @@ public class NewsService extends BaseService { public void fetchWebInbox(final boolean markAsSeen, final ServiceCallback> callback) { - final Call request = repository.webInbox(); + final Call request = repository.webInbox(browserUa); request.enqueue(new Callback() { @Override public void onResponse(@NonNull final Call call, @NonNull final Response response) { @@ -181,7 +185,7 @@ public class NewsService extends BaseService { data.getLong("profile_id"), data.getString("profile_name"), data.getString("profile_image"), - !data.isNull("media") ? data.getJSONArray("media").getJSONObject(0).getLong("id") : 0, + !data.isNull("media") ? Long.valueOf(data.getJSONArray("media").getJSONObject(0).getString("id").split("_")[0]) : 0, !data.isNull("media") ? data.getJSONArray("media").getJSONObject(0).getString("image") : null, notificationType); } @@ -206,7 +210,7 @@ public class NewsService extends BaseService { form.put("device_id", UUID.randomUUID().toString()); form.put("module", "discover_people"); form.put("paginate", "false"); - final Call request = repository.getAyml(form); + final Call request = repository.getAyml(appUa, form); request.enqueue(new Callback() { @Override public void onResponse(@NonNull final Call call, @NonNull final Response response) { diff --git a/app/src/main/java/awais/instagrabber/webservices/StoriesService.java b/app/src/main/java/awais/instagrabber/webservices/StoriesService.java index 00c863c1..07fbd448 100644 --- a/app/src/main/java/awais/instagrabber/webservices/StoriesService.java +++ b/app/src/main/java/awais/instagrabber/webservices/StoriesService.java @@ -325,7 +325,7 @@ public class StoriesService extends BaseService { public void getUserStory(final StoryViewerOptions options, final ServiceCallback> callback) { final String url = buildUrl(options); - final Call userStoryCall = repository.getUserStory(Constants.I_USER_AGENT, url); + final Call userStoryCall = repository.getUserStory(url); final boolean isLoc = options.getType() == StoryViewerOptions.Type.LOCATION; final boolean isHashtag = options.getType() == StoryViewerOptions.Type.HASHTAG; final boolean isHighlight = options.getType() == StoryViewerOptions.Type.HIGHLIGHT; @@ -400,7 +400,7 @@ public class StoriesService extends BaseService { form.put(arg1, arg2); final Map signedForm = Utils.sign(form); final Call request = - repository.respondToSticker(Constants.I_USER_AGENT, storyId, stickerId, action, signedForm); + repository.respondToSticker(storyId, stickerId, action, signedForm); request.enqueue(new Callback() { @Override public void onResponse(@NonNull final Call call, diff --git a/app/src/main/java/awais/instagrabber/webservices/TagsService.java b/app/src/main/java/awais/instagrabber/webservices/TagsService.java index 004bcd8f..532a61d4 100644 --- a/app/src/main/java/awais/instagrabber/webservices/TagsService.java +++ b/app/src/main/java/awais/instagrabber/webservices/TagsService.java @@ -12,7 +12,6 @@ import org.json.JSONObject; import awais.instagrabber.repositories.TagsRepository; import awais.instagrabber.repositories.responses.PostsFetchResponse; import awais.instagrabber.repositories.responses.TagFeedResponse; -import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.TextUtils; import retrofit2.Call; import retrofit2.Callback; @@ -46,12 +45,11 @@ public class TagsService extends BaseService { return instance; } - public void follow(@NonNull final String tag, + public void follow(@NonNull final String ua, + @NonNull final String tag, @NonNull final String csrfToken, final ServiceCallback callback) { - final Call request = webRepository.follow(Constants.USER_AGENT, - csrfToken, - tag); + final Call request = webRepository.follow(ua, csrfToken, tag); request.enqueue(new Callback() { @Override public void onResponse(@NonNull final Call call, @NonNull final Response response) { @@ -77,12 +75,11 @@ public class TagsService extends BaseService { }); } - public void unfollow(@NonNull final String tag, + public void unfollow(@NonNull final String ua, + @NonNull final String tag, @NonNull final String csrfToken, final ServiceCallback callback) { - final Call request = webRepository.unfollow(Constants.USER_AGENT, - csrfToken, - tag); + final Call request = webRepository.unfollow(ua, csrfToken, tag); request.enqueue(new Callback() { @Override public void onResponse(@NonNull final Call call, @NonNull final Response response) { diff --git a/app/src/main/res/layout/fragment_direct_messages_settings.xml b/app/src/main/res/layout/fragment_direct_messages_settings.xml index a5116031..1723c9fc 100644 --- a/app/src/main/res/layout/fragment_direct_messages_settings.xml +++ b/app/src/main/res/layout/fragment_direct_messages_settings.xml @@ -72,6 +72,7 @@ android:layout_height="wrap_content" android:paddingStart="0dp" android:paddingEnd="8dp" + android:layout_margin="8dp" app:layout_constraintBottom_toTopOf="@id/mute_mentions" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toBottomOf="@id/title_edit_input_layout" /> @@ -98,6 +99,7 @@ android:layout_height="wrap_content" android:paddingStart="0dp" android:paddingEnd="8dp" + android:layout_margin="8dp" app:layout_constraintBottom_toTopOf="@id/leave" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toBottomOf="@id/mute_messages" />