From 8ebe8dd0f92ec88ad4b5e9e4b7c80b79fcc5fdbf Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Tue, 29 Dec 2020 17:33:44 -0500 Subject: [PATCH] story archive, so half of #460 --- .../adapters/FeedStoriesListAdapter.java | 42 +------ .../adapters/HighlightStoriesListAdapter.java | 6 +- .../viewholder/StoryListViewHolder.java | 28 +++-- .../fragments/HashTagFragment.java | 2 +- .../fragments/LikesViewerFragment.java | 2 +- .../fragments/LocationFragment.java | 2 +- .../fragments/StoryListViewerFragment.java | 115 +++++++++++++++--- .../fragments/StoryViewerFragment.java | 35 +++++- .../fragments/main/FeedFragment.java | 5 +- .../fragments/main/ProfileFragment.java | 4 +- .../instagrabber/models/FeedStoryModel.java | 10 +- .../instagrabber/models/HighlightModel.java | 9 +- .../awais/instagrabber/models/StoryModel.java | 7 +- .../repositories/StoriesRepository.java | 6 +- .../instagrabber/utils/ResponseBodyUtils.java | 9 +- .../viewmodels/ArchivesViewModel.java | 19 +++ .../webservices/StoriesService.java | 50 +++++--- .../main/res/navigation/feed_nav_graph.xml | 4 + .../main/res/navigation/hashtag_nav_graph.xml | 4 + .../res/navigation/location_nav_graph.xml | 4 + .../main/res/navigation/profile_nav_graph.xml | 4 + .../res/navigation/story_list_nav_graph.xml | 4 + app/src/main/res/values/strings.xml | 4 + 23 files changed, 270 insertions(+), 105 deletions(-) create mode 100644 app/src/main/java/awais/instagrabber/viewmodels/ArchivesViewModel.java diff --git a/app/src/main/java/awais/instagrabber/adapters/FeedStoriesListAdapter.java b/app/src/main/java/awais/instagrabber/adapters/FeedStoriesListAdapter.java index fb4584fa..6c54b587 100755 --- a/app/src/main/java/awais/instagrabber/adapters/FeedStoriesListAdapter.java +++ b/app/src/main/java/awais/instagrabber/adapters/FeedStoriesListAdapter.java @@ -15,7 +15,6 @@ import java.util.List; import awais.instagrabber.adapters.viewholder.StoryListViewHolder; import awais.instagrabber.databinding.ItemNotificationBinding; import awais.instagrabber.models.FeedStoryModel; -import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Utils; public final class FeedStoriesListAdapter extends ListAdapter { @@ -49,48 +48,11 @@ public final class FeedStoriesListAdapter extends ListAdapter list, @Nullable final Runnable commitCallback) { - if (list == null) { - super.submitList(null, commitCallback); - return; - } - super.submitList(sort(list), commitCallback); - } - - @Override - public void submitList(@Nullable final List list) { - if (list == null) { - super.submitList(null); - return; - } - super.submitList(sort(list)); - } - - private List sort(final List list) { - final List listCopy = new ArrayList<>(list); - Collections.sort(listCopy, (o1, o2) -> { - int result; - switch (Utils.settingsHelper.getString(Constants.STORY_SORT)) { - case "1": - result = o1.getTimestamp() > o2.getTimestamp() ? -1 : (o1.getTimestamp() == o2.getTimestamp() ? 0 : 1); - break; - case "2": - result = o1.getTimestamp() > o2.getTimestamp() ? 1 : (o1.getTimestamp() == o2.getTimestamp() ? 0 : -1); - break; - default: - result = 0; - } - return result; - }); - return listCopy; + holder.bind(model, position, listener); } public interface OnFeedStoryClickListener { - void onFeedStoryClick(final FeedStoryModel model); + void onFeedStoryClick(final FeedStoryModel model, final int position); void onProfileClick(final String username); } diff --git a/app/src/main/java/awais/instagrabber/adapters/HighlightStoriesListAdapter.java b/app/src/main/java/awais/instagrabber/adapters/HighlightStoriesListAdapter.java index d88ef28c..9547b9c1 100755 --- a/app/src/main/java/awais/instagrabber/adapters/HighlightStoriesListAdapter.java +++ b/app/src/main/java/awais/instagrabber/adapters/HighlightStoriesListAdapter.java @@ -42,12 +42,12 @@ public final class HighlightStoriesListAdapter extends ListAdapter { - if (notificationClickListener == null) return; - notificationClickListener.onFeedStoryClick(model); - }); + if (model.getFirstStoryModel() != null) { + binding.ivPreviewPic.setVisibility(View.VISIBLE); + binding.ivPreviewPic.setImageURI(model.getFirstStoryModel().getThumbnail()); + } + else binding.ivPreviewPic.setVisibility(View.INVISIBLE); itemView.setOnClickListener(v -> { if (notificationClickListener == null) return; - notificationClickListener.onFeedStoryClick(model); + notificationClickListener.onFeedStoryClick(model, position); }); } public void bind(final HighlightModel model, + final int position, final OnHighlightStoryClickListener notificationClickListener) { if (model == null) return; - binding.tvComment.setVisibility(View.GONE); + + final int storiesCount = model.getMediaCount(); + binding.tvComment.setVisibility(View.VISIBLE); + binding.tvComment.setText(itemView.getResources().getQuantityString(R.plurals.stories_count, storiesCount, storiesCount)); + binding.tvSubComment.setVisibility(View.GONE); binding.tvUsername.setText(model.getDateTime()); @@ -63,7 +73,7 @@ public final class StoryListViewHolder extends RecyclerView.ViewHolder { itemView.setOnClickListener(v -> { if (notificationClickListener == null) return; - notificationClickListener.onHighlightClick(model); + notificationClickListener.onHighlightClick(model, position); }); } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java index e44d21db..ac6df39d 100644 --- a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java @@ -545,7 +545,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe if (!hasStories) return; // show stories final NavDirections action = HashTagFragmentDirections - .actionHashtagFragmentToStoryViewerFragment(-1, null, true, false, hashtagModel.getName(), hashtagModel.getName()); + .actionHashtagFragmentToStoryViewerFragment(-1, null, true, false, hashtagModel.getName(), hashtagModel.getName(), false); NavHostFragment.findNavController(this).navigate(action); }); } diff --git a/app/src/main/java/awais/instagrabber/fragments/LikesViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/LikesViewerFragment.java index ddb93fb0..315bc038 100644 --- a/app/src/main/java/awais/instagrabber/fragments/LikesViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/LikesViewerFragment.java @@ -160,7 +160,7 @@ public final class LikesViewerFragment extends BottomSheetDialogFragment impleme if (isComment && !isLoggedIn) { lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> { if (!TextUtils.isEmpty(endCursor)) - graphQLService.fetchCommentLikers(postId, null, acb); + graphQLService.fetchCommentLikers(postId, endCursor, acb); endCursor = null; }); binding.rvLikes.addOnScrollListener(lazyLoader); diff --git a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java index 69ec1a82..55ba2b11 100644 --- a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java @@ -523,7 +523,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR if (hasStories) { // show stories final NavDirections action = LocationFragmentDirections - .actionLocationFragmentToStoryViewerFragment(-1, null, false, true, locationId, locationModel.getName()); + .actionLocationFragmentToStoryViewerFragment(-1, null, false, true, locationId, locationModel.getName(), false); NavHostFragment.findNavController(this).navigate(action); } }); diff --git a/app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java index 01a061fb..d35d62e0 100644 --- a/app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java @@ -2,12 +2,16 @@ package awais.instagrabber.fragments; import android.content.Context; import android.os.Bundle; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.navigation.NavDirections; @@ -15,34 +19,47 @@ import androidx.navigation.fragment.NavHostFragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; +import java.util.ArrayList; +import java.util.List; + +import awais.instagrabber.R; import awais.instagrabber.adapters.FeedStoriesListAdapter; import awais.instagrabber.adapters.FeedStoriesListAdapter.OnFeedStoryClickListener; +import awais.instagrabber.adapters.HighlightStoriesListAdapter; +import awais.instagrabber.adapters.HighlightStoriesListAdapter.OnHighlightStoryClickListener; +import awais.instagrabber.customviews.helpers.RecyclerLazyLoader; import awais.instagrabber.databinding.FragmentStoryListViewerBinding; +import awais.instagrabber.fragments.main.FeedFragment; import awais.instagrabber.fragments.settings.MorePreferencesFragmentDirections; import awais.instagrabber.models.FeedStoryModel; -import awais.instagrabber.utils.Constants; -import awais.instagrabber.viewmodels.StoriesViewModel; +import awais.instagrabber.models.HighlightModel; +import awais.instagrabber.utils.TextUtils; +import awais.instagrabber.viewmodels.FeedStoriesViewModel; +import awais.instagrabber.viewmodels.ArchivesViewModel; +import awais.instagrabber.webservices.ServiceCallback; import awais.instagrabber.webservices.StoriesService; - -import static awais.instagrabber.utils.Utils.settingsHelper; +import awais.instagrabber.webservices.StoriesService.ArchiveFetchResponse; public final class StoryListViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener { private static final String TAG = "StoryListViewerFragment"; + private AppCompatActivity fragmentActivity; private FragmentStoryListViewerBinding binding; private SwipeRefreshLayout root; private boolean shouldRefresh = true; - private StoriesViewModel storiesViewModel; + private FeedStoriesViewModel feedStoriesViewModel; + private ArchivesViewModel archivesViewModel; private StoriesService storiesService; private Context context; - private String type; + private String type, endCursor = null; + private RecyclerLazyLoader lazyLoader; private final OnFeedStoryClickListener clickListener = new OnFeedStoryClickListener() { @Override - public void onFeedStoryClick(final FeedStoryModel model) { + public void onFeedStoryClick(final FeedStoryModel model, final int position) { if (model == null) return; -// final NavDirections action = StoryListNavGraphDirections.actionStoryListFragmentToStoryViewerFragment(position, null, false, false, null, null); -// NavHostFragment.findNavController(StoryListViewerFragment.this).navigate(action); + final NavDirections action = StoryListViewerFragmentDirections.actionStoryListFragmentToStoryViewerFragment(position, null, false, false, null, null, false); + NavHostFragment.findNavController(StoryListViewerFragment.this).navigate(action); } @Override @@ -51,9 +68,47 @@ public final class StoryListViewerFragment extends Fragment implements SwipeRefr } }; + private final OnHighlightStoryClickListener archiveClickListener = new OnHighlightStoryClickListener() { + @Override + public void onHighlightClick(final HighlightModel model, final int position) { + if (model == null) return; + final NavDirections action = StoryListViewerFragmentDirections.actionStoryListFragmentToStoryViewerFragment( + position, getString(R.string.action_archive), false, false, null, null, true); + NavHostFragment.findNavController(StoryListViewerFragment.this).navigate(action); + } + + @Override + public void onProfileClick(final String username) { + openProfile(username); + } + }; + + private final ServiceCallback cb = new ServiceCallback() { + @Override + public void onSuccess(final ArchiveFetchResponse result) { + endCursor = result.getNextCursor(); + final List models = archivesViewModel.getList().getValue(); + final List modelsCopy = models == null ? new ArrayList<>() : new ArrayList<>(models); + modelsCopy.addAll(result.getResult()); + archivesViewModel.getList().postValue(modelsCopy); + binding.swipeRefreshLayout.setRefreshing(false); + } + + @Override + public void onFailure(final Throwable t) { + Log.e(TAG, "Error", t); + try { + final Context context = getContext(); + Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show(); + } + catch (Exception e) {} + } + }; + @Override public void onCreate(@Nullable final Bundle savedInstanceState) { super.onCreate(savedInstanceState); + fragmentActivity = (AppCompatActivity) requireActivity(); context = getContext(); if (context == null) return; storiesService = StoriesService.getInstance(); @@ -78,26 +133,54 @@ public final class StoryListViewerFragment extends Fragment implements SwipeRefr shouldRefresh = false; } + @Override + public void onDestroy() { + if (archivesViewModel != null) archivesViewModel.getList().postValue(null); + super.onDestroy(); + } + private void init() { final Context context = getContext(); if (getArguments() == null) return; final StoryListViewerFragmentArgs fragmentArgs = StoryListViewerFragmentArgs.fromBundle(getArguments()); type = fragmentArgs.getType(); binding.swipeRefreshLayout.setOnRefreshListener(this); - storiesViewModel = new ViewModelProvider(this).get(StoriesViewModel.class); -// final NotificationsAdapter adapter = new NotificationsAdapter(clickListener, mentionClickListener); - binding.rvStories.setLayoutManager(new LinearLayoutManager(context)); -// binding.rvStories.setAdapter(adapter); -// storiesViewModel.getList().observe(getViewLifecycleOwner(), adapter::submitList); + final LinearLayoutManager layoutManager = new LinearLayoutManager(context); + final ActionBar actionBar = fragmentActivity.getSupportActionBar(); + if (type == "feed") { + if (actionBar != null) actionBar.setTitle(R.string.feed_stories); + feedStoriesViewModel = new ViewModelProvider(this).get(FeedStoriesViewModel.class); + final FeedStoriesListAdapter adapter = new FeedStoriesListAdapter(clickListener); + binding.rvStories.setLayoutManager(layoutManager); + binding.rvStories.setAdapter(adapter); + feedStoriesViewModel.getList().observe(getViewLifecycleOwner(), adapter::submitList); + } + else { + if (actionBar != null) actionBar.setTitle(R.string.action_archive); + lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> { + if (!TextUtils.isEmpty(endCursor)) onRefresh(); + endCursor = null; + }); + binding.rvStories.addOnScrollListener(lazyLoader); + archivesViewModel = new ViewModelProvider(fragmentActivity).get(ArchivesViewModel.class); + final HighlightStoriesListAdapter adapter = new HighlightStoriesListAdapter(archiveClickListener); + binding.rvStories.setLayoutManager(layoutManager); + binding.rvStories.setAdapter(adapter); + archivesViewModel.getList().observe(getViewLifecycleOwner(), adapter::submitList); + } onRefresh(); } @Override public void onRefresh() { binding.swipeRefreshLayout.setRefreshing(true); - + if (type == "feed") { binding.swipeRefreshLayout.setRefreshing(false); -// storiesViewModel.getList().postValue(); + feedStoriesViewModel.getList().postValue(FeedFragment.feedStories); + } + else if (type == "archive") { + storiesService.fetchArchive(endCursor, cb); + } } private void openProfile(final String username) { diff --git a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java index 1c4235b8..2f513b07 100644 --- a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java @@ -91,6 +91,7 @@ import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.DownloadUtils; import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.Utils; +import awais.instagrabber.viewmodels.ArchivesViewModel; import awais.instagrabber.viewmodels.FeedStoriesViewModel; import awais.instagrabber.viewmodels.HighlightsViewModel; import awais.instagrabber.viewmodels.StoriesViewModel; @@ -138,7 +139,7 @@ public class StoryViewerFragment extends Fragment { private boolean shouldRefresh = true; private StoryViewerFragmentArgs fragmentArgs; private ViewModel viewModel; - private boolean isHighlight; + private boolean isHighlight, isArchive; private final String cookie = settingsHelper.getString(Constants.COOKIE); @@ -255,9 +256,12 @@ public class StoryViewerFragment extends Fragment { currentFeedStoryIndex = fragmentArgs.getFeedStoryIndex(); highlight = fragmentArgs.getHighlight(); isHighlight = !TextUtils.isEmpty(highlight); + isArchive = fragmentArgs.getIsArchive(); if (currentFeedStoryIndex >= 0) { viewModel = isHighlight - ? new ViewModelProvider(fragmentActivity).get(HighlightsViewModel.class) + ? isArchive + ? new ViewModelProvider(fragmentActivity).get(ArchivesViewModel.class) + : new ViewModelProvider(fragmentActivity).get(HighlightsViewModel.class) : new ViewModelProvider(fragmentActivity).get(FeedStoriesViewModel.class); } // feedStoryModels = feedStoriesViewModel.getList().getValue(); @@ -287,7 +291,11 @@ public class StoryViewerFragment extends Fragment { final boolean hasFeedStories; List models = null; if (currentFeedStoryIndex >= 0) { - if (isHighlight) { + if (isArchive) { + final ArchivesViewModel archivesViewModel = (ArchivesViewModel) viewModel; + models = archivesViewModel.getList().getValue(); + } + else if (isHighlight) { final HighlightsViewModel highlightsViewModel = (HighlightsViewModel) viewModel; models = highlightsViewModel.getList().getValue(); // final HighlightModel model = models.get(currentFeedStoryIndex); @@ -633,7 +641,18 @@ public class StoryViewerFragment extends Fragment { releasePlayer(); String currentStoryMediaId = null; if (currentFeedStoryIndex >= 0) { - if (isHighlight) { + if (isArchive) { + final ArchivesViewModel archivesViewModel = (ArchivesViewModel) viewModel; + final List models = archivesViewModel.getList().getValue(); + if (models == null || models.isEmpty() || currentFeedStoryIndex >= models.size()) { + Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); + return; + } + final HighlightModel model = models.get(currentFeedStoryIndex); + currentStoryMediaId = model.getId(); + currentStoryUsername = model.getTitle(); + } + else if (isHighlight) { final HighlightsViewModel highlightsViewModel = (HighlightsViewModel) viewModel; final List models = highlightsViewModel.getList().getValue(); if (models == null || models.isEmpty() || currentFeedStoryIndex >= models.size()) { @@ -658,7 +677,13 @@ public class StoryViewerFragment extends Fragment { isHashtag = fragmentArgs.getIsHashtag(); isLoc = fragmentArgs.getIsLoc(); final boolean hasUsername = !TextUtils.isEmpty(currentStoryUsername); - if (hasUsername) { + if (isHighlight) { + final ActionBar actionBar = fragmentActivity.getSupportActionBar(); + if (actionBar != null) { + actionBar.setTitle(highlight); + } + } + else if (hasUsername) { currentStoryUsername = currentStoryUsername.replace("@", ""); final ActionBar actionBar = fragmentActivity.getSupportActionBar(); if (actionBar != null) { 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 23993c82..5992e17d 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java @@ -78,6 +78,8 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre private PostsLayoutPreferences layoutPreferences = Utils.getPostsLayoutPreferences(Constants.PREF_POSTS_LAYOUT); private RecyclerView storiesRecyclerView; + public static List feedStories; + private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() { @Override public void onPostClick(final FeedModel feedModel, final View profilePicView, final View mainPostImage) { @@ -362,7 +364,7 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre new FeedStoriesAdapter.OnFeedStoryClickListener() { @Override public void onFeedStoryClick(FeedStoryModel model, int position) { - final NavDirections action = FeedFragmentDirections.actionFeedFragmentToStoryViewerFragment(position, null, false, false, null, null); + final NavDirections action = FeedFragmentDirections.actionFeedFragmentToStoryViewerFragment(position, null, false, false, null, null, false); NavHostFragment.findNavController(FeedFragment.this).navigate(action); } @@ -398,6 +400,7 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre @Override public void onSuccess(final List result) { feedStoriesViewModel.getList().postValue(result); + feedStories = result; storiesFetching = false; updateSwipeRefreshState(); } 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 615245d9..82c72607 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java @@ -1008,7 +1008,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe if (which == 1) { // show stories final NavDirections action = ProfileFragmentDirections - .actionProfileFragmentToStoryViewerFragment(-1, null, false, false, profileModel.getId(), username); + .actionProfileFragmentToStoryViewerFragment(-1, null, false, false, profileModel.getId(), username, false); NavHostFragment.findNavController(this).navigate(action); return; } @@ -1069,7 +1069,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe highlightsViewModel = new ViewModelProvider(fragmentActivity).get(HighlightsViewModel.class); highlightsAdapter = new HighlightsAdapter((model, position) -> { final NavDirections action = ProfileFragmentDirections - .actionProfileFragmentToStoryViewerFragment(position, model.getTitle(), false, false, null, null); + .actionProfileFragmentToStoryViewerFragment(position, model.getTitle(), false, false, null, null, false); NavHostFragment.findNavController(this).navigate(action); }); final Context context = getContext(); diff --git a/app/src/main/java/awais/instagrabber/models/FeedStoryModel.java b/app/src/main/java/awais/instagrabber/models/FeedStoryModel.java index 242af21f..01ffac76 100755 --- a/app/src/main/java/awais/instagrabber/models/FeedStoryModel.java +++ b/app/src/main/java/awais/instagrabber/models/FeedStoryModel.java @@ -15,14 +15,16 @@ public final class FeedStoryModel implements Serializable { private final StoryModel firstStoryModel; private Boolean fullyRead; private final long timestamp; + private final int mediaCount; - public FeedStoryModel(final String storyMediaId, final ProfileModel profileModel, - final boolean fullyRead, final long timestamp, final StoryModel firstStoryModel) { + public FeedStoryModel(final String storyMediaId, final ProfileModel profileModel, final boolean fullyRead, + final long timestamp, final StoryModel firstStoryModel, final int mediaCount) { this.storyMediaId = storyMediaId; this.profileModel = profileModel; this.fullyRead = fullyRead; this.timestamp = timestamp; this.firstStoryModel = firstStoryModel; + this.mediaCount = mediaCount; } public String getStoryMediaId() { @@ -38,6 +40,10 @@ public final class FeedStoryModel implements Serializable { return Utils.datetimeParser.format(new Date(timestamp * 1000L)); } + public int getMediaCount() { + return mediaCount; + } + public ProfileModel getProfileModel() { return profileModel; } diff --git a/app/src/main/java/awais/instagrabber/models/HighlightModel.java b/app/src/main/java/awais/instagrabber/models/HighlightModel.java index 741d9d47..86d0327d 100755 --- a/app/src/main/java/awais/instagrabber/models/HighlightModel.java +++ b/app/src/main/java/awais/instagrabber/models/HighlightModel.java @@ -11,15 +11,18 @@ public final class HighlightModel { private final String id; private final String thumbnailUrl; private final long timestamp; + private final int mediaCount; public HighlightModel(final String title, final String id, final String thumbnailUrl, - final long timestamp) { + final long timestamp, + final int mediaCount) { this.title = title; this.id = id; this.thumbnailUrl = thumbnailUrl; this.timestamp = timestamp; + this.mediaCount = mediaCount; } public String getTitle() { @@ -42,4 +45,8 @@ public final class HighlightModel { public String getDateTime() { return Utils.datetimeParser.format(new Date(timestamp * 1000L)); } + + public int getMediaCount() { + return mediaCount; + } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/models/StoryModel.java b/app/src/main/java/awais/instagrabber/models/StoryModel.java index d535a212..80b451c8 100755 --- a/app/src/main/java/awais/instagrabber/models/StoryModel.java +++ b/app/src/main/java/awais/instagrabber/models/StoryModel.java @@ -11,7 +11,8 @@ import awais.instagrabber.models.stickers.SwipeUpModel; public final class StoryModel implements Serializable { private final String storyMediaId; - private final String storyUrl, thumbnail; + private final String storyUrl; + private String thumbnail; private final String username; private final String userId; private final MediaItemType itemType; @@ -96,6 +97,10 @@ public final class StoryModel implements Serializable { return position; } + public void setThumbnail(final String thumbnail) { + this.thumbnail = thumbnail; + } + public void setVideoUrl(final String videoUrl) { this.videoUrl = videoUrl; } diff --git a/app/src/main/java/awais/instagrabber/repositories/StoriesRepository.java b/app/src/main/java/awais/instagrabber/repositories/StoriesRepository.java index 945b76c2..139ba352 100644 --- a/app/src/main/java/awais/instagrabber/repositories/StoriesRepository.java +++ b/app/src/main/java/awais/instagrabber/repositories/StoriesRepository.java @@ -18,10 +18,8 @@ public interface StoriesRepository { Call fetch(@Path("mediaId") final String mediaId); // this one is the same as MediaRepository.fetch BUT you need to make sure it's a story - @FormUrlEncoded - @POST("/api/v1/feed/reels_tray/") - Call getFeedStories(@Header("User-Agent") String userAgent, - @FieldMap Map form); + @GET("/api/v1/feed/reels_tray/") + Call getFeedStories(); @GET("/api/v1/highlights/{uid}/highlights_tray/") Call fetchHighlights(@Path("uid") final String uid); diff --git a/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java b/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java index 5ebef48b..9ae87dbf 100644 --- a/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java +++ b/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java @@ -889,9 +889,7 @@ public final class ResponseBodyUtils { final boolean isVideo = data.has("video_duration"); final StoryModel model = new StoryModel(data.getString("id"), data.getJSONObject("image_versions2").getJSONArray("candidates").getJSONObject(0) - .getString("url"), - data.getJSONObject("image_versions2").getJSONArray("candidates").getJSONObject(1) - .getString("url"), + .getString("url"), null, isVideo ? MediaItemType.MEDIA_TYPE_VIDEO : MediaItemType.MEDIA_TYPE_IMAGE, data.optLong("taken_at", 0), (isLoc || isHashtag) @@ -900,6 +898,11 @@ public final class ResponseBodyUtils { data.getJSONObject("user").getString("pk"), data.optBoolean("can_reply")); + if (data.getJSONObject("image_versions2").getJSONArray("candidates").length() > 1) { + model.setThumbnail(data.getJSONObject("image_versions2").getJSONArray("candidates").getJSONObject(1) + .getString("url")); + } + final JSONArray videoResources = data.optJSONArray("video_versions"); if (isVideo && videoResources != null) model.setVideoUrl(ResponseBodyUtils.getHighQualityPost(videoResources, true, true, false)); diff --git a/app/src/main/java/awais/instagrabber/viewmodels/ArchivesViewModel.java b/app/src/main/java/awais/instagrabber/viewmodels/ArchivesViewModel.java new file mode 100644 index 00000000..18802826 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/viewmodels/ArchivesViewModel.java @@ -0,0 +1,19 @@ +package awais.instagrabber.viewmodels; + +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; + +import java.util.List; + +import awais.instagrabber.models.HighlightModel; + +public class ArchivesViewModel extends ViewModel { + private MutableLiveData> list; + + public MutableLiveData> getList() { + if (list == null) { + list = new MutableLiveData<>(); + } + return list; + } +} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/webservices/StoriesService.java b/app/src/main/java/awais/instagrabber/webservices/StoriesService.java index f77cdee3..d3ad3294 100644 --- a/app/src/main/java/awais/instagrabber/webservices/StoriesService.java +++ b/app/src/main/java/awais/instagrabber/webservices/StoriesService.java @@ -9,6 +9,7 @@ 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; @@ -84,13 +85,7 @@ public class StoriesService extends BaseService { } public void getFeedStories(final String csrfToken, final ServiceCallback> callback) { - final Map form = new HashMap<>(4); - form.put("reason", "cold_start"); - form.put("_csrftoken", csrfToken); - form.put("_uuid", UUID.randomUUID().toString()); - form.put("supported_capabilities_new", Constants.SUPPORTED_CAPABILITIES); - final Map signedForm = Utils.sign(form); - final Call response = repository.getFeedStories(Constants.I_USER_AGENT, signedForm); + final Call response = repository.getFeedStories(); response.enqueue(new Callback() { @Override public void onResponse(@NonNull final Call call, @NonNull final Response response) { @@ -124,12 +119,17 @@ public class StoriesService extends BaseService { null, 0, 0, 0, false, false, false, false, false); final String id = node.getString("id"); final long timestamp = node.getLong("latest_reel_media"); + final int mediaCount = node.getInt("media_count"); final boolean fullyRead = !node.isNull("seen") && node.getLong("seen") == timestamp; - final JSONObject itemJson = node.getJSONArray("items").getJSONObject(0); - final StoryModel firstStoryModel = ResponseBodyUtils.parseStoryItem(itemJson, false, false, null); - feedStoryModels.add(new FeedStoryModel(id, profileModel, fullyRead, timestamp, firstStoryModel)); + final JSONObject itemJson = node.has("items") ? node.getJSONArray("items").getJSONObject(0) : null; + StoryModel firstStoryModel = null; + if (itemJson != null) { + firstStoryModel = ResponseBodyUtils.parseStoryItem(itemJson, false, false, null); + } + else Log.d("austin_debug", "node: "+node); + feedStoryModels.add(new FeedStoryModel(id, profileModel, fullyRead, timestamp, firstStoryModel, mediaCount)); } - callback.onSuccess(feedStoryModels); + callback.onSuccess(sort(feedStoryModels)); } catch (JSONException e) { Log.e(TAG, "Error parsing json", e); } @@ -163,7 +163,8 @@ public class StoriesService extends BaseService { highlightNode.getJSONObject("cover_media") .getJSONObject("cropped_image_version") .getString("url"), - highlightNode.getLong("latest_reel_media") + highlightNode.getLong("latest_reel_media"), + highlightNode.getInt("media_count") )); } callback.onSuccess(highlightModels); @@ -217,7 +218,8 @@ public class StoriesService extends BaseService { null, highlightNode.getString(Constants.EXTRAS_ID), highlightNode.getJSONObject("cover_image_version").getString("url"), - highlightNode.getLong("timestamp") + highlightNode.getLong("timestamp"), + highlightNode.getInt("media_count") )); } callback.onSuccess(new ArchiveFetchResponse(highlightModels, @@ -393,6 +395,25 @@ public class StoriesService extends BaseService { 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(Constants.STORY_SORT)) { + case "1": + result = o1.getTimestamp() > o2.getTimestamp() ? -1 : (o1.getTimestamp() == o2.getTimestamp() ? 0 : 1); + break; + case "2": + result = o1.getTimestamp() > o2.getTimestamp() ? 1 : (o1.getTimestamp() == o2.getTimestamp() ? 0 : -1); + break; + default: + result = 0; + } + return result; + }); + return listCopy; + } + public class ArchiveFetchResponse { private final List archives; private final boolean hasNextPage; @@ -404,7 +425,7 @@ public class StoriesService extends BaseService { this.nextCursor = nextCursor; } - public List getArchives() { + public List getResult() { return archives; } @@ -416,5 +437,4 @@ public class StoriesService extends BaseService { return nextCursor; } } - } diff --git a/app/src/main/res/navigation/feed_nav_graph.xml b/app/src/main/res/navigation/feed_nav_graph.xml index 23db2806..25bb47f9 100644 --- a/app/src/main/res/navigation/feed_nav_graph.xml +++ b/app/src/main/res/navigation/feed_nav_graph.xml @@ -125,5 +125,9 @@ android:name="username" app:argType="string" app:nullable="true" /> + \ No newline at end of file diff --git a/app/src/main/res/navigation/hashtag_nav_graph.xml b/app/src/main/res/navigation/hashtag_nav_graph.xml index dab927b9..633d0cd0 100644 --- a/app/src/main/res/navigation/hashtag_nav_graph.xml +++ b/app/src/main/res/navigation/hashtag_nav_graph.xml @@ -97,6 +97,10 @@ android:name="username" app:argType="string" app:nullable="true" /> + + \ No newline at end of file diff --git a/app/src/main/res/navigation/profile_nav_graph.xml b/app/src/main/res/navigation/profile_nav_graph.xml index 857331db..808b9396 100644 --- a/app/src/main/res/navigation/profile_nav_graph.xml +++ b/app/src/main/res/navigation/profile_nav_graph.xml @@ -174,6 +174,10 @@ android:name="username" app:argType="string" app:nullable="true" /> + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cc45796e..dad71d71 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -347,5 +347,9 @@ %d comment %d comments + + %s story + %s stories + Storage permission not granted!