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

convert FeedStoryModel to Story and Broadcast response data classes

there seems to be a toolbar problem with live stories, will check tomorrow
This commit is contained in:
Austin Huang 2021-06-28 22:34:02 -04:00
parent aa175c5101
commit 817a16873c
No known key found for this signature in database
GPG Key ID: 84C23AA04587A91F
18 changed files with 197 additions and 236 deletions

View File

@ -9,20 +9,20 @@ import androidx.recyclerview.widget.ListAdapter;
import awais.instagrabber.adapters.viewholder.FeedStoryViewHolder; import awais.instagrabber.adapters.viewholder.FeedStoryViewHolder;
import awais.instagrabber.databinding.ItemHighlightBinding; import awais.instagrabber.databinding.ItemHighlightBinding;
import awais.instagrabber.models.FeedStoryModel; import awais.instagrabber.repositories.responses.stories.Story;
public final class FeedStoriesAdapter extends ListAdapter<FeedStoryModel, FeedStoryViewHolder> { public final class FeedStoriesAdapter extends ListAdapter<Story, FeedStoryViewHolder> {
private final OnFeedStoryClickListener listener; private final OnFeedStoryClickListener listener;
private static final DiffUtil.ItemCallback<FeedStoryModel> diffCallback = new DiffUtil.ItemCallback<FeedStoryModel>() { private static final DiffUtil.ItemCallback<Story> diffCallback = new DiffUtil.ItemCallback<Story>() {
@Override @Override
public boolean areItemsTheSame(@NonNull final FeedStoryModel oldItem, @NonNull final FeedStoryModel newItem) { public boolean areItemsTheSame(@NonNull final Story oldItem, @NonNull final Story newItem) {
return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId()); return oldItem.getId().equals(newItem.getId());
} }
@Override @Override
public boolean areContentsTheSame(@NonNull final FeedStoryModel oldItem, @NonNull final FeedStoryModel newItem) { public boolean areContentsTheSame(@NonNull final Story oldItem, @NonNull final Story newItem) {
return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId()) && oldItem.isFullyRead() == newItem.isFullyRead(); return oldItem.getId().equals(newItem.getId()) && oldItem.getSeen() == newItem.getSeen();
} }
}; };
@ -41,13 +41,13 @@ public final class FeedStoriesAdapter extends ListAdapter<FeedStoryModel, FeedSt
@Override @Override
public void onBindViewHolder(@NonNull final FeedStoryViewHolder holder, final int position) { public void onBindViewHolder(@NonNull final FeedStoryViewHolder holder, final int position) {
final FeedStoryModel model = getItem(position); final Story model = getItem(position);
holder.bind(model, position, listener); holder.bind(model, position, listener);
} }
public interface OnFeedStoryClickListener { public interface OnFeedStoryClickListener {
void onFeedStoryClick(FeedStoryModel model, int position); void onFeedStoryClick(Story model, int position);
void onFeedStoryLongClick(FeedStoryModel model, int position); void onFeedStoryLongClick(Story model, int position);
} }
} }

View File

@ -15,22 +15,22 @@ import java.util.stream.Collectors;
import awais.instagrabber.adapters.viewholder.StoryListViewHolder; import awais.instagrabber.adapters.viewholder.StoryListViewHolder;
import awais.instagrabber.databinding.ItemNotificationBinding; import awais.instagrabber.databinding.ItemNotificationBinding;
import awais.instagrabber.models.FeedStoryModel; import awais.instagrabber.repositories.responses.stories.Story;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
public final class FeedStoriesListAdapter extends ListAdapter<FeedStoryModel, StoryListViewHolder> implements Filterable { public final class FeedStoriesListAdapter extends ListAdapter<Story, StoryListViewHolder> implements Filterable {
private final OnFeedStoryClickListener listener; private final OnFeedStoryClickListener listener;
private List<FeedStoryModel> list; private List<Story> list;
private final Filter filter = new Filter() { private final Filter filter = new Filter() {
@NonNull @NonNull
@Override @Override
protected FilterResults performFiltering(final CharSequence filter) { protected FilterResults performFiltering(final CharSequence filter) {
final String query = TextUtils.isEmpty(filter) ? null : filter.toString().toLowerCase(); final String query = TextUtils.isEmpty(filter) ? null : filter.toString().toLowerCase();
List<FeedStoryModel> filteredList = list; List<Story> filteredList = list;
if (list != null && query != null) { if (list != null && query != null) {
filteredList = list.stream() filteredList = list.stream()
.filter(feedStoryModel -> feedStoryModel.getProfileModel() .filter(feedStoryModel -> feedStoryModel.getUser()
.getUsername() .getUsername()
.toLowerCase() .toLowerCase()
.contains(query)) .contains(query))
@ -45,19 +45,19 @@ public final class FeedStoriesListAdapter extends ListAdapter<FeedStoryModel, St
@Override @Override
protected void publishResults(final CharSequence constraint, final FilterResults results) { protected void publishResults(final CharSequence constraint, final FilterResults results) {
//noinspection unchecked //noinspection unchecked
submitList((List<FeedStoryModel>) results.values, true); submitList((List<Story>) results.values, true);
} }
}; };
private static final DiffUtil.ItemCallback<FeedStoryModel> diffCallback = new DiffUtil.ItemCallback<FeedStoryModel>() { private static final DiffUtil.ItemCallback<Story> diffCallback = new DiffUtil.ItemCallback<Story>() {
@Override @Override
public boolean areItemsTheSame(@NonNull final FeedStoryModel oldItem, @NonNull final FeedStoryModel newItem) { public boolean areItemsTheSame(@NonNull final Story oldItem, @NonNull final Story newItem) {
return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId()); return oldItem.getId().equals(newItem.getId());
} }
@Override @Override
public boolean areContentsTheSame(@NonNull final FeedStoryModel oldItem, @NonNull final FeedStoryModel newItem) { public boolean areContentsTheSame(@NonNull final Story oldItem, @NonNull final Story newItem) {
return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId()) && oldItem.isFullyRead() == newItem.isFullyRead(); return oldItem.getId().equals(newItem.getId()) && oldItem.getSeen() == newItem.getSeen();
} }
}; };
@ -71,7 +71,7 @@ public final class FeedStoriesListAdapter extends ListAdapter<FeedStoryModel, St
return filter; return filter;
} }
private void submitList(@Nullable final List<FeedStoryModel> list, final boolean isFiltered) { private void submitList(@Nullable final List<Story> list, final boolean isFiltered) {
if (!isFiltered) { if (!isFiltered) {
this.list = list; this.list = list;
} }
@ -79,7 +79,7 @@ public final class FeedStoriesListAdapter extends ListAdapter<FeedStoryModel, St
} }
@Override @Override
public void submitList(final List<FeedStoryModel> list) { public void submitList(final List<Story> list) {
submitList(list, false); submitList(list, false);
} }
@ -93,12 +93,12 @@ public final class FeedStoriesListAdapter extends ListAdapter<FeedStoryModel, St
@Override @Override
public void onBindViewHolder(@NonNull final StoryListViewHolder holder, final int position) { public void onBindViewHolder(@NonNull final StoryListViewHolder holder, final int position) {
final FeedStoryModel model = getItem(position); final Story model = getItem(position);
holder.bind(model, listener); holder.bind(model, listener);
} }
public interface OnFeedStoryClickListener { public interface OnFeedStoryClickListener {
void onFeedStoryClick(final FeedStoryModel model); void onFeedStoryClick(final Story model);
void onProfileClick(final String username); void onProfileClick(final String username);
} }

View File

@ -4,7 +4,7 @@ import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.adapters.FeedStoriesAdapter; import awais.instagrabber.adapters.FeedStoriesAdapter;
import awais.instagrabber.databinding.ItemHighlightBinding; import awais.instagrabber.databinding.ItemHighlightBinding;
import awais.instagrabber.models.FeedStoryModel; import awais.instagrabber.repositories.responses.stories.Story;
import awais.instagrabber.repositories.responses.User; import awais.instagrabber.repositories.responses.User;
public final class FeedStoryViewHolder extends RecyclerView.ViewHolder { public final class FeedStoryViewHolder extends RecyclerView.ViewHolder {
@ -16,7 +16,7 @@ public final class FeedStoryViewHolder extends RecyclerView.ViewHolder {
this.binding = binding; this.binding = binding;
} }
public void bind(final FeedStoryModel model, public void bind(final Story model,
final int position, final int position,
final FeedStoriesAdapter.OnFeedStoryClickListener listener) { final FeedStoriesAdapter.OnFeedStoryClickListener listener) {
if (model == null) return; if (model == null) return;
@ -28,14 +28,17 @@ public final class FeedStoryViewHolder extends RecyclerView.ViewHolder {
if (listener != null) listener.onFeedStoryLongClick(model, position); if (listener != null) listener.onFeedStoryLongClick(model, position);
return true; return true;
}); });
final User profileModel = model.getProfileModel(); final User profileModel = model.getUser();
binding.title.setText(profileModel.getUsername()); binding.title.setText(profileModel.getUsername());
binding.title.setAlpha(model.isFullyRead() ? 0.5F : 1.0F); final boolean isFullyRead =
model.getSeen() != null &&
model.getSeen().equals(model.getLatestReelMedia());
binding.title.setAlpha(isFullyRead ? 0.5F : 1.0F);
binding.icon.setImageURI(profileModel.getProfilePicUrl()); binding.icon.setImageURI(profileModel.getProfilePicUrl());
binding.icon.setAlpha(model.isFullyRead() ? 0.5F : 1.0F); binding.icon.setAlpha(isFullyRead ? 0.5F : 1.0F);
if (model.isLive()) binding.icon.setStoriesBorder(2); if (model.getBroadcast() != null) binding.icon.setStoriesBorder(2);
else if (model.isBestie()) binding.icon.setStoriesBorder(1); else if (model.getHasBestiesMedia()) binding.icon.setStoriesBorder(1);
else binding.icon.setStoriesBorder(0); else binding.icon.setStoriesBorder(0);
} }
} }

View File

@ -8,8 +8,9 @@ import awais.instagrabber.R;
import awais.instagrabber.adapters.FeedStoriesListAdapter.OnFeedStoryClickListener; import awais.instagrabber.adapters.FeedStoriesListAdapter.OnFeedStoryClickListener;
import awais.instagrabber.adapters.HighlightStoriesListAdapter.OnHighlightStoryClickListener; import awais.instagrabber.adapters.HighlightStoriesListAdapter.OnHighlightStoryClickListener;
import awais.instagrabber.databinding.ItemNotificationBinding; import awais.instagrabber.databinding.ItemNotificationBinding;
import awais.instagrabber.models.FeedStoryModel;
import awais.instagrabber.models.HighlightModel; import awais.instagrabber.models.HighlightModel;
import awais.instagrabber.repositories.responses.stories.Story;
import awais.instagrabber.utils.ResponseBodyUtils;
public final class StoryListViewHolder extends RecyclerView.ViewHolder { public final class StoryListViewHolder extends RecyclerView.ViewHolder {
private final ItemNotificationBinding binding; private final ItemNotificationBinding binding;
@ -19,7 +20,7 @@ public final class StoryListViewHolder extends RecyclerView.ViewHolder {
this.binding = binding; this.binding = binding;
} }
public void bind(final FeedStoryModel model, public void bind(final Story model,
final OnFeedStoryClickListener notificationClickListener) { final OnFeedStoryClickListener notificationClickListener) {
if (model == null) return; if (model == null) return;
@ -31,19 +32,20 @@ public final class StoryListViewHolder extends RecyclerView.ViewHolder {
binding.tvDate.setText(model.getDateTime()); binding.tvDate.setText(model.getDateTime());
binding.tvUsername.setText(model.getProfileModel().getUsername()); binding.tvUsername.setText(model.getUser().getUsername());
binding.ivProfilePic.setImageURI(model.getProfileModel().getProfilePicUrl()); binding.ivProfilePic.setImageURI(model.getUser().getProfilePicUrl());
binding.ivProfilePic.setOnClickListener(v -> { binding.ivProfilePic.setOnClickListener(v -> {
if (notificationClickListener == null) return; if (notificationClickListener == null) return;
notificationClickListener.onProfileClick(model.getProfileModel().getUsername()); notificationClickListener.onProfileClick(model.getUser().getUsername());
}); });
if (model.getFirstStoryModel() != null) { if (model.getItems() != null && model.getItems().size() > 0) {
binding.ivPreviewPic.setVisibility(View.VISIBLE); binding.ivPreviewPic.setVisibility(View.VISIBLE);
binding.ivPreviewPic.setImageURI(model.getFirstStoryModel().getThumbnail()); binding.ivPreviewPic.setImageURI(ResponseBodyUtils.getThumbUrl(model.getItems().get(0)));
} else binding.ivPreviewPic.setVisibility(View.INVISIBLE); } else binding.ivPreviewPic.setVisibility(View.INVISIBLE);
float alpha = model.isFullyRead() ? 0.5F : 1.0F; float alpha = model.getSeen() != null && model.getSeen().equals(model.getLatestReelMedia())
? 0.5F : 1.0F;
binding.ivProfilePic.setAlpha(alpha); binding.ivProfilePic.setAlpha(alpha);
binding.ivPreviewPic.setAlpha(alpha); binding.ivPreviewPic.setAlpha(alpha);
binding.tvUsername.setAlpha(alpha); binding.tvUsername.setAlpha(alpha);

View File

@ -38,9 +38,9 @@ import awais.instagrabber.adapters.HighlightStoriesListAdapter.OnHighlightStoryC
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader; import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
import awais.instagrabber.databinding.FragmentStoryListViewerBinding; import awais.instagrabber.databinding.FragmentStoryListViewerBinding;
import awais.instagrabber.fragments.settings.MorePreferencesFragmentDirections; import awais.instagrabber.fragments.settings.MorePreferencesFragmentDirections;
import awais.instagrabber.models.FeedStoryModel;
import awais.instagrabber.models.HighlightModel; import awais.instagrabber.models.HighlightModel;
import awais.instagrabber.repositories.requests.StoryViewerOptions; import awais.instagrabber.repositories.requests.StoryViewerOptions;
import awais.instagrabber.repositories.responses.stories.Story;
import awais.instagrabber.utils.AppExecutors; import awais.instagrabber.utils.AppExecutors;
import awais.instagrabber.utils.CoroutineUtilsKt; import awais.instagrabber.utils.CoroutineUtilsKt;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
@ -69,12 +69,12 @@ public final class StoryListViewerFragment extends Fragment implements SwipeRefr
private final OnFeedStoryClickListener clickListener = new OnFeedStoryClickListener() { private final OnFeedStoryClickListener clickListener = new OnFeedStoryClickListener() {
@Override @Override
public void onFeedStoryClick(final FeedStoryModel model) { public void onFeedStoryClick(final Story model) {
if (model == null) return; if (model == null) return;
final List<FeedStoryModel> feedStoryModels = feedStoriesViewModel.getList().getValue(); final List<Story> feedStoryModels = feedStoriesViewModel.getList().getValue();
if (feedStoryModels == null) return; if (feedStoryModels == null) return;
final int position = Iterables.indexOf(feedStoryModels, feedStoryModel -> feedStoryModel != null final int position = Iterables.indexOf(feedStoryModels, feedStoryModel -> feedStoryModel != null
&& Objects.equals(feedStoryModel.getStoryMediaId(), model.getStoryMediaId())); && Objects.equals(feedStoryModel.getId(), model.getId()));
final NavDirections action = StoryListViewerFragmentDirections final NavDirections action = StoryListViewerFragmentDirections
.actionStoryListFragmentToStoryViewerFragment(StoryViewerOptions.forFeedStoryPosition(position)); .actionStoryListFragmentToStoryViewerFragment(StoryViewerOptions.forFeedStoryPosition(position));
NavHostFragment.findNavController(StoryListViewerFragment.this).navigate(action); NavHostFragment.findNavController(StoryListViewerFragment.this).navigate(action);
@ -236,7 +236,7 @@ public final class StoryListViewerFragment extends Fragment implements SwipeRefr
binding.swipeRefreshLayout.setRefreshing(true); binding.swipeRefreshLayout.setRefreshing(true);
if (type.equals("feed") && firstRefresh) { if (type.equals("feed") && firstRefresh) {
binding.swipeRefreshLayout.setRefreshing(false); binding.swipeRefreshLayout.setRefreshing(false);
final List<FeedStoryModel> value = feedStoriesViewModel.getList().getValue(); final List<Story> value = feedStoriesViewModel.getList().getValue();
if (value != null) { if (value != null) {
adapter.submitList(value); adapter.submitList(value);
} }
@ -250,9 +250,9 @@ public final class StoryListViewerFragment extends Fragment implements SwipeRefr
return; return;
} }
//noinspection unchecked //noinspection unchecked
feedStoriesViewModel.getList().postValue((List<FeedStoryModel>) feedStoryModels); feedStoriesViewModel.getList().postValue((List<Story>) feedStoryModels);
//noinspection unchecked //noinspection unchecked
adapter.submitList((List<FeedStoryModel>) feedStoryModels); adapter.submitList((List<Story>) feedStoryModels);
binding.swipeRefreshLayout.setRefreshing(false); binding.swipeRefreshLayout.setRefreshing(false);
}), Dispatchers.getIO()) }), Dispatchers.getIO())
); );

View File

@ -75,7 +75,6 @@ import awais.instagrabber.databinding.FragmentStoryViewerBinding;
import awais.instagrabber.fragments.main.ProfileFragmentDirections; import awais.instagrabber.fragments.main.ProfileFragmentDirections;
import awais.instagrabber.fragments.settings.PreferenceKeys; import awais.instagrabber.fragments.settings.PreferenceKeys;
import awais.instagrabber.interfaces.SwipeEvent; import awais.instagrabber.interfaces.SwipeEvent;
import awais.instagrabber.models.FeedStoryModel;
import awais.instagrabber.models.HighlightModel; import awais.instagrabber.models.HighlightModel;
import awais.instagrabber.models.StoryModel; import awais.instagrabber.models.StoryModel;
import awais.instagrabber.models.enums.MediaItemType; import awais.instagrabber.models.enums.MediaItemType;
@ -87,6 +86,8 @@ import awais.instagrabber.models.stickers.SwipeUpModel;
import awais.instagrabber.repositories.requests.StoryViewerOptions; import awais.instagrabber.repositories.requests.StoryViewerOptions;
import awais.instagrabber.repositories.requests.StoryViewerOptions.Type; import awais.instagrabber.repositories.requests.StoryViewerOptions.Type;
import awais.instagrabber.repositories.requests.directmessages.ThreadIdsOrUserIds; import awais.instagrabber.repositories.requests.directmessages.ThreadIdsOrUserIds;
import awais.instagrabber.repositories.responses.stories.Broadcast;
import awais.instagrabber.repositories.responses.stories.Story;
import awais.instagrabber.utils.AppExecutors; import awais.instagrabber.utils.AppExecutors;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.CookieUtils;
@ -125,6 +126,7 @@ public class StoryViewerFragment extends Fragment {
private StoriesRepository storiesRepository; private StoriesRepository storiesRepository;
private MediaRepository mediaRepository; private MediaRepository mediaRepository;
private StoryModel currentStory; private StoryModel currentStory;
private Broadcast live;
private int slidePos; private int slidePos;
private int lastSlidePos; private int lastSlidePos;
private String url; private String url;
@ -717,7 +719,7 @@ public class StoryViewerFragment extends Fragment {
private void resetView() { private void resetView() {
final Context context = getContext(); final Context context = getContext();
if (context == null) return; if (context == null) return;
StoryModel live = null; live = null;
slidePos = 0; slidePos = 0;
lastSlidePos = 0; lastSlidePos = 0;
if (menuDownload != null) menuDownload.setVisible(false); if (menuDownload != null) menuDownload.setVisible(false);
@ -747,15 +749,13 @@ public class StoryViewerFragment extends Fragment {
} }
case FEED_STORY_POSITION: { case FEED_STORY_POSITION: {
final FeedStoriesViewModel feedStoriesViewModel = (FeedStoriesViewModel) viewModel; final FeedStoriesViewModel feedStoriesViewModel = (FeedStoriesViewModel) viewModel;
final List<FeedStoryModel> models = feedStoriesViewModel.getList().getValue(); 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 FeedStoryModel model = models.get(currentFeedStoryIndex); final Story model = models.get(currentFeedStoryIndex);
currentStoryMediaId = model.getStoryMediaId(); currentStoryMediaId = model.getId();
currentStoryUsername = model.getProfileModel().getUsername(); currentStoryUsername = model.getUser().getUsername();
fetchOptions = StoryViewerOptions.forUser(Long.parseLong(currentStoryMediaId), currentStoryUsername); fetchOptions = StoryViewerOptions.forUser(Long.parseLong(currentStoryMediaId), currentStoryUsername);
if (model.isLive()) { live = model.getBroadcast();
live = model.getFirstStoryModel();
}
break; break;
} }
case STORY_ARCHIVE: { case STORY_ARCHIVE: {
@ -803,6 +803,11 @@ public class StoryViewerFragment extends Fragment {
return; return;
} }
if (currentStoryMediaId == null) return; if (currentStoryMediaId == null) return;
if (live != null) {
currentStory = null;
refreshLive();
return;
}
final ServiceCallback<List<StoryModel>> storyCallback = new ServiceCallback<List<StoryModel>>() { final ServiceCallback<List<StoryModel>> storyCallback = new ServiceCallback<List<StoryModel>>() {
@Override @Override
public void onSuccess(final List<StoryModel> storyModels) { public void onSuccess(final List<StoryModel> storyModels) {
@ -829,10 +834,6 @@ public class StoryViewerFragment extends Fragment {
Log.e(TAG, "Error", t); Log.e(TAG, "Error", t);
} }
}; };
if (live != null) {
storyCallback.onSuccess(Collections.singletonList(live));
return;
}
storiesRepository.getUserStory( storiesRepository.getUserStory(
fetchOptions, fetchOptions,
CoroutineUtilsKt.getContinuation((storyModels, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { CoroutineUtilsKt.getContinuation((storyModels, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
@ -864,6 +865,30 @@ public class StoryViewerFragment extends Fragment {
} }
} }
private synchronized void refreshLive() {
binding.storiesList.setVisibility(View.INVISIBLE);
binding.viewStoryPost.setVisibility(View.GONE);
binding.spotify.setVisibility(View.GONE);
binding.poll.setVisibility(View.GONE);
binding.answer.setVisibility(View.GONE);
binding.mention.setVisibility(View.GONE);
binding.quiz.setVisibility(View.GONE);
binding.slider.setVisibility(View.GONE);
lastSlidePos = slidePos;
releasePlayer();
url = live.getDashPlaybackUrl();
setupLive();
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
actionBarSubtitle = TextUtils.epochSecondToString(live.getPublishedTime());
if (actionBar != null) {
try {
actionBar.setSubtitle(actionBarSubtitle);
} catch (Exception e) {
Log.e(TAG, "refreshLive: ", e);
}
}
}
private synchronized void refreshStory() { private synchronized void refreshStory() {
if (binding.storiesList.getVisibility() == View.VISIBLE) { if (binding.storiesList.getVisibility() == View.VISIBLE) {
final List<StoryModel> storyModels = storiesViewModel.getList().getValue(); final List<StoryModel> storyModels = storiesViewModel.getList().getValue();
@ -886,7 +911,6 @@ public class StoryViewerFragment extends Fragment {
url = itemType == MediaItemType.MEDIA_TYPE_IMAGE ? currentStory.getStoryUrl() : currentStory.getVideoUrl(); url = itemType == MediaItemType.MEDIA_TYPE_IMAGE ? currentStory.getStoryUrl() : currentStory.getVideoUrl();
if (itemType != MediaItemType.MEDIA_TYPE_LIVE) {
final String shortCode = currentStory.getTappableShortCode(); final String shortCode = currentStory.getTappableShortCode();
binding.viewStoryPost.setVisibility(shortCode != null ? View.VISIBLE : View.GONE); binding.viewStoryPost.setVisibility(shortCode != null ? View.VISIBLE : View.GONE);
binding.viewStoryPost.setTag(shortCode); binding.viewStoryPost.setTag(shortCode);
@ -921,7 +945,6 @@ public class StoryViewerFragment extends Fragment {
binding.swipeUp.setText(swipeUp.getText()); binding.swipeUp.setText(swipeUp.getText());
binding.swipeUp.setTag(swipeUp.getUrl()); binding.swipeUp.setTag(swipeUp.getUrl());
} else binding.swipeUp.setVisibility(View.GONE); } else binding.swipeUp.setVisibility(View.GONE);
}
releasePlayer(); releasePlayer();
final Type type = options.getType(); final Type type = options.getType();
@ -933,7 +956,6 @@ public class StoryViewerFragment extends Fragment {
} }
} }
if (itemType == MediaItemType.MEDIA_TYPE_VIDEO) setupVideo(); if (itemType == MediaItemType.MEDIA_TYPE_VIDEO) setupVideo();
else if (itemType == MediaItemType.MEDIA_TYPE_LIVE) setupLive();
else setupImage(); else setupImage();
final ActionBar actionBar = fragmentActivity.getSupportActionBar(); final ActionBar actionBar = fragmentActivity.getSupportActionBar();
@ -1205,14 +1227,14 @@ public class StoryViewerFragment extends Fragment {
return; return;
} }
if (settingsHelper.getBoolean(MARK_AS_SEEN) if (settingsHelper.getBoolean(MARK_AS_SEEN)
&& oldFeedStory instanceof FeedStoryModel && oldFeedStory instanceof Story
&& viewModel instanceof FeedStoriesViewModel) { && viewModel instanceof FeedStoriesViewModel) {
final FeedStoriesViewModel feedStoriesViewModel = (FeedStoriesViewModel) viewModel; final FeedStoriesViewModel feedStoriesViewModel = (FeedStoriesViewModel) viewModel;
final FeedStoryModel oldFeedStoryModel = (FeedStoryModel) oldFeedStory; final Story oldFeedStoryModel = (Story) oldFeedStory;
if (!oldFeedStoryModel.isFullyRead()) { if (oldFeedStoryModel.getSeen() == null || !oldFeedStoryModel.getSeen().equals(oldFeedStoryModel.getLatestReelMedia())) {
oldFeedStoryModel.setFullyRead(true); oldFeedStoryModel.setSeen(oldFeedStoryModel.getLatestReelMedia());
final List<FeedStoryModel> models = feedStoriesViewModel.getList().getValue(); final List<Story> models = feedStoriesViewModel.getList().getValue();
final List<FeedStoryModel> modelsCopy = models == null ? new ArrayList<>() : new ArrayList<>(models); final List<Story> modelsCopy = models == null ? new ArrayList<>() : new ArrayList<>(models);
modelsCopy.set(currentFeedStoryIndex, oldFeedStoryModel); modelsCopy.set(currentFeedStoryIndex, oldFeedStoryModel);
feedStoriesViewModel.getList().postValue(models); feedStoriesViewModel.getList().postValue(models);
} }

View File

@ -43,9 +43,9 @@ import awais.instagrabber.customviews.PrimaryActionModeCallback;
import awais.instagrabber.databinding.FragmentFeedBinding; import awais.instagrabber.databinding.FragmentFeedBinding;
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment; import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
import awais.instagrabber.fragments.PostViewV2Fragment; import awais.instagrabber.fragments.PostViewV2Fragment;
import awais.instagrabber.models.FeedStoryModel;
import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.models.PostsLayoutPreferences;
import awais.instagrabber.repositories.requests.StoryViewerOptions; import awais.instagrabber.repositories.requests.StoryViewerOptions;
import awais.instagrabber.repositories.responses.stories.Story;
import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.utils.AppExecutors; import awais.instagrabber.utils.AppExecutors;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
@ -76,7 +76,7 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
private final FeedStoriesAdapter feedStoriesAdapter = new FeedStoriesAdapter( private final FeedStoriesAdapter feedStoriesAdapter = new FeedStoriesAdapter(
new FeedStoriesAdapter.OnFeedStoryClickListener() { new FeedStoriesAdapter.OnFeedStoryClickListener() {
@Override @Override
public void onFeedStoryClick(FeedStoryModel model, int position) { public void onFeedStoryClick(Story model, int position) {
final NavController navController = NavHostFragment.findNavController(FeedFragment.this); final NavController navController = NavHostFragment.findNavController(FeedFragment.this);
if (isSafeToNavigate(navController)) { if (isSafeToNavigate(navController)) {
final NavDirections action = FeedFragmentDirections final NavDirections action = FeedFragmentDirections
@ -86,8 +86,8 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
} }
@Override @Override
public void onFeedStoryLongClick(FeedStoryModel model, int position) { public void onFeedStoryLongClick(Story model, int position) {
navigateToProfile("@" + model.getProfileModel().getUsername()); navigateToProfile("@" + model.getUser().getUsername());
} }
} }
); );
@ -399,9 +399,9 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
} }
storiesFetching = false; storiesFetching = false;
//noinspection unchecked //noinspection unchecked
feedStoriesViewModel.getList().postValue((List<FeedStoryModel>) feedStoryModels); feedStoriesViewModel.getList().postValue((List<Story>) feedStoryModels);
//noinspection unchecked //noinspection unchecked
feedStoriesAdapter.submitList((List<FeedStoryModel>) feedStoryModels); feedStoriesAdapter.submitList((List<Story>) feedStoryModels);
if (storyListMenu != null) storyListMenu.setVisible(true); if (storyListMenu != null) storyListMenu.setVisible(true);
updateSwipeRefreshState(); updateSwipeRefreshState();
}), Dispatchers.getIO()) }), Dispatchers.getIO())

View File

@ -1,20 +0,0 @@
package awais.instagrabber.models
import awais.instagrabber.repositories.responses.User
import awais.instagrabber.utils.TextUtils
import java.io.Serializable
import java.util.*
data class FeedStoryModel(
val storyMediaId: String,
val profileModel: User,
var isFullyRead: Boolean,
val timestamp: Long,
val firstStoryModel: StoryModel?,
val mediaCount: Int,
val isLive: Boolean,
val isBestie: Boolean
) : Serializable {
val dateTime: String
get() = TextUtils.epochSecondToString(timestamp)
}

View File

@ -1,6 +1,7 @@
package awais.instagrabber.repositories package awais.instagrabber.repositories
import awais.instagrabber.repositories.responses.StoryStickerResponse import awais.instagrabber.repositories.responses.stories.ReelsTrayResponse
import awais.instagrabber.repositories.responses.stories.StoryStickerResponse
import retrofit2.http.* import retrofit2.http.*
interface StoriesService { interface StoriesService {
@ -9,7 +10,7 @@ interface StoriesService {
suspend fun fetch(@Path("mediaId") mediaId: Long): String suspend fun fetch(@Path("mediaId") mediaId: Long): String
@GET("/api/v1/feed/reels_tray/") @GET("/api/v1/feed/reels_tray/")
suspend fun getFeedStories(): String suspend fun getFeedStories(): ReelsTrayResponse
@GET("/api/v1/highlights/{uid}/highlights_tray/") @GET("/api/v1/highlights/{uid}/highlights_tray/")
suspend fun fetchHighlights(@Path("uid") uid: Long): String suspend fun fetchHighlights(@Path("uid") uid: Long): String

View File

@ -4,7 +4,7 @@ import java.io.Serializable
import awais.instagrabber.repositories.responses.User import awais.instagrabber.repositories.responses.User
data class Broadcast( data class Broadcast(
val id: Long?, val id: String?,
val dashPlaybackUrl: String?, val dashPlaybackUrl: String?,
val dashAbrPlaybackUrl: String?, // adaptive quality val dashAbrPlaybackUrl: String?, // adaptive quality
val viewerCount: Double?, // always .0 val viewerCount: Double?, // always .0

View File

@ -3,14 +3,19 @@ package awais.instagrabber.repositories.responses.stories
import java.io.Serializable import java.io.Serializable
import awais.instagrabber.repositories.responses.Media import awais.instagrabber.repositories.responses.Media
import awais.instagrabber.repositories.responses.User import awais.instagrabber.repositories.responses.User
import awais.instagrabber.utils.TextUtils
data class Story( data class Story(
val id: Long?, val id: String?,
val latestReelMedia: Long?, // = timestamp val latestReelMedia: Long?, // = timestamp
val seen: Long?, var seen: Long?,
val user: User?, val user: User?,
val muted: Boolean?, val muted: Boolean?,
val hasBestiesMedia: Boolean?, val hasBestiesMedia: Boolean?,
val mediaCount: Int?, val mediaCount: Int?,
val items: List<Media>? // may be null val items: List<StoryMedia>?, // may be null
) : Serializable val broadcast: Broadcast? // does not naturally occur
) : Serializable {
val dateTime: String
get() = if (latestReelMedia != null) TextUtils.epochSecondToString(latestReelMedia) else ""
}

View File

@ -3,6 +3,7 @@ package awais.instagrabber.repositories.responses.stories
import awais.instagrabber.models.enums.MediaItemType import awais.instagrabber.models.enums.MediaItemType
import awais.instagrabber.utils.TextUtils import awais.instagrabber.utils.TextUtils
import awais.instagrabber.repositories.responses.ImageVersions2 import awais.instagrabber.repositories.responses.ImageVersions2
import awais.instagrabber.repositories.responses.Media
import awais.instagrabber.repositories.responses.User import awais.instagrabber.repositories.responses.User
import awais.instagrabber.repositories.responses.MediaCandidate import awais.instagrabber.repositories.responses.MediaCandidate
import java.io.Serializable import java.io.Serializable

View File

@ -1,20 +1,3 @@
package awais.instagrabber.repositories.responses; package awais.instagrabber.repositories.responses.stories
public class StoryStickerResponse { data class StoryStickerResponse(val status: String?)
private final String status;
public StoryStickerResponse(final String status) {
this.status = status;
}
public String getStatus() {
return status;
}
@Override
public String toString() {
return "StoryStickerResponse{" +
"status='" + status + '\'' +
'}';
}
}

View File

@ -21,6 +21,7 @@ import awais.instagrabber.models.stickers.QuestionModel;
import awais.instagrabber.models.stickers.QuizModel; import awais.instagrabber.models.stickers.QuizModel;
import awais.instagrabber.models.stickers.SliderModel; import awais.instagrabber.models.stickers.SliderModel;
import awais.instagrabber.models.stickers.SwipeUpModel; import awais.instagrabber.models.stickers.SwipeUpModel;
import awais.instagrabber.repositories.responses.stories.StoryMedia;
import awais.instagrabber.repositories.responses.Caption; import awais.instagrabber.repositories.responses.Caption;
import awais.instagrabber.repositories.responses.FriendshipStatus; import awais.instagrabber.repositories.responses.FriendshipStatus;
import awais.instagrabber.repositories.responses.ImageVersions2; import awais.instagrabber.repositories.responses.ImageVersions2;
@ -406,27 +407,38 @@ public final class ResponseBodyUtils {
return model; return model;
} }
public static String getThumbUrl(final Media media) { public static String getThumbUrl(final Object media) {
return getImageCandidate(media, CandidateType.THUMBNAIL); return getImageCandidate(media, CandidateType.THUMBNAIL);
} }
public static String getImageUrl(final Media media) { public static String getImageUrl(final Object media) {
return getImageCandidate(media, CandidateType.DOWNLOAD); return getImageCandidate(media, CandidateType.DOWNLOAD);
} }
private static String getImageCandidate(final Media media, final CandidateType type) { private static String getImageCandidate(final Object rawMedia, final CandidateType type) {
if (media == null) return null; final ImageVersions2 imageVersions2;
final ImageVersions2 imageVersions2 = media.getImageVersions2(); final int originalWidth, originalHeight;
if (rawMedia instanceof StoryMedia) {
imageVersions2 = ((StoryMedia) rawMedia).getImageVersions2();
originalWidth = ((StoryMedia) rawMedia).getOriginalWidth();
originalHeight = ((StoryMedia) rawMedia).getOriginalHeight();
}
else if (rawMedia instanceof Media) {
imageVersions2 = ((Media) rawMedia).getImageVersions2();
originalWidth = ((Media) rawMedia).getOriginalWidth();
originalHeight = ((Media) rawMedia).getOriginalHeight();
}
else return null;
if (imageVersions2 == null) return null; if (imageVersions2 == null) return null;
final List<MediaCandidate> candidates = imageVersions2.getCandidates(); final List<MediaCandidate> candidates = imageVersions2.getCandidates();
if (candidates == null || candidates.isEmpty()) 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() final List<MediaCandidate> sortedCandidates = candidates.stream()
.sorted((c1, c2) -> Integer.compare(c2.getWidth(), c1.getWidth())) .sorted((c1, c2) -> Integer.compare(c2.getWidth(), c1.getWidth()))
.collect(Collectors.toList()); .collect(Collectors.toList());
final List<MediaCandidate> filteredCandidates = sortedCandidates.stream() final List<MediaCandidate> filteredCandidates = sortedCandidates.stream()
.filter(c -> .filter(c ->
c.getWidth() <= media.getOriginalWidth() c.getWidth() <= originalWidth
&& c.getWidth() <= type.getValue() && c.getWidth() <= type.getValue()
&& (isSquare || Integer && (isSquare || Integer
.compare(c.getWidth(), c.getHeight()) != 0) .compare(c.getWidth(), c.getHeight()) != 0)

View File

@ -6,12 +6,12 @@ import androidx.lifecycle.ViewModel;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import awais.instagrabber.models.FeedStoryModel; import awais.instagrabber.repositories.responses.stories.Story;
public class FeedStoriesViewModel extends ViewModel { public class FeedStoriesViewModel extends ViewModel {
private MutableLiveData<List<FeedStoryModel>> list; private MutableLiveData<List<Story>> list;
public MutableLiveData<List<FeedStoryModel>> getList() { public MutableLiveData<List<Story>> getList() {
if (list == null) { if (list == null) {
list = new MutableLiveData<>(); list = new MutableLiveData<>();
} }

View File

@ -1,19 +1,16 @@
package awais.instagrabber.webservices package awais.instagrabber.webservices
import android.util.Log
import awais.instagrabber.fragments.settings.PreferenceKeys import awais.instagrabber.fragments.settings.PreferenceKeys
import awais.instagrabber.models.FeedStoryModel
import awais.instagrabber.models.HighlightModel import awais.instagrabber.models.HighlightModel
import awais.instagrabber.models.StoryModel import awais.instagrabber.models.StoryModel
import awais.instagrabber.repositories.StoriesService import awais.instagrabber.repositories.StoriesService
import awais.instagrabber.repositories.requests.StoryViewerOptions import awais.instagrabber.repositories.requests.StoryViewerOptions
import awais.instagrabber.repositories.responses.StoryStickerResponse import awais.instagrabber.repositories.responses.stories.Story
import awais.instagrabber.repositories.responses.User import awais.instagrabber.repositories.responses.stories.StoryStickerResponse
import awais.instagrabber.utils.Constants import awais.instagrabber.utils.Constants
import awais.instagrabber.utils.ResponseBodyUtils import awais.instagrabber.utils.ResponseBodyUtils
import awais.instagrabber.utils.TextUtils.isEmpty import awais.instagrabber.utils.TextUtils.isEmpty
import awais.instagrabber.utils.Utils import awais.instagrabber.utils.Utils
import awais.instagrabber.utils.extensions.TAG
import awais.instagrabber.webservices.RetrofitFactory.retrofit import awais.instagrabber.webservices.RetrofitFactory.retrofit
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONObject import org.json.JSONObject
@ -27,76 +24,29 @@ open class StoriesRepository(private val service: StoriesService) {
return ResponseBodyUtils.parseStoryItem(itemJson, false, null) return ResponseBodyUtils.parseStoryItem(itemJson, false, null)
} }
suspend fun getFeedStories(): List<FeedStoryModel> { suspend fun getFeedStories(): List<Story> {
val response = service.getFeedStories() val response = service.getFeedStories()
return parseStoriesBody(response) val result = response.tray?.toMutableList() ?: mutableListOf()
} if (response.broadcasts != null) {
val length = response.broadcasts.size
private fun parseStoriesBody(body: String): List<FeedStoryModel> { for (i in 0 until length) {
val feedStoryModels: MutableList<FeedStoryModel> = ArrayList() val broadcast = response.broadcasts.get(i)
val feedStoriesReel = JSONObject(body).getJSONArray("tray") result.add(
for (i in 0 until feedStoriesReel.length()) { Story(
val node = feedStoriesReel.getJSONObject(i) broadcast.id,
if (node.optBoolean("hide_from_feed_unit") && Utils.settingsHelper.getBoolean(PreferenceKeys.HIDE_MUTED_REELS)) continue broadcast.publishedTime,
val userJson = node.getJSONObject(if (node.has("user")) "user" else "owner") 0L,
try { broadcast.broadcastOwner,
val user = User( broadcast.muted,
userJson.getLong("pk"), false, // unclear
userJson.getString("username"),
userJson.optString("full_name"),
userJson.optBoolean("is_private"),
userJson.getString("profile_pic_url"),
userJson.optBoolean("is_verified")
)
val timestamp = node.getLong("latest_reel_media")
val fullyRead = !node.isNull("seen") && node.getLong("seen") == timestamp
val itemJson = if (node.has("items")) node.getJSONArray("items").optJSONObject(0) else null
var firstStoryModel: StoryModel? = null
if (itemJson != null) {
firstStoryModel = ResponseBodyUtils.parseStoryItem(itemJson, false, null)
}
feedStoryModels.add(
FeedStoryModel(
node.getString("id"),
user,
fullyRead,
timestamp,
firstStoryModel,
node.getInt("media_count"),
false,
node.optBoolean("has_besties_media")
)
)
} catch (e: Exception) {
Log.e(TAG, "parseStoriesBody: ", e)
} // to cover promotional reels with non-long user pk's
}
val broadcasts = JSONObject(body).getJSONArray("broadcasts")
for (i in 0 until broadcasts.length()) {
val node = broadcasts.getJSONObject(i)
val userJson = node.getJSONObject("broadcast_owner")
val user = User(
userJson.getLong("pk"),
userJson.getString("username"),
userJson.optString("full_name"),
userJson.optBoolean("is_private"),
userJson.getString("profile_pic_url"),
userJson.optBoolean("is_verified")
)
feedStoryModels.add(
FeedStoryModel(
node.getString("id"),
user,
false,
node.getLong("published_time"),
ResponseBodyUtils.parseBroadcastItem(node),
1, 1,
isLive = true, null,
isBestie = false broadcast
) )
) )
} }
return sort(feedStoryModels) }
return sort(result.toList())
} }
open suspend fun fetchHighlights(profileId: Long): List<HighlightModel> { open suspend fun fetchHighlights(profileId: Long): List<HighlightModel> {
@ -299,12 +249,13 @@ open class StoriesRepository(private val service: StoriesService) {
return builder.toString() return builder.toString()
} }
private fun sort(list: List<FeedStoryModel>): List<FeedStoryModel> { private fun sort(list: List<Story>): List<Story> {
val listCopy = ArrayList(list) val listCopy = ArrayList(list)
listCopy.sortWith { o1, o2 -> listCopy.sortWith { o1, o2 ->
when (Utils.settingsHelper.getString(PreferenceKeys.STORY_SORT)) { if (o1.latestReelMedia == null || o2.latestReelMedia == null) return@sortWith 0
"1" -> return@sortWith o2.timestamp.compareTo(o1.timestamp) else when (Utils.settingsHelper.getString(PreferenceKeys.STORY_SORT)) {
"2" -> return@sortWith o1.timestamp.compareTo(o2.timestamp) "1" -> return@sortWith o2.latestReelMedia.compareTo(o1.latestReelMedia)
"2" -> return@sortWith o1.latestReelMedia.compareTo(o2.latestReelMedia)
else -> return@sortWith 0 else -> return@sortWith 0
} }
} }

View File

@ -133,7 +133,7 @@
android:id="@+id/btnBackward" android:id="@+id/btnBackward"
style="@style/Widget.MaterialComponents.Button.TextButton" style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="0dp" android:layout_height="@dimen/story_item_height"
android:visibility="visible" android:visibility="visible"
app:icon="@drawable/exo_ic_skip_previous" app:icon="@drawable/exo_ic_skip_previous"
app:iconGravity="textStart" app:iconGravity="textStart"
@ -146,7 +146,7 @@
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/storiesList" android:id="@+id/storiesList"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="@dimen/story_item_height"
android:clipToPadding="false" android:clipToPadding="false"
app:layout_constraintTop_toBottomOf="@id/postActions" app:layout_constraintTop_toBottomOf="@id/postActions"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
@ -157,7 +157,7 @@
android:id="@+id/btnForward" android:id="@+id/btnForward"
style="@style/Widget.MaterialComponents.Button.TextButton" style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="0dp" android:layout_height="@dimen/story_item_height"
android:visibility="visible" android:visibility="visible"
app:icon="@drawable/exo_ic_skip_next" app:icon="@drawable/exo_ic_skip_next"
app:iconGravity="textStart" app:iconGravity="textStart"

View File

@ -7,6 +7,7 @@ import awais.instagrabber.db.entities.Favorite
import awais.instagrabber.models.enums.FavoriteType import awais.instagrabber.models.enums.FavoriteType
import awais.instagrabber.repositories.* import awais.instagrabber.repositories.*
import awais.instagrabber.repositories.responses.* import awais.instagrabber.repositories.responses.*
import awais.instagrabber.repositories.responses.stories.StoryStickerResponse
open class UserServiceAdapter : UserService { open class UserServiceAdapter : UserService {
override suspend fun getUserInfo(uid: Long): WrappedUser { override suspend fun getUserInfo(uid: Long): WrappedUser {