Browse Source

More updated. Handle clicks. Updated comments viewer, etc

renovate/org.robolectric-robolectric-4.x
Ammar Githam 4 years ago
parent
commit
9b83c5e832
  1. 6
      .idea/compiler.xml
  2. 1
      .idea/gradle.xml
  3. 2
      .idea/misc.xml
  4. 8
      .idea/runConfigurations/app.xml
  5. 9
      app/build.gradle
  6. 260
      app/src/main/java/awais/instagrabber/adapters/CommentsAdapter.java
  7. 222
      app/src/main/java/awais/instagrabber/adapters/FeedAdapter.java
  8. 79
      app/src/main/java/awais/instagrabber/adapters/FeedAdapterV2.java
  9. 40
      app/src/main/java/awais/instagrabber/adapters/FeedItemCallbackAdapter.java
  10. 9
      app/src/main/java/awais/instagrabber/adapters/SliderItemsAdapter.java
  11. 95
      app/src/main/java/awais/instagrabber/adapters/viewholder/CommentViewHolder.java
  12. 31
      app/src/main/java/awais/instagrabber/adapters/viewholder/FeedGridItemViewHolder.java
  13. 54
      app/src/main/java/awais/instagrabber/adapters/viewholder/SliderVideoViewHolder.java
  14. 93
      app/src/main/java/awais/instagrabber/adapters/viewholder/comments/ChildCommentViewHolder.java
  15. 93
      app/src/main/java/awais/instagrabber/adapters/viewholder/comments/ParentCommentViewHolder.java
  16. 157
      app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedItemViewHolder.java
  17. 109
      app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedPhotoViewHolder.java
  18. 54
      app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedSliderViewHolder.java
  19. 49
      app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedVideoViewHolder.java
  20. 189
      app/src/main/java/awais/instagrabber/asyncs/CommentsFetcher.java
  21. 60
      app/src/main/java/awais/instagrabber/customviews/PostsRecyclerView.java
  22. 152
      app/src/main/java/awais/instagrabber/customviews/RamboTextViewV2.java
  23. 2
      app/src/main/java/awais/instagrabber/customviews/SharedElementTransitionDialogFragment.java
  24. 64
      app/src/main/java/awais/instagrabber/customviews/VideoPlayerViewHelper.java
  25. 70
      app/src/main/java/awais/instagrabber/dialogs/PostsLayoutPreferencesDialogFragment.java
  26. 466
      app/src/main/java/awais/instagrabber/fragments/CommentsViewerFragment.java
  27. 38
      app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java
  28. 101
      app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java
  29. 406
      app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java
  30. 2
      app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java
  31. 62
      app/src/main/java/awais/instagrabber/models/CommentModel.java
  32. 20
      app/src/main/java/awais/instagrabber/utils/Utils.java
  33. 19
      app/src/main/java/awais/instagrabber/viewmodels/CommentsViewModel.java
  34. 56
      app/src/main/java/awais/instagrabber/workers/DownloadWorker.java
  35. 26
      app/src/main/res/layout/dialog_post_view.xml
  36. 6
      app/src/main/res/layout/fragment_comments.xml
  37. 10
      app/src/main/res/layout/fragment_feed.xml
  38. 211
      app/src/main/res/layout/item_comment.xml
  39. 15
      app/src/main/res/layout/item_comment_small.xml
  40. 92
      app/src/main/res/layout/item_feed_bottom.xml
  41. 6
      app/src/main/res/layout/item_feed_photo.xml
  42. 6
      app/src/main/res/layout/item_feed_slider.xml
  43. 20
      app/src/main/res/layout/item_feed_top.xml
  44. 6
      app/src/main/res/layout/item_feed_video.xml
  45. 2
      app/src/main/res/layout/layout_video_player_with_thumbnail.xml
  46. 4
      app/src/main/res/navigation/comments_nav_graph.xml
  47. 2
      app/src/main/res/values/color.xml
  48. 3
      app/src/main/res/values/strings.xml

6
.idea/compiler.xml

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="1.8" />
</component>
</project>

1
.idea/gradle.xml

@ -15,6 +15,7 @@
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
<option name="useQualifiedModuleNames" value="true" />
</GradleProjectSettings>
</option>
</component>

2
.idea/misc.xml

@ -40,7 +40,7 @@
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

8
.idea/runConfigurations/app.xml

@ -1,11 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="app" type="AndroidRunConfigurationType" factoryName="Android App" activateToolWindowBeforeRun="false">
<module name="app" />
<module name="InstaGrabber.app" />
<option name="DEPLOY" value="true" />
<option name="DEPLOY_APK_FROM_BUNDLE" value="false" />
<option name="DEPLOY_AS_INSTANT" value="false" />
<option name="ARTIFACT_NAME" value="" />
<option name="PM_INSTALL_OPTIONS" value="" />
<option name="ALL_USERS" value="false" />
<option name="DYNAMIC_FEATURES_DISABLED_LIST" value="" />
<option name="ACTIVITY_EXTRA_FLAGS" value="" />
<option name="MODE" value="default_activity" />
@ -41,11 +42,16 @@
</Native>
<Profilers>
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
<option name="STARTUP_PROFILING_ENABLED" value="false" />
<option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Sample Java Methods" />
<option name="STARTUP_NATIVE_MEMORY_PROFILING_ENABLED" value="false" />
<option name="NATIVE_MEMORY_SAMPLE_RATE_BYTES" value="2048" />
</Profilers>
<option name="DEEP_LINK" value="" />
<option name="ACTIVITY_CLASS" value="awais.instagrabber.activities.MainActivity" />
<option name="SEARCH_ACTIVITY_IN_GLOBAL_SCOPE" value="false" />
<option name="SKIP_ACTIVITY_VALIDATION" value="false" />
<method v="2">
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
</method>

9
app/build.gradle

@ -39,6 +39,12 @@ android {
}
}
configurations.all {
resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
}
dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10'
@ -62,7 +68,8 @@ dependencies {
implementation 'com.google.guava:guava:27.0.1-android'
// implementation 'com.github.hendrawd:StorageUtil:1.1.0'
implementation 'com.github.armcha:AutoLinkTextViewV2:2.1.1'
// implementation 'com.github.armcha:AutoLinkTextViewV2:2.1.1'
implementation 'com.github.ammargitham:AutoLinkTextViewV2:master-SNAPSHOT'
implementation 'org.jsoup:jsoup:1.13.1'
implementation 'com.facebook.fresco:fresco:2.3.0'

260
app/src/main/java/awais/instagrabber/adapters/CommentsAdapter.java

@ -2,137 +2,189 @@ package awais.instagrabber.adapters;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Filter;
import android.widget.Filterable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import awais.instagrabber.R;
import awais.instagrabber.adapters.viewholder.CommentViewHolder;
import awais.instagrabber.interfaces.MentionClickListener;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import awais.instagrabber.adapters.viewholder.comments.ChildCommentViewHolder;
import awais.instagrabber.adapters.viewholder.comments.ParentCommentViewHolder;
import awais.instagrabber.databinding.ItemCommentBinding;
import awais.instagrabber.databinding.ItemCommentSmallBinding;
import awais.instagrabber.models.CommentModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.utils.LocaleUtils;
import awais.instagrabber.utils.TextUtils;
public final class CommentsAdapter extends RecyclerView.Adapter<CommentViewHolder> implements Filterable {
private CommentModel[] filteredCommentModels;
private LayoutInflater layoutInflater;
private final boolean isParent;
private final View.OnClickListener onClickListener;
private final MentionClickListener mentionClickListener;
private final CommentModel[] commentModels;
private final String[] quantityStrings = new String[2];
private final Filter filter = new Filter() {
@NonNull
@Override
protected FilterResults performFiltering(final CharSequence filter) {
final FilterResults results = new FilterResults();
results.values = commentModels;
final int commentsLen = commentModels == null ? 0 : commentModels.length;
if (commentModels != null && commentsLen > 0 && !TextUtils.isEmpty(filter)) {
final String query = filter.toString().toLowerCase();
final ArrayList<CommentModel> filterList = new ArrayList<>(commentsLen);
for (final CommentModel commentModel : commentModels) {
final String commentText = commentModel.getText().toString().toLowerCase();
if (commentText.contains(query)) filterList.add(commentModel);
else {
final CommentModel[] childCommentModels = commentModel.getChildCommentModels();
if (childCommentModels != null) {
for (final CommentModel childCommentModel : childCommentModels) {
final String childCommentText = childCommentModel.getText().toString().toLowerCase();
if (childCommentText.contains(query)) filterList.add(commentModel);
}
}
}
}
filterList.trimToSize();
results.values = filterList.toArray(new CommentModel[0]);
}
return results;
public final class CommentsAdapter extends ListAdapter<CommentModel, RecyclerView.ViewHolder> {
private static final int TYPE_PARENT = 1;
private static final int TYPE_CHILD = 2;
private final Map<Integer, Integer> positionTypeMap = new HashMap<>();
// private final Filter filter = new Filter() {
// @NonNull
// @Override
// protected FilterResults performFiltering(final CharSequence filter) {
// final FilterResults results = new FilterResults();
// results.values = commentModels;
//
// final int commentsLen = commentModels == null ? 0 : commentModels.size();
// if (commentModels != null && commentsLen > 0 && !TextUtils.isEmpty(filter)) {
// final String query = filter.toString().toLowerCase();
// final ArrayList<CommentModel> filterList = new ArrayList<>(commentsLen);
//
// for (final CommentModel commentModel : commentModels) {
// final String commentText = commentModel.getText().toString().toLowerCase();
//
// if (commentText.contains(query)) filterList.add(commentModel);
// else {
// final List<CommentModel> childCommentModels = commentModel.getChildCommentModels();
// if (childCommentModels != null) {
// for (final CommentModel childCommentModel : childCommentModels) {
// final String childCommentText = childCommentModel.getText().toString().toLowerCase();
// if (childCommentText.contains(query)) filterList.add(commentModel);
// }
// }
// }
// }
// filterList.trimToSize();
// results.values = filterList.toArray(new CommentModel[0]);
// }
//
// return results;
// }
//
// @Override
// protected void publishResults(final CharSequence constraint, @NonNull final FilterResults results) {
// if (results.values instanceof List) {
// //noinspection unchecked
// filteredCommentModels = (List<CommentModel>) results.values;
// notifyDataSetChanged();
// }
// }
// };
private static final DiffUtil.ItemCallback<CommentModel> DIFF_CALLBACK = new DiffUtil.ItemCallback<CommentModel>() {
@Override
public boolean areItemsTheSame(@NonNull final CommentModel oldItem, @NonNull final CommentModel newItem) {
return oldItem.getId().equals(newItem.getId());
}
@Override
protected void publishResults(final CharSequence constraint, @NonNull final FilterResults results) {
if (results.values instanceof CommentModel[]) {
filteredCommentModels = (CommentModel[]) results.values;
notifyDataSetChanged();
}
public boolean areContentsTheSame(@NonNull final CommentModel oldItem, @NonNull final CommentModel newItem) {
return oldItem.getId().equals(newItem.getId());
}
};
private final CommentCallback commentCallback;
private CommentModel selected;
private int selectedIndex;
public CommentsAdapter(final CommentCallback commentCallback) {
super(DIFF_CALLBACK);
this.commentCallback = commentCallback;
}
public CommentsAdapter(final CommentModel[] commentModels,
final boolean isParent,
final View.OnClickListener onClickListener,
final MentionClickListener mentionClickListener) {
super();
this.commentModels = this.filteredCommentModels = commentModels;
this.isParent = isParent;
this.onClickListener = onClickListener;
this.mentionClickListener = mentionClickListener;
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) {
final Context context = parent.getContext();
final LayoutInflater layoutInflater = LayoutInflater.from(context);
if (type == TYPE_PARENT) {
final ItemCommentBinding binding = ItemCommentBinding.inflate(layoutInflater, parent, false);
return new ParentCommentViewHolder(binding);
}
final ItemCommentSmallBinding binding = ItemCommentSmallBinding.inflate(layoutInflater, parent, false);
return new ChildCommentViewHolder(binding);
}
@Override
public Filter getFilter() {
return filter;
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {
final CommentModel commentModel = getItem(position);
if (commentModel == null) return;
final int type = getItemViewType(position);
final boolean selected = this.selected != null && this.selected.getId().equals(commentModel.getId());
if (type == TYPE_PARENT) {
final ParentCommentViewHolder viewHolder = (ParentCommentViewHolder) holder;
viewHolder.bind(commentModel, selected, commentCallback);
return;
}
final ChildCommentViewHolder viewHolder = (ChildCommentViewHolder) holder;
viewHolder.bind(commentModel, selected, commentCallback);
}
@NonNull
@Override
public CommentViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) {
final Context context = parent.getContext();
if (quantityStrings[0] == null) quantityStrings[0] = context.getString(R.string.single_like);
if (quantityStrings[1] == null) quantityStrings[1] = context.getString(R.string.multiple_likes);
if (layoutInflater == null) layoutInflater = LayoutInflater.from(context);
final View view = layoutInflater.inflate(isParent ? R.layout.item_comment
: R.layout.item_comment_small,
parent,
false);
return new CommentViewHolder(view,
onClickListener,
mentionClickListener);
public void submitList(@Nullable final List<CommentModel> list) {
final List<CommentModel> flatList = flattenList(list);
super.submitList(flatList);
}
@Override
public void onBindViewHolder(@NonNull final CommentViewHolder holder, final int position) {
final CommentModel commentModel = filteredCommentModels[position];
if (commentModel != null) {
holder.setCommentModel(commentModel);
holder.setComment(commentModel.getText());
holder.setDate(commentModel.getDateTime());
holder.setLiked(commentModel.getLiked());
final long likes = commentModel.getLikes();
holder.setLikes(String.format(LocaleUtils.getCurrentLocale(), "%d %s", likes, quantityStrings[likes == 1 ? 0 : 1]));
final ProfileModel profileModel = commentModel.getProfileModel();
if (profileModel != null) {
holder.setUsername(profileModel.getUsername());
holder.getProfilePicView().setImageURI(profileModel.getSdProfilePic());
}
if (holder.isParent()) {
final CommentModel[] childCommentModels = commentModel.getChildCommentModels();
if (childCommentModels != null && childCommentModels.length > 0)
holder.setChildAdapter(new CommentsAdapter(childCommentModels, false, onClickListener, mentionClickListener));
else holder.hideChildComments();
public void submitList(@Nullable final List<CommentModel> list, @Nullable final Runnable commitCallback) {
final List<CommentModel> flatList = flattenList(list);
super.submitList(flatList, commitCallback);
}
private List<CommentModel> flattenList(final List<CommentModel> list) {
if (list == null) {
return Collections.emptyList();
}
final List<CommentModel> flatList = new ArrayList<>();
int lastCommentIndex = -1;
for (final CommentModel parent : list) {
lastCommentIndex++;
flatList.add(parent);
positionTypeMap.put(lastCommentIndex, TYPE_PARENT);
final List<CommentModel> children = parent.getChildCommentModels();
for (final CommentModel child : children) {
lastCommentIndex++;
flatList.add(child);
positionTypeMap.put(lastCommentIndex, TYPE_CHILD);
}
}
return flatList;
}
@Override
public int getItemCount() {
return filteredCommentModels == null ? 0 : filteredCommentModels.length;
public int getItemViewType(final int position) {
final Integer type = positionTypeMap.get(position);
if (type == null) {
return TYPE_PARENT;
}
return type;
}
public void setSelected(final CommentModel commentModel) {
this.selected = commentModel;
selectedIndex = getCurrentList().indexOf(commentModel);
notifyItemChanged(selectedIndex);
}
public void clearSelection() {
this.selected = null;
notifyItemChanged(selectedIndex);
}
public CommentModel getSelected() {
return selected;
}
public interface CommentCallback {
void onClick(final CommentModel comment);
void onHashtagClick(final String hashtag);
void onMentionClick(final String mention);
void onURLClick(final String url);
void onEmailClick(final String emailAddress);
}
}

222
app/src/main/java/awais/instagrabber/adapters/FeedAdapter.java

@ -1,111 +1,111 @@
package awais.instagrabber.adapters;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import awais.instagrabber.adapters.viewholder.feed.FeedItemViewHolder;
import awais.instagrabber.adapters.viewholder.feed.FeedPhotoViewHolder;
import awais.instagrabber.adapters.viewholder.feed.FeedSliderViewHolder;
import awais.instagrabber.adapters.viewholder.feed.FeedVideoViewHolder;
import awais.instagrabber.customviews.RamboTextView;
import awais.instagrabber.databinding.ItemFeedPhotoBinding;
import awais.instagrabber.databinding.ItemFeedSliderBinding;
import awais.instagrabber.databinding.ItemFeedVideoBinding;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.utils.Utils;
public final class FeedAdapter extends ListAdapter<FeedModel, FeedItemViewHolder> {
private static final String TAG = "FeedAdapter";
private final View.OnClickListener clickListener;
private final MentionClickListener mentionClickListener;
private final View.OnLongClickListener longClickListener = v -> {
final Object tag;
if (v instanceof RamboTextView && (tag = v.getTag()) instanceof FeedModel)
Utils.copyText(v.getContext(), ((FeedModel) tag).getPostCaption());
return true;
};
private static final DiffUtil.ItemCallback<FeedModel> diffCallback = new DiffUtil.ItemCallback<FeedModel>() {
@Override
public boolean areItemsTheSame(@NonNull final FeedModel oldItem, @NonNull final FeedModel newItem) {
return oldItem.getPostId().equals(newItem.getPostId());
}
@Override
public boolean areContentsTheSame(@NonNull final FeedModel oldItem, @NonNull final FeedModel newItem) {
return oldItem.getPostId().equals(newItem.getPostId());
}
};
public FeedAdapter(final View.OnClickListener clickListener,
final MentionClickListener mentionClickListener) {
super(diffCallback);
// private final static String ellipsize = "… more";
this.clickListener = clickListener;
this.mentionClickListener = mentionClickListener;
}
@NonNull
@Override
public FeedItemViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final Context context = parent.getContext();
final LayoutInflater layoutInflater = LayoutInflater.from(context);
final MediaItemType type = MediaItemType.valueOf(viewType);
switch (type) {
case MEDIA_TYPE_VIDEO: {
final ItemFeedVideoBinding binding = ItemFeedVideoBinding.inflate(layoutInflater, parent, false);
return new FeedVideoViewHolder(binding, mentionClickListener, clickListener, longClickListener);
}
case MEDIA_TYPE_SLIDER: {
final ItemFeedSliderBinding binding = ItemFeedSliderBinding.inflate(layoutInflater, parent, false);
return new FeedSliderViewHolder(binding, mentionClickListener, clickListener, longClickListener);
}
case MEDIA_TYPE_IMAGE:
default: {
final ItemFeedPhotoBinding binding = ItemFeedPhotoBinding.inflate(layoutInflater, parent, false);
return new FeedPhotoViewHolder(binding, mentionClickListener, clickListener, longClickListener);
}
}
}
@Override
public void onBindViewHolder(@NonNull final FeedItemViewHolder viewHolder, final int position) {
final FeedModel feedModel = getItem(position);
if (feedModel == null) {
return;
}
feedModel.setPosition(position);
viewHolder.bind(feedModel, (feedModel1, view, postImage) -> {});
}
@Override
public int getItemViewType(final int position) {
return getItem(position).getItemType().getId();
}
@Override
public void onViewAttachedToWindow(@NonNull final FeedItemViewHolder holder) {
super.onViewAttachedToWindow(holder);
// Log.d(TAG, "attached holder: " + holder);
if (!(holder instanceof FeedSliderViewHolder)) return;
final FeedSliderViewHolder feedSliderViewHolder = (FeedSliderViewHolder) holder;
feedSliderViewHolder.startPlayingVideo();
}
@Override
public void onViewDetachedFromWindow(@NonNull final FeedItemViewHolder holder) {
super.onViewDetachedFromWindow(holder);
// Log.d(TAG, "detached holder: " + holder);
if (!(holder instanceof FeedSliderViewHolder)) return;
final FeedSliderViewHolder feedSliderViewHolder = (FeedSliderViewHolder) holder;
feedSliderViewHolder.stopPlayingVideo();
}
}
// package awais.instagrabber.adapters;
//
// import android.content.Context;
// import android.view.LayoutInflater;
// import android.view.View;
// import android.view.ViewGroup;
//
// import androidx.annotation.NonNull;
// import androidx.recyclerview.widget.DiffUtil;
// import androidx.recyclerview.widget.ListAdapter;
//
// import awais.instagrabber.adapters.viewholder.feed.FeedItemViewHolder;
// import awais.instagrabber.adapters.viewholder.feed.FeedPhotoViewHolder;
// import awais.instagrabber.adapters.viewholder.feed.FeedSliderViewHolder;
// import awais.instagrabber.adapters.viewholder.feed.FeedVideoViewHolder;
// import awais.instagrabber.customviews.RamboTextView;
// import awais.instagrabber.databinding.ItemFeedPhotoBinding;
// import awais.instagrabber.databinding.ItemFeedSliderBinding;
// import awais.instagrabber.databinding.ItemFeedVideoBinding;
// import awais.instagrabber.interfaces.MentionClickListener;
// import awais.instagrabber.models.FeedModel;
// import awais.instagrabber.models.enums.MediaItemType;
// import awais.instagrabber.utils.Utils;
//
// public final class FeedAdapter extends ListAdapter<FeedModel, FeedItemViewHolder> {
// private static final String TAG = "FeedAdapter";
// private final View.OnClickListener clickListener;
// private final MentionClickListener mentionClickListener;
// private final View.OnLongClickListener longClickListener = v -> {
// final Object tag;
// if (v instanceof RamboTextView && (tag = v.getTag()) instanceof FeedModel)
// Utils.copyText(v.getContext(), ((FeedModel) tag).getPostCaption());
// return true;
// };
//
// private static final DiffUtil.ItemCallback<FeedModel> diffCallback = new DiffUtil.ItemCallback<FeedModel>() {
// @Override
// public boolean areItemsTheSame(@NonNull final FeedModel oldItem, @NonNull final FeedModel newItem) {
// return oldItem.getPostId().equals(newItem.getPostId());
// }
//
// @Override
// public boolean areContentsTheSame(@NonNull final FeedModel oldItem, @NonNull final FeedModel newItem) {
// return oldItem.getPostId().equals(newItem.getPostId());
// }
// };
//
// public FeedAdapter(final View.OnClickListener clickListener,
// final MentionClickListener mentionClickListener) {
// super(diffCallback);
// // private final static String ellipsize = "… more";
// this.clickListener = clickListener;
// this.mentionClickListener = mentionClickListener;
// }
//
// @NonNull
// @Override
// public FeedItemViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
// final Context context = parent.getContext();
// final LayoutInflater layoutInflater = LayoutInflater.from(context);
// final MediaItemType type = MediaItemType.valueOf(viewType);
// switch (type) {
// case MEDIA_TYPE_VIDEO: {
// final ItemFeedVideoBinding binding = ItemFeedVideoBinding.inflate(layoutInflater, parent, false);
// return new FeedVideoViewHolder(binding, mentionClickListener, clickListener, longClickListener);
// }
// case MEDIA_TYPE_SLIDER: {
// final ItemFeedSliderBinding binding = ItemFeedSliderBinding.inflate(layoutInflater, parent, false);
// return new FeedSliderViewHolder(binding, mentionClickListener, clickListener, longClickListener);
// }
// case MEDIA_TYPE_IMAGE:
// default: {
// final ItemFeedPhotoBinding binding = ItemFeedPhotoBinding.inflate(layoutInflater, parent, false);
// return new FeedPhotoViewHolder(binding, mentionClickListener, clickListener, longClickListener);
// }
// }
// }
//
// @Override
// public void onBindViewHolder(@NonNull final FeedItemViewHolder viewHolder, final int position) {
// final FeedModel feedModel = getItem(position);
// if (feedModel == null) {
// return;
// }
// feedModel.setPosition(position);
// viewHolder.bind(feedModel, (feedModel1, view, postImage) -> {});
// }
//
// @Override
// public int getItemViewType(final int position) {
// return getItem(position).getItemType().getId();
// }
//
// @Override
// public void onViewAttachedToWindow(@NonNull final FeedItemViewHolder holder) {
// super.onViewAttachedToWindow(holder);
// // Log.d(TAG, "attached holder: " + holder);
// if (!(holder instanceof FeedSliderViewHolder)) return;
// final FeedSliderViewHolder feedSliderViewHolder = (FeedSliderViewHolder) holder;
// feedSliderViewHolder.startPlayingVideo();
// }
//
// @Override
// public void onViewDetachedFromWindow(@NonNull final FeedItemViewHolder holder) {
// super.onViewDetachedFromWindow(holder);
// // Log.d(TAG, "detached holder: " + holder);
// if (!(holder instanceof FeedSliderViewHolder)) return;
// final FeedSliderViewHolder feedSliderViewHolder = (FeedSliderViewHolder) holder;
// feedSliderViewHolder.stopPlayingVideo();
// }
// }

79
app/src/main/java/awais/instagrabber/adapters/FeedAdapterV2.java

@ -15,32 +15,26 @@ import awais.instagrabber.adapters.viewholder.feed.FeedItemViewHolder;
import awais.instagrabber.adapters.viewholder.feed.FeedPhotoViewHolder;
import awais.instagrabber.adapters.viewholder.feed.FeedSliderViewHolder;
import awais.instagrabber.adapters.viewholder.feed.FeedVideoViewHolder;
import awais.instagrabber.customviews.RamboTextView;
import awais.instagrabber.databinding.ItemFeedGridBinding;
import awais.instagrabber.databinding.ItemFeedPhotoBinding;
import awais.instagrabber.databinding.ItemFeedSliderBinding;
import awais.instagrabber.databinding.ItemFeedVideoBinding;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.PostsLayoutPreferences;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.utils.Utils;
public final class FeedAdapterV2 extends ListAdapter<FeedModel, RecyclerView.ViewHolder> {
private static final String TAG = "FeedAdapterV2";
private PostsLayoutPreferences layoutPreferences;
private OnPostClickListener postClickListener;
private int lastAnimatedPosition;
private final View.OnClickListener clickListener;
private final MentionClickListener mentionClickListener;
private final View.OnLongClickListener longClickListener = v -> {
final Object tag;
if (v instanceof RamboTextView && (tag = v.getTag()) instanceof FeedModel)
Utils.copyText(v.getContext(), ((FeedModel) tag).getPostCaption());
return true;
};
private final FeedItemCallback feedItemCallback;
// private final View.OnLongClickListener longClickListener = v -> {
// final Object tag;
// if (v instanceof RamboTextView && (tag = v.getTag()) instanceof FeedModel)
// Utils.copyText(v.getContext(), ((FeedModel) tag).getPostCaption());
// return true;
// };
private static final DiffUtil.ItemCallback<FeedModel> DIFF_CALLBACK = new DiffUtil.ItemCallback<FeedModel>() {
@ -56,14 +50,10 @@ public final class FeedAdapterV2 extends ListAdapter<FeedModel, RecyclerView.Vie
};
public FeedAdapterV2(@NonNull final PostsLayoutPreferences layoutPreferences,
final View.OnClickListener clickListener,
final MentionClickListener mentionClickListener,
final OnPostClickListener postClickListener) {
final FeedItemCallback feedItemCallback) {
super(DIFF_CALLBACK);
this.layoutPreferences = layoutPreferences;
this.clickListener = clickListener;
this.mentionClickListener = mentionClickListener;
this.postClickListener = postClickListener;
this.feedItemCallback = feedItemCallback;
}
@NonNull
@ -89,16 +79,16 @@ public final class FeedAdapterV2 extends ListAdapter<FeedModel, RecyclerView.Vie
switch (MediaItemType.valueOf(viewType)) {
case MEDIA_TYPE_VIDEO: {
final ItemFeedVideoBinding binding = ItemFeedVideoBinding.inflate(layoutInflater, parent, false);
return new FeedVideoViewHolder(binding, mentionClickListener, clickListener, longClickListener);
return new FeedVideoViewHolder(binding, feedItemCallback);
}
case MEDIA_TYPE_SLIDER: {
final ItemFeedSliderBinding binding = ItemFeedSliderBinding.inflate(layoutInflater, parent, false);
return new FeedSliderViewHolder(binding, mentionClickListener, clickListener, longClickListener);
return new FeedSliderViewHolder(binding, feedItemCallback);
}
case MEDIA_TYPE_IMAGE:
default: {
final ItemFeedPhotoBinding binding = ItemFeedPhotoBinding.inflate(layoutInflater, parent, false);
return new FeedPhotoViewHolder(binding, mentionClickListener, clickListener, longClickListener);
return new FeedPhotoViewHolder(binding, feedItemCallback);
}
}
}
@ -110,15 +100,13 @@ public final class FeedAdapterV2 extends ListAdapter<FeedModel, RecyclerView.Vie
feedModel.setPosition(position);
switch (layoutPreferences.getType()) {
case LINEAR:
((FeedItemViewHolder) viewHolder).bind(feedModel, postClickListener);
((FeedItemViewHolder) viewHolder).bind(feedModel);
break;
case GRID:
case STAGGERED_GRID:
default:
final boolean animate = position > lastAnimatedPosition;
((FeedGridItemViewHolder) viewHolder).bind(feedModel, layoutPreferences, postClickListener, false);
((FeedGridItemViewHolder) viewHolder).bind(feedModel, layoutPreferences, feedItemCallback);
}
lastAnimatedPosition = position;
}
@Override
@ -126,19 +114,6 @@ public final class FeedAdapterV2 extends ListAdapter<FeedModel, RecyclerView.Vie
return getItem(position).getItemType().getId();
}
@Override
public void onViewDetachedFromWindow(@NonNull final RecyclerView.ViewHolder viewHolder) {
switch (layoutPreferences.getType()) {
case LINEAR:
((FeedItemViewHolder) viewHolder).clearAnimation();
break;
case GRID:
case STAGGERED_GRID:
default:
((FeedGridItemViewHolder) viewHolder).clearAnimation();
}
}
public void setLayoutPreferences(@NonNull final PostsLayoutPreferences layoutPreferences) {
this.layoutPreferences = layoutPreferences;
}
@ -161,9 +136,31 @@ public final class FeedAdapterV2 extends ListAdapter<FeedModel, RecyclerView.Vie
// feedSliderViewHolder.stopPlayingVideo();
// }
public interface OnPostClickListener {
public interface FeedItemCallback {
void onPostClick(final FeedModel feedModel,
final View profilePicView,
final View mainPostImage);
void onProfilePicClick(final FeedModel feedModel,
final View profilePicView);
void onNameClick(final FeedModel feedModel,
final View profilePicView);
void onLocationClick(final FeedModel feedModel);
void onMentionClick(final String mention);
void onHashtagClick(final String hashtag);
void onCommentsClick(final FeedModel feedModel);
void onDownloadClick(final FeedModel feedModel);
void onEmailClick(final String emailId);
void onURLClick(final String url);
void onSliderClick(FeedModel feedModel, int position);
}
}

40
app/src/main/java/awais/instagrabber/adapters/FeedItemCallbackAdapter.java

@ -0,0 +1,40 @@
package awais.instagrabber.adapters;
import android.view.View;
import awais.instagrabber.models.FeedModel;
public class FeedItemCallbackAdapter implements FeedAdapterV2.FeedItemCallback {
@Override
public void onPostClick(final FeedModel feedModel, final View profilePicView, final View mainPostImage) {}
@Override
public void onProfilePicClick(final FeedModel feedModel, final View profilePicView) {}
@Override
public void onNameClick(final FeedModel feedModel, final View profilePicView) {}
@Override
public void onLocationClick(final FeedModel feedModel) {}
@Override
public void onMentionClick(final String mention) {}
@Override
public void onHashtagClick(final String hashtag) {}
@Override
public void onCommentsClick(final FeedModel feedModel) {}
@Override
public void onDownloadClick(final FeedModel feedModel) {}
@Override
public void onEmailClick(final String emailId) {}
@Override
public void onURLClick(final String url) {}
@Override
public void onSliderClick(final FeedModel feedModel, final int position) {}
}

9
app/src/main/java/awais/instagrabber/adapters/SliderItemsAdapter.java

@ -20,6 +20,7 @@ import awais.instagrabber.models.enums.MediaItemType;
public final class SliderItemsAdapter extends ListAdapter<PostChild, SliderItemViewHolder> {
private final VerticalDragHelper.OnVerticalDragListener onVerticalDragListener;
private final boolean loadVideoOnItemClick;
private final SliderCallback sliderCallback;
private final awais.instagrabber.databinding.LayoutExoCustomControlsBinding controlsBinding;
@ -35,15 +36,13 @@ public final class SliderItemsAdapter extends ListAdapter<PostChild, SliderItemV
}
};
public SliderItemsAdapter() {
this(null, null, null);
}
public SliderItemsAdapter(final VerticalDragHelper.OnVerticalDragListener onVerticalDragListener,
final LayoutExoCustomControlsBinding controlsBinding,
final boolean loadVideoOnItemClick,
final SliderCallback sliderCallback) {
super(DIFF_CALLBACK);
this.onVerticalDragListener = onVerticalDragListener;
this.loadVideoOnItemClick = loadVideoOnItemClick;
this.sliderCallback = sliderCallback;
this.controlsBinding = controlsBinding;
}
@ -56,7 +55,7 @@ public final class SliderItemsAdapter extends ListAdapter<PostChild, SliderItemV
switch (mediaItemType) {
case MEDIA_TYPE_VIDEO: {
final LayoutVideoPlayerWithThumbnailBinding binding = LayoutVideoPlayerWithThumbnailBinding.inflate(inflater, parent, false);
return new SliderVideoViewHolder(binding, onVerticalDragListener, controlsBinding);
return new SliderVideoViewHolder(binding, onVerticalDragListener, controlsBinding, loadVideoOnItemClick);
}
case MEDIA_TYPE_IMAGE:
default:

95
app/src/main/java/awais/instagrabber/adapters/viewholder/CommentViewHolder.java

@ -1,95 +0,0 @@
package awais.instagrabber.adapters.viewholder;
import android.text.Spannable;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.facebook.drawee.view.SimpleDraweeView;
import awais.instagrabber.R;
import awais.instagrabber.adapters.CommentsAdapter;
import awais.instagrabber.customviews.RamboTextView;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.CommentModel;
public final class CommentViewHolder extends RecyclerView.ViewHolder {
private final MentionClickListener mentionClickListener;
private final RecyclerView rvChildComments;
private final SimpleDraweeView ivProfilePic;
private final TextView tvUsername;
private final TextView tvDate;
private final TextView tvComment;
private final TextView tvLikes;
private final View container;
public CommentViewHolder(@NonNull final View itemView,
final View.OnClickListener onClickListener,
final MentionClickListener mentionClickListener) {
super(itemView);
container = itemView.findViewById(R.id.container);
if (onClickListener != null) container.setOnClickListener(onClickListener);
this.mentionClickListener = mentionClickListener;
ivProfilePic = itemView.findViewById(R.id.ivProfilePic);
tvUsername = itemView.findViewById(R.id.tvUsername);
tvDate = itemView.findViewById(R.id.tvDate);
tvLikes = itemView.findViewById(R.id.tvLikes);
tvComment = itemView.findViewById(R.id.tvComment);
tvUsername.setSelected(true);
tvDate.setSelected(true);
rvChildComments = itemView.findViewById(R.id.rvChildComments);
}
public final SimpleDraweeView getProfilePicView() {
return ivProfilePic;
}
public final boolean isParent() {
return rvChildComments != null;
}
public final void setCommentModel(final CommentModel commentModel) {
if (container != null) container.setTag(commentModel);
}
public final void setUsername(final String username) {
if (tvUsername != null) tvUsername.setText(username);
}
public final void setDate(final String date) {
if (tvDate != null) tvDate.setText(date);
}
public final void setLikes(final String likes) {
if (tvLikes != null) tvLikes.setText(likes);
}
public final void setLiked(final boolean liked) {
if (liked) container.setBackgroundColor(0x40FF69B4);
}
public final void setComment(final CharSequence comment) {
if (tvComment != null) {
tvComment.setText(comment, comment instanceof Spannable ? TextView.BufferType.SPANNABLE : TextView.BufferType.NORMAL);
((RamboTextView) tvComment).setMentionClickListener(mentionClickListener);
}
}
public final void setChildAdapter(final CommentsAdapter adapter) {
if (isParent()) {
rvChildComments.setAdapter(adapter);
rvChildComments.setVisibility(View.VISIBLE);
}
}
public final void hideChildComments() {
if (isParent()) rvChildComments.setVisibility(View.GONE);
}
}

31
app/src/main/java/awais/instagrabber/adapters/viewholder/FeedGridItemViewHolder.java

@ -1,11 +1,8 @@
package awais.instagrabber.adapters.viewholder;
import android.graphics.drawable.Animatable;
import android.net.Uri;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import androidx.annotation.DimenRes;
import androidx.annotation.NonNull;
@ -13,8 +10,6 @@ import androidx.recyclerview.widget.RecyclerView;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.backends.pipeline.PipelineDraweeControllerBuilder;
import com.facebook.drawee.controller.BaseControllerListener;
import com.facebook.imagepipeline.image.ImageInfo;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
@ -42,10 +37,9 @@ public class FeedGridItemViewHolder extends RecyclerView.ViewHolder {
public void bind(@NonNull final FeedModel feedModel,
@NonNull final PostsLayoutPreferences layoutPreferences,
final FeedAdapterV2.OnPostClickListener postClickListener,
final boolean animate) {
if (postClickListener != null) {
itemView.setOnClickListener(v -> postClickListener.onPostClick(feedModel, binding.profilePic, binding.postImage));
final FeedAdapterV2.FeedItemCallback feedItemCallback) {
if (feedItemCallback != null) {
itemView.setOnClickListener(v -> feedItemCallback.onPostClick(feedModel, binding.profilePic, binding.postImage));
}
itemView.setClipToOutline(layoutPreferences.getHasRoundedCorners());
if (layoutPreferences.getType() == STAGGERED_GRID) {
@ -123,25 +117,6 @@ public class FeedGridItemViewHolder extends RecyclerView.ViewHolder {
final PipelineDraweeControllerBuilder builder = Fresco.newDraweeControllerBuilder()
.setImageRequest(requestBuilder)
.setOldController(binding.postImage.getController());
if (animate) {
final BaseControllerListener<ImageInfo> imageListener = new BaseControllerListener<ImageInfo>() {
@Override
public void onFinalImageSet(final String id, final ImageInfo imageInfo, final Animatable animatable) {
setAnimation(binding.getRoot());
}
};
builder.setControllerListener(imageListener);
}
binding.postImage.setController(builder.build());
}
private void setAnimation(View viewToAnimate) {
final Animation animation = AnimationUtils.loadAnimation(viewToAnimate.getContext(), android.R.anim.fade_in);
animation.setDuration(300);
viewToAnimate.startAnimation(animation);
}
public void clearAnimation() {
binding.getRoot().clearAnimation();
}
}

54
app/src/main/java/awais/instagrabber/adapters/viewholder/SliderVideoViewHolder.java

@ -22,34 +22,39 @@ public class SliderVideoViewHolder extends SliderItemViewHolder {
private static final String TAG = "SliderVideoViewHolder";
private final LayoutVideoPlayerWithThumbnailBinding binding;
private final awais.instagrabber.databinding.LayoutExoCustomControlsBinding controlsBinding;
private final LayoutExoCustomControlsBinding controlsBinding;
private final boolean loadVideoOnItemClick;
private VideoPlayerViewHelper videoPlayerViewHelper;
@SuppressLint("ClickableViewAccessibility")
public SliderVideoViewHolder(@NonNull final LayoutVideoPlayerWithThumbnailBinding binding,
final VerticalDragHelper.OnVerticalDragListener onVerticalDragListener,
final LayoutExoCustomControlsBinding controlsBinding) {
final LayoutExoCustomControlsBinding controlsBinding,
final boolean loadVideoOnItemClick) {
super(binding.getRoot());
this.binding = binding;
this.controlsBinding = controlsBinding;
final VerticalDragHelper thumbnailVerticalDragHelper = new VerticalDragHelper(binding.thumbnailParent);
final VerticalDragHelper playerVerticalDragHelper = new VerticalDragHelper(binding.playerView);
thumbnailVerticalDragHelper.setOnVerticalDragListener(onVerticalDragListener);
playerVerticalDragHelper.setOnVerticalDragListener(onVerticalDragListener);
binding.thumbnailParent.setOnTouchListener((v, event) -> {
final boolean onDragTouch = thumbnailVerticalDragHelper.onDragTouch(event);
if (onDragTouch) {
return true;
}
return thumbnailVerticalDragHelper.onGestureTouchEvent(event);
});
binding.playerView.setOnTouchListener((v, event) -> {
final boolean onDragTouch = playerVerticalDragHelper.onDragTouch(event);
if (onDragTouch) {
return true;
}
return playerVerticalDragHelper.onGestureTouchEvent(event);
});
this.loadVideoOnItemClick = loadVideoOnItemClick;
if (onVerticalDragListener != null) {
final VerticalDragHelper thumbnailVerticalDragHelper = new VerticalDragHelper(binding.thumbnailParent);
final VerticalDragHelper playerVerticalDragHelper = new VerticalDragHelper(binding.playerView);
thumbnailVerticalDragHelper.setOnVerticalDragListener(onVerticalDragListener);
playerVerticalDragHelper.setOnVerticalDragListener(onVerticalDragListener);
binding.thumbnailParent.setOnTouchListener((v, event) -> {
final boolean onDragTouch = thumbnailVerticalDragHelper.onDragTouch(event);
if (onDragTouch) {
return true;
}
return thumbnailVerticalDragHelper.onGestureTouchEvent(event);
});
binding.playerView.setOnTouchListener((v, event) -> {
final boolean onDragTouch = playerVerticalDragHelper.onDragTouch(event);
if (onDragTouch) {
return true;
}
return playerVerticalDragHelper.onGestureTouchEvent(event);
});
}
}
public void bind(@NonNull final PostChild model,
@ -57,6 +62,14 @@ public class SliderVideoViewHolder extends SliderItemViewHolder {
final SliderItemsAdapter.SliderCallback sliderCallback) {
final float vol = settingsHelper.getBoolean(Constants.MUTED_VIDEOS) ? 0f : 1f;
final VideoPlayerViewHelper.VideoPlayerCallback videoPlayerCallback = new VideoPlayerCallbackAdapter() {
@Override
public void onThumbnailClick() {
if (sliderCallback != null) {
sliderCallback.onItemClicked(position);
}
}
@Override
public void onThumbnailLoaded() {
if (sliderCallback != null) {
@ -97,6 +110,7 @@ public class SliderVideoViewHolder extends SliderItemViewHolder {
vol,
aspectRatio,
model.getThumbnailUrl(),
loadVideoOnItemClick,
controlsBinding,
videoPlayerCallback);
// binding.itemFeedBottom.btnMute.setOnClickListener(v -> {

93
app/src/main/java/awais/instagrabber/adapters/viewholder/comments/ChildCommentViewHolder.java

@ -0,0 +1,93 @@
package awais.instagrabber.adapters.viewholder.comments;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.R;
import awais.instagrabber.adapters.CommentsAdapter.CommentCallback;
import awais.instagrabber.databinding.ItemCommentSmallBinding;
import awais.instagrabber.models.CommentModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.utils.Utils;
public final class ChildCommentViewHolder extends RecyclerView.ViewHolder {
private final ItemCommentSmallBinding binding;
public ChildCommentViewHolder(@NonNull final ItemCommentSmallBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void bind(final CommentModel comment,
final boolean selected,
final CommentCallback commentCallback) {
if (comment == null) return;
if (commentCallback != null) {
itemView.setOnClickListener(v -> commentCallback.onClick(comment));
}
if (selected) {
itemView.setBackgroundColor(itemView.getResources().getColor(R.color.comment_selected));
} else {
itemView.setBackgroundColor(itemView.getResources().getColor(android.R.color.transparent));
}
setupCommentText(comment, commentCallback);
binding.tvDate.setText(comment.getDateTime());
setLiked(comment.getLiked());
setLikes((int) comment.getLikes());
setUser(comment);
}
private void setupCommentText(final CommentModel comment, final CommentCallback commentCallback) {
binding.tvComment.clearOnURLClickListeners();
binding.tvComment.clearOnHashtagClickListeners();
binding.tvComment.clearOnMentionClickListeners();
binding.tvComment.clearOnEmailClickListeners();
binding.tvComment.setText(comment.getText());
binding.tvComment.addOnHashtagListener(autoLinkItem -> {
final String originalText = autoLinkItem.getOriginalText();
if (commentCallback == null) return;
commentCallback.onHashtagClick(originalText);
});
binding.tvComment.addOnMentionClickListener(autoLinkItem -> {
final String originalText = autoLinkItem.getOriginalText();
if (commentCallback == null) return;
commentCallback.onMentionClick(originalText);
});
binding.tvComment.addOnEmailClickListener(autoLinkItem -> {
final String originalText = autoLinkItem.getOriginalText();
if (commentCallback == null) return;
commentCallback.onEmailClick(originalText);
});
binding.tvComment.addOnURLClickListener(autoLinkItem -> {
final String originalText = autoLinkItem.getOriginalText();
if (commentCallback == null) return;
commentCallback.onURLClick(originalText);
});
binding.tvComment.setOnLongClickListener(v -> {
Utils.copyText(itemView.getContext(), comment.getText());
return true;
});
binding.tvComment.setOnClickListener(v -> commentCallback.onClick(comment));
}
private void setUser(final CommentModel comment) {
final ProfileModel profileModel = comment.getProfileModel();
if (profileModel == null) return;
binding.tvUsername.setText(profileModel.getUsername());
binding.ivProfilePic.setImageURI(profileModel.getSdProfilePic());
}
private void setLikes(final int likes) {
final String likesString = itemView.getResources().getQuantityString(R.plurals.likes_count, likes, likes);
binding.tvLikes.setText(likesString);
}
public final void setLiked(final boolean liked) {
if (liked) {
// container.setBackgroundColor(0x40FF69B4);
return;
}
}
}

93
app/src/main/java/awais/instagrabber/adapters/viewholder/comments/ParentCommentViewHolder.java

@ -0,0 +1,93 @@
package awais.instagrabber.adapters.viewholder.comments;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.R;
import awais.instagrabber.adapters.CommentsAdapter.CommentCallback;
import awais.instagrabber.databinding.ItemCommentBinding;
import awais.instagrabber.models.CommentModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.utils.Utils;
public final class ParentCommentViewHolder extends RecyclerView.ViewHolder {
private final ItemCommentBinding binding;
public ParentCommentViewHolder(@NonNull final ItemCommentBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void bind(final CommentModel comment,
final boolean selected,
final CommentCallback commentCallback) {
if (comment == null) return;
if (commentCallback != null) {
itemView.setOnClickListener(v -> commentCallback.onClick(comment));
}
if (selected) {
itemView.setBackgroundColor(itemView.getResources().getColor(R.color.comment_selected));
} else {
itemView.setBackgroundColor(itemView.getResources().getColor(android.R.color.transparent));
}
setupCommentText(comment, commentCallback);
binding.tvDate.setText(comment.getDateTime());
setLiked(comment.getLiked());
setLikes((int) comment.getLikes());
setUser(comment);
}
private void setupCommentText(final CommentModel comment, final CommentCallback commentCallback) {
binding.tvComment.clearOnURLClickListeners();
binding.tvComment.clearOnHashtagClickListeners();
binding.tvComment.clearOnMentionClickListeners();
binding.tvComment.clearOnEmailClickListeners();
binding.tvComment.setText(comment.getText());
binding.tvComment.addOnHashtagListener(autoLinkItem -> {
final String originalText = autoLinkItem.getOriginalText();
if (commentCallback == null) return;
commentCallback.onHashtagClick(originalText);
});
binding.tvComment.addOnMentionClickListener(autoLinkItem -> {
final String originalText = autoLinkItem.getOriginalText();
if (commentCallback == null) return;
commentCallback.onMentionClick(originalText);
});
binding.tvComment.addOnEmailClickListener(autoLinkItem -> {
final String originalText = autoLinkItem.getOriginalText();
if (commentCallback == null) return;
commentCallback.onEmailClick(originalText);
});
binding.tvComment.addOnURLClickListener(autoLinkItem -> {
final String originalText = autoLinkItem.getOriginalText();
if (commentCallback == null) return;
commentCallback.onURLClick(originalText);
});
binding.tvComment.setOnLongClickListener(v -> {
Utils.copyText(itemView.getContext(), comment.getText());
return true;
});
binding.tvComment.setOnClickListener(v -> commentCallback.onClick(comment));
}
private void setUser(final CommentModel comment) {
final ProfileModel profileModel = comment.getProfileModel();
if (profileModel == null) return;
binding.tvUsername.setText(profileModel.getUsername());
binding.ivProfilePic.setImageURI(profileModel.getSdProfilePic());
}
private void setLikes(final int likes) {
final String likesString = itemView.getResources().getQuantityString(R.plurals.likes_count, likes, likes);
binding.tvLikes.setText(likesString);
}
public final void setLiked(final boolean liked) {
if (liked) {
// container.setBackgroundColor(0x40FF69B4);
return;
}
}
}

157
app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedItemViewHolder.java

@ -1,95 +1,107 @@
package awais.instagrabber.adapters.viewholder.feed;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.transition.TransitionManager;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.customviews.CommentMentionClickSpan;
import awais.instagrabber.customviews.RamboTextView;
import awais.instagrabber.databinding.ItemFeedBottomBinding;
import awais.instagrabber.databinding.ItemFeedTopBinding;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.utils.TextUtils;
import static android.text.TextUtils.TruncateAt.END;
public abstract class FeedItemViewHolder extends RecyclerView.ViewHolder {
public static final int MAX_CHARS = 255;
public static final int MAX_LINES_COLLAPSED = 5;
private final ItemFeedTopBinding topBinding;
private final ItemFeedBottomBinding bottomBinding;
private final MentionClickListener mentionClickListener;
private final FeedAdapterV2.FeedItemCallback feedItemCallback;
public FeedItemViewHolder(@NonNull final View root,
final ItemFeedTopBinding topBinding,
final ItemFeedBottomBinding bottomBinding,
final MentionClickListener mentionClickListener,
final View.OnClickListener clickListener,
final View.OnLongClickListener longClickListener) {
final FeedAdapterV2.FeedItemCallback feedItemCallback) {
super(root);
this.topBinding = topBinding;
this.bottomBinding = bottomBinding;
this.mentionClickListener = mentionClickListener;
// itemView.setOnClickListener(clickListener);
// topBinding.title.setMovementMethod(new LinkMovementMethod());
// bottomBinding.btnComments.setOnClickListener(clickListener);
// topBinding.viewStoryPost.setOnClickListener(clickListener);
// topBinding.ivProfilePic.setOnClickListener(clickListener);
// bottomBinding.btnDownload.setOnClickListener(clickListener);
// bottomBinding.viewerCaption.setOnClickListener(clickListener);
// bottomBinding.viewerCaption.setOnLongClickListener(longClickListener);
// bottomBinding.viewerCaption.setMentionClickListener(mentionClickListener);
topBinding.title.setMovementMethod(new LinkMovementMethod());
this.feedItemCallback = feedItemCallback;
}
public void bind(final FeedModel feedModel,
final FeedAdapterV2.OnPostClickListener postClickListener) {
public void bind(final FeedModel feedModel) {
if (feedModel == null) {
return;
}
topBinding.viewStoryPost.setTag(feedModel);
topBinding.ivProfilePic.setTag(feedModel);
bottomBinding.btnDownload.setTag(feedModel);
bottomBinding.viewerCaption.setTag(feedModel);
bottomBinding.btnComments.setTag(feedModel);
setupProfilePic(feedModel);
setupLocation(feedModel);
bottomBinding.tvPostDate.setText(feedModel.getPostDate());
setupComments(feedModel);
setupCaption(feedModel);
bottomBinding.btnDownload.setOnClickListener(v -> feedItemCallback.onDownloadClick(feedModel));
bindItem(feedModel);
}
private void setupComments(final FeedModel feedModel) {
final long commentsCount = feedModel.getCommentsCount();
bottomBinding.commentsCount.setText(String.valueOf(commentsCount));
bottomBinding.commentsCount.setOnClickListener(v -> feedItemCallback.onCommentsClick(feedModel));
}
private void setupProfilePic(final FeedModel feedModel) {
final ProfileModel profileModel = feedModel.getProfileModel();
if (profileModel != null) {
topBinding.ivProfilePic.setOnClickListener(v -> feedItemCallback.onProfilePicClick(feedModel, topBinding.ivProfilePic));
topBinding.ivProfilePic.setImageURI(profileModel.getSdProfilePic());
final int titleLen = profileModel.getUsername().length() + 1;
final SpannableString spannableString = new SpannableString("@" + profileModel.getUsername());
spannableString.setSpan(new CommentMentionClickSpan(), 0, titleLen, 0);
topBinding.title.setText(spannableString);
topBinding.title.setMentionClickListener(
(view, text, isHashtag, isLocation) -> mentionClickListener.onClick(null, profileModel.getUsername(), false, false));
setupTitle(feedModel);
}
bottomBinding.tvPostDate.setText(feedModel.getPostDate());
final long commentsCount = feedModel.getCommentsCount();
bottomBinding.commentsCount.setText(String.valueOf(commentsCount));
}
final String locationName = feedModel.getLocationName();
final String locationId = feedModel.getLocationId();
setLocation(locationName, locationId);
CharSequence postCaption = feedModel.getPostCaption();
private void setupTitle(final FeedModel feedModel) {
// final int titleLen = profileModel.getUsername().length() + 1;
// final SpannableString spannableString = new SpannableString();
// spannableString.setSpan(new CommentMentionClickSpan(), 0, titleLen, 0);
final ProfileModel profileModel = feedModel.getProfileModel();
final String title = "@" + profileModel.getUsername();
topBinding.title.setText(title);
topBinding.title.setOnClickListener(v -> feedItemCallback.onNameClick(feedModel, topBinding.ivProfilePic));
}
private void setupCaption(final FeedModel feedModel) {
bottomBinding.viewerCaption.clearOnMentionClickListeners();
bottomBinding.viewerCaption.clearOnHashtagClickListeners();
bottomBinding.viewerCaption.clearOnURLClickListeners();
bottomBinding.viewerCaption.clearOnEmailClickListeners();
final CharSequence postCaption = feedModel.getPostCaption();
final boolean captionEmpty = TextUtils.isEmpty(postCaption);
bottomBinding.viewerCaption.setVisibility(captionEmpty ? View.GONE : View.VISIBLE);
if (!captionEmpty) {
if (TextUtils.hasMentions(postCaption)) {
postCaption = TextUtils.getMentionText(postCaption);
feedModel.setPostCaption(postCaption);
bottomBinding.viewerCaption.setText(postCaption, TextView.BufferType.SPANNABLE);
} else {
bottomBinding.viewerCaption.setText(postCaption);
if (captionEmpty) return;
bottomBinding.viewerCaption.setText(postCaption);
bottomBinding.viewerCaption.setMaxLines(MAX_LINES_COLLAPSED);
bottomBinding.viewerCaption.setEllipsize(END);
bottomBinding.viewerCaption.setOnClickListener(v -> bottomBinding.getRoot().post(() -> {
TransitionManager.beginDelayedTransition(bottomBinding.getRoot());
if (bottomBinding.viewerCaption.getMaxLines() == MAX_LINES_COLLAPSED) {
bottomBinding.viewerCaption.setMaxLines(Integer.MAX_VALUE);
bottomBinding.viewerCaption.setEllipsize(null);
return;
}
}
expandCollapseTextView(bottomBinding.viewerCaption, feedModel.getPostCaption());
bindItem(feedModel, postClickListener);
bottomBinding.viewerCaption.setMaxLines(MAX_LINES_COLLAPSED);
bottomBinding.viewerCaption.setEllipsize(END);
}));
bottomBinding.viewerCaption.addOnMentionClickListener(autoLinkItem -> feedItemCallback.onMentionClick(autoLinkItem.getOriginalText()));
bottomBinding.viewerCaption.addOnHashtagListener(autoLinkItem -> feedItemCallback.onHashtagClick(autoLinkItem.getOriginalText()));
bottomBinding.viewerCaption.addOnEmailClickListener(autoLinkItem -> feedItemCallback.onEmailClick(autoLinkItem.getOriginalText()));
bottomBinding.viewerCaption.addOnURLClickListener(autoLinkItem -> feedItemCallback.onURLClick(autoLinkItem.getOriginalText()));
}
private void setLocation(final String locationName, final String locationId) {
private void setupLocation(final FeedModel feedModel) {
final String locationName = feedModel.getLocationName();
if (TextUtils.isEmpty(locationName)) {
topBinding.location.setVisibility(View.GONE);
topBinding.title.setLayoutParams(new RelativeLayout.LayoutParams(
@ -101,46 +113,9 @@ public abstract class FeedItemViewHolder extends RecyclerView.ViewHolder {
topBinding.title.setLayoutParams(new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT
));
topBinding.location.setOnClickListener(v -> mentionClickListener.onClick(topBinding.location, locationId, false, true));
topBinding.location.setOnClickListener(v -> feedItemCallback.onLocationClick(feedModel));
}
}
/**
* expands or collapses {@link RamboTextView} [stg idek why i wrote this documentation]
*
* @param textView the {@link RamboTextView} view, to expand and collapse
* @param caption caption
* @return isExpanded
*/
public static boolean expandCollapseTextView(@NonNull final RamboTextView textView, final CharSequence caption) {
if (TextUtils.isEmpty(caption)) return false;
final TextView.BufferType bufferType = caption instanceof Spanned ? TextView.BufferType.SPANNABLE : TextView.BufferType.NORMAL;
if (textView.isCaptionExpanded()) {
textView.setText(caption, bufferType);
// textView.setCaptionIsExpanded(false);
return true;
}
int i = TextUtils.indexOfChar(caption, '\r', 0);
if (i == -1) i = TextUtils.indexOfChar(caption, '\n', 0);
if (i == -1) i = MAX_CHARS;
final int captionLen = caption.length();
final int minTrim = Math.min(MAX_CHARS, i);
if (captionLen <= minTrim) return false;
if (TextUtils.hasMentions(caption))
textView.setText(TextUtils.getMentionText(caption), TextView.BufferType.SPANNABLE);
// textView.setCaptionIsExpandable(true);
// textView.setCaptionIsExpanded(true);
return true;
}
public abstract void bindItem(final FeedModel feedModel,
final FeedAdapterV2.OnPostClickListener postClickListener);
public void clearAnimation() {
itemView.clearAnimation();
}
public abstract void bindItem(final FeedModel feedModel);
}

109
app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedPhotoViewHolder.java

@ -16,7 +16,6 @@ import com.facebook.imagepipeline.request.ImageRequestBuilder;
import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.databinding.ItemFeedPhotoBinding;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.utils.TextUtils;
@ -24,17 +23,15 @@ public class FeedPhotoViewHolder extends FeedItemViewHolder {
private static final String TAG = "FeedPhotoViewHolder";
private final ItemFeedPhotoBinding binding;
// private final long animationDuration;
private final FeedAdapterV2.FeedItemCallback feedItemCallback;
public FeedPhotoViewHolder(@NonNull final ItemFeedPhotoBinding binding,
final MentionClickListener mentionClickListener,
final View.OnClickListener clickListener,
final View.OnLongClickListener longClickListener) {
super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, mentionClickListener, clickListener, longClickListener);
final FeedAdapterV2.FeedItemCallback feedItemCallback) {
super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, feedItemCallback);
this.binding = binding;
// this.animationDuration = animationDuration;
binding.itemFeedBottom.videoViewsContainer.setVisibility(View.GONE);
binding.itemFeedBottom.btnMute.setVisibility(View.GONE);
this.feedItemCallback = feedItemCallback;
binding.itemFeedBottom.tvVideoViews.setVisibility(View.GONE);
// binding.itemFeedBottom.btnMute.setVisibility(View.GONE);
binding.imageViewer.setAllowTouchInterceptionWhileZoomed(false);
final GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(itemView.getContext().getResources())
.setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER)
@ -43,85 +40,39 @@ public class FeedPhotoViewHolder extends FeedItemViewHolder {
}
@Override
public void bindItem(final FeedModel feedModel,
final FeedAdapterV2.OnPostClickListener postClickListener) {
public void bindItem(final FeedModel feedModel) {
if (feedModel == null) {
return;
}
setDimensions(feedModel);
showOrHideDetails(false);
final String thumbnailUrl = feedModel.getThumbnailUrl();
String url = feedModel.getDisplayUrl();
if (TextUtils.isEmpty(url)) url = thumbnailUrl;
final ImageRequest requestBuilder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(url))
.setLocalThumbnailPreviewsEnabled(true)
.setProgressiveRenderingEnabled(true)
.build();
binding.imageViewer.setController(Fresco.newDraweeControllerBuilder()
.setImageRequest(requestBuilder)
.setOldController(binding.imageViewer.getController())
.setLowResImageRequest(ImageRequest.fromUri(thumbnailUrl))
.build());
binding.imageViewer.setTapListener(new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapConfirmed(final MotionEvent e) {
if (postClickListener != null) {
postClickListener.onPostClick(feedModel, binding.itemFeedTop.ivProfilePic, binding.imageViewer);
return true;
binding.getRoot().post(() -> {
setDimensions(feedModel);
final String thumbnailUrl = feedModel.getThumbnailUrl();
String url = feedModel.getDisplayUrl();
if (TextUtils.isEmpty(url)) url = thumbnailUrl;
final ImageRequest requestBuilder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(url))
// .setLocalThumbnailPreviewsEnabled(true)
// .setProgressiveRenderingEnabled(true)
.build();
binding.imageViewer.setController(Fresco.newDraweeControllerBuilder()
.setImageRequest(requestBuilder)
.setOldController(binding.imageViewer.getController())
.setLowResImageRequest(ImageRequest.fromUri(thumbnailUrl))
.build());
binding.imageViewer.setTapListener(new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapConfirmed(final MotionEvent e) {
if (feedItemCallback != null) {
feedItemCallback.onPostClick(feedModel, binding.itemFeedTop.ivProfilePic, binding.imageViewer);
return true;
}
return false;
}
return false;
}
});
});
}
private void setDimensions(final FeedModel feedModel) {
// final ViewGroup.LayoutParams layoutParams = binding.imageViewer.getLayoutParams();
// final int deviceWidth = Utils.displayMetrics.widthPixels;
// final int spanWidth = deviceWidth / spanCount;
// final int spanHeight = NumberUtils.getResultingHeight(spanWidth, feedModel.getImageHeight(), feedModel.getImageWidth());
// final int width = spanWidth == 0 ? deviceWidth : spanWidth;
// final int height = spanHeight == 0 ? deviceWidth + 1 : spanHeight;
final float aspectRatio = (float) feedModel.getImageWidth() / feedModel.getImageHeight();
binding.imageViewer.setAspectRatio(aspectRatio);
// Log.d(TAG, "setDimensions: aspectRatio:" + aspectRatio);
// if (animate) {
// Animation animation = AnimationUtils.expand(
// binding.imageViewer,
// layoutParams.width,
// layoutParams.height,
// width,
// height,
// new Animation.AnimationListener() {
// @Override
// public void onAnimationStart(final Animation animation) {
// showOrHideDetails(spanCount);
// }
//
// @Override
// public void onAnimationEnd(final Animation animation) {
// // showOrHideDetails(spanCount);
// }
//
// @Override
// public void onAnimationRepeat(final Animation animation) {
//
// }
// });
// binding.imageViewer.startAnimation(animation);
// } else {
// layoutParams.width = width;
// layoutParams.height = height;
// binding.imageViewer.requestLayout();
// }
}
private void showOrHideDetails(final boolean show) {
if (show) {
binding.itemFeedTop.getRoot().setVisibility(View.VISIBLE);
binding.itemFeedBottom.getRoot().setVisibility(View.VISIBLE);
} else {
binding.itemFeedTop.getRoot().setVisibility(View.GONE);
binding.itemFeedBottom.getRoot().setVisibility(View.GONE);
}
}
}

54
app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedSliderViewHolder.java

@ -21,11 +21,10 @@ import com.google.android.exoplayer2.upstream.cache.SimpleCache;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.adapters.SliderCallbackAdapter;
import awais.instagrabber.adapters.SliderItemsAdapter;
import awais.instagrabber.databinding.ItemFeedSliderBinding;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.PostChild;
import awais.instagrabber.models.enums.MediaItemType;
@ -40,25 +39,20 @@ public class FeedSliderViewHolder extends FeedItemViewHolder {
private static final boolean shouldAutoPlay = settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS);
private final ItemFeedSliderBinding binding;
private final FeedAdapterV2.FeedItemCallback feedItemCallback;
private final DefaultDataSourceFactory dataSourceFactory;
private final PlayerChangeListener playerChangeListener = (position, player) -> {
pagerPlayer = player;
playerPosition = position;
};
private CacheDataSourceFactory cacheDataSourceFactory;
private SimpleExoPlayer pagerPlayer;
private int playerPosition = 0;
public FeedSliderViewHolder(@NonNull final ItemFeedSliderBinding binding,
final MentionClickListener mentionClickListener,
final View.OnClickListener clickListener,
final View.OnLongClickListener longClickListener) {
super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, mentionClickListener, clickListener, longClickListener);
final FeedAdapterV2.FeedItemCallback feedItemCallback) {
super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, feedItemCallback);
this.binding = binding;
binding.itemFeedBottom.videoViewsContainer.setVisibility(View.GONE);
binding.itemFeedBottom.btnMute.setVisibility(View.GONE);
this.feedItemCallback = feedItemCallback;
binding.itemFeedBottom.tvVideoViews.setVisibility(View.GONE);
// binding.itemFeedBottom.btnMute.setVisibility(View.GONE);
final ViewGroup.LayoutParams layoutParams = binding.mediaList.getLayoutParams();
layoutParams.height = Utils.displayMetrics.widthPixels + 1;
binding.mediaList.setLayoutParams(layoutParams);
@ -71,31 +65,31 @@ public class FeedSliderViewHolder extends FeedItemViewHolder {
}
@Override
public void bindItem(final FeedModel feedModel,
final FeedAdapterV2.OnPostClickListener postClickListener) {
public void bindItem(final FeedModel feedModel) {
final List<PostChild> sliderItems = feedModel.getSliderItems();
final int sliderItemLen = sliderItems != null ? sliderItems.size() : 0;
if (sliderItemLen <= 0) return;
final String text = "1/" + sliderItemLen;
binding.mediaCounter.setText(text);
binding.mediaList.setOffscreenPageLimit(1);
SliderItemsAdapter adapter = (SliderItemsAdapter) binding.mediaList.getAdapter();
if (adapter == null) {
adapter = new SliderItemsAdapter();
}
// adapter.setSpanCount(spanCount);
final SliderItemsAdapter adapter = new SliderItemsAdapter(null, null, false, new SliderCallbackAdapter() {
@Override
public void onItemClicked(final int position) {
feedItemCallback.onSliderClick(feedModel, position);
}
});
binding.mediaList.setAdapter(adapter);
binding.mediaList.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageSelected(final int position) {
if (position >= sliderItemLen) return;
final String text = (position + 1) + "/" + sliderItemLen;
binding.mediaCounter.setText(text);
setDimensions(binding.mediaList, sliderItems.get(position));
}
});
setDimensions(binding.mediaList, sliderItems.get(0));
//noinspection deprecation
// binding.mediaList.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
// private int prevPos = 0;
//
@ -149,15 +143,15 @@ public class FeedSliderViewHolder extends FeedItemViewHolder {
final SimpleExoPlayer player = (SimpleExoPlayer) tag;
final float intVol = player.getVolume() == 0f ? 1f : 0f;
player.setVolume(intVol);
binding.itemFeedBottom.btnMute.setImageResource(intVol == 0f ? R.drawable.ic_volume_up_24 : R.drawable.ic_volume_off_24);
Utils.sessionVolumeFull = intVol == 1f;
// binding.itemFeedBottom.btnMute.setImageResource(intVol == 0f ? R.drawable.ic_volume_up_24 : R.drawable.ic_volume_off_24);
// Utils.sessionVolumeFull = intVol == 1f;
};
final PostChild firstItem = sliderItems.get(0);
if (firstItem.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO) {
binding.itemFeedBottom.btnMute.setVisibility(View.VISIBLE);
}
binding.itemFeedBottom.btnMute.setImageResource(Utils.sessionVolumeFull ? R.drawable.ic_volume_off_24 : R.drawable.ic_volume_up_24);
binding.itemFeedBottom.btnMute.setOnClickListener(muteClickListener);
// final PostChild firstItem = sliderItems.get(0);
// if (firstItem.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO) {
// binding.itemFeedBottom.btnMute.setVisibility(View.VISIBLE);
// }
// binding.itemFeedBottom.btnMute.setImageResource(Utils.sessionVolumeFull ? R.drawable.ic_volume_off_24 : R.drawable.ic_volume_up_24);
// binding.itemFeedBottom.btnMute.setOnClickListener(muteClickListener);
}
private void setDimensions(final View view, final PostChild model) {

49
app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedVideoViewHolder.java

@ -12,12 +12,10 @@ import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory;
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
import awais.instagrabber.R;
import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.customviews.VideoPlayerCallbackAdapter;
import awais.instagrabber.customviews.VideoPlayerViewHelper;
import awais.instagrabber.databinding.ItemFeedVideoBinding;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.NumberUtils;
@ -29,6 +27,7 @@ public class FeedVideoViewHolder extends FeedItemViewHolder {
private static final String TAG = "FeedVideoViewHolder";
private final ItemFeedVideoBinding binding;
private final FeedAdapterV2.FeedItemCallback feedItemCallback;
private final Handler handler;
private final DefaultDataSourceFactory dataSourceFactory;
@ -43,12 +42,11 @@ public class FeedVideoViewHolder extends FeedItemViewHolder {
// };
public FeedVideoViewHolder(@NonNull final ItemFeedVideoBinding binding,
final MentionClickListener mentionClickListener,
final View.OnClickListener clickListener,
final View.OnLongClickListener longClickListener) {
super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, mentionClickListener, clickListener, longClickListener);
final FeedAdapterV2.FeedItemCallback feedItemCallback) {
super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, feedItemCallback);
this.binding = binding;
binding.itemFeedBottom.videoViewsContainer.setVisibility(View.VISIBLE);
this.feedItemCallback = feedItemCallback;
binding.itemFeedBottom.tvVideoViews.setVisibility(View.VISIBLE);
handler = new Handler(Looper.getMainLooper());
final Context context = binding.getRoot().getContext();
dataSourceFactory = new DefaultDataSourceFactory(context, "instagram");
@ -59,8 +57,7 @@ public class FeedVideoViewHolder extends FeedItemViewHolder {
}
@Override
public void bindItem(final FeedModel feedModel,
final FeedAdapterV2.OnPostClickListener postClickListener) {
public void bindItem(final FeedModel feedModel) {
// Log.d(TAG, "Binding post: " + feedModel.getPostId());
this.feedModel = feedModel;
binding.itemFeedBottom.tvVideoViews.setText(String.valueOf(feedModel.getViewCount()));
@ -70,12 +67,12 @@ public class FeedVideoViewHolder extends FeedItemViewHolder {
@Override
public void onThumbnailClick() {
postClickListener.onPostClick(feedModel, binding.itemFeedTop.ivProfilePic, binding.videoPost.thumbnail);
feedItemCallback.onPostClick(feedModel, binding.itemFeedTop.ivProfilePic, binding.videoPost.thumbnail);
}
@Override
public void onPlayerViewLoaded() {
binding.itemFeedBottom.btnMute.setVisibility(View.VISIBLE);
// binding.itemFeedBottom.btnMute.setVisibility(View.VISIBLE);
final ViewGroup.LayoutParams layoutParams = binding.videoPost.playerView.getLayoutParams();
final int requiredWidth = Utils.displayMetrics.widthPixels;
final int resultingHeight = NumberUtils.getResultingHeight(requiredWidth, feedModel.getImageHeight(), feedModel.getImageWidth());
@ -97,19 +94,27 @@ public class FeedVideoViewHolder extends FeedItemViewHolder {
vol,
aspectRatio,
feedModel.getThumbnailUrl(),
false,
null,
videoPlayerCallback);
binding.itemFeedBottom.btnMute.setOnClickListener(v -> {
final float newVol = videoPlayerViewHelper.toggleMute();
setMuteIcon(newVol);
Utils.sessionVolumeFull = newVol == 1f;
binding.videoPost.thumbnail.post(() -> {
if (feedModel.getImageHeight() > 0.8 * Utils.displayMetrics.heightPixels) {
final ViewGroup.LayoutParams layoutParams = binding.videoPost.thumbnail.getLayoutParams();
layoutParams.height = (int) (0.8 * Utils.displayMetrics.heightPixels);
binding.videoPost.thumbnail.requestLayout();
}
});
binding.videoPost.playerView.setOnClickListener(v -> videoPlayerViewHelper.togglePlayback());
// binding.itemFeedBottom.btnMute.setOnClickListener(v -> {
// final float newVol = videoPlayerViewHelper.toggleMute();
// setMuteIcon(newVol);
// Utils.sessionVolumeFull = newVol == 1f;
// });
// binding.videoPost.playerView.setOnClickListener(v -> videoPlayerViewHelper.togglePlayback());
}
private void setMuteIcon(final float vol) {
binding.itemFeedBottom.btnMute.setImageResource(vol == 0f ? R.drawable.ic_volume_up_24 : R.drawable.ic_volume_off_24);
// binding.itemFeedBottom.btnMute.setImageResource(vol == 0f ? R.drawable.ic_volume_up_24 : R.drawable.ic_volume_off_24);
}
public FeedModel getCurrentFeedModel() {
@ -131,14 +136,4 @@ public class FeedVideoViewHolder extends FeedItemViewHolder {
// handler.removeCallbacks(loadRunnable);
// handler.postDelayed(loadRunnable, 800);
// }
private void showOrHideDetails(final boolean show) {
if (show) {
binding.itemFeedTop.getRoot().setVisibility(View.VISIBLE);
binding.itemFeedBottom.getRoot().setVisibility(View.VISIBLE);
} else {
binding.itemFeedTop.getRoot().setVisibility(View.GONE);
binding.itemFeedBottom.getRoot().setVisibility(View.GONE);
}
}
}

189
app/src/main/java/awais/instagrabber/asyncs/CommentsFetcher.java

@ -12,7 +12,7 @@ import org.json.JSONObject;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.interfaces.FetchListener;
@ -25,23 +25,20 @@ import awaisomereport.LogCollector;
import static awais.instagrabber.utils.Utils.logCollector;
public final class CommentsFetcher extends AsyncTask<Void, Void, CommentModel[]> {
public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentModel>> {
private static final String TAG = "CommentsFetcher";
private final String shortCode;
private final FetchListener<CommentModel[]> fetchListener;
/*
* i fucking spent the whole day on this and fixing all the fucking problems in this class.
* DO NO FUCK WITH THIS CODE!
* -AWAiS (The Badak) @the.badak
*/
public CommentsFetcher(final String shortCode, final FetchListener<CommentModel[]> fetchListener) {
private final FetchListener<List<CommentModel>> fetchListener;
public CommentsFetcher(final String shortCode, final FetchListener<List<CommentModel>> fetchListener) {
this.shortCode = shortCode;
this.fetchListener = fetchListener;
}
@NonNull
@Override
protected CommentModel[] doInBackground(final Void... voids) {
protected List<CommentModel> doInBackground(final Void... voids) {
/*
"https://www.instagram.com/graphql/query/?query_hash=97b41c52301f77ce508f55e66d17620e&variables=" + "{\"shortcode\":\"" + shortcode + "\",\"first\":50,\"after\":\"" + endCursor + "\"}";
@ -50,23 +47,20 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, CommentModel[]>
https://www.instagram.com/graphql/query/?query_hash=51fdd02b67508306ad4484ff574a0b62&variables={"comment_id":"18100041898085322","first":50,"after":""}
*/
final ArrayList<CommentModel> commentModels = getParentComments();
final List<CommentModel> commentModels = getParentComments();
for (final CommentModel commentModel : commentModels) {
final CommentModel[] childCommentModels = commentModel.getChildCommentModels();
final List<CommentModel> childCommentModels = commentModel.getChildCommentModels();
if (childCommentModels != null) {
final int childCommentsLen = childCommentModels.length;
final CommentModel lastChild = childCommentModels[childCommentsLen - 1];
final int childCommentsLen = childCommentModels.size();
final CommentModel lastChild = childCommentModels.get(childCommentsLen - 1);
if (lastChild != null && lastChild.hasNextPage() && !TextUtils.isEmpty(lastChild.getEndCursor())) {
final CommentModel[] remoteChildComments = getChildComments(commentModel.getId());
final List<CommentModel> remoteChildComments = getChildComments(commentModel.getId());
commentModel.setChildCommentModels(remoteChildComments);
lastChild.setPageCursor(false, null);
}
}
}
return commentModels.toArray(new CommentModel[0]);
return commentModels;
}
@Override
@ -75,14 +69,13 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, CommentModel[]>
}
@Override
protected void onPostExecute(final CommentModel[] result) {
protected void onPostExecute(final List<CommentModel> result) {
if (fetchListener != null) fetchListener.onResult(result);
}
@NonNull
private synchronized CommentModel[] getChildComments(final String commentId) {
final ArrayList<CommentModel> commentModels = new ArrayList<>();
private synchronized List<CommentModel> getChildComments(final String commentId) {
final List<CommentModel> commentModels = new ArrayList<>();
String endCursor = "";
while (endCursor != null) {
final String url = "https://www.instagram.com/graphql/query/?query_hash=51fdd02b67508306ad4484ff574a0b62&variables=" +
@ -96,7 +89,8 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, CommentModel[]>
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) break;
else {
final JSONObject data = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("data")
.getJSONObject("comment").getJSONObject("edge_threaded_comments");
.getJSONObject("comment")
.getJSONObject("edge_threaded_comments");
final JSONObject pageInfo = data.getJSONObject("page_info");
endCursor = pageInfo.getString("end_cursor");
@ -110,43 +104,54 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, CommentModel[]>
if (childComment != null) {
final JSONObject owner = childComment.getJSONObject("owner");
final ProfileModel profileModel = new ProfileModel(false, false, false,
owner.getString(Constants.EXTRAS_ID),
owner.getString(Constants.EXTRAS_USERNAME),
null, null, null,
owner.getString("profile_pic_url"),
null, 0, 0, 0, false, false, false, false);
final ProfileModel profileModel = new ProfileModel(false,
false,
false,
owner.getString(Constants.EXTRAS_ID),
owner.getString(Constants.EXTRAS_USERNAME),
null,
null,
null,
owner.getString("profile_pic_url"),
null,
0,
0,
0,
false,
false,
false,
false);
final JSONObject likedBy = childComment.optJSONObject("edge_liked_by");
commentModels.add(new CommentModel(childComment.getString(Constants.EXTRAS_ID),
childComment.getString("text"),
childComment.getLong("created_at"),
likedBy != null ? likedBy.optLong("count", 0) : 0,
childComment.getBoolean("viewer_has_liked"),
profileModel));
childComment.getString("text"),
childComment.getLong("created_at"),
likedBy != null ? likedBy.optLong("count", 0) : 0,
childComment.getBoolean("viewer_has_liked"),
profileModel));
}
}
}
}
conn.disconnect();
} catch (final Exception e) {
if (logCollector != null)
logCollector.appendException(e, LogCollector.LogFile.ASYNC_COMMENTS_FETCHER, "getChildComments",
new Pair<>("commentModels.size", commentModels.size()));
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
logCollector.appendException(e,
LogCollector.LogFile.ASYNC_COMMENTS_FETCHER,
"getChildComments",
new Pair<>("commentModels.size", commentModels.size()));
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
break;
}
}
return commentModels.toArray(new CommentModel[0]);
return commentModels;
}
@NonNull
private synchronized ArrayList<CommentModel> getParentComments() {
final ArrayList<CommentModel> commentModelsList = new ArrayList<>();
private synchronized List<CommentModel> getParentComments() {
final List<CommentModel> commentModels = new ArrayList<>();
String endCursor = "";
while (endCursor != null) {
final String url = "https://www.instagram.com/graphql/query/?query_hash=bc3296d1ce80a24b1b6e40b1e72903f5&variables=" +
@ -160,7 +165,9 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, CommentModel[]>
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) break;
else {
final JSONObject parentComments = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("data")
.getJSONObject("shortcode_media").getJSONObject("edge_media_to_parent_comment");
.getJSONObject("shortcode_media")
.getJSONObject(
"edge_media_to_parent_comment");
final JSONObject pageInfo = parentComments.getJSONObject("page_info");
endCursor = pageInfo.optString("end_cursor");
@ -182,31 +189,37 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, CommentModel[]>
final JSONArray comments = parentComments.getJSONArray("edges");
final int commentsLen = comments.length();
final CommentModel[] commentModels = new CommentModel[commentsLen];
for (int i = 0; i < commentsLen; ++i) {
final JSONObject comment = comments.getJSONObject(i).getJSONObject("node");
final JSONObject owner = comment.getJSONObject("owner");
final ProfileModel profileModel = new ProfileModel(false, false,
owner.optBoolean("is_verified"),
owner.getString(Constants.EXTRAS_ID),
owner.getString(Constants.EXTRAS_USERNAME),
null, null, null,
owner.getString("profile_pic_url"),
null, 0, 0, 0, false, false, false, false);
final ProfileModel profileModel = new ProfileModel(false,
false,
owner.optBoolean("is_verified"),
owner.getString(Constants.EXTRAS_ID),
owner.getString(Constants.EXTRAS_USERNAME),
null,
null,
null,
owner.getString("profile_pic_url"),
null,
0,
0,
0,
false,
false,
false,
false);
final JSONObject likedBy = comment.optJSONObject("edge_liked_by");
final String commentId = comment.getString(Constants.EXTRAS_ID);
commentModels[i] = new CommentModel(commentId,
comment.getString("text"),
comment.getLong("created_at"),
likedBy != null ? likedBy.optLong("count", 0) : 0,
comment.getBoolean("viewer_has_liked"),
profileModel);
final CommentModel commentModel = new CommentModel(commentId,
comment.getString("text"),
comment.getLong("created_at"),
likedBy != null ? likedBy.optLong("count", 0) : 0,
comment.getBoolean("viewer_has_liked"),
profileModel);
JSONObject tempJsonObject;
final JSONArray childCommentsArray;
final int childCommentsLen;
if ((tempJsonObject = comment.optJSONObject("edge_threaded_comments")) != null &&
@ -223,47 +236,53 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, CommentModel[]>
hasNextPage = false;
}
final CommentModel[] childCommentModels = new CommentModel[childCommentsLen];
final List<CommentModel> childCommentModels = new ArrayList<>();
for (int j = 0; j < childCommentsLen; ++j) {
final JSONObject childComment = childCommentsArray.getJSONObject(j).getJSONObject("node");
tempJsonObject = childComment.getJSONObject("owner");
final ProfileModel childProfileModel = new ProfileModel(false, false,
tempJsonObject.optBoolean("is_verified"),
tempJsonObject.getString(Constants.EXTRAS_ID),
tempJsonObject.getString(Constants.EXTRAS_USERNAME),
null, null, null,
tempJsonObject.getString("profile_pic_url"),
null, 0, 0, 0, false, false, false, false);
final ProfileModel childProfileModel = new ProfileModel(false,
false,
tempJsonObject.optBoolean("is_verified"),
tempJsonObject.getString(Constants.EXTRAS_ID),
tempJsonObject.getString(Constants.EXTRAS_USERNAME),
null,
null,
null,
tempJsonObject.getString("profile_pic_url"),
null,
0,
0,
0,
false,
false,
false,
false);
tempJsonObject = childComment.optJSONObject("edge_liked_by");
childCommentModels[j] = new CommentModel(childComment.getString(Constants.EXTRAS_ID),
childComment.getString("text"),
childComment.getLong("created_at"),
tempJsonObject != null ? tempJsonObject.optLong("count", 0) : 0,
childComment.getBoolean("viewer_has_liked"),
childProfileModel);
childCommentModels.add(new CommentModel(childComment.getString(Constants.EXTRAS_ID),
childComment.getString("text"),
childComment.getLong("created_at"),
tempJsonObject != null ? tempJsonObject.optLong("count", 0) : 0,
childComment.getBoolean("viewer_has_liked"),
childProfileModel));
}
childCommentModels[childCommentsLen - 1].setPageCursor(hasNextPage, childEndCursor);
commentModels[i].setChildCommentModels(childCommentModels);
childCommentModels.get(childCommentsLen - 1).setPageCursor(hasNextPage, childEndCursor);
commentModel.setChildCommentModels(childCommentModels);
commentModels.add(commentModel);
}
}
Collections.addAll(commentModelsList, commentModels);
}
conn.disconnect();
} catch (final Exception e) {
if (logCollector != null)
logCollector.appendException(e, LogCollector.LogFile.ASYNC_COMMENTS_FETCHER, "getParentComments",
new Pair<>("commentModelsList.size", commentModelsList.size()));
new Pair<>("commentModelsList.size", commentModels.size()));
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
break;
}
}
return commentModelsList;
return commentModels;
}
}

60
app/src/main/java/awais/instagrabber/customviews/PostsRecyclerView.java

@ -19,12 +19,10 @@ import java.util.ArrayList;
import java.util.List;
import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.adapters.FeedAdapterV2.OnPostClickListener;
import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
import awais.instagrabber.customviews.helpers.PostFetcher;
import awais.instagrabber.customviews.helpers.RecyclerLazyLoaderAtBottom;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.PostsLayoutPreferences;
import awais.instagrabber.utils.Constants;
@ -34,12 +32,10 @@ import awais.instagrabber.viewmodels.FeedViewModel;
public class PostsRecyclerView extends RecyclerView {
private static final String TAG = "PostsRecyclerView";
private StaggeredGridLayoutManager gridLayoutManager;
private StaggeredGridLayoutManager layoutManager;
private PostsLayoutPreferences layoutPreferences;
private PostFetcher.PostFetchService postFetchService;
private Transition transition;
private OnClickListener postViewClickListener;
private MentionClickListener mentionClickListener;
private PostFetcher postFetcher;
private ViewModelStoreOwner viewModelStoreOwner;
private FeedAdapterV2 feedAdapter;
@ -48,7 +44,7 @@ public class PostsRecyclerView extends RecyclerView {
private boolean initCalled = false;
private GridSpacingItemDecoration gridSpacingItemDecoration;
private RecyclerLazyLoaderAtBottom lazyLoader;
private OnPostClickListener onPostClickListener;
private FeedAdapterV2.FeedItemCallback feedItemCallback;
private final FetchListener<List<FeedModel>> fetchListener = new FetchListener<List<FeedModel>>() {
@Override
@ -109,13 +105,8 @@ public class PostsRecyclerView extends RecyclerView {
return this;
}
public PostsRecyclerView setOnPostClickListener(@NonNull final OnPostClickListener onPostClickListener) {
this.onPostClickListener = onPostClickListener;
return this;
}
public PostsRecyclerView setMentionClickListener(final MentionClickListener mentionClickListener) {
this.mentionClickListener = mentionClickListener;
public PostsRecyclerView setFeedItemCallback(@NonNull final FeedAdapterV2.FeedItemCallback feedItemCallback) {
this.feedItemCallback = feedItemCallback;
return this;
}
@ -160,30 +151,19 @@ public class PostsRecyclerView extends RecyclerView {
private void initTransition() {
transition = new ChangeBounds();
// transition.addListener(new TransitionListenerAdapter(){
// @Override
// public void onTransitionEnd(@NonNull final Transition transition) {
// super.onTransitionEnd(transition);
// }
// });
transition.setDuration(300);
}
private void initLayoutManager() {
gridLayoutManager = new StaggeredGridLayoutManager(layoutPreferences.getColCount(), StaggeredGridLayoutManager.VERTICAL);
setLayoutManager(gridLayoutManager);
layoutManager = new StaggeredGridLayoutManager(layoutPreferences.getColCount(), StaggeredGridLayoutManager.VERTICAL);
if (layoutPreferences.getHasGap()) {
addItemDecoration(gridSpacingItemDecoration);
}
setLayoutManager(layoutManager);
}
private void initAdapter() {
feedAdapter = new FeedAdapterV2(
layoutPreferences,
postViewClickListener,
mentionClickListener,
(feedModel, view, postImage) -> {
if (onPostClickListener != null) {
onPostClickListener.onPostClick(feedModel, view, postImage);
}
});
feedAdapter = new FeedAdapterV2(layoutPreferences, feedItemCallback);
feedAdapter.setStateRestorationPolicy(RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY);
setAdapter(feedAdapter);
}
@ -194,7 +174,8 @@ public class PostsRecyclerView extends RecyclerView {
postFetcher = new PostFetcher(postFetchService, fetchListener);
addItemDecoration(gridSpacingItemDecoration);
setHasFixedSize(true);
lazyLoader = new RecyclerLazyLoaderAtBottom(gridLayoutManager, (page) -> {
setNestedScrollingEnabled(true);
lazyLoader = new RecyclerLazyLoaderAtBottom(layoutManager, (page) -> {
if (postFetcher.hasMore()) {
postFetcher.fetchNextPage();
dispatchFetchStatus();
@ -211,8 +192,23 @@ public class PostsRecyclerView extends RecyclerView {
feedAdapter.notifyDataSetChanged();
if (!layoutPreferences.getHasGap()) {
removeItemDecoration(gridSpacingItemDecoration);
} else {
addItemDecoration(gridSpacingItemDecoration);
}
if (layoutPreferences.getType() == PostsLayoutPreferences.PostsLayoutType.LINEAR) {
if (layoutManager.getSpanCount() != 1) {
layoutManager.setSpanCount(1);
setAdapter(null);
setAdapter(feedAdapter);
}
} else {
boolean shouldRedraw = layoutManager.getSpanCount() == 1;
layoutManager.setSpanCount(layoutPreferences.getColCount());
if (shouldRedraw) {
setAdapter(null);
setAdapter(feedAdapter);
}
}
gridLayoutManager.setSpanCount(layoutPreferences.getColCount());
});
}

152
app/src/main/java/awais/instagrabber/customviews/RamboTextViewV2.java

@ -0,0 +1,152 @@
package awais.instagrabber.customviews;
import android.content.Context;
import android.util.AttributeSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import io.github.armcha.autolink.AutoLinkItem;
import io.github.armcha.autolink.AutoLinkTextView;
import io.github.armcha.autolink.MODE_EMAIL;
import io.github.armcha.autolink.MODE_HASHTAG;
import io.github.armcha.autolink.MODE_MENTION;
import io.github.armcha.autolink.MODE_URL;
import io.github.armcha.autolink.Mode;
import kotlin.Unit;
public class RamboTextViewV2 extends AutoLinkTextView {
private final List<OnMentionClickListener> onMentionClickListeners = new ArrayList<>();
private final List<OnHashtagClickListener> onHashtagClickListeners = new ArrayList<>();
private final List<OnURLClickListener> onURLClickListeners = new ArrayList<>();
private final List<OnEmailClickListener> onEmailClickListeners = new ArrayList<>();
public RamboTextViewV2(@NotNull final Context context,
@Nullable final AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
addAutoLinkMode(MODE_HASHTAG.INSTANCE, MODE_MENTION.INSTANCE, MODE_EMAIL.INSTANCE, MODE_URL.INSTANCE);
onAutoLinkClick(autoLinkItem -> {
final Mode mode = autoLinkItem.getMode();
if (mode.equals(MODE_MENTION.INSTANCE)) {
for (final OnMentionClickListener onMentionClickListener : onMentionClickListeners) {
onMentionClickListener.onMentionClick(autoLinkItem);
}
return Unit.INSTANCE;
}
if (mode.equals(MODE_HASHTAG.INSTANCE)) {
for (final OnHashtagClickListener onHashtagClickListener : onHashtagClickListeners) {
onHashtagClickListener.onHashtagClick(autoLinkItem);
}
return Unit.INSTANCE;
}
if (mode.equals(MODE_URL.INSTANCE)) {
for (final OnURLClickListener onURLClickListener : onURLClickListeners) {
onURLClickListener.onURLClick(autoLinkItem);
}
return Unit.INSTANCE;
}
if (mode.equals(MODE_EMAIL.INSTANCE)) {
for (final OnEmailClickListener onEmailClickListener : onEmailClickListeners) {
onEmailClickListener.onEmailClick(autoLinkItem);
}
return Unit.INSTANCE;
}
return Unit.INSTANCE;
});
}
public void addOnMentionClickListener(final OnMentionClickListener onMentionClickListener) {
if (onMentionClickListener == null) {
return;
}
onMentionClickListeners.add(onMentionClickListener);
}
public void removeOnMentionClickListener(final OnMentionClickListener onMentionClickListener) {
if (onMentionClickListener == null) {
return;
}
onMentionClickListeners.remove(onMentionClickListener);
}
public void clearOnMentionClickListeners() {
onMentionClickListeners.clear();
}
public void addOnHashtagListener(final OnHashtagClickListener onHashtagClickListener) {
if (onHashtagClickListener == null) {
return;
}
onHashtagClickListeners.add(onHashtagClickListener);
}
public void removeOnHashtagListener(final OnHashtagClickListener onHashtagClickListener) {
if (onHashtagClickListener == null) {
return;
}
onHashtagClickListeners.remove(onHashtagClickListener);
}
public void clearOnHashtagClickListeners() {
onHashtagClickListeners.clear();
}
public void addOnURLClickListener(final OnURLClickListener onURLClickListener) {
if (onURLClickListener == null) {
return;
}
onURLClickListeners.add(onURLClickListener);
}
public void removeOnURLClickListener(final OnURLClickListener onURLClickListener) {
if (onURLClickListener == null) {
return;
}
onURLClickListeners.remove(onURLClickListener);
}
public void clearOnURLClickListeners() {
onURLClickListeners.clear();
}
public void addOnEmailClickListener(final OnEmailClickListener onEmailClickListener) {
if (onEmailClickListener == null) {
return;
}
onEmailClickListeners.add(onEmailClickListener);
}
public void removeOnEmailClickListener(final OnEmailClickListener onEmailClickListener) {
if (onEmailClickListener == null) {
return;
}
onEmailClickListeners.remove(onEmailClickListener);
}
public void clearOnEmailClickListeners() {
onEmailClickListeners.clear();
}
public interface OnMentionClickListener {
void onMentionClick(final AutoLinkItem autoLinkItem);
}
public interface OnHashtagClickListener {
void onHashtagClick(final AutoLinkItem autoLinkItem);
}
public interface OnURLClickListener {
void onURLClick(final AutoLinkItem autoLinkItem);
}
public interface OnEmailClickListener {
void onEmailClick(final AutoLinkItem autoLinkItem);
}
}

2
app/src/main/java/awais/instagrabber/customviews/SharedElementTransitionDialogFragment.java

@ -77,6 +77,7 @@ public abstract class SharedElementTransitionDialogFragment extends DialogFragme
final View startView = startViews.get(key);
final View destView = destViews.get(key);
final ViewBounds viewBounds = viewBoundsMap.get(key);
if (startView == null || destView == null || viewBounds == null) return;
onEndSharedElementAnimation(startView, destView, viewBounds);
}
}
@ -87,6 +88,7 @@ public abstract class SharedElementTransitionDialogFragment extends DialogFragme
final View startView = startViews.get(key);
final View destView = destViews.get(key);
final ViewBounds viewBounds = viewBoundsMap.get(key);
if (startView == null || destView == null || viewBounds == null) return;
onBeforeSharedElementAnimation(startView, destView, viewBounds);
setDestBounds(key);
}

64
app/src/main/java/awais/instagrabber/customviews/VideoPlayerViewHelper.java

@ -45,6 +45,7 @@ public class VideoPlayerViewHelper implements Player.EventListener {
private final float initialVolume;
private final float thumbnailAspectRatio;
private final String thumbnailUrl;
private final boolean loadPlayerOnClick;
private final awais.instagrabber.databinding.LayoutExoCustomControlsBinding controlsBinding;
private final VideoPlayerCallback videoPlayerCallback;
private final String videoUrl;
@ -58,6 +59,7 @@ public class VideoPlayerViewHelper implements Player.EventListener {
final float initialVolume,
final float thumbnailAspectRatio,
final String thumbnailUrl,
final boolean loadPlayerOnClick,
final LayoutExoCustomControlsBinding controlsBinding,
final VideoPlayerCallback videoPlayerCallback) {
this.context = context;
@ -65,6 +67,7 @@ public class VideoPlayerViewHelper implements Player.EventListener {
this.initialVolume = initialVolume;
this.thumbnailAspectRatio = thumbnailAspectRatio;
this.thumbnailUrl = thumbnailUrl;
this.loadPlayerOnClick = loadPlayerOnClick;
this.controlsBinding = controlsBinding;
this.videoPlayerCallback = videoPlayerCallback;
this.videoUrl = videoUrl;
@ -77,7 +80,9 @@ public class VideoPlayerViewHelper implements Player.EventListener {
if (videoPlayerCallback != null) {
videoPlayerCallback.onThumbnailClick();
}
loadPlayer();
if (loadPlayerOnClick) {
loadPlayer();
}
});
setThumbnail();
setupControls();
@ -262,38 +267,31 @@ public class VideoPlayerViewHelper implements Player.EventListener {
speedPopup.setOnMenuItemClickListener(item -> {
float nextSpeed;
int textResId;
switch (item.getItemId()) {
case R.id.pt_two_five_x:
nextSpeed = 0.25f;
textResId = R.string.pt_two_five_x;
break;
case R.id.pt_five_x:
nextSpeed = 0.5f;
textResId = R.string.pt_five_x;
break;
case R.id.pt_seven_five_x:
nextSpeed = 0.75f;
textResId = R.string.pt_seven_five_x;
break;
case R.id.one_x:
nextSpeed = 1f;
textResId = R.string.one_x;
break;
case R.id.one_pt_two_five_x:
nextSpeed = 1.25f;
textResId = R.string.one_pt_two_five_x;
break;
case R.id.one_pt_five_x:
nextSpeed = 1.5f;
textResId = R.string.one_pt_five_x;
break;
case R.id.two_x:
nextSpeed = 2f;
textResId = R.string.two_x;
break;
default:
nextSpeed = 1;
textResId = R.string.one_x;
int itemId = item.getItemId();
if (itemId == R.id.pt_two_five_x) {
nextSpeed = 0.25f;
textResId = R.string.pt_two_five_x;
} else if (itemId == R.id.pt_five_x) {
nextSpeed = 0.5f;
textResId = R.string.pt_five_x;
} else if (itemId == R.id.pt_seven_five_x) {
nextSpeed = 0.75f;
textResId = R.string.pt_seven_five_x;
} else if (itemId == R.id.one_x) {
nextSpeed = 1f;
textResId = R.string.one_x;
} else if (itemId == R.id.one_pt_two_five_x) {
nextSpeed = 1.25f;
textResId = R.string.one_pt_two_five_x;
} else if (itemId == R.id.one_pt_five_x) {
nextSpeed = 1.5f;
textResId = R.string.one_pt_five_x;
} else if (itemId == R.id.two_x) {
nextSpeed = 2f;
textResId = R.string.two_x;
} else {
nextSpeed = 1;
textResId = R.string.one_x;
}
player.setPlaybackParameters(new PlaybackParameters(nextSpeed));
controlsBinding.speed.setText(textResId);

70
app/src/main/java/awais/instagrabber/dialogs/PostsLayoutPreferencesDialogFragment.java

@ -82,26 +82,31 @@ public class PostsLayoutPreferencesDialogFragment extends DialogFragment {
}
private void initLayoutToggle() {
binding.layoutToggle.check(getSelectedLayoutId());
// binding.staggeredOrGridOptions.setVisibility(getSelectedLayoutId() != R.id.layout_linear ? View.VISIBLE : View.GONE);
final int selectedLayoutId = getSelectedLayoutId();
binding.layoutToggle.check(selectedLayoutId);
if (selectedLayoutId == R.id.layout_linear) {
binding.staggeredOrGridOptions.setVisibility(View.GONE);
}
binding.layoutToggle.addOnButtonCheckedListener((group, checkedId, isChecked) -> {
if (isChecked) {
switch (checkedId) {
case R.id.layout_linear:
preferencesBuilder.setType(PostsLayoutPreferences.PostsLayoutType.LINEAR);
binding.staggeredOrGridOptions.setVisibility(View.GONE);
break;
case R.id.layout_staggered:
preferencesBuilder.setType(PostsLayoutPreferences.PostsLayoutType.STAGGERED_GRID);
binding.staggeredOrGridOptions.setVisibility(View.VISIBLE);
initStaggeredOrGridOptions();
break;
case R.id.layout_grid:
default:
preferencesBuilder.setType(PostsLayoutPreferences.PostsLayoutType.GRID);
binding.staggeredOrGridOptions.setVisibility(View.VISIBLE);
initStaggeredOrGridOptions();
break;
if (checkedId == R.id.layout_linear) {
preferencesBuilder.setType(PostsLayoutPreferences.PostsLayoutType.LINEAR);
preferencesBuilder.setColCount(1);
binding.staggeredOrGridOptions.setVisibility(View.GONE);
} else if (checkedId == R.id.layout_staggered) {
preferencesBuilder.setType(PostsLayoutPreferences.PostsLayoutType.STAGGERED_GRID);
if (preferencesBuilder.getColCount() == 1) {
preferencesBuilder.setColCount(2);
}
binding.staggeredOrGridOptions.setVisibility(View.VISIBLE);
initStaggeredOrGridOptions();
} else {
preferencesBuilder.setType(PostsLayoutPreferences.PostsLayoutType.GRID);
if (preferencesBuilder.getColCount() == 1) {
preferencesBuilder.setColCount(2);
}
binding.staggeredOrGridOptions.setVisibility(View.VISIBLE);
initStaggeredOrGridOptions();
}
}
});
@ -111,14 +116,10 @@ public class PostsLayoutPreferencesDialogFragment extends DialogFragment {
binding.colCountToggle.check(getSelectedColCountId());
binding.colCountToggle.addOnButtonCheckedListener((group, checkedId, isChecked) -> {
if (!isChecked) return;
switch (checkedId) {
case R.id.col_count_two:
preferencesBuilder.setColCount(2);
break;
case R.id.col_count_three:
default:
preferencesBuilder.setColCount(3);
break;
if (checkedId == R.id.col_count_two) {
preferencesBuilder.setColCount(2);
} else {
preferencesBuilder.setColCount(3);
}
});
}
@ -135,17 +136,12 @@ public class PostsLayoutPreferencesDialogFragment extends DialogFragment {
binding.avatarSizeToggle.setVisibility(preferencesBuilder.isAvatarVisible() ? View.VISIBLE : View.GONE);
binding.avatarSizeToggle.addOnButtonCheckedListener((group, checkedId, isChecked) -> {
if (!isChecked) return;
switch (checkedId) {
case R.id.avatar_size_tiny:
preferencesBuilder.setProfilePicSize(PostsLayoutPreferences.ProfilePicSize.TINY);
break;
case R.id.avatar_size_small:
preferencesBuilder.setProfilePicSize(PostsLayoutPreferences.ProfilePicSize.SMALL);
break;
case R.id.avatar_size_regular:
default:
preferencesBuilder.setProfilePicSize(PostsLayoutPreferences.ProfilePicSize.REGULAR);
break;
if (checkedId == R.id.avatar_size_tiny) {
preferencesBuilder.setProfilePicSize(PostsLayoutPreferences.ProfilePicSize.TINY);
} else if (checkedId == R.id.avatar_size_small) {
preferencesBuilder.setProfilePicSize(PostsLayoutPreferences.ProfilePicSize.SMALL);
} else {
preferencesBuilder.setProfilePicSize(PostsLayoutPreferences.ProfilePicSize.REGULAR);
}
});
}

466
app/src/main/java/awais/instagrabber/fragments/CommentsViewerFragment.java

@ -12,69 +12,129 @@ import android.text.TextWatcher;
import android.text.style.RelativeSizeSpan;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.LinearLayout;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SearchView;
import androidx.fragment.app.Fragment;
import androidx.appcompat.widget.LinearLayoutCompat;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavDirections;
import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import awais.instagrabber.R;
import awais.instagrabber.adapters.CommentsAdapter;
import awais.instagrabber.asyncs.CommentsFetcher;
import awais.instagrabber.databinding.FragmentCommentsBinding;
import awais.instagrabber.dialogs.ProfilePicDialogFragment;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.CommentModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.viewmodels.CommentsViewModel;
import awais.instagrabber.webservices.MediaService;
import awais.instagrabber.webservices.ServiceCallback;
import static android.content.Context.INPUT_METHOD_SERVICE;
public final class CommentsViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
public final class CommentsViewerFragment extends BottomSheetDialogFragment implements SwipeRefreshLayout.OnRefreshListener {
private static final String TAG = "CommentsViewerFragment";
private final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);
private CommentsAdapter commentsAdapter;
private CommentModel commentModel;
private FragmentCommentsBinding binding;
private String shortCode;
private String userId;
private Resources resources;
private InputMethodManager imm;
private AppCompatActivity fragmentActivity;
private LinearLayout root;
private LinearLayoutCompat root;
private boolean shouldRefresh = true;
private MediaService mediaService;
private String postId;
private CommentsViewModel commentsViewModel;
private final CommentsAdapter.CommentCallback commentCallback = new CommentsAdapter.CommentCallback() {
@Override
public void onClick(final CommentModel comment) {
onCommentClick(comment);
}
@Override
public void onHashtagClick(final String hashtag) {
final NavDirections action = CommentsViewerFragmentDirections.actionGlobalHashTagFragment(hashtag);
NavHostFragment.findNavController(CommentsViewerFragment.this).navigate(action);
}
@Override
public void onMentionClick(final String mention) {
openProfile(mention);
}
@Override
public void onURLClick(final String url) {
Utils.openURL(getContext(), url);
}
@Override
public void onEmailClick(final String emailAddress) {
Utils.openEmailAddress(getContext(), emailAddress);
}
};
private final View.OnClickListener newCommentListener = v -> {
final Editable text = binding.commentText.getText();
final Context context = getContext();
if (context == null) return;
if (text == null || TextUtils.isEmpty(text.toString())) {
Toast.makeText(context, R.string.comment_send_empty_comment, Toast.LENGTH_SHORT).show();
return;
}
final String userId = CookieUtils.getUserIdFromCookie(cookie);
if (userId == null) return;
String replyToId = null;
final CommentModel commentModel = commentsAdapter.getSelected();
if (commentModel != null) {
replyToId = commentModel.getId();
}
mediaService.comment(postId, text.toString(), userId, replyToId, CookieUtils.getCsrfTokenFromCookie(cookie), new ServiceCallback<Boolean>() {
@Override
public void onSuccess(final Boolean result) {
commentsAdapter.clearSelection();
binding.commentText.setText("");
if (!result) {
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
return;
}
onRefresh();
}
@Override
public void onFailure(final Throwable t) {
Log.e(TAG, "Error during comment", t);
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
};
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fragmentActivity = (AppCompatActivity) getActivity();
mediaService = MediaService.getInstance();
setHasOptionsMenu(true);
// setHasOptionsMenu(true);
}
@NonNull
@ -85,6 +145,8 @@ public final class CommentsViewerFragment extends Fragment implements SwipeRefre
return root;
}
binding = FragmentCommentsBinding.inflate(getLayoutInflater());
binding.swipeRefreshLayout.setEnabled(false);
binding.swipeRefreshLayout.setNestedScrollingEnabled(false);
root = binding.getRoot();
return root;
}
@ -96,34 +158,33 @@ public final class CommentsViewerFragment extends Fragment implements SwipeRefre
shouldRefresh = false;
}
@Override
public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
inflater.inflate(R.menu.follow, menu);
menu.findItem(R.id.action_compare).setVisible(false);
final MenuItem menuSearch = menu.findItem(R.id.action_search);
final SearchView searchView = (SearchView) menuSearch.getActionView();
searchView.setQueryHint(getResources().getString(R.string.action_search));
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(final String query) {
return false;
}
@Override
public boolean onQueryTextChange(final String query) {
if (commentsAdapter != null) commentsAdapter.getFilter().filter(query);
return true;
}
});
}
// @Override
// public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
// inflater.inflate(R.menu.follow, menu);
// menu.findItem(R.id.action_compare).setVisible(false);
// final MenuItem menuSearch = menu.findItem(R.id.action_search);
// final SearchView searchView = (SearchView) menuSearch.getActionView();
// searchView.setQueryHint(getResources().getString(R.string.action_search));
// searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
// @Override
// public boolean onQueryTextSubmit(final String query) {
// return false;
// }
//
// @Override
// public boolean onQueryTextChange(final String query) {
// // if (commentsAdapter != null) commentsAdapter.getFilter().filter(query);
// return true;
// }
// });
// }
@Override
public void onRefresh() {
binding.swipeRefreshLayout.setRefreshing(true);
new CommentsFetcher(shortCode, commentModels -> {
commentsViewModel.getList().postValue(commentModels);
binding.swipeRefreshLayout.setRefreshing(false);
commentsAdapter = new CommentsAdapter(commentModels, true, clickListener, mentionClickListener);
binding.rvComments.setAdapter(commentsAdapter);
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
@ -133,9 +194,14 @@ public final class CommentsViewerFragment extends Fragment implements SwipeRefre
shortCode = fragmentArgs.getShortCode();
postId = fragmentArgs.getPostId();
userId = fragmentArgs.getPostUserId();
setTitle();
// setTitle();
binding.swipeRefreshLayout.setOnRefreshListener(this);
binding.swipeRefreshLayout.setRefreshing(true);
commentsViewModel = new ViewModelProvider(this).get(CommentsViewModel.class);
binding.rvComments.setLayoutManager(new LinearLayoutManager(getContext()));
commentsAdapter = new CommentsAdapter(commentCallback);
binding.rvComments.setAdapter(commentsAdapter);
commentsViewModel.getList().observe(getViewLifecycleOwner(), commentsAdapter::submitList);
resources = getResources();
if (!TextUtils.isEmpty(cookie)) {
binding.commentField.setStartIconVisible(false);
@ -155,121 +221,107 @@ public final class CommentsViewerFragment extends Fragment implements SwipeRefre
public void afterTextChanged(final Editable s) {}
});
binding.commentField.setStartIconOnClickListener(v -> {
if (commentModel != null) {
final View focus = binding.rvComments.findViewWithTag(commentModel);
focus.setBackgroundColor(0x00000000);
commentModel = null;
}
commentsAdapter.clearSelection();
binding.commentText.setText("");
});
binding.commentField.setEndIconOnClickListener(newCommentListener);
}
new CommentsFetcher(this.shortCode, commentModels -> {
commentsAdapter = new CommentsAdapter(commentModels, true, clickListener, mentionClickListener);
binding.rvComments.setAdapter(commentsAdapter);
binding.swipeRefreshLayout.setRefreshing(false);
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
onRefresh();
}
private void setTitle() {
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
if (actionBar == null) return;
actionBar.setTitle(R.string.title_comments);
// private void setTitle() {
// final ActionBar actionBar = fragmentActivity.getSupportActionBar();
// if (actionBar == null) return;
// actionBar.setTitle(R.string.title_comments);
// actionBar.setSubtitle(shortCode);
}
// }
private void onCommentClick(final CommentModel commentModel) {
final String username = commentModel.getProfileModel().getUsername();
final SpannableString title = new SpannableString(username + ":\n" + commentModel.getText());
title.setSpan(new RelativeSizeSpan(1.23f), 0, username.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
final DialogInterface.OnClickListener profileDialogListener = (dialog, which) -> {
String[] commentDialogList;
final String userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
if (!TextUtils.isEmpty(cookie)
&& userIdFromCookie != null
&& (userIdFromCookie.equals(commentModel.getProfileModel().getId()) || userIdFromCookie.equals(userId))) {
commentDialogList = new String[]{
resources.getString(R.string.open_profile),
resources.getString(R.string.view_pfp),
resources.getString(R.string.comment_viewer_copy_user),
// resources.getString(R.string.comment_viewer_copy_comment),
resources.getString(R.string.comment_viewer_reply_comment),
commentModel.getLiked() ? resources.getString(R.string.comment_viewer_unlike_comment)
: resources.getString(R.string.comment_viewer_like_comment),
resources.getString(R.string.comment_viewer_delete_comment)
};
} else if (!TextUtils.isEmpty(cookie)) {
commentDialogList = new String[]{
resources.getString(R.string.open_profile),
resources.getString(R.string.view_pfp),
resources.getString(R.string.comment_viewer_copy_user),
// resources.getString(R.string.comment_viewer_copy_comment),
resources.getString(R.string.comment_viewer_reply_comment),
commentModel.getLiked() ? resources.getString(R.string.comment_viewer_unlike_comment)
: resources.getString(R.string.comment_viewer_like_comment),
};
} else {
commentDialogList = new String[]{
resources.getString(R.string.open_profile),
resources.getString(R.string.view_pfp),
resources.getString(R.string.comment_viewer_copy_user),
// resources.getString(R.string.comment_viewer_copy_comment)
};
}
final Context context = getContext();
if (context == null) return;
if (commentModel == null) {
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
return;
}
final ProfileModel profileModel = commentModel.getProfileModel();
switch (which) {
case 0: // open profile
openProfile(profileModel.getUsername());
break;
case 1: // view profile pic
final FragmentManager fragmentManager = getParentFragmentManager();
final ProfilePicDialogFragment fragment = new ProfilePicDialogFragment(profileModel.getId(),
profileModel.getUsername(),
profileModel.getHdProfilePic());
final FragmentTransaction ft = fragmentManager.beginTransaction();
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.add(fragment, "profilePicDialog")
.commit();
break;
case 2: // copy username
Utils.copyText(context, profileModel.getUsername());
break;
case 3: // copy comment
Utils.copyText(context, commentModel.getText().toString());
break;
case 4: // reply to comment
final View focus = binding.rvComments.findViewWithTag(commentModel);
focus.setBackgroundColor(0x80888888);
String mention = "@" + profileModel.getUsername() + " ";
binding.commentText.setText(mention);
binding.commentText.requestFocus();
binding.commentText.setSelection(mention.length());
binding.commentText.postDelayed(new Runnable() {
@Override
public void run() {
final DialogInterface.OnClickListener profileDialogListener = (dialog, which) -> {
final ProfileModel profileModel = commentModel.getProfileModel();
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
switch (which) {
case 0: // open profile
openProfile("@" + profileModel.getUsername());
break;
case 1: // view profile pic
final FragmentManager fragmentManager = getParentFragmentManager();
final ProfilePicDialogFragment fragment = new ProfilePicDialogFragment(profileModel.getId(),
profileModel.getUsername(),
profileModel.getHdProfilePic());
final FragmentTransaction ft = fragmentManager.beginTransaction();
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.add(fragment, "profilePicDialog")
.commit();
break;
case 2: // copy username
Utils.copyText(context, profileModel.getUsername());
break;
// case 3: // copy comment
// Utils.copyText(context, commentModel.getText().toString());
// break;
case 3: // reply to comment
// final View focus = binding.rvComments.findViewWithTag(commentModel);
// focus.setBackgroundColor(0x80888888);
commentsAdapter.setSelected(commentModel);
String mention = "@" + profileModel.getUsername() + " ";
binding.commentText.setText(mention);
binding.commentText.requestFocus();
binding.commentText.setSelection(mention.length());
binding.commentText.postDelayed(() -> {
imm = (InputMethodManager) context.getSystemService(INPUT_METHOD_SERVICE);
if (imm == null) return;
imm.showSoftInput(binding.commentText, 0);
}, 200);
break;
case 4: // like/unlike comment
if (csrfToken == null) {
return;
}
}, 200);
break;
case 5: // like/unlike comment
if (!commentModel.getLiked()) {
mediaService.commentLike(commentModel.getId(), CookieUtils.getCsrfTokenFromCookie(cookie), new ServiceCallback<Boolean>() {
@Override
public void onSuccess(final Boolean result) {
commentModel = null;
if (!result) {
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
return;
}
onRefresh();
}
@Override
public void onFailure(final Throwable t) {
Log.e(TAG, "Error liking comment", t);
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
return;
}
mediaService.commentUnlike(commentModel.getId(), CookieUtils.getCsrfTokenFromCookie(cookie), new ServiceCallback<Boolean>() {
@Override
public void onSuccess(final Boolean result) {
commentModel = null;
if (!result) {
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
return;
}
onRefresh();
}
@Override
public void onFailure(final Throwable t) {
Log.e(TAG, "Error unliking comment", t);
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
break;
case 6: // delete comment
final String userId = CookieUtils.getUserIdFromCookie(cookie);
if (userId == null) return;
mediaService.deleteComment(
postId, userId, commentModel.getId(), CookieUtils.getCsrfTokenFromCookie(cookie),
new ServiceCallback<Boolean>() {
if (!commentModel.getLiked()) {
mediaService.commentLike(commentModel.getId(), csrfToken, new ServiceCallback<Boolean>() {
@Override
public void onSuccess(final Boolean result) {
commentModel = null;
if (!result) {
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
return;
@ -279,112 +331,62 @@ public final class CommentsViewerFragment extends Fragment implements SwipeRefre
@Override
public void onFailure(final Throwable t) {
Log.e(TAG, "Error deleting comment", t);
Log.e(TAG, "Error liking comment", t);
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
break;
}
};
private final View.OnClickListener clickListener = v -> {
final Object tag = v.getTag();
if (tag instanceof CommentModel) {
commentModel = (CommentModel) tag;
final String username = commentModel.getProfileModel().getUsername();
final SpannableString title = new SpannableString(username + ":\n" + commentModel.getText());
title.setSpan(new RelativeSizeSpan(1.23f), 0, username.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
String[] commentDialogList;
final String userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
if (!TextUtils.isEmpty(cookie)
&& userIdFromCookie != null
&& (userIdFromCookie.equals(commentModel.getProfileModel().getId()) || userIdFromCookie.equals(userId))) {
commentDialogList = new String[]{
resources.getString(R.string.open_profile),
resources.getString(R.string.view_pfp),
resources.getString(R.string.comment_viewer_copy_user),
resources.getString(R.string.comment_viewer_copy_comment),
resources.getString(R.string.comment_viewer_reply_comment),
commentModel.getLiked() ? resources.getString(R.string.comment_viewer_unlike_comment)
: resources.getString(R.string.comment_viewer_like_comment),
resources.getString(R.string.comment_viewer_delete_comment)
};
} else if (!TextUtils.isEmpty(cookie)) {
commentDialogList = new String[]{
resources.getString(R.string.open_profile),
resources.getString(R.string.view_pfp),
resources.getString(R.string.comment_viewer_copy_user),
resources.getString(R.string.comment_viewer_copy_comment),
resources.getString(R.string.comment_viewer_reply_comment),
commentModel.getLiked() ? resources.getString(R.string.comment_viewer_unlike_comment)
: resources.getString(R.string.comment_viewer_like_comment),
};
} else {
commentDialogList = new String[]{
resources.getString(R.string.open_profile),
resources.getString(R.string.view_pfp),
resources.getString(R.string.comment_viewer_copy_user),
resources.getString(R.string.comment_viewer_copy_comment)
};
}
final Context context = getContext();
if (context == null) return;
new AlertDialog.Builder(context)
.setTitle(title)
.setItems(commentDialogList, profileDialogListener)
.setNegativeButton(R.string.cancel, null)
.show();
}
};
private final MentionClickListener mentionClickListener = (view, text, isHashtag, isLocation) -> {
if (isHashtag) {
final NavDirections action = CommentsViewerFragmentDirections.actionGlobalHashTagFragment(text);
NavHostFragment.findNavController(this).navigate(action);
return;
}
openProfile(text);
};
return;
}
mediaService.commentUnlike(commentModel.getId(), csrfToken, new ServiceCallback<Boolean>() {
@Override
public void onSuccess(final Boolean result) {
if (!result) {
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
return;
}
onRefresh();
}
private final View.OnClickListener newCommentListener = v -> {
final Editable text = binding.commentText.getText();
final Context context = getContext();
if (context == null) return;
if (text == null || TextUtils.isEmpty(text.toString())) {
Toast.makeText(context, R.string.comment_send_empty_comment, Toast.LENGTH_SHORT).show();
return;
}
final String userId = CookieUtils.getUserIdFromCookie(cookie);
if (userId == null) return;
String replyToId = null;
if (commentModel != null) {
replyToId = commentModel.getId();
}
mediaService.comment(postId, text.toString(), userId, replyToId, CookieUtils.getCsrfTokenFromCookie(cookie), new ServiceCallback<Boolean>() {
@Override
public void onSuccess(final Boolean result) {
commentModel = null;
binding.commentText.setText("");
if (!result) {
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
return;
}
onRefresh();
}
@Override
public void onFailure(final Throwable t) {
Log.e(TAG, "Error unliking comment", t);
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
break;
case 5: // delete comment
final String userId = CookieUtils.getUserIdFromCookie(cookie);
if (userId == null) return;
mediaService.deleteComment(
postId, userId, commentModel.getId(), csrfToken,
new ServiceCallback<Boolean>() {
@Override
public void onSuccess(final Boolean result) {
if (!result) {
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
return;
}
onRefresh();
}
@Override
public void onFailure(final Throwable t) {
Log.e(TAG, "Error during comment", t);
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
@Override
public void onFailure(final Throwable t) {
Log.e(TAG, "Error deleting comment", t);
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
break;
}
});
};
};
new AlertDialog.Builder(context)
.setTitle(title)
.setItems(commentDialogList, profileDialogListener)
.setNegativeButton(R.string.cancel, null)
.show();
}
private void openProfile(final String username) {
final NavDirections action = CommentsViewerFragmentDirections.actionGlobalProfileFragment("@" + username);
final NavDirections action = CommentsViewerFragmentDirections.actionGlobalProfileFragment(username);
NavHostFragment.findNavController(this).navigate(action);
}
}

38
app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java

@ -1,7 +1,6 @@
package awais.instagrabber.fragments;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Typeface;
import android.os.AsyncTask;
import android.os.Bundle;
@ -22,7 +21,6 @@ import androidx.activity.OnBackPressedDispatcher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavDirections;
@ -73,6 +71,8 @@ import static awais.instagrabber.utils.Utils.settingsHelper;
public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
private static final String TAG = "HashTagFragment";
public static final String ARG_HASHTAG = "hashtag";
private MainActivity fragmentActivity;
private FragmentHashtagBinding binding;
private NestedCoordinatorLayout root;
@ -298,24 +298,24 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
if (context == null) return;
if (isLoggedIn) {
storiesService.getUserStory(hashtagModel.getName(),
null,
false,
true,
false,
new ServiceCallback<List<StoryModel>>() {
@Override
public void onSuccess(final List<StoryModel> storyModels) {
if (storyModels != null && !storyModels.isEmpty()) {
binding.mainHashtagImage.setStoriesBorder();
hasStories = true;
}
}
null,
false,
true,
false,
new ServiceCallback<List<StoryModel>>() {
@Override
public void onSuccess(final List<StoryModel> storyModels) {
if (storyModels != null && !storyModels.isEmpty()) {
binding.mainHashtagImage.setStoriesBorder();
hasStories = true;
}
}
@Override
public void onFailure(final Throwable t) {
Log.e(TAG, "Error", t);
}
});
@Override
public void onFailure(final Throwable t) {
Log.e(TAG, "Error", t);
}
});
binding.btnFollowTag.setVisibility(View.VISIBLE);
binding.btnFollowTag.setText(hashtagModel.getFollowing() ? R.string.unfollow : R.string.follow);
binding.btnFollowTag.setChipIconResource(hashtagModel.getFollowing()

101
app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java

@ -76,12 +76,9 @@ import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.webservices.MediaService;
import awais.instagrabber.webservices.ServiceCallback;
import io.github.armcha.autolink.MODE_EMAIL;
import io.github.armcha.autolink.MODE_HASHTAG;
import io.github.armcha.autolink.MODE_MENTION;
import kotlin.Unit;
import static androidx.core.content.PermissionChecker.checkSelfPermission;
import static awais.instagrabber.fragments.HashTagFragment.ARG_HASHTAG;
import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION;
import static awais.instagrabber.utils.Utils.settingsHelper;
@ -90,6 +87,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
private static final String COOKIE = settingsHelper.getString(Constants.COOKIE);
private static final int DETAILS_HIDE_DELAY_MILLIS = 2000;
private static final String ARG_FEED_MODEL = "feedModel";
private static final String ARG_SLIDER_POSITION = "position";
private static final int STORAGE_PERM_REQUEST_CODE = 8020;
private FeedModel feedModel;
@ -105,6 +103,8 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
private SliderItemsAdapter sliderItemsAdapter;
private boolean wasControlsVisible;
private boolean wasPaused;
private int captionState = BottomSheetBehavior.STATE_HIDDEN;
private int sliderPosition;
private final VerticalDragHelper.OnVerticalDragListener onVerticalDragListener = new VerticalDragHelper.OnVerticalDragListener() {
@ -151,6 +151,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
private final FeedModel feedModel;
private View profilePicElement;
private View mainPostElement;
private int position;
public Builder setSharedProfilePicElement(final View profilePicElement) {
this.profilePicElement = profilePicElement;
@ -162,8 +163,13 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
return this;
}
public Builder setPosition(final int position) {
this.position = position;
return this;
}
public PostViewV2Fragment build() {
return PostViewV2Fragment.newInstance(feedModel, profilePicElement, mainPostElement);
return PostViewV2Fragment.newInstance(feedModel, profilePicElement, mainPostElement, position);
}
public Builder(final FeedModel feedModel) {
@ -171,10 +177,16 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
}
}
private static PostViewV2Fragment newInstance(final FeedModel feedModel, final View profilePicElement, final View mainPostElement) {
private static PostViewV2Fragment newInstance(final FeedModel feedModel,
final View profilePicElement,
final View mainPostElement,
final int position) {
final PostViewV2Fragment f = new PostViewV2Fragment(profilePicElement, mainPostElement);
final Bundle args = new Bundle();
args.putSerializable(ARG_FEED_MODEL, feedModel);
if (position >= 0) {
args.putInt(ARG_SLIDER_POSITION, position);
}
f.setArguments(args);
return f;
}
@ -353,6 +365,9 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
return;
}
feedModel = (FeedModel) feedModelSerializable;
if (feedModel.getItemType() == MediaItemType.MEDIA_TYPE_SLIDER) {
sliderPosition = arguments.getInt(ARG_SLIDER_POSITION, 0);
}
}
@Nullable
@ -402,6 +417,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
public void onPause() {
super.onPause();
wasPaused = true;
captionState = bottomSheetBehavior.getState();
}
@Override
@ -424,7 +440,9 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
@Override
public void onSaveInstanceState(@NonNull final Bundle outState) {
super.onSaveInstanceState(outState);
Log.d(TAG, "onSaveInstanceState");
if (feedModel.getItemType() == MediaItemType.MEDIA_TYPE_SLIDER) {
outState.putInt(ARG_SLIDER_POSITION, sliderPosition);
}
}
@Override
@ -505,32 +523,34 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
}
private void init() {
binding.getRoot().getBackground().mutate().setAlpha(0);
// if (getArguments() == null) return;
// final PostViewV2FragmentArgs fragmentArgs = PostViewV2FragmentArgs.fromBundle(getArguments());
// feedModel = fragmentArgs.getFeedModel();
if (!wasPaused && (sharedProfilePicElement != null || sharedMainPostElement != null)) {
binding.getRoot().getBackground().mutate().setAlpha(0);
}
setupProfilePic();
setupTitles();
setupCaption();
setupCounts();
setupPostTypeLayout();
setupCommonActions();
// binding.getRoot().setOnTouchListener(onTouchListener);
// final String[] idOrCodeArray = fragmentArgs.getIdOrCodeArray();
// if (idOrCodeArray.length == 0) return;
// currentPostIndex = fragmentArgs.getIndex();
// if (currentPostIndex < 0) return;
// if (currentPostIndex >= idOrCodeArray.length) return;
// idOrCodeList = Arrays.asList(idOrCodeArray);
// viewerPostViewModel.getList().setValue(createPlaceholderModels(idOrCodeArray.length));
// isId = fragmentArgs.getIsId();
// fetchPost();
}
private void setupCommonActions() {
setupLike();
setupSave();
setupDownload();
setupComment();
}
private void setupComment() {
binding.comment.setOnClickListener(v -> {
final NavController navController = getNavController();
if (navController == null) return;
final Bundle bundle = new Bundle();
bundle.putString("shortCode", feedModel.getShortCode());
bundle.putString("postId", feedModel.getPostId());
bundle.putString("postUserId", feedModel.getProfileModel().getId());
navController.navigate(R.id.action_global_commentsViewerFragment, bundle);
});
}
private void setupDownload() {
@ -758,30 +778,26 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
private void setupCaption() {
final CharSequence postCaption = feedModel.getPostCaption();
binding.caption.addAutoLinkMode(MODE_HASHTAG.INSTANCE, MODE_MENTION.INSTANCE, MODE_EMAIL.INSTANCE);
binding.caption.onAutoLinkClick(autoLinkItem -> {
// Log.d(TAG, "setupCaption: autoLinkItem: " + autoLinkItem.getMode().getModeName() + " : " + autoLinkItem.getOriginalText());
final String originalText = autoLinkItem.getOriginalText();
if (autoLinkItem.getMode().equals(MODE_HASHTAG.INSTANCE)) {
final NavController navController = NavHostFragment.findNavController(this);
final Bundle bundle = new Bundle();
bundle.putString("hashtag", originalText);
navController.navigate(R.id.action_global_hashTagFragment, bundle);
return Unit.INSTANCE;
}
if (autoLinkItem.getMode().equals(MODE_MENTION.INSTANCE)) {
navigateToProfile(originalText);
return Unit.INSTANCE;
}
return Unit.INSTANCE;
binding.caption.addOnHashtagListener(autoLinkItem -> {
final NavController navController = NavHostFragment.findNavController(this);
final Bundle bundle = new Bundle();
final String originalText = autoLinkItem.getOriginalText().trim();
bundle.putString(ARG_HASHTAG, originalText);
navController.navigate(R.id.action_global_hashTagFragment, bundle);
});
binding.caption.addOnMentionClickListener(autoLinkItem -> {
final String originalText = autoLinkItem.getOriginalText().trim();
navigateToProfile(originalText);
});
binding.caption.addOnEmailClickListener(autoLinkItem -> Utils.openEmailAddress(getContext(), autoLinkItem.getOriginalText().trim()));
binding.caption.addOnURLClickListener(autoLinkItem -> Utils.openURL(getContext(), autoLinkItem.getOriginalText().trim()));
binding.caption.setOnLongClickListener(v -> {
Utils.copyText(getContext(), postCaption);
Utils.copyText(context, postCaption);
return true;
});
binding.caption.setText(postCaption);
bottomSheetBehavior = BottomSheetBehavior.from(binding.captionParent);
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
bottomSheetBehavior.setState(captionState);
bottomSheetBehavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull final View bottomSheet, final int newState) {}
@ -900,7 +916,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
if (sharedMainPostElement != null) {
addSharedElement(sharedMainPostElement, binding.sliderParent);
}
sliderItemsAdapter = new SliderItemsAdapter(onVerticalDragListener, binding.playerControls, new SliderCallbackAdapter() {
sliderItemsAdapter = new SliderItemsAdapter(onVerticalDragListener, binding.playerControls, true, new SliderCallbackAdapter() {
@Override
public void onThumbnailLoaded(final int position) {
if (position != 0) return;
@ -926,6 +942,9 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
}
});
binding.sliderParent.setAdapter(sliderItemsAdapter);
if (sliderPosition >= 0 && sliderPosition < feedModel.getSliderItems().size()) {
binding.sliderParent.setCurrentItem(sliderPosition);
}
binding.sliderParent.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
int prevPosition = -1;
@ -947,6 +966,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
public void onPageSelected(final int position) {
final int size = feedModel.getSliderItems().size();
if (position < 0 || position >= size) return;
sliderPosition = position;
final String text = (position + 1) + "/" + size;
binding.mediaCounter.setText(text);
final PostChild postChild = feedModel.getSliderItems().get(position);
@ -1046,6 +1066,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
vol,
aspectRatio,
feedModel.getThumbnailUrl(),
true,
binding.playerControls,
videoPlayerCallback);
}

406
app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java

@ -14,265 +14,159 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.content.PermissionChecker;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavController;
import androidx.navigation.NavDirections;
import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.activities.MainActivity;
import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.adapters.FeedStoriesAdapter;
import awais.instagrabber.asyncs.FeedPostFetchService;
import awais.instagrabber.customviews.helpers.VideoAwareRecyclerScroller;
import awais.instagrabber.databinding.FragmentFeedBinding;
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
import awais.instagrabber.fragments.PostViewV2Fragment;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.FeedStoryModel;
import awais.instagrabber.models.PostsLayoutPreferences;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.DownloadUtils;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.viewmodels.FeedStoriesViewModel;
import awais.instagrabber.webservices.ServiceCallback;
import awais.instagrabber.webservices.StoriesService;
import static androidx.core.content.PermissionChecker.checkSelfPermission;
import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
private static final String TAG = "FeedFragment";
private static final double MAX_VIDEO_HEIGHT = 0.9 * Utils.displayMetrics.heightPixels;
private static final int RESIZED_VIDEO_HEIGHT = (int) (0.8 * Utils.displayMetrics.heightPixels);
private static final int STORAGE_PERM_REQUEST_CODE = 8020;
// private static final double MAX_VIDEO_HEIGHT = 0.9 * Utils.displayMetrics.heightPixels;
// private static final int RESIZED_VIDEO_HEIGHT = (int) (0.8 * Utils.displayMetrics.heightPixels);
private MainActivity fragmentActivity;
private CoordinatorLayout root;
private FragmentFeedBinding binding;
private StoriesService storiesService;
private boolean feedHasNextPage = false;
private String feedEndCursor = null;
// private boolean feedHasNextPage = false;
// private String feedEndCursor = null;
// private FeedViewModel feedViewModel;
private VideoAwareRecyclerScroller videoAwareRecyclerScroller;
// private VideoAwareRecyclerScroller videoAwareRecyclerScroller;
private boolean shouldRefresh = true;
private boolean isPullToRefresh;
// private boolean isPullToRefresh;
private FeedStoriesViewModel feedStoriesViewModel;
private StaggeredGridLayoutManager gridLayoutManager;
// private StaggeredGridLayoutManager gridLayoutManager;
private boolean storiesFetching;
private final boolean shouldAutoPlay = settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS);
// private final FetchListener<FeedModel[]> feedFetchListener = new FetchListener<FeedModel[]>() {
// @Override
// public void doBefore() {
// binding.feedSwipeRefreshLayout.post(() -> binding.feedSwipeRefreshLayout.setRefreshing(true));
// }
//
// @Override
// public void onResult(final FeedModel[] result) {
// if (result == null || result.length <= 0) {
// binding.feedSwipeRefreshLayout.setRefreshing(false);
// return;
// }
// final List<FeedModel> currentFeedModelList = feedViewModel.getList().getValue();
// final Map<String, FeedModel> thumbToFeedMap = new HashMap<>();
// for (final FeedModel feedModel : result) {
// thumbToFeedMap.put(feedModel.getThumbnailUrl(), feedModel);
// }
// final BaseDataSubscriber<Void> subscriber = new BaseDataSubscriber<Void>() {
// int success = 0;
// int failed = 0;
//
// @Override
// protected void onNewResultImpl(@NonNull final DataSource<Void> dataSource) {
// final Map<String, Object> extras = dataSource.getExtras();
// if (extras == null) return;
// final Uri thumbUri = (Uri) extras.get("uri_source");
// if (thumbUri == null) return;
// final Integer encodedWidth = (Integer) extras.get("encoded_width");
// final Integer encodedHeight = (Integer) extras.get("encoded_height");
// if (encodedWidth == null || encodedHeight == null) return;
// final FeedModel feedModel = thumbToFeedMap.get(thumbUri.toString());
// if (feedModel == null) return;
// int requiredWidth = Utils.displayMetrics.widthPixels;
// int resultingHeight = NumberUtils
// .getResultingHeight(requiredWidth, encodedHeight, encodedWidth);
// if (feedModel
// .getItemType() == MediaItemType.MEDIA_TYPE_VIDEO && resultingHeight >= MAX_VIDEO_HEIGHT) {
// // If its a video and the height is too large, need to reduce the height,
// // so that entire video fits on screen
// resultingHeight = RESIZED_VIDEO_HEIGHT;
// requiredWidth = NumberUtils.getResultingWidth(RESIZED_VIDEO_HEIGHT,
// resultingHeight,
// requiredWidth);
// }
// feedModel.setImageWidth(requiredWidth);
// feedModel.setImageHeight(resultingHeight);
// success++;
// updateAdapter();
// }
//
// @Override
// protected void onFailureImpl(@NonNull final DataSource<Void> dataSource) {
// failed++;
// updateAdapter();
// }
//
// public void updateAdapter() {
// if (failed + success != result.length) return;
//
// }
// };
// for (final FeedModel feedModel : result) {
// final DataSource<Void> ds = Fresco.getImagePipeline()
// .prefetchToBitmapCache(ImageRequest.fromUri(feedModel.getThumbnailUrl()), null);
// ds.subscribe(subscriber, UiThreadImmediateExecutorService.getInstance());
// }
// List<FeedModel> finalList = currentFeedModelList == null || currentFeedModelList.isEmpty()
// ? new ArrayList<>()
// : new ArrayList<>(currentFeedModelList);
// final List<FeedModel> resultList = Arrays.asList(result);
// if (isPullToRefresh) {
// finalList = resultList;
// isPullToRefresh = false;
// } else {
// finalList.addAll(resultList);
// }
// // feedViewModel.getList().postValue(finalList);
// final PostModel feedPostModel = result[result.length - 1];
// if (feedPostModel != null) {
// feedEndCursor = feedPostModel.getEndCursor();
// feedHasNextPage = feedPostModel.hasNextPage();
// feedPostModel.setPageCursor(false, null);
// }
// binding.feedSwipeRefreshLayout.setRefreshing(false);
// }
// };
private final MentionClickListener mentionClickListener = (view, text, isHashtag, isLocation) -> {
if (isHashtag) {
final NavDirections action = FeedFragmentDirections.actionGlobalHashTagFragment(text);
NavHostFragment.findNavController(this).navigate(action);
return;
// private final boolean shouldAutoPlay = settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS);
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
@Override
public void onPostClick(final FeedModel feedModel, final View profilePicView, final View mainPostImage) {
openPostDialog(feedModel, profilePicView, mainPostImage, -1);
}
if (isLocation) {
final NavDirections action = FeedFragmentDirections.actionGlobalLocationFragment(text);
NavHostFragment.findNavController(this).navigate(action);
return;
@Override
public void onSliderClick(final FeedModel feedModel, final int position) {
openPostDialog(feedModel, null, null, position);
}
@Override
public void onCommentsClick(final FeedModel feedModel) {
final NavDirections commentsAction = FeedFragmentDirections.actionGlobalCommentsViewerFragment(
feedModel.getShortCode(),
feedModel.getPostId(),
feedModel.getProfileModel().getId()
);
NavHostFragment.findNavController(FeedFragment.this).navigate(commentsAction);
}
@Override
public void onDownloadClick(final FeedModel feedModel) {
final Context context = getContext();
if (context == null) return;
if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
showDownloadDialog(feedModel);
return;
}
requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE);
}
@Override
public void onHashtagClick(final String hashtag) {
final NavDirections action = FeedFragmentDirections.actionGlobalHashTagFragment(hashtag);
NavHostFragment.findNavController(FeedFragment.this).navigate(action);
}
@Override
public void onLocationClick(final FeedModel feedModel) {
final NavDirections action = FeedFragmentDirections.actionGlobalLocationFragment(feedModel.getLocationId());
NavHostFragment.findNavController(FeedFragment.this).navigate(action);
}
@Override
public void onMentionClick(final String mention) {
navigateToProfile(mention.trim());
}
@Override
public void onNameClick(final FeedModel feedModel, final View profilePicView) {
navigateToProfile("@" + feedModel.getProfileModel().getUsername());
}
@Override
public void onProfilePicClick(final FeedModel feedModel, final View profilePicView) {
navigateToProfile("@" + feedModel.getProfileModel().getUsername());
}
@Override
public void onURLClick(final String url) {
Utils.openURL(getContext(), url);
}
@Override
public void onEmailClick(final String emailId) {
Utils.openEmailAddress(getContext(), emailId);
}
private void openPostDialog(final FeedModel feedModel,
final View profilePicView,
final View mainPostImage,
final int position) {
final PostViewV2Fragment.Builder builder = PostViewV2Fragment
.builder(feedModel);
if (position >= 0) {
builder.setPosition(position);
}
final PostViewV2Fragment fragment = builder
.setSharedProfilePicElement(profilePicView)
.setSharedMainPostElement(mainPostImage)
.build();
fragment.show(getChildFragmentManager(), "post_view");
}
final NavDirections action = FeedFragmentDirections.actionGlobalProfileFragment("@" + text);
NavHostFragment.findNavController(this).navigate(action);
};
private final View.OnClickListener postViewClickListener = v -> {
// gridLayoutManager.setSpanCount(1);
// final Object tag = v.getTag();
// if (!(tag instanceof FeedModel)) return;
//
// final FeedModel feedModel = (FeedModel) tag;
// if (v instanceof RamboTextView) {
// if (feedModel.isMentionClicked()) feedModel.toggleCaption();
// feedModel.setMentionClicked(false);
// if (!FeedItemViewHolder.expandCollapseTextView((RamboTextView) v, feedModel.getPostCaption()))
// feedModel.toggleCaption();
// return;
// }
//
// final int id = v.getId();
// switch (id) {
// case R.id.btnComments:
// final NavDirections commentsAction = FeedFragmentDirections.actionGlobalCommentsViewerFragment(
// feedModel.getShortCode(),
// feedModel.getPostId(),
// feedModel.getProfileModel().getId()
// );
// NavHostFragment.findNavController(this).navigate(commentsAction);
// break;
// // case R.id.viewStoryPost:
// // final List<FeedModel> feedModels = feedViewModel.getList().getValue();
// // if (feedModels == null || feedModels.size() == 0) return;
// // if (feedModels.get(0) == null) return;
// // final String postId = feedModels.get(0).getPostId();
// // final boolean isId = postId != null;
// // final String[] idsOrShortCodes = new String[feedModels.size()];
// // for (int i = 0; i < feedModels.size(); i++) {
// // idsOrShortCodes[i] = isId ? feedModels.get(i).getPostId()
// // : feedModels.get(i).getShortCode();
// // }
// // final NavDirections action = FeedFragmentDirections.actionGlobalPostViewFragment(
// // feedModel.getPosition(),
// // idsOrShortCodes,
// // isId);
// // NavHostFragment.findNavController(this).navigate(action);
// // break;
// case R.id.btnDownload:
// ProfileModel profileModel = feedModel.getProfileModel();
// final String username = profileModel != null ? profileModel.getUsername() : null;
//
// final ViewerPostModel[] sliderItems = feedModel.getSliderItems();
//
// final Context context = getContext();
// if (context == null) return;
// if (feedModel
// .getItemType() != MediaItemType.MEDIA_TYPE_SLIDER || sliderItems == null || sliderItems.length == 1)
// DownloadUtils.batchDownload(context,
// username,
// DownloadMethod.DOWNLOAD_FEED,
// Collections.singletonList(feedModel));
// else {
// final ArrayList<BasePostModel> postModels = new ArrayList<>();
// final DialogInterface.OnClickListener clickListener1 = (dialog, which) -> {
// postModels.clear();
//
// final boolean breakWhenFoundSelected = which == DialogInterface.BUTTON_POSITIVE;
//
// for (final ViewerPostModel sliderItem : sliderItems) {
// if (sliderItem != null) {
// if (!breakWhenFoundSelected) postModels.add(sliderItem);
// else if (sliderItem.isSelected()) {
// postModels.add(sliderItem);
// break;
// }
// }
// }
//
// // shows 0 items on first item of viewpager cause onPageSelected hasn't been called yet
// if (breakWhenFoundSelected && postModels.size() == 0) {
// postModels.add(sliderItems[0]);
// }
// if (postModels.size() > 0) {
// DownloadUtils.batchDownload(context,
// username,
// DownloadMethod.DOWNLOAD_FEED,
// postModels);
// }
// };
//
// new AlertDialog.Builder(context)
// .setTitle(R.string.post_viewer_download_dialog_title)
// .setPositiveButton(R.string.post_viewer_download_current, clickListener1)
// .setNegativeButton(R.string.post_viewer_download_album, clickListener1)
// .show();
// }
// break;
//
// case R.id.ivProfilePic:
// profileModel = feedModel.getProfileModel();
// if (profileModel != null) mentionClickListener.onClick(null, profileModel.getUsername(), false, false);
// break;
// default:
// break;
// }
};
private void navigateToProfile(final String username) {
final NavController navController = NavHostFragment.findNavController(this);
final Bundle bundle = new Bundle();
bundle.putString("username", username);
navController.navigate(R.id.action_global_profileFragment, bundle);
}
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fragmentActivity = (MainActivity) requireActivity();
storiesService = StoriesService.getInstance();
// feedService = FeedService.getInstance();
// final TransitionInflater inflater = TransitionInflater.from(getContext());
// setExitTransition(inflater.inflateTransition(android.R.transition.move));
setHasOptionsMenu(true);
}
@ -316,74 +210,44 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
@Override
public void onPause() {
super.onPause();
if (videoAwareRecyclerScroller != null) {
videoAwareRecyclerScroller.stopPlaying();
}
// if (videoAwareRecyclerScroller != null) {
// videoAwareRecyclerScroller.stopPlaying();
// }
}
@Override
public void onResume() {
super.onResume();
binding.feedSwipeRefreshLayout.setRefreshing(false);
if (videoAwareRecyclerScroller != null && shouldAutoPlay) {
videoAwareRecyclerScroller.startPlaying();
}
// if (videoAwareRecyclerScroller != null && shouldAutoPlay) {
// videoAwareRecyclerScroller.startPlaying();
// }
}
@Override
public void onRefresh() {
isPullToRefresh = true;
feedEndCursor = null;
// isPullToRefresh = true;
// feedEndCursor = null;
binding.feedRecyclerView.refresh();
fetchStories();
}
private void setupFeed() {
// feedViewModel = new ViewModelProvider(fragmentActivity).get(FeedViewModel.class);
final Context context = getContext();
if (context == null) return;
// final PostFetcher.PostFetchService feedPostsFetchService = new PostFetcher.PostFetchService() {
// @Override
// public void fetch(final int page, final FetchListener<List<FeedModel>> fetchListener) {
// new FeedFetcher(feedEndCursor, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
// }
// };
binding.feedRecyclerView.setViewModelStoreOwner(this)
.setLifeCycleOwner(this)
.setPostFetchService(new FeedPostFetchService())
.setLayoutPreferences(PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_POSTS_LAYOUT)))
.addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
.setOnPostClickListener((feedModel, profilePicView, mainPostImage) -> {
final PostViewV2Fragment fragment = PostViewV2Fragment
.builder(feedModel)
.setSharedProfilePicElement(profilePicView)
.setSharedMainPostElement(mainPostImage)
.build();
fragment.show(getChildFragmentManager(), "post_view");
})
.setFeedItemCallback(feedItemCallback)
.init();
binding.feedSwipeRefreshLayout.setRefreshing(true);
// final LinearLayoutManager layoutManager = new LinearLayoutManager(context);
// binding.feedRecyclerView.setLayoutManager(gridLayoutManager);
// feedAdapter = new FeedAdapterV2(spanCount[0], postViewClickListener, mentionClickListener, feedModel -> {
// final ChangeBounds transition = new ChangeBounds();
// transition.setDuration(200);
// TransitionManager.beginDelayedTransition(binding.feedRecyclerView, transition);
// spanCount[0] = spanCount[0] - 1;
// if (spanCount[0] == 0) {
// spanCount[0] = 3;
// }
// gridLayoutManager.setSpanCount(spanCount[0]);
// });
// feedAdapter.setStateRestorationPolicy(RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY);
// binding.feedRecyclerView.setAdapter(feedAdapter);
// feedViewModel.getList().observe(fragmentActivity, feedAdapter::submitList);
// if (shouldAutoPlay) {
// videoAwareRecyclerScroller = new VideoAwareRecyclerScroller();
// binding.feedRecyclerView.addOnScrollListener(videoAwareRecyclerScroller);
// }
// fetchFeed();
}
private void updateSwipeRefreshState() {
@ -424,6 +288,48 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
});
}
private void showDownloadDialog(final FeedModel feedModel) {
final Context context = getContext();
if (context == null) return;
DownloadUtils.download(context, feedModel);
// switch (feedModel.getItemType()) {
// case MEDIA_TYPE_IMAGE:
// case MEDIA_TYPE_VIDEO:
// break;
// case MEDIA_TYPE_SLIDER:
// break;
// }
// final List<ViewerPostModel> postModelsToDownload = new ArrayList<>();
// // if (!session) {
// final DialogInterface.OnClickListener clickListener = (dialog, which) -> {
// if (which == DialogInterface.BUTTON_NEGATIVE) {
// postModelsToDownload.addAll(postModels);
// } else if (which == DialogInterface.BUTTON_POSITIVE) {
// postModelsToDownload.add(postModels.get(childPosition));
// } else {
// session = true;
// postModelsToDownload.add(postModels.get(childPosition));
// }
// if (postModelsToDownload.size() > 0) {
// DownloadUtils.batchDownload(context,
// username,
// DownloadMethod.DOWNLOAD_POST_VIEWER,
// postModelsToDownload);
// }
// };
// new AlertDialog.Builder(context)
// .setTitle(R.string.post_viewer_download_dialog_title)
// .setMessage(R.string.post_viewer_download_message)
// .setNeutralButton(R.string.post_viewer_download_session, clickListener)
// .setPositiveButton(R.string.post_viewer_download_current, clickListener)
// .setNegativeButton(R.string.post_viewer_download_album, clickListener).show();
// } else {
// DownloadUtils.batchDownload(context,
// username,
// DownloadMethod.DOWNLOAD_POST_VIEWER,
// Collections.singletonList(postModels.get(childPosition)));
}
private void showPostsLayoutPreferences() {
final PostsLayoutPreferencesDialogFragment fragment = new PostsLayoutPreferencesDialogFragment(preferences -> new Handler()
.postDelayed(() -> binding.feedRecyclerView.setLayoutPreferences(preferences), 200));

2
app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java

@ -420,7 +420,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
private void fetchProfileDetails() {
if (TextUtils.isEmpty(username)) return;
new ProfileFetcher(username.substring(1), profileModel -> {
new ProfileFetcher(username.trim().substring(1), profileModel -> {
if (getContext() == null) return;
this.profileModel = profileModel;
// final String userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);

62
app/src/main/java/awais/instagrabber/models/CommentModel.java

@ -3,23 +3,29 @@ package awais.instagrabber.models;
import androidx.annotation.NonNull;
import java.util.Date;
import java.util.List;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
public final class CommentModel {
public class CommentModel {
private final ProfileModel profileModel;
private final String id;
private final CharSequence text;
private final long likes, timestamp;
private CommentModel[] childCommentModels;
private boolean hasNextPage, liked;
private final String text;
private final long likes;
private final long timestamp;
private List<CommentModel> childCommentModels;
private final boolean liked;
private boolean hasNextPage;
private String endCursor;
public CommentModel(final String id, final String text, final long timestamp, final long likes, final boolean liked,
public CommentModel(final String id,
final String text,
final long timestamp,
final long likes,
final boolean liked,
final ProfileModel profileModel) {
this.id = id;
this.text = TextUtils.hasMentions(text) ? TextUtils.getMentionText(text) : text;
this.text = text;
this.likes = likes;
this.liked = liked;
this.timestamp = timestamp;
@ -30,7 +36,7 @@ public final class CommentModel {
return id;
}
public CharSequence getText() {
public String getText() {
return text;
}
@ -51,11 +57,11 @@ public final class CommentModel {
return profileModel;
}
public CommentModel[] getChildCommentModels() {
public List<CommentModel> getChildCommentModels() {
return childCommentModels;
}
public void setChildCommentModels(final CommentModel[] childCommentModels) {
public void setChildCommentModels(final List<CommentModel> childCommentModels) {
this.childCommentModels = childCommentModels;
}
@ -72,21 +78,21 @@ public final class CommentModel {
return endCursor;
}
// @NonNull
// @Override
// public String toString() {
// try {
// final JSONObject object = new JSONObject();
// object.put(Constants.EXTRAS_ID, id);
// object.put("text", text);
// object.put(Constants.EXTRAS_NAME, profileModel != null ? profileModel.getUsername() : "");
// if (childCommentModels != null) object.put("childComments", childCommentModels);
// return object.toString();
// } catch (Exception e) {
// return "{\"id\":\"" + id + "\", \"text\":\"" + text
// //(text != null ? text.replaceAll("\"", "\\\\\"") : "")
// + "\", \"name\":\"" + (profileModel != null ? profileModel.getUsername() : "") +
// (childCommentModels != null ? "\", \"childComments\":" + childCommentModels.length : "\"") + '}';
// }
// }
// @NonNull
// @Override
// public String toString() {
// try {
// final JSONObject object = new JSONObject();
// object.put(Constants.EXTRAS_ID, id);
// object.put("text", text);
// object.put(Constants.EXTRAS_NAME, profileModel != null ? profileModel.getUsername() : "");
// if (childCommentModels != null) object.put("childComments", childCommentModels);
// return object.toString();
// } catch (Exception e) {
// return "{\"id\":\"" + id + "\", \"text\":\"" + text
// //(text != null ? text.replaceAll("\"", "\\\\\"") : "")
// + "\", \"name\":\"" + (profileModel != null ? profileModel.getUsername() : "") +
// (childCommentModels != null ? "\", \"childComments\":" + childCommentModels.length : "\"") + '}';
// }
// }
}

20
app/src/main/java/awais/instagrabber/utils/Utils.java

@ -5,6 +5,7 @@ import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.net.Uri;
import android.util.DisplayMetrics;
@ -158,4 +159,23 @@ public final class Utils {
}
return statusBarHeight;
}
public static void openURL(final Context context, final String url) {
if (context == null || TextUtils.isEmpty(url)) {
return;
}
final Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(url));
context.startActivity(i);
}
public static void openEmailAddress(final Context context, final String emailAddress) {
if (context == null || TextUtils.isEmpty(emailAddress)) {
return;
}
Intent emailIntent = new Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:" + emailAddress));
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "");
emailIntent.putExtra(Intent.EXTRA_TEXT, "");
context.startActivity(emailIntent);
}
}

19
app/src/main/java/awais/instagrabber/viewmodels/CommentsViewModel.java

@ -0,0 +1,19 @@
package awais.instagrabber.viewmodels;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import java.util.List;
import awais.instagrabber.models.CommentModel;
public class CommentsViewModel extends ViewModel {
private MutableLiveData<List<CommentModel>> list;
public MutableLiveData<List<CommentModel>> getList() {
if (list == null) {
list = new MutableLiveData<>();
}
return list;
}
}

56
app/src/main/java/awais/instagrabber/workers/DownloadWorker.java

@ -134,34 +134,34 @@ public class DownloadWorker extends Worker {
boolean deletedIPTC = false;
while ((count = bis.read(buffer, 0, 0x2000)) != -1) {
totalRead = totalRead + count;
if (!deletedIPTC) {
int iptcStart = -1;
int fbmdStart = -1;
int fbmdBytesLen = -1;
for (int i = 0; i < buffer.length; ++i) {
if (buffer[i] == (byte) 0xFF && buffer[i + 1] == (byte) 0xED)
iptcStart = i;
else if (buffer[i] == (byte) 'F' && buffer[i + 1] == (byte) 'B'
&& buffer[i + 2] == (byte) 'M' && buffer[i + 3] == (byte) 'D') {
fbmdStart = i;
fbmdBytesLen = buffer[i - 10] << 24 | (buffer[i - 9] & 0xFF) << 16 |
(buffer[i - 8] & 0xFF) << 8 | (buffer[i - 7] & 0xFF) |
(buffer[i - 6] & 0xFF);
break;
}
}
if (iptcStart != -1 && fbmdStart != -1 && fbmdBytesLen != -1) {
final int fbmdDataLen = (iptcStart + (fbmdStart - iptcStart) + (fbmdBytesLen - iptcStart)) - 4;
fos.write(buffer, 0, iptcStart);
fos.write(buffer, fbmdDataLen + iptcStart, count - fbmdDataLen - iptcStart);
// setProgressAsync(new Data.Builder().putString(URL, url)
// .putFloat(PROGRESS, totalRead * 100f / fileSize)
// .build());
updateDownloadProgress(notificationId, position, total, totalRead * 100f / fileSize);
deletedIPTC = true;
continue;
}
}
// if (!deletedIPTC) {
// int iptcStart = -1;
// int fbmdStart = -1;
// int fbmdBytesLen = -1;
// for (int i = 0; i < buffer.length; ++i) {
// if (buffer[i] == (byte) 0xFF && buffer[i + 1] == (byte) 0xED)
// iptcStart = i;
// else if (buffer[i] == (byte) 'F' && buffer[i + 1] == (byte) 'B'
// && buffer[i + 2] == (byte) 'M' && buffer[i + 3] == (byte) 'D') {
// fbmdStart = i;
// fbmdBytesLen = buffer[i - 10] << 24 | (buffer[i - 9] & 0xFF) << 16 |
// (buffer[i - 8] & 0xFF) << 8 | (buffer[i - 7] & 0xFF) |
// (buffer[i - 6] & 0xFF);
// break;
// }
// }
// if (iptcStart != -1 && fbmdStart != -1 && fbmdBytesLen != -1) {
// final int fbmdDataLen = (iptcStart + (fbmdStart - iptcStart) + (fbmdBytesLen - iptcStart)) - 4;
// fos.write(buffer, 0, iptcStart);
// fos.write(buffer, fbmdDataLen + iptcStart, count - fbmdDataLen - iptcStart);
// // setProgressAsync(new Data.Builder().putString(URL, url)
// // .putFloat(PROGRESS, totalRead * 100f / fileSize)
// // .build());
// updateDownloadProgress(notificationId, position, total, totalRead * 100f / fileSize);
// deletedIPTC = true;
// continue;
// }
// }
fos.write(buffer, 0, count);
// setProgressAsync(new Data.Builder().putString(URL, url)
// .putFloat(PROGRESS, totalRead * 100f / fileSize)

26
app/src/main/res/layout/dialog_post_view.xml

@ -132,7 +132,7 @@
android:layout_height="match_parent"
android:background="@null">
<io.github.armcha.autolink.AutoLinkTextView
<awais.instagrabber.customviews.RamboTextViewV2
android:id="@+id/caption"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -253,12 +253,32 @@
app:iconSize="16dp"
app:iconTint="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/player_controls_toggle"
app:layout_constraintEnd_toStartOf="@id/comment"
app:layout_constraintStart_toEndOf="@id/caption_toggle"
app:layout_constraintTop_toTopOf="@id/caption_toggle"
app:rippleColor="@color/grey_300"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/comment"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/comment"
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
android:textColor="@color/white"
android:visibility="visible"
app:icon="@drawable/ic_outline_comments_24"
app:iconGravity="top"
app:iconSize="16dp"
app:iconTint="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/player_controls_toggle"
app:layout_constraintStart_toEndOf="@id/like"
app:layout_constraintTop_toTopOf="@id/caption_toggle"
app:rippleColor="@color/grey_300"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/player_controls_toggle"
style="@style/Widget.MaterialComponents.Button.TextButton"
@ -274,7 +294,7 @@
app:iconTint="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/save"
app:layout_constraintStart_toEndOf="@id/like"
app:layout_constraintStart_toEndOf="@id/comment"
app:layout_constraintTop_toTopOf="@id/caption_toggle"
app:rippleColor="@color/grey_300"
tools:visibility="visible" />

6
app/src/main/res/layout/fragment_comments.xml

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
@ -17,13 +17,11 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
app:layoutManager="LinearLayoutManager"
tools:listitem="@layout/item_comment" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/commentField"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
@ -45,4 +43,4 @@
android:scrollHorizontally="false" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</androidx.appcompat.widget.LinearLayoutCompat>

10
app/src/main/res/layout/fragment_feed.xml

@ -38,9 +38,9 @@
tools:listitem="@layout/item_feed_photo" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/frag_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<!--<androidx.fragment.app.FragmentContainerView-->
<!-- android:id="@+id/frag_container"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="match_parent"-->
<!-- app:layout_behavior="@string/appbar_scrolling_view_behavior" />-->
</awais.instagrabber.customviews.helpers.NestedCoordinatorLayout>

211
app/src/main/res/layout/item_comment.xml

@ -1,118 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:viewBindingIgnore="true">
android:background="?android:selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:orientation="horizontal"
android:padding="8dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:orientation="horizontal">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/ivProfilePic"
android:layout_width="@dimen/simple_item_picture_size"
android:layout_height="@dimen/simple_item_picture_size"
android:padding="4dp"
app:actualImageScaleType="centerCrop"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:roundAsCircle="true" />
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/ivProfilePic"
android:layout_width="@dimen/simple_item_picture_size"
android:layout_height="@dimen/simple_item_picture_size"
android:padding="4dp"
app:actualImageScaleType="centerCrop"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:roundAsCircle="true"
tools:background="@mipmap/ic_launcher" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvUsername"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:paddingStart="4dp"
android:paddingTop="2dp"
android:paddingEnd="4dp"
android:paddingBottom="2dp"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="?android:textColorPrimary"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@id/tvComment"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/ivProfilePic"
app:layout_constraintTop_toTopOf="parent"
tools:text="username" />
<awais.instagrabber.customviews.RamboTextView
android:id="@+id/tvComment"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:autoLink="web|email"
android:ellipsize="end"
android:linksClickable="true"
android:paddingStart="4dp"
android:paddingTop="2dp"
android:paddingEnd="4dp"
android:paddingBottom="2dp"
android:textAppearance="@style/TextAppearance.AppCompat"
app:layout_constraintBottom_toTopOf="@id/tvLikes"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@id/ivProfilePic"
app:layout_constraintTop_toBottomOf="@id/tvUsername"
tools:text="comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment " />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvUsername"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:paddingStart="4dp"
android:paddingTop="2dp"
android:paddingEnd="4dp"
android:paddingBottom="2dp"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="?android:textColorPrimary"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@id/tvComment"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/ivProfilePic"
app:layout_constraintTop_toTopOf="parent"
tools:text="username" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvLikes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_vertical"
android:paddingStart="4dp"
android:paddingTop="2dp"
android:paddingEnd="4dp"
android:paddingBottom="2dp"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/tvDate"
app:layout_constraintStart_toEndOf="@id/ivProfilePic"
app:layout_constraintTop_toBottomOf="@id/tvComment"
tools:text="likes" />
<awais.instagrabber.customviews.RamboTextViewV2
android:id="@+id/tvComment"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:autoLink="web|email"
android:ellipsize="end"
android:linksClickable="true"
android:paddingStart="4dp"
android:paddingTop="2dp"
android:paddingEnd="4dp"
android:paddingBottom="2dp"
android:textAppearance="@style/TextAppearance.AppCompat"
app:layout_constraintBottom_toTopOf="@id/tvLikes"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@id/ivProfilePic"
app:layout_constraintTop_toBottomOf="@id/tvUsername"
tools:text="comment comment comment comment comment comment comment comment comment comment comment comment
comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvDate"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:gravity="end"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
android:textStyle="italic"
app:layout_constraintBaseline_toBaselineOf="@id/tvLikes"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/tvLikes"
app:layout_constraintTop_toBottomOf="@id/tvComment"
tools:text="long date................................" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvLikes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_vertical"
android:paddingStart="4dp"
android:paddingTop="2dp"
android:paddingEnd="4dp"
android:paddingBottom="2dp"
android:scrollbars="none"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/tvDate"
app:layout_constraintStart_toEndOf="@id/ivProfilePic"
app:layout_constraintTop_toBottomOf="@id/tvComment"
tools:text="likes" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvChildComments"
android:layout_width="match_parent"
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvDate"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="40dp"
android:layout_marginLeft="40dp"
app:layoutManager="LinearLayoutManager"
tools:itemCount="5"
tools:listitem="@layout/item_comment_small" />
android:ellipsize="marquee"
android:gravity="end"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
android:textStyle="italic"
app:layout_constraintBaseline_toBaselineOf="@id/tvLikes"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/tvLikes"
app:layout_constraintTop_toBottomOf="@id/tvComment"
tools:text="long date................................" />
</androidx.constraintlayout.widget.ConstraintLayout>
<!--<androidx.recyclerview.widget.RecyclerView-->
<!-- android:id="@+id/rvChildComments"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_marginStart="40dp"-->
<!-- android:layout_marginLeft="40dp"-->
<!-- app:layoutManager="LinearLayoutManager"-->
<!-- tools:itemCount="5"-->
<!-- tools:listitem="@layout/item_comment_small" />-->
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_gravity="bottom"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:layout_marginBottom="4dp"
android:background="#32888888" />
</LinearLayout>
<!--<View-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="1dp"-->
<!-- android:layout_gravity="bottom"-->
<!-- android:layout_marginStart="4dp"-->
<!-- android:layout_marginEnd="4dp"-->
<!-- android:layout_marginBottom="4dp"-->
<!-- android:background="#32888888" />-->

15
app/src/main/res/layout/item_comment_small.xml

@ -5,6 +5,9 @@
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="40dp"
android:paddingTop="8dp"
android:paddingEnd="8dp"
android:paddingBottom="2dp">
<com.facebook.drawee.view.SimpleDraweeView
@ -15,7 +18,8 @@
app:layout_constraintEnd_toStartOf="@id/tvUsername"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:roundAsCircle="true" />
app:roundAsCircle="true"
tools:background="@mipmap/ic_launcher" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvUsername"
@ -36,7 +40,7 @@
app:layout_constraintTop_toTopOf="parent"
tools:text="username" />
<awais.instagrabber.customviews.RamboTextView
<awais.instagrabber.customviews.RamboTextViewV2
android:id="@+id/tvComment"
android:layout_width="0dp"
android:layout_height="wrap_content"
@ -55,17 +59,20 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/ivProfilePic"
app:layout_constraintTop_toBottomOf="@id/tvUsername"
tools:text="comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment " />
tools:text="comment comment comment comment comment comment comment comment
comment comment comment comment comment comment comment comment comment comment comment comment comment comment
comment comment comment comment comment comment " />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvLikes"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:singleLine="true"
android:paddingStart="4dp"
android:paddingTop="2dp"
android:paddingEnd="4dp"
android:paddingBottom="2dp"
android:scrollbars="none"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/tvDate"

92
app/src/main/res/layout/item_feed_bottom.xml

@ -37,29 +37,13 @@
tools:text="690000" />
</LinearLayout>
<LinearLayout
android:id="@+id/videoViewsContainer"
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvVideoViews"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal"
android:visibility="gone">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4dp"
app:srcCompat="@drawable/ic_outline_views_24"
app:tint="?android:textColorPrimary" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvVideoViews"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:textAppearance="?attr/textAppearanceButton"
tools:text="690000" />
</LinearLayout>
android:gravity="center_vertical"
android:textAppearance="?attr/textAppearanceCaption"
tools:text="690000 views" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvPostDate"
@ -70,17 +54,18 @@
android:maxLines="1"
android:padding="8dp"
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
tools:text="690000" />
tools:text="2020-01-01 12:00:00" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/btnMute"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="?selectableItemBackgroundBorderless"
android:padding="4dp"
android:visibility="gone"
app:srcCompat="@drawable/ic_volume_up_24"
app:tint="?android:textColorPrimary" />
<!--<androidx.appcompat.widget.AppCompatImageView-->
<!-- android:id="@+id/btnMute"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="match_parent"-->
<!-- android:background="?selectableItemBackgroundBorderless"-->
<!-- android:padding="4dp"-->
<!-- android:visibility="gone"-->
<!-- app:srcCompat="@drawable/ic_volume_up_24"-->
<!-- app:tint="?android:textColorPrimary"-->
<!-- tools:visibility="visible" />-->
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/btnDownload"
@ -92,34 +77,21 @@
app:tint="?android:textColorPrimary" />
</LinearLayout>
<ScrollView
<awais.instagrabber.customviews.RamboTextViewV2
android:id="@+id/viewerCaption"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:isScrollContainer="true"
android:scrollbars="vertical"
android:scrollHorizontally="false">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:clickable="true"
android:focusable="true">
<awais.instagrabber.customviews.RamboTextView
android:id="@+id/viewerCaption"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:selectableItemBackground"
android:clipToPadding="false"
android:paddingStart="8dp"
android:paddingLeft="8dp"
android:paddingEnd="8dp"
android:paddingRight="8dp"
android:paddingBottom="8dp"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
tools:text="BOTTOM TEXT" />
</FrameLayout>
</ScrollView>
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:clickable="true"
android:clipToPadding="false"
android:ellipsize="end"
android:focusable="true"
android:maxLines="5"
android:paddingStart="8dp"
android:paddingLeft="8dp"
android:paddingEnd="8dp"
android:paddingRight="8dp"
android:paddingBottom="8dp"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
tools:text="Bottom text with hashtags etc." />
</LinearLayout>

6
app/src/main/res/layout/item_feed_photo.xml

@ -7,8 +7,7 @@
<include
android:id="@+id/item_feed_top"
layout="@layout/item_feed_top"
android:visibility="gone" />
layout="@layout/item_feed_top" />
<awais.instagrabber.customviews.drawee.ZoomableDraweeView
android:id="@+id/imageViewer"
@ -22,6 +21,5 @@
<include
android:id="@+id/item_feed_bottom"
layout="@layout/item_feed_bottom"
android:visibility="gone" />
layout="@layout/item_feed_bottom" />
</LinearLayout>

6
app/src/main/res/layout/item_feed_slider.xml

@ -8,8 +8,7 @@
<include
android:id="@+id/item_feed_top"
layout="@layout/item_feed_top"
android:visibility="gone" />
layout="@layout/item_feed_top" />
<FrameLayout
android:layout_width="match_parent"
@ -42,6 +41,5 @@
<include
android:id="@+id/item_feed_bottom"
layout="@layout/item_feed_bottom"
android:visibility="gone" />
layout="@layout/item_feed_bottom" />
</LinearLayout>

20
app/src/main/res/layout/item_feed_top.xml

@ -30,7 +30,7 @@
android:paddingRight="8dp"
android:weightSum="2">
<awais.instagrabber.customviews.RamboTextView
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -38,7 +38,7 @@
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
tools:text="username" />
<awais.instagrabber.customviews.RamboTextView
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/location"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -50,12 +50,12 @@
tools:text="location" />
</RelativeLayout>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/viewStoryPost"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
android:background="?selectableItemBackgroundBorderless"
app:srcCompat="@drawable/ic_open_in_new_24"
app:tint="?android:textColorPrimary" />
<!--<androidx.appcompat.widget.AppCompatImageView-->
<!-- android:id="@+id/viewStoryPost"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="match_parent"-->
<!-- android:layout_gravity="center"-->
<!-- android:background="?selectableItemBackgroundBorderless"-->
<!-- app:srcCompat="@drawable/ic_open_in_new_24"-->
<!-- app:tint="?android:textColorPrimary" />-->
</LinearLayout>

6
app/src/main/res/layout/item_feed_video.xml

@ -7,8 +7,7 @@
<include
android:id="@+id/item_feed_top"
layout="@layout/item_feed_top"
android:visibility="gone" />
layout="@layout/item_feed_top" />
<include
android:id="@+id/video_post"
@ -16,6 +15,5 @@
<include
android:id="@+id/item_feed_bottom"
layout="@layout/item_feed_bottom"
android:visibility="gone" />
layout="@layout/item_feed_bottom" />
</LinearLayout>

2
app/src/main/res/layout/layout_video_player_with_thumbnail.xml

@ -15,7 +15,7 @@
android:id="@+id/thumbnail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:actualImageScaleType="fitCenter"
app:actualImageScaleType="centerCrop"
app:viewAspectRatio="1" />
<androidx.appcompat.widget.AppCompatImageView

4
app/src/main/res/navigation/comments_nav_graph.xml

@ -27,7 +27,7 @@
app:nullable="true" />
</action>
<fragment
<dialog
android:id="@+id/commentsViewerFragment"
android:name="awais.instagrabber.fragments.CommentsViewerFragment"
android:label="Comments"
@ -44,7 +44,7 @@
android:name="postUserId"
app:argType="string"
app:nullable="false" />
</fragment>
</dialog>
<action
android:id="@+id/action_global_commentsViewerFragment"

2
app/src/main/res/values/color.xml

@ -30,6 +30,8 @@
<color name="dm_profile_button_color">#efefef</color>
<color name="comment_selected">#888888</color>
<color name="white">#FFFFFF</color>
<color name="black">#000000</color>

3
app/src/main/res/values/strings.xml

@ -9,8 +9,6 @@
<string name="action_fdroid" translatable="false">F-Droid</string>
<string name="action_search">Search username…</string>
<string name="action_compare">Compare</string>
<string name="single_like">like</string>
<string name="multiple_likes">likes</string>
<string name="clipboard_error">Error copying text</string>
<string name="clipboard_copied">Copied to clipboard!</string>
<string name="report">Report</string>
@ -325,6 +323,7 @@
<string name="downloading">Downloading…</string>
<string name="downloader_downloading_child">Download item %d of %d</string>
<string name="delete">Delete</string>
<string name="comment">Comment</string>
<plurals name="likes_count">
<item quantity="one">%d like</item>
<item quantity="other">%d likes</item>

Loading…
Cancel
Save