mirror of
https://github.com/KokaKiwi/BarInsta
synced 2024-12-22 21:16:57 +00:00
Improve comments viewer ui and ux. Update ui for likes viewer and follows viewer.
This commit is contained in:
parent
18292ead4e
commit
074ee18c9d
@ -1,195 +1,60 @@
|
||||
package awais.instagrabber.adapters;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
import androidx.recyclerview.widget.ListAdapter;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import awais.instagrabber.adapters.viewholder.comments.ChildCommentViewHolder;
|
||||
import awais.instagrabber.adapters.viewholder.comments.ParentCommentViewHolder;
|
||||
import awais.instagrabber.adapters.viewholder.CommentViewHolder;
|
||||
import awais.instagrabber.databinding.ItemCommentBinding;
|
||||
import awais.instagrabber.databinding.ItemCommentSmallBinding;
|
||||
import awais.instagrabber.models.CommentModel;
|
||||
import awais.instagrabber.models.Comment;
|
||||
|
||||
public final class CommentsAdapter extends ListAdapter<CommentModel, RecyclerView.ViewHolder> {
|
||||
private static final int TYPE_PARENT = 1;
|
||||
private static final int TYPE_CHILD = 2;
|
||||
|
||||
private final Map<Integer, Integer> positionTypeMap = new HashMap<>();
|
||||
|
||||
// private final Filter filter = new Filter() {
|
||||
// @NonNull
|
||||
// @Override
|
||||
// protected FilterResults performFiltering(final CharSequence filter) {
|
||||
// final FilterResults results = new FilterResults();
|
||||
// results.values = commentModels;
|
||||
//
|
||||
// final int commentsLen = commentModels == null ? 0 : commentModels.size();
|
||||
// if (commentModels != null && commentsLen > 0 && !TextUtils.isEmpty(filter)) {
|
||||
// final String query = filter.toString().toLowerCase();
|
||||
// final ArrayList<CommentModel> filterList = new ArrayList<>(commentsLen);
|
||||
//
|
||||
// for (final CommentModel commentModel : commentModels) {
|
||||
// final String commentText = commentModel.getText().toString().toLowerCase();
|
||||
//
|
||||
// if (commentText.contains(query)) filterList.add(commentModel);
|
||||
// else {
|
||||
// final List<CommentModel> childCommentModels = commentModel.getChildCommentModels();
|
||||
// if (childCommentModels != null) {
|
||||
// for (final CommentModel childCommentModel : childCommentModels) {
|
||||
// final String childCommentText = childCommentModel.getText().toString().toLowerCase();
|
||||
// if (childCommentText.contains(query)) filterList.add(commentModel);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// filterList.trimToSize();
|
||||
// results.values = filterList.toArray(new CommentModel[0]);
|
||||
// }
|
||||
//
|
||||
// return results;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// protected void publishResults(final CharSequence constraint, @NonNull final FilterResults results) {
|
||||
// if (results.values instanceof List) {
|
||||
// //noinspection unchecked
|
||||
// filteredCommentModels = (List<CommentModel>) results.values;
|
||||
// notifyDataSetChanged();
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
|
||||
private static final DiffUtil.ItemCallback<CommentModel> DIFF_CALLBACK = new DiffUtil.ItemCallback<CommentModel>() {
|
||||
public final class CommentsAdapter extends ListAdapter<Comment, CommentViewHolder> {
|
||||
private static final DiffUtil.ItemCallback<Comment> DIFF_CALLBACK = new DiffUtil.ItemCallback<Comment>() {
|
||||
@Override
|
||||
public boolean areItemsTheSame(@NonNull final CommentModel oldItem, @NonNull final CommentModel newItem) {
|
||||
return oldItem.getId().equals(newItem.getId());
|
||||
public boolean areItemsTheSame(@NonNull final Comment oldItem, @NonNull final Comment newItem) {
|
||||
return Objects.equals(oldItem.getId(), newItem.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(@NonNull final CommentModel oldItem, @NonNull final CommentModel newItem) {
|
||||
return oldItem.getId().equals(newItem.getId());
|
||||
public boolean areContentsTheSame(@NonNull final Comment oldItem, @NonNull final Comment newItem) {
|
||||
return Objects.equals(oldItem, newItem);
|
||||
}
|
||||
};
|
||||
private final CommentCallback commentCallback;
|
||||
private CommentModel selected, toChangeLike;
|
||||
private int selectedIndex, likedIndex;
|
||||
|
||||
public CommentsAdapter(final CommentCallback commentCallback) {
|
||||
private final boolean showingReplies;
|
||||
private final CommentCallback commentCallback;
|
||||
private final long currentUserId;
|
||||
|
||||
public CommentsAdapter(final long currentUserId,
|
||||
final boolean showingReplies,
|
||||
final CommentCallback commentCallback) {
|
||||
super(DIFF_CALLBACK);
|
||||
this.showingReplies = showingReplies;
|
||||
this.currentUserId = currentUserId;
|
||||
this.commentCallback = commentCallback;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) {
|
||||
final Context context = parent.getContext();
|
||||
final LayoutInflater layoutInflater = LayoutInflater.from(context);
|
||||
if (type == TYPE_PARENT) {
|
||||
final ItemCommentBinding binding = ItemCommentBinding.inflate(layoutInflater, parent, false);
|
||||
return new ParentCommentViewHolder(binding);
|
||||
}
|
||||
final ItemCommentSmallBinding binding = ItemCommentSmallBinding.inflate(layoutInflater, parent, false);
|
||||
return new ChildCommentViewHolder(binding);
|
||||
public CommentViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) {
|
||||
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
|
||||
final ItemCommentBinding binding = ItemCommentBinding.inflate(layoutInflater, parent, false);
|
||||
return new CommentViewHolder(binding, currentUserId, commentCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {
|
||||
CommentModel commentModel = getItem(position);
|
||||
if (commentModel == null) return;
|
||||
final int type = getItemViewType(position);
|
||||
final boolean selected = this.selected != null && this.selected.getId().equals(commentModel.getId());
|
||||
final boolean toLike = this.toChangeLike != null && this.toChangeLike.getId().equals(commentModel.getId());
|
||||
if (toLike) commentModel = this.toChangeLike;
|
||||
if (type == TYPE_PARENT) {
|
||||
final ParentCommentViewHolder viewHolder = (ParentCommentViewHolder) holder;
|
||||
viewHolder.bind(commentModel, selected, commentCallback);
|
||||
return;
|
||||
}
|
||||
final ChildCommentViewHolder viewHolder = (ChildCommentViewHolder) holder;
|
||||
viewHolder.bind(commentModel, selected, commentCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void submitList(@Nullable final List<CommentModel> list) {
|
||||
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();
|
||||
if (children != null) {
|
||||
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 void setLiked(final CommentModel commentModel, final boolean liked) {
|
||||
likedIndex = getCurrentList().indexOf(commentModel);
|
||||
CommentModel newCommentModel = commentModel;
|
||||
newCommentModel.setLiked(liked);
|
||||
this.toChangeLike = newCommentModel;
|
||||
notifyItemChanged(likedIndex);
|
||||
}
|
||||
|
||||
public CommentModel getSelected() {
|
||||
return selected;
|
||||
public void onBindViewHolder(@NonNull final CommentViewHolder holder, final int position) {
|
||||
final Comment comment = getItem(position);
|
||||
holder.bind(comment, showingReplies && position == 0, showingReplies && position != 0);
|
||||
}
|
||||
|
||||
public interface CommentCallback {
|
||||
void onClick(final CommentModel comment);
|
||||
void onClick(final Comment comment);
|
||||
|
||||
void onHashtagClick(final String hashtag);
|
||||
|
||||
@ -198,5 +63,15 @@ public final class CommentsAdapter extends ListAdapter<CommentModel, RecyclerVie
|
||||
void onURLClick(final String url);
|
||||
|
||||
void onEmailClick(final String emailAddress);
|
||||
|
||||
void onLikeClick(final Comment comment, boolean liked, final boolean isReply);
|
||||
|
||||
void onRepliesClick(final Comment comment);
|
||||
|
||||
void onViewLikes(Comment comment);
|
||||
|
||||
void onTranslate(Comment comment);
|
||||
|
||||
void onDelete(Comment comment, boolean isReply);
|
||||
}
|
||||
}
|
@ -34,7 +34,7 @@ public final class LikesAdapter extends RecyclerView.Adapter<FollowsViewHolder>
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull final FollowsViewHolder holder, final int position) {
|
||||
final User model = profileModels.get(position);
|
||||
holder.bind(model, null, onClickListener);
|
||||
holder.bind(model, onClickListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,209 @@
|
||||
package awais.instagrabber.adapters.viewholder;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Menu;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.view.ContextThemeWrapper;
|
||||
import androidx.appcompat.widget.PopupMenu;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.adapters.CommentsAdapter.CommentCallback;
|
||||
import awais.instagrabber.customviews.ProfilePicView;
|
||||
import awais.instagrabber.databinding.ItemCommentBinding;
|
||||
import awais.instagrabber.models.Comment;
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
|
||||
public final class CommentViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private final ItemCommentBinding binding;
|
||||
private final long currentUserId;
|
||||
private final CommentCallback commentCallback;
|
||||
@ColorInt
|
||||
private int parentCommentHighlightColor;
|
||||
private PopupMenu optionsPopup;
|
||||
|
||||
public CommentViewHolder(@NonNull final ItemCommentBinding binding,
|
||||
final long currentUserId,
|
||||
final CommentCallback commentCallback) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
this.currentUserId = currentUserId;
|
||||
this.commentCallback = commentCallback;
|
||||
final Context context = itemView.getContext();
|
||||
if (context == null) return;
|
||||
final Resources.Theme theme = context.getTheme();
|
||||
if (theme == null) return;
|
||||
final TypedValue typedValue = new TypedValue();
|
||||
final boolean resolved = theme.resolveAttribute(R.attr.parentCommentHighlightColor, typedValue, true);
|
||||
if (resolved) {
|
||||
parentCommentHighlightColor = typedValue.data;
|
||||
}
|
||||
}
|
||||
|
||||
public void bind(final Comment comment, final boolean isReplyParent, final boolean isReply) {
|
||||
if (comment == null) return;
|
||||
itemView.setOnClickListener(v -> {
|
||||
if (commentCallback != null) {
|
||||
commentCallback.onClick(comment);
|
||||
}
|
||||
});
|
||||
if (isReplyParent && parentCommentHighlightColor != 0) {
|
||||
itemView.setBackgroundColor(parentCommentHighlightColor);
|
||||
} else {
|
||||
itemView.setBackgroundColor(itemView.getResources().getColor(android.R.color.transparent));
|
||||
}
|
||||
setupCommentText(comment, isReply);
|
||||
binding.date.setText(comment.getDateTime());
|
||||
setLikes(comment, isReply);
|
||||
setReplies(comment, isReply);
|
||||
setUser(comment, isReply);
|
||||
setupOptions(comment, isReply);
|
||||
}
|
||||
|
||||
private void setupCommentText(@NonNull final Comment comment, final boolean isReply) {
|
||||
binding.comment.clearOnURLClickListeners();
|
||||
binding.comment.clearOnHashtagClickListeners();
|
||||
binding.comment.clearOnMentionClickListeners();
|
||||
binding.comment.clearOnEmailClickListeners();
|
||||
binding.comment.setText(comment.getText());
|
||||
binding.comment.setTextSize(TypedValue.COMPLEX_UNIT_SP, isReply ? 12 : 14);
|
||||
binding.comment.addOnHashtagListener(autoLinkItem -> {
|
||||
final String originalText = autoLinkItem.getOriginalText();
|
||||
if (commentCallback == null) return;
|
||||
commentCallback.onHashtagClick(originalText);
|
||||
});
|
||||
binding.comment.addOnMentionClickListener(autoLinkItem -> {
|
||||
final String originalText = autoLinkItem.getOriginalText();
|
||||
if (commentCallback == null) return;
|
||||
commentCallback.onMentionClick(originalText);
|
||||
|
||||
});
|
||||
binding.comment.addOnEmailClickListener(autoLinkItem -> {
|
||||
final String originalText = autoLinkItem.getOriginalText();
|
||||
if (commentCallback == null) return;
|
||||
commentCallback.onEmailClick(originalText);
|
||||
});
|
||||
binding.comment.addOnURLClickListener(autoLinkItem -> {
|
||||
final String originalText = autoLinkItem.getOriginalText();
|
||||
if (commentCallback == null) return;
|
||||
commentCallback.onURLClick(originalText);
|
||||
});
|
||||
binding.comment.setOnLongClickListener(v -> {
|
||||
Utils.copyText(itemView.getContext(), comment.getText());
|
||||
return true;
|
||||
});
|
||||
binding.comment.setOnClickListener(v -> commentCallback.onClick(comment));
|
||||
}
|
||||
|
||||
private void setUser(@NonNull final Comment comment, final boolean isReply) {
|
||||
final User user = comment.getUser();
|
||||
if (user == null) return;
|
||||
binding.username.setUsername(user.getUsername(), user.isVerified());
|
||||
binding.username.setTextAppearance(itemView.getContext(), isReply ? R.style.TextAppearance_MaterialComponents_Subtitle2
|
||||
: R.style.TextAppearance_MaterialComponents_Subtitle1);
|
||||
binding.username.setOnClickListener(v -> {
|
||||
if (commentCallback == null) return;
|
||||
commentCallback.onMentionClick("@" + user.getUsername());
|
||||
});
|
||||
binding.profilePic.setImageURI(user.getProfilePicUrl());
|
||||
binding.profilePic.setSize(isReply ? ProfilePicView.Size.SMALLER : ProfilePicView.Size.SMALL);
|
||||
binding.profilePic.setOnClickListener(v -> {
|
||||
if (commentCallback == null) return;
|
||||
commentCallback.onMentionClick("@" + user.getUsername());
|
||||
});
|
||||
}
|
||||
|
||||
private void setLikes(@NonNull final Comment comment, final boolean isReply) {
|
||||
// final String likesString = itemView.getResources().getQuantityString(R.plurals.likes_count, likes, likes);
|
||||
binding.likes.setText(String.valueOf(comment.getLikes()));
|
||||
binding.likes.setOnLongClickListener(v -> {
|
||||
if (commentCallback == null) return false;
|
||||
commentCallback.onViewLikes(comment);
|
||||
return true;
|
||||
});
|
||||
if (currentUserId == 0) { // not logged in
|
||||
binding.likes.setOnClickListener(v -> {
|
||||
if (commentCallback == null) return;
|
||||
commentCallback.onViewLikes(comment);
|
||||
});
|
||||
return;
|
||||
}
|
||||
final boolean liked = comment.getLiked();
|
||||
final int resId = liked ? R.drawable.ic_like : R.drawable.ic_not_liked;
|
||||
binding.likes.setCompoundDrawablesRelativeWithSize(ContextCompat.getDrawable(itemView.getContext(), resId), null, null, null);
|
||||
binding.likes.setOnClickListener(v -> {
|
||||
if (commentCallback == null) return;
|
||||
// toggle like
|
||||
commentCallback.onLikeClick(comment, !liked, isReply);
|
||||
});
|
||||
}
|
||||
|
||||
private void setReplies(@NonNull final Comment comment, final boolean isReply) {
|
||||
final int replies = comment.getReplyCount();
|
||||
binding.replies.setVisibility(View.VISIBLE);
|
||||
final String text = isReply ? "" : String.valueOf(replies);
|
||||
// final String string = itemView.getResources().getQuantityString(R.plurals.replies_count, replies, replies);
|
||||
binding.replies.setText(text);
|
||||
binding.replies.setOnClickListener(v -> {
|
||||
if (commentCallback == null) return;
|
||||
commentCallback.onRepliesClick(comment);
|
||||
});
|
||||
}
|
||||
|
||||
private void setupOptions(final Comment comment, final boolean isReply) {
|
||||
binding.options.setOnClickListener(v -> {
|
||||
if (optionsPopup == null) {
|
||||
createOptionsPopupMenu(comment, isReply);
|
||||
}
|
||||
if (optionsPopup == null) return;
|
||||
optionsPopup.show();
|
||||
});
|
||||
}
|
||||
|
||||
private void createOptionsPopupMenu(final Comment comment, final boolean isReply) {
|
||||
if (optionsPopup == null) {
|
||||
final ContextThemeWrapper themeWrapper = new ContextThemeWrapper(itemView.getContext(), R.style.popupMenuStyle);
|
||||
optionsPopup = new PopupMenu(themeWrapper, binding.options);
|
||||
} else {
|
||||
optionsPopup.getMenu().clear();
|
||||
}
|
||||
optionsPopup.getMenuInflater().inflate(R.menu.comment_options_menu, optionsPopup.getMenu());
|
||||
final User user = comment.getUser();
|
||||
if (currentUserId == 0 || user == null || user.getPk() != currentUserId) {
|
||||
final Menu menu = optionsPopup.getMenu();
|
||||
menu.removeItem(R.id.delete);
|
||||
}
|
||||
optionsPopup.setOnMenuItemClickListener(item -> {
|
||||
if (commentCallback == null) return false;
|
||||
int itemId = item.getItemId();
|
||||
if (itemId == R.id.translate) {
|
||||
commentCallback.onTranslate(comment);
|
||||
return true;
|
||||
}
|
||||
if (itemId == R.id.delete) {
|
||||
commentCallback.onDelete(comment, isReply);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
// private void setupReply(final Comment comment) {
|
||||
// if (!isLoggedIn) {
|
||||
// binding.reply.setVisibility(View.GONE);
|
||||
// return;
|
||||
// }
|
||||
// binding.reply.setOnClickListener(v -> {
|
||||
// if (commentCallback == null) return;
|
||||
// // toggle like
|
||||
// commentCallback.onReplyClick(comment);
|
||||
// });
|
||||
// }
|
||||
}
|
@ -2,10 +2,9 @@ package awais.instagrabber.adapters.viewholder;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.databinding.ItemFollowBinding;
|
||||
import awais.instagrabber.models.FollowModel;
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
@ -14,23 +13,19 @@ public final class FollowsViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private final ItemFollowBinding binding;
|
||||
|
||||
public FollowsViewHolder(final ItemFollowBinding binding) {
|
||||
public FollowsViewHolder(@NonNull final ItemFollowBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
|
||||
public void bind(final User model,
|
||||
final List<Long> admins,
|
||||
final View.OnClickListener onClickListener) {
|
||||
if (model == null) return;
|
||||
itemView.setTag(model);
|
||||
itemView.setOnClickListener(onClickListener);
|
||||
binding.tvUsername.setText(model.getUsername());
|
||||
binding.tvFullName.setText(model.getFullName());
|
||||
if (admins != null && admins.contains(model.getPk())) {
|
||||
binding.isAdmin.setVisibility(View.VISIBLE);
|
||||
}
|
||||
binding.ivProfilePic.setImageURI(model.getProfilePicUrl());
|
||||
binding.username.setUsername("@" + model.getUsername(), model.isVerified());
|
||||
binding.fullName.setText(model.getFullName());
|
||||
binding.profilePic.setImageURI(model.getProfilePicUrl());
|
||||
}
|
||||
|
||||
public void bind(final FollowModel model,
|
||||
@ -38,8 +33,8 @@ public final class FollowsViewHolder extends RecyclerView.ViewHolder {
|
||||
if (model == null) return;
|
||||
itemView.setTag(model);
|
||||
itemView.setOnClickListener(onClickListener);
|
||||
binding.tvUsername.setText(model.getUsername());
|
||||
binding.tvFullName.setText(model.getFullName());
|
||||
binding.ivProfilePic.setImageURI(model.getProfilePicUrl());
|
||||
binding.username.setUsername("@" + model.getUsername());
|
||||
binding.fullName.setText(model.getFullName());
|
||||
binding.profilePic.setImageURI(model.getProfilePicUrl());
|
||||
}
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
package awais.instagrabber.adapters.viewholder.comments;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
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.repositories.responses.User;
|
||||
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 User profileModel = comment.getProfileModel();
|
||||
if (profileModel == null) return;
|
||||
binding.tvUsername.setText(profileModel.getUsername());
|
||||
binding.ivProfilePic.setImageURI(profileModel.getProfilePicUrl());
|
||||
binding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
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) {
|
||||
itemView.setBackgroundColor(itemView.getResources().getColor(R.color.comment_liked));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
package awais.instagrabber.adapters.viewholder.comments;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
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.repositories.responses.User;
|
||||
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 User profileModel = comment.getProfileModel();
|
||||
if (profileModel == null) return;
|
||||
binding.tvUsername.setText(profileModel.getUsername());
|
||||
binding.ivProfilePic.setImageURI(profileModel.getProfilePicUrl());
|
||||
binding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
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) {
|
||||
itemView.setBackgroundColor(itemView.getResources().getColor(R.color.comment_liked));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,268 +0,0 @@
|
||||
package awais.instagrabber.asyncs;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.BuildConfig;
|
||||
import awais.instagrabber.interfaces.FetchListener;
|
||||
import awais.instagrabber.models.CommentModel;
|
||||
import awais.instagrabber.repositories.responses.FriendshipStatus;
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.NetworkUtils;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
//import awaisomereport.LogCollector;
|
||||
|
||||
//import static awais.instagrabber.utils.Utils.logCollector;
|
||||
|
||||
public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentModel>> {
|
||||
private static final String TAG = "CommentsFetcher";
|
||||
|
||||
private final String shortCode, endCursor;
|
||||
private final FetchListener<List<CommentModel>> fetchListener;
|
||||
|
||||
public CommentsFetcher(final String shortCode, final String endCursor, final FetchListener<List<CommentModel>> fetchListener) {
|
||||
this.shortCode = shortCode;
|
||||
this.endCursor = endCursor;
|
||||
this.fetchListener = fetchListener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected List<CommentModel> doInBackground(final Void... voids) {
|
||||
/*
|
||||
"https://www.instagram.com/graphql/query/?query_hash=97b41c52301f77ce508f55e66d17620e&variables=" + "{\"shortcode\":\"" + shortcode + "\",\"first\":50,\"after\":\"" + endCursor + "\"}";
|
||||
|
||||
97b41c52301f77ce508f55e66d17620e -> for comments
|
||||
51fdd02b67508306ad4484ff574a0b62 -> for child comments
|
||||
|
||||
https://www.instagram.com/graphql/query/?query_hash=51fdd02b67508306ad4484ff574a0b62&variables={"comment_id":"18100041898085322","first":50,"after":""}
|
||||
*/
|
||||
final List<CommentModel> commentModels = getParentComments();
|
||||
if (commentModels != null) {
|
||||
for (final CommentModel commentModel : commentModels) {
|
||||
final List<CommentModel> childCommentModels = commentModel.getChildCommentModels();
|
||||
if (childCommentModels != null) {
|
||||
final int childCommentsLen = childCommentModels.size();
|
||||
final CommentModel lastChild = childCommentModels.get(childCommentsLen - 1);
|
||||
if (lastChild != null && lastChild.hasNextPage() && !TextUtils.isEmpty(lastChild.getEndCursor())) {
|
||||
final List<CommentModel> remoteChildComments = getChildComments(commentModel.getId());
|
||||
commentModel.setChildCommentModels(remoteChildComments);
|
||||
lastChild.setPageCursor(false, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return commentModels;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
if (fetchListener != null) fetchListener.doBefore();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(final List<CommentModel> result) {
|
||||
if (fetchListener != null) fetchListener.onResult(result);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private synchronized List<CommentModel> getChildComments(final String commentId) {
|
||||
final List<CommentModel> commentModels = new ArrayList<>();
|
||||
String childEndCursor = "";
|
||||
while (childEndCursor != null) {
|
||||
final String url = "https://www.instagram.com/graphql/query/?query_hash=51fdd02b67508306ad4484ff574a0b62&variables=" +
|
||||
"{\"comment_id\":\"" + commentId + "\",\"first\":50,\"after\":\"" + childEndCursor + "\"}";
|
||||
try {
|
||||
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
|
||||
conn.setUseCaches(false);
|
||||
conn.connect();
|
||||
|
||||
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) break;
|
||||
else {
|
||||
final JSONObject data = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("data")
|
||||
.getJSONObject("comment")
|
||||
.getJSONObject("edge_threaded_comments");
|
||||
|
||||
final JSONObject pageInfo = data.getJSONObject("page_info");
|
||||
childEndCursor = pageInfo.getString("end_cursor");
|
||||
if (TextUtils.isEmpty(childEndCursor)) childEndCursor = null;
|
||||
|
||||
final JSONArray childComments = data.optJSONArray("edges");
|
||||
if (childComments != null) {
|
||||
final int length = childComments.length();
|
||||
for (int i = 0; i < length; ++i) {
|
||||
final JSONObject childComment = childComments.getJSONObject(i).optJSONObject("node");
|
||||
|
||||
if (childComment != null) {
|
||||
final JSONObject owner = childComment.getJSONObject("owner");
|
||||
final User user = new User(
|
||||
owner.optLong(Constants.EXTRAS_ID, 0),
|
||||
owner.getString(Constants.EXTRAS_USERNAME),
|
||||
null,
|
||||
false,
|
||||
owner.getString("profile_pic_url"),
|
||||
null,
|
||||
new FriendshipStatus(false, false, false, false, false, false, false, false, false, false),
|
||||
false, false, false, false, false, null, null, 0, 0, 0, 0, null, null, 0, null, null, null,
|
||||
null, null, null);
|
||||
final JSONObject likedBy = childComment.optJSONObject("edge_liked_by");
|
||||
commentModels.add(new CommentModel(childComment.getString(Constants.EXTRAS_ID),
|
||||
childComment.getString("text"),
|
||||
childComment.getLong("created_at"),
|
||||
likedBy != null ? likedBy.optLong("count", 0) : 0,
|
||||
childComment.getBoolean("viewer_has_liked"),
|
||||
user));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
conn.disconnect();
|
||||
} catch (final Exception e) {
|
||||
// if (logCollector != null)
|
||||
// logCollector.appendException(e,
|
||||
// LogCollector.LogFile.ASYNC_COMMENTS_FETCHER,
|
||||
// "getChildComments",
|
||||
// new Pair<>("commentModels.size", commentModels.size()));
|
||||
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
|
||||
if (fetchListener != null) fetchListener.onFailure(e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return commentModels;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private synchronized List<CommentModel> getParentComments() {
|
||||
final List<CommentModel> commentModels = new ArrayList<>();
|
||||
final String url = "https://www.instagram.com/graphql/query/?query_hash=bc3296d1ce80a24b1b6e40b1e72903f5&variables=" +
|
||||
"{\"shortcode\":\"" + shortCode + "\",\"first\":50,\"after\":\"" + endCursor.replace("\"", "\\\"") + "\"}";
|
||||
try {
|
||||
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
|
||||
conn.setUseCaches(false);
|
||||
conn.connect();
|
||||
|
||||
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) return null;
|
||||
else {
|
||||
final JSONObject parentComments = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("data")
|
||||
.getJSONObject("shortcode_media")
|
||||
.getJSONObject(
|
||||
"edge_media_to_parent_comment");
|
||||
|
||||
final JSONObject pageInfo = parentComments.getJSONObject("page_info");
|
||||
final String foundEndCursor = pageInfo.optString("end_cursor");
|
||||
final boolean hasNextPage = pageInfo.optBoolean("has_next_page", !TextUtils.isEmpty(foundEndCursor));
|
||||
|
||||
// final boolean containsToken = endCursor.contains("bifilter_token");
|
||||
// if (!Utils.isEmpty(endCursor) && (containsToken || endCursor.contains("cached_comments_cursor"))) {
|
||||
// final JSONObject endCursorObject = new JSONObject(endCursor);
|
||||
// endCursor = endCursorObject.optString("cached_comments_cursor");
|
||||
//
|
||||
// if (!Utils.isEmpty(endCursor))
|
||||
// endCursor = "{\\\"cached_comments_cursor\\\": \\\"" + endCursor + "\\\", ";
|
||||
// else
|
||||
// endCursor = "{";
|
||||
//
|
||||
// endCursor = endCursor + "\\\"bifilter_token\\\": \\\"" + endCursorObject.getString("bifilter_token") + "\\\"}";
|
||||
// }
|
||||
// else if (containsToken) endCursor = null;
|
||||
|
||||
final JSONArray comments = parentComments.getJSONArray("edges");
|
||||
final int commentsLen = comments.length();
|
||||
for (int i = 0; i < commentsLen; ++i) {
|
||||
final JSONObject comment = comments.getJSONObject(i).getJSONObject("node");
|
||||
|
||||
final JSONObject owner = comment.getJSONObject("owner");
|
||||
final User user = new User(
|
||||
owner.optLong(Constants.EXTRAS_ID, 0),
|
||||
owner.getString(Constants.EXTRAS_USERNAME),
|
||||
null,
|
||||
false,
|
||||
owner.getString("profile_pic_url"),
|
||||
null,
|
||||
new FriendshipStatus(false, false, false, false, false, false, false, false, false, false),
|
||||
owner.optBoolean("is_verified"),
|
||||
false, false, false, false, null, null, 0, 0, 0, 0, null, null, 0, null, null, null, null,
|
||||
null, null);
|
||||
final JSONObject likedBy = comment.optJSONObject("edge_liked_by");
|
||||
final String commentId = comment.getString(Constants.EXTRAS_ID);
|
||||
final CommentModel commentModel = new CommentModel(commentId,
|
||||
comment.getString("text"),
|
||||
comment.getLong("created_at"),
|
||||
likedBy != null ? likedBy.optLong("count", 0) : 0,
|
||||
comment.getBoolean("viewer_has_liked"),
|
||||
user);
|
||||
if (i == 0 && !foundEndCursor.contains("tao_cursor"))
|
||||
commentModel.setPageCursor(hasNextPage, TextUtils.isEmpty(foundEndCursor) ? null : foundEndCursor);
|
||||
JSONObject tempJsonObject;
|
||||
final JSONArray childCommentsArray;
|
||||
final int childCommentsLen;
|
||||
if ((tempJsonObject = comment.optJSONObject("edge_threaded_comments")) != null &&
|
||||
(childCommentsArray = tempJsonObject.optJSONArray("edges")) != null
|
||||
&& (childCommentsLen = childCommentsArray.length()) > 0) {
|
||||
|
||||
final String childEndCursor;
|
||||
final boolean childHasNextPage;
|
||||
if ((tempJsonObject = tempJsonObject.optJSONObject("page_info")) != null) {
|
||||
childEndCursor = tempJsonObject.optString("end_cursor");
|
||||
childHasNextPage = tempJsonObject.optBoolean("has_next_page", !TextUtils.isEmpty(childEndCursor));
|
||||
} else {
|
||||
childEndCursor = null;
|
||||
childHasNextPage = false;
|
||||
}
|
||||
|
||||
final List<CommentModel> childCommentModels = new ArrayList<>();
|
||||
for (int j = 0; j < childCommentsLen; ++j) {
|
||||
final JSONObject childComment = childCommentsArray.getJSONObject(j).getJSONObject("node");
|
||||
|
||||
tempJsonObject = childComment.getJSONObject("owner");
|
||||
final User childUser = new User(
|
||||
tempJsonObject.optLong(Constants.EXTRAS_ID, 0),
|
||||
tempJsonObject.getString(Constants.EXTRAS_USERNAME),
|
||||
null,
|
||||
false,
|
||||
tempJsonObject.getString("profile_pic_url"),
|
||||
null,
|
||||
new FriendshipStatus(false, false, false, false, false, false, false, false, false, false),
|
||||
tempJsonObject.optBoolean("is_verified"), false, false, false, false, null, null, 0, 0, 0, 0, null, null, 0,
|
||||
null, null, null, null, null, null);
|
||||
|
||||
tempJsonObject = childComment.optJSONObject("edge_liked_by");
|
||||
childCommentModels.add(new CommentModel(childComment.getString(Constants.EXTRAS_ID),
|
||||
childComment.getString("text"),
|
||||
childComment.getLong("created_at"),
|
||||
tempJsonObject != null ? tempJsonObject.optLong("count", 0) : 0,
|
||||
childComment.getBoolean("viewer_has_liked"),
|
||||
childUser));
|
||||
}
|
||||
childCommentModels.get(childCommentsLen - 1).setPageCursor(childHasNextPage, childEndCursor);
|
||||
commentModel.setChildCommentModels(childCommentModels);
|
||||
}
|
||||
commentModels.add(commentModel);
|
||||
}
|
||||
}
|
||||
|
||||
conn.disconnect();
|
||||
} catch (final Exception e) {
|
||||
// if (logCollector != null)
|
||||
// logCollector.appendException(e, LogCollector.LogFile.ASYNC_COMMENTS_FETCHER, "getParentComments",
|
||||
// new Pair<>("commentModelsList.size", commentModels.size()));
|
||||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
|
||||
if (fetchListener != null) fetchListener.onFailure(e);
|
||||
return null;
|
||||
}
|
||||
return commentModels;
|
||||
}
|
||||
}
|
@ -70,6 +70,9 @@ public final class ProfilePicView extends CircularImageView {
|
||||
case SMALL:
|
||||
dimenRes = R.dimen.profile_pic_size_small;
|
||||
break;
|
||||
case SMALLER:
|
||||
dimenRes = R.dimen.profile_pic_size_smaller;
|
||||
break;
|
||||
case TINY:
|
||||
dimenRes = R.dimen.profile_pic_size_tiny;
|
||||
break;
|
||||
@ -113,7 +116,8 @@ public final class ProfilePicView extends CircularImageView {
|
||||
TINY(0),
|
||||
SMALL(1),
|
||||
REGULAR(2),
|
||||
LARGE(3);
|
||||
LARGE(3),
|
||||
SMALLER(4);
|
||||
|
||||
private final int value;
|
||||
private static final Map<Integer, Size> map = new HashMap<>();
|
||||
|
@ -1,10 +1,12 @@
|
||||
package awais.instagrabber.customviews;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.InputFilter;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.emoji.widget.EmojiTextViewHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -23,6 +25,8 @@ public class RamboTextViewV2 extends AutoLinkTextView {
|
||||
private final List<OnURLClickListener> onURLClickListeners = new ArrayList<>();
|
||||
private final List<OnEmailClickListener> onEmailClickListeners = new ArrayList<>();
|
||||
|
||||
private EmojiTextViewHelper emojiTextViewHelper;
|
||||
|
||||
public RamboTextViewV2(@NonNull final Context context,
|
||||
@Nullable final AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
@ -30,6 +34,7 @@ public class RamboTextViewV2 extends AutoLinkTextView {
|
||||
}
|
||||
|
||||
private void init() {
|
||||
getEmojiTextViewHelper().updateTransformationMethod();
|
||||
addAutoLinkMode(MODE_HASHTAG.INSTANCE, MODE_MENTION.INSTANCE, MODE_EMAIL.INSTANCE, MODE_URL.INSTANCE);
|
||||
onAutoLinkClick(autoLinkItem -> {
|
||||
final Mode mode = autoLinkItem.getMode();
|
||||
@ -60,6 +65,25 @@ public class RamboTextViewV2 extends AutoLinkTextView {
|
||||
onAutoLinkLongClick(autoLinkItem -> {});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFilters(InputFilter[] filters) {
|
||||
super.setFilters(getEmojiTextViewHelper().getFilters(filters));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAllCaps(boolean allCaps) {
|
||||
super.setAllCaps(allCaps);
|
||||
getEmojiTextViewHelper().setAllCaps(allCaps);
|
||||
}
|
||||
|
||||
|
||||
private EmojiTextViewHelper getEmojiTextViewHelper() {
|
||||
if (emojiTextViewHelper == null) {
|
||||
emojiTextViewHelper = new EmojiTextViewHelper(this);
|
||||
}
|
||||
return emojiTextViewHelper;
|
||||
}
|
||||
|
||||
public void addOnMentionClickListener(final OnMentionClickListener onMentionClickListener) {
|
||||
if (onMentionClickListener == null) {
|
||||
return;
|
||||
|
@ -0,0 +1,95 @@
|
||||
package awais.instagrabber.customviews;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.emoji.widget.EmojiAppCompatTextView;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
|
||||
/**
|
||||
* https://stackoverflow.com/a/31916731
|
||||
*/
|
||||
public class TextViewDrawableSize extends EmojiAppCompatTextView {
|
||||
|
||||
private int mDrawableWidth;
|
||||
private int mDrawableHeight;
|
||||
private boolean calledFromInit = false;
|
||||
|
||||
public TextViewDrawableSize(final Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public TextViewDrawableSize(final Context context, final AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public TextViewDrawableSize(final Context context, final AttributeSet attrs, final int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
private void init(@NonNull final Context context, final AttributeSet attrs, final int defStyleAttr) {
|
||||
final TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.TextViewDrawableSize, defStyleAttr, 0);
|
||||
|
||||
try {
|
||||
mDrawableWidth = array.getDimensionPixelSize(R.styleable.TextViewDrawableSize_compoundDrawableWidth, -1);
|
||||
mDrawableHeight = array.getDimensionPixelSize(R.styleable.TextViewDrawableSize_compoundDrawableHeight, -1);
|
||||
} finally {
|
||||
array.recycle();
|
||||
}
|
||||
|
||||
if (mDrawableWidth > 0 || mDrawableHeight > 0) {
|
||||
initCompoundDrawableSize();
|
||||
}
|
||||
}
|
||||
|
||||
private void initCompoundDrawableSize() {
|
||||
final Drawable[] drawables = getCompoundDrawablesRelative();
|
||||
for (Drawable drawable : drawables) {
|
||||
if (drawable == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final Rect realBounds = drawable.getBounds();
|
||||
float scaleFactor = drawable.getIntrinsicHeight() / (float) drawable.getIntrinsicWidth();
|
||||
|
||||
float drawableWidth = drawable.getIntrinsicWidth();
|
||||
float drawableHeight = drawable.getIntrinsicHeight();
|
||||
|
||||
if (mDrawableWidth > 0) {
|
||||
// save scale factor of image
|
||||
if (drawableWidth > mDrawableWidth) {
|
||||
drawableWidth = mDrawableWidth;
|
||||
drawableHeight = drawableWidth * scaleFactor;
|
||||
}
|
||||
}
|
||||
if (mDrawableHeight > 0) {
|
||||
// save scale factor of image
|
||||
if (drawableHeight > mDrawableHeight) {
|
||||
drawableHeight = mDrawableHeight;
|
||||
drawableWidth = drawableHeight / scaleFactor;
|
||||
}
|
||||
}
|
||||
|
||||
realBounds.right = realBounds.left + Math.round(drawableWidth);
|
||||
realBounds.bottom = realBounds.top + Math.round(drawableHeight);
|
||||
|
||||
drawable.setBounds(realBounds);
|
||||
}
|
||||
setCompoundDrawablesRelative(drawables[0], drawables[1], drawables[2], drawables[3]);
|
||||
}
|
||||
|
||||
public void setCompoundDrawablesRelativeWithSize(@Nullable final Drawable start,
|
||||
@Nullable final Drawable top,
|
||||
@Nullable final Drawable end,
|
||||
@Nullable final Drawable bottom) {
|
||||
setCompoundDrawablesRelative(start, top, end, bottom);
|
||||
initCompoundDrawableSize();
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
package awais.instagrabber.customviews;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import androidx.appcompat.widget.AppCompatTextView;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
|
||||
public class UsernameTextView extends AppCompatTextView {
|
||||
private static final String TAG = UsernameTextView.class.getSimpleName();
|
||||
|
||||
private final int drawableSize = Utils.convertDpToPx(24);
|
||||
|
||||
private boolean verified;
|
||||
private VerticalImageSpan verifiedSpan;
|
||||
|
||||
public UsernameTextView(@NonNull final Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public UsernameTextView(@NonNull final Context context, @Nullable final AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public UsernameTextView(@NonNull final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
try {
|
||||
final Drawable verifiedDrawable = AppCompatResources.getDrawable(getContext(), R.drawable.verified);
|
||||
final Drawable drawable = verifiedDrawable.mutate();
|
||||
drawable.setBounds(0, 0, drawableSize, drawableSize);
|
||||
verifiedSpan = new VerticalImageSpan(drawable);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "init: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setUsername(final CharSequence username) {
|
||||
setUsername(username, false);
|
||||
}
|
||||
|
||||
public void setUsername(final CharSequence username, final boolean verified) {
|
||||
this.verified = verified;
|
||||
final SpannableStringBuilder sb = new SpannableStringBuilder(username);
|
||||
if (verified) {
|
||||
try {
|
||||
if (verifiedSpan != null) {
|
||||
sb.append(" ");
|
||||
sb.setSpan(verifiedSpan, sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "bind: ", e);
|
||||
}
|
||||
}
|
||||
super.setText(sb);
|
||||
}
|
||||
|
||||
public boolean isVerified() {
|
||||
return verified;
|
||||
}
|
||||
|
||||
public void setVerified(final boolean verified) {
|
||||
setUsername(getText(), verified);
|
||||
}
|
||||
}
|
@ -1,478 +0,0 @@
|
||||
package awais.instagrabber.fragments;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.res.Resources;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextWatcher;
|
||||
import android.text.style.RelativeSizeSpan;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.LinearLayoutCompat;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.NavController;
|
||||
import androidx.navigation.NavDirections;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.BuildConfig;
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.adapters.CommentsAdapter;
|
||||
import awais.instagrabber.asyncs.CommentsFetcher;
|
||||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
|
||||
import awais.instagrabber.databinding.FragmentCommentsBinding;
|
||||
import awais.instagrabber.interfaces.FetchListener;
|
||||
import awais.instagrabber.models.CommentModel;
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.CookieUtils;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
import awais.instagrabber.viewmodels.CommentsViewModel;
|
||||
import awais.instagrabber.webservices.MediaService;
|
||||
import awais.instagrabber.webservices.ServiceCallback;
|
||||
|
||||
import static android.content.Context.INPUT_METHOD_SERVICE;
|
||||
|
||||
public final class CommentsViewerFragment extends BottomSheetDialogFragment implements SwipeRefreshLayout.OnRefreshListener {
|
||||
private static final String TAG = "CommentsViewerFragment";
|
||||
|
||||
private final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);
|
||||
|
||||
private CommentsAdapter commentsAdapter;
|
||||
private FragmentCommentsBinding binding;
|
||||
private RecyclerLazyLoader lazyLoader;
|
||||
private String shortCode;
|
||||
private long authorUserId, userIdFromCookie;
|
||||
private String endCursor = null;
|
||||
private Resources resources;
|
||||
private InputMethodManager imm;
|
||||
private LinearLayoutCompat root;
|
||||
private boolean shouldRefresh = true, hasNextPage = false;
|
||||
private MediaService mediaService;
|
||||
private String postId;
|
||||
private AsyncTask<Void, Void, List<CommentModel>> currentlyRunning;
|
||||
private CommentsViewModel commentsViewModel;
|
||||
|
||||
private final FetchListener<List<CommentModel>> fetchListener = new FetchListener<List<CommentModel>>() {
|
||||
@Override
|
||||
public void doBefore() {
|
||||
binding.swipeRefreshLayout.setRefreshing(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResult(final List<CommentModel> commentModels) {
|
||||
if (commentModels != null && commentModels.size() > 0) {
|
||||
endCursor = commentModels.get(0).getEndCursor();
|
||||
hasNextPage = commentModels.get(0).hasNextPage();
|
||||
List<CommentModel> list = commentsViewModel.getList().getValue();
|
||||
list = list != null ? new LinkedList<>(list) : new LinkedList<>();
|
||||
// final int oldSize = list != null ? list.size() : 0;
|
||||
list.addAll(commentModels);
|
||||
commentsViewModel.getList().postValue(list);
|
||||
}
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
stopCurrentExecutor(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
stopCurrentExecutor(t);
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
if (userIdFromCookie == 0) return;
|
||||
String replyToId = null;
|
||||
final CommentModel commentModel = commentsAdapter.getSelected();
|
||||
if (commentModel != null) {
|
||||
replyToId = commentModel.getId();
|
||||
}
|
||||
mediaService.comment(postId, text.toString(), replyToId, new ServiceCallback<Boolean>() {
|
||||
@Override
|
||||
public void onSuccess(final Boolean result) {
|
||||
commentsAdapter.clearSelection();
|
||||
binding.commentText.setText("");
|
||||
if (!result) {
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
onRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "Error during comment", t);
|
||||
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
final String deviceUuid = Utils.settingsHelper.getString(Constants.DEVICE_UUID);
|
||||
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
|
||||
userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
|
||||
mediaService = MediaService.getInstance(deviceUuid, csrfToken, userIdFromCookie);
|
||||
// setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {
|
||||
if (root != null) {
|
||||
shouldRefresh = false;
|
||||
return root;
|
||||
}
|
||||
binding = FragmentCommentsBinding.inflate(getLayoutInflater());
|
||||
binding.swipeRefreshLayout.setEnabled(false);
|
||||
binding.swipeRefreshLayout.setNestedScrollingEnabled(false);
|
||||
root = binding.getRoot();
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
|
||||
if (!shouldRefresh) return;
|
||||
init();
|
||||
shouldRefresh = false;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
|
||||
// inflater.inflate(R.menu.follow, menu);
|
||||
// menu.findItem(R.id.action_compare).setVisible(false);
|
||||
// final MenuItem menuSearch = menu.findItem(R.id.action_search);
|
||||
// final SearchView searchView = (SearchView) menuSearch.getActionView();
|
||||
// searchView.setQueryHint(getResources().getString(R.string.action_search));
|
||||
// searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||
// @Override
|
||||
// public boolean onQueryTextSubmit(final String query) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean onQueryTextChange(final String query) {
|
||||
// // if (commentsAdapter != null) commentsAdapter.getFilter().filter(query);
|
||||
// return true;
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
endCursor = null;
|
||||
lazyLoader.resetState();
|
||||
commentsViewModel.getList().postValue(Collections.emptyList());
|
||||
stopCurrentExecutor(null);
|
||||
currentlyRunning = new CommentsFetcher(shortCode, "", fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
private void init() {
|
||||
if (getArguments() == null) return;
|
||||
final CommentsViewerFragmentArgs fragmentArgs = CommentsViewerFragmentArgs.fromBundle(getArguments());
|
||||
shortCode = fragmentArgs.getShortCode();
|
||||
postId = fragmentArgs.getPostId();
|
||||
authorUserId = fragmentArgs.getPostUserId();
|
||||
// setTitle();
|
||||
binding.swipeRefreshLayout.setOnRefreshListener(this);
|
||||
binding.swipeRefreshLayout.setRefreshing(true);
|
||||
commentsViewModel = new ViewModelProvider(this).get(CommentsViewModel.class);
|
||||
final LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
|
||||
binding.rvComments.setLayoutManager(layoutManager);
|
||||
commentsAdapter = new CommentsAdapter(commentCallback);
|
||||
binding.rvComments.setAdapter(commentsAdapter);
|
||||
commentsViewModel.getList().observe(getViewLifecycleOwner(), commentsAdapter::submitList);
|
||||
resources = getResources();
|
||||
if (!TextUtils.isEmpty(cookie)) {
|
||||
binding.commentField.setStartIconVisible(false);
|
||||
binding.commentField.setEndIconVisible(false);
|
||||
binding.commentField.setVisibility(View.VISIBLE);
|
||||
binding.commentText.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
|
||||
binding.commentField.setStartIconVisible(s.length() > 0);
|
||||
binding.commentField.setEndIconVisible(s.length() > 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(final Editable s) {}
|
||||
});
|
||||
binding.commentField.setStartIconOnClickListener(v -> {
|
||||
commentsAdapter.clearSelection();
|
||||
binding.commentText.setText("");
|
||||
});
|
||||
binding.commentField.setEndIconOnClickListener(newCommentListener);
|
||||
}
|
||||
lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
|
||||
if (hasNextPage && !TextUtils.isEmpty(endCursor))
|
||||
currentlyRunning = new CommentsFetcher(shortCode, endCursor, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
endCursor = null;
|
||||
});
|
||||
binding.rvComments.addOnScrollListener(lazyLoader);
|
||||
stopCurrentExecutor(null);
|
||||
onRefresh();
|
||||
}
|
||||
|
||||
// private void setTitle() {
|
||||
// final ActionBar actionBar = fragmentActivity.getSupportActionBar();
|
||||
// if (actionBar == null) return;
|
||||
// actionBar.setTitle(R.string.title_comments);
|
||||
// actionBar.setSubtitle(shortCode);
|
||||
// }
|
||||
|
||||
private void onCommentClick(final CommentModel commentModel) {
|
||||
final String username = commentModel.getProfileModel().getUsername();
|
||||
final SpannableString title = new SpannableString(username + ":\n" + commentModel.getText());
|
||||
title.setSpan(new RelativeSizeSpan(1.23f), 0, username.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
|
||||
String[] commentDialogList;
|
||||
|
||||
if (!TextUtils.isEmpty(cookie)
|
||||
&& userIdFromCookie != 0
|
||||
&& (userIdFromCookie == commentModel.getProfileModel().getPk() || userIdFromCookie == authorUserId)) {
|
||||
commentDialogList = new String[]{
|
||||
resources.getString(R.string.open_profile),
|
||||
resources.getString(R.string.comment_viewer_copy_comment),
|
||||
resources.getString(R.string.comment_viewer_see_likers),
|
||||
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_translate_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.comment_viewer_copy_comment),
|
||||
resources.getString(R.string.comment_viewer_see_likers),
|
||||
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_translate_comment)
|
||||
};
|
||||
} else {
|
||||
commentDialogList = new String[]{
|
||||
resources.getString(R.string.open_profile),
|
||||
resources.getString(R.string.comment_viewer_copy_comment),
|
||||
resources.getString(R.string.comment_viewer_see_likers)
|
||||
};
|
||||
}
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
final DialogInterface.OnClickListener profileDialogListener = (dialog, which) -> {
|
||||
final User profileModel = commentModel.getProfileModel();
|
||||
switch (which) {
|
||||
case 0: // open profile
|
||||
openProfile("@" + profileModel.getUsername());
|
||||
break;
|
||||
case 1: // copy comment
|
||||
Utils.copyText(context, "@" + profileModel.getUsername() + ": " + commentModel.getText());
|
||||
break;
|
||||
case 2: // see comment likers, this is surprisingly available to anons
|
||||
final NavController navController = getNavController();
|
||||
if (navController != null) {
|
||||
final Bundle bundle = new Bundle();
|
||||
bundle.putString("postId", commentModel.getId());
|
||||
bundle.putBoolean("isComment", true);
|
||||
navController.navigate(R.id.action_global_likesViewerFragment, bundle);
|
||||
} else Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
case 3: // reply to comment
|
||||
commentsAdapter.setSelected(commentModel);
|
||||
String mention = "@" + profileModel.getUsername() + " ";
|
||||
binding.commentText.setText(mention);
|
||||
binding.commentText.requestFocus();
|
||||
binding.commentText.setSelection(mention.length());
|
||||
binding.commentText.postDelayed(() -> {
|
||||
imm = (InputMethodManager) context.getSystemService(INPUT_METHOD_SERVICE);
|
||||
if (imm == null) return;
|
||||
imm.showSoftInput(binding.commentText, 0);
|
||||
}, 200);
|
||||
break;
|
||||
case 4: // like/unlike comment
|
||||
if (!commentModel.getLiked()) {
|
||||
mediaService.commentLike(commentModel.getId(), new ServiceCallback<Boolean>() {
|
||||
@Override
|
||||
public void onSuccess(final Boolean result) {
|
||||
if (!result) {
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
commentsAdapter.setLiked(commentModel, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "Error liking comment", t);
|
||||
try {
|
||||
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
} catch (final Throwable ignored) {}
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
mediaService.commentUnlike(commentModel.getId(), new ServiceCallback<Boolean>() {
|
||||
@Override
|
||||
public void onSuccess(final Boolean result) {
|
||||
if (!result) {
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
commentsAdapter.setLiked(commentModel, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "Error unliking comment", t);
|
||||
try {
|
||||
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
} catch (final Throwable ignored) {}
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 5: // translate comment
|
||||
mediaService.translate(commentModel.getId(), "2", new ServiceCallback<String>() {
|
||||
@Override
|
||||
public void onSuccess(final String result) {
|
||||
if (TextUtils.isEmpty(result)) {
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(username)
|
||||
.setMessage(result)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "Error translating comment", t);
|
||||
try {
|
||||
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
} catch (final Throwable ignored) {}
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 6: // delete comment
|
||||
if (userIdFromCookie == 0) return;
|
||||
mediaService.deleteComment(
|
||||
postId, commentModel.getId(),
|
||||
new ServiceCallback<Boolean>() {
|
||||
@Override
|
||||
public void onSuccess(final Boolean result) {
|
||||
if (!result) {
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
onRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "Error deleting comment", t);
|
||||
try {
|
||||
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
} catch (final Throwable ignored) {}
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
};
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(title)
|
||||
.setItems(commentDialogList, profileDialogListener)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void openProfile(final String username) {
|
||||
final NavDirections action = CommentsViewerFragmentDirections.actionGlobalProfileFragment(username);
|
||||
NavHostFragment.findNavController(this).navigate(action);
|
||||
}
|
||||
|
||||
private void stopCurrentExecutor(final Throwable t) {
|
||||
if (currentlyRunning != null) {
|
||||
try {
|
||||
currentlyRunning.cancel(true);
|
||||
} catch (final Exception e) {
|
||||
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
|
||||
}
|
||||
}
|
||||
if (t != null) {
|
||||
try {
|
||||
Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private NavController getNavController() {
|
||||
NavController navController = null;
|
||||
try {
|
||||
navController = NavHostFragment.findNavController(this);
|
||||
} catch (IllegalStateException e) {
|
||||
Log.e(TAG, "navigateToProfile", e);
|
||||
}
|
||||
return navController;
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ import android.widget.Toast;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
@ -34,7 +35,7 @@ import awais.instagrabber.webservices.ServiceCallback;
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public final class LikesViewerFragment extends BottomSheetDialogFragment implements SwipeRefreshLayout.OnRefreshListener {
|
||||
private static final String TAG = "LikesViewerFragment";
|
||||
private static final String TAG = LikesViewerFragment.class.getSimpleName();
|
||||
|
||||
private FragmentLikesBinding binding;
|
||||
private RecyclerLazyLoader lazyLoader;
|
||||
@ -58,6 +59,7 @@ public final class LikesViewerFragment extends BottomSheetDialogFragment impleme
|
||||
});
|
||||
binding.rvLikes.setAdapter(likesAdapter);
|
||||
binding.rvLikes.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
binding.rvLikes.addItemDecoration(new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL));
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
|
||||
@ -71,7 +73,7 @@ public final class LikesViewerFragment extends BottomSheetDialogFragment impleme
|
||||
}
|
||||
};
|
||||
|
||||
private final ServiceCallback<GraphQLUserListFetchResponse> acb = new ServiceCallback<GraphQLUserListFetchResponse>() {
|
||||
private final ServiceCallback<GraphQLUserListFetchResponse> anonCb = new ServiceCallback<GraphQLUserListFetchResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final GraphQLUserListFetchResponse result) {
|
||||
endCursor = result.getNextMaxId();
|
||||
@ -127,7 +129,7 @@ public final class LikesViewerFragment extends BottomSheetDialogFragment impleme
|
||||
public void onRefresh() {
|
||||
if (isComment && !isLoggedIn) {
|
||||
lazyLoader.resetState();
|
||||
graphQLService.fetchCommentLikers(postId, null, acb);
|
||||
graphQLService.fetchCommentLikers(postId, null, anonCb);
|
||||
} else mediaService.fetchLikes(postId, isComment, cb);
|
||||
}
|
||||
|
||||
@ -141,9 +143,10 @@ public final class LikesViewerFragment extends BottomSheetDialogFragment impleme
|
||||
if (isComment && !isLoggedIn) {
|
||||
final LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
|
||||
binding.rvLikes.setLayoutManager(layoutManager);
|
||||
binding.rvLikes.addItemDecoration(new DividerItemDecoration(getContext(), DividerItemDecoration.HORIZONTAL));
|
||||
lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
|
||||
if (!TextUtils.isEmpty(endCursor))
|
||||
graphQLService.fetchCommentLikers(postId, endCursor, acb);
|
||||
graphQLService.fetchCommentLikers(postId, endCursor, anonCb);
|
||||
endCursor = null;
|
||||
});
|
||||
binding.rvLikes.addOnScrollListener(lazyLoader);
|
||||
|
@ -0,0 +1,237 @@
|
||||
package awais.instagrabber.fragments.comments;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.RelativeSizeSpan;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.NavController;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.adapters.CommentsAdapter;
|
||||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
|
||||
import awais.instagrabber.databinding.FragmentCommentsBinding;
|
||||
import awais.instagrabber.models.Comment;
|
||||
import awais.instagrabber.models.Resource;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
import awais.instagrabber.viewmodels.AppStateViewModel;
|
||||
import awais.instagrabber.viewmodels.CommentsViewerViewModel;
|
||||
|
||||
public final class CommentsViewerFragment extends BottomSheetDialogFragment {
|
||||
private static final String TAG = CommentsViewerFragment.class.getSimpleName();
|
||||
|
||||
private CommentsViewerViewModel viewModel;
|
||||
private CommentsAdapter commentsAdapter;
|
||||
private FragmentCommentsBinding binding;
|
||||
private ConstraintLayout root;
|
||||
private boolean shouldRefresh = true;
|
||||
private AppStateViewModel appStateViewModel;
|
||||
private boolean showingReplies;
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
final Dialog dialog = getDialog();
|
||||
if (dialog == null) return;
|
||||
final BottomSheetDialog bottomSheetDialog = (BottomSheetDialog) dialog;
|
||||
final View bottomSheetInternal = bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheet);
|
||||
if (bottomSheetInternal == null) return;
|
||||
bottomSheetInternal.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
bottomSheetInternal.requestLayout();
|
||||
final BottomSheetBehavior<View> behavior = BottomSheetBehavior.from(bottomSheetInternal);
|
||||
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
|
||||
behavior.setSkipCollapsed(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
final FragmentActivity activity = getActivity();
|
||||
if (activity == null) return;
|
||||
viewModel = new ViewModelProvider(this).get(CommentsViewerViewModel.class);
|
||||
appStateViewModel = new ViewModelProvider(activity).get(AppStateViewModel.class);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {
|
||||
return new BottomSheetDialog(getContext(), getTheme()) {
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (showingReplies) {
|
||||
getChildFragmentManager().popBackStack();
|
||||
showingReplies = false;
|
||||
return;
|
||||
}
|
||||
super.onBackPressed();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {
|
||||
if (root != null) {
|
||||
shouldRefresh = false;
|
||||
return root;
|
||||
}
|
||||
binding = FragmentCommentsBinding.inflate(getLayoutInflater());
|
||||
binding.swipeRefreshLayout.setEnabled(false);
|
||||
binding.swipeRefreshLayout.setNestedScrollingEnabled(false);
|
||||
root = binding.getRoot();
|
||||
appStateViewModel.getCurrentUserLiveData().observe(getViewLifecycleOwner(), user -> viewModel.setCurrentUser(user));
|
||||
if (getArguments() == null) return root;
|
||||
final CommentsViewerFragmentArgs args = CommentsViewerFragmentArgs.fromBundle(getArguments());
|
||||
viewModel.setPostDetails(args.getShortCode(), args.getPostId(), args.getPostUserId());
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
|
||||
if (!shouldRefresh) return;
|
||||
shouldRefresh = false;
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
setupToolbar();
|
||||
setupList();
|
||||
setupObservers();
|
||||
}
|
||||
|
||||
private void setupObservers() {
|
||||
viewModel.getCurrentUserId().observe(getViewLifecycleOwner(), currentUserId -> {
|
||||
long userId = 0;
|
||||
if (currentUserId != null) {
|
||||
userId = currentUserId;
|
||||
}
|
||||
setupAdapter(userId);
|
||||
if (userId == 0) return;
|
||||
Helper.setupCommentInput(binding.commentField, binding.commentText, false, text -> {
|
||||
final LiveData<Resource<Object>> resourceLiveData = viewModel.comment(text, false);
|
||||
resourceLiveData.observe(getViewLifecycleOwner(), new Observer<Resource<Object>>() {
|
||||
@Override
|
||||
public void onChanged(final Resource<Object> objectResource) {
|
||||
if (objectResource == null) return;
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
Helper.handleCommentResource(
|
||||
context,
|
||||
objectResource.status,
|
||||
objectResource.message,
|
||||
resourceLiveData,
|
||||
this,
|
||||
binding.commentField,
|
||||
binding.commentText,
|
||||
binding.comments);
|
||||
}
|
||||
});
|
||||
return null;
|
||||
});
|
||||
});
|
||||
viewModel.getRootList().observe(getViewLifecycleOwner(), listResource -> {
|
||||
if (listResource == null) return;
|
||||
switch (listResource.status) {
|
||||
case SUCCESS:
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
if (commentsAdapter != null) {
|
||||
commentsAdapter.submitList(listResource.data);
|
||||
}
|
||||
break;
|
||||
case ERROR:
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
if (!TextUtils.isEmpty(listResource.message)) {
|
||||
Snackbar.make(binding.getRoot(), listResource.message, Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
break;
|
||||
case LOADING:
|
||||
binding.swipeRefreshLayout.setRefreshing(true);
|
||||
break;
|
||||
}
|
||||
});
|
||||
viewModel.getRootCommentsCount().observe(getViewLifecycleOwner(), count -> {
|
||||
if (count == null || count == 0) {
|
||||
binding.toolbar.setTitle(R.string.title_comments);
|
||||
return;
|
||||
}
|
||||
final String titleComments = getString(R.string.title_comments);
|
||||
final String countString = String.valueOf(count);
|
||||
final SpannableString titleWithCount = new SpannableString(String.format("%s %s", titleComments, countString));
|
||||
titleWithCount.setSpan(new RelativeSizeSpan(0.8f),
|
||||
titleWithCount.length() - countString.length(),
|
||||
titleWithCount.length(),
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
binding.toolbar.setTitle(titleWithCount);
|
||||
});
|
||||
}
|
||||
|
||||
private void setupToolbar() {
|
||||
binding.toolbar.setTitle(R.string.title_comments);
|
||||
}
|
||||
|
||||
private void setupAdapter(final long currentUserId) {
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
commentsAdapter = new CommentsAdapter(currentUserId, false, Helper.getCommentCallback(
|
||||
context,
|
||||
getViewLifecycleOwner(),
|
||||
getNavController(),
|
||||
viewModel,
|
||||
(comment, focusInput) -> {
|
||||
if (comment == null) return null;
|
||||
final RepliesFragment repliesFragment = RepliesFragment.newInstance(comment, focusInput == null ? false : focusInput);
|
||||
getChildFragmentManager().beginTransaction()
|
||||
.setCustomAnimations(R.anim.slide_left, R.anim.slide_right, 0, R.anim.slide_right)
|
||||
.add(R.id.replies_container_view, repliesFragment)
|
||||
.addToBackStack(RepliesFragment.TAG)
|
||||
.commit();
|
||||
showingReplies = true;
|
||||
return null;
|
||||
}));
|
||||
final Resource<List<Comment>> listResource = viewModel.getRootList().getValue();
|
||||
binding.comments.setAdapter(commentsAdapter);
|
||||
commentsAdapter.submitList(listResource != null ? listResource.data : Collections.emptyList());
|
||||
}
|
||||
|
||||
private void setupList() {
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
final LinearLayoutManager layoutManager = new LinearLayoutManager(context);
|
||||
final RecyclerLazyLoader lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> viewModel.fetchComments());
|
||||
Helper.setupList(context, binding.comments, layoutManager, lazyLoader);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private NavController getNavController() {
|
||||
NavController navController = null;
|
||||
try {
|
||||
navController = NavHostFragment.findNavController(this);
|
||||
} catch (IllegalStateException e) {
|
||||
Log.e(TAG, "navigateToProfile", e);
|
||||
}
|
||||
return navController;
|
||||
}
|
||||
}
|
@ -0,0 +1,288 @@
|
||||
package awais.instagrabber.fragments.comments;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.navigation.NavController;
|
||||
import androidx.navigation.NavDirections;
|
||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.android.material.internal.CheckableImageButton;
|
||||
import com.google.android.material.textfield.TextInputEditText;
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.adapters.CommentsAdapter.CommentCallback;
|
||||
import awais.instagrabber.customviews.helpers.TextWatcherAdapter;
|
||||
import awais.instagrabber.models.Comment;
|
||||
import awais.instagrabber.models.Resource;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
import awais.instagrabber.viewmodels.CommentsViewerViewModel;
|
||||
import awais.instagrabber.webservices.ServiceCallback;
|
||||
|
||||
public final class Helper {
|
||||
private static final String TAG = Helper.class.getSimpleName();
|
||||
|
||||
public static void setupList(@NonNull final Context context,
|
||||
@NonNull final RecyclerView list,
|
||||
@NonNull final RecyclerView.LayoutManager layoutManager,
|
||||
@NonNull final RecyclerView.OnScrollListener lazyLoader) {
|
||||
list.setLayoutManager(layoutManager);
|
||||
final DividerItemDecoration itemDecoration = new DividerItemDecoration(context, LinearLayoutManager.VERTICAL);
|
||||
itemDecoration.setDrawable(ContextCompat.getDrawable(context, R.drawable.pref_list_divider_material));
|
||||
list.addItemDecoration(itemDecoration);
|
||||
list.addOnScrollListener(lazyLoader);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static CommentCallback getCommentCallback(@NonNull final Context context,
|
||||
final LifecycleOwner lifecycleOwner,
|
||||
final NavController navController,
|
||||
@NonNull final CommentsViewerViewModel viewModel,
|
||||
final BiFunction<Comment, Boolean, Void> onRepliesClick) {
|
||||
return new CommentCallback() {
|
||||
@Override
|
||||
public void onClick(final Comment comment) {
|
||||
// onCommentClick(comment);
|
||||
if (onRepliesClick == null) return;
|
||||
onRepliesClick.apply(comment, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHashtagClick(final String hashtag) {
|
||||
try {
|
||||
if (navController == null) return;
|
||||
final NavDirections action = CommentsViewerFragmentDirections.actionGlobalHashTagFragment(hashtag);
|
||||
navController.navigate(action);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "onHashtagClick: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMentionClick(final String mention) {
|
||||
openProfile(navController, mention);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onURLClick(final String url) {
|
||||
Utils.openURL(context, url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEmailClick(final String emailAddress) {
|
||||
Utils.openEmailAddress(context, emailAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLikeClick(final Comment comment, final boolean liked, final boolean isReply) {
|
||||
if (comment == null) return;
|
||||
final LiveData<Resource<Object>> resourceLiveData = viewModel.likeComment(comment, liked, isReply);
|
||||
resourceLiveData.observe(lifecycleOwner, new Observer<Resource<Object>>() {
|
||||
@Override
|
||||
public void onChanged(final Resource<Object> objectResource) {
|
||||
if (objectResource == null) return;
|
||||
switch (objectResource.status) {
|
||||
case SUCCESS:
|
||||
resourceLiveData.removeObserver(this);
|
||||
break;
|
||||
case LOADING:
|
||||
break;
|
||||
case ERROR:
|
||||
if (objectResource.message != null) {
|
||||
Toast.makeText(context, objectResource.message, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
resourceLiveData.removeObserver(this);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRepliesClick(final Comment comment) {
|
||||
// viewModel.showReplies(comment);
|
||||
if (onRepliesClick == null) return;
|
||||
onRepliesClick.apply(comment, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewLikes(final Comment comment) {
|
||||
if (navController == null) return;
|
||||
try {
|
||||
final Bundle bundle = new Bundle();
|
||||
bundle.putString("postId", comment.getId());
|
||||
bundle.putBoolean("isComment", true);
|
||||
navController.navigate(R.id.action_global_likesViewerFragment, bundle);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "onViewLikes: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTranslate(final Comment comment) {
|
||||
if (comment == null) return;
|
||||
viewModel.translate(comment, new ServiceCallback<String>() {
|
||||
@Override
|
||||
public void onSuccess(final String result) {
|
||||
if (TextUtils.isEmpty(result)) {
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
String username = "";
|
||||
if (comment.getUser() != null) {
|
||||
username = comment.getUser().getUsername();
|
||||
}
|
||||
new MaterialAlertDialogBuilder(context)
|
||||
.setTitle(username)
|
||||
.setMessage(result)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "Error translating comment", t);
|
||||
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDelete(final Comment comment, final boolean isReply) {
|
||||
if (comment == null) return;
|
||||
final LiveData<Resource<Object>> resourceLiveData = viewModel.deleteComment(comment, isReply);
|
||||
resourceLiveData.observe(lifecycleOwner, new Observer<Resource<Object>>() {
|
||||
@Override
|
||||
public void onChanged(final Resource<Object> objectResource) {
|
||||
if (objectResource == null) return;
|
||||
switch (objectResource.status) {
|
||||
case SUCCESS:
|
||||
resourceLiveData.removeObserver(this);
|
||||
break;
|
||||
case ERROR:
|
||||
if (objectResource.message != null) {
|
||||
Toast.makeText(context, objectResource.message, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
resourceLiveData.removeObserver(this);
|
||||
break;
|
||||
case LOADING:
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static void openProfile(final NavController navController,
|
||||
@NonNull final String username) {
|
||||
if (navController == null) return;
|
||||
try {
|
||||
final NavDirections action = CommentsViewerFragmentDirections.actionGlobalProfileFragment(username);
|
||||
navController.navigate(action);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "openProfile: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setupCommentInput(@NonNull final TextInputLayout commentField,
|
||||
@NonNull final TextInputEditText commentText,
|
||||
final boolean isReplyFragment,
|
||||
@NonNull final Function<String, Void> commentFunction) {
|
||||
// commentField.setStartIconVisible(false);
|
||||
commentField.setVisibility(View.VISIBLE);
|
||||
commentField.setEndIconVisible(false);
|
||||
if (isReplyFragment) {
|
||||
commentField.setHint(R.string.reply_hint);
|
||||
}
|
||||
commentText.addTextChangedListener(new TextWatcherAdapter() {
|
||||
@Override
|
||||
public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
|
||||
final boolean isEmpty = TextUtils.isEmpty(s);
|
||||
commentField.setStartIconVisible(!isEmpty);
|
||||
commentField.setEndIconVisible(!isEmpty);
|
||||
commentField.setCounterEnabled(s != null && s.length() > 2000); // show the counter when user approaches the limit
|
||||
}
|
||||
});
|
||||
// commentField.setStartIconOnClickListener(v -> {
|
||||
// // commentsAdapter.clearSelection();
|
||||
// commentText.setText("");
|
||||
// });
|
||||
commentField.setEndIconOnClickListener(v -> {
|
||||
final Editable text = commentText.getText();
|
||||
if (TextUtils.isEmpty(text)) return;
|
||||
commentFunction.apply(text.toString().trim());
|
||||
});
|
||||
}
|
||||
|
||||
public static void handleCommentResource(@NonNull final Context context,
|
||||
@NonNull final Resource.Status status,
|
||||
final String message,
|
||||
@NonNull final LiveData<Resource<Object>> resourceLiveData,
|
||||
@NonNull final Observer<Resource<Object>> observer,
|
||||
@NonNull final TextInputLayout commentField,
|
||||
@NonNull final TextInputEditText commentText,
|
||||
@NonNull final RecyclerView comments) {
|
||||
CheckableImageButton endIcon = null;
|
||||
try {
|
||||
endIcon = (CheckableImageButton) commentField.findViewById(com.google.android.material.R.id.text_input_end_icon);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "setupObservers: ", e);
|
||||
}
|
||||
CheckableImageButton startIcon = null;
|
||||
try {
|
||||
startIcon = (CheckableImageButton) commentField.findViewById(com.google.android.material.R.id.text_input_start_icon);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "setupObservers: ", e);
|
||||
}
|
||||
switch (status) {
|
||||
case SUCCESS:
|
||||
resourceLiveData.removeObserver(observer);
|
||||
comments.postDelayed(() -> comments.scrollToPosition(0), 500);
|
||||
if (startIcon != null) {
|
||||
startIcon.setEnabled(true);
|
||||
}
|
||||
if (endIcon != null) {
|
||||
endIcon.setEnabled(true);
|
||||
}
|
||||
commentText.setText("");
|
||||
break;
|
||||
case LOADING:
|
||||
commentText.setEnabled(false);
|
||||
if (startIcon != null) {
|
||||
startIcon.setEnabled(false);
|
||||
}
|
||||
if (endIcon != null) {
|
||||
endIcon.setEnabled(false);
|
||||
}
|
||||
break;
|
||||
case ERROR:
|
||||
if (message != null && context != null) {
|
||||
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
if (startIcon != null) {
|
||||
startIcon.setEnabled(true);
|
||||
}
|
||||
if (endIcon != null) {
|
||||
endIcon.setEnabled(true);
|
||||
}
|
||||
resourceLiveData.removeObserver(observer);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,214 @@
|
||||
package awais.instagrabber.fragments.comments;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.NavController;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.adapters.CommentsAdapter;
|
||||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
|
||||
import awais.instagrabber.databinding.FragmentCommentsBinding;
|
||||
import awais.instagrabber.models.Comment;
|
||||
import awais.instagrabber.models.Resource;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
import awais.instagrabber.viewmodels.CommentsViewerViewModel;
|
||||
|
||||
public class RepliesFragment extends Fragment {
|
||||
public static final String TAG = RepliesFragment.class.getSimpleName();
|
||||
private static final String ARG_PARENT = "parent";
|
||||
private static final String ARG_FOCUS_INPUT = "focus";
|
||||
|
||||
private FragmentCommentsBinding binding;
|
||||
private CommentsViewerViewModel viewModel;
|
||||
private CommentsAdapter commentsAdapter;
|
||||
|
||||
@NonNull
|
||||
public static RepliesFragment newInstance(@NonNull final Comment parent,
|
||||
final boolean focusInput) {
|
||||
final Bundle args = new Bundle();
|
||||
args.putSerializable(ARG_PARENT, parent);
|
||||
args.putBoolean(ARG_FOCUS_INPUT, focusInput);
|
||||
final RepliesFragment fragment = new RepliesFragment();
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(getParentFragment()).get(CommentsViewerViewModel.class);
|
||||
final Bundle bundle = getArguments();
|
||||
if (bundle == null) return;
|
||||
final Serializable serializable = bundle.getSerializable(ARG_PARENT);
|
||||
if (!(serializable instanceof Comment)) return;
|
||||
viewModel.showReplies((Comment) serializable);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {
|
||||
binding = FragmentCommentsBinding.inflate(inflater, container, false);
|
||||
binding.swipeRefreshLayout.setEnabled(false);
|
||||
binding.swipeRefreshLayout.setNestedScrollingEnabled(false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
|
||||
setupToolbar();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
|
||||
if (!enter || nextAnim == 0) {
|
||||
return super.onCreateAnimation(transit, enter, nextAnim);
|
||||
}
|
||||
final Animation animation = AnimationUtils.loadAnimation(getContext(), nextAnim);
|
||||
animation.setAnimationListener(new Animation.AnimationListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
setupList();
|
||||
setupObservers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {}
|
||||
});
|
||||
return animation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
viewModel.clearReplies();
|
||||
}
|
||||
|
||||
private void setupObservers() {
|
||||
viewModel.getCurrentUserId().observe(getViewLifecycleOwner(), currentUserId -> {
|
||||
long userId = 0;
|
||||
if (currentUserId != null) {
|
||||
userId = currentUserId;
|
||||
}
|
||||
setupAdapter(userId);
|
||||
if (userId == 0) return;
|
||||
Helper.setupCommentInput(binding.commentField, binding.commentText, true, text -> {
|
||||
final LiveData<Resource<Object>> resourceLiveData = viewModel.comment(text, true);
|
||||
resourceLiveData.observe(getViewLifecycleOwner(), new Observer<Resource<Object>>() {
|
||||
@Override
|
||||
public void onChanged(final Resource<Object> objectResource) {
|
||||
if (objectResource == null) return;
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
Helper.handleCommentResource(context,
|
||||
objectResource.status,
|
||||
objectResource.message,
|
||||
resourceLiveData,
|
||||
this,
|
||||
binding.commentField,
|
||||
binding.commentText,
|
||||
binding.comments);
|
||||
}
|
||||
});
|
||||
return null;
|
||||
});
|
||||
final Bundle bundle = getArguments();
|
||||
if (bundle == null) return;
|
||||
final boolean focusInput = bundle.getBoolean(ARG_FOCUS_INPUT);
|
||||
if (focusInput && viewModel.getRepliesParent() != null && viewModel.getRepliesParent().getUser() != null) {
|
||||
binding.commentText.setText(String.format("@%s ", viewModel.getRepliesParent().getUser().getUsername()));
|
||||
Utils.showKeyboard(binding.commentText);
|
||||
}
|
||||
});
|
||||
viewModel.getReplyList().observe(getViewLifecycleOwner(), listResource -> {
|
||||
if (listResource == null) return;
|
||||
switch (listResource.status) {
|
||||
case SUCCESS:
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
if (commentsAdapter != null) {
|
||||
commentsAdapter.submitList(listResource.data);
|
||||
}
|
||||
break;
|
||||
case ERROR:
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
if (!TextUtils.isEmpty(listResource.message)) {
|
||||
Snackbar.make(binding.getRoot(), listResource.message, Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
break;
|
||||
case LOADING:
|
||||
binding.swipeRefreshLayout.setRefreshing(true);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupToolbar() {
|
||||
binding.toolbar.setTitle("Replies");
|
||||
binding.toolbar.setNavigationIcon(R.drawable.ic_round_arrow_back_24);
|
||||
binding.toolbar.setNavigationOnClickListener(v -> {
|
||||
final FragmentManager fragmentManager = getParentFragmentManager();
|
||||
fragmentManager.popBackStack();
|
||||
});
|
||||
}
|
||||
|
||||
private void setupAdapter(final long currentUserId) {
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
commentsAdapter = new CommentsAdapter(currentUserId,
|
||||
true,
|
||||
Helper.getCommentCallback(context, getViewLifecycleOwner(), getNavController(), viewModel, null));
|
||||
binding.comments.setAdapter(commentsAdapter);
|
||||
final Resource<List<Comment>> listResource = viewModel.getReplyList().getValue();
|
||||
commentsAdapter.submitList(listResource != null ? listResource.data : Collections.emptyList());
|
||||
}
|
||||
|
||||
private void setupList() {
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
final LinearLayoutManager layoutManager = new LinearLayoutManager(context);
|
||||
final RecyclerLazyLoader lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> viewModel.fetchReplies());
|
||||
Helper.setupList(context, binding.comments, layoutManager, lazyLoader);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private NavController getNavController() {
|
||||
NavController navController = null;
|
||||
try {
|
||||
navController = NavHostFragment.findNavController(this);
|
||||
} catch (IllegalStateException e) {
|
||||
Log.e(TAG, "navigateToProfile", e);
|
||||
}
|
||||
return navController;
|
||||
}
|
||||
}
|
@ -514,7 +514,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@SuppressLint("UnsafeExperimentalUsageError")
|
||||
@SuppressLint("UnsafeOptInUsageError")
|
||||
private void cleanup() {
|
||||
if (prevTitleRunnable != null) {
|
||||
appExecutors.mainThread().cancel(prevTitleRunnable);
|
||||
@ -840,7 +840,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("UnsafeExperimentalUsageError")
|
||||
@SuppressLint("UnsafeOptInUsageError")
|
||||
private void attachPendingRequestsBadge(@Nullable final Integer count) {
|
||||
if (pendingRequestCountBadgeDrawable == null) {
|
||||
final Context context = getContext();
|
||||
|
@ -111,12 +111,16 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
|
||||
|
||||
@Override
|
||||
public void onCommentsClick(final Media feedModel) {
|
||||
final NavDirections commentsAction = FeedFragmentDirections.actionGlobalCommentsViewerFragment(
|
||||
feedModel.getCode(),
|
||||
feedModel.getPk(),
|
||||
feedModel.getUser().getPk()
|
||||
);
|
||||
NavHostFragment.findNavController(FeedFragment.this).navigate(commentsAction);
|
||||
try {
|
||||
final NavDirections commentsAction = FeedFragmentDirections.actionGlobalCommentsViewerFragment(
|
||||
feedModel.getCode(),
|
||||
feedModel.getPk(),
|
||||
feedModel.getUser().getPk()
|
||||
);
|
||||
NavHostFragment.findNavController(FeedFragment.this).navigate(commentsAction);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "onCommentsClick: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -188,7 +188,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
|
||||
@Override
|
||||
public void onCommentsClick(final Media feedModel) {
|
||||
final NavDirections commentsAction = FeedFragmentDirections.actionGlobalCommentsViewerFragment(
|
||||
final NavDirections commentsAction = ProfileFragmentDirections.actionGlobalCommentsViewerFragment(
|
||||
feedModel.getCode(),
|
||||
feedModel.getPk(),
|
||||
feedModel.getUser().getPk()
|
||||
@ -991,7 +991,6 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
if (chainingMenuItem != null) {
|
||||
chainingMenuItem.setVisible(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
117
app/src/main/java/awais/instagrabber/models/Comment.java
Normal file
117
app/src/main/java/awais/instagrabber/models/Comment.java
Normal file
@ -0,0 +1,117 @@
|
||||
package awais.instagrabber.models;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
|
||||
public class Comment implements Serializable, Cloneable {
|
||||
private final User user;
|
||||
private final String id;
|
||||
private final String text;
|
||||
private long likes;
|
||||
private final long timestamp;
|
||||
private boolean liked;
|
||||
private final int replyCount;
|
||||
private final boolean isChild;
|
||||
|
||||
public Comment(final String id,
|
||||
final String text,
|
||||
final long timestamp,
|
||||
final long likes,
|
||||
final boolean liked,
|
||||
final User user,
|
||||
final int replyCount, final boolean isChild) {
|
||||
this.id = id;
|
||||
this.text = text;
|
||||
this.likes = likes;
|
||||
this.liked = liked;
|
||||
this.timestamp = timestamp;
|
||||
this.user = user;
|
||||
this.replyCount = replyCount;
|
||||
this.isChild = isChild;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getDateTime() {
|
||||
return Utils.datetimeParser.format(new Date(timestamp * 1000L));
|
||||
}
|
||||
|
||||
public long getLikes() {
|
||||
return likes;
|
||||
}
|
||||
|
||||
public boolean getLiked() {
|
||||
return liked;
|
||||
}
|
||||
|
||||
public void setLiked(boolean liked) {
|
||||
this.likes = liked ? likes + 1 : likes - 1;
|
||||
this.liked = liked;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public int getReplyCount() {
|
||||
return replyCount;
|
||||
}
|
||||
|
||||
public boolean isChild() {
|
||||
return isChild;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final Comment comment = (Comment) o;
|
||||
return likes == comment.likes &&
|
||||
timestamp == comment.timestamp &&
|
||||
liked == comment.liked &&
|
||||
replyCount == comment.replyCount &&
|
||||
Objects.equals(user, comment.user) &&
|
||||
Objects.equals(id, comment.id) &&
|
||||
Objects.equals(text, comment.text) &&
|
||||
isChild == comment.isChild;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(user, id, text, likes, timestamp, liked, replyCount, isChild);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Comment{" +
|
||||
"user=" + user +
|
||||
", id='" + id + '\'' +
|
||||
", text='" + text + '\'' +
|
||||
", likes=" + likes +
|
||||
", timestamp=" + timestamp +
|
||||
", liked=" + liked +
|
||||
", replyCount" + replyCount +
|
||||
", isChild" + isChild +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
package awais.instagrabber.models;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
|
||||
public class CommentModel {
|
||||
private final User profileModel;
|
||||
private final String id;
|
||||
private final String text;
|
||||
private long likes;
|
||||
private final long timestamp;
|
||||
private List<CommentModel> childCommentModels;
|
||||
private boolean liked, hasNextPage;
|
||||
private String endCursor;
|
||||
|
||||
public CommentModel(final String id,
|
||||
final String text,
|
||||
final long timestamp,
|
||||
final long likes,
|
||||
final boolean liked,
|
||||
final User profileModel) {
|
||||
this.id = id;
|
||||
this.text = text;
|
||||
this.likes = likes;
|
||||
this.liked = liked;
|
||||
this.timestamp = timestamp;
|
||||
this.profileModel = profileModel;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getDateTime() {
|
||||
return Utils.datetimeParser.format(new Date(timestamp * 1000L));
|
||||
}
|
||||
|
||||
public long getLikes() {
|
||||
return likes;
|
||||
}
|
||||
|
||||
public boolean getLiked() {
|
||||
return liked;
|
||||
}
|
||||
|
||||
public void setLiked(boolean liked) {
|
||||
this.likes = liked ? likes + 1 : likes - 1;
|
||||
this.liked = liked;
|
||||
}
|
||||
|
||||
public User getProfileModel() {
|
||||
return profileModel;
|
||||
}
|
||||
|
||||
public List<CommentModel> getChildCommentModels() {
|
||||
return childCommentModels;
|
||||
}
|
||||
|
||||
public void setChildCommentModels(final List<CommentModel> childCommentModels) {
|
||||
this.childCommentModels = childCommentModels;
|
||||
}
|
||||
|
||||
public void setPageCursor(final boolean hasNextPage, final String endCursor) {
|
||||
this.hasNextPage = hasNextPage;
|
||||
this.endCursor = endCursor;
|
||||
}
|
||||
|
||||
public boolean hasNextPage() {
|
||||
return hasNextPage;
|
||||
}
|
||||
|
||||
public String getEndCursor() {
|
||||
return endCursor;
|
||||
}
|
||||
|
||||
// @NonNull
|
||||
// @Override
|
||||
// public String toString() {
|
||||
// try {
|
||||
// final JSONObject object = new JSONObject();
|
||||
// object.put(Constants.EXTRAS_ID, id);
|
||||
// object.put("text", text);
|
||||
// object.put(Constants.EXTRAS_NAME, profileModel != null ? profileModel.getUsername() : "");
|
||||
// if (childCommentModels != null) object.put("childComments", childCommentModels);
|
||||
// return object.toString();
|
||||
// } catch (Exception e) {
|
||||
// return "{\"id\":\"" + id + "\", \"text\":\"" + text
|
||||
// //(text != null ? text.replaceAll("\"", "\\\\\"") : "")
|
||||
// + "\", \"name\":\"" + (profileModel != null ? profileModel.getUsername() : "") +
|
||||
// (childCommentModels != null ? "\", \"childComments\":" + childCommentModels.length : "\"") + '}';
|
||||
// }
|
||||
// }
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package awais.instagrabber.repositories.responses;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.models.Comment;
|
||||
|
||||
public class GraphQLCommentsFetchResponse {
|
||||
private final int count;
|
||||
private final String cursor;
|
||||
private final boolean hasNext;
|
||||
private final List<Comment> comments;
|
||||
|
||||
public GraphQLCommentsFetchResponse(final int count,
|
||||
final String cursor,
|
||||
final boolean hasNext,
|
||||
final List<Comment> comments) {
|
||||
this.count = count;
|
||||
this.cursor = cursor;
|
||||
this.hasNext = hasNext;
|
||||
this.comments = comments;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public String getCursor() {
|
||||
return cursor;
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return hasNext;
|
||||
}
|
||||
|
||||
public List<Comment> getComments() {
|
||||
return comments;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GraphQLCommentsFetchResponse{" +
|
||||
"count=" + count +
|
||||
", cursor='" + cursor + '\'' +
|
||||
", hasNext=" + hasNext +
|
||||
", comments=" + comments +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -347,6 +347,18 @@ public final class Utils {
|
||||
);
|
||||
}
|
||||
|
||||
public static void showKeyboard(@NonNull final View view) {
|
||||
final Context context = view.getContext();
|
||||
if (context == null) return;
|
||||
final InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
if (imm == null) return;
|
||||
view.requestFocus();
|
||||
final boolean shown = imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
|
||||
if (!shown) {
|
||||
Log.e(TAG, "showKeyboard: System did not display the keyboard");
|
||||
}
|
||||
}
|
||||
|
||||
public static void hideKeyboard(final View view) {
|
||||
if (view == null) return;
|
||||
final Context context = view.getContext();
|
||||
|
@ -4,6 +4,7 @@ import android.app.Application;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.lifecycle.AndroidViewModel;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
@ -36,6 +37,7 @@ public class AppStateViewModel extends AndroidViewModel {
|
||||
fetchProfileDetails();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public User getCurrentUser() {
|
||||
return currentUser.getValue();
|
||||
}
|
||||
|
@ -1,19 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,447 @@
|
||||
package awais.instagrabber.viewmodels;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.models.Comment;
|
||||
import awais.instagrabber.models.Resource;
|
||||
import awais.instagrabber.repositories.responses.FriendshipStatus;
|
||||
import awais.instagrabber.repositories.responses.GraphQLCommentsFetchResponse;
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.CookieUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
import awais.instagrabber.webservices.GraphQLService;
|
||||
import awais.instagrabber.webservices.MediaService;
|
||||
import awais.instagrabber.webservices.ServiceCallback;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public class CommentsViewerViewModel extends ViewModel {
|
||||
private static final String TAG = CommentsViewerViewModel.class.getSimpleName();
|
||||
|
||||
private final MutableLiveData<Boolean> isLoggedIn = new MutableLiveData<>(false);
|
||||
private final MutableLiveData<Long> currentUserId = new MutableLiveData<>(0L);
|
||||
private final MutableLiveData<Resource<List<Comment>>> rootList = new MutableLiveData<>();
|
||||
private final MutableLiveData<Integer> rootCount = new MutableLiveData<>(0);
|
||||
private final MutableLiveData<Resource<List<Comment>>> replyList = new MutableLiveData<>();
|
||||
private final GraphQLService service;
|
||||
|
||||
private String shortCode;
|
||||
private String postId;
|
||||
private String rootCursor;
|
||||
private boolean rootHasNext = true;
|
||||
private Comment repliesParent;
|
||||
private String repliesCursor;
|
||||
private boolean repliesHasNext = true;
|
||||
private final MediaService mediaService;
|
||||
private List<Comment> prevReplies;
|
||||
private String prevRepliesCursor;
|
||||
private boolean prevRepliesHasNext = true;
|
||||
|
||||
public CommentsViewerViewModel() {
|
||||
service = GraphQLService.getInstance();
|
||||
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||
final String deviceUuid = Utils.settingsHelper.getString(Constants.DEVICE_UUID);
|
||||
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
|
||||
final long userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
|
||||
mediaService = MediaService.getInstance(deviceUuid, csrfToken, userIdFromCookie);
|
||||
}
|
||||
|
||||
public void setCurrentUser(final User currentUser) {
|
||||
isLoggedIn.postValue(currentUser != null);
|
||||
currentUserId.postValue(currentUser == null ? 0 : currentUser.getPk());
|
||||
}
|
||||
|
||||
public void setPostDetails(final String shortCode, final String postId, final long postUserId) {
|
||||
this.shortCode = shortCode;
|
||||
this.postId = postId;
|
||||
fetchComments();
|
||||
}
|
||||
|
||||
public LiveData<Boolean> isLoggedIn() {
|
||||
return isLoggedIn;
|
||||
}
|
||||
|
||||
public LiveData<Long> getCurrentUserId() {
|
||||
return currentUserId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Comment getRepliesParent() {
|
||||
return repliesParent;
|
||||
}
|
||||
|
||||
public LiveData<Resource<List<Comment>>> getRootList() {
|
||||
return rootList;
|
||||
}
|
||||
|
||||
public LiveData<Resource<List<Comment>>> getReplyList() {
|
||||
return replyList;
|
||||
}
|
||||
|
||||
public LiveData<Integer> getRootCommentsCount() {
|
||||
return rootCount;
|
||||
}
|
||||
|
||||
public void fetchComments() {
|
||||
if (shortCode == null) return;
|
||||
fetchComments(shortCode, true);
|
||||
}
|
||||
|
||||
public void fetchReplies() {
|
||||
if (repliesParent == null) return;
|
||||
fetchReplies(repliesParent.getId());
|
||||
}
|
||||
|
||||
public void fetchReplies(@NonNull final String commentId) {
|
||||
fetchComments(commentId, false);
|
||||
}
|
||||
|
||||
public void fetchComments(@NonNull final String shortCodeOrCommentId,
|
||||
final boolean root) {
|
||||
if (root) {
|
||||
if (!rootHasNext) return;
|
||||
rootList.postValue(Resource.loading(getPrevList(rootList)));
|
||||
} else {
|
||||
if (!repliesHasNext) return;
|
||||
final List<Comment> list;
|
||||
if (repliesParent != null && !Objects.equals(repliesParent.getId(), shortCodeOrCommentId)) {
|
||||
repliesCursor = null;
|
||||
repliesHasNext = false;
|
||||
list = Collections.emptyList();
|
||||
} else {
|
||||
list = getPrevList(replyList);
|
||||
}
|
||||
replyList.postValue(Resource.loading(list));
|
||||
}
|
||||
final Call<String> request = service.fetchComments(shortCodeOrCommentId, root, root ? rootCursor : repliesCursor);
|
||||
enqueueRequest(request, root, shortCodeOrCommentId, new ServiceCallback<GraphQLCommentsFetchResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final GraphQLCommentsFetchResponse result) {
|
||||
// Log.d(TAG, "onSuccess: " + result);
|
||||
List<Comment> comments = result.getComments();
|
||||
if (root) {
|
||||
if (rootCursor == null) {
|
||||
rootCount.postValue(result.getCount());
|
||||
}
|
||||
if (rootCursor != null) {
|
||||
comments = mergeList(rootList, comments);
|
||||
}
|
||||
rootCursor = result.getCursor();
|
||||
rootHasNext = result.hasNext();
|
||||
rootList.postValue(Resource.success(comments));
|
||||
return;
|
||||
}
|
||||
// Replies
|
||||
if (repliesCursor == null) {
|
||||
// add parent to top of replies
|
||||
comments = ImmutableList.<Comment>builder()
|
||||
.add(repliesParent)
|
||||
.addAll(comments)
|
||||
.build();
|
||||
}
|
||||
if (repliesCursor != null) {
|
||||
comments = mergeList(replyList, comments);
|
||||
}
|
||||
repliesCursor = result.getCursor();
|
||||
repliesHasNext = result.hasNext();
|
||||
replyList.postValue(Resource.success(comments));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "onFailure: ", t);
|
||||
if (root) {
|
||||
rootList.postValue(Resource.error(t.getMessage(), getPrevList(rootList)));
|
||||
return;
|
||||
}
|
||||
replyList.postValue(Resource.error(t.getMessage(), getPrevList(replyList)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void enqueueRequest(@NonNull final Call<String> request,
|
||||
final boolean root,
|
||||
final String shortCodeOrCommentId,
|
||||
final ServiceCallback<GraphQLCommentsFetchResponse> callback) {
|
||||
request.enqueue(new Callback<String>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
||||
final String rawBody = response.body();
|
||||
if (rawBody == null) {
|
||||
Log.e(TAG, "Error occurred while fetching gql comments of " + shortCodeOrCommentId);
|
||||
callback.onSuccess(null);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final JSONObject body = root ? new JSONObject(rawBody).getJSONObject("data")
|
||||
.getJSONObject("shortcode_media")
|
||||
.getJSONObject("edge_media_to_parent_comment")
|
||||
: new JSONObject(rawBody).getJSONObject("data")
|
||||
.getJSONObject("comment")
|
||||
.getJSONObject("edge_threaded_comments");
|
||||
final int count = body.optInt("count");
|
||||
final JSONObject pageInfo = body.getJSONObject("page_info");
|
||||
final boolean hasNextPage = pageInfo.getBoolean("has_next_page");
|
||||
final String endCursor = pageInfo.isNull("end_cursor") ? null : pageInfo.optString("end_cursor");
|
||||
final JSONArray commentsJsonArray = body.getJSONArray("edges");
|
||||
final ImmutableList.Builder<Comment> builder = ImmutableList.builder();
|
||||
for (int i = 0; i < commentsJsonArray.length(); i++) {
|
||||
final Comment commentModel = getComment(commentsJsonArray.getJSONObject(i).getJSONObject("node"), root);
|
||||
builder.add(commentModel);
|
||||
}
|
||||
callback.onSuccess(new GraphQLCommentsFetchResponse(count, endCursor, hasNextPage, builder.build()));
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "onResponse", e);
|
||||
callback.onFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
||||
callback.onFailure(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Comment getComment(@NonNull final JSONObject commentJsonObject, final boolean root) throws JSONException {
|
||||
final JSONObject owner = commentJsonObject.getJSONObject("owner");
|
||||
final User user = new User(
|
||||
owner.optLong(Constants.EXTRAS_ID, 0),
|
||||
owner.getString(Constants.EXTRAS_USERNAME),
|
||||
null,
|
||||
false,
|
||||
owner.getString("profile_pic_url"),
|
||||
null,
|
||||
new FriendshipStatus(false, false, false, false, false, false, false, false, false, false),
|
||||
owner.optBoolean("is_verified"),
|
||||
false, false, false, false, null, null, 0, 0, 0, 0, null, null, 0, null, null, null, null,
|
||||
null, null);
|
||||
final JSONObject likedBy = commentJsonObject.optJSONObject("edge_liked_by");
|
||||
final String commentId = commentJsonObject.getString("id");
|
||||
final JSONObject childCommentsJsonObject = commentJsonObject.optJSONObject("edge_threaded_comments");
|
||||
int replyCount = 0;
|
||||
if (childCommentsJsonObject != null) {
|
||||
replyCount = childCommentsJsonObject.optInt("count");
|
||||
}
|
||||
return new Comment(commentId,
|
||||
commentJsonObject.getString("text"),
|
||||
commentJsonObject.getLong("created_at"),
|
||||
likedBy != null ? likedBy.optLong("count", 0) : 0,
|
||||
commentJsonObject.getBoolean("viewer_has_liked"),
|
||||
user,
|
||||
replyCount,
|
||||
!root);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<Comment> getPrevList(@NonNull final LiveData<Resource<List<Comment>>> list) {
|
||||
if (list.getValue() == null) return Collections.emptyList();
|
||||
final Resource<List<Comment>> listResource = list.getValue();
|
||||
if (listResource.data == null) return Collections.emptyList();
|
||||
return listResource.data;
|
||||
}
|
||||
|
||||
private List<Comment> mergeList(@NonNull final LiveData<Resource<List<Comment>>> list,
|
||||
final List<Comment> comments) {
|
||||
final List<Comment> prevList = getPrevList(list);
|
||||
if (comments == null) {
|
||||
return prevList;
|
||||
}
|
||||
return ImmutableList.<Comment>builder()
|
||||
.addAll(prevList)
|
||||
.addAll(comments)
|
||||
.build();
|
||||
}
|
||||
|
||||
public void showReplies(final Comment comment) {
|
||||
if (comment == null) return;
|
||||
if (repliesParent == null || !Objects.equals(repliesParent.getId(), comment.getId())) {
|
||||
repliesParent = comment;
|
||||
prevReplies = null;
|
||||
prevRepliesCursor = null;
|
||||
prevRepliesHasNext = true;
|
||||
fetchReplies(comment.getId());
|
||||
return;
|
||||
}
|
||||
if (prevReplies != null && !prevReplies.isEmpty()) {
|
||||
// user clicked same comment, show prev loaded replies
|
||||
repliesCursor = prevRepliesCursor;
|
||||
repliesHasNext = prevRepliesHasNext;
|
||||
replyList.postValue(Resource.success(prevReplies));
|
||||
return;
|
||||
}
|
||||
// prev list was null or empty, fetch
|
||||
prevRepliesCursor = null;
|
||||
prevRepliesHasNext = true;
|
||||
fetchReplies(comment.getId());
|
||||
}
|
||||
|
||||
public LiveData<Resource<Object>> likeComment(@NonNull final Comment comment, final boolean liked, final boolean isReply) {
|
||||
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>(Resource.loading(null));
|
||||
final ServiceCallback<Boolean> callback = new ServiceCallback<Boolean>() {
|
||||
@Override
|
||||
public void onSuccess(final Boolean result) {
|
||||
if (result == null || !result) {
|
||||
data.postValue(Resource.error(R.string.downloader_unknown_error, null));
|
||||
return;
|
||||
}
|
||||
data.postValue(Resource.success(new Object()));
|
||||
setLiked(isReply, comment, liked);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "Error liking comment", t);
|
||||
data.postValue(Resource.error(t.getMessage(), null));
|
||||
}
|
||||
};
|
||||
if (liked) {
|
||||
mediaService.commentLike(comment.getId(), callback);
|
||||
} else {
|
||||
mediaService.commentUnlike(comment.getId(), callback);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private void setLiked(final boolean isReply,
|
||||
@NonNull final Comment comment,
|
||||
final boolean liked) {
|
||||
final List<Comment> list = getPrevList(isReply ? replyList : rootList);
|
||||
if (list == null) return;
|
||||
final List<Comment> copy = new ArrayList<>(list);
|
||||
OptionalInt indexOpt = IntStream.range(0, copy.size())
|
||||
.filter(i -> copy.get(i) != null && Objects.equals(copy.get(i).getId(), comment.getId()))
|
||||
.findFirst();
|
||||
if (!indexOpt.isPresent()) return;
|
||||
try {
|
||||
final Comment clone = (Comment) comment.clone();
|
||||
clone.setLiked(liked);
|
||||
copy.set(indexOpt.getAsInt(), clone);
|
||||
final MutableLiveData<Resource<List<Comment>>> liveData = isReply ? replyList : rootList;
|
||||
liveData.postValue(Resource.success(copy));
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "setLiked: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
public LiveData<Resource<Object>> comment(@NonNull final String text,
|
||||
final boolean isReply) {
|
||||
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>(Resource.loading(null));
|
||||
String replyToId = null;
|
||||
if (isReply && repliesParent != null) {
|
||||
replyToId = repliesParent.getId();
|
||||
}
|
||||
if (isReply && replyToId == null) {
|
||||
data.postValue(Resource.error(null, null));
|
||||
return data;
|
||||
}
|
||||
mediaService.comment(postId, text, replyToId, new ServiceCallback<Comment>() {
|
||||
@Override
|
||||
public void onSuccess(final Comment result) {
|
||||
if (result == null) {
|
||||
data.postValue(Resource.error(R.string.downloader_unknown_error, null));
|
||||
return;
|
||||
}
|
||||
addComment(result, isReply);
|
||||
data.postValue(Resource.success(new Object()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "Error during comment", t);
|
||||
data.postValue(Resource.error(t.getMessage(), null));
|
||||
}
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
private void addComment(@NonNull final Comment comment, final boolean isReply) {
|
||||
final List<Comment> list = getPrevList(isReply ? replyList : rootList);
|
||||
final ImmutableList.Builder<Comment> builder = ImmutableList.builder();
|
||||
if (isReply) {
|
||||
// in a reply list the first comment is the parent comment
|
||||
builder.add(list.get(0))
|
||||
.add(comment)
|
||||
.addAll(list.subList(1, list.size()));
|
||||
} else {
|
||||
builder.add(comment)
|
||||
.addAll(list);
|
||||
}
|
||||
final MutableLiveData<Resource<List<Comment>>> liveData = isReply ? replyList : rootList;
|
||||
liveData.postValue(Resource.success(builder.build()));
|
||||
}
|
||||
|
||||
public void translate(@NonNull final Comment comment,
|
||||
@NonNull final ServiceCallback<String> callback) {
|
||||
mediaService.translate(comment.getId(), "2", callback);
|
||||
}
|
||||
|
||||
public LiveData<Resource<Object>> deleteComment(@NonNull final Comment comment, final boolean isReply) {
|
||||
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>(Resource.loading(null));
|
||||
mediaService.deleteComment(postId, comment.getId(), new ServiceCallback<Boolean>() {
|
||||
@Override
|
||||
public void onSuccess(final Boolean result) {
|
||||
if (result == null || !result) {
|
||||
data.postValue(Resource.error(R.string.downloader_unknown_error, null));
|
||||
return;
|
||||
}
|
||||
removeComment(comment, isReply);
|
||||
data.postValue(Resource.success(new Object()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "Error deleting comment", t);
|
||||
data.postValue(Resource.error(t.getMessage(), null));
|
||||
}
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
private void removeComment(@NonNull final Comment comment, final boolean isReply) {
|
||||
final List<Comment> list = getPrevList(isReply ? replyList : rootList);
|
||||
final List<Comment> updated = list.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(c -> !Objects.equals(c.getId(), comment.getId()))
|
||||
.collect(Collectors.toList());
|
||||
final MutableLiveData<Resource<List<Comment>>> liveData = isReply ? replyList : rootList;
|
||||
liveData.postValue(Resource.success(updated));
|
||||
}
|
||||
|
||||
public void clearReplies() {
|
||||
prevRepliesCursor = repliesCursor;
|
||||
prevRepliesHasNext = repliesHasNext;
|
||||
repliesCursor = null;
|
||||
repliesHasNext = true;
|
||||
// cache prev reply list to save time and data if user clicks same comment again
|
||||
prevReplies = getPrevList(replyList);
|
||||
replyList.postValue(Resource.success(Collections.emptyList()));
|
||||
}
|
||||
}
|
@ -4,6 +4,8 @@ import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
@ -290,6 +292,20 @@ public class GraphQLService extends BaseService {
|
||||
});
|
||||
}
|
||||
|
||||
public Call<String> fetchComments(final String shortCodeOrCommentId,
|
||||
final boolean root,
|
||||
final String cursor) {
|
||||
final Map<String, String> queryMap = new HashMap<>();
|
||||
queryMap.put("query_hash", root ? "bc3296d1ce80a24b1b6e40b1e72903f5" : "51fdd02b67508306ad4484ff574a0b62");
|
||||
final Map<String, Object> variables = ImmutableMap.of(
|
||||
root ? "shortcode" : "comment_id", shortCodeOrCommentId,
|
||||
"first", 50,
|
||||
"after", cursor == null ? "" : cursor
|
||||
);
|
||||
queryMap.put("variables", new JSONObject(variables).toString());
|
||||
return repository.fetch(queryMap);
|
||||
}
|
||||
|
||||
public void fetchUser(final String username,
|
||||
final ServiceCallback<User> callback) {
|
||||
final Call<String> request = repository.getUser(username);
|
||||
@ -305,7 +321,7 @@ public class GraphQLService extends BaseService {
|
||||
try {
|
||||
final JSONObject body = new JSONObject(rawBody);
|
||||
final JSONObject userJson = body.getJSONObject("graphql")
|
||||
.getJSONObject(Constants.EXTRAS_USER);
|
||||
.getJSONObject(Constants.EXTRAS_USER);
|
||||
|
||||
boolean isPrivate = userJson.getBoolean("is_private");
|
||||
final long id = userJson.optLong(Constants.EXTRAS_ID, 0);
|
||||
|
@ -1,12 +1,12 @@
|
||||
package awais.instagrabber.webservices;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
@ -18,6 +18,7 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
import awais.instagrabber.models.Comment;
|
||||
import awais.instagrabber.models.enums.MediaItemType;
|
||||
import awais.instagrabber.repositories.MediaRepository;
|
||||
import awais.instagrabber.repositories.requests.UploadFinishOptions;
|
||||
@ -27,6 +28,7 @@ import awais.instagrabber.repositories.responses.MediaInfoResponse;
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
import awais.instagrabber.utils.DateUtils;
|
||||
import awais.instagrabber.utils.MediaUploadHelper;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
@ -170,7 +172,7 @@ public class MediaService extends BaseService {
|
||||
public void comment(@NonNull final String mediaId,
|
||||
@NonNull final String comment,
|
||||
final String replyToCommentId,
|
||||
@NonNull final ServiceCallback<Boolean> callback) {
|
||||
@NonNull final ServiceCallback<Comment> callback) {
|
||||
final String module = "self_comments_v2";
|
||||
final Map<String, Object> form = new HashMap<>();
|
||||
// form.put("user_breadcrumb", userBreadcrumb(comment.length()));
|
||||
@ -191,15 +193,33 @@ public class MediaService extends BaseService {
|
||||
final String body = response.body();
|
||||
if (body == null) {
|
||||
Log.e(TAG, "Error occurred while creating comment");
|
||||
callback.onSuccess(false);
|
||||
callback.onSuccess(null);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final JSONObject jsonObject = new JSONObject(body);
|
||||
final String status = jsonObject.optString("status");
|
||||
callback.onSuccess(status.equals("ok"));
|
||||
} catch (JSONException e) {
|
||||
// Log.e(TAG, "Error parsing body", e);
|
||||
// final String status = jsonObject.optString("status");
|
||||
final JSONObject commentJsonObject = jsonObject.optJSONObject("comment");
|
||||
Comment comment = null;
|
||||
if (commentJsonObject != null) {
|
||||
final JSONObject userJsonObject = commentJsonObject.optJSONObject("user");
|
||||
if (userJsonObject != null) {
|
||||
final Gson gson = new Gson();
|
||||
final User user = gson.fromJson(userJsonObject.toString(), User.class);
|
||||
comment = new Comment(
|
||||
commentJsonObject.optString("pk"),
|
||||
commentJsonObject.optString("text"),
|
||||
commentJsonObject.optLong("created_at"),
|
||||
0,
|
||||
false,
|
||||
user,
|
||||
0,
|
||||
!TextUtils.isEmpty(replyToCommentId)
|
||||
);
|
||||
}
|
||||
}
|
||||
callback.onSuccess(comment);
|
||||
} catch (Exception e) {
|
||||
callback.onFailure(e);
|
||||
}
|
||||
}
|
||||
@ -221,7 +241,7 @@ public class MediaService extends BaseService {
|
||||
final List<String> commentIds,
|
||||
@NonNull final ServiceCallback<Boolean> callback) {
|
||||
final Map<String, Object> form = new HashMap<>();
|
||||
form.put("comment_ids_to_delete", TextUtils.join(",", commentIds));
|
||||
form.put("comment_ids_to_delete", android.text.TextUtils.join(",", commentIds));
|
||||
form.put("_csrftoken", csrfToken);
|
||||
form.put("_uid", userId);
|
||||
form.put("_uuid", deviceUuid);
|
||||
|
@ -12,7 +12,7 @@ import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.ResponseBody;
|
||||
|
||||
class LoggingInterceptor implements Interceptor {
|
||||
public class LoggingInterceptor implements Interceptor {
|
||||
private static final String TAG = "LoggingInterceptor";
|
||||
|
||||
@NonNull
|
||||
@ -30,7 +30,11 @@ class LoggingInterceptor implements Interceptor {
|
||||
String content = "";
|
||||
if (body != null) {
|
||||
contentType = body.contentType();
|
||||
content = body.string();
|
||||
try {
|
||||
content = body.string();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "intercept: ", e);
|
||||
}
|
||||
Log.d(TAG, content);
|
||||
}
|
||||
final ResponseBody wrappedBody = ResponseBody.create(contentType, content);
|
||||
|
@ -1,10 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M20,17.17L18.83,16H4V4h16v13.17zM20,2H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h14l4,4V4c0,-1.1 -0.9,-2 -2,-2z"/>
|
||||
</vector>
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000"
|
||||
android:pathData="M9,22A1,1 0 0,1 8,21V18H4A2,2 0 0,1 2,16V4C2,2.89 2.9,2 4,2H20A2,2 0 0,1 22,4V16A2,2 0 0,1 20,18H13.9L10.2,21.71C10,21.9 9.75,22 9.5,22V22H9M10,16V19.08L13.08,16H20V4H4V16H10Z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_round_arrow_back_24.xml
Normal file
10
app/src/main/res/drawable/ic_round_arrow_back_24.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M19,11H7.83l4.88,-4.88c0.39,-0.39 0.39,-1.03 0,-1.42 -0.39,-0.39 -1.02,-0.39 -1.41,0l-6.59,6.59c-0.39,0.39 -0.39,1.02 0,1.41l6.59,6.59c0.39,0.39 1.02,0.39 1.41,0 0.39,-0.39 0.39,-1.02 0,-1.41L7.83,13H19c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1z"/>
|
||||
</vector>
|
@ -1,10 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M22,4c0,-1.1 -0.9,-2 -2,-2H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h14l4,4V4z"/>
|
||||
</vector>
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000"
|
||||
android:pathData="M9,22A1,1 0 0,1 8,21V18H4A2,2 0 0,1 2,16V4C2,2.89 2.9,2 4,2H20A2,2 0 0,1 22,4V16A2,2 0 0,1 20,18H13.9L10.2,21.71C10,21.9 9.75,22 9.5,22V22H9Z" />
|
||||
</vector>
|
@ -1,49 +1,74 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
android:background="?colorSurface">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/replies_container_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:elevation="10dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toTopOf="@id/swipe_refresh_layout"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/swipe_refresh_layout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
app:layout_constraintBottom_toTopOf="@id/comment_field"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/toolbar">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvComments"
|
||||
android:id="@+id/comments"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
tools:listitem="@layout/item_comment" />
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<!--app:startIconDrawable="@drawable/ic_close_24"-->
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/commentField"
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/comment_field"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="4dp"
|
||||
android:hint="@string/comment_hint"
|
||||
android:visibility="gone"
|
||||
app:counterEnabled="true"
|
||||
app:counterEnabled="false"
|
||||
app:counterMaxLength="2200"
|
||||
app:endIconDrawable="@drawable/ic_round_send_24"
|
||||
app:endIconMode="custom"
|
||||
app:startIconDrawable="@drawable/ic_close_24"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/swipe_refresh_layout"
|
||||
tools:visibility="visible">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/commentText"
|
||||
android:id="@+id/comment_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:autofillHints="no"
|
||||
android:inputType="textMultiLine"
|
||||
android:maxLength="2200"
|
||||
android:maxLines="10"
|
||||
android:scrollHorizontally="false"
|
||||
tools:text="test" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@ -16,6 +15,7 @@
|
||||
android:id="@+id/rvLikes"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false" />
|
||||
android:clipToPadding="false"
|
||||
tools:listitem="@layout/item_follow" />
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
@ -5,125 +5,135 @@
|
||||
android:id="@+id/container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:focusable="true"
|
||||
android:orientation="horizontal"
|
||||
android:padding="8dp">
|
||||
android:foreground="?selectableItemBackground"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="4dp">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
android:id="@+id/ivProfilePic"
|
||||
android:layout_width="@dimen/simple_item_picture_size"
|
||||
android:layout_height="@dimen/simple_item_picture_size"
|
||||
android:padding="4dp"
|
||||
app:actualImageScaleType="centerCrop"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:roundAsCircle="true"
|
||||
tools:background="@mipmap/ic_launcher" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/tvUsername"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingTop="2dp"
|
||||
android:paddingEnd="4dp"
|
||||
android:paddingBottom="2dp"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@id/tvComment"
|
||||
app:layout_constraintStart_toEndOf="@id/ivProfilePic"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="username" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/isVerified"
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/profile_pic_guideline"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:scaleType="fitStart"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tvUsername"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/tvUsername"
|
||||
app:layout_constraintTop_toTopOf="@id/tvUsername"
|
||||
app:srcCompat="@drawable/verified"
|
||||
tools:visibility="visible" />
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_begin="56dp" />
|
||||
|
||||
<awais.instagrabber.customviews.RamboTextViewV2
|
||||
android:id="@+id/tvComment"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:autoLink="web|email"
|
||||
android:ellipsize="end"
|
||||
android:linksClickable="true"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingTop="2dp"
|
||||
android:paddingEnd="4dp"
|
||||
android:paddingBottom="2dp"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat"
|
||||
app:layout_constraintBottom_toTopOf="@id/tvLikes"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toEndOf="@id/ivProfilePic"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvUsername"
|
||||
tools:text="comment comment comment comment comment comment comment comment comment comment comment comment
|
||||
comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment comment" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/tvLikes"
|
||||
<awais.instagrabber.customviews.ProfilePicView
|
||||
android:id="@+id/profile_pic"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingTop="2dp"
|
||||
android:paddingEnd="4dp"
|
||||
android:paddingBottom="2dp"
|
||||
android:scrollbars="none"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/tvDate"
|
||||
app:layout_constraintStart_toEndOf="@id/ivProfilePic"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvComment"
|
||||
tools:text="likes" />
|
||||
android:layout_marginEnd="16dp"
|
||||
app:actualImageScaleType="centerCrop"
|
||||
app:layout_constraintEnd_toStartOf="@id/profile_pic_guideline"
|
||||
app:layout_constraintHorizontal_bias="1"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:size="small" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/tvDate"
|
||||
<awais.instagrabber.customviews.UsernameTextView
|
||||
android:id="@+id/username"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:gravity="end"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
|
||||
android:textStyle="italic"
|
||||
app:layout_constraintBaseline_toBaselineOf="@id/tvLikes"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@id/date"
|
||||
app:layout_constraintEnd_toStartOf="@id/options"
|
||||
app:layout_constraintStart_toEndOf="@id/profile_pic_guideline"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="username username username" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/options"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:padding="4dp"
|
||||
android:scaleType="fitCenter"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_more_vert_24" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/date"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:alpha="0.8"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
|
||||
app:layout_constraintBottom_toTopOf="@id/comment"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@id/username"
|
||||
app:layout_constraintTop_toBottomOf="@id/username"
|
||||
tools:text="long date..................." />
|
||||
|
||||
<awais.instagrabber.customviews.RamboTextViewV2
|
||||
android:id="@+id/comment"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
|
||||
app:layout_constraintBottom_toTopOf="@id/comment_barrier"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@id/username"
|
||||
app:layout_constraintTop_toBottomOf="@id/date"
|
||||
tools:text="comment comment comment comment comment comment comment comment comment comment comment comment" />
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/comment_barrier"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:barrierDirection="bottom" />
|
||||
|
||||
<awais.instagrabber.customviews.TextViewDrawableSize
|
||||
android:id="@+id/likes"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:clickable="true"
|
||||
android:drawablePadding="4dp"
|
||||
android:focusable="true"
|
||||
android:gravity="center_vertical"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle2"
|
||||
app:compoundDrawableHeight="16dp"
|
||||
app:compoundDrawableWidth="16dp"
|
||||
app:drawableTint="@color/red_800"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/replies"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="@id/comment"
|
||||
app:layout_constraintTop_toBottomOf="@id/comment_barrier"
|
||||
tools:text="99999" />
|
||||
|
||||
<awais.instagrabber.customviews.TextViewDrawableSize
|
||||
android:id="@+id/replies"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="32dp"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:clickable="true"
|
||||
android:drawablePadding="4dp"
|
||||
android:focusable="true"
|
||||
android:gravity="center_vertical"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle2"
|
||||
app:compoundDrawableHeight="16dp"
|
||||
app:compoundDrawableWidth="16dp"
|
||||
app:drawableStartCompat="@drawable/ic_round_mode_comment_24"
|
||||
app:drawableTint="@color/grey_500"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/tvLikes"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvComment"
|
||||
tools:text="long date................................" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<!--<androidx.recyclerview.widget.RecyclerView-->
|
||||
<!-- android:id="@+id/rvChildComments"-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:layout_marginStart="40dp"-->
|
||||
<!-- android:layout_marginLeft="40dp"-->
|
||||
<!-- app:layoutManager="LinearLayoutManager"-->
|
||||
<!-- tools:itemCount="5"-->
|
||||
<!-- tools:listitem="@layout/item_comment_small" />-->
|
||||
|
||||
<!--<View-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="1dp"-->
|
||||
<!-- android:layout_gravity="bottom"-->
|
||||
<!-- android:layout_marginStart="4dp"-->
|
||||
<!-- android:layout_marginEnd="4dp"-->
|
||||
<!-- android:layout_marginBottom="4dp"-->
|
||||
<!-- android:background="#32888888" />-->
|
||||
app:layout_constraintStart_toEndOf="@id/likes"
|
||||
app:layout_constraintTop_toBottomOf="@id/comment_barrier"
|
||||
tools:text="9999" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,109 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="40dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingBottom="2dp">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
android:id="@+id/ivProfilePic"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
app:actualImageScaleType="centerCrop"
|
||||
app:layout_constraintEnd_toStartOf="@id/tvUsername"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:roundAsCircle="true"
|
||||
tools:background="@mipmap/ic_launcher" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/tvUsername"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingTop="2dp"
|
||||
android:paddingEnd="4dp"
|
||||
android:paddingBottom="2dp"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@id/tvComment"
|
||||
app:layout_constraintStart_toEndOf="@id/ivProfilePic"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="username" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/isVerified"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:scaleType="fitStart"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tvUsername"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/tvUsername"
|
||||
app:layout_constraintTop_toTopOf="@id/tvUsername"
|
||||
app:srcCompat="@drawable/verified"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<awais.instagrabber.customviews.RamboTextViewV2
|
||||
android:id="@+id/tvComment"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:autoLink="web|email"
|
||||
android:clickable="true"
|
||||
android:ellipsize="end"
|
||||
android:focusable="true"
|
||||
android:linksClickable="true"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingTop="2dp"
|
||||
android:paddingEnd="4dp"
|
||||
android:paddingBottom="2dp"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat"
|
||||
app:layout_constraintBottom_toTopOf="@id/tvLikes"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/ivProfilePic"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvUsername"
|
||||
tools:text="comment comment comment comment comment comment comment comment
|
||||
comment comment comment comment comment comment comment comment comment comment comment comment comment comment
|
||||
comment comment comment comment comment comment " />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/tvLikes"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingTop="2dp"
|
||||
android:paddingEnd="4dp"
|
||||
android:paddingBottom="2dp"
|
||||
android:scrollbars="none"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/tvDate"
|
||||
app:layout_constraintStart_toEndOf="@id/ivProfilePic"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvComment"
|
||||
tools:text="likes" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/tvDate"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="end"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
|
||||
android:textStyle="italic"
|
||||
app:layout_constraintBaseline_toBaselineOf="@id/tvLikes"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/tvLikes"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvComment"
|
||||
tools:text="date....." />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,51 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:foreground="?android:selectableItemBackground"
|
||||
android:padding="8dp">
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:padding="16dp">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
android:id="@+id/ivProfilePic"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="fitCenter" />
|
||||
<awais.instagrabber.customviews.ProfilePicView
|
||||
android:id="@+id/profile_pic"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/username"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:size="small" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:layout_marginStart="66dp"
|
||||
android:layout_marginEnd="26dp"
|
||||
android:orientation="vertical">
|
||||
<awais.instagrabber.customviews.UsernameTextView
|
||||
android:id="@+id/username"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
|
||||
app:layout_constraintBottom_toTopOf="@id/full_name"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/profile_pic"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="username" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/tvFullName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Large"
|
||||
app:autoSizeMaxTextSize="28sp"
|
||||
app:autoSizeMinTextSize="16sp"
|
||||
app:autoSizeTextType="uniform" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/tvUsername"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/isAdmin"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_gravity="end"
|
||||
android:scaleType="fitCenter"
|
||||
android:visibility="gone"
|
||||
app:srcCompat="@drawable/ic_star_24" />
|
||||
</FrameLayout>
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/full_name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle2"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@id/username"
|
||||
app:layout_constraintTop_toBottomOf="@id/username"
|
||||
tools:text="Full Name" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
9
app/src/main/res/menu/comment_options_menu.xml
Normal file
9
app/src/main/res/menu/comment_options_menu.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/translate"
|
||||
android:title="@string/comment_viewer_translate_comment" />
|
||||
<item
|
||||
android:id="@+id/delete"
|
||||
android:title="@string/delete" />
|
||||
</menu>
|
@ -29,7 +29,7 @@
|
||||
|
||||
<dialog
|
||||
android:id="@+id/commentsViewerFragment"
|
||||
android:name="awais.instagrabber.fragments.CommentsViewerFragment"
|
||||
android:name="awais.instagrabber.fragments.comments.CommentsViewerFragment"
|
||||
android:label="Comments"
|
||||
tools:layout="@layout/fragment_comments">
|
||||
<argument
|
||||
|
@ -6,6 +6,7 @@
|
||||
<enum name="small" value="1" />
|
||||
<enum name="regular" value="2" />
|
||||
<enum name="large" value="3" />
|
||||
<enum name="smaller" value="4" />
|
||||
</attr>
|
||||
</declare-styleable>
|
||||
<declare-styleable name="ChatMessageLayout">
|
||||
@ -22,6 +23,7 @@
|
||||
|
||||
<attr name="toolbarColor" format="reference" />
|
||||
<attr name="searchInputStyle" format="reference" />
|
||||
<attr name="parentCommentHighlightColor" format="reference" />
|
||||
|
||||
<declare-styleable name="RecordView">
|
||||
<attr name="slide_to_cancel_text" format="string" />
|
||||
@ -37,4 +39,9 @@
|
||||
<attr name="waveformBackgroundColor" format="reference" />
|
||||
<attr name="waveformProgressColor" format="reference" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="TextViewDrawableSize">
|
||||
<attr name="compoundDrawableWidth" format="dimension" />
|
||||
<attr name="compoundDrawableHeight" format="dimension" />
|
||||
</declare-styleable>
|
||||
</resources>
|
@ -25,8 +25,9 @@
|
||||
|
||||
<color name="dm_profile_button_color">#efefef</color>
|
||||
|
||||
<color name="comment_selected">#888888</color>
|
||||
<color name="comment_liked">#40FF69B4</color>
|
||||
<color name="parent_comment_dark_materialdark">#FF082654</color>
|
||||
<color name="parent_comment_light_white">@color/grey_300</color>
|
||||
<!--<color name="comment_liked">#40FF69B4</color>-->
|
||||
|
||||
<color name="white">#FFFFFF</color>
|
||||
<color name="white_a50">#80FFFFFF</color>
|
||||
@ -63,6 +64,10 @@
|
||||
<color name="blue_A400">#2979FF</color>
|
||||
<color name="blue_A700">#2962FF</color>
|
||||
|
||||
<color name="light_blue_900">#01579b</color>
|
||||
|
||||
<color name="indigo_900">#1A237E</color>
|
||||
|
||||
<color name="brown_50">#EFEBE9</color>
|
||||
<color name="brown_100">#D7CCC8</color>
|
||||
<color name="brown_200">#BCAAA4</color>
|
||||
|
@ -9,6 +9,7 @@
|
||||
<dimen name="image_size_40">40dp</dimen>
|
||||
|
||||
<dimen name="profile_pic_size_tiny">24dp</dimen>
|
||||
<dimen name="profile_pic_size_smaller">32dp</dimen>
|
||||
<dimen name="profile_pic_size_small">40dp</dimen>
|
||||
<dimen name="profile_pic_size_regular">48dp</dimen>
|
||||
<dimen name="profile_pic_size_large">90dp</dimen>
|
||||
|
@ -214,7 +214,7 @@
|
||||
<string name="comment_viewer_like_comment">Like comment</string>
|
||||
<string name="comment_viewer_unlike_comment">Unlike comment</string>
|
||||
<string name="comment_viewer_translate_comment">Translate comment</string>
|
||||
<string name="comment_viewer_delete_comment">Delete comment</string>
|
||||
<!--<string name="comment_viewer_delete_comment">Delete comment</string>-->
|
||||
<string name="comment_send_empty_comment">No empty comments!</string>
|
||||
<string name="comment_view_mention_user_search">Do you want to search the username?</string>
|
||||
<string name="comment_view_mention_hash_search">Do you want to search the hashtag?</string>
|
||||
@ -381,6 +381,10 @@
|
||||
<item quantity="one">%d like</item>
|
||||
<item quantity="other">%d likes</item>
|
||||
</plurals>
|
||||
<plurals name="replies_count">
|
||||
<item quantity="one">%d reply</item>
|
||||
<item quantity="other">%d replies</item>
|
||||
</plurals>
|
||||
<plurals name="comments_count">
|
||||
<item quantity="one">%d comment</item>
|
||||
<item quantity="other">%d comments</item>
|
||||
|
@ -261,13 +261,13 @@
|
||||
<item name="colorPrimary">@color/white</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dark.Black" parent="Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
||||
<item name="materialThemeOverlay">@style/ThemeOverlay.MaterialComponents.TextInputEditText.OutlinedBox.Dark.Black</item>
|
||||
<style name="Widget.MaterialComponents.TextInputLayout.FilledBox.Dark.Black" parent="Widget.MaterialComponents.TextInputLayout.FilledBox">
|
||||
<item name="materialThemeOverlay">@style/ThemeOverlay.MaterialComponents.TextInputEditText.FilledBox.Dark.Black</item>
|
||||
<item name="hintTextColor">@color/white</item>
|
||||
<item name="boxStrokeColor">@color/white</item>
|
||||
</style>
|
||||
|
||||
<style name="ThemeOverlay.MaterialComponents.TextInputEditText.OutlinedBox.Dark.Black" parent="ThemeOverlay.MaterialComponents.TextInputEditText.OutlinedBox">
|
||||
<style name="ThemeOverlay.MaterialComponents.TextInputEditText.FilledBox.Dark.Black" parent="ThemeOverlay.MaterialComponents.TextInputEditText.FilledBox">
|
||||
<item name="colorPrimary">@color/white</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
@ -53,6 +53,7 @@
|
||||
<item name="dmWaveformProgressColor">@color/blue_800</item>
|
||||
<item name="dmInputTextColor">@color/black</item>
|
||||
<item name="tabStyle">@style/Widget.MaterialComponents.TabLayout.Light.White</item>
|
||||
<item name="parentCommentHighlightColor">@color/parent_comment_light_white</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.Light.Barinsta" parent="AppTheme.Light">
|
||||
@ -72,6 +73,7 @@
|
||||
<item name="toolbarStyle">@style/Widget.MaterialComponents.Toolbar.Light.Barinsta</item>
|
||||
<item name="dmInputTextColor">@color/black</item>
|
||||
<item name="searchInputStyle">@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Light.Barinsta</item>
|
||||
<item name="parentCommentHighlightColor">@color/parent_comment_light_white</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.Light.Bibliogram" parent="AppTheme.Light">
|
||||
@ -90,6 +92,7 @@
|
||||
<item name="toolbarStyle">@style/Widget.MaterialComponents.Toolbar.Light.Barinsta</item>
|
||||
<item name="dmInputTextColor">@color/black</item>
|
||||
<item name="searchInputStyle">@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Light.Barinsta</item>
|
||||
<item name="parentCommentHighlightColor">@color/parent_comment_light_white</item>
|
||||
</style>
|
||||
|
||||
|
||||
@ -146,7 +149,8 @@
|
||||
<item name="dmOutgoingBgColor">@color/deep_purple_400</item>
|
||||
<item name="dmDateHeaderBgColor">@color/deep_purple_600</item>
|
||||
<item name="tabStyle">@style/Widget.MaterialComponents.TabLayout.Dark.Black</item>
|
||||
<item name="textInputStyle">@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dark.Black</item>
|
||||
<item name="textInputStyle">@style/Widget.MaterialComponents.TextInputLayout.FilledBox.Dark.Black</item>
|
||||
<item name="parentCommentHighlightColor">@color/parent_comment_dark_materialdark</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.Dark.MaterialDark" parent="AppTheme.Dark">
|
||||
@ -169,6 +173,7 @@
|
||||
<item name="dmIncomingBgColor">@color/grey_600</item>
|
||||
<item name="dmOutgoingBgColor">@color/deep_purple_400</item>
|
||||
<item name="dmDateHeaderBgColor">@color/deep_purple_600</item>
|
||||
<item name="parentCommentHighlightColor">@color/parent_comment_dark_materialdark</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.Launcher" parent="AppTheme.Dark">
|
||||
|
Loading…
Reference in New Issue
Block a user