1
0
mirror of https://github.com/KokaKiwi/BarInsta synced 2024-12-23 13:26:59 +00:00

More updated. Handle clicks. Updated comments viewer, etc

This commit is contained in:
Ammar Githam 2020-10-24 18:10:21 +09:00
parent 6bf59e83ad
commit 9b83c5e832
48 changed files with 1861 additions and 1630 deletions

6
.idea/compiler.xml Normal file
View File

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

View File

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

View File

@ -40,7 +40,7 @@
</value> </value>
</option> </option>
</component> </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" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">

View File

@ -1,11 +1,12 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="app" type="AndroidRunConfigurationType" factoryName="Android App" activateToolWindowBeforeRun="false"> <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" value="true" />
<option name="DEPLOY_APK_FROM_BUNDLE" value="false" /> <option name="DEPLOY_APK_FROM_BUNDLE" value="false" />
<option name="DEPLOY_AS_INSTANT" value="false" /> <option name="DEPLOY_AS_INSTANT" value="false" />
<option name="ARTIFACT_NAME" value="" /> <option name="ARTIFACT_NAME" value="" />
<option name="PM_INSTALL_OPTIONS" value="" /> <option name="PM_INSTALL_OPTIONS" value="" />
<option name="ALL_USERS" value="false" />
<option name="DYNAMIC_FEATURES_DISABLED_LIST" value="" /> <option name="DYNAMIC_FEATURES_DISABLED_LIST" value="" />
<option name="ACTIVITY_EXTRA_FLAGS" value="" /> <option name="ACTIVITY_EXTRA_FLAGS" value="" />
<option name="MODE" value="default_activity" /> <option name="MODE" value="default_activity" />
@ -41,11 +42,16 @@
</Native> </Native>
<Profilers> <Profilers>
<option name="ADVANCED_PROFILING_ENABLED" value="false" /> <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_ENABLED" value="false" />
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Sample Java Methods" /> <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> </Profilers>
<option name="DEEP_LINK" value="" /> <option name="DEEP_LINK" value="" />
<option name="ACTIVITY_CLASS" value="awais.instagrabber.activities.MainActivity" /> <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"> <method v="2">
<option name="Android.Gradle.BeforeRunTask" enabled="true" /> <option name="Android.Gradle.BeforeRunTask" enabled="true" />
</method> </method>

View File

@ -39,6 +39,12 @@ android {
} }
} }
configurations.all {
resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
}
dependencies { dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10' 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.google.guava:guava:27.0.1-android'
// implementation 'com.github.hendrawd:StorageUtil:1.1.0' // 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 'org.jsoup:jsoup:1.13.1'
implementation 'com.facebook.fresco:fresco:2.3.0' implementation 'com.facebook.fresco:fresco:2.3.0'

View File

@ -2,137 +2,189 @@ package awais.instagrabber.adapters;
import android.content.Context; import android.content.Context;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Filter;
import android.widget.Filterable;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import awais.instagrabber.R; import awais.instagrabber.adapters.viewholder.comments.ChildCommentViewHolder;
import awais.instagrabber.adapters.viewholder.CommentViewHolder; import awais.instagrabber.adapters.viewholder.comments.ParentCommentViewHolder;
import awais.instagrabber.interfaces.MentionClickListener; import awais.instagrabber.databinding.ItemCommentBinding;
import awais.instagrabber.databinding.ItemCommentSmallBinding;
import awais.instagrabber.models.CommentModel; 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 { public final class CommentsAdapter extends ListAdapter<CommentModel, RecyclerView.ViewHolder> {
private static final int TYPE_PARENT = 1;
private static final int TYPE_CHILD = 2;
private CommentModel[] filteredCommentModels; private final Map<Integer, Integer> positionTypeMap = new HashMap<>();
private LayoutInflater layoutInflater;
private final boolean isParent; // private final Filter filter = new Filter() {
private final View.OnClickListener onClickListener; // @NonNull
private final MentionClickListener mentionClickListener; // @Override
private final CommentModel[] commentModels; // protected FilterResults performFiltering(final CharSequence filter) {
private final String[] quantityStrings = new String[2]; // final FilterResults results = new FilterResults();
private final Filter filter = new Filter() { // results.values = commentModels;
@NonNull //
// 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 @Override
protected FilterResults performFiltering(final CharSequence filter) { public boolean areItemsTheSame(@NonNull final CommentModel oldItem, @NonNull final CommentModel newItem) {
final FilterResults results = new FilterResults(); return oldItem.getId().equals(newItem.getId());
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;
} }
@Override @Override
protected void publishResults(final CharSequence constraint, @NonNull final FilterResults results) { public boolean areContentsTheSame(@NonNull final CommentModel oldItem, @NonNull final CommentModel newItem) {
if (results.values instanceof CommentModel[]) { return oldItem.getId().equals(newItem.getId());
filteredCommentModels = (CommentModel[]) results.values;
notifyDataSetChanged();
}
} }
}; };
private final CommentCallback commentCallback;
private CommentModel selected;
private int selectedIndex;
public CommentsAdapter(final CommentModel[] commentModels, public CommentsAdapter(final CommentCallback commentCallback) {
final boolean isParent, super(DIFF_CALLBACK);
final View.OnClickListener onClickListener, this.commentCallback = commentCallback;
final MentionClickListener mentionClickListener) {
super();
this.commentModels = this.filteredCommentModels = commentModels;
this.isParent = isParent;
this.onClickListener = onClickListener;
this.mentionClickListener = mentionClickListener;
}
@Override
public Filter getFilter() {
return filter;
} }
@NonNull @NonNull
@Override @Override
public CommentViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) { public RecyclerView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) {
final Context context = parent.getContext(); final Context context = parent.getContext();
if (quantityStrings[0] == null) quantityStrings[0] = context.getString(R.string.single_like); final LayoutInflater layoutInflater = LayoutInflater.from(context);
if (quantityStrings[1] == null) quantityStrings[1] = context.getString(R.string.multiple_likes); if (type == TYPE_PARENT) {
if (layoutInflater == null) layoutInflater = LayoutInflater.from(context); final ItemCommentBinding binding = ItemCommentBinding.inflate(layoutInflater, parent, false);
final View view = layoutInflater.inflate(isParent ? R.layout.item_comment return new ParentCommentViewHolder(binding);
: R.layout.item_comment_small, }
parent, final ItemCommentSmallBinding binding = ItemCommentSmallBinding.inflate(layoutInflater, parent, false);
false); return new ChildCommentViewHolder(binding);
return new CommentViewHolder(view,
onClickListener,
mentionClickListener);
} }
@Override @Override
public void onBindViewHolder(@NonNull final CommentViewHolder holder, final int position) { public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {
final CommentModel commentModel = filteredCommentModels[position]; final CommentModel commentModel = getItem(position);
if (commentModel != null) { if (commentModel == null) return;
holder.setCommentModel(commentModel); final int type = getItemViewType(position);
final boolean selected = this.selected != null && this.selected.getId().equals(commentModel.getId());
holder.setComment(commentModel.getText()); if (type == TYPE_PARENT) {
holder.setDate(commentModel.getDateTime()); final ParentCommentViewHolder viewHolder = (ParentCommentViewHolder) holder;
holder.setLiked(commentModel.getLiked()); viewHolder.bind(commentModel, selected, commentCallback);
return;
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();
}
} }
final ChildCommentViewHolder viewHolder = (ChildCommentViewHolder) holder;
viewHolder.bind(commentModel, selected, commentCallback);
} }
@Override @Override
public int getItemCount() { public void submitList(@Nullable final List<CommentModel> list) {
return filteredCommentModels == null ? 0 : filteredCommentModels.length; final List<CommentModel> flatList = flattenList(list);
super.submitList(flatList);
}
@Override
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 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);
} }
} }

View File

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

View File

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

View File

@ -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) {}
}

View File

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

View File

@ -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);
}
}

View File

@ -1,11 +1,8 @@
package awais.instagrabber.adapters.viewholder; package awais.instagrabber.adapters.viewholder;
import android.graphics.drawable.Animatable;
import android.net.Uri; import android.net.Uri;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import androidx.annotation.DimenRes; import androidx.annotation.DimenRes;
import androidx.annotation.NonNull; 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.Fresco;
import com.facebook.drawee.backends.pipeline.PipelineDraweeControllerBuilder; 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.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder; import com.facebook.imagepipeline.request.ImageRequestBuilder;
@ -42,10 +37,9 @@ public class FeedGridItemViewHolder extends RecyclerView.ViewHolder {
public void bind(@NonNull final FeedModel feedModel, public void bind(@NonNull final FeedModel feedModel,
@NonNull final PostsLayoutPreferences layoutPreferences, @NonNull final PostsLayoutPreferences layoutPreferences,
final FeedAdapterV2.OnPostClickListener postClickListener, final FeedAdapterV2.FeedItemCallback feedItemCallback) {
final boolean animate) { if (feedItemCallback != null) {
if (postClickListener != null) { itemView.setOnClickListener(v -> feedItemCallback.onPostClick(feedModel, binding.profilePic, binding.postImage));
itemView.setOnClickListener(v -> postClickListener.onPostClick(feedModel, binding.profilePic, binding.postImage));
} }
itemView.setClipToOutline(layoutPreferences.getHasRoundedCorners()); itemView.setClipToOutline(layoutPreferences.getHasRoundedCorners());
if (layoutPreferences.getType() == STAGGERED_GRID) { if (layoutPreferences.getType() == STAGGERED_GRID) {
@ -123,25 +117,6 @@ public class FeedGridItemViewHolder extends RecyclerView.ViewHolder {
final PipelineDraweeControllerBuilder builder = Fresco.newDraweeControllerBuilder() final PipelineDraweeControllerBuilder builder = Fresco.newDraweeControllerBuilder()
.setImageRequest(requestBuilder) .setImageRequest(requestBuilder)
.setOldController(binding.postImage.getController()); .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()); 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();
}
} }

View File

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

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -1,95 +1,107 @@
package awais.instagrabber.adapters.viewholder.feed; package awais.instagrabber.adapters.viewholder.feed;
import android.text.SpannableString; import android.text.method.LinkMovementMethod;
import android.text.Spanned; import android.transition.TransitionManager;
import android.view.View; import android.view.View;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.adapters.FeedAdapterV2; import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.customviews.CommentMentionClickSpan;
import awais.instagrabber.customviews.RamboTextView;
import awais.instagrabber.databinding.ItemFeedBottomBinding; import awais.instagrabber.databinding.ItemFeedBottomBinding;
import awais.instagrabber.databinding.ItemFeedTopBinding; import awais.instagrabber.databinding.ItemFeedTopBinding;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.FeedModel; import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.ProfileModel; import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import static android.text.TextUtils.TruncateAt.END;
public abstract class FeedItemViewHolder extends RecyclerView.ViewHolder { 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 ItemFeedTopBinding topBinding;
private final ItemFeedBottomBinding bottomBinding; private final ItemFeedBottomBinding bottomBinding;
private final MentionClickListener mentionClickListener; private final FeedAdapterV2.FeedItemCallback feedItemCallback;
public FeedItemViewHolder(@NonNull final View root, public FeedItemViewHolder(@NonNull final View root,
final ItemFeedTopBinding topBinding, final ItemFeedTopBinding topBinding,
final ItemFeedBottomBinding bottomBinding, final ItemFeedBottomBinding bottomBinding,
final MentionClickListener mentionClickListener, final FeedAdapterV2.FeedItemCallback feedItemCallback) {
final View.OnClickListener clickListener,
final View.OnLongClickListener longClickListener) {
super(root); super(root);
this.topBinding = topBinding; this.topBinding = topBinding;
this.bottomBinding = bottomBinding; this.bottomBinding = bottomBinding;
this.mentionClickListener = mentionClickListener; topBinding.title.setMovementMethod(new LinkMovementMethod());
// itemView.setOnClickListener(clickListener); this.feedItemCallback = feedItemCallback;
// 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);
} }
public void bind(final FeedModel feedModel, public void bind(final FeedModel feedModel) {
final FeedAdapterV2.OnPostClickListener postClickListener) {
if (feedModel == null) { if (feedModel == null) {
return; return;
} }
topBinding.viewStoryPost.setTag(feedModel); setupProfilePic(feedModel);
topBinding.ivProfilePic.setTag(feedModel); setupLocation(feedModel);
bottomBinding.btnDownload.setTag(feedModel);
bottomBinding.viewerCaption.setTag(feedModel);
bottomBinding.btnComments.setTag(feedModel);
final ProfileModel profileModel = feedModel.getProfileModel();
if (profileModel != null) {
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));
}
bottomBinding.tvPostDate.setText(feedModel.getPostDate()); 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(); final long commentsCount = feedModel.getCommentsCount();
bottomBinding.commentsCount.setText(String.valueOf(commentsCount)); bottomBinding.commentsCount.setText(String.valueOf(commentsCount));
bottomBinding.commentsCount.setOnClickListener(v -> feedItemCallback.onCommentsClick(feedModel));
}
final String locationName = feedModel.getLocationName(); private void setupProfilePic(final FeedModel feedModel) {
final String locationId = feedModel.getLocationId(); final ProfileModel profileModel = feedModel.getProfileModel();
setLocation(locationName, locationId); if (profileModel != null) {
CharSequence postCaption = feedModel.getPostCaption(); topBinding.ivProfilePic.setOnClickListener(v -> feedItemCallback.onProfilePicClick(feedModel, topBinding.ivProfilePic));
topBinding.ivProfilePic.setImageURI(profileModel.getSdProfilePic());
setupTitle(feedModel);
}
}
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); final boolean captionEmpty = TextUtils.isEmpty(postCaption);
bottomBinding.viewerCaption.setVisibility(captionEmpty ? View.GONE : View.VISIBLE); bottomBinding.viewerCaption.setVisibility(captionEmpty ? View.GONE : View.VISIBLE);
if (!captionEmpty) { if (captionEmpty) return;
if (TextUtils.hasMentions(postCaption)) {
postCaption = TextUtils.getMentionText(postCaption);
feedModel.setPostCaption(postCaption);
bottomBinding.viewerCaption.setText(postCaption, TextView.BufferType.SPANNABLE);
} else {
bottomBinding.viewerCaption.setText(postCaption); 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;
} }
} bottomBinding.viewerCaption.setMaxLines(MAX_LINES_COLLAPSED);
expandCollapseTextView(bottomBinding.viewerCaption, feedModel.getPostCaption()); bottomBinding.viewerCaption.setEllipsize(END);
bindItem(feedModel, postClickListener); }));
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)) { if (TextUtils.isEmpty(locationName)) {
topBinding.location.setVisibility(View.GONE); topBinding.location.setVisibility(View.GONE);
topBinding.title.setLayoutParams(new RelativeLayout.LayoutParams( topBinding.title.setLayoutParams(new RelativeLayout.LayoutParams(
@ -101,46 +113,9 @@ public abstract class FeedItemViewHolder extends RecyclerView.ViewHolder {
topBinding.title.setLayoutParams(new RelativeLayout.LayoutParams( topBinding.title.setLayoutParams(new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT 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));
} }
} }
/** public abstract void bindItem(final FeedModel 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();
}
} }

View File

@ -16,7 +16,6 @@ import com.facebook.imagepipeline.request.ImageRequestBuilder;
import awais.instagrabber.adapters.FeedAdapterV2; import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.databinding.ItemFeedPhotoBinding; import awais.instagrabber.databinding.ItemFeedPhotoBinding;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.FeedModel; import awais.instagrabber.models.FeedModel;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
@ -24,17 +23,15 @@ public class FeedPhotoViewHolder extends FeedItemViewHolder {
private static final String TAG = "FeedPhotoViewHolder"; private static final String TAG = "FeedPhotoViewHolder";
private final ItemFeedPhotoBinding binding; private final ItemFeedPhotoBinding binding;
// private final long animationDuration; private final FeedAdapterV2.FeedItemCallback feedItemCallback;
public FeedPhotoViewHolder(@NonNull final ItemFeedPhotoBinding binding, public FeedPhotoViewHolder(@NonNull final ItemFeedPhotoBinding binding,
final MentionClickListener mentionClickListener, final FeedAdapterV2.FeedItemCallback feedItemCallback) {
final View.OnClickListener clickListener, super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, feedItemCallback);
final View.OnLongClickListener longClickListener) {
super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, mentionClickListener, clickListener, longClickListener);
this.binding = binding; this.binding = binding;
// this.animationDuration = animationDuration; this.feedItemCallback = feedItemCallback;
binding.itemFeedBottom.videoViewsContainer.setVisibility(View.GONE); binding.itemFeedBottom.tvVideoViews.setVisibility(View.GONE);
binding.itemFeedBottom.btnMute.setVisibility(View.GONE); // binding.itemFeedBottom.btnMute.setVisibility(View.GONE);
binding.imageViewer.setAllowTouchInterceptionWhileZoomed(false); binding.imageViewer.setAllowTouchInterceptionWhileZoomed(false);
final GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(itemView.getContext().getResources()) final GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(itemView.getContext().getResources())
.setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER) .setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER)
@ -43,19 +40,18 @@ public class FeedPhotoViewHolder extends FeedItemViewHolder {
} }
@Override @Override
public void bindItem(final FeedModel feedModel, public void bindItem(final FeedModel feedModel) {
final FeedAdapterV2.OnPostClickListener postClickListener) {
if (feedModel == null) { if (feedModel == null) {
return; return;
} }
binding.getRoot().post(() -> {
setDimensions(feedModel); setDimensions(feedModel);
showOrHideDetails(false);
final String thumbnailUrl = feedModel.getThumbnailUrl(); final String thumbnailUrl = feedModel.getThumbnailUrl();
String url = feedModel.getDisplayUrl(); String url = feedModel.getDisplayUrl();
if (TextUtils.isEmpty(url)) url = thumbnailUrl; if (TextUtils.isEmpty(url)) url = thumbnailUrl;
final ImageRequest requestBuilder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(url)) final ImageRequest requestBuilder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(url))
.setLocalThumbnailPreviewsEnabled(true) // .setLocalThumbnailPreviewsEnabled(true)
.setProgressiveRenderingEnabled(true) // .setProgressiveRenderingEnabled(true)
.build(); .build();
binding.imageViewer.setController(Fresco.newDraweeControllerBuilder() binding.imageViewer.setController(Fresco.newDraweeControllerBuilder()
.setImageRequest(requestBuilder) .setImageRequest(requestBuilder)
@ -65,63 +61,18 @@ public class FeedPhotoViewHolder extends FeedItemViewHolder {
binding.imageViewer.setTapListener(new GestureDetector.SimpleOnGestureListener() { binding.imageViewer.setTapListener(new GestureDetector.SimpleOnGestureListener() {
@Override @Override
public boolean onSingleTapConfirmed(final MotionEvent e) { public boolean onSingleTapConfirmed(final MotionEvent e) {
if (postClickListener != null) { if (feedItemCallback != null) {
postClickListener.onPostClick(feedModel, binding.itemFeedTop.ivProfilePic, binding.imageViewer); feedItemCallback.onPostClick(feedModel, binding.itemFeedTop.ivProfilePic, binding.imageViewer);
return true; return true;
} }
return false; return false;
} }
}); });
});
} }
private void setDimensions(final FeedModel feedModel) { 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(); final float aspectRatio = (float) feedModel.getImageWidth() / feedModel.getImageHeight();
binding.imageViewer.setAspectRatio(aspectRatio); 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);
}
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
}
}

View File

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

View File

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

View File

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

View File

@ -12,69 +12,129 @@ import android.text.TextWatcher;
import android.text.style.RelativeSizeSpan; import android.text.style.RelativeSizeSpan;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.widget.LinearLayout;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SearchView; import androidx.appcompat.widget.LinearLayoutCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction; import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavDirections; import androidx.navigation.NavDirections;
import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.adapters.CommentsAdapter; import awais.instagrabber.adapters.CommentsAdapter;
import awais.instagrabber.asyncs.CommentsFetcher; import awais.instagrabber.asyncs.CommentsFetcher;
import awais.instagrabber.databinding.FragmentCommentsBinding; import awais.instagrabber.databinding.FragmentCommentsBinding;
import awais.instagrabber.dialogs.ProfilePicDialogFragment; import awais.instagrabber.dialogs.ProfilePicDialogFragment;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.CommentModel; import awais.instagrabber.models.CommentModel;
import awais.instagrabber.models.ProfileModel; import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.Utils;
import awais.instagrabber.viewmodels.CommentsViewModel;
import awais.instagrabber.webservices.MediaService; import awais.instagrabber.webservices.MediaService;
import awais.instagrabber.webservices.ServiceCallback; import awais.instagrabber.webservices.ServiceCallback;
import static android.content.Context.INPUT_METHOD_SERVICE; 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 static final String TAG = "CommentsViewerFragment";
private final String cookie = Utils.settingsHelper.getString(Constants.COOKIE); private final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);
private CommentsAdapter commentsAdapter; private CommentsAdapter commentsAdapter;
private CommentModel commentModel;
private FragmentCommentsBinding binding; private FragmentCommentsBinding binding;
private String shortCode; private String shortCode;
private String userId; private String userId;
private Resources resources; private Resources resources;
private InputMethodManager imm; private InputMethodManager imm;
private AppCompatActivity fragmentActivity; private AppCompatActivity fragmentActivity;
private LinearLayout root; private LinearLayoutCompat root;
private boolean shouldRefresh = true; private boolean shouldRefresh = true;
private MediaService mediaService; private MediaService mediaService;
private String postId; 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 @Override
public void onCreate(@Nullable final Bundle savedInstanceState) { public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
fragmentActivity = (AppCompatActivity) getActivity(); fragmentActivity = (AppCompatActivity) getActivity();
mediaService = MediaService.getInstance(); mediaService = MediaService.getInstance();
setHasOptionsMenu(true); // setHasOptionsMenu(true);
} }
@NonNull @NonNull
@ -85,6 +145,8 @@ public final class CommentsViewerFragment extends Fragment implements SwipeRefre
return root; return root;
} }
binding = FragmentCommentsBinding.inflate(getLayoutInflater()); binding = FragmentCommentsBinding.inflate(getLayoutInflater());
binding.swipeRefreshLayout.setEnabled(false);
binding.swipeRefreshLayout.setNestedScrollingEnabled(false);
root = binding.getRoot(); root = binding.getRoot();
return root; return root;
} }
@ -96,34 +158,33 @@ public final class CommentsViewerFragment extends Fragment implements SwipeRefre
shouldRefresh = false; shouldRefresh = false;
} }
@Override // @Override
public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) { // public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
inflater.inflate(R.menu.follow, menu); // inflater.inflate(R.menu.follow, menu);
menu.findItem(R.id.action_compare).setVisible(false); // menu.findItem(R.id.action_compare).setVisible(false);
final MenuItem menuSearch = menu.findItem(R.id.action_search); // final MenuItem menuSearch = menu.findItem(R.id.action_search);
final SearchView searchView = (SearchView) menuSearch.getActionView(); // final SearchView searchView = (SearchView) menuSearch.getActionView();
searchView.setQueryHint(getResources().getString(R.string.action_search)); // searchView.setQueryHint(getResources().getString(R.string.action_search));
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { // searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override // @Override
public boolean onQueryTextSubmit(final String query) { // public boolean onQueryTextSubmit(final String query) {
return false; // return false;
} // }
//
@Override // @Override
public boolean onQueryTextChange(final String query) { // public boolean onQueryTextChange(final String query) {
if (commentsAdapter != null) commentsAdapter.getFilter().filter(query); // // if (commentsAdapter != null) commentsAdapter.getFilter().filter(query);
return true; // return true;
} // }
}); // });
} // }
@Override @Override
public void onRefresh() { public void onRefresh() {
binding.swipeRefreshLayout.setRefreshing(true); binding.swipeRefreshLayout.setRefreshing(true);
new CommentsFetcher(shortCode, commentModels -> { new CommentsFetcher(shortCode, commentModels -> {
commentsViewModel.getList().postValue(commentModels);
binding.swipeRefreshLayout.setRefreshing(false); binding.swipeRefreshLayout.setRefreshing(false);
commentsAdapter = new CommentsAdapter(commentModels, true, clickListener, mentionClickListener);
binding.rvComments.setAdapter(commentsAdapter);
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
@ -133,9 +194,14 @@ public final class CommentsViewerFragment extends Fragment implements SwipeRefre
shortCode = fragmentArgs.getShortCode(); shortCode = fragmentArgs.getShortCode();
postId = fragmentArgs.getPostId(); postId = fragmentArgs.getPostId();
userId = fragmentArgs.getPostUserId(); userId = fragmentArgs.getPostUserId();
setTitle(); // setTitle();
binding.swipeRefreshLayout.setOnRefreshListener(this); binding.swipeRefreshLayout.setOnRefreshListener(this);
binding.swipeRefreshLayout.setRefreshing(true); 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(); resources = getResources();
if (!TextUtils.isEmpty(cookie)) { if (!TextUtils.isEmpty(cookie)) {
binding.commentField.setStartIconVisible(false); binding.commentField.setStartIconVisible(false);
@ -155,40 +221,68 @@ public final class CommentsViewerFragment extends Fragment implements SwipeRefre
public void afterTextChanged(final Editable s) {} public void afterTextChanged(final Editable s) {}
}); });
binding.commentField.setStartIconOnClickListener(v -> { binding.commentField.setStartIconOnClickListener(v -> {
if (commentModel != null) { commentsAdapter.clearSelection();
final View focus = binding.rvComments.findViewWithTag(commentModel);
focus.setBackgroundColor(0x00000000);
commentModel = null;
}
binding.commentText.setText(""); binding.commentText.setText("");
}); });
binding.commentField.setEndIconOnClickListener(newCommentListener); binding.commentField.setEndIconOnClickListener(newCommentListener);
} }
new CommentsFetcher(this.shortCode, commentModels -> { onRefresh();
commentsAdapter = new CommentsAdapter(commentModels, true, clickListener, mentionClickListener);
binding.rvComments.setAdapter(commentsAdapter);
binding.swipeRefreshLayout.setRefreshing(false);
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
private void setTitle() { // private void setTitle() {
final ActionBar actionBar = fragmentActivity.getSupportActionBar(); // final ActionBar actionBar = fragmentActivity.getSupportActionBar();
if (actionBar == null) return; // if (actionBar == null) return;
actionBar.setTitle(R.string.title_comments); // actionBar.setTitle(R.string.title_comments);
// actionBar.setSubtitle(shortCode); // actionBar.setSubtitle(shortCode);
} // }
final DialogInterface.OnClickListener profileDialogListener = (dialog, which) -> { 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);
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(); final Context context = getContext();
if (context == null) return; if (context == null) return;
if (commentModel == null) { final DialogInterface.OnClickListener profileDialogListener = (dialog, which) -> {
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
return;
}
final ProfileModel profileModel = commentModel.getProfileModel(); final ProfileModel profileModel = commentModel.getProfileModel();
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
switch (which) { switch (which) {
case 0: // open profile case 0: // open profile
openProfile(profileModel.getUsername()); openProfile("@" + profileModel.getUsername());
break; break;
case 1: // view profile pic case 1: // view profile pic
final FragmentManager fragmentManager = getParentFragmentManager(); final FragmentManager fragmentManager = getParentFragmentManager();
@ -203,31 +297,31 @@ public final class CommentsViewerFragment extends Fragment implements SwipeRefre
case 2: // copy username case 2: // copy username
Utils.copyText(context, profileModel.getUsername()); Utils.copyText(context, profileModel.getUsername());
break; break;
case 3: // copy comment // case 3: // copy comment
Utils.copyText(context, commentModel.getText().toString()); // Utils.copyText(context, commentModel.getText().toString());
break; // break;
case 4: // reply to comment case 3: // reply to comment
final View focus = binding.rvComments.findViewWithTag(commentModel); // final View focus = binding.rvComments.findViewWithTag(commentModel);
focus.setBackgroundColor(0x80888888); // focus.setBackgroundColor(0x80888888);
commentsAdapter.setSelected(commentModel);
String mention = "@" + profileModel.getUsername() + " "; String mention = "@" + profileModel.getUsername() + " ";
binding.commentText.setText(mention); binding.commentText.setText(mention);
binding.commentText.requestFocus(); binding.commentText.requestFocus();
binding.commentText.setSelection(mention.length()); binding.commentText.setSelection(mention.length());
binding.commentText.postDelayed(new Runnable() { binding.commentText.postDelayed(() -> {
@Override
public void run() {
imm = (InputMethodManager) context.getSystemService(INPUT_METHOD_SERVICE); imm = (InputMethodManager) context.getSystemService(INPUT_METHOD_SERVICE);
if (imm == null) return; if (imm == null) return;
imm.showSoftInput(binding.commentText, 0); imm.showSoftInput(binding.commentText, 0);
}
}, 200); }, 200);
break; break;
case 5: // like/unlike comment case 4: // like/unlike comment
if (csrfToken == null) {
return;
}
if (!commentModel.getLiked()) { if (!commentModel.getLiked()) {
mediaService.commentLike(commentModel.getId(), CookieUtils.getCsrfTokenFromCookie(cookie), new ServiceCallback<Boolean>() { mediaService.commentLike(commentModel.getId(), csrfToken, new ServiceCallback<Boolean>() {
@Override @Override
public void onSuccess(final Boolean result) { public void onSuccess(final Boolean result) {
commentModel = null;
if (!result) { if (!result) {
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
return; return;
@ -243,10 +337,9 @@ public final class CommentsViewerFragment extends Fragment implements SwipeRefre
}); });
return; return;
} }
mediaService.commentUnlike(commentModel.getId(), CookieUtils.getCsrfTokenFromCookie(cookie), new ServiceCallback<Boolean>() { mediaService.commentUnlike(commentModel.getId(), csrfToken, new ServiceCallback<Boolean>() {
@Override @Override
public void onSuccess(final Boolean result) { public void onSuccess(final Boolean result) {
commentModel = null;
if (!result) { if (!result) {
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
return; return;
@ -261,15 +354,14 @@ public final class CommentsViewerFragment extends Fragment implements SwipeRefre
} }
}); });
break; break;
case 6: // delete comment case 5: // delete comment
final String userId = CookieUtils.getUserIdFromCookie(cookie); final String userId = CookieUtils.getUserIdFromCookie(cookie);
if (userId == null) return; if (userId == null) return;
mediaService.deleteComment( mediaService.deleteComment(
postId, userId, commentModel.getId(), CookieUtils.getCsrfTokenFromCookie(cookie), postId, userId, commentModel.getId(), csrfToken,
new ServiceCallback<Boolean>() { new ServiceCallback<Boolean>() {
@Override @Override
public void onSuccess(final Boolean result) { public void onSuccess(final Boolean result) {
commentModel = null;
if (!result) { if (!result) {
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
return; return;
@ -286,105 +378,15 @@ public final class CommentsViewerFragment extends Fragment implements SwipeRefre
break; 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) new AlertDialog.Builder(context)
.setTitle(title) .setTitle(title)
.setItems(commentDialogList, profileDialogListener) .setItems(commentDialogList, profileDialogListener)
.setNegativeButton(R.string.cancel, null) .setNegativeButton(R.string.cancel, null)
.show(); .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);
};
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 during comment", t);
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
};
private void openProfile(final String username) { private void openProfile(final String username) {
final NavDirections action = CommentsViewerFragmentDirections.actionGlobalProfileFragment("@" + username); final NavDirections action = CommentsViewerFragmentDirections.actionGlobalProfileFragment(username);
NavHostFragment.findNavController(this).navigate(action); NavHostFragment.findNavController(this).navigate(action);
} }
} }

View File

@ -1,7 +1,6 @@
package awais.instagrabber.fragments; package awais.instagrabber.fragments;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
@ -22,7 +21,6 @@ import androidx.activity.OnBackPressedDispatcher;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavDirections; import androidx.navigation.NavDirections;
@ -73,6 +71,8 @@ import static awais.instagrabber.utils.Utils.settingsHelper;
public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener { public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
private static final String TAG = "HashTagFragment"; private static final String TAG = "HashTagFragment";
public static final String ARG_HASHTAG = "hashtag";
private MainActivity fragmentActivity; private MainActivity fragmentActivity;
private FragmentHashtagBinding binding; private FragmentHashtagBinding binding;
private NestedCoordinatorLayout root; private NestedCoordinatorLayout root;

View File

@ -76,12 +76,9 @@ import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.Utils;
import awais.instagrabber.webservices.MediaService; import awais.instagrabber.webservices.MediaService;
import awais.instagrabber.webservices.ServiceCallback; 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 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.DownloadUtils.WRITE_PERMISSION;
import static awais.instagrabber.utils.Utils.settingsHelper; 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 String COOKIE = settingsHelper.getString(Constants.COOKIE);
private static final int DETAILS_HIDE_DELAY_MILLIS = 2000; private static final int DETAILS_HIDE_DELAY_MILLIS = 2000;
private static final String ARG_FEED_MODEL = "feedModel"; 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 static final int STORAGE_PERM_REQUEST_CODE = 8020;
private FeedModel feedModel; private FeedModel feedModel;
@ -105,6 +103,8 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
private SliderItemsAdapter sliderItemsAdapter; private SliderItemsAdapter sliderItemsAdapter;
private boolean wasControlsVisible; private boolean wasControlsVisible;
private boolean wasPaused; private boolean wasPaused;
private int captionState = BottomSheetBehavior.STATE_HIDDEN;
private int sliderPosition;
private final VerticalDragHelper.OnVerticalDragListener onVerticalDragListener = new VerticalDragHelper.OnVerticalDragListener() { private final VerticalDragHelper.OnVerticalDragListener onVerticalDragListener = new VerticalDragHelper.OnVerticalDragListener() {
@ -151,6 +151,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
private final FeedModel feedModel; private final FeedModel feedModel;
private View profilePicElement; private View profilePicElement;
private View mainPostElement; private View mainPostElement;
private int position;
public Builder setSharedProfilePicElement(final View profilePicElement) { public Builder setSharedProfilePicElement(final View profilePicElement) {
this.profilePicElement = profilePicElement; this.profilePicElement = profilePicElement;
@ -162,8 +163,13 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
return this; return this;
} }
public Builder setPosition(final int position) {
this.position = position;
return this;
}
public PostViewV2Fragment build() { public PostViewV2Fragment build() {
return PostViewV2Fragment.newInstance(feedModel, profilePicElement, mainPostElement); return PostViewV2Fragment.newInstance(feedModel, profilePicElement, mainPostElement, position);
} }
public Builder(final FeedModel feedModel) { 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 PostViewV2Fragment f = new PostViewV2Fragment(profilePicElement, mainPostElement);
final Bundle args = new Bundle(); final Bundle args = new Bundle();
args.putSerializable(ARG_FEED_MODEL, feedModel); args.putSerializable(ARG_FEED_MODEL, feedModel);
if (position >= 0) {
args.putInt(ARG_SLIDER_POSITION, position);
}
f.setArguments(args); f.setArguments(args);
return f; return f;
} }
@ -353,6 +365,9 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
return; return;
} }
feedModel = (FeedModel) feedModelSerializable; feedModel = (FeedModel) feedModelSerializable;
if (feedModel.getItemType() == MediaItemType.MEDIA_TYPE_SLIDER) {
sliderPosition = arguments.getInt(ARG_SLIDER_POSITION, 0);
}
} }
@Nullable @Nullable
@ -402,6 +417,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
public void onPause() { public void onPause() {
super.onPause(); super.onPause();
wasPaused = true; wasPaused = true;
captionState = bottomSheetBehavior.getState();
} }
@Override @Override
@ -424,7 +440,9 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
@Override @Override
public void onSaveInstanceState(@NonNull final Bundle outState) { public void onSaveInstanceState(@NonNull final Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
Log.d(TAG, "onSaveInstanceState"); if (feedModel.getItemType() == MediaItemType.MEDIA_TYPE_SLIDER) {
outState.putInt(ARG_SLIDER_POSITION, sliderPosition);
}
} }
@Override @Override
@ -505,32 +523,34 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
} }
private void init() { private void init() {
if (!wasPaused && (sharedProfilePicElement != null || sharedMainPostElement != null)) {
binding.getRoot().getBackground().mutate().setAlpha(0); binding.getRoot().getBackground().mutate().setAlpha(0);
// if (getArguments() == null) return; }
// final PostViewV2FragmentArgs fragmentArgs = PostViewV2FragmentArgs.fromBundle(getArguments());
// feedModel = fragmentArgs.getFeedModel();
setupProfilePic(); setupProfilePic();
setupTitles(); setupTitles();
setupCaption(); setupCaption();
setupCounts(); setupCounts();
setupPostTypeLayout(); setupPostTypeLayout();
setupCommonActions(); 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() { private void setupCommonActions() {
setupLike(); setupLike();
setupSave(); setupSave();
setupDownload(); 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() { private void setupDownload() {
@ -758,30 +778,26 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
private void setupCaption() { private void setupCaption() {
final CharSequence postCaption = feedModel.getPostCaption(); final CharSequence postCaption = feedModel.getPostCaption();
binding.caption.addAutoLinkMode(MODE_HASHTAG.INSTANCE, MODE_MENTION.INSTANCE, MODE_EMAIL.INSTANCE); binding.caption.addOnHashtagListener(autoLinkItem -> {
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 NavController navController = NavHostFragment.findNavController(this);
final Bundle bundle = new Bundle(); final Bundle bundle = new Bundle();
bundle.putString("hashtag", originalText); final String originalText = autoLinkItem.getOriginalText().trim();
bundle.putString(ARG_HASHTAG, originalText);
navController.navigate(R.id.action_global_hashTagFragment, bundle); 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.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 -> { binding.caption.setOnLongClickListener(v -> {
Utils.copyText(getContext(), postCaption); Utils.copyText(context, postCaption);
return true; return true;
}); });
binding.caption.setText(postCaption); binding.caption.setText(postCaption);
bottomSheetBehavior = BottomSheetBehavior.from(binding.captionParent); bottomSheetBehavior = BottomSheetBehavior.from(binding.captionParent);
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); bottomSheetBehavior.setState(captionState);
bottomSheetBehavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { bottomSheetBehavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override @Override
public void onStateChanged(@NonNull final View bottomSheet, final int newState) {} public void onStateChanged(@NonNull final View bottomSheet, final int newState) {}
@ -900,7 +916,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
if (sharedMainPostElement != null) { if (sharedMainPostElement != null) {
addSharedElement(sharedMainPostElement, binding.sliderParent); addSharedElement(sharedMainPostElement, binding.sliderParent);
} }
sliderItemsAdapter = new SliderItemsAdapter(onVerticalDragListener, binding.playerControls, new SliderCallbackAdapter() { sliderItemsAdapter = new SliderItemsAdapter(onVerticalDragListener, binding.playerControls, true, new SliderCallbackAdapter() {
@Override @Override
public void onThumbnailLoaded(final int position) { public void onThumbnailLoaded(final int position) {
if (position != 0) return; if (position != 0) return;
@ -926,6 +942,9 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
} }
}); });
binding.sliderParent.setAdapter(sliderItemsAdapter); binding.sliderParent.setAdapter(sliderItemsAdapter);
if (sliderPosition >= 0 && sliderPosition < feedModel.getSliderItems().size()) {
binding.sliderParent.setCurrentItem(sliderPosition);
}
binding.sliderParent.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { binding.sliderParent.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
int prevPosition = -1; int prevPosition = -1;
@ -947,6 +966,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
public void onPageSelected(final int position) { public void onPageSelected(final int position) {
final int size = feedModel.getSliderItems().size(); final int size = feedModel.getSliderItems().size();
if (position < 0 || position >= size) return; if (position < 0 || position >= size) return;
sliderPosition = position;
final String text = (position + 1) + "/" + size; final String text = (position + 1) + "/" + size;
binding.mediaCounter.setText(text); binding.mediaCounter.setText(text);
final PostChild postChild = feedModel.getSliderItems().get(position); final PostChild postChild = feedModel.getSliderItems().get(position);
@ -1046,6 +1066,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
vol, vol,
aspectRatio, aspectRatio,
feedModel.getThumbnailUrl(), feedModel.getThumbnailUrl(),
true,
binding.playerControls, binding.playerControls,
videoPlayerCallback); videoPlayerCallback);
} }

View File

@ -14,265 +14,159 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.content.PermissionChecker;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavController;
import androidx.navigation.NavDirections; import androidx.navigation.NavDirections;
import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import java.util.List; import java.util.List;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.activities.MainActivity; import awais.instagrabber.activities.MainActivity;
import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.adapters.FeedStoriesAdapter; import awais.instagrabber.adapters.FeedStoriesAdapter;
import awais.instagrabber.asyncs.FeedPostFetchService; import awais.instagrabber.asyncs.FeedPostFetchService;
import awais.instagrabber.customviews.helpers.VideoAwareRecyclerScroller;
import awais.instagrabber.databinding.FragmentFeedBinding; import awais.instagrabber.databinding.FragmentFeedBinding;
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment; import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
import awais.instagrabber.fragments.PostViewV2Fragment; import awais.instagrabber.fragments.PostViewV2Fragment;
import awais.instagrabber.interfaces.MentionClickListener; import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.FeedStoryModel; import awais.instagrabber.models.FeedStoryModel;
import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.models.PostsLayoutPreferences;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.DownloadUtils;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.Utils;
import awais.instagrabber.viewmodels.FeedStoriesViewModel; import awais.instagrabber.viewmodels.FeedStoriesViewModel;
import awais.instagrabber.webservices.ServiceCallback; import awais.instagrabber.webservices.ServiceCallback;
import awais.instagrabber.webservices.StoriesService; 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; import static awais.instagrabber.utils.Utils.settingsHelper;
public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener { public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
private static final String TAG = "FeedFragment"; private static final String TAG = "FeedFragment";
private static final double MAX_VIDEO_HEIGHT = 0.9 * Utils.displayMetrics.heightPixels; private static final int STORAGE_PERM_REQUEST_CODE = 8020;
private static final int RESIZED_VIDEO_HEIGHT = (int) (0.8 * Utils.displayMetrics.heightPixels); // 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 MainActivity fragmentActivity;
private CoordinatorLayout root; private CoordinatorLayout root;
private FragmentFeedBinding binding; private FragmentFeedBinding binding;
private StoriesService storiesService; private StoriesService storiesService;
private boolean feedHasNextPage = false; // private boolean feedHasNextPage = false;
private String feedEndCursor = null; // private String feedEndCursor = null;
// private FeedViewModel feedViewModel; // private FeedViewModel feedViewModel;
private VideoAwareRecyclerScroller videoAwareRecyclerScroller; // private VideoAwareRecyclerScroller videoAwareRecyclerScroller;
private boolean shouldRefresh = true; private boolean shouldRefresh = true;
private boolean isPullToRefresh; // private boolean isPullToRefresh;
private FeedStoriesViewModel feedStoriesViewModel; private FeedStoriesViewModel feedStoriesViewModel;
private StaggeredGridLayoutManager gridLayoutManager; // private StaggeredGridLayoutManager gridLayoutManager;
private boolean storiesFetching; private boolean storiesFetching;
private final boolean shouldAutoPlay = settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS); // private final boolean shouldAutoPlay = settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS);
// private final FetchListener<FeedModel[]> feedFetchListener = new FetchListener<FeedModel[]>() { private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
// @Override @Override
// public void doBefore() { public void onPostClick(final FeedModel feedModel, final View profilePicView, final View mainPostImage) {
// binding.feedSwipeRefreshLayout.post(() -> binding.feedSwipeRefreshLayout.setRefreshing(true)); openPostDialog(feedModel, profilePicView, mainPostImage, -1);
// } }
//
// @Override @Override
// public void onResult(final FeedModel[] result) { public void onSliderClick(final FeedModel feedModel, final int position) {
// if (result == null || result.length <= 0) { openPostDialog(feedModel, null, null, position);
// binding.feedSwipeRefreshLayout.setRefreshing(false); }
// return;
// } @Override
// final List<FeedModel> currentFeedModelList = feedViewModel.getList().getValue(); public void onCommentsClick(final FeedModel feedModel) {
// final Map<String, FeedModel> thumbToFeedMap = new HashMap<>(); final NavDirections commentsAction = FeedFragmentDirections.actionGlobalCommentsViewerFragment(
// for (final FeedModel feedModel : result) { feedModel.getShortCode(),
// thumbToFeedMap.put(feedModel.getThumbnailUrl(), feedModel); feedModel.getPostId(),
// } feedModel.getProfileModel().getId()
// final BaseDataSubscriber<Void> subscriber = new BaseDataSubscriber<Void>() { );
// int success = 0; NavHostFragment.findNavController(FeedFragment.this).navigate(commentsAction);
// int failed = 0; }
//
// @Override @Override
// protected void onNewResultImpl(@NonNull final DataSource<Void> dataSource) { public void onDownloadClick(final FeedModel feedModel) {
// final Map<String, Object> extras = dataSource.getExtras(); final Context context = getContext();
// if (extras == null) return; if (context == null) return;
// final Uri thumbUri = (Uri) extras.get("uri_source"); if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
// if (thumbUri == null) return; showDownloadDialog(feedModel);
// 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; return;
} }
if (isLocation) { requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE);
final NavDirections action = FeedFragmentDirections.actionGlobalLocationFragment(text); }
NavHostFragment.findNavController(this).navigate(action);
return; @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 @Override
public void onCreate(@Nullable final Bundle savedInstanceState) { public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
fragmentActivity = (MainActivity) requireActivity(); fragmentActivity = (MainActivity) requireActivity();
storiesService = StoriesService.getInstance(); storiesService = StoriesService.getInstance();
// feedService = FeedService.getInstance();
// final TransitionInflater inflater = TransitionInflater.from(getContext());
// setExitTransition(inflater.inflateTransition(android.R.transition.move));
setHasOptionsMenu(true); setHasOptionsMenu(true);
} }
@ -316,74 +210,44 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
@Override @Override
public void onPause() { public void onPause() {
super.onPause(); super.onPause();
if (videoAwareRecyclerScroller != null) { // if (videoAwareRecyclerScroller != null) {
videoAwareRecyclerScroller.stopPlaying(); // videoAwareRecyclerScroller.stopPlaying();
} // }
} }
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
binding.feedSwipeRefreshLayout.setRefreshing(false); binding.feedSwipeRefreshLayout.setRefreshing(false);
if (videoAwareRecyclerScroller != null && shouldAutoPlay) { // if (videoAwareRecyclerScroller != null && shouldAutoPlay) {
videoAwareRecyclerScroller.startPlaying(); // videoAwareRecyclerScroller.startPlaying();
} // }
} }
@Override @Override
public void onRefresh() { public void onRefresh() {
isPullToRefresh = true; // isPullToRefresh = true;
feedEndCursor = null; // feedEndCursor = null;
binding.feedRecyclerView.refresh(); binding.feedRecyclerView.refresh();
fetchStories(); fetchStories();
} }
private void setupFeed() { private void setupFeed() {
// feedViewModel = new ViewModelProvider(fragmentActivity).get(FeedViewModel.class);
final Context context = getContext(); final Context context = getContext();
if (context == null) return; 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) binding.feedRecyclerView.setViewModelStoreOwner(this)
.setLifeCycleOwner(this) .setLifeCycleOwner(this)
.setPostFetchService(new FeedPostFetchService()) .setPostFetchService(new FeedPostFetchService())
.setLayoutPreferences(PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_POSTS_LAYOUT))) .setLayoutPreferences(PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_POSTS_LAYOUT)))
.addFetchStatusChangeListener(fetching -> updateSwipeRefreshState()) .addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
.setOnPostClickListener((feedModel, profilePicView, mainPostImage) -> { .setFeedItemCallback(feedItemCallback)
final PostViewV2Fragment fragment = PostViewV2Fragment
.builder(feedModel)
.setSharedProfilePicElement(profilePicView)
.setSharedMainPostElement(mainPostImage)
.build();
fragment.show(getChildFragmentManager(), "post_view");
})
.init(); .init();
binding.feedSwipeRefreshLayout.setRefreshing(true); 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); // feedAdapter.setStateRestorationPolicy(RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY);
// binding.feedRecyclerView.setAdapter(feedAdapter);
// feedViewModel.getList().observe(fragmentActivity, feedAdapter::submitList);
// if (shouldAutoPlay) { // if (shouldAutoPlay) {
// videoAwareRecyclerScroller = new VideoAwareRecyclerScroller(); // videoAwareRecyclerScroller = new VideoAwareRecyclerScroller();
// binding.feedRecyclerView.addOnScrollListener(videoAwareRecyclerScroller); // binding.feedRecyclerView.addOnScrollListener(videoAwareRecyclerScroller);
// } // }
// fetchFeed();
} }
private void updateSwipeRefreshState() { 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() { private void showPostsLayoutPreferences() {
final PostsLayoutPreferencesDialogFragment fragment = new PostsLayoutPreferencesDialogFragment(preferences -> new Handler() final PostsLayoutPreferencesDialogFragment fragment = new PostsLayoutPreferencesDialogFragment(preferences -> new Handler()
.postDelayed(() -> binding.feedRecyclerView.setLayoutPreferences(preferences), 200)); .postDelayed(() -> binding.feedRecyclerView.setLayoutPreferences(preferences), 200));

View File

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

View File

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

View File

@ -5,6 +5,7 @@ import android.content.ClipData;
import android.content.ClipboardManager; import android.content.ClipboardManager;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.res.Resources; import android.content.res.Resources;
import android.net.Uri; import android.net.Uri;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
@ -158,4 +159,23 @@ public final class Utils {
} }
return statusBarHeight; 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);
}
} }

View File

@ -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;
}
}

View File

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

View File

@ -132,7 +132,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@null"> android:background="@null">
<io.github.armcha.autolink.AutoLinkTextView <awais.instagrabber.customviews.RamboTextViewV2
android:id="@+id/caption" android:id="@+id/caption"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -253,12 +253,32 @@
app:iconSize="16dp" app:iconSize="16dp"
app:iconTint="@color/white" app:iconTint="@color/white"
app:layout_constraintBottom_toBottomOf="parent" 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_constraintStart_toEndOf="@id/caption_toggle"
app:layout_constraintTop_toTopOf="@id/caption_toggle" app:layout_constraintTop_toTopOf="@id/caption_toggle"
app:rippleColor="@color/grey_300" app:rippleColor="@color/grey_300"
tools:visibility="visible" /> 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 <com.google.android.material.button.MaterialButton
android:id="@+id/player_controls_toggle" android:id="@+id/player_controls_toggle"
style="@style/Widget.MaterialComponents.Button.TextButton" style="@style/Widget.MaterialComponents.Button.TextButton"
@ -274,7 +294,7 @@
app:iconTint="@color/white" app:iconTint="@color/white"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/save" 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:layout_constraintTop_toTopOf="@id/caption_toggle"
app:rippleColor="@color/grey_300" app:rippleColor="@color/grey_300"
tools:visibility="visible" /> tools:visibility="visible" />

View File

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

View File

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

View File

@ -1,18 +1,15 @@
<?xml version="1.0" encoding="utf-8"?> <?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:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:viewBindingIgnore="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/container" android:id="@+id/container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?android:selectableItemBackground" android:background="?android:selectableItemBackground"
android:orientation="horizontal"> android:clickable="true"
android:focusable="true"
android:orientation="horizontal"
android:padding="8dp">
<com.facebook.drawee.view.SimpleDraweeView <com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/ivProfilePic" android:id="@+id/ivProfilePic"
@ -22,7 +19,8 @@
app:actualImageScaleType="centerCrop" app:actualImageScaleType="centerCrop"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:roundAsCircle="true" /> app:roundAsCircle="true"
tools:background="@mipmap/ic_launcher" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvUsername" android:id="@+id/tvUsername"
@ -43,7 +41,7 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:text="username" /> tools:text="username" />
<awais.instagrabber.customviews.RamboTextView <awais.instagrabber.customviews.RamboTextViewV2
android:id="@+id/tvComment" android:id="@+id/tvComment"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -60,7 +58,8 @@
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@id/ivProfilePic" app:layout_constraintStart_toEndOf="@id/ivProfilePic"
app:layout_constraintTop_toBottomOf="@id/tvUsername" 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 <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvLikes" android:id="@+id/tvLikes"
@ -72,6 +71,7 @@
android:paddingTop="2dp" android:paddingTop="2dp"
android:paddingEnd="4dp" android:paddingEnd="4dp"
android:paddingBottom="2dp" android:paddingBottom="2dp"
android:scrollbars="none"
android:singleLine="true" android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Caption" android:textAppearance="@style/TextAppearance.AppCompat.Caption"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
@ -95,24 +95,23 @@
app:layout_constraintStart_toEndOf="@id/tvLikes" app:layout_constraintStart_toEndOf="@id/tvLikes"
app:layout_constraintTop_toBottomOf="@id/tvComment" app:layout_constraintTop_toBottomOf="@id/tvComment"
tools:text="long date................................" /> tools:text="long date................................" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<androidx.recyclerview.widget.RecyclerView <!--<androidx.recyclerview.widget.RecyclerView-->
android:id="@+id/rvChildComments" <!-- android:id="@+id/rvChildComments"-->
android:layout_width="match_parent" <!-- android:layout_width="match_parent"-->
android:layout_height="wrap_content" <!-- android:layout_height="wrap_content"-->
android:layout_marginStart="40dp" <!-- android:layout_marginStart="40dp"-->
android:layout_marginLeft="40dp" <!-- android:layout_marginLeft="40dp"-->
app:layoutManager="LinearLayoutManager" <!-- app:layoutManager="LinearLayoutManager"-->
tools:itemCount="5" <!-- tools:itemCount="5"-->
tools:listitem="@layout/item_comment_small" /> <!-- tools:listitem="@layout/item_comment_small" />-->
<View <!--<View-->
android:layout_width="match_parent" <!-- android:layout_width="match_parent"-->
android:layout_height="1dp" <!-- android:layout_height="1dp"-->
android:layout_gravity="bottom" <!-- android:layout_gravity="bottom"-->
android:layout_marginStart="4dp" <!-- android:layout_marginStart="4dp"-->
android:layout_marginEnd="4dp" <!-- android:layout_marginEnd="4dp"-->
android:layout_marginBottom="4dp" <!-- android:layout_marginBottom="4dp"-->
android:background="#32888888" /> <!-- android:background="#32888888" />-->
</LinearLayout>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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