mirror of
				https://github.com/KokaKiwi/BarInsta
				synced 2025-10-31 11:35:34 +00:00 
			
		
		
		
	
							parent
							
								
									53b0301385
								
							
						
					
					
						commit
						4d9494cbcf
					
				| @ -10,20 +10,21 @@ import androidx.recyclerview.widget.ListAdapter; | ||||
| import androidx.recyclerview.widget.RecyclerView; | ||||
| 
 | ||||
| import awais.instagrabber.databinding.ItemStoryBinding; | ||||
| import awais.instagrabber.models.StoryModel; | ||||
| import awais.instagrabber.repositories.responses.stories.StoryMedia; | ||||
| import awais.instagrabber.utils.ResponseBodyUtils; | ||||
| 
 | ||||
| public final class StoriesAdapter extends ListAdapter<StoryModel, StoriesAdapter.StoryViewHolder> { | ||||
| public final class StoriesAdapter extends ListAdapter<StoryMedia, StoriesAdapter.StoryViewHolder> { | ||||
|     private final OnItemClickListener onItemClickListener; | ||||
| 
 | ||||
|     private static final DiffUtil.ItemCallback<StoryModel> diffCallback = new DiffUtil.ItemCallback<StoryModel>() { | ||||
|     private static final DiffUtil.ItemCallback<StoryMedia> diffCallback = new DiffUtil.ItemCallback<StoryMedia>() { | ||||
|         @Override | ||||
|         public boolean areItemsTheSame(@NonNull final StoryModel oldItem, @NonNull final StoryModel newItem) { | ||||
|             return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId()); | ||||
|         public boolean areItemsTheSame(@NonNull final StoryMedia oldItem, @NonNull final StoryMedia newItem) { | ||||
|             return oldItem.getId().equals(newItem.getId()); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public boolean areContentsTheSame(@NonNull final StoryModel oldItem, @NonNull final StoryModel newItem) { | ||||
|             return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId()); | ||||
|         public boolean areContentsTheSame(@NonNull final StoryMedia oldItem, @NonNull final StoryMedia newItem) { | ||||
|             return oldItem.getId().equals(newItem.getId()); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
| @ -42,8 +43,8 @@ public final class StoriesAdapter extends ListAdapter<StoryModel, StoriesAdapter | ||||
| 
 | ||||
|     @Override | ||||
|     public void onBindViewHolder(@NonNull final StoryViewHolder holder, final int position) { | ||||
|         final StoryModel storyModel = getItem(position); | ||||
|         holder.bind(storyModel, position, onItemClickListener); | ||||
|         final StoryMedia storyMedia = getItem(position); | ||||
|         holder.bind(storyMedia, position, onItemClickListener); | ||||
|     } | ||||
| 
 | ||||
|     public final static class StoryViewHolder extends RecyclerView.ViewHolder { | ||||
| @ -54,7 +55,7 @@ public final class StoriesAdapter extends ListAdapter<StoryModel, StoriesAdapter | ||||
|             this.binding = binding; | ||||
|         } | ||||
| 
 | ||||
|         public void bind(final StoryModel model, | ||||
|         public void bind(final StoryMedia model, | ||||
|                          final int position, | ||||
|                          final OnItemClickListener clickListener) { | ||||
|             if (model == null) return; | ||||
| @ -67,14 +68,11 @@ public final class StoriesAdapter extends ListAdapter<StoryModel, StoriesAdapter | ||||
|             }); | ||||
| 
 | ||||
|             binding.selectedView.setVisibility(model.isCurrentSlide() ? View.VISIBLE : View.GONE); | ||||
|             binding.icon.setImageURI(model.getStoryUrl()); | ||||
|             // Glide.with(itemView).load(model.getStoryUrl()) | ||||
|             //      .apply(new RequestOptions().override(width, height)) | ||||
|             //      .into(holder.icon); | ||||
|             binding.icon.setImageURI(ResponseBodyUtils.getThumbUrl(model)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public interface OnItemClickListener { | ||||
|         void onItemClick(StoryModel storyModel, int position); | ||||
|         void onItemClick(StoryMedia storyModel, int position); | ||||
|     } | ||||
| } | ||||
| @ -548,7 +548,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe | ||||
| //    private void fetchStories() { | ||||
| //        if (!isLoggedIn) return; | ||||
| //        storiesFetching = true; | ||||
| //        storiesRepository.getUserStory( | ||||
| //        storiesRepository.getStories( | ||||
| //                StoryViewerOptions.forHashtag(hashtagModel.getName()), | ||||
| //                CoroutineUtilsKt.getContinuation((storyModels, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { | ||||
| //                    if (throwable != null) { | ||||
|  | ||||
| @ -552,7 +552,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR | ||||
| //    private void fetchStories() { | ||||
| //        if (isLoggedIn) { | ||||
| //            storiesFetching = true; | ||||
| //            storiesRepository.getUserStory( | ||||
| //            storiesRepository.getStories( | ||||
| //                    StoryViewerOptions.forLocation(locationId, locationModel.getName()), | ||||
| //                    CoroutineUtilsKt.getContinuation((storyModels, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { | ||||
| //                        if (throwable != null) { | ||||
|  | ||||
| @ -58,8 +58,10 @@ import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| import java.text.NumberFormat; | ||||
| import java.util.Arrays; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.stream.Collectors; | ||||
| import java.util.List; | ||||
| import java.util.regex.Matcher; | ||||
| import java.util.regex.Pattern; | ||||
| @ -72,23 +74,17 @@ import awais.instagrabber.databinding.FragmentStoryViewerBinding; | ||||
| import awais.instagrabber.fragments.main.ProfileFragmentDirections; | ||||
| import awais.instagrabber.fragments.settings.PreferenceKeys; | ||||
| import awais.instagrabber.interfaces.SwipeEvent; | ||||
| import awais.instagrabber.models.StoryModel; | ||||
| import awais.instagrabber.models.enums.MediaItemType; | ||||
| import awais.instagrabber.models.stickers.PollModel; | ||||
| import awais.instagrabber.models.stickers.QuestionModel; | ||||
| import awais.instagrabber.models.stickers.QuizModel; | ||||
| import awais.instagrabber.models.stickers.SliderModel; | ||||
| 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.ThreadIdsOrUserIds; | ||||
| import awais.instagrabber.repositories.responses.stories.Broadcast; | ||||
| import awais.instagrabber.repositories.responses.stories.Story; | ||||
| import awais.instagrabber.repositories.responses.stories.*; | ||||
| import awais.instagrabber.utils.AppExecutors; | ||||
| import awais.instagrabber.utils.Constants; | ||||
| import awais.instagrabber.utils.CookieUtils; | ||||
| import awais.instagrabber.utils.CoroutineUtilsKt; | ||||
| import awais.instagrabber.utils.DownloadUtils; | ||||
| import awais.instagrabber.utils.ResponseBodyUtils; | ||||
| import awais.instagrabber.utils.TextUtils; | ||||
| import awais.instagrabber.utils.Utils; | ||||
| import awais.instagrabber.viewmodels.ArchivesViewModel; | ||||
| @ -121,16 +117,16 @@ public class StoryViewerFragment extends Fragment { | ||||
|     private GestureDetectorCompat gestureDetector; | ||||
|     private StoriesRepository storiesRepository; | ||||
|     private MediaRepository mediaRepository; | ||||
|     private StoryModel currentStory; | ||||
|     private StoryMedia currentStory; | ||||
|     private Broadcast live; | ||||
|     private int slidePos; | ||||
|     private int lastSlidePos; | ||||
|     private String url; | ||||
|     private PollModel poll; | ||||
|     private QuestionModel question; | ||||
|     private String[] mentions; | ||||
|     private QuizModel quiz; | ||||
|     private SliderModel slider; | ||||
|     private PollSticker poll; | ||||
|     private QuestionSticker question; | ||||
|     private List<String> mentions = new ArrayList<String>(); | ||||
|     private QuizSticker quiz; | ||||
|     private SliderSticker slider; | ||||
|     private MenuItem menuDownload, menuDm, menuProfile; | ||||
|     private SimpleExoPlayer player; | ||||
|     // private boolean isHashtag; | ||||
| @ -220,10 +216,10 @@ public class StoryViewerFragment extends Fragment { | ||||
|                                         csrfToken, | ||||
|                                         userId, | ||||
|                                         deviceId, | ||||
|                                         ThreadIdsOrUserIds.Companion.ofOneUser(String.valueOf(currentStory.getUserId())), | ||||
|                                         ThreadIdsOrUserIds.Companion.ofOneUser(String.valueOf(currentStory.getUser().getPk())), | ||||
|                                         input.getText().toString(), | ||||
|                                         currentStory.getStoryMediaId(), | ||||
|                                         String.valueOf(currentStory.getUserId()), | ||||
|                                         currentStory.getId(), | ||||
|                                         String.valueOf(currentStory.getUser().getPk()), | ||||
|                                         CoroutineUtilsKt.getContinuation( | ||||
|                                                 (directThreadBroadcastResponse, throwable1) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { | ||||
|                                                     if (throwable1 != null) { | ||||
| @ -253,7 +249,7 @@ public class StoryViewerFragment extends Fragment { | ||||
|             return true; | ||||
|         } | ||||
|         if (itemId == R.id.action_profile) { | ||||
|             openProfile("@" + currentStory.getUsername()); | ||||
|             openProfile("@" + currentStory.getUser().getPk()); | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| @ -357,7 +353,7 @@ public class StoryViewerFragment extends Fragment { | ||||
|         final Context context = getContext(); | ||||
|         if (context == null) return; | ||||
|         swipeEvent = isRightSwipe -> { | ||||
|             final List<StoryModel> storyModels = storiesViewModel.getList().getValue(); | ||||
|             final List<StoryMedia> storyModels = storiesViewModel.getList().getValue(); | ||||
|             final int storiesLen = storyModels == null ? 0 : storyModels.size(); | ||||
|             if (sticking) { | ||||
|                 Toast.makeText(context, R.string.follower_wait_to_load, Toast.LENGTH_SHORT).show(); | ||||
| @ -373,12 +369,14 @@ public class StoryViewerFragment extends Fragment { | ||||
|                     Toast.makeText(context, R.string.no_more_stories, Toast.LENGTH_SHORT).show(); | ||||
|                     return; | ||||
|                 } | ||||
|                 removeStickers(); | ||||
|                 final Object feedStoryModel = isRightSwipe | ||||
|                                               ? finalModels.get(index - 1) | ||||
|                                               : finalModels.size() == index + 1 ? null : finalModels.get(index + 1); | ||||
|                 paginateStories(feedStoryModel, finalModels.get(index), context, isRightSwipe, currentFeedStoryIndex == finalModels.size() - 2); | ||||
|                 return; | ||||
|             } | ||||
|             removeStickers(); | ||||
|             if (isRightSwipe) { | ||||
|                 if (--slidePos <= 0) { | ||||
|                     slidePos = 0; | ||||
| @ -471,35 +469,31 @@ public class StoryViewerFragment extends Fragment { | ||||
|         }); | ||||
|         final View.OnClickListener storyActionListener = v -> { | ||||
|             final Object tag = v.getTag(); | ||||
|             if (tag instanceof PollModel) { | ||||
|                 poll = (PollModel) tag; | ||||
|                 if (poll.getMyChoice() > -1) { | ||||
|             if (tag instanceof PollSticker) { | ||||
|                 poll = (PollSticker) tag; | ||||
|                 final List<Tally> tallies = poll.getTallies(); | ||||
|                 final String[] choices = tallies.stream() | ||||
|                         .map(t -> (poll.getViewerVote() == tallies.indexOf(t) ? "√ " : "") | ||||
|                                 + t.getText() + " (" + t.getCount() + ")" ) | ||||
|                         .toArray(String[]::new); | ||||
|                 final ArrayAdapter adapter = new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, choices); | ||||
|                 if (poll.getViewerVote() > -1) { | ||||
|                     new AlertDialog.Builder(context) | ||||
|                             .setTitle(R.string.voted_story_poll) | ||||
|                             .setAdapter(new ArrayAdapter<>( | ||||
|                                                 context, | ||||
|                                                 android.R.layout.simple_list_item_1, | ||||
|                                                 new String[]{ | ||||
|                                                         (poll.getMyChoice() == 0 ? "√ " : "") + poll.getLeftChoice() + " (" + poll.getLeftCount() + ")", | ||||
|                                                         (poll.getMyChoice() == 1 ? "√ " : "") + poll.getRightChoice() + " (" + poll.getRightCount() + ")" | ||||
|                                                 }), | ||||
|                                         null) | ||||
|                             .setAdapter(adapter, null) | ||||
|                             .setPositiveButton(R.string.ok, null) | ||||
|                             .show(); | ||||
|                 } else { | ||||
|                     new AlertDialog.Builder(context) | ||||
|                             .setTitle(poll.getQuestion()) | ||||
|                             .setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, new String[]{ | ||||
|                                     poll.getLeftChoice() + " (" + poll.getLeftCount() + ")", | ||||
|                                     poll.getRightChoice() + " (" + poll.getRightCount() + ")" | ||||
|                             }), (d, w) -> { | ||||
|                             .setAdapter(adapter, (d, w) -> { | ||||
|                                 sticking = true; | ||||
|                                 storiesRepository.respondToPoll( | ||||
|                                         csrfToken, | ||||
|                                         userId, | ||||
|                                         deviceId, | ||||
|                                         currentStory.getStoryMediaId().split("_")[0], | ||||
|                                         poll.getId(), | ||||
|                                         currentStory.getId().split("_")[0], | ||||
|                                         poll.getPollId(), | ||||
|                                         w, | ||||
|                                         CoroutineUtilsKt.getContinuation( | ||||
|                                                 (storyStickerResponse, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { | ||||
| @ -513,7 +507,7 @@ public class StoryViewerFragment extends Fragment { | ||||
|                                                     } | ||||
|                                                     sticking = false; | ||||
|                                                     try { | ||||
|                                                         poll.setMyChoice(w); | ||||
|                                                         poll.setViewerVote(w); | ||||
|                                                         Toast.makeText(context, R.string.votef_story_poll, Toast.LENGTH_SHORT).show(); | ||||
|                                                     } catch (Exception ignored) {} | ||||
|                                                 }), | ||||
| @ -524,8 +518,8 @@ public class StoryViewerFragment extends Fragment { | ||||
|                             .setPositiveButton(R.string.cancel, null) | ||||
|                             .show(); | ||||
|                 } | ||||
|             } else if (tag instanceof QuestionModel) { | ||||
|                 question = (QuestionModel) tag; | ||||
|             } else if (tag instanceof QuestionSticker) { | ||||
|                 question = (QuestionSticker) tag; | ||||
|                 final EditText input = new EditText(context); | ||||
|                 input.setHint(R.string.answer_hint); | ||||
|                 final AlertDialog ad = new AlertDialog.Builder(context) | ||||
| @ -537,8 +531,8 @@ public class StoryViewerFragment extends Fragment { | ||||
|                                     csrfToken, | ||||
|                                     userId, | ||||
|                                     deviceId, | ||||
|                                     currentStory.getStoryMediaId().split("_")[0], | ||||
|                                     question.getId(), | ||||
|                                     currentStory.getId().split("_")[0], | ||||
|                                     question.getQuestionId(), | ||||
|                                     input.getText().toString(), | ||||
|                                     CoroutineUtilsKt.getContinuation( | ||||
|                                             (storyStickerResponse, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { | ||||
| @ -575,28 +569,31 @@ public class StoryViewerFragment extends Fragment { | ||||
|                     public void afterTextChanged(final Editable s) {} | ||||
|                 }); | ||||
|             } else if (tag instanceof String[]) { | ||||
|                 mentions = (String[]) tag; | ||||
|                 final String[] rawMentions = (String[]) tag; | ||||
|                 mentions = new ArrayList<String>(Arrays.asList(rawMentions)); | ||||
|                 new AlertDialog.Builder(context) | ||||
|                         .setTitle(R.string.story_mentions) | ||||
|                         .setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, mentions), (d, w) -> openProfile(mentions[w])) | ||||
|                         .setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, rawMentions), (d, w) -> openProfile(mentions.get(w))) | ||||
|                         .setPositiveButton(R.string.cancel, null) | ||||
|                         .show(); | ||||
|             } else if (tag instanceof QuizModel) { | ||||
|                 String[] choices = new String[quiz.getChoices().length]; | ||||
|                 for (int q = 0; q < choices.length; ++q) { | ||||
|                     choices[q] = (quiz.getMyChoice() == q ? "√ " : "") + quiz.getChoices()[q] + " (" + quiz.getCounts()[q] + ")"; | ||||
|                 } | ||||
|             } else if (tag instanceof QuizSticker) { | ||||
|                 final List<Tally> tallies = quiz.getTallies(); | ||||
|                 final String[] choices = tallies.stream().map( | ||||
|                         t -> (quiz.getViewerAnswer() == tallies.indexOf(t) ? "√ " : "") + | ||||
|                                 (quiz.getCorrectAnswer() == tallies.indexOf(t) ? "*** " : "") + | ||||
|                                 t.getText() + " (" + t.getCount() + ")" | ||||
|                 ).toArray(String[]::new); | ||||
|                 new AlertDialog.Builder(context) | ||||
|                         .setTitle(quiz.getMyChoice() > -1 ? getString(R.string.story_quizzed) : quiz.getQuestion()) | ||||
|                         .setTitle(quiz.getViewerAnswer() > -1 ? getString(R.string.story_quizzed) : quiz.getQuestion()) | ||||
|                         .setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, choices), (d, w) -> { | ||||
|                             if (quiz.getMyChoice() == -1) { | ||||
|                             if (quiz.getViewerAnswer() == -1) { | ||||
|                                 sticking = true; | ||||
|                                 storiesRepository.respondToQuiz( | ||||
|                                         csrfToken, | ||||
|                                         userId, | ||||
|                                         deviceId, | ||||
|                                         currentStory.getStoryMediaId().split("_")[0], | ||||
|                                         quiz.getId(), | ||||
|                                         currentStory.getId().split("_")[0], | ||||
|                                         quiz.getQuizId(), | ||||
|                                         w, | ||||
|                                         CoroutineUtilsKt.getContinuation( | ||||
|                                                 (storyStickerResponse, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { | ||||
| @ -610,7 +607,7 @@ public class StoryViewerFragment extends Fragment { | ||||
|                                                     } | ||||
|                                                     sticking = false; | ||||
|                                                     try { | ||||
|                                                         quiz.setMyChoice(w); | ||||
|                                                         quiz.setViewerAnswer(w); | ||||
|                                                         Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT).show(); | ||||
|                                                     } catch (Exception ignored) {} | ||||
|                                                 }), | ||||
| @ -621,8 +618,8 @@ public class StoryViewerFragment extends Fragment { | ||||
|                         }) | ||||
|                         .setPositiveButton(R.string.cancel, null) | ||||
|                         .show(); | ||||
|             } else if (tag instanceof SliderModel) { | ||||
|                 slider = (SliderModel) tag; | ||||
|             } else if (tag instanceof SliderSticker) { | ||||
|                 slider = (SliderSticker) tag; | ||||
|                 NumberFormat percentage = NumberFormat.getPercentInstance(); | ||||
|                 percentage.setMaximumFractionDigits(2); | ||||
|                 LinearLayout sliderView = new LinearLayout(context); | ||||
| @ -633,11 +630,11 @@ public class StoryViewerFragment extends Fragment { | ||||
|                 TextView tv = new TextView(context); | ||||
|                 tv.setGravity(Gravity.CENTER_HORIZONTAL); | ||||
|                 final SeekBar input = new SeekBar(context); | ||||
|                 double avg = slider.getAverage() * 100; | ||||
|                 double avg = slider.getSliderVoteAverage() * 100; | ||||
|                 input.setProgress((int) avg); | ||||
|                 sliderView.addView(input); | ||||
|                 sliderView.addView(tv); | ||||
|                 if (slider.getMyChoice().isNaN() && slider.canVote()) { | ||||
|                 if (slider.getViewerVote().isNaN() && slider.getViewerCanVote()) { | ||||
|                     input.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { | ||||
|                         @Override | ||||
|                         public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { | ||||
| @ -656,9 +653,9 @@ public class StoryViewerFragment extends Fragment { | ||||
|                     new AlertDialog.Builder(context) | ||||
|                             .setTitle(TextUtils.isEmpty(slider.getQuestion()) ? slider.getEmoji() : slider.getQuestion()) | ||||
|                             .setMessage(getResources().getQuantityString(R.plurals.slider_info, | ||||
|                                                                          slider.getVoteCount(), | ||||
|                                                                          slider.getVoteCount(), | ||||
|                                                                          percentage.format(slider.getAverage()))) | ||||
|                                                                          slider.getSliderVoteCount(), | ||||
|                                                                          slider.getSliderVoteCount(), | ||||
|                                                                          percentage.format(slider.getSliderVoteAverage()))) | ||||
|                             .setView(sliderView) | ||||
|                             .setPositiveButton(R.string.confirm, (d, w) -> { | ||||
|                                 sticking = true; | ||||
| @ -666,8 +663,8 @@ public class StoryViewerFragment extends Fragment { | ||||
|                                         csrfToken, | ||||
|                                         userId, | ||||
|                                         deviceId, | ||||
|                                         currentStory.getStoryMediaId().split("_")[0], | ||||
|                                         slider.getId(), | ||||
|                                         currentStory.getId().split("_")[0], | ||||
|                                         slider.getSliderId(), | ||||
|                                         sliderValue, | ||||
|                                         CoroutineUtilsKt.getContinuation( | ||||
|                                                 (storyStickerResponse, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { | ||||
| @ -681,7 +678,7 @@ public class StoryViewerFragment extends Fragment { | ||||
|                                                     } | ||||
|                                                     sticking = false; | ||||
|                                                     try { | ||||
|                                                         slider.setMyChoice(sliderValue); | ||||
|                                                         slider.setViewerVote(sliderValue); | ||||
|                                                         Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT).show(); | ||||
|                                                     } catch (Exception ignored) {} | ||||
|                                                 }), Dispatchers.getIO() | ||||
| @ -692,13 +689,13 @@ public class StoryViewerFragment extends Fragment { | ||||
|                             .show(); | ||||
|                 } else { | ||||
|                     input.setEnabled(false); | ||||
|                     tv.setText(getString(R.string.slider_answer, percentage.format(slider.getMyChoice()))); | ||||
|                     tv.setText(getString(R.string.slider_answer, percentage.format(slider.getViewerVote()))); | ||||
|                     new AlertDialog.Builder(context) | ||||
|                             .setTitle(TextUtils.isEmpty(slider.getQuestion()) ? slider.getEmoji() : slider.getQuestion()) | ||||
|                             .setMessage(getResources().getQuantityString(R.plurals.slider_info, | ||||
|                                                                          slider.getVoteCount(), | ||||
|                                                                          slider.getVoteCount(), | ||||
|                                                                          percentage.format(slider.getAverage()))) | ||||
|                                                                          slider.getSliderVoteCount(), | ||||
|                                                                          slider.getSliderVoteCount(), | ||||
|                                                                          percentage.format(slider.getSliderVoteAverage()))) | ||||
|                             .setView(sliderView) | ||||
|                             .setPositiveButton(R.string.ok, null) | ||||
|                             .show(); | ||||
| @ -746,11 +743,12 @@ public class StoryViewerFragment extends Fragment { | ||||
|             case FEED_STORY_POSITION: { | ||||
|                 final FeedStoriesViewModel feedStoriesViewModel = (FeedStoriesViewModel) viewModel; | ||||
|                 final List<Story> models = feedStoriesViewModel.getList().getValue(); | ||||
|                 if (models == null || currentFeedStoryIndex >= models.size() || currentFeedStoryIndex < 0) return; | ||||
|                 if (models == null || currentFeedStoryIndex >= models.size() || currentFeedStoryIndex < 0) | ||||
|                     return; | ||||
|                 final Story model = models.get(currentFeedStoryIndex); | ||||
|                 currentStoryMediaId = model.getId(); | ||||
|                 currentStoryMediaId = String.valueOf(model.getUser().getPk()); | ||||
|                 currentStoryUsername = model.getUser().getUsername(); | ||||
|                 fetchOptions = StoryViewerOptions.forUser(Long.parseLong(currentStoryMediaId), currentStoryUsername); | ||||
|                 fetchOptions = StoryViewerOptions.forUser(model.getUser().getPk(), currentStoryUsername); | ||||
|                 live = model.getBroadcast(); | ||||
|                 break; | ||||
|             } | ||||
| @ -767,11 +765,12 @@ public class StoryViewerFragment extends Fragment { | ||||
|                 fetchOptions = StoryViewerOptions.forStoryArchive(model.getId()); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         if (type == Type.USER) { | ||||
|             currentStoryMediaId = String.valueOf(options.getId()); | ||||
|             currentStoryUsername = options.getName(); | ||||
|             fetchOptions = StoryViewerOptions.forUser(options.getId(), currentStoryUsername); | ||||
|             case USER: { | ||||
|                 currentStoryMediaId = String.valueOf(options.getId()); | ||||
|                 currentStoryUsername = options.getName(); | ||||
|                 fetchOptions = StoryViewerOptions.forUser(options.getId(), currentStoryUsername); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         setTitle(type); | ||||
|         storiesViewModel.getList().setValue(Collections.emptyList()); | ||||
| @ -804,9 +803,9 @@ public class StoryViewerFragment extends Fragment { | ||||
|             refreshLive(); | ||||
|             return; | ||||
|         } | ||||
|         final ServiceCallback<List<StoryModel>> storyCallback = new ServiceCallback<List<StoryModel>>() { | ||||
|         final ServiceCallback<List<StoryMedia>> storyCallback = new ServiceCallback<List<StoryMedia>>() { | ||||
|             @Override | ||||
|             public void onSuccess(final List<StoryModel> storyModels) { | ||||
|             public void onSuccess(final List<StoryMedia> storyModels) { | ||||
|                 fetching = false; | ||||
|                 if (storyModels == null || storyModels.isEmpty()) { | ||||
|                     storiesViewModel.getList().setValue(Collections.emptyList()); | ||||
| @ -826,11 +825,10 @@ public class StoryViewerFragment extends Fragment { | ||||
| 
 | ||||
|             @Override | ||||
|             public void onFailure(final Throwable t) { | ||||
|                 Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show(); | ||||
|                 Log.e(TAG, "Error", t); | ||||
|             } | ||||
|         }; | ||||
|         storiesRepository.getUserStory( | ||||
|         storiesRepository.getStories( | ||||
|                 fetchOptions, | ||||
|                 CoroutineUtilsKt.getContinuation((storyModels, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { | ||||
|                     if (throwable != null) { | ||||
| @ -838,7 +836,7 @@ public class StoryViewerFragment extends Fragment { | ||||
|                         return; | ||||
|                     } | ||||
|                     //noinspection unchecked | ||||
|                     storyCallback.onSuccess((List<StoryModel>) storyModels); | ||||
|                     storyCallback.onSuccess((List<StoryMedia>) storyModels); | ||||
|                 }), Dispatchers.getIO()) | ||||
|         ); | ||||
|     } | ||||
| @ -887,9 +885,9 @@ public class StoryViewerFragment extends Fragment { | ||||
| 
 | ||||
|     private synchronized void refreshStory() { | ||||
|         if (binding.storiesList.getVisibility() == View.VISIBLE) { | ||||
|             final List<StoryModel> storyModels = storiesViewModel.getList().getValue(); | ||||
|             final List<StoryMedia> storyModels = storiesViewModel.getList().getValue(); | ||||
|             if (storyModels != null && storyModels.size() > 0) { | ||||
|                 StoryModel item = storyModels.get(lastSlidePos); | ||||
|                 StoryMedia item = storyModels.get(lastSlidePos); | ||||
|                 if (item != null) { | ||||
|                     item.setCurrentSlide(false); | ||||
|                     storiesAdapter.notifyItemChanged(lastSlidePos, item); | ||||
| @ -903,59 +901,96 @@ public class StoryViewerFragment extends Fragment { | ||||
|         } | ||||
|         lastSlidePos = slidePos; | ||||
| 
 | ||||
|         final MediaItemType itemType = currentStory.getItemType(); | ||||
|         final MediaItemType itemType = currentStory.getMediaType(); | ||||
| 
 | ||||
|         url = itemType == MediaItemType.MEDIA_TYPE_IMAGE ? currentStory.getStoryUrl() : currentStory.getVideoUrl(); | ||||
|         url = itemType == MediaItemType.MEDIA_TYPE_IMAGE | ||||
|                 ? ResponseBodyUtils.getImageUrl(currentStory) | ||||
|                 : ResponseBodyUtils.getVideoUrl(currentStory); | ||||
| 
 | ||||
|         final String shortCode = currentStory.getTappableShortCode(); | ||||
|         binding.viewStoryPost.setVisibility(shortCode != null ? View.VISIBLE : View.GONE); | ||||
|         binding.viewStoryPost.setTag(shortCode); | ||||
|         if (currentStory.getStoryFeedMedia() != null) { | ||||
|             final String shortCode = currentStory.getStoryFeedMedia().get(0).getMediaId(); | ||||
|             binding.viewStoryPost.setVisibility(View.VISIBLE); | ||||
|             binding.viewStoryPost.setTag(shortCode); | ||||
|         } | ||||
| 
 | ||||
|         final String spotify = currentStory.getSpotify(); | ||||
|         binding.spotify.setVisibility(spotify != null ? View.VISIBLE : View.GONE); | ||||
|         binding.spotify.setTag(spotify); | ||||
|         final StoryAppAttribution spotify = currentStory.getStoryAppAttribution(); | ||||
|         if (spotify != null) { | ||||
|             binding.spotify.setVisibility(View.VISIBLE); | ||||
|             binding.spotify.setText(spotify.getName()); | ||||
|             binding.spotify.setTag(spotify.getContentUrl().split("?")[0]); | ||||
|         } | ||||
| 
 | ||||
|         poll = currentStory.getPoll(); | ||||
|         binding.poll.setVisibility(poll != null ? View.VISIBLE : View.GONE); | ||||
|         binding.poll.setTag(poll); | ||||
|         if (currentStory.getStoryPolls() != null) { | ||||
|             poll = currentStory.getStoryPolls().get(0).getPollSticker(); | ||||
|             binding.poll.setVisibility(View.VISIBLE); | ||||
|             binding.poll.setTag(poll); | ||||
|         } | ||||
| 
 | ||||
|         question = currentStory.getQuestion(); | ||||
|         binding.answer.setVisibility((question != null) ? View.VISIBLE : View.GONE); | ||||
|         binding.answer.setTag(question); | ||||
|         if (currentStory.getStoryQuestions() != null) { | ||||
|             question = currentStory.getStoryQuestions().get(0).getQuestionSticker(); | ||||
|             binding.answer.setVisibility(View.VISIBLE); | ||||
|             binding.answer.setTag(question); | ||||
|         } | ||||
| 
 | ||||
|         mentions = currentStory.getMentions(); | ||||
|         binding.mention.setVisibility((mentions != null && mentions.length > 0) ? View.VISIBLE : View.GONE); | ||||
|         binding.mention.setTag(mentions); | ||||
|         mentions.clear(); | ||||
|         if (currentStory.getReelMentions() != null) { | ||||
|             mentions.addAll(currentStory.getReelMentions().stream().map( | ||||
|                     s -> s.getUser().getUsername() | ||||
|             ).distinct().collect(Collectors.toList())); | ||||
|         } | ||||
|         if (currentStory.getStoryHashtags() != null) { | ||||
|             mentions.addAll(currentStory.getStoryHashtags().stream().map( | ||||
|                     s -> s.getHashtag().getName() | ||||
|             ).distinct().collect(Collectors.toList())); | ||||
|         } | ||||
|         if (currentStory.getStoryLocations() != null) { | ||||
|             mentions.addAll(currentStory.getStoryLocations().stream().map( | ||||
|                     s -> s.getLocation().getShortName() + " (" + s.getLocation().getPk() + ")" | ||||
|             ).distinct().collect(Collectors.toList())); | ||||
|         } | ||||
|         if (mentions.size() > 0) { | ||||
|             binding.mention.setVisibility(View.VISIBLE); | ||||
|             binding.mention.setTag(mentions.stream().toArray(String[]::new)); | ||||
|         } | ||||
| 
 | ||||
|         quiz = currentStory.getQuiz(); | ||||
|         binding.quiz.setVisibility(quiz != null ? View.VISIBLE : View.GONE); | ||||
|         binding.quiz.setTag(quiz); | ||||
|         if (currentStory.getStoryQuizs() != null) { | ||||
|             quiz = currentStory.getStoryQuizs().get(0).getQuizSticker(); | ||||
|             binding.quiz.setVisibility(View.VISIBLE); | ||||
|             binding.quiz.setTag(quiz); | ||||
|         } | ||||
| 
 | ||||
|         slider = currentStory.getSlider(); | ||||
|         binding.slider.setVisibility(slider != null ? View.VISIBLE : View.GONE); | ||||
|         binding.slider.setTag(slider); | ||||
|         if (currentStory.getStorySliders() != null) { | ||||
|             slider = currentStory.getStorySliders().get(0).getSliderSticker(); | ||||
|             binding.slider.setVisibility(View.VISIBLE); | ||||
|             binding.slider.setTag(slider); | ||||
|         } | ||||
| 
 | ||||
|         final SwipeUpModel swipeUp = currentStory.getSwipeUp(); | ||||
|         if (swipeUp != null) { | ||||
|         if (currentStory.getStoryCta() != null) { | ||||
|             final StoryCta swipeUp = currentStory.getStoryCta().get(0).getLinks(); | ||||
|             binding.swipeUp.setVisibility(View.VISIBLE); | ||||
|             binding.swipeUp.setText(swipeUp.getText()); | ||||
|             binding.swipeUp.setTag(swipeUp.getUrl()); | ||||
|         } else binding.swipeUp.setVisibility(View.GONE); | ||||
|             binding.swipeUp.setText(currentStory.getLinkText()); | ||||
|             final String swipeUpUrl = swipeUp.getWebUri(); | ||||
|             final String actualLink = swipeUpUrl.startsWith("https://l.instagram.com/") | ||||
|                 ? Uri.parse(swipeUpUrl).getQueryParameter("u") | ||||
|                 : null; | ||||
|             binding.swipeUp.setTag(actualLink == null && actualLink.startsWith("http") | ||||
|                     ? swipeUpUrl : actualLink); | ||||
|         } | ||||
| 
 | ||||
|         releasePlayer(); | ||||
|         final Type type = options.getType(); | ||||
|         if (type == Type.HASHTAG || type == Type.LOCATION) { | ||||
|             final ActionBar actionBar = fragmentActivity.getSupportActionBar(); | ||||
|             if (actionBar != null) { | ||||
|                 actionBarTitle = currentStory.getUsername(); | ||||
|                 actionBar.setTitle(currentStory.getUsername()); | ||||
|                 actionBarTitle = currentStory.getUser().getUsername(); | ||||
|                 actionBar.setTitle(currentStory.getUser().getUsername()); | ||||
|             } | ||||
|         } | ||||
|         if (itemType == MediaItemType.MEDIA_TYPE_VIDEO) setupVideo(); | ||||
|         else setupImage(); | ||||
| 
 | ||||
|         final ActionBar actionBar = fragmentActivity.getSupportActionBar(); | ||||
|         actionBarSubtitle = TextUtils.epochSecondToString(currentStory.getTimestamp()); | ||||
|         actionBarSubtitle = TextUtils.epochSecondToString(currentStory.getTakenAt()); | ||||
|         if (actionBar != null) { | ||||
|             try { | ||||
|                 actionBar.setSubtitle(actionBarSubtitle); | ||||
| @ -969,13 +1004,23 @@ public class StoryViewerFragment extends Fragment { | ||||
|                     csrfToken, | ||||
|                     userId, | ||||
|                     deviceId, | ||||
|                     currentStory.getStoryMediaId(), | ||||
|                     currentStory.getTimestamp(), | ||||
|                     currentStory.getId(), | ||||
|                     currentStory.getTakenAt(), | ||||
|                     System.currentTimeMillis() / 1000, | ||||
|                     CoroutineUtilsKt.getContinuation((s, throwable) -> {}, Dispatchers.getIO()) | ||||
|             ); | ||||
|     } | ||||
| 
 | ||||
|     private void removeStickers() { | ||||
|         binding.swipeUp.setVisibility(View.GONE); | ||||
|         binding.quiz.setVisibility(View.GONE); | ||||
|         binding.spotify.setVisibility(View.GONE); | ||||
|         binding.mention.setVisibility(View.GONE); | ||||
|         binding.viewStoryPost.setVisibility(View.GONE); | ||||
|         binding.answer.setVisibility(View.GONE); | ||||
|         binding.slider.setVisibility(View.GONE); | ||||
|     } | ||||
| 
 | ||||
|     private void downloadStory() { | ||||
|         final Context context = getContext(); | ||||
|         if (context == null) return; | ||||
| @ -1016,7 +1061,7 @@ public class StoryViewerFragment extends Fragment { | ||||
|                                                               dmVisible = true; | ||||
|                                                               menuDm.setVisible(true); | ||||
|                                                           } | ||||
|                                                           if (!TextUtils.isEmpty(currentStory.getUsername())) { | ||||
|                                                           if (!TextUtils.isEmpty(currentStory.getUser().getUsername())) { | ||||
|                                                               profileVisible = true; | ||||
|                                                               menuProfile.setVisible(true); | ||||
|                                                           } | ||||
| @ -1057,7 +1102,7 @@ public class StoryViewerFragment extends Fragment { | ||||
|                     dmVisible = true; | ||||
|                     menuDm.setVisible(true); | ||||
|                 } | ||||
|                 if (!TextUtils.isEmpty(currentStory.getUsername()) && menuProfile != null) { | ||||
|                 if (!TextUtils.isEmpty(currentStory.getUser().getUsername()) && menuProfile != null) { | ||||
|                     profileVisible = true; | ||||
|                     menuProfile.setVisible(true); | ||||
|                 } | ||||
| @ -1077,7 +1122,7 @@ public class StoryViewerFragment extends Fragment { | ||||
|                     dmVisible = true; | ||||
|                     menuDm.setVisible(true); | ||||
|                 } | ||||
|                 if (!TextUtils.isEmpty(currentStory.getUsername()) && menuProfile != null) { | ||||
|                 if (!TextUtils.isEmpty(currentStory.getUser().getUsername()) && menuProfile != null) { | ||||
|                     profileVisible = true; | ||||
|                     menuProfile.setVisible(true); | ||||
|                 } | ||||
|  | ||||
| @ -1,29 +0,0 @@ | ||||
| package awais.instagrabber.models | ||||
| 
 | ||||
| import awais.instagrabber.models.enums.MediaItemType | ||||
| import awais.instagrabber.models.stickers.* | ||||
| import java.io.Serializable | ||||
| 
 | ||||
| data class StoryModel( | ||||
|     val storyMediaId: String? = null, | ||||
|     val storyUrl: String? = null, | ||||
|     var thumbnail: String? = null, | ||||
|     val itemType: MediaItemType? = null, | ||||
|     val timestamp: Long = 0, | ||||
|     val username: String? = null, | ||||
|     val userId: Long = 0, | ||||
|     val canReply: Boolean = false, | ||||
| ) : Serializable { | ||||
|     var videoUrl: String? = null | ||||
|     var tappableShortCode: String? = null | ||||
|     val tappableId: String? = null | ||||
|     var spotify: String? = null | ||||
|     var poll: PollModel? = null | ||||
|     var question: QuestionModel? = null | ||||
|     var slider: SliderModel? = null | ||||
|     var quiz: QuizModel? = null | ||||
|     var swipeUp: SwipeUpModel? = null | ||||
|     var mentions: Array<String>? = null | ||||
|     var position = 0 | ||||
|     var isCurrentSlide = false | ||||
| } | ||||
| @ -1,52 +0,0 @@ | ||||
| package awais.instagrabber.models.stickers; | ||||
| 
 | ||||
| import java.io.Serializable; | ||||
| 
 | ||||
| public final class PollModel implements Serializable { | ||||
|     private int leftcount, rightcount, mychoice; | ||||
|     private final String id, question, leftchoice, rightchoice; | ||||
| 
 | ||||
|     public PollModel(final String id, final String question, final String leftchoice, final int leftcount, | ||||
|                      final String rightchoice, final int rightcount, final int mychoice) { | ||||
|         this.id = id; // only the poll id | ||||
|         this.question = question; | ||||
|         this.leftchoice = leftchoice; | ||||
|         this.leftcount = leftcount; | ||||
|         this.rightchoice = rightchoice; | ||||
|         this.rightcount = rightcount; | ||||
|         this.mychoice = mychoice; | ||||
|     } | ||||
| 
 | ||||
|     public String getId() { | ||||
|         return id; | ||||
|     } | ||||
| 
 | ||||
|     public String getQuestion() { | ||||
|         return question; | ||||
|     } | ||||
| 
 | ||||
|     public String getLeftChoice() { | ||||
|         return leftchoice; | ||||
|     } | ||||
| 
 | ||||
|     public int getLeftCount() { | ||||
|         return leftcount; | ||||
|     } | ||||
| 
 | ||||
|     public String getRightChoice() { | ||||
|         return rightchoice; | ||||
|     } | ||||
| 
 | ||||
|     public int getRightCount() { | ||||
|         return rightcount; | ||||
|     } | ||||
| 
 | ||||
|     public int getMyChoice() { return mychoice; } | ||||
| 
 | ||||
|     public int setMyChoice(final int choice) { | ||||
|         this.mychoice = choice; | ||||
|         if (choice == 0) this.leftcount += 1; | ||||
|         else if (choice == 1) this.rightcount += 1; | ||||
|         return choice; | ||||
|     } | ||||
| } | ||||
| @ -1,20 +0,0 @@ | ||||
| package awais.instagrabber.models.stickers; | ||||
| 
 | ||||
| import java.io.Serializable; | ||||
| 
 | ||||
| public final class QuestionModel implements Serializable { | ||||
|     private final String id, question; | ||||
| 
 | ||||
|     public QuestionModel(final String id, final String question) { | ||||
|         this.id = id; // only the poll id | ||||
|         this.question = question; | ||||
|     } | ||||
| 
 | ||||
|     public String getId() { | ||||
|         return id; | ||||
|     } | ||||
| 
 | ||||
|     public String getQuestion() { | ||||
|         return question; | ||||
|     } | ||||
| } | ||||
| @ -1,37 +0,0 @@ | ||||
| package awais.instagrabber.models.stickers; | ||||
| 
 | ||||
| import java.io.Serializable; | ||||
| 
 | ||||
| public final class QuizModel implements Serializable { | ||||
|     private final String id, question; | ||||
|     private final String[] choices; | ||||
|     private Long[] counts; | ||||
|     private int mychoice; | ||||
| 
 | ||||
|     public QuizModel(final String id, final String question, final String[] choices, final Long[] counts, final int mychoice) { | ||||
|         this.id = id; // only the poll id | ||||
|         this.question = question; | ||||
|         this.choices = choices; | ||||
|         this.counts = counts; | ||||
|         this.mychoice = mychoice; | ||||
|     } | ||||
| 
 | ||||
|     public String getId() { | ||||
|         return id; | ||||
|     } | ||||
| 
 | ||||
|     public String getQuestion() { | ||||
|         return question; | ||||
|     } | ||||
| 
 | ||||
|     public String[] getChoices() { return choices;} | ||||
| 
 | ||||
|     public Long[] getCounts() { return counts;} | ||||
| 
 | ||||
|     public int getMyChoice() { return mychoice; } | ||||
| 
 | ||||
|     public void setMyChoice(final int choice) { | ||||
|         this.mychoice = choice; | ||||
|         counts[choice] += 1L; | ||||
|     } | ||||
| } | ||||
| @ -1,53 +0,0 @@ | ||||
| package awais.instagrabber.models.stickers; | ||||
| 
 | ||||
| import java.io.Serializable; | ||||
| 
 | ||||
| public final class SliderModel implements Serializable { | ||||
|     private final int voteCount; | ||||
|     private final Double average; | ||||
|     private Double myChoice; | ||||
|     private final boolean canVote; | ||||
|     private final String id, question, emoji; | ||||
| 
 | ||||
|     public SliderModel(final String id, final String question, final String emoji, final boolean canVote, | ||||
|                        final Double average, final int voteCount, final Double myChoice) { | ||||
|         this.id = id; | ||||
|         this.question = question; | ||||
|         this.emoji = emoji; | ||||
|         this.canVote = canVote; | ||||
|         this.average = average; | ||||
|         this.voteCount = voteCount; | ||||
|         this.myChoice = myChoice; | ||||
|     } | ||||
| 
 | ||||
|     public String getId() { | ||||
|         return id; | ||||
|     } | ||||
| 
 | ||||
|     public String getQuestion() { | ||||
|         return question; | ||||
|     } | ||||
| 
 | ||||
|     public String getEmoji() { | ||||
|         return emoji; | ||||
|     } | ||||
| 
 | ||||
|     public boolean canVote() { | ||||
|         return canVote; | ||||
|     } | ||||
| 
 | ||||
|     public int getVoteCount() { | ||||
|         return voteCount; | ||||
|     } | ||||
| 
 | ||||
|     public Double getAverage() { | ||||
|         return average; | ||||
|     } | ||||
| 
 | ||||
|     public Double getMyChoice() { return myChoice; } | ||||
| 
 | ||||
|     public Double setMyChoice(final Double choice) { | ||||
|         this.myChoice = choice; | ||||
|         return choice; | ||||
|     } | ||||
| } | ||||
| @ -1,20 +0,0 @@ | ||||
| package awais.instagrabber.models.stickers; | ||||
| 
 | ||||
| import java.io.Serializable; | ||||
| 
 | ||||
| public final class SwipeUpModel implements Serializable { | ||||
|     private final String url, text; | ||||
| 
 | ||||
|     public SwipeUpModel(final String url, final String text) { | ||||
|         this.url = url; | ||||
|         this.text = text; | ||||
|     } | ||||
| 
 | ||||
|     public String getUrl() { | ||||
|         return url; | ||||
|     } | ||||
| 
 | ||||
|     public String getText() { | ||||
|         return text; | ||||
|     } | ||||
| } | ||||
| @ -1,14 +1,17 @@ | ||||
| package awais.instagrabber.repositories | ||||
| 
 | ||||
| import awais.instagrabber.repositories.responses.stories.ArchiveResponse | ||||
| import awais.instagrabber.repositories.responses.stories.ReelsMediaResponse | ||||
| import awais.instagrabber.repositories.responses.stories.ReelsResponse | ||||
| import awais.instagrabber.repositories.responses.stories.ReelsTrayResponse | ||||
| import awais.instagrabber.repositories.responses.stories.StoryMediaResponse | ||||
| import awais.instagrabber.repositories.responses.stories.StoryStickerResponse | ||||
| import retrofit2.http.* | ||||
| 
 | ||||
| interface StoriesService { | ||||
|     // 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 | ||||
|     suspend fun fetch(@Path("mediaId") mediaId: Long): StoryMediaResponse | ||||
| 
 | ||||
|     @GET("/api/v1/feed/reels_tray/") | ||||
|     suspend fun getFeedStories(): ReelsTrayResponse? | ||||
| @ -19,14 +22,20 @@ interface StoriesService { | ||||
|     @GET("/api/v1/archive/reel/day_shells/") | ||||
|     suspend fun fetchArchive(@QueryMap queryParams: Map<String, String>): ArchiveResponse? | ||||
| 
 | ||||
|     @GET | ||||
|     suspend fun getUserStory(@Url url: String): String | ||||
|     @GET("/api/v1/feed/reels_media/") | ||||
|     suspend fun getReelsMedia(@Query("user_ids") id: String): ReelsMediaResponse | ||||
| 
 | ||||
|     @GET("/api/v1/{type}/{id}/story/") | ||||
|     suspend fun getStories(@Path("type") type: String, @Path("id") id: String): ReelsResponse | ||||
| 
 | ||||
|     @GET("/api/v1/feed/user/{id}/story/") | ||||
|     suspend fun getUserStories(@Path("id") id: String): ReelsResponse | ||||
| 
 | ||||
|     @FormUrlEncoded | ||||
|     @POST("/api/v1/media/{storyId}/{stickerId}/{action}/") | ||||
|     suspend fun respondToSticker( | ||||
|         @Path("storyId") storyId: String, | ||||
|         @Path("stickerId") stickerId: String, | ||||
|         @Path("stickerId") stickerId: Long, | ||||
|         @Path("action") action: String,  // story_poll_vote, story_question_response, story_slider_vote, story_quiz_answer | ||||
|         @FieldMap form: Map<String, String>, | ||||
|     ): StoryStickerResponse | ||||
|  | ||||
| @ -42,7 +42,7 @@ public class StoryViewerOptions implements Serializable { | ||||
|     } | ||||
| 
 | ||||
|     public static StoryViewerOptions forUser(final long id, final String name) { | ||||
|         return new StoryViewerOptions(id, name,Type.USER); | ||||
|         return new StoryViewerOptions(id, name, Type.USER); | ||||
|     } | ||||
| 
 | ||||
|     public static StoryViewerOptions forHighlight(final String highlight) { | ||||
|  | ||||
| @ -9,5 +9,5 @@ data class PollSticker( | ||||
|     val pollId: Long?, | ||||
|     val question: String?, | ||||
|     val tallies: List<Tally>?, | ||||
|     val viewerVote: Int? | ||||
|     var viewerVote: Int = -1 | ||||
| ) : Serializable | ||||
| @ -9,6 +9,6 @@ data class QuizSticker( | ||||
|     val quizId: Long?, | ||||
|     val question: String?, | ||||
|     val tallies: List<Tally>?, | ||||
|     val viewerAnswer: Int?, | ||||
|     var viewerAnswer: Int? = -1, | ||||
|     val correctAnswer: Int? | ||||
| ) : Serializable | ||||
| @ -0,0 +1,8 @@ | ||||
| package awais.instagrabber.repositories.responses.stories | ||||
| 
 | ||||
| import java.io.Serializable | ||||
| 
 | ||||
| data class ReelsMediaResponse( | ||||
|     val status: String?, | ||||
|     val reels: Map<String, Story?>? | ||||
| ) : Serializable | ||||
| @ -4,6 +4,7 @@ import java.io.Serializable | ||||
| 
 | ||||
| data class ReelsResponse( | ||||
|     val status: String?, | ||||
|     val reel: Story?, | ||||
|     val reel: Story?, // users | ||||
|     val story: Story?, // hashtag and locations (unused) | ||||
|     val broadcast: Broadcast? | ||||
| ) : Serializable | ||||
| @ -10,7 +10,7 @@ data class SliderSticker( | ||||
|     val question: String?, | ||||
|     val emoji: String?, | ||||
|     val viewerCanVote: Boolean?, | ||||
|     val viewerVote: Double?, | ||||
|     var viewerVote: Double?, | ||||
|     val sliderVoteAverage: Double?, | ||||
|     val sliderVoteCount: Int?, | ||||
| ) : Serializable | ||||
| @ -41,6 +41,8 @@ data class StoryMedia( | ||||
|         val storyAppAttribution: StoryAppAttribution? = null | ||||
| ) : Serializable { | ||||
|     private var dateString: String? = null | ||||
|     var position = 0 | ||||
|     var isCurrentSlide = false | ||||
| 
 | ||||
|     // TODO use extension once all usages are converted to kotlin | ||||
|     // val date: String by lazy { | ||||
|  | ||||
| @ -0,0 +1,14 @@ | ||||
| package awais.instagrabber.repositories.responses.stories | ||||
| 
 | ||||
| import awais.instagrabber.models.enums.MediaItemType | ||||
| import awais.instagrabber.repositories.responses.ImageVersions2 | ||||
| import awais.instagrabber.repositories.responses.MediaCandidate | ||||
| import awais.instagrabber.repositories.responses.User | ||||
| import awais.instagrabber.utils.TextUtils | ||||
| import java.io.Serializable | ||||
| 
 | ||||
| data class StoryMediaResponse( | ||||
|     val items: List<StoryMedia?>?, // length 1 | ||||
|     val status: String? | ||||
|     // ignoring pagination properties | ||||
| ) : Serializable | ||||
| @ -13,9 +13,9 @@ import androidx.documentfile.provider.DocumentFile | ||||
| import androidx.work.* | ||||
| import awais.instagrabber.R | ||||
| import awais.instagrabber.fragments.settings.PreferenceKeys | ||||
| import awais.instagrabber.models.StoryModel | ||||
| import awais.instagrabber.models.enums.MediaItemType | ||||
| import awais.instagrabber.repositories.responses.Media | ||||
| import awais.instagrabber.repositories.responses.stories.StoryMedia | ||||
| import awais.instagrabber.utils.TextUtils.isEmpty | ||||
| import awais.instagrabber.workers.DownloadWorker | ||||
| import com.google.gson.Gson | ||||
| @ -392,18 +392,19 @@ object DownloadUtils { | ||||
|     @JvmStatic | ||||
|     fun download( | ||||
|         context: Context, | ||||
|         storyModel: StoryModel | ||||
|         storyModel: StoryMedia | ||||
|     ) { | ||||
|         val downloadDir = getDownloadDir(context, storyModel.username) ?: return | ||||
|         val downloadDir = getDownloadDir(context, storyModel.user?.username) ?: return | ||||
|         val url = | ||||
|             if (storyModel.itemType == MediaItemType.MEDIA_TYPE_VIDEO) storyModel.videoUrl else storyModel.storyUrl | ||||
|             if (storyModel.mediaType == MediaItemType.MEDIA_TYPE_VIDEO) ResponseBodyUtils.getVideoUrl(storyModel) | ||||
|             else ResponseBodyUtils.getImageUrl(storyModel) | ||||
|         val extension = getFileExtensionFromUrl(url) | ||||
|         val baseFileName = (storyModel.storyMediaId + "_" | ||||
|                 + storyModel.timestamp + extension) | ||||
|         val baseFileName = (storyModel.id + "_" | ||||
|                 + storyModel.takenAt + extension) | ||||
|         val usernamePrepend = | ||||
|             if (Utils.settingsHelper.getBoolean(PreferenceKeys.DOWNLOAD_PREPEND_USER_NAME) | ||||
|                 && storyModel.username != null | ||||
|             ) storyModel.username + "_" else "" | ||||
|                 && storyModel.user?.username != null | ||||
|             ) storyModel.user.username + "_" else "" | ||||
|         val fileName = usernamePrepend + baseFileName | ||||
|         var saveFile = downloadDir.findFile(fileName) | ||||
|         if (saveFile == null) { | ||||
|  | ||||
| @ -14,13 +14,7 @@ import java.util.Collections; | ||||
| import java.util.List; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| import awais.instagrabber.models.StoryModel; | ||||
| import awais.instagrabber.models.enums.MediaItemType; | ||||
| import awais.instagrabber.models.stickers.PollModel; | ||||
| import awais.instagrabber.models.stickers.QuestionModel; | ||||
| import awais.instagrabber.models.stickers.QuizModel; | ||||
| import awais.instagrabber.models.stickers.SliderModel; | ||||
| import awais.instagrabber.models.stickers.SwipeUpModel; | ||||
| import awais.instagrabber.repositories.responses.Caption; | ||||
| import awais.instagrabber.repositories.responses.FriendshipStatus; | ||||
| import awais.instagrabber.repositories.responses.ImageVersions2; | ||||
| @ -276,136 +270,6 @@ public final class ResponseBodyUtils { | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     public static StoryModel parseStoryItem(final JSONObject data, | ||||
|                                             final boolean isLocOrHashtag, | ||||
|                                             final String username) throws JSONException { | ||||
|         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"), null, | ||||
|                                                 isVideo ? MediaItemType.MEDIA_TYPE_VIDEO : MediaItemType.MEDIA_TYPE_IMAGE, | ||||
|                                                 data.optLong("taken_at", 0), | ||||
|                                                 isLocOrHashtag ? data.getJSONObject("user").getString("username") : username, | ||||
|                                                 data.getJSONObject("user").getLong("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)); | ||||
| 
 | ||||
|         if (data.has("story_feed_media")) { | ||||
|             model.setTappableShortCode(data.getJSONArray("story_feed_media").getJSONObject(0).optString("media_id")); | ||||
|         } | ||||
| 
 | ||||
|         // TODO: this may not be limited to spotify | ||||
|         if (!data.isNull("story_app_attribution")) | ||||
|             model.setSpotify(data.getJSONObject("story_app_attribution").optString("content_url").split("\\?")[0]); | ||||
| 
 | ||||
|         if (data.has("story_polls")) { | ||||
|             final JSONArray storyPolls = data.optJSONArray("story_polls"); | ||||
|             JSONObject tappableObject = null; | ||||
|             if (storyPolls != null) { | ||||
|                 tappableObject = storyPolls.getJSONObject(0).optJSONObject("poll_sticker"); | ||||
|             } | ||||
|             if (tappableObject != null) model.setPoll(new PollModel( | ||||
|                     String.valueOf(tappableObject.getLong("poll_id")), | ||||
|                     tappableObject.getString("question"), | ||||
|                     tappableObject.getJSONArray("tallies").getJSONObject(0).getString("text"), | ||||
|                     tappableObject.getJSONArray("tallies").getJSONObject(0).getInt("count"), | ||||
|                     tappableObject.getJSONArray("tallies").getJSONObject(1).getString("text"), | ||||
|                     tappableObject.getJSONArray("tallies").getJSONObject(1).getInt("count"), | ||||
|                     tappableObject.optInt("viewer_vote", -1) | ||||
|             )); | ||||
|         } | ||||
|         if (data.has("story_questions")) { | ||||
|             final JSONObject tappableObject = data.getJSONArray("story_questions").getJSONObject(0) | ||||
|                                                   .optJSONObject("question_sticker"); | ||||
|             if (tappableObject != null && !tappableObject.getString("question_type").equals("music")) | ||||
|                 model.setQuestion(new QuestionModel( | ||||
|                         String.valueOf(tappableObject.getLong("question_id")), | ||||
|                         tappableObject.getString("question") | ||||
|                 )); | ||||
|         } | ||||
|         if (data.has("story_quizs")) { | ||||
|             JSONObject tappableObject = data.getJSONArray("story_quizs").getJSONObject(0).optJSONObject("quiz_sticker"); | ||||
|             if (tappableObject != null) { | ||||
|                 String[] choices = new String[tappableObject.getJSONArray("tallies").length()]; | ||||
|                 Long[] counts = new Long[choices.length]; | ||||
|                 for (int q = 0; q < choices.length; ++q) { | ||||
|                     JSONObject tempchoice = tappableObject.getJSONArray("tallies").getJSONObject(q); | ||||
|                     choices[q] = (q == tappableObject.getInt("correct_answer") ? "*** " : "") | ||||
|                             + tempchoice.getString("text"); | ||||
|                     counts[q] = tempchoice.getLong("count"); | ||||
|                 } | ||||
|                 model.setQuiz(new QuizModel( | ||||
|                         String.valueOf(tappableObject.getLong("quiz_id")), | ||||
|                         tappableObject.getString("question"), | ||||
|                         choices, | ||||
|                         counts, | ||||
|                         tappableObject.optInt("viewer_answer", -1) | ||||
|                 )); | ||||
|             } | ||||
|         } | ||||
|         if (data.has("story_cta") && data.has("link_text")) { | ||||
|             JSONObject tappableObject = data.getJSONArray("story_cta").getJSONObject(0).getJSONArray("links").getJSONObject(0); | ||||
|             String swipeUpUrl = tappableObject.optString("webUri"); | ||||
|             final String backupSwipeUpUrl = swipeUpUrl; | ||||
|             if (swipeUpUrl != null && swipeUpUrl.startsWith("https://l.instagram.com/")) { | ||||
|                 swipeUpUrl = Uri.parse(swipeUpUrl).getQueryParameter("u"); | ||||
|             } | ||||
|             if (swipeUpUrl != null && swipeUpUrl.startsWith("http")) | ||||
|                 model.setSwipeUp(new SwipeUpModel(swipeUpUrl, data.getString("link_text"))); | ||||
|             else if (backupSwipeUpUrl != null && backupSwipeUpUrl.startsWith("http")) | ||||
|                 model.setSwipeUp(new SwipeUpModel(backupSwipeUpUrl, data.getString("link_text"))); | ||||
|         } | ||||
|         if (data.has("story_sliders")) { | ||||
|             final JSONObject tappableObject = data.getJSONArray("story_sliders").getJSONObject(0) | ||||
|                                                   .optJSONObject("slider_sticker"); | ||||
|             if (tappableObject != null) | ||||
|                 model.setSlider(new SliderModel( | ||||
|                         String.valueOf(tappableObject.getLong("slider_id")), | ||||
|                         tappableObject.getString("question"), | ||||
|                         tappableObject.getString("emoji"), | ||||
|                         tappableObject.getBoolean("viewer_can_vote"), | ||||
|                         tappableObject.optDouble("slider_vote_average"), | ||||
|                         tappableObject.getInt("slider_vote_count"), | ||||
|                         tappableObject.optDouble("viewer_vote") | ||||
|                 )); | ||||
|         } | ||||
|         JSONArray hashtags = data.optJSONArray("story_hashtags"); | ||||
|         JSONArray locations = data.optJSONArray("story_locations"); | ||||
|         JSONArray atmarks = data.optJSONArray("reel_mentions"); | ||||
|         String[] mentions = new String[(hashtags == null ? 0 : hashtags.length()) | ||||
|                 + (atmarks == null ? 0 : atmarks.length()) | ||||
|                 + (locations == null ? 0 : locations.length())]; | ||||
|         if (hashtags != null) { | ||||
|             for (int h = 0; h < hashtags.length(); ++h) { | ||||
|                 mentions[h] = "#" + hashtags.getJSONObject(h).getJSONObject("hashtag").getString("name"); | ||||
|             } | ||||
|         } | ||||
|         if (atmarks != null) { | ||||
|             for (int h = 0; h < atmarks.length(); ++h) { | ||||
|                 mentions[h + (hashtags == null ? 0 : hashtags.length())] = | ||||
|                         "@" + atmarks.getJSONObject(h).getJSONObject("user").getString("username"); | ||||
|             } | ||||
|         } | ||||
|         if (locations != null) { | ||||
|             for (int h = 0; h < locations.length(); ++h) { | ||||
|                 mentions[h + (hashtags == null ? 0 : hashtags.length()) + (atmarks == null ? 0 : atmarks.length())] = | ||||
|                         locations.getJSONObject(h).getJSONObject("location").getString("short_name") | ||||
|                                 + " (" + locations.getJSONObject(h).getJSONObject("location").getLong("pk") + ")"; | ||||
|             } | ||||
|         } | ||||
|         if (mentions.length != 0) model.setMentions(mentions); | ||||
| 
 | ||||
|         return model; | ||||
|     } | ||||
| 
 | ||||
|     public static String getThumbUrl(final Object media) { | ||||
|         return getImageCandidate(media, CandidateType.THUMBNAIL); | ||||
|     } | ||||
| @ -415,6 +279,7 @@ public final class ResponseBodyUtils { | ||||
|     } | ||||
| 
 | ||||
|     private static String getImageCandidate(final Object rawMedia, final CandidateType type) { | ||||
|         if (rawMedia == null) return null; | ||||
|         final ImageVersions2 imageVersions2; | ||||
|         final int originalWidth, originalHeight; | ||||
|         if (rawMedia instanceof StoryMedia) { | ||||
| @ -453,22 +318,34 @@ public final class ResponseBodyUtils { | ||||
|         return getVideoCandidate(media, CandidateType.VIDEO_THUMBNAIL); | ||||
|     } | ||||
| 
 | ||||
|     public static String getVideoUrl(final Media media) { | ||||
|     public static String getVideoUrl(final Object media) { | ||||
|         return getVideoCandidate(media, CandidateType.DOWNLOAD); | ||||
|     } | ||||
| 
 | ||||
|     // TODO: merge with getImageCandidate when Kotlin | ||||
|     private static String getVideoCandidate(final Media media, final CandidateType type) { | ||||
|         if (media == null) return null; | ||||
|         final List<MediaCandidate> candidates = media.getVideoVersions(); | ||||
|     private static String getVideoCandidate(final Object rawMedia, final CandidateType type) { | ||||
|         if (rawMedia == null) return null; | ||||
|         final List<MediaCandidate> candidates; | ||||
|         final int originalWidth, originalHeight; | ||||
|         if (rawMedia instanceof StoryMedia) { | ||||
|             candidates = ((StoryMedia) rawMedia).getVideoVersions(); | ||||
|             originalWidth = ((StoryMedia) rawMedia).getOriginalWidth(); | ||||
|             originalHeight = ((StoryMedia) rawMedia).getOriginalHeight(); | ||||
|         } | ||||
|         else if (rawMedia instanceof Media) { | ||||
|             candidates = ((Media) rawMedia).getVideoVersions(); | ||||
|             originalWidth = ((Media) rawMedia).getOriginalWidth(); | ||||
|             originalHeight = ((Media) rawMedia).getOriginalHeight(); | ||||
|         } | ||||
|         else return null; | ||||
|         if (candidates == null || candidates.isEmpty()) return null; | ||||
|         final boolean isSquare = Integer.compare(media.getOriginalWidth(), media.getOriginalHeight()) == 0; | ||||
|         final boolean isSquare = Integer.compare(originalWidth, originalHeight) == 0; | ||||
|         final List<MediaCandidate> sortedCandidates = candidates.stream() | ||||
|                                                                 .sorted((c1, c2) -> Integer.compare(c2.getWidth(), c1.getWidth())) | ||||
|                                                                 .collect(Collectors.toList()); | ||||
|         final List<MediaCandidate> filteredCandidates = sortedCandidates.stream() | ||||
|                                                                         .filter(c -> | ||||
|                                                                                         c.getWidth() <= media.getOriginalWidth() | ||||
|                                                                                         c.getWidth() <= originalWidth | ||||
|                                                                                                 && c.getWidth() <= type.getValue() | ||||
|                                                                                                 && (isSquare || Integer | ||||
|                                                                                                 .compare(c.getWidth(), c.getHeight()) != 0) | ||||
| @ -480,19 +357,6 @@ public final class ResponseBodyUtils { | ||||
|         return candidate.getUrl(); | ||||
|     } | ||||
| 
 | ||||
|     public static StoryModel parseBroadcastItem(final JSONObject data) throws JSONException { | ||||
|         final StoryModel model = new StoryModel(data.getString("id"), | ||||
|                                                 data.getString("cover_frame_url"), | ||||
|                                                 data.getString("cover_frame_url"), | ||||
|                                                 MediaItemType.MEDIA_TYPE_LIVE, | ||||
|                                                 data.optLong("published_time", 0), | ||||
|                                                 data.getJSONObject("broadcast_owner").getString("username"), | ||||
|                                                 data.getJSONObject("broadcast_owner").getLong("pk"), | ||||
|                                                 false); | ||||
|         model.setVideoUrl(data.getString("dash_playback_url")); | ||||
|         return model; | ||||
|     } | ||||
| 
 | ||||
|     private enum CandidateType { | ||||
|         VIDEO_THUMBNAIL(700), | ||||
|         THUMBNAIL(1000), | ||||
|  | ||||
| @ -8,7 +8,6 @@ import awais.instagrabber.db.entities.Favorite | ||||
| import awais.instagrabber.db.repositories.FavoriteRepository | ||||
| import awais.instagrabber.managers.DirectMessagesManager | ||||
| import awais.instagrabber.models.Resource | ||||
| import awais.instagrabber.models.StoryModel | ||||
| import awais.instagrabber.models.enums.BroadcastItemType | ||||
| import awais.instagrabber.models.enums.FavoriteType | ||||
| import awais.instagrabber.repositories.requests.StoryViewerOptions | ||||
| @ -17,6 +16,7 @@ import awais.instagrabber.repositories.responses.User | ||||
| import awais.instagrabber.repositories.responses.UserProfileContextLink | ||||
| import awais.instagrabber.repositories.responses.directmessages.RankedRecipient | ||||
| import awais.instagrabber.repositories.responses.stories.Story | ||||
| import awais.instagrabber.repositories.responses.stories.StoryMedia | ||||
| import awais.instagrabber.utils.ControlledRunner | ||||
| import awais.instagrabber.utils.Event | ||||
| import awais.instagrabber.utils.SingleRunner | ||||
| @ -153,9 +153,9 @@ class ProfileFragmentViewModel( | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|     private val storyFetchControlledRunner = ControlledRunner<List<StoryModel>?>() | ||||
|     val userStories: LiveData<Resource<List<StoryModel>?>> = currentUserProfileActionLiveData.switchMap { currentUserAndProfilePair -> | ||||
|         liveData<Resource<List<StoryModel>?>>(context = viewModelScope.coroutineContext + ioDispatcher) { | ||||
|     private val storyFetchControlledRunner = ControlledRunner<List<StoryMedia>?>() | ||||
|     val userStories: LiveData<Resource<List<StoryMedia>?>> = currentUserProfileActionLiveData.switchMap { currentUserAndProfilePair -> | ||||
|         liveData<Resource<List<StoryMedia>?>>(context = viewModelScope.coroutineContext + ioDispatcher) { | ||||
|             val (currentUserResource, profileResource, action) = currentUserAndProfilePair | ||||
|             if (action != INIT && action != REFRESH) { | ||||
|                 return@liveData | ||||
| @ -231,7 +231,7 @@ class ProfileFragmentViewModel( | ||||
|         return graphQLRepository.fetchUser(stateUsername) | ||||
|     } | ||||
| 
 | ||||
|     private suspend fun fetchUserStory(fetchedUser: User): List<StoryModel> = storiesRepository.getUserStory( | ||||
|     private suspend fun fetchUserStory(fetchedUser: User): List<StoryMedia> = storiesRepository.getStories( | ||||
|         StoryViewerOptions.forUser(fetchedUser.pk, fetchedUser.fullName) | ||||
|     ) | ||||
| 
 | ||||
|  | ||||
| @ -5,12 +5,12 @@ import androidx.lifecycle.ViewModel; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| import awais.instagrabber.models.StoryModel; | ||||
| import awais.instagrabber.repositories.responses.stories.StoryMedia; | ||||
| 
 | ||||
| public class StoriesViewModel extends ViewModel { | ||||
|     private MutableLiveData<List<StoryModel>> list; | ||||
|     private MutableLiveData<List<StoryMedia>> list; | ||||
| 
 | ||||
|     public MutableLiveData<List<StoryModel>> getList() { | ||||
|     public MutableLiveData<List<StoryMedia>> getList() { | ||||
|         if (list == null) { | ||||
|             list = new MutableLiveData<>(); | ||||
|         } | ||||
|  | ||||
| @ -1,26 +1,22 @@ | ||||
| package awais.instagrabber.webservices | ||||
| 
 | ||||
| import awais.instagrabber.fragments.settings.PreferenceKeys | ||||
| import awais.instagrabber.models.StoryModel | ||||
| import awais.instagrabber.repositories.StoriesService | ||||
| import awais.instagrabber.repositories.requests.StoryViewerOptions | ||||
| import awais.instagrabber.repositories.responses.stories.ArchiveResponse | ||||
| import awais.instagrabber.repositories.responses.stories.Story | ||||
| import awais.instagrabber.repositories.responses.stories.StoryMedia | ||||
| import awais.instagrabber.repositories.responses.stories.StoryStickerResponse | ||||
| import awais.instagrabber.utils.ResponseBodyUtils | ||||
| import awais.instagrabber.utils.TextUtils.isEmpty | ||||
| import awais.instagrabber.utils.Utils | ||||
| import awais.instagrabber.webservices.RetrofitFactory.retrofit | ||||
| import org.json.JSONArray | ||||
| import org.json.JSONObject | ||||
| import java.util.* | ||||
| import java.util.UUID | ||||
| 
 | ||||
| open class StoriesRepository(private val service: StoriesService) { | ||||
| 
 | ||||
|     suspend fun fetch(mediaId: Long): StoryModel { | ||||
|     suspend fun fetch(mediaId: Long): StoryMedia? { | ||||
|         val response = service.fetch(mediaId) | ||||
|         val itemJson = JSONObject(response).getJSONArray("items").getJSONObject(0) | ||||
|         return ResponseBodyUtils.parseStoryItem(itemJson, false, null) | ||||
|         return response.items?.get(0) | ||||
|     } | ||||
| 
 | ||||
|     suspend fun getFeedStories(): List<Story> { | ||||
| @ -70,31 +66,30 @@ open class StoriesRepository(private val service: StoriesService) { | ||||
|         return service.fetchArchive(form) | ||||
|     } | ||||
| 
 | ||||
|     open suspend fun getUserStory(options: StoryViewerOptions): List<StoryModel> { | ||||
|         val url = buildUrl(options) ?: return emptyList() | ||||
|         val response = service.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<StoryModel> = ArrayList() | ||||
|             for (i in 0 until mediaLen) { | ||||
|                 data = media.getJSONObject(i) | ||||
|                 models.add(ResponseBodyUtils.parseStoryItem(data, isLocOrHashtag, username)) | ||||
|     open suspend fun getStories(options: StoryViewerOptions): List<StoryMedia> { | ||||
|         return when (options.type) { | ||||
|             StoryViewerOptions.Type.HIGHLIGHT, | ||||
|             StoryViewerOptions.Type.STORY_ARCHIVE | ||||
|             -> { | ||||
|                 val response = service.getReelsMedia(options.name) | ||||
|                 val story: Story? = response.reels?.get(options.name) | ||||
|                 story?.items ?: emptyList() | ||||
|             } | ||||
|             models | ||||
|         } else emptyList() | ||||
|             StoryViewerOptions.Type.USER -> { | ||||
|                 val response = service.getUserStories(options.id.toString()) | ||||
|                 response.reel?.items ?: emptyList() | ||||
|             } | ||||
|             // should not reach beyond this point | ||||
|             StoryViewerOptions.Type.LOCATION -> { | ||||
|                 val response = service.getStories("locations", options.id.toString()) | ||||
|                 response.story?.items ?: emptyList() | ||||
|             } | ||||
|             StoryViewerOptions.Type.HASHTAG -> { | ||||
|                 val response = service.getStories("tags", options.name) | ||||
|                 response.story?.items ?: emptyList() | ||||
|             } | ||||
|             else -> emptyList() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private suspend fun respondToSticker( | ||||
| @ -102,7 +97,7 @@ open class StoriesRepository(private val service: StoriesService) { | ||||
|         userId: Long, | ||||
|         deviceUuid: String, | ||||
|         storyId: String, | ||||
|         stickerId: String, | ||||
|         stickerId: Long, | ||||
|         action: String, | ||||
|         arg1: String, | ||||
|         arg2: String, | ||||
| @ -125,7 +120,7 @@ open class StoriesRepository(private val service: StoriesService) { | ||||
|         userId: Long, | ||||
|         deviceUuid: String, | ||||
|         storyId: String, | ||||
|         stickerId: String, | ||||
|         stickerId: Long, | ||||
|         answer: String, | ||||
|     ): StoryStickerResponse = respondToSticker(csrfToken, userId, deviceUuid, storyId, stickerId, "story_question_response", "response", answer) | ||||
| 
 | ||||
| @ -134,7 +129,7 @@ open class StoriesRepository(private val service: StoriesService) { | ||||
|         userId: Long, | ||||
|         deviceUuid: String, | ||||
|         storyId: String, | ||||
|         stickerId: String, | ||||
|         stickerId: Long, | ||||
|         answer: Int, | ||||
|     ): StoryStickerResponse { | ||||
|         return respondToSticker(csrfToken, userId, deviceUuid, storyId, stickerId, "story_quiz_answer", "answer", answer.toString()) | ||||
| @ -145,7 +140,7 @@ open class StoriesRepository(private val service: StoriesService) { | ||||
|         userId: Long, | ||||
|         deviceUuid: String, | ||||
|         storyId: String, | ||||
|         stickerId: String, | ||||
|         stickerId: Long, | ||||
|         answer: Int, | ||||
|     ): StoryStickerResponse = respondToSticker(csrfToken, userId, deviceUuid, storyId, stickerId, "story_poll_vote", "vote", answer.toString()) | ||||
| 
 | ||||
| @ -154,7 +149,7 @@ open class StoriesRepository(private val service: StoriesService) { | ||||
|         userId: Long, | ||||
|         deviceUuid: String, | ||||
|         storyId: String, | ||||
|         stickerId: String, | ||||
|         stickerId: Long, | ||||
|         answer: Double, | ||||
|     ): StoryStickerResponse = respondToSticker(csrfToken, userId, deviceUuid, storyId, stickerId, "story_slider_vote", "vote", answer.toString()) | ||||
| 
 | ||||
| @ -182,43 +177,6 @@ open class StoriesRepository(private val service: StoriesService) { | ||||
|         return service.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 | ||||
|             } | ||||
|             StoryViewerOptions.Type.LOCATION -> { | ||||
|                 builder.append("locations/") | ||||
|                 id = options.id.toString() | ||||
|             } | ||||
|             StoryViewerOptions.Type.USER -> { | ||||
|                 builder.append("feed/user/") | ||||
|                 id = options.id.toString() | ||||
|             } | ||||
|             StoryViewerOptions.Type.HIGHLIGHT, StoryViewerOptions.Type.STORY_ARCHIVE -> { | ||||
|                 builder.append("feed/reels_media/?user_ids=") | ||||
|                 id = options.name | ||||
|             } | ||||
|             StoryViewerOptions.Type.STORY -> { | ||||
|             } | ||||
|             else -> { | ||||
|             } | ||||
|         } | ||||
|         if (id == null) { | ||||
|             return null | ||||
|         } | ||||
|         builder.append(id) | ||||
|         if (type != StoryViewerOptions.Type.HIGHLIGHT && type != StoryViewerOptions.Type.STORY_ARCHIVE) { | ||||
|             builder.append("/story/") | ||||
|         } | ||||
|         return builder.toString() | ||||
|     } | ||||
| 
 | ||||
|     private fun sort(list: List<Story>): List<Story> { | ||||
|         val listCopy = ArrayList(list) | ||||
|         listCopy.sortWith { o1, o2 -> | ||||
|  | ||||
| @ -10,12 +10,12 @@ import awais.instagrabber.db.entities.Favorite | ||||
| import awais.instagrabber.db.repositories.FavoriteRepository | ||||
| import awais.instagrabber.getOrAwaitValue | ||||
| import awais.instagrabber.models.Resource | ||||
| import awais.instagrabber.models.StoryModel | ||||
| import awais.instagrabber.models.enums.FavoriteType | ||||
| import awais.instagrabber.repositories.requests.StoryViewerOptions | ||||
| import awais.instagrabber.repositories.responses.FriendshipStatus | ||||
| import awais.instagrabber.repositories.responses.User | ||||
| import awais.instagrabber.repositories.responses.stories.Story | ||||
| import awais.instagrabber.repositories.responses.stories.StoryMedia | ||||
| import awais.instagrabber.webservices.* | ||||
| import kotlinx.coroutines.ExperimentalCoroutinesApi | ||||
| import org.json.JSONException | ||||
| @ -320,13 +320,13 @@ internal class ProfileFragmentViewModelTest { | ||||
|                 "username" to testPublicUser.username | ||||
|             ) | ||||
|         ) | ||||
|         val testUserStories = listOf(StoryModel()) | ||||
|         val testUserStories = listOf(StoryMedia()) | ||||
|         val testUserHighlights = listOf(Story()) | ||||
|         val userRepository = object : UserRepository(UserServiceAdapter()) { | ||||
|             override suspend fun getUsernameInfo(username: String): User = testPublicUser | ||||
|         } | ||||
|         val storiesRepository = object : StoriesRepository(StoriesServiceAdapter()) { | ||||
|             override suspend fun getUserStory(options: StoryViewerOptions): List<StoryModel> = testUserStories | ||||
|             override suspend fun getStories(options: StoryViewerOptions): List<StoryMedia> = testUserStories | ||||
|             override suspend fun fetchHighlights(profileId: Long): List<Story> = testUserHighlights | ||||
|         } | ||||
|         val viewModel = ProfileFragmentViewModel( | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user