From cae457aa9ae703038c78035578838e8f73de36c5 Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Sun, 6 Jun 2021 19:29:21 +0900 Subject: [PATCH] Convert StoriesRepository and StoriesService to kotlin --- .../fragments/HashTagFragment.java | 31 +- .../fragments/LocationFragment.java | 28 +- .../fragments/StoryListViewerFragment.java | 44 +- .../fragments/StoryViewerFragment.java | 245 +++--- .../fragments/main/FeedFragment.java | 40 +- .../fragments/main/ProfileFragment.java | 57 +- .../repositories/StoriesRepository.kt | 47 +- .../webservices/StoriesService.kt | 789 ++++++------------ 8 files changed, 533 insertions(+), 748 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java index ce3d6dd7..1d141437 100644 --- a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java @@ -39,7 +39,6 @@ import com.google.android.material.snackbar.Snackbar; import com.google.common.collect.ImmutableList; import java.time.LocalDateTime; -import java.util.List; import java.util.Set; import awais.instagrabber.R; @@ -55,7 +54,6 @@ import awais.instagrabber.db.repositories.FavoriteRepository; import awais.instagrabber.db.repositories.RepositoryCallback; import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment; import awais.instagrabber.models.PostsLayoutPreferences; -import awais.instagrabber.models.StoryModel; import awais.instagrabber.models.enums.FavoriteType; import awais.instagrabber.models.enums.FollowingType; import awais.instagrabber.repositories.requests.StoryViewerOptions; @@ -301,7 +299,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe final String cookie = settingsHelper.getString(Constants.COOKIE); isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0; tagsService = isLoggedIn ? TagsService.getInstance() : null; - storiesService = isLoggedIn ? StoriesService.getInstance(null, 0L, null) : null; + storiesService = isLoggedIn ? StoriesService.INSTANCE : null; graphQLService = isLoggedIn ? null : GraphQLService.INSTANCE; setHasOptionsMenu(true); } @@ -582,24 +580,21 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe storiesFetching = true; storiesService.getUserStory( StoryViewerOptions.forHashtag(hashtagModel.getName()), - new ServiceCallback>() { - @Override - public void onSuccess(final List storyModels) { - if (storyModels != null && !storyModels.isEmpty()) { - hashtagDetailsBinding.mainHashtagImage.setStoriesBorder(1); - hasStories = true; - } else { - hasStories = false; - } + CoroutineUtilsKt.getContinuation((storyModels, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { + if (throwable != null) { + Log.e(TAG, "Error", throwable); storiesFetching = false; + return; } - - @Override - public void onFailure(final Throwable t) { - Log.e(TAG, "Error", t); - storiesFetching = false; + if (storyModels != null && !storyModels.isEmpty()) { + hashtagDetailsBinding.mainHashtagImage.setStoriesBorder(1); + hasStories = true; + } else { + hasStories = false; } - }); + storiesFetching = false; + }), Dispatchers.getIO()) + ); } private void setTitle() { diff --git a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java index 0ac2571b..df0ff4eb 100644 --- a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java @@ -37,7 +37,6 @@ import com.google.android.material.snackbar.Snackbar; import com.google.common.collect.ImmutableList; import java.time.LocalDateTime; -import java.util.List; import java.util.Set; import awais.instagrabber.R; @@ -53,7 +52,6 @@ import awais.instagrabber.db.repositories.FavoriteRepository; import awais.instagrabber.db.repositories.RepositoryCallback; import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment; import awais.instagrabber.models.PostsLayoutPreferences; -import awais.instagrabber.models.StoryModel; import awais.instagrabber.models.enums.FavoriteType; import awais.instagrabber.repositories.requests.StoryViewerOptions; import awais.instagrabber.repositories.responses.Location; @@ -70,6 +68,7 @@ import awais.instagrabber.webservices.GraphQLService; import awais.instagrabber.webservices.LocationService; import awais.instagrabber.webservices.ServiceCallback; import awais.instagrabber.webservices.StoriesService; +import kotlinx.coroutines.Dispatchers; import static androidx.core.content.PermissionChecker.checkSelfPermission; import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION; @@ -293,7 +292,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR final String cookie = settingsHelper.getString(Constants.COOKIE); isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0; locationService = isLoggedIn ? LocationService.getInstance() : null; - storiesService = StoriesService.getInstance(null, 0L, null); + storiesService = StoriesService.INSTANCE; graphQLService = isLoggedIn ? null : GraphQLService.INSTANCE; setHasOptionsMenu(true); } @@ -586,22 +585,19 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR storiesFetching = true; storiesService.getUserStory( StoryViewerOptions.forLocation(locationId, locationModel.getName()), - new ServiceCallback>() { - @Override - public void onSuccess(final List storyModels) { - if (storyModels != null && !storyModels.isEmpty()) { - locationDetailsBinding.mainLocationImage.setStoriesBorder(1); - hasStories = true; - } + CoroutineUtilsKt.getContinuation((storyModels, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { + if (throwable != null) { + Log.e(TAG, "Error", throwable); storiesFetching = false; + return; } - - @Override - public void onFailure(final Throwable t) { - Log.e(TAG, "Error", t); - storiesFetching = false; + if (storyModels != null && !storyModels.isEmpty()) { + locationDetailsBinding.mainLocationImage.setStoriesBorder(1); + hasStories = true; } - }); + storiesFetching = false; + }), Dispatchers.getIO()) + ); } } diff --git a/app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java index 6521e735..b21a77e8 100644 --- a/app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java @@ -41,12 +41,15 @@ import awais.instagrabber.fragments.settings.MorePreferencesFragmentDirections; import awais.instagrabber.models.FeedStoryModel; import awais.instagrabber.models.HighlightModel; import awais.instagrabber.repositories.requests.StoryViewerOptions; +import awais.instagrabber.utils.AppExecutors; +import awais.instagrabber.utils.CoroutineUtilsKt; import awais.instagrabber.utils.TextUtils; import awais.instagrabber.viewmodels.ArchivesViewModel; import awais.instagrabber.viewmodels.FeedStoriesViewModel; import awais.instagrabber.webservices.ServiceCallback; import awais.instagrabber.webservices.StoriesService; import awais.instagrabber.webservices.StoriesService.ArchiveFetchResponse; +import kotlinx.coroutines.Dispatchers; public final class StoryListViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener { private static final String TAG = "StoryListViewerFragment"; @@ -133,7 +136,7 @@ public final class StoryListViewerFragment extends Fragment implements SwipeRefr context = getContext(); if (context == null) return; setHasOptionsMenu(true); - storiesService = StoriesService.getInstance(null, 0L, null); + storiesService = StoriesService.INSTANCE; } @NonNull @@ -239,22 +242,31 @@ public final class StoryListViewerFragment extends Fragment implements SwipeRefr } firstRefresh = false; } else if (type.equals("feed")) { - storiesService.getFeedStories(new ServiceCallback>() { - @Override - public void onSuccess(final List result) { - feedStoriesViewModel.getList().postValue(result); - adapter.submitList(result); - binding.swipeRefreshLayout.setRefreshing(false); - } - - @Override - public void onFailure(final Throwable t) { - Log.e(TAG, "failed", t); - Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show(); - } - }); + storiesService.getFeedStories( + CoroutineUtilsKt.getContinuation((feedStoryModels, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { + if (throwable != null) { + Log.e(TAG, "failed", throwable); + Toast.makeText(context, throwable.getMessage(), Toast.LENGTH_SHORT).show(); + return; + } + //noinspection unchecked + feedStoriesViewModel.getList().postValue((List) feedStoryModels); + //noinspection unchecked + adapter.submitList((List) feedStoryModels); + binding.swipeRefreshLayout.setRefreshing(false); + }), Dispatchers.getIO()) + ); } else if (type.equals("archive")) { - storiesService.fetchArchive(endCursor, cb); + storiesService.fetchArchive( + endCursor, + CoroutineUtilsKt.getContinuation((archiveFetchResponse, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { + if (throwable != null) { + cb.onFailure(throwable); + return; + } + cb.onSuccess(archiveFetchResponse); + }), Dispatchers.getIO()) + ); } } diff --git a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java index c56438d0..4239c24e 100644 --- a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java @@ -87,7 +87,6 @@ import awais.instagrabber.models.stickers.SwipeUpModel; import awais.instagrabber.repositories.requests.StoryViewerOptions; import awais.instagrabber.repositories.requests.StoryViewerOptions.Type; import awais.instagrabber.repositories.requests.directmessages.ThreadIdOrUserIds; -import awais.instagrabber.repositories.responses.StoryStickerResponse; import awais.instagrabber.utils.AppExecutors; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; @@ -113,6 +112,8 @@ import static awais.instagrabber.utils.Utils.settingsHelper; public class StoryViewerFragment extends Fragment { private static final String TAG = "StoryViewerFragment"; + private final String cookie = settingsHelper.getString(Constants.COOKIE); + private AppCompatActivity fragmentActivity; private View root; private FragmentStoryViewerBinding binding; @@ -148,21 +149,22 @@ public class StoryViewerFragment extends Fragment { // private boolean isArchive; // private boolean isNotification; private DirectMessagesService directMessagesService; - - private final String cookie = settingsHelper.getString(Constants.COOKIE); private StoryViewerOptions options; + private String csrfToken; + private String deviceId; + private long userId; @Override public void onCreate(@Nullable final Bundle savedInstanceState) { super.onCreate(savedInstanceState); - final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie); + csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie); if (csrfToken == null) return; - final long userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie); - final String deviceId = settingsHelper.getString(Constants.DEVICE_UUID); + userId = CookieUtils.getUserIdFromCookie(cookie); + deviceId = settingsHelper.getString(Constants.DEVICE_UUID); fragmentActivity = (AppCompatActivity) requireActivity(); - storiesService = StoriesService.getInstance(csrfToken, userIdFromCookie, deviceId); + storiesService = StoriesService.INSTANCE; mediaService = MediaService.INSTANCE; - directMessagesService = DirectMessagesService.getInstance(csrfToken, userIdFromCookie, deviceId); + directMessagesService = DirectMessagesService.getInstance(csrfToken, userId, deviceId); setHasOptionsMenu(true); } @@ -514,28 +516,31 @@ public class StoryViewerFragment extends Fragment { }), (d, w) -> { sticking = true; storiesService.respondToPoll( + csrfToken, + userId, + deviceId, currentStory.getStoryMediaId().split("_")[0], poll.getId(), w, - new ServiceCallback() { - @Override - public void onSuccess(final StoryStickerResponse result) { - sticking = false; - try { - poll.setMyChoice(w); - Toast.makeText(context, R.string.votef_story_poll, Toast.LENGTH_SHORT).show(); - } catch (Exception ignored) {} - } - - @Override - public void onFailure(final Throwable t) { - sticking = false; - Log.e(TAG, "Error responding", t); - try { - Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); - } catch (Exception ignored) {} - } - }); + CoroutineUtilsKt.getContinuation( + (storyStickerResponse, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { + if (throwable != null) { + sticking = false; + Log.e(TAG, "Error responding", throwable); + try { + Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); + } catch (Exception ignored) {} + return; + } + sticking = false; + try { + poll.setMyChoice(w); + Toast.makeText(context, R.string.votef_story_poll, Toast.LENGTH_SHORT).show(); + } catch (Exception ignored) {} + }), + Dispatchers.getIO() + ) + ); }) .setPositiveButton(R.string.cancel, null) .show(); @@ -550,27 +555,30 @@ public class StoryViewerFragment extends Fragment { .setPositiveButton(R.string.confirm, (d, w) -> { sticking = true; storiesService.respondToQuestion( + csrfToken, + userId, + deviceId, currentStory.getStoryMediaId().split("_")[0], question.getId(), input.getText().toString(), - new ServiceCallback() { - @Override - public void onSuccess(final StoryStickerResponse result) { - sticking = false; - try { - Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT).show(); - } catch (Exception ignored) {} - } - - @Override - public void onFailure(final Throwable t) { - sticking = false; - Log.e(TAG, "Error responding", t); - try { - Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); - } catch (Exception ignored) {} - } - }); + CoroutineUtilsKt.getContinuation( + (storyStickerResponse, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { + if (throwable != null) { + sticking = false; + Log.e(TAG, "Error responding", throwable); + try { + Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); + } catch (Exception ignored) {} + return; + } + sticking = false; + try { + Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT).show(); + } catch (Exception ignored) {} + }), + Dispatchers.getIO() + ) + ); }) .setNegativeButton(R.string.cancel, null) .show(); @@ -605,28 +613,31 @@ public class StoryViewerFragment extends Fragment { if (quiz.getMyChoice() == -1) { sticking = true; storiesService.respondToQuiz( + csrfToken, + userId, + deviceId, currentStory.getStoryMediaId().split("_")[0], quiz.getId(), w, - new ServiceCallback() { - @Override - public void onSuccess(final StoryStickerResponse result) { - sticking = false; - try { - quiz.setMyChoice(w); - Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT).show(); - } catch (Exception ignored) {} - } - - @Override - public void onFailure(final Throwable t) { - sticking = false; - Log.e(TAG, "Error responding", t); - try { - Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); - } catch (Exception ignored) {} - } - }); + CoroutineUtilsKt.getContinuation( + (storyStickerResponse, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { + if (throwable != null) { + sticking = false; + Log.e(TAG, "Error responding", throwable); + try { + Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); + } catch (Exception ignored) {} + return; + } + sticking = false; + try { + quiz.setMyChoice(w); + Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT).show(); + } catch (Exception ignored) {} + }), + Dispatchers.getIO() + ) + ); } }) .setPositiveButton(R.string.cancel, null) @@ -673,28 +684,30 @@ public class StoryViewerFragment extends Fragment { .setPositiveButton(R.string.confirm, (d, w) -> { sticking = true; storiesService.respondToSlider( + csrfToken, + userId, + deviceId, currentStory.getStoryMediaId().split("_")[0], slider.getId(), sliderValue, - new ServiceCallback() { - @Override - public void onSuccess(final StoryStickerResponse result) { - sticking = false; - try { - slider.setMyChoice(sliderValue); - Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT).show(); - } catch (Exception ignored) {} - } - - @Override - public void onFailure(final Throwable t) { - sticking = false; - Log.e(TAG, "Error responding", t); - try { - Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); - } catch (Exception ignored) {} - } - }); + CoroutineUtilsKt.getContinuation( + (storyStickerResponse, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { + if (throwable != null) { + sticking = false; + Log.e(TAG, "Error responding", throwable); + try { + Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); + } catch (Exception ignored) {} + return; + } + sticking = false; + try { + slider.setMyChoice(sliderValue); + Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT).show(); + } catch (Exception ignored) {} + }), Dispatchers.getIO() + ) + ); }) .setNegativeButton(R.string.cancel, null) .show(); @@ -786,27 +799,26 @@ public class StoryViewerFragment extends Fragment { setTitle(type); storiesViewModel.getList().setValue(Collections.emptyList()); if (type == Type.STORY) { - storiesService.fetch(options.getId(), new ServiceCallback() { - @Override - public void onSuccess(final StoryModel storyModel) { - fetching = false; - binding.storiesList.setVisibility(View.GONE); - if (storyModel == null) { - storiesViewModel.getList().setValue(Collections.emptyList()); - currentStory = null; - return; - } - storiesViewModel.getList().setValue(Collections.singletonList(storyModel)); - currentStory = storyModel; - refreshStory(); - } - - @Override - public void onFailure(final Throwable t) { - Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show(); - Log.e(TAG, "Error", t); - } - }); + storiesService.fetch( + options.getId(), + CoroutineUtilsKt.getContinuation((storyModel, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { + if (throwable != null) { + Toast.makeText(context, throwable.getMessage(), Toast.LENGTH_SHORT).show(); + Log.e(TAG, "Error", throwable); + return; + } + fetching = false; + binding.storiesList.setVisibility(View.GONE); + if (storyModel == null) { + storiesViewModel.getList().setValue(Collections.emptyList()); + currentStory = null; + return; + } + storiesViewModel.getList().setValue(Collections.singletonList(storyModel)); + currentStory = storyModel; + refreshStory(); + }), Dispatchers.getIO()) + ); return; } if (currentStoryMediaId == null) return; @@ -840,7 +852,17 @@ public class StoryViewerFragment extends Fragment { storyCallback.onSuccess(Collections.singletonList(live)); return; } - storiesService.getUserStory(fetchOptions, storyCallback); + storiesService.getUserStory( + fetchOptions, + CoroutineUtilsKt.getContinuation((storyModels, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { + if (throwable != null) { + storyCallback.onFailure(throwable); + return; + } + //noinspection unchecked + storyCallback.onSuccess((List) storyModels); + }), Dispatchers.getIO()) + ); } private void setTitle(final Type type) { @@ -944,10 +966,15 @@ public class StoryViewerFragment extends Fragment { } if (settingsHelper.getBoolean(MARK_AS_SEEN)) - storiesService.seen(currentStory.getStoryMediaId(), - currentStory.getTimestamp(), - System.currentTimeMillis() / 1000, - null); + storiesService.seen( + csrfToken, + userId, + deviceId, + currentStory.getStoryMediaId(), + currentStory.getTimestamp(), + System.currentTimeMillis() / 1000, + CoroutineUtilsKt.getContinuation((s, throwable) -> {}, Dispatchers.getIO()) + ); } private void downloadStory() { 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 d22b5a51..d1d36562 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java @@ -48,12 +48,14 @@ import awais.instagrabber.models.FeedStoryModel; import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.repositories.requests.StoryViewerOptions; import awais.instagrabber.repositories.responses.Media; +import awais.instagrabber.utils.AppExecutors; import awais.instagrabber.utils.Constants; +import awais.instagrabber.utils.CoroutineUtilsKt; import awais.instagrabber.utils.DownloadUtils; import awais.instagrabber.utils.Utils; import awais.instagrabber.viewmodels.FeedStoriesViewModel; -import awais.instagrabber.webservices.ServiceCallback; import awais.instagrabber.webservices.StoriesService; +import kotlinx.coroutines.Dispatchers; import static androidx.core.content.PermissionChecker.checkSelfPermission; import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION; @@ -274,7 +276,7 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre public void onCreate(@Nullable final Bundle savedInstanceState) { super.onCreate(savedInstanceState); fragmentActivity = (MainActivity) requireActivity(); - storiesService = StoriesService.getInstance(null, 0L, null); + storiesService = StoriesService.INSTANCE; setHasOptionsMenu(true); } @@ -428,23 +430,23 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre // final String cookie = settingsHelper.getString(Constants.COOKIE); storiesFetching = true; updateSwipeRefreshState(); - storiesService.getFeedStories(new ServiceCallback>() { - @Override - public void onSuccess(final List result) { - storiesFetching = false; - feedStoriesViewModel.getList().postValue(result); - feedStoriesAdapter.submitList(result); - if (storyListMenu != null) storyListMenu.setVisible(true); - updateSwipeRefreshState(); - } - - @Override - public void onFailure(final Throwable t) { - Log.e(TAG, "failed", t); - storiesFetching = false; - updateSwipeRefreshState(); - } - }); + storiesService.getFeedStories( + CoroutineUtilsKt.getContinuation((feedStoryModels, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { + if (throwable != null) { + Log.e(TAG, "failed", throwable); + storiesFetching = false; + updateSwipeRefreshState(); + return; + } + storiesFetching = false; + //noinspection unchecked + feedStoriesViewModel.getList().postValue((List) feedStoryModels); + //noinspection unchecked + feedStoriesAdapter.submitList((List) feedStoryModels); + if (storyListMenu != null) storyListMenu.setVisible(true); + updateSwipeRefreshState(); + }), Dispatchers.getIO()) + ); } private void showPostsLayoutPreferences() { 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 099cbabb..ee3640a0 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java @@ -74,7 +74,6 @@ import awais.instagrabber.managers.DirectMessagesManager; import awais.instagrabber.managers.InboxManager; import awais.instagrabber.models.HighlightModel; import awais.instagrabber.models.PostsLayoutPreferences; -import awais.instagrabber.models.StoryModel; import awais.instagrabber.models.enums.FavoriteType; import awais.instagrabber.models.enums.PostItemType; import awais.instagrabber.repositories.requests.StoryViewerOptions; @@ -337,7 +336,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe fragmentActivity = (MainActivity) requireActivity(); friendshipService = isLoggedIn ? FriendshipService.INSTANCE : null; directMessagesService = isLoggedIn ? DirectMessagesService.getInstance(csrfToken, myId, deviceUuid) : null; - storiesService = isLoggedIn ? StoriesService.getInstance(null, 0L, null) : null; + storiesService = isLoggedIn ? StoriesService.INSTANCE : null; mediaService = isLoggedIn ? MediaService.INSTANCE : null; userService = isLoggedIn ? UserService.INSTANCE : null; graphQLService = isLoggedIn ? null : GraphQLService.INSTANCE; @@ -1044,36 +1043,34 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe private void fetchStoryAndHighlights(final long profileId) { storiesService.getUserStory( StoryViewerOptions.forUser(profileId, profileModel.getFullName()), - new ServiceCallback>() { - @Override - public void onSuccess(final List storyModels) { - if (storyModels != null && !storyModels.isEmpty()) { - profileDetailsBinding.mainProfileImage.setStoriesBorder(1); - hasStories = true; - } + CoroutineUtilsKt.getContinuation((storyModels, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { + if (throwable != null) { + Log.e(TAG, "Error", throwable); + return; } - - @Override - public void onFailure(final Throwable t) { - Log.e(TAG, "Error", t); + if (storyModels != null && !storyModels.isEmpty()) { + profileDetailsBinding.mainProfileImage.setStoriesBorder(1); + hasStories = true; } - }); - storiesService.fetchHighlights(profileId, - new ServiceCallback>() { - @Override - public void onSuccess(final List result) { - if (result != null) { - profileDetailsBinding.highlightsList.setVisibility(View.VISIBLE); - highlightsViewModel.getList().postValue(result); - } else profileDetailsBinding.highlightsList.setVisibility(View.GONE); - } - - @Override - public void onFailure(final Throwable t) { - profileDetailsBinding.highlightsList.setVisibility(View.GONE); - Log.e(TAG, "Error", t); - } - }); + }), Dispatchers.getIO()) + ); + storiesService.fetchHighlights( + profileId, + CoroutineUtilsKt.getContinuation((highlightModels, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { + if (throwable != null) { + profileDetailsBinding.highlightsList.setVisibility(View.GONE); + Log.e(TAG, "Error", throwable); + return; + } + if (highlightModels != null) { + profileDetailsBinding.highlightsList.setVisibility(View.VISIBLE); + //noinspection unchecked + highlightsViewModel.getList().postValue((List) highlightModels); + } else { + profileDetailsBinding.highlightsList.setVisibility(View.GONE); + } + }), Dispatchers.getIO()) + ); } private void setupCommonListeners() { diff --git a/app/src/main/java/awais/instagrabber/repositories/StoriesRepository.kt b/app/src/main/java/awais/instagrabber/repositories/StoriesRepository.kt index 766649c4..28f540e5 100644 --- a/app/src/main/java/awais/instagrabber/repositories/StoriesRepository.kt +++ b/app/src/main/java/awais/instagrabber/repositories/StoriesRepository.kt @@ -1,43 +1,38 @@ -package awais.instagrabber.repositories; +package awais.instagrabber.repositories -import java.util.Map; +import awais.instagrabber.repositories.responses.StoryStickerResponse +import retrofit2.http.* -import awais.instagrabber.repositories.responses.StoryStickerResponse; -import retrofit2.Call; -import retrofit2.http.FieldMap; -import retrofit2.http.FormUrlEncoded; -import retrofit2.http.GET; -import retrofit2.http.POST; -import retrofit2.http.Path; -import retrofit2.http.QueryMap; -import retrofit2.http.Url; - -public interface StoriesRepository { - @GET("/api/v1/media/{mediaId}/info/") - Call fetch(@Path("mediaId") final long mediaId); +interface StoriesRepository { // this one is the same as MediaRepository.fetch BUT you need to make sure it's a story + @GET("/api/v1/media/{mediaId}/info/") + suspend fun fetch(@Path("mediaId") mediaId: Long): String @GET("/api/v1/feed/reels_tray/") - Call getFeedStories(); + suspend fun getFeedStories(): String @GET("/api/v1/highlights/{uid}/highlights_tray/") - Call fetchHighlights(@Path("uid") final long uid); + suspend fun fetchHighlights(@Path("uid") uid: Long): String @GET("/api/v1/archive/reel/day_shells/") - Call fetchArchive(@QueryMap Map queryParams); + suspend fun fetchArchive(@QueryMap queryParams: Map): String @GET - Call getUserStory(@Url String url); + suspend fun getUserStory(@Url url: String): String @FormUrlEncoded @POST("/api/v1/media/{storyId}/{stickerId}/{action}/") - 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 - @FieldMap Map form); + suspend fun respondToSticker( + @Path("storyId") storyId: String, + @Path("stickerId") stickerId: String, + @Path("action") action: String, // story_poll_vote, story_question_response, story_slider_vote, story_quiz_answer + @FieldMap form: Map, + ): StoryStickerResponse @FormUrlEncoded @POST("/api/v2/media/seen/") - Call seen(@QueryMap Map queryParams, @FieldMap Map form); -} + suspend fun seen( + @QueryMap queryParams: Map, + @FieldMap form: Map, + ): String +} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/webservices/StoriesService.kt b/app/src/main/java/awais/instagrabber/webservices/StoriesService.kt index f72d8344..b64adf42 100644 --- a/app/src/main/java/awais/instagrabber/webservices/StoriesService.kt +++ b/app/src/main/java/awais/instagrabber/webservices/StoriesService.kt @@ -1,548 +1,309 @@ -package awais.instagrabber.webservices; +package awais.instagrabber.webservices -import android.util.Log; +import android.util.Log +import awais.instagrabber.fragments.settings.PreferenceKeys +import awais.instagrabber.models.FeedStoryModel +import awais.instagrabber.models.HighlightModel +import awais.instagrabber.models.StoryModel +import awais.instagrabber.repositories.StoriesRepository +import awais.instagrabber.repositories.requests.StoryViewerOptions +import awais.instagrabber.repositories.responses.StoryStickerResponse +import awais.instagrabber.repositories.responses.User +import awais.instagrabber.utils.Constants +import awais.instagrabber.utils.ResponseBodyUtils +import awais.instagrabber.utils.TextUtils.isEmpty +import awais.instagrabber.utils.Utils +import awais.instagrabber.utils.extensions.TAG +import awais.instagrabber.webservices.RetrofitFactory.retrofit +import org.json.JSONArray +import org.json.JSONObject +import java.util.* -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; +object StoriesService : BaseService() { + private val repository: StoriesRepository = retrofit.create(StoriesRepository::class.java) -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.ArrayList; -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.fragments.settings.PreferenceKeys; -import awais.instagrabber.models.FeedStoryModel; -import awais.instagrabber.models.HighlightModel; -import awais.instagrabber.models.StoryModel; -import awais.instagrabber.repositories.StoriesRepository; -import awais.instagrabber.repositories.requests.StoryViewerOptions; -import awais.instagrabber.repositories.responses.StoryStickerResponse; -import awais.instagrabber.repositories.responses.User; -import awais.instagrabber.utils.Constants; -import awais.instagrabber.utils.ResponseBodyUtils; -import awais.instagrabber.utils.TextUtils; -import awais.instagrabber.utils.Utils; -import retrofit2.Call; -import retrofit2.Callback; -import retrofit2.Response; - -public class StoriesService extends BaseService { - private static final String TAG = "StoriesService"; - - private static StoriesService instance; - - private final StoriesRepository repository; - private final String csrfToken; - private final long userId; - private final String deviceUuid; - - private StoriesService(@NonNull final String csrfToken, - final long userId, - @NonNull final String deviceUuid) { - this.csrfToken = csrfToken; - this.userId = userId; - this.deviceUuid = deviceUuid; - repository = RetrofitFactory.INSTANCE - .getRetrofit() - .create(StoriesRepository.class); + suspend fun fetch(mediaId: Long): StoryModel { + val response = repository.fetch(mediaId) + val itemJson = JSONObject(response).getJSONArray("items").getJSONObject(0) + return ResponseBodyUtils.parseStoryItem(itemJson, false, null) } - public String getCsrfToken() { - return csrfToken; + suspend fun getFeedStories(): List { + val response = repository.getFeedStories() + return parseStoriesBody(response) } - public long getUserId() { - return userId; - } - - public String getDeviceUuid() { - return deviceUuid; - } - - public static StoriesService getInstance(final String csrfToken, - final long userId, - final String deviceUuid) { - if (instance == null - || !Objects.equals(instance.getCsrfToken(), csrfToken) - || !Objects.equals(instance.getUserId(), userId) - || !Objects.equals(instance.getDeviceUuid(), deviceUuid)) { - instance = new StoriesService(csrfToken, userId, deviceUuid); + private fun parseStoriesBody(body: String): List { + val feedStoryModels: MutableList = ArrayList() + val feedStoriesReel = JSONObject(body).getJSONArray("tray") + for (i in 0 until feedStoriesReel.length()) { + val node = feedStoriesReel.getJSONObject(i) + if (node.optBoolean("hide_from_feed_unit") && Utils.settingsHelper.getBoolean(PreferenceKeys.HIDE_MUTED_REELS)) continue + val userJson = node.getJSONObject(if (node.has("user")) "user" else "owner") + try { + val user = User(userJson.getLong("pk"), + userJson.getString("username"), + userJson.optString("full_name"), + userJson.optBoolean("is_private"), + userJson.getString("profile_pic_url"), + userJson.optBoolean("is_verified") + ) + val timestamp = node.getLong("latest_reel_media") + val fullyRead = !node.isNull("seen") && node.getLong("seen") == timestamp + val itemJson = if (node.has("items")) node.getJSONArray("items").optJSONObject(0) else null + var firstStoryModel: StoryModel? = null + if (itemJson != null) { + firstStoryModel = ResponseBodyUtils.parseStoryItem(itemJson, false, null) + } + feedStoryModels.add(FeedStoryModel( + node.getString("id"), + user, + fullyRead, + timestamp, + firstStoryModel, + node.getInt("media_count"), + false, + node.optBoolean("has_besties_media"))) + } catch (e: Exception) { + Log.e(TAG, "parseStoriesBody: ", e) + } // to cover promotional reels with non-long user pk's } - return instance; - } - - public void fetch(final long mediaId, - final ServiceCallback callback) { - final Call request = repository.fetch(mediaId); - request.enqueue(new Callback() { - @Override - public void onResponse(@NonNull final Call call, - @NonNull final Response response) { - if (callback == null) return; - final String body = response.body(); - if (body == null) { - callback.onSuccess(null); - return; - } - try { - final JSONObject itemJson = new JSONObject(body).getJSONArray("items").getJSONObject(0); - callback.onSuccess(ResponseBodyUtils.parseStoryItem(itemJson, false, null)); - } catch (JSONException e) { - callback.onFailure(e); - } - } - - @Override - public void onFailure(@NonNull final Call call, - @NonNull final Throwable t) { - if (callback != null) { - callback.onFailure(t); - } - } - }); - } - - public void getFeedStories(final ServiceCallback> callback) { - final Call response = repository.getFeedStories(); - response.enqueue(new Callback() { - @Override - public void onResponse(@NonNull final Call call, @NonNull final Response response) { - final String body = response.body(); - if (body == null) { - Log.e(TAG, "getFeedStories: body is empty"); - return; - } - parseStoriesBody(body, callback); - } - - @Override - public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { - callback.onFailure(t); - } - }); - } - - private void parseStoriesBody(final String body, final ServiceCallback> callback) { - try { - final List feedStoryModels = new ArrayList<>(); - final JSONArray feedStoriesReel = new JSONObject(body).getJSONArray("tray"); - for (int i = 0; i < feedStoriesReel.length(); ++i) { - final JSONObject node = feedStoriesReel.getJSONObject(i); - if (node.optBoolean("hide_from_feed_unit") && Utils.settingsHelper.getBoolean(PreferenceKeys.HIDE_MUTED_REELS)) continue; - final JSONObject userJson = node.getJSONObject(node.has("user") ? "user" : "owner"); - try { - final User user = new User(userJson.getLong("pk"), - userJson.getString("username"), - userJson.optString("full_name"), - userJson.optBoolean("is_private"), - userJson.getString("profile_pic_url"), - userJson.optBoolean("is_verified") - ); - final long timestamp = node.getLong("latest_reel_media"); - final boolean fullyRead = !node.isNull("seen") && node.getLong("seen") == timestamp; - final JSONObject itemJson = node.has("items") ? node.getJSONArray("items").optJSONObject(0) : null; - StoryModel firstStoryModel = null; - if (itemJson != null) { - firstStoryModel = ResponseBodyUtils.parseStoryItem(itemJson, false, null); - } - feedStoryModels.add(new FeedStoryModel( - node.getString("id"), - user, - fullyRead, - timestamp, - firstStoryModel, - node.getInt("media_count"), - false, - node.optBoolean("has_besties_media"))); - } catch (Exception e) { - Log.e(TAG, "parseStoriesBody: ", e); - } // to cover promotional reels with non-long user pk's - } - final JSONArray broadcasts = new JSONObject(body).getJSONArray("broadcasts"); - for (int i = 0; i < broadcasts.length(); ++i) { - final JSONObject node = broadcasts.getJSONObject(i); - final JSONObject userJson = node.getJSONObject("broadcast_owner"); - // final ProfileModel profileModel = new ProfileModel(false, false, false, - // userJson.getString("pk"), - // userJson.getString("username"), - // null, null, null, - // userJson.getString("profile_pic_url"), - // null, 0, 0, 0, false, false, false, false, false); - final User user = new User(userJson.getLong("pk"), - userJson.getString("username"), - userJson.optString("full_name"), - userJson.optBoolean("is_private"), - userJson.getString("profile_pic_url"), - userJson.optBoolean("is_verified") - ); - feedStoryModels.add(new FeedStoryModel( - node.getString("id"), - user, - false, - node.getLong("published_time"), - ResponseBodyUtils.parseBroadcastItem(node), - 1, - true, - false - )); - } - callback.onSuccess(sort(feedStoryModels)); - } catch (JSONException e) { - Log.e(TAG, "Error parsing json", e); + val broadcasts = JSONObject(body).getJSONArray("broadcasts") + for (i in 0 until broadcasts.length()) { + val node = broadcasts.getJSONObject(i) + val userJson = node.getJSONObject("broadcast_owner") + val user = User(userJson.getLong("pk"), + userJson.getString("username"), + userJson.optString("full_name"), + userJson.optBoolean("is_private"), + userJson.getString("profile_pic_url"), + userJson.optBoolean("is_verified") + ) + feedStoryModels.add(FeedStoryModel( + node.getString("id"), + user, + false, + node.getLong("published_time"), + ResponseBodyUtils.parseBroadcastItem(node), + 1, + isLive = true, + isBestie = false + )) } + return sort(feedStoryModels) } - public void fetchHighlights(final long profileId, - final ServiceCallback> callback) { - final Call request = repository.fetchHighlights(profileId); - request.enqueue(new Callback() { - @Override - public void onResponse(@NonNull final Call call, @NonNull final Response response) { - try { - if (callback == null) { - return; - } - final String body = response.body(); - if (TextUtils.isEmpty(body)) { - callback.onSuccess(null); - return; - } - final JSONArray highlightsReel = new JSONObject(body).getJSONArray("tray"); - - final int length = highlightsReel.length(); - final List highlightModels = new ArrayList<>(); - - for (int i = 0; i < length; ++i) { - final JSONObject highlightNode = highlightsReel.getJSONObject(i); - highlightModels.add(new HighlightModel( - highlightNode.getString("title"), - highlightNode.getString(Constants.EXTRAS_ID), - highlightNode.getJSONObject("cover_media") - .getJSONObject("cropped_image_version") - .getString("url"), - highlightNode.getLong("latest_reel_media"), - highlightNode.getInt("media_count") - )); - } - callback.onSuccess(highlightModels); - } catch (JSONException e) { - Log.e(TAG, "onResponse", e); - callback.onFailure(e); - } - } - - @Override - public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { - if (callback != null) { - callback.onFailure(t); - } - } - }); - } - - public void fetchArchive(final String maxId, - final ServiceCallback callback) { - final Map form = new HashMap<>(); - form.put("include_suggested_highlights", "false"); - form.put("is_in_archive_home", "true"); - form.put("include_cover", "1"); - if (!TextUtils.isEmpty(maxId)) { - form.put("max_id", maxId); // NOT TESTED + suspend fun fetchHighlights(profileId: Long): List { + val response = repository.fetchHighlights(profileId) + val highlightsReel = JSONObject(response).getJSONArray("tray") + val length = highlightsReel.length() + val highlightModels: MutableList = ArrayList() + for (i in 0 until length) { + val highlightNode = highlightsReel.getJSONObject(i) + highlightModels.add(HighlightModel( + highlightNode.getString("title"), + highlightNode.getString(Constants.EXTRAS_ID), + highlightNode.getJSONObject("cover_media") + .getJSONObject("cropped_image_version") + .getString("url"), + highlightNode.getLong("latest_reel_media"), + highlightNode.getInt("media_count") + )) } - final Call request = repository.fetchArchive(form); - request.enqueue(new Callback() { - @Override - public void onResponse(@NonNull final Call call, @NonNull final Response response) { - try { - if (callback == null) { - return; - } - final String body = response.body(); - if (TextUtils.isEmpty(body)) { - callback.onSuccess(null); - return; - } - final JSONObject data = new JSONObject(body); - final JSONArray highlightsReel = data.getJSONArray("items"); - - final int length = highlightsReel.length(); - final List highlightModels = new ArrayList<>(); - - for (int i = 0; i < length; ++i) { - final JSONObject highlightNode = highlightsReel.getJSONObject(i); - highlightModels.add(new HighlightModel( - null, - highlightNode.getString(Constants.EXTRAS_ID), - highlightNode.getJSONObject("cover_image_version").getString("url"), - highlightNode.getLong("latest_reel_media"), - highlightNode.getInt("media_count") - )); - } - callback.onSuccess(new ArchiveFetchResponse(highlightModels, - data.getBoolean("more_available"), - data.getString("max_id"))); - } catch (JSONException e) { - Log.e(TAG, "onResponse", e); - callback.onFailure(e); - } - } - - @Override - public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { - if (callback != null) { - callback.onFailure(t); - } - } - }); + return highlightModels } - public void getUserStory(final StoryViewerOptions options, - final ServiceCallback> callback) { - final String url = buildUrl(options); - final Call userStoryCall = repository.getUserStory(url); - final boolean isLocOrHashtag = options.getType() == StoryViewerOptions.Type.LOCATION || options.getType() == StoryViewerOptions.Type.HASHTAG; - final boolean isHighlight = options.getType() == StoryViewerOptions.Type.HIGHLIGHT || options - .getType() == StoryViewerOptions.Type.STORY_ARCHIVE; - userStoryCall.enqueue(new Callback() { - @Override - public void onResponse(@NonNull final Call call, @NonNull final Response response) { - JSONObject data; - try { - final String body = response.body(); - if (body == null) { - Log.e(TAG, "body is null"); - return; - } - data = new JSONObject(body); + suspend fun fetchArchive(maxId: String): ArchiveFetchResponse { + val form = mutableMapOf( + "include_suggested_highlights" to "false", + "is_in_archive_home" to "true", + "include_cover" to "1", + ) + if (!isEmpty(maxId)) { + form["max_id"] = maxId // NOT TESTED + } + val response = repository.fetchArchive(form) + val data = JSONObject(response) + val highlightsReel = data.getJSONArray("items") + val length = highlightsReel.length() + val highlightModels: MutableList = ArrayList() + for (i in 0 until length) { + val highlightNode = highlightsReel.getJSONObject(i) + highlightModels.add(HighlightModel( + null, + highlightNode.getString(Constants.EXTRAS_ID), + highlightNode.getJSONObject("cover_image_version").getString("url"), + highlightNode.getLong("latest_reel_media"), + highlightNode.getInt("media_count") + )) + } + return ArchiveFetchResponse(highlightModels, data.getBoolean("more_available"), data.getString("max_id")) + } - if (!isHighlight) { - data = data.optJSONObject((isLocOrHashtag) ? "story" : "reel"); - } else { - data = data.getJSONObject("reels").optJSONObject(options.getName()); - } - - String username = null; - if (data != null - // && localUsername == null - && !isLocOrHashtag) { - username = data.getJSONObject("user").getString("username"); - } - - JSONArray media; - if (data != null - && (media = data.optJSONArray("items")) != null - && media.length() > 0 && media.optJSONObject(0) != null) { - final int mediaLen = media.length(); - final List models = new ArrayList<>(); - for (int i = 0; i < mediaLen; ++i) { - data = media.getJSONObject(i); - models.add(ResponseBodyUtils.parseStoryItem(data, isLocOrHashtag, username)); - } - callback.onSuccess(models); - } else { - callback.onSuccess(null); - } - } catch (JSONException e) { - Log.e(TAG, "Error parsing string", e); - } + suspend fun getUserStory(options: StoryViewerOptions): List { + val url = buildUrl(options) ?: return emptyList() + val response = repository.getUserStory(url) + val isLocOrHashtag = options.type == StoryViewerOptions.Type.LOCATION || options.type == StoryViewerOptions.Type.HASHTAG + val isHighlight = options.type == StoryViewerOptions.Type.HIGHLIGHT || options.type == StoryViewerOptions.Type.STORY_ARCHIVE + var data: JSONObject? = JSONObject(response) + data = if (!isHighlight) { + data?.optJSONObject(if (isLocOrHashtag) "story" else "reel") + } else { + data?.getJSONObject("reels")?.optJSONObject(options.name) + } + var username: String? = null + if (data != null && !isLocOrHashtag) { + username = data.getJSONObject("user").getString("username") + } + val media: JSONArray? = data?.optJSONArray("items") + return if (media?.length() ?: 0 > 0 && media?.optJSONObject(0) != null) { + val mediaLen = media.length() + val models: MutableList = ArrayList() + for (i in 0 until mediaLen) { + data = media.getJSONObject(i) + models.add(ResponseBodyUtils.parseStoryItem(data, isLocOrHashtag, username)) } + models + } else emptyList() + } - @Override - public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { - callback.onFailure(t); + private suspend fun respondToSticker( + csrfToken: String, + userId: Long, + deviceUuid: String, + storyId: String, + stickerId: String, + action: String, + arg1: String, + arg2: String, + ): StoryStickerResponse { + val form = mapOf( + "_csrftoken" to csrfToken, + "_uid" to userId, + "_uuid" to deviceUuid, + "mutation_token" to UUID.randomUUID().toString(), + "client_context" to UUID.randomUUID().toString(), + "radio_type" to "wifi-none", + arg1 to arg2, + ) + val signedForm = Utils.sign(form) + return repository.respondToSticker(storyId, stickerId, action, signedForm) + } + + suspend fun respondToQuestion( + csrfToken: String, + userId: Long, + deviceUuid: String, + storyId: String, + stickerId: String, + answer: String, + ): StoryStickerResponse = respondToSticker(csrfToken, userId, deviceUuid, storyId, stickerId, "story_question_response", "response", answer) + + suspend fun respondToQuiz( + csrfToken: String, + userId: Long, + deviceUuid: String, + storyId: String, + stickerId: String, + answer: Int, + ): StoryStickerResponse { + return respondToSticker(csrfToken, userId, deviceUuid, storyId, stickerId, "story_quiz_answer", "answer", answer.toString()) + } + + suspend fun respondToPoll( + csrfToken: String, + userId: Long, + deviceUuid: String, + storyId: String, + stickerId: String, + answer: Int, + ): StoryStickerResponse = respondToSticker(csrfToken, userId, deviceUuid, storyId, stickerId, "story_poll_vote", "vote", answer.toString()) + + suspend fun respondToSlider( + csrfToken: String, + userId: Long, + deviceUuid: String, + storyId: String, + stickerId: String, + answer: Double, + ): StoryStickerResponse = respondToSticker(csrfToken, userId, deviceUuid, storyId, stickerId, "story_slider_vote", "vote", answer.toString()) + + suspend fun seen( + csrfToken: String, + userId: Long, + deviceUuid: String, + storyMediaId: String, + takenAt: Long, + seenAt: Long, + ): String { + val reelsForm = mapOf(storyMediaId to listOf(takenAt.toString() + "_" + seenAt)) + val form = mutableMapOf( + "_csrftoken" to csrfToken, + "_uid" to userId, + "_uuid" to deviceUuid, + "container_module" to "feed_timeline", + "reels" to reelsForm, + ) + val signedForm = Utils.sign(form) + val queryMap = mapOf( + "reel" to "1", + "live_vod" to "0", + ) + return repository.seen(queryMap, signedForm) + } + + private fun buildUrl(options: StoryViewerOptions): String? { + val builder = StringBuilder() + builder.append("https://i.instagram.com/api/v1/") + val type = options.type + var id: String? = null + when (type) { + StoryViewerOptions.Type.HASHTAG -> { + builder.append("tags/") + id = options.name } - }); - } - - private void respondToSticker(final String storyId, - final String stickerId, - final String action, - final String arg1, - final String arg2, - final ServiceCallback callback) { - final Map form = new HashMap<>(); - form.put("_csrftoken", csrfToken); - form.put("_uid", userId); - form.put("_uuid", deviceUuid); - form.put("mutation_token", UUID.randomUUID().toString()); - form.put("client_context", UUID.randomUUID().toString()); - form.put("radio_type", "wifi-none"); - form.put(arg1, arg2); - final Map signedForm = Utils.sign(form); - final Call request = - repository.respondToSticker(storyId, stickerId, action, signedForm); - request.enqueue(new Callback() { - @Override - public void onResponse(@NonNull final Call call, - @NonNull final Response response) { - if (callback != null) { - callback.onSuccess(response.body()); - } + StoryViewerOptions.Type.LOCATION -> { + builder.append("locations/") + id = options.id.toString() } - - @Override - public void onFailure(@NonNull final Call call, - @NonNull final Throwable t) { - if (callback != null) { - callback.onFailure(t); - } + StoryViewerOptions.Type.USER -> { + builder.append("feed/user/") + id = options.id.toString() } - }); - } - - // RespondAction.java - public void respondToQuestion(final String storyId, - final String stickerId, - final String answer, - final ServiceCallback callback) { - respondToSticker(storyId, stickerId, "story_question_response", "response", answer, callback); - } - - // QuizAction.java - public void respondToQuiz(final String storyId, - final String stickerId, - final int answer, - final ServiceCallback callback) { - respondToSticker(storyId, stickerId, "story_quiz_answer", "answer", String.valueOf(answer), callback); - } - - // VoteAction.java - public void respondToPoll(final String storyId, - final String stickerId, - final int answer, - final ServiceCallback callback) { - respondToSticker(storyId, stickerId, "story_poll_vote", "vote", String.valueOf(answer), callback); - } - - public void respondToSlider(final String storyId, - final String stickerId, - final double answer, - final ServiceCallback callback) { - respondToSticker(storyId, stickerId, "story_slider_vote", "vote", String.valueOf(answer), callback); - } - - public void seen(final String storyMediaId, - final long takenAt, - final long seenAt, - final ServiceCallback callback) { - final Map form = new HashMap<>(); - form.put("_csrftoken", csrfToken); - form.put("_uid", userId); - form.put("_uuid", deviceUuid); - form.put("container_module", "feed_timeline"); - final Map reelsForm = new HashMap<>(); - reelsForm.put(storyMediaId, Collections.singletonList(takenAt + "_" + seenAt)); - form.put("reels", reelsForm); - final Map signedForm = Utils.sign(form); - final Map queryMap = new HashMap<>(); - queryMap.put("reel", "1"); - queryMap.put("live_vod", "0"); - final Call request = repository.seen(queryMap, signedForm); - request.enqueue(new Callback() { - @Override - public void onResponse(@NonNull final Call call, - @NonNull final Response response) { - if (callback != null) { - callback.onSuccess(response.body()); - } + StoryViewerOptions.Type.HIGHLIGHT, StoryViewerOptions.Type.STORY_ARCHIVE -> { + builder.append("feed/reels_media/?user_ids=") + id = options.name } - - @Override - public void onFailure(@NonNull final Call call, - @NonNull final Throwable t) { - if (callback != null) { - callback.onFailure(t); - } + StoryViewerOptions.Type.STORY -> { + } + else -> { } - }); - } - - @Nullable - private String buildUrl(@NonNull final StoryViewerOptions options) { - final StringBuilder builder = new StringBuilder(); - builder.append("https://i.instagram.com/api/v1/"); - final StoryViewerOptions.Type type = options.getType(); - String id = null; - switch (type) { - case HASHTAG: - builder.append("tags/"); - id = options.getName(); - break; - case LOCATION: - builder.append("locations/"); - id = String.valueOf(options.getId()); - break; - case USER: - builder.append("feed/user/"); - id = String.valueOf(options.getId()); - break; - case HIGHLIGHT: - case STORY_ARCHIVE: - builder.append("feed/reels_media/?user_ids="); - id = options.getName(); - break; - case STORY: - break; - // case FEED_STORY_POSITION: - // break; } if (id == null) { - return null; + return null } - builder.append(id); + builder.append(id) if (type != StoryViewerOptions.Type.HIGHLIGHT && type != StoryViewerOptions.Type.STORY_ARCHIVE) { - builder.append("/story/"); + builder.append("/story/") } - return builder.toString(); + return builder.toString() } - private List sort(final List list) { - final List listCopy = new ArrayList<>(list); - Collections.sort(listCopy, (o1, o2) -> { - int result; - switch (Utils.settingsHelper.getString(PreferenceKeys.STORY_SORT)) { - case "1": - result = Long.compare(o2.getTimestamp(), o1.getTimestamp()); - break; - case "2": - result = Long.compare(o1.getTimestamp(), o2.getTimestamp()); - break; - default: - result = 0; + private fun sort(list: List): List { + val listCopy = ArrayList(list) + listCopy.sortWith { o1, o2 -> + when (Utils.settingsHelper.getString(PreferenceKeys.STORY_SORT)) { + "1" -> return@sortWith o2.timestamp.compareTo(o1.timestamp) + "2" -> return@sortWith o1.timestamp.compareTo(o2.timestamp) + else -> return@sortWith 0 } - return result; - }); - return listCopy; + } + return listCopy } - public static class ArchiveFetchResponse { - private final List archives; - private final boolean hasNextPage; - private final String nextCursor; - - public ArchiveFetchResponse(final List archives, final boolean hasNextPage, final String nextCursor) { - this.archives = archives; - this.hasNextPage = hasNextPage; - this.nextCursor = nextCursor; - } - - public List getResult() { - return archives; - } - - public boolean hasNextPage() { - return hasNextPage; - } - - public String getNextCursor() { - return nextCursor; + class ArchiveFetchResponse(val result: List, val hasNextPage: Boolean, val nextCursor: String) { + fun hasNextPage(): Boolean { + return hasNextPage } } -} +} \ No newline at end of file