mirror of
https://github.com/KokaKiwi/BarInsta
synced 2024-11-22 06:37:30 +00:00
Merge branch 'master' into pr/1542
This commit is contained in:
commit
3dcb8c5c7d
@ -139,7 +139,7 @@ public final class DirectItemsAdapter extends RecyclerView.Adapter<RecyclerView.
|
|||||||
return new HeaderViewHolder(LayoutDmHeaderBinding.inflate(layoutInflater, parent, false));
|
return new HeaderViewHolder(LayoutDmHeaderBinding.inflate(layoutInflater, parent, false));
|
||||||
}
|
}
|
||||||
final LayoutDmBaseBinding baseBinding = LayoutDmBaseBinding.inflate(layoutInflater, parent, false);
|
final LayoutDmBaseBinding baseBinding = LayoutDmBaseBinding.inflate(layoutInflater, parent, false);
|
||||||
final DirectItemType directItemType = DirectItemType.Companion.getId(type);
|
final DirectItemType directItemType = DirectItemType.Companion.getTypeFromId(type);
|
||||||
final DirectItemViewHolder itemViewHolder = getItemViewHolder(layoutInflater, baseBinding, directItemType);
|
final DirectItemViewHolder itemViewHolder = getItemViewHolder(layoutInflater, baseBinding, directItemType);
|
||||||
itemViewHolder.setLongClickListener(longClickListener);
|
itemViewHolder.setLongClickListener(longClickListener);
|
||||||
return itemViewHolder;
|
return itemViewHolder;
|
||||||
|
@ -208,15 +208,11 @@ public final class FeedAdapterV2 extends ListAdapter<Media, RecyclerView.ViewHol
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
public interface FeedItemCallback {
|
public interface FeedItemCallback {
|
||||||
void onPostClick(final Media feedModel,
|
void onPostClick(final Media feedModel);
|
||||||
final View profilePicView,
|
|
||||||
final View mainPostImage);
|
|
||||||
|
|
||||||
void onProfilePicClick(final Media feedModel,
|
void onProfilePicClick(final Media feedModel);
|
||||||
final View profilePicView);
|
|
||||||
|
|
||||||
void onNameClick(final Media feedModel,
|
void onNameClick(final Media feedModel);
|
||||||
final View profilePicView);
|
|
||||||
|
|
||||||
void onLocationClick(final Media feedModel);
|
void onLocationClick(final Media feedModel);
|
||||||
|
|
||||||
|
@ -7,13 +7,13 @@ import awais.instagrabber.repositories.responses.Media;
|
|||||||
|
|
||||||
public class FeedItemCallbackAdapter implements FeedAdapterV2.FeedItemCallback {
|
public class FeedItemCallbackAdapter implements FeedAdapterV2.FeedItemCallback {
|
||||||
@Override
|
@Override
|
||||||
public void onPostClick(final Media media, final View profilePicView, final View mainPostImage) {}
|
public void onPostClick(final Media media) {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onProfilePicClick(final Media media, final View profilePicView) {}
|
public void onProfilePicClick(final Media media) {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNameClick(final Media media, final View profilePicView) {}
|
public void onNameClick(final Media media) {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLocationClick(final Media media) {}
|
public void onLocationClick(final Media media) {}
|
||||||
|
@ -12,12 +12,13 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import awais.instagrabber.R;
|
import awais.instagrabber.R;
|
||||||
import awais.instagrabber.adapters.viewholder.FollowsViewHolder;
|
import awais.instagrabber.adapters.viewholder.FollowsViewHolder;
|
||||||
import awais.instagrabber.databinding.ItemFollowBinding;
|
import awais.instagrabber.databinding.ItemFollowBinding;
|
||||||
import awais.instagrabber.interfaces.OnGroupClickListener;
|
import awais.instagrabber.interfaces.OnGroupClickListener;
|
||||||
import awais.instagrabber.models.FollowModel;
|
import awais.instagrabber.repositories.responses.User;
|
||||||
import awais.instagrabber.utils.TextUtils;
|
import awais.instagrabber.utils.TextUtils;
|
||||||
import thoughtbot.expandableadapter.ExpandableGroup;
|
import thoughtbot.expandableadapter.ExpandableGroup;
|
||||||
import thoughtbot.expandableadapter.ExpandableList;
|
import thoughtbot.expandableadapter.ExpandableList;
|
||||||
@ -27,28 +28,33 @@ import thoughtbot.expandableadapter.GroupViewHolder;
|
|||||||
// thanks to ThoughtBot's ExpandableRecyclerViewAdapter
|
// thanks to ThoughtBot's ExpandableRecyclerViewAdapter
|
||||||
// https://github.com/thoughtbot/expandable-recycler-view
|
// https://github.com/thoughtbot/expandable-recycler-view
|
||||||
public final class FollowAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements OnGroupClickListener, Filterable {
|
public final class FollowAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements OnGroupClickListener, Filterable {
|
||||||
|
private final View.OnClickListener onClickListener;
|
||||||
|
private final ExpandableList expandableListOriginal;
|
||||||
|
private final boolean hasManyGroups;
|
||||||
|
private ExpandableList expandableList;
|
||||||
|
|
||||||
private final Filter filter = new Filter() {
|
private final Filter filter = new Filter() {
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
protected FilterResults performFiltering(final CharSequence filter) {
|
protected FilterResults performFiltering(final CharSequence filter) {
|
||||||
if (expandableList.groups != null) {
|
final List<User> filteredItems = new ArrayList<User>();
|
||||||
final boolean isFilterEmpty = TextUtils.isEmpty(filter);
|
if (expandableListOriginal.groups == null || TextUtils.isEmpty(filter)) return null;
|
||||||
final String query = isFilterEmpty ? null : filter.toString().toLowerCase();
|
final String query = filter.toString().toLowerCase();
|
||||||
|
final ArrayList<ExpandableGroup> groups = new ArrayList<ExpandableGroup>();
|
||||||
for (int x = 0; x < expandableList.groups.size(); ++x) {
|
for (int x = 0; x < expandableListOriginal.groups.size(); ++x) {
|
||||||
final ExpandableGroup expandableGroup = expandableList.groups.get(x);
|
final ExpandableGroup expandableGroup = expandableListOriginal.groups.get(x);
|
||||||
final List<FollowModel> items = expandableGroup.getItems(false);
|
final String title = expandableGroup.getTitle();
|
||||||
final int itemCount = expandableGroup.getItemCount(false);
|
final List<User> items = expandableGroup.getItems();
|
||||||
|
if (items != null) {
|
||||||
for (int i = 0; i < itemCount; ++i) {
|
final List<User> toReturn = items.stream()
|
||||||
final FollowModel followModel = items.get(i);
|
.filter(u -> hasKey(query, u.getUsername(), u.getFullName()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
if (isFilterEmpty) followModel.setShown(true);
|
groups.add(new ExpandableGroup(title, toReturn));
|
||||||
else followModel.setShown(hasKey(query, followModel.getUsername(), followModel.getFullName()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
final FilterResults filterResults = new FilterResults();
|
||||||
|
filterResults.values = new ExpandableList(groups, expandableList.expandedGroupIndexes);
|
||||||
|
return filterResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasKey(final String key, final String username, final String name) {
|
private boolean hasKey(final String key, final String username, final String name) {
|
||||||
@ -60,15 +66,20 @@ public final class FollowAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void publishResults(final CharSequence constraint, final FilterResults results) {
|
protected void publishResults(final CharSequence constraint, final FilterResults results) {
|
||||||
|
if (results == null) {
|
||||||
|
expandableList = expandableListOriginal;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final ExpandableList filteredList = (ExpandableList) results.values;
|
||||||
|
expandableList = filteredList;
|
||||||
|
}
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
private final View.OnClickListener onClickListener;
|
|
||||||
private final ExpandableList expandableList;
|
|
||||||
private final boolean hasManyGroups;
|
|
||||||
|
|
||||||
public FollowAdapter(final View.OnClickListener onClickListener, @NonNull final ArrayList<ExpandableGroup> groups) {
|
public FollowAdapter(final View.OnClickListener onClickListener, @NonNull final ArrayList<ExpandableGroup> groups) {
|
||||||
this.expandableList = new ExpandableList(groups);
|
this.expandableListOriginal = new ExpandableList(groups);
|
||||||
|
expandableList = this.expandableListOriginal;
|
||||||
this.onClickListener = onClickListener;
|
this.onClickListener = onClickListener;
|
||||||
this.hasManyGroups = groups.size() > 1;
|
this.hasManyGroups = groups.size() > 1;
|
||||||
}
|
}
|
||||||
@ -104,7 +115,7 @@ public final class FollowAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
|
|||||||
gvh.toggle(isGroupExpanded(group));
|
gvh.toggle(isGroupExpanded(group));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final FollowModel model = group.getItems(true).get(hasManyGroups ? listPos.childPos : position);
|
final User model = group.getItems().get(hasManyGroups ? listPos.childPos : position);
|
||||||
((FollowsViewHolder) holder).bind(model, onClickListener);
|
((FollowsViewHolder) holder).bind(model, onClickListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +135,7 @@ public final class FollowAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
|
|||||||
|
|
||||||
final int groupPos = listPosition.groupPos;
|
final int groupPos = listPosition.groupPos;
|
||||||
final int positionStart = expandableList.getFlattenedGroupIndex(listPosition) + 1;
|
final int positionStart = expandableList.getFlattenedGroupIndex(listPosition) + 1;
|
||||||
final int positionEnd = expandableList.groups.get(groupPos).getItemCount(true);
|
final int positionEnd = expandableList.groups.get(groupPos).getItemCount();
|
||||||
|
|
||||||
final boolean isExpanded = expandableList.expandedGroupIndexes[groupPos];
|
final boolean isExpanded = expandableList.expandedGroupIndexes[groupPos];
|
||||||
expandableList.expandedGroupIndexes[groupPos] = !isExpanded;
|
expandableList.expandedGroupIndexes[groupPos] = !isExpanded;
|
||||||
|
@ -24,12 +24,12 @@ public final class NotificationsAdapter extends ListAdapter<Notification, Notifi
|
|||||||
private static final DiffUtil.ItemCallback<Notification> DIFF_CALLBACK = new DiffUtil.ItemCallback<Notification>() {
|
private static final DiffUtil.ItemCallback<Notification> DIFF_CALLBACK = new DiffUtil.ItemCallback<Notification>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean areItemsTheSame(final Notification oldItem, final Notification newItem) {
|
public boolean areItemsTheSame(final Notification oldItem, final Notification newItem) {
|
||||||
return oldItem != null && newItem != null && oldItem.getPk().equals(newItem.getPk());
|
return oldItem.getPk().equals(newItem.getPk());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean areContentsTheSame(@NonNull final Notification oldItem, @NonNull final Notification newItem) {
|
public boolean areContentsTheSame(@NonNull final Notification oldItem, @NonNull final Notification newItem) {
|
||||||
return oldItem.getPk().equals(newItem.getPk());
|
return oldItem.getPk().equals(newItem.getPk()) && oldItem.getType() == newItem.getType();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ public class FeedGridItemViewHolder extends RecyclerView.ViewHolder {
|
|||||||
final boolean selected) {
|
final boolean selected) {
|
||||||
itemView.setOnClickListener(v -> {
|
itemView.setOnClickListener(v -> {
|
||||||
if (!selectionModeActive && feedItemCallback != null) {
|
if (!selectionModeActive && feedItemCallback != null) {
|
||||||
feedItemCallback.onPostClick(media, binding.profilePic, binding.postImage);
|
feedItemCallback.onPostClick(media);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (selectionModeActive && adapterSelectionCallback != null) {
|
if (selectionModeActive && adapterSelectionCallback != null) {
|
||||||
|
@ -6,7 +6,6 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import awais.instagrabber.databinding.ItemFollowBinding;
|
import awais.instagrabber.databinding.ItemFollowBinding;
|
||||||
import awais.instagrabber.models.FollowModel;
|
|
||||||
import awais.instagrabber.repositories.responses.User;
|
import awais.instagrabber.repositories.responses.User;
|
||||||
|
|
||||||
public final class FollowsViewHolder extends RecyclerView.ViewHolder {
|
public final class FollowsViewHolder extends RecyclerView.ViewHolder {
|
||||||
@ -27,14 +26,4 @@ public final class FollowsViewHolder extends RecyclerView.ViewHolder {
|
|||||||
binding.fullName.setText(model.getFullName());
|
binding.fullName.setText(model.getFullName());
|
||||||
binding.profilePic.setImageURI(model.getProfilePicUrl());
|
binding.profilePic.setImageURI(model.getProfilePicUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bind(final FollowModel model,
|
|
||||||
final View.OnClickListener onClickListener) {
|
|
||||||
if (model == null) return;
|
|
||||||
itemView.setTag(model);
|
|
||||||
itemView.setOnClickListener(onClickListener);
|
|
||||||
binding.username.setUsername("@" + model.getUsername());
|
|
||||||
binding.fullName.setText(model.getFullName());
|
|
||||||
binding.profilePic.setImageURI(model.getProfilePicUrl());
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,39 +1,45 @@
|
|||||||
package awais.instagrabber.adapters.viewholder.feed;
|
package awais.instagrabber.adapters.viewholder.feed;
|
||||||
|
|
||||||
import android.text.method.LinkMovementMethod;
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
import android.text.Spanned;
|
||||||
import android.transition.TransitionManager;
|
import android.transition.TransitionManager;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import awais.instagrabber.R;
|
||||||
import awais.instagrabber.adapters.FeedAdapterV2;
|
import awais.instagrabber.adapters.FeedAdapterV2;
|
||||||
import awais.instagrabber.databinding.ItemFeedBottomBinding;
|
import awais.instagrabber.customviews.VerticalImageSpan;
|
||||||
import awais.instagrabber.databinding.ItemFeedTopBinding;
|
import awais.instagrabber.databinding.ItemFeedTopBinding;
|
||||||
|
import awais.instagrabber.databinding.LayoutPostViewBottomBinding;
|
||||||
import awais.instagrabber.models.enums.MediaItemType;
|
import awais.instagrabber.models.enums.MediaItemType;
|
||||||
import awais.instagrabber.repositories.responses.Caption;
|
import awais.instagrabber.repositories.responses.Caption;
|
||||||
import awais.instagrabber.repositories.responses.Location;
|
import awais.instagrabber.repositories.responses.Location;
|
||||||
import awais.instagrabber.repositories.responses.Media;
|
import awais.instagrabber.repositories.responses.Media;
|
||||||
import awais.instagrabber.repositories.responses.User;
|
import awais.instagrabber.repositories.responses.User;
|
||||||
import awais.instagrabber.utils.TextUtils;
|
import awais.instagrabber.utils.TextUtils;
|
||||||
|
import awais.instagrabber.utils.Utils;
|
||||||
|
|
||||||
import static android.text.TextUtils.TruncateAt.END;
|
import static android.text.TextUtils.TruncateAt.END;
|
||||||
|
|
||||||
public abstract class FeedItemViewHolder extends RecyclerView.ViewHolder {
|
public abstract class FeedItemViewHolder extends RecyclerView.ViewHolder {
|
||||||
public static final int MAX_LINES_COLLAPSED = 5;
|
public static final int MAX_LINES_COLLAPSED = 5;
|
||||||
private final ItemFeedTopBinding topBinding;
|
private final ItemFeedTopBinding topBinding;
|
||||||
private final ItemFeedBottomBinding bottomBinding;
|
private final LayoutPostViewBottomBinding bottomBinding;
|
||||||
|
private final ViewGroup bottomFrame;
|
||||||
private final FeedAdapterV2.FeedItemCallback feedItemCallback;
|
private final FeedAdapterV2.FeedItemCallback feedItemCallback;
|
||||||
|
|
||||||
public FeedItemViewHolder(@NonNull final View root,
|
public FeedItemViewHolder(@NonNull final ViewGroup root,
|
||||||
final ItemFeedTopBinding topBinding,
|
|
||||||
final ItemFeedBottomBinding bottomBinding,
|
|
||||||
final FeedAdapterV2.FeedItemCallback feedItemCallback) {
|
final FeedAdapterV2.FeedItemCallback feedItemCallback) {
|
||||||
super(root);
|
super(root);
|
||||||
this.topBinding = topBinding;
|
this.bottomFrame = root;
|
||||||
this.bottomBinding = bottomBinding;
|
this.topBinding = ItemFeedTopBinding.bind(root);
|
||||||
topBinding.title.setMovementMethod(new LinkMovementMethod());
|
this.bottomBinding = LayoutPostViewBottomBinding.bind(root);
|
||||||
this.feedItemCallback = feedItemCallback;
|
this.feedItemCallback = feedItemCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,33 +48,35 @@ public abstract class FeedItemViewHolder extends RecyclerView.ViewHolder {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setupProfilePic(media);
|
setupProfilePic(media);
|
||||||
setupLocation(media);
|
bottomBinding.date.setText(media.getDate());
|
||||||
bottomBinding.tvPostDate.setText(media.getDate());
|
|
||||||
setupComments(media);
|
setupComments(media);
|
||||||
setupCaption(media);
|
setupCaption(media);
|
||||||
|
setupActions(media);
|
||||||
if (media.getType() != MediaItemType.MEDIA_TYPE_SLIDER) {
|
if (media.getType() != MediaItemType.MEDIA_TYPE_SLIDER) {
|
||||||
bottomBinding.btnDownload.setOnClickListener(v ->
|
bottomBinding.download.setOnClickListener(v ->
|
||||||
feedItemCallback.onDownloadClick(media, -1, null)
|
feedItemCallback.onDownloadClick(media, -1, null)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
bindItem(media);
|
bindItem(media);
|
||||||
|
bottomFrame.post(() -> setupLocation(media));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupComments(@NonNull final Media feedModel) {
|
private void setupComments(@NonNull final Media feedModel) {
|
||||||
final long commentsCount = feedModel.getCommentCount();
|
final long commentsCount = feedModel.getCommentCount();
|
||||||
bottomBinding.commentsCount.setText(String.valueOf(commentsCount));
|
bottomBinding.commentsCount.setText(String.valueOf(commentsCount));
|
||||||
bottomBinding.btnComments.setOnClickListener(v -> feedItemCallback.onCommentsClick(feedModel));
|
bottomBinding.comment.setOnClickListener(v -> feedItemCallback.onCommentsClick(feedModel));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupProfilePic(@NonNull final Media media) {
|
private void setupProfilePic(@NonNull final Media media) {
|
||||||
final User user = media.getUser();
|
final User user = media.getUser();
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
topBinding.ivProfilePic.setVisibility(View.GONE);
|
topBinding.profilePic.setVisibility(View.GONE);
|
||||||
topBinding.title.setVisibility(View.GONE);
|
topBinding.title.setVisibility(View.GONE);
|
||||||
|
topBinding.subtitle.setVisibility(View.GONE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
topBinding.ivProfilePic.setOnClickListener(v -> feedItemCallback.onProfilePicClick(media, topBinding.ivProfilePic));
|
topBinding.profilePic.setOnClickListener(v -> feedItemCallback.onProfilePicClick(media));
|
||||||
topBinding.ivProfilePic.setImageURI(user.getProfilePicUrl());
|
topBinding.profilePic.setImageURI(user.getProfilePicUrl());
|
||||||
setupTitle(media);
|
setupTitle(media);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,68 +86,97 @@ public abstract class FeedItemViewHolder extends RecyclerView.ViewHolder {
|
|||||||
// spannableString.setSpan(new CommentMentionClickSpan(), 0, titleLen, 0);
|
// spannableString.setSpan(new CommentMentionClickSpan(), 0, titleLen, 0);
|
||||||
final User user = media.getUser();
|
final User user = media.getUser();
|
||||||
if (user == null) return;
|
if (user == null) return;
|
||||||
final String title = "@" + user.getUsername();
|
setUsername(user);
|
||||||
topBinding.title.setText(title);
|
topBinding.title.setOnClickListener(v -> feedItemCallback.onNameClick(media));
|
||||||
topBinding.title.setOnClickListener(v -> feedItemCallback.onNameClick(media, topBinding.ivProfilePic));
|
final String fullName = user.getFullName();
|
||||||
|
if (TextUtils.isEmpty(fullName)) {
|
||||||
|
topBinding.subtitle.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
topBinding.subtitle.setVisibility(View.VISIBLE);
|
||||||
|
topBinding.subtitle.setText(fullName);
|
||||||
|
}
|
||||||
|
topBinding.subtitle.setOnClickListener(v -> feedItemCallback.onNameClick(media));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupCaption(final Media media) {
|
private void setupCaption(final Media media) {
|
||||||
bottomBinding.viewerCaption.clearOnMentionClickListeners();
|
bottomBinding.caption.clearOnMentionClickListeners();
|
||||||
bottomBinding.viewerCaption.clearOnHashtagClickListeners();
|
bottomBinding.caption.clearOnHashtagClickListeners();
|
||||||
bottomBinding.viewerCaption.clearOnURLClickListeners();
|
bottomBinding.caption.clearOnURLClickListeners();
|
||||||
bottomBinding.viewerCaption.clearOnEmailClickListeners();
|
bottomBinding.caption.clearOnEmailClickListeners();
|
||||||
final Caption caption = media.getCaption();
|
final Caption caption = media.getCaption();
|
||||||
if (caption == null) {
|
if (caption == null) {
|
||||||
bottomBinding.viewerCaption.setVisibility(View.GONE);
|
bottomBinding.caption.setVisibility(View.GONE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final CharSequence postCaption = caption.getText();
|
final CharSequence postCaption = caption.getText();
|
||||||
final boolean captionEmpty = TextUtils.isEmpty(postCaption);
|
final boolean captionEmpty = TextUtils.isEmpty(postCaption);
|
||||||
bottomBinding.viewerCaption.setVisibility(captionEmpty ? View.GONE : View.VISIBLE);
|
bottomBinding.caption.setVisibility(captionEmpty ? View.GONE : View.VISIBLE);
|
||||||
if (captionEmpty) return;
|
if (captionEmpty) return;
|
||||||
bottomBinding.viewerCaption.setText(postCaption);
|
bottomBinding.caption.setText(postCaption);
|
||||||
bottomBinding.viewerCaption.setMaxLines(MAX_LINES_COLLAPSED);
|
bottomBinding.caption.setMaxLines(MAX_LINES_COLLAPSED);
|
||||||
bottomBinding.viewerCaption.setEllipsize(END);
|
bottomBinding.caption.setEllipsize(END);
|
||||||
bottomBinding.viewerCaption.setOnClickListener(v -> bottomBinding.getRoot().post(() -> {
|
bottomBinding.caption.setOnClickListener(v -> bottomFrame.post(() -> {
|
||||||
TransitionManager.beginDelayedTransition(bottomBinding.getRoot());
|
TransitionManager.beginDelayedTransition(bottomFrame);
|
||||||
if (bottomBinding.viewerCaption.getMaxLines() == MAX_LINES_COLLAPSED) {
|
if (bottomBinding.caption.getMaxLines() == MAX_LINES_COLLAPSED) {
|
||||||
bottomBinding.viewerCaption.setMaxLines(Integer.MAX_VALUE);
|
bottomBinding.caption.setMaxLines(Integer.MAX_VALUE);
|
||||||
bottomBinding.viewerCaption.setEllipsize(null);
|
bottomBinding.caption.setEllipsize(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bottomBinding.viewerCaption.setMaxLines(MAX_LINES_COLLAPSED);
|
bottomBinding.caption.setMaxLines(MAX_LINES_COLLAPSED);
|
||||||
bottomBinding.viewerCaption.setEllipsize(END);
|
bottomBinding.caption.setEllipsize(END);
|
||||||
}));
|
}));
|
||||||
bottomBinding.viewerCaption.addOnMentionClickListener(autoLinkItem -> feedItemCallback.onMentionClick(autoLinkItem.getOriginalText()));
|
bottomBinding.caption.addOnMentionClickListener(autoLinkItem -> feedItemCallback.onMentionClick(autoLinkItem.getOriginalText()));
|
||||||
bottomBinding.viewerCaption.addOnHashtagListener(autoLinkItem -> feedItemCallback.onHashtagClick(autoLinkItem.getOriginalText()));
|
bottomBinding.caption.addOnHashtagListener(autoLinkItem -> feedItemCallback.onHashtagClick(autoLinkItem.getOriginalText()));
|
||||||
bottomBinding.viewerCaption.addOnEmailClickListener(autoLinkItem -> feedItemCallback.onEmailClick(autoLinkItem.getOriginalText()));
|
bottomBinding.caption.addOnEmailClickListener(autoLinkItem -> feedItemCallback.onEmailClick(autoLinkItem.getOriginalText()));
|
||||||
bottomBinding.viewerCaption.addOnURLClickListener(autoLinkItem -> feedItemCallback.onURLClick(autoLinkItem.getOriginalText()));
|
bottomBinding.caption.addOnURLClickListener(autoLinkItem -> feedItemCallback.onURLClick(autoLinkItem.getOriginalText()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupLocation(@NonNull final Media media) {
|
private void setupLocation(@NonNull final Media media) {
|
||||||
final Location location = media.getLocation();
|
final Location location = media.getLocation();
|
||||||
if (location == null) {
|
if (location == null) {
|
||||||
topBinding.location.setVisibility(View.GONE);
|
topBinding.location.setVisibility(View.GONE);
|
||||||
topBinding.title.setLayoutParams(new RelativeLayout.LayoutParams(
|
|
||||||
RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT
|
|
||||||
));
|
|
||||||
} else {
|
} else {
|
||||||
final String locationName = location.getName();
|
final String locationName = location.getName();
|
||||||
if (TextUtils.isEmpty(locationName)) {
|
if (TextUtils.isEmpty(locationName)) {
|
||||||
topBinding.location.setVisibility(View.GONE);
|
topBinding.location.setVisibility(View.GONE);
|
||||||
topBinding.title.setLayoutParams(new RelativeLayout.LayoutParams(
|
|
||||||
RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT
|
|
||||||
));
|
|
||||||
} else {
|
} else {
|
||||||
topBinding.location.setVisibility(View.VISIBLE);
|
topBinding.location.setVisibility(View.VISIBLE);
|
||||||
topBinding.location.setText(locationName);
|
topBinding.location.setText(locationName);
|
||||||
topBinding.title.setLayoutParams(new RelativeLayout.LayoutParams(
|
|
||||||
RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT
|
|
||||||
));
|
|
||||||
topBinding.location.setOnClickListener(v -> feedItemCallback.onLocationClick(media));
|
topBinding.location.setOnClickListener(v -> feedItemCallback.onLocationClick(media));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setupActions(@NonNull final Media media) {
|
||||||
|
// temporary - to be set up later
|
||||||
|
bottomBinding.like.setVisibility(View.GONE);
|
||||||
|
bottomBinding.save.setVisibility(View.GONE);
|
||||||
|
bottomBinding.translate.setVisibility(View.GONE);
|
||||||
|
bottomBinding.share.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setUsername(final User user) {
|
||||||
|
final SpannableStringBuilder sb = new SpannableStringBuilder(user.getUsername());
|
||||||
|
final int drawableSize = Utils.convertDpToPx(24);
|
||||||
|
if (user.isVerified()) {
|
||||||
|
final Drawable verifiedDrawable = itemView.getResources().getDrawable(R.drawable.verified);
|
||||||
|
VerticalImageSpan verifiedSpan = null;
|
||||||
|
if (verifiedDrawable != null) {
|
||||||
|
final Drawable drawable = verifiedDrawable.mutate();
|
||||||
|
drawable.setBounds(0, 0, drawableSize, drawableSize);
|
||||||
|
verifiedSpan = new VerticalImageSpan(drawable);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (verifiedSpan != null) {
|
||||||
|
sb.append(" ");
|
||||||
|
sb.setSpan(verifiedSpan, sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e("FeedItemViewHolder", "setUsername: ", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
topBinding.title.setText(sb);
|
||||||
|
}
|
||||||
|
|
||||||
public abstract void bindItem(final Media media);
|
public abstract void bindItem(final Media media);
|
||||||
}
|
}
|
@ -16,6 +16,7 @@ import com.facebook.imagepipeline.request.ImageRequestBuilder;
|
|||||||
|
|
||||||
import awais.instagrabber.adapters.FeedAdapterV2;
|
import awais.instagrabber.adapters.FeedAdapterV2;
|
||||||
import awais.instagrabber.databinding.ItemFeedPhotoBinding;
|
import awais.instagrabber.databinding.ItemFeedPhotoBinding;
|
||||||
|
import awais.instagrabber.databinding.LayoutPostViewBottomBinding;
|
||||||
import awais.instagrabber.repositories.responses.Media;
|
import awais.instagrabber.repositories.responses.Media;
|
||||||
import awais.instagrabber.utils.ResponseBodyUtils;
|
import awais.instagrabber.utils.ResponseBodyUtils;
|
||||||
import awais.instagrabber.utils.TextUtils;
|
import awais.instagrabber.utils.TextUtils;
|
||||||
@ -28,10 +29,11 @@ public class FeedPhotoViewHolder extends FeedItemViewHolder {
|
|||||||
|
|
||||||
public FeedPhotoViewHolder(@NonNull final ItemFeedPhotoBinding binding,
|
public FeedPhotoViewHolder(@NonNull final ItemFeedPhotoBinding binding,
|
||||||
final FeedAdapterV2.FeedItemCallback feedItemCallback) {
|
final FeedAdapterV2.FeedItemCallback feedItemCallback) {
|
||||||
super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, feedItemCallback);
|
super(binding.getRoot(), feedItemCallback);
|
||||||
this.binding = binding;
|
this.binding = binding;
|
||||||
this.feedItemCallback = feedItemCallback;
|
this.feedItemCallback = feedItemCallback;
|
||||||
binding.itemFeedBottom.btnViews.setVisibility(View.GONE);
|
final LayoutPostViewBottomBinding bottom = LayoutPostViewBottomBinding.bind(binding.getRoot());
|
||||||
|
bottom.viewsCount.setVisibility(View.GONE);
|
||||||
// binding.itemFeedBottom.btnMute.setVisibility(View.GONE);
|
// binding.itemFeedBottom.btnMute.setVisibility(View.GONE);
|
||||||
binding.imageViewer.setAllowTouchInterceptionWhileZoomed(false);
|
binding.imageViewer.setAllowTouchInterceptionWhileZoomed(false);
|
||||||
final GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(itemView.getContext().getResources())
|
final GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(itemView.getContext().getResources())
|
||||||
@ -61,7 +63,7 @@ public class FeedPhotoViewHolder extends FeedItemViewHolder {
|
|||||||
@Override
|
@Override
|
||||||
public boolean onSingleTapConfirmed(final MotionEvent e) {
|
public boolean onSingleTapConfirmed(final MotionEvent e) {
|
||||||
if (feedItemCallback != null) {
|
if (feedItemCallback != null) {
|
||||||
feedItemCallback.onPostClick(media, binding.itemFeedTop.ivProfilePic, binding.imageViewer);
|
feedItemCallback.onPostClick(media);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -13,6 +13,7 @@ import awais.instagrabber.adapters.FeedAdapterV2;
|
|||||||
import awais.instagrabber.adapters.SliderCallbackAdapter;
|
import awais.instagrabber.adapters.SliderCallbackAdapter;
|
||||||
import awais.instagrabber.adapters.SliderItemsAdapter;
|
import awais.instagrabber.adapters.SliderItemsAdapter;
|
||||||
import awais.instagrabber.databinding.ItemFeedSliderBinding;
|
import awais.instagrabber.databinding.ItemFeedSliderBinding;
|
||||||
|
import awais.instagrabber.databinding.LayoutPostViewBottomBinding;
|
||||||
import awais.instagrabber.repositories.responses.Media;
|
import awais.instagrabber.repositories.responses.Media;
|
||||||
import awais.instagrabber.utils.NumberUtils;
|
import awais.instagrabber.utils.NumberUtils;
|
||||||
import awais.instagrabber.utils.Utils;
|
import awais.instagrabber.utils.Utils;
|
||||||
@ -23,14 +24,16 @@ public class FeedSliderViewHolder extends FeedItemViewHolder {
|
|||||||
|
|
||||||
private final ItemFeedSliderBinding binding;
|
private final ItemFeedSliderBinding binding;
|
||||||
private final FeedAdapterV2.FeedItemCallback feedItemCallback;
|
private final FeedAdapterV2.FeedItemCallback feedItemCallback;
|
||||||
|
private final LayoutPostViewBottomBinding bottom;
|
||||||
|
|
||||||
public FeedSliderViewHolder(@NonNull final ItemFeedSliderBinding binding,
|
public FeedSliderViewHolder(@NonNull final ItemFeedSliderBinding binding,
|
||||||
final FeedAdapterV2.FeedItemCallback feedItemCallback) {
|
final FeedAdapterV2.FeedItemCallback feedItemCallback) {
|
||||||
super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, feedItemCallback);
|
super(binding.getRoot(), feedItemCallback);
|
||||||
this.binding = binding;
|
this.binding = binding;
|
||||||
this.feedItemCallback = feedItemCallback;
|
this.feedItemCallback = feedItemCallback;
|
||||||
binding.itemFeedBottom.btnViews.setVisibility(View.GONE);
|
bottom = LayoutPostViewBottomBinding.bind(binding.getRoot());
|
||||||
// binding.itemFeedBottom.btnMute.setVisibility(View.GONE);
|
bottom.viewsCount.setVisibility(View.GONE);
|
||||||
|
// bottom.btnMute.setVisibility(View.GONE);
|
||||||
final ViewGroup.LayoutParams layoutParams = binding.mediaList.getLayoutParams();
|
final ViewGroup.LayoutParams layoutParams = binding.mediaList.getLayoutParams();
|
||||||
layoutParams.height = Utils.displayMetrics.widthPixels + 1;
|
layoutParams.height = Utils.displayMetrics.widthPixels + 1;
|
||||||
binding.mediaList.setLayoutParams(layoutParams);
|
binding.mediaList.setLayoutParams(layoutParams);
|
||||||
@ -59,14 +62,14 @@ public class FeedSliderViewHolder extends FeedItemViewHolder {
|
|||||||
final String text = (position + 1) + "/" + sliderItemLen;
|
final String text = (position + 1) + "/" + sliderItemLen;
|
||||||
binding.mediaCounter.setText(text);
|
binding.mediaCounter.setText(text);
|
||||||
setDimensions(binding.mediaList, sliderItems.get(position));
|
setDimensions(binding.mediaList, sliderItems.get(position));
|
||||||
binding.itemFeedBottom.btnDownload.setOnClickListener(v ->
|
bottom.download.setOnClickListener(v ->
|
||||||
feedItemCallback.onDownloadClick(feedModel, position, binding.itemFeedBottom.btnDownload)
|
feedItemCallback.onDownloadClick(feedModel, position, bottom.download)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
setDimensions(binding.mediaList, sliderItems.get(0));
|
setDimensions(binding.mediaList, sliderItems.get(0));
|
||||||
binding.itemFeedBottom.btnDownload.setOnClickListener(v ->
|
bottom.download.setOnClickListener(v ->
|
||||||
feedItemCallback.onDownloadClick(feedModel, 0, binding.itemFeedBottom.btnDownload)
|
feedItemCallback.onDownloadClick(feedModel, 0, bottom.download)
|
||||||
);
|
);
|
||||||
adapter.submitList(sliderItems);
|
adapter.submitList(sliderItems);
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,12 @@ package awais.instagrabber.adapters.viewholder.feed;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||||
|
|
||||||
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
||||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory;
|
import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory;
|
||||||
@ -14,13 +16,17 @@ import com.google.android.exoplayer2.upstream.cache.SimpleCache;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import awais.instagrabber.R;
|
||||||
import awais.instagrabber.adapters.FeedAdapterV2;
|
import awais.instagrabber.adapters.FeedAdapterV2;
|
||||||
import awais.instagrabber.customviews.VideoPlayerCallbackAdapter;
|
import awais.instagrabber.customviews.VideoPlayerCallbackAdapter;
|
||||||
import awais.instagrabber.customviews.VideoPlayerViewHelper;
|
import awais.instagrabber.customviews.VideoPlayerViewHelper;
|
||||||
import awais.instagrabber.databinding.ItemFeedVideoBinding;
|
import awais.instagrabber.databinding.ItemFeedVideoBinding;
|
||||||
|
import awais.instagrabber.databinding.LayoutPostViewBottomBinding;
|
||||||
|
import awais.instagrabber.databinding.LayoutVideoPlayerWithThumbnailBinding;
|
||||||
import awais.instagrabber.fragments.settings.PreferenceKeys;
|
import awais.instagrabber.fragments.settings.PreferenceKeys;
|
||||||
import awais.instagrabber.repositories.responses.Media;
|
import awais.instagrabber.repositories.responses.Media;
|
||||||
import awais.instagrabber.repositories.responses.MediaCandidate;
|
import awais.instagrabber.repositories.responses.MediaCandidate;
|
||||||
|
import awais.instagrabber.utils.NullSafePair;
|
||||||
import awais.instagrabber.utils.NumberUtils;
|
import awais.instagrabber.utils.NumberUtils;
|
||||||
import awais.instagrabber.utils.ResponseBodyUtils;
|
import awais.instagrabber.utils.ResponseBodyUtils;
|
||||||
import awais.instagrabber.utils.Utils;
|
import awais.instagrabber.utils.Utils;
|
||||||
@ -35,6 +41,7 @@ public class FeedVideoViewHolder extends FeedItemViewHolder {
|
|||||||
private final Handler handler;
|
private final Handler handler;
|
||||||
private final DefaultDataSourceFactory dataSourceFactory;
|
private final DefaultDataSourceFactory dataSourceFactory;
|
||||||
|
|
||||||
|
private final LayoutPostViewBottomBinding bottom;
|
||||||
private CacheDataSourceFactory cacheDataSourceFactory;
|
private CacheDataSourceFactory cacheDataSourceFactory;
|
||||||
private Media media;
|
private Media media;
|
||||||
|
|
||||||
@ -47,10 +54,11 @@ public class FeedVideoViewHolder extends FeedItemViewHolder {
|
|||||||
|
|
||||||
public FeedVideoViewHolder(@NonNull final ItemFeedVideoBinding binding,
|
public FeedVideoViewHolder(@NonNull final ItemFeedVideoBinding binding,
|
||||||
final FeedAdapterV2.FeedItemCallback feedItemCallback) {
|
final FeedAdapterV2.FeedItemCallback feedItemCallback) {
|
||||||
super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, feedItemCallback);
|
super(binding.getRoot(), feedItemCallback);
|
||||||
|
bottom = LayoutPostViewBottomBinding.bind(binding.getRoot());
|
||||||
this.binding = binding;
|
this.binding = binding;
|
||||||
this.feedItemCallback = feedItemCallback;
|
this.feedItemCallback = feedItemCallback;
|
||||||
binding.itemFeedBottom.btnViews.setVisibility(View.VISIBLE);
|
bottom.viewsCount.setVisibility(View.VISIBLE);
|
||||||
handler = new Handler(Looper.getMainLooper());
|
handler = new Handler(Looper.getMainLooper());
|
||||||
final Context context = binding.getRoot().getContext();
|
final Context context = binding.getRoot().getContext();
|
||||||
dataSourceFactory = new DefaultDataSourceFactory(context, "instagram");
|
dataSourceFactory = new DefaultDataSourceFactory(context, "instagram");
|
||||||
@ -64,23 +72,35 @@ public class FeedVideoViewHolder extends FeedItemViewHolder {
|
|||||||
public void bindItem(final Media media) {
|
public void bindItem(final Media media) {
|
||||||
// Log.d(TAG, "Binding post: " + feedModel.getPostId());
|
// Log.d(TAG, "Binding post: " + feedModel.getPostId());
|
||||||
this.media = media;
|
this.media = media;
|
||||||
binding.itemFeedBottom.tvVideoViews.setText(String.valueOf(media.getViewCount()));
|
final String viewCount = itemView.getResources().getQuantityString(R.plurals.views_count, (int) media.getViewCount(), media.getViewCount());
|
||||||
|
bottom.viewsCount.setText(viewCount);
|
||||||
|
final LayoutVideoPlayerWithThumbnailBinding videoPost =
|
||||||
|
LayoutVideoPlayerWithThumbnailBinding.inflate(LayoutInflater.from(itemView.getContext()), binding.getRoot(), false);
|
||||||
|
final ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) videoPost.getRoot().getLayoutParams();
|
||||||
|
final NullSafePair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(media.getOriginalHeight(),
|
||||||
|
media.getOriginalWidth(),
|
||||||
|
(int) (Utils.displayMetrics.heightPixels * 0.8),
|
||||||
|
Utils.displayMetrics.widthPixels);
|
||||||
|
layoutParams.width = ConstraintLayout.LayoutParams.MATCH_PARENT;
|
||||||
|
layoutParams.height = widthHeight.second;
|
||||||
|
final View postView = videoPost.getRoot();
|
||||||
|
binding.postContainer.addView(postView);
|
||||||
final float vol = settingsHelper.getBoolean(PreferenceKeys.MUTED_VIDEOS) ? 0f : 1f;
|
final float vol = settingsHelper.getBoolean(PreferenceKeys.MUTED_VIDEOS) ? 0f : 1f;
|
||||||
final VideoPlayerViewHelper.VideoPlayerCallback videoPlayerCallback = new VideoPlayerCallbackAdapter() {
|
final VideoPlayerViewHelper.VideoPlayerCallback videoPlayerCallback = new VideoPlayerCallbackAdapter() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onThumbnailClick() {
|
public void onThumbnailClick() {
|
||||||
feedItemCallback.onPostClick(media, binding.itemFeedTop.ivProfilePic, binding.videoPost.thumbnail);
|
feedItemCallback.onPostClick(media);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPlayerViewLoaded() {
|
public void onPlayerViewLoaded() {
|
||||||
final ViewGroup.LayoutParams layoutParams = binding.videoPost.playerView.getLayoutParams();
|
final ViewGroup.LayoutParams layoutParams = videoPost.playerView.getLayoutParams();
|
||||||
final int requiredWidth = Utils.displayMetrics.widthPixels;
|
final int requiredWidth = Utils.displayMetrics.widthPixels;
|
||||||
final int resultingHeight = NumberUtils.getResultingHeight(requiredWidth, media.getOriginalHeight(), media.getOriginalWidth());
|
final int resultingHeight = NumberUtils.getResultingHeight(requiredWidth, media.getOriginalHeight(), media.getOriginalWidth());
|
||||||
layoutParams.width = requiredWidth;
|
layoutParams.width = requiredWidth;
|
||||||
layoutParams.height = resultingHeight;
|
layoutParams.height = resultingHeight;
|
||||||
binding.videoPost.playerView.requestLayout();
|
videoPost.playerView.requestLayout();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
final float aspectRatio = (float) media.getOriginalWidth() / media.getOriginalHeight();
|
final float aspectRatio = (float) media.getOriginalWidth() / media.getOriginalHeight();
|
||||||
@ -91,7 +111,7 @@ public class FeedVideoViewHolder extends FeedItemViewHolder {
|
|||||||
videoUrl = videoVersion.getUrl();
|
videoUrl = videoVersion.getUrl();
|
||||||
}
|
}
|
||||||
final VideoPlayerViewHelper videoPlayerViewHelper = new VideoPlayerViewHelper(binding.getRoot().getContext(),
|
final VideoPlayerViewHelper videoPlayerViewHelper = new VideoPlayerViewHelper(binding.getRoot().getContext(),
|
||||||
binding.videoPost,
|
videoPost,
|
||||||
videoUrl,
|
videoUrl,
|
||||||
vol,
|
vol,
|
||||||
aspectRatio,
|
aspectRatio,
|
||||||
@ -99,11 +119,11 @@ public class FeedVideoViewHolder extends FeedItemViewHolder {
|
|||||||
false,
|
false,
|
||||||
// null,
|
// null,
|
||||||
videoPlayerCallback);
|
videoPlayerCallback);
|
||||||
binding.videoPost.thumbnail.post(() -> {
|
videoPost.thumbnail.post(() -> {
|
||||||
if (media.getOriginalHeight() > 0.8 * Utils.displayMetrics.heightPixels) {
|
if (media.getOriginalHeight() > 0.8 * Utils.displayMetrics.heightPixels) {
|
||||||
final ViewGroup.LayoutParams layoutParams = binding.videoPost.thumbnail.getLayoutParams();
|
final ViewGroup.LayoutParams tLayoutParams = videoPost.thumbnail.getLayoutParams();
|
||||||
layoutParams.height = (int) (0.8 * Utils.displayMetrics.heightPixels);
|
tLayoutParams.height = (int) (0.8 * Utils.displayMetrics.heightPixels);
|
||||||
binding.videoPost.thumbnail.requestLayout();
|
videoPost.thumbnail.requestLayout();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -118,8 +138,8 @@ public class FeedVideoViewHolder extends FeedItemViewHolder {
|
|||||||
// if (player != null) {
|
// if (player != null) {
|
||||||
// player.release();
|
// player.release();
|
||||||
// }
|
// }
|
||||||
// if (binding.videoPost.root.getDisplayedChild() == 1) {
|
// if (videoPost.root.getDisplayedChild() == 1) {
|
||||||
// binding.videoPost.root.showPrevious();
|
// videoPost.root.showPrevious();
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
|
@ -94,7 +94,7 @@ public class ChatMessageLayout extends FrameLayout {
|
|||||||
heightSize += viewPartMainHeight;
|
heightSize += viewPartMainHeight;
|
||||||
} else if (firstChildId == R.id.raven_media_container || firstChildId == R.id.profile_container || firstChildId == R.id.voice_media
|
} else if (firstChildId == R.id.raven_media_container || firstChildId == R.id.profile_container || firstChildId == R.id.voice_media
|
||||||
|| firstChildId == R.id.story_container || firstChildId == R.id.media_share_container || firstChildId == R.id.link_container
|
|| firstChildId == R.id.story_container || firstChildId == R.id.media_share_container || firstChildId == R.id.link_container
|
||||||
|| firstChildId == R.id.ivAnimatedMessage) {
|
|| firstChildId == R.id.ivAnimatedMessage || firstChildId == R.id.reel_share_container) {
|
||||||
widthSize += viewPartMainWidth;
|
widthSize += viewPartMainWidth;
|
||||||
heightSize += viewPartMainHeight + viewPartInfoHeight;
|
heightSize += viewPartMainHeight + viewPartInfoHeight;
|
||||||
} else {
|
} else {
|
||||||
@ -104,12 +104,6 @@ public class ChatMessageLayout extends FrameLayout {
|
|||||||
if (firstChild instanceof TextView) {
|
if (firstChild instanceof TextView) {
|
||||||
textMessage = (TextView) firstChild;
|
textMessage = (TextView) firstChild;
|
||||||
}
|
}
|
||||||
else if (firstChildId == R.id.reel_share_container) {
|
|
||||||
textMessage = (TextView) ((ConstraintLayout) firstChild).getChildAt(5);
|
|
||||||
}
|
|
||||||
else if (firstChildId == R.id.story_container) {
|
|
||||||
textMessage = (TextView) ((ConstraintLayout) firstChild).getChildAt(2);
|
|
||||||
}
|
|
||||||
else textMessage = null;
|
else textMessage = null;
|
||||||
if (textMessage != null) {
|
if (textMessage != null) {
|
||||||
viewPartMainLineCount = textMessage.getLineCount();
|
viewPartMainLineCount = textMessage.getLineCount();
|
||||||
|
@ -56,6 +56,7 @@ import awais.instagrabber.repositories.responses.Location;
|
|||||||
import awais.instagrabber.repositories.responses.Media;
|
import awais.instagrabber.repositories.responses.Media;
|
||||||
import awais.instagrabber.repositories.responses.User;
|
import awais.instagrabber.repositories.responses.User;
|
||||||
import awais.instagrabber.repositories.responses.saved.SavedCollection;
|
import awais.instagrabber.repositories.responses.saved.SavedCollection;
|
||||||
|
import awais.instagrabber.utils.AppExecutors;
|
||||||
import awais.instagrabber.utils.Constants;
|
import awais.instagrabber.utils.Constants;
|
||||||
import awais.instagrabber.utils.CookieUtils;
|
import awais.instagrabber.utils.CookieUtils;
|
||||||
import awais.instagrabber.utils.DownloadUtils;
|
import awais.instagrabber.utils.DownloadUtils;
|
||||||
@ -105,7 +106,7 @@ public class CollectionPostsFragment extends Fragment implements SwipeRefreshLay
|
|||||||
});
|
});
|
||||||
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
|
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onPostClick(final Media feedModel, final View profilePicView, final View mainPostImage) {
|
public void onPostClick(final Media feedModel) {
|
||||||
openPostDialog(feedModel, -1);
|
openPostDialog(feedModel, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,14 +166,14 @@ public class CollectionPostsFragment extends Fragment implements SwipeRefreshLay
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNameClick(final Media feedModel, final View profilePicView) {
|
public void onNameClick(final Media feedModel) {
|
||||||
final User user = feedModel.getUser();
|
final User user = feedModel.getUser();
|
||||||
if (user == null) return;
|
if (user == null) return;
|
||||||
navigateToProfile("@" + user.getUsername());
|
navigateToProfile("@" + user.getUsername());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onProfilePicClick(final Media feedModel, final View profilePicView) {
|
public void onProfilePicClick(final Media feedModel) {
|
||||||
final User user = feedModel.getUser();
|
final User user = feedModel.getUser();
|
||||||
if (user == null) return;
|
if (user == null) return;
|
||||||
navigateToProfile("@" + user.getUsername());
|
navigateToProfile("@" + user.getUsername());
|
||||||
@ -461,7 +462,9 @@ public class CollectionPostsFragment extends Fragment implements SwipeRefreshLay
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateSwipeRefreshState() {
|
private void updateSwipeRefreshState() {
|
||||||
binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching());
|
AppExecutors.INSTANCE.getMainThread().execute(() ->
|
||||||
|
binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void navigateToProfile(final String username) {
|
private void navigateToProfile(final String username) {
|
||||||
|
@ -1,456 +0,0 @@
|
|||||||
package awais.instagrabber.fragments;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuInflater;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.appcompat.app.ActionBar;
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
|
||||||
import androidx.appcompat.widget.SearchView;
|
|
||||||
import androidx.fragment.app.Fragment;
|
|
||||||
import androidx.navigation.NavDirections;
|
|
||||||
import androidx.navigation.fragment.NavHostFragment;
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import awais.instagrabber.R;
|
|
||||||
import awais.instagrabber.adapters.FollowAdapter;
|
|
||||||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
|
|
||||||
import awais.instagrabber.databinding.FragmentFollowersViewerBinding;
|
|
||||||
import awais.instagrabber.models.FollowModel;
|
|
||||||
import awais.instagrabber.repositories.responses.FriendshipListFetchResponse;
|
|
||||||
import awais.instagrabber.utils.AppExecutors;
|
|
||||||
import awais.instagrabber.utils.CoroutineUtilsKt;
|
|
||||||
import awais.instagrabber.utils.TextUtils;
|
|
||||||
import awais.instagrabber.webservices.FriendshipRepository;
|
|
||||||
import awais.instagrabber.webservices.ServiceCallback;
|
|
||||||
import kotlinx.coroutines.Dispatchers;
|
|
||||||
import thoughtbot.expandableadapter.ExpandableGroup;
|
|
||||||
|
|
||||||
public final class FollowViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
|
||||||
private static final String TAG = "FollowViewerFragment";
|
|
||||||
|
|
||||||
private final ArrayList<FollowModel> followModels = new ArrayList<>();
|
|
||||||
private final ArrayList<FollowModel> followingModels = new ArrayList<>();
|
|
||||||
private final ArrayList<FollowModel> followersModels = new ArrayList<>();
|
|
||||||
private final ArrayList<FollowModel> allFollowing = new ArrayList<>();
|
|
||||||
|
|
||||||
private boolean moreAvailable = true, isFollowersList, isCompare = false, loading = false, shouldRefresh = true, searching = false;
|
|
||||||
private long profileId;
|
|
||||||
private String username;
|
|
||||||
private String namePost;
|
|
||||||
private String type;
|
|
||||||
private String endCursor;
|
|
||||||
private Resources resources;
|
|
||||||
private LinearLayoutManager layoutManager;
|
|
||||||
private RecyclerLazyLoader lazyLoader;
|
|
||||||
private FollowModel model;
|
|
||||||
private FollowAdapter adapter;
|
|
||||||
private View.OnClickListener clickListener;
|
|
||||||
private FragmentFollowersViewerBinding binding;
|
|
||||||
private SwipeRefreshLayout root;
|
|
||||||
private FriendshipRepository friendshipRepository;
|
|
||||||
private AppCompatActivity fragmentActivity;
|
|
||||||
|
|
||||||
final ServiceCallback<FriendshipListFetchResponse> followingFetchCb = new ServiceCallback<FriendshipListFetchResponse>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(final FriendshipListFetchResponse result) {
|
|
||||||
if (result != null && isCompare) {
|
|
||||||
followingModels.addAll(result.getItems());
|
|
||||||
if (!isFollowersList) followModels.addAll(result.getItems());
|
|
||||||
if (result.isMoreAvailable()) {
|
|
||||||
endCursor = result.getNextMaxId();
|
|
||||||
friendshipRepository.getList(
|
|
||||||
false,
|
|
||||||
profileId,
|
|
||||||
endCursor,
|
|
||||||
CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
|
|
||||||
if (throwable != null) {
|
|
||||||
onFailure(throwable);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onSuccess(response);
|
|
||||||
}), Dispatchers.getIO())
|
|
||||||
);
|
|
||||||
} else if (followersModels.size() == 0) {
|
|
||||||
if (!isFollowersList) moreAvailable = false;
|
|
||||||
friendshipRepository.getList(
|
|
||||||
true,
|
|
||||||
profileId,
|
|
||||||
null,
|
|
||||||
CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
|
|
||||||
if (throwable != null) {
|
|
||||||
followingFetchCb.onFailure(throwable);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
followingFetchCb.onSuccess(response);
|
|
||||||
}), Dispatchers.getIO())
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
if (!isFollowersList) moreAvailable = false;
|
|
||||||
showCompare();
|
|
||||||
}
|
|
||||||
} else if (isCompare) binding.swipeRefreshLayout.setRefreshing(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(final Throwable t) {
|
|
||||||
try {
|
|
||||||
binding.swipeRefreshLayout.setRefreshing(false);
|
|
||||||
Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
|
|
||||||
} catch (Throwable ignored) {}
|
|
||||||
Log.e(TAG, "Error fetching list (double, following)", t);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
final ServiceCallback<FriendshipListFetchResponse> followersFetchCb = new ServiceCallback<FriendshipListFetchResponse>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(final FriendshipListFetchResponse result) {
|
|
||||||
if (result != null && isCompare) {
|
|
||||||
followersModels.addAll(result.getItems());
|
|
||||||
if (isFollowersList) followModels.addAll(result.getItems());
|
|
||||||
if (result.isMoreAvailable()) {
|
|
||||||
endCursor = result.getNextMaxId();
|
|
||||||
friendshipRepository.getList(
|
|
||||||
true,
|
|
||||||
profileId,
|
|
||||||
endCursor,
|
|
||||||
CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
|
|
||||||
if (throwable != null) {
|
|
||||||
onFailure(throwable);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onSuccess(response);
|
|
||||||
}), Dispatchers.getIO())
|
|
||||||
);
|
|
||||||
} else if (followingModels.size() == 0) {
|
|
||||||
if (isFollowersList) moreAvailable = false;
|
|
||||||
friendshipRepository.getList(
|
|
||||||
false,
|
|
||||||
profileId,
|
|
||||||
null,
|
|
||||||
CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
|
|
||||||
if (throwable != null) {
|
|
||||||
followingFetchCb.onFailure(throwable);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
followingFetchCb.onSuccess(response);
|
|
||||||
}), Dispatchers.getIO())
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
if (isFollowersList) moreAvailable = false;
|
|
||||||
showCompare();
|
|
||||||
}
|
|
||||||
} else if (isCompare) binding.swipeRefreshLayout.setRefreshing(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(final Throwable t) {
|
|
||||||
try {
|
|
||||||
binding.swipeRefreshLayout.setRefreshing(false);
|
|
||||||
Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
|
|
||||||
} catch (Throwable ignored) {}
|
|
||||||
Log.e(TAG, "Error fetching list (double, follower)", t);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
friendshipRepository = FriendshipRepository.Companion.getInstance();
|
|
||||||
fragmentActivity = (AppCompatActivity) getActivity();
|
|
||||||
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 = FragmentFollowersViewerBinding.inflate(getLayoutInflater());
|
|
||||||
root = binding.getRoot();
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
|
|
||||||
if (!shouldRefresh) return;
|
|
||||||
init();
|
|
||||||
shouldRefresh = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init() {
|
|
||||||
if (getArguments() == null) return;
|
|
||||||
final FollowViewerFragmentArgs fragmentArgs = FollowViewerFragmentArgs.fromBundle(getArguments());
|
|
||||||
profileId = fragmentArgs.getProfileId();
|
|
||||||
isFollowersList = fragmentArgs.getIsFollowersList();
|
|
||||||
username = fragmentArgs.getUsername();
|
|
||||||
namePost = username;
|
|
||||||
if (TextUtils.isEmpty(username)) {
|
|
||||||
// this usually should not occur
|
|
||||||
username = "You";
|
|
||||||
namePost = "You're";
|
|
||||||
}
|
|
||||||
setTitle(username);
|
|
||||||
resources = getResources();
|
|
||||||
clickListener = v -> {
|
|
||||||
final Object tag = v.getTag();
|
|
||||||
if (tag instanceof FollowModel) {
|
|
||||||
model = (FollowModel) tag;
|
|
||||||
try {
|
|
||||||
final NavDirections action = FollowViewerFragmentDirections.actionToProfile().setUsername(model.getUsername());
|
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(TAG, "init: ", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
binding.swipeRefreshLayout.setOnRefreshListener(this);
|
|
||||||
onRefresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
setTitle(username);
|
|
||||||
setSubtitle(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setTitle(final String title) {
|
|
||||||
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
|
|
||||||
if (actionBar == null) return;
|
|
||||||
actionBar.setTitle(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setSubtitle(final String subtitle) {
|
|
||||||
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
|
|
||||||
if (actionBar == null) return;
|
|
||||||
actionBar.setSubtitle(subtitle);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setSubtitle(@SuppressWarnings("SameParameterValue") final int subtitleRes) {
|
|
||||||
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
|
|
||||||
if (actionBar == null) return;
|
|
||||||
actionBar.setSubtitle(subtitleRes);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRefresh() {
|
|
||||||
if (isCompare) listCompare();
|
|
||||||
else listFollows();
|
|
||||||
endCursor = null;
|
|
||||||
lazyLoader.resetState();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void listFollows() {
|
|
||||||
type = resources.getString(isFollowersList ? R.string.followers_type_followers : R.string.followers_type_following);
|
|
||||||
setSubtitle(type);
|
|
||||||
final ServiceCallback<FriendshipListFetchResponse> cb = new ServiceCallback<FriendshipListFetchResponse>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(final FriendshipListFetchResponse result) {
|
|
||||||
if (result == null) {
|
|
||||||
binding.swipeRefreshLayout.setRefreshing(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int oldSize = followModels.size() == 0 ? 0 : followModels.size() - 1;
|
|
||||||
followModels.addAll(result.getItems());
|
|
||||||
if (result.isMoreAvailable()) {
|
|
||||||
moreAvailable = true;
|
|
||||||
endCursor = result.getNextMaxId();
|
|
||||||
} else moreAvailable = false;
|
|
||||||
binding.swipeRefreshLayout.setRefreshing(false);
|
|
||||||
if (isFollowersList) followersModels.addAll(result.getItems());
|
|
||||||
else followingModels.addAll(result.getItems());
|
|
||||||
refreshAdapter(followModels, null, null, null);
|
|
||||||
layoutManager.scrollToPosition(oldSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(final Throwable t) {
|
|
||||||
try {
|
|
||||||
binding.swipeRefreshLayout.setRefreshing(false);
|
|
||||||
Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
|
|
||||||
} catch (Throwable ignored) {}
|
|
||||||
Log.e(TAG, "Error fetching list (single)", t);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
layoutManager = new LinearLayoutManager(getContext());
|
|
||||||
lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
|
|
||||||
if (!TextUtils.isEmpty(endCursor) && !searching) {
|
|
||||||
binding.swipeRefreshLayout.setRefreshing(true);
|
|
||||||
layoutManager.setStackFromEnd(true);
|
|
||||||
friendshipRepository.getList(
|
|
||||||
isFollowersList,
|
|
||||||
profileId,
|
|
||||||
endCursor,
|
|
||||||
CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
|
|
||||||
if (throwable != null) {
|
|
||||||
cb.onFailure(throwable);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cb.onSuccess(response);
|
|
||||||
}), Dispatchers.getIO())
|
|
||||||
);
|
|
||||||
endCursor = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
binding.rvFollow.addOnScrollListener(lazyLoader);
|
|
||||||
binding.rvFollow.setLayoutManager(layoutManager);
|
|
||||||
if (moreAvailable) {
|
|
||||||
binding.swipeRefreshLayout.setRefreshing(true);
|
|
||||||
friendshipRepository.getList(
|
|
||||||
isFollowersList,
|
|
||||||
profileId,
|
|
||||||
endCursor,
|
|
||||||
CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
|
|
||||||
if (throwable != null) {
|
|
||||||
cb.onFailure(throwable);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cb.onSuccess(response);
|
|
||||||
}), Dispatchers.getIO())
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
refreshAdapter(followModels, null, null, null);
|
|
||||||
layoutManager.scrollToPosition(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void listCompare() {
|
|
||||||
layoutManager.setStackFromEnd(false);
|
|
||||||
binding.rvFollow.clearOnScrollListeners();
|
|
||||||
loading = true;
|
|
||||||
setSubtitle(R.string.followers_compare);
|
|
||||||
allFollowing.clear();
|
|
||||||
if (moreAvailable) {
|
|
||||||
binding.swipeRefreshLayout.setRefreshing(true);
|
|
||||||
Toast.makeText(getContext(), R.string.follower_start_compare, Toast.LENGTH_LONG).show();
|
|
||||||
friendshipRepository.getList(
|
|
||||||
isFollowersList,
|
|
||||||
profileId,
|
|
||||||
endCursor,
|
|
||||||
CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
|
|
||||||
final ServiceCallback<FriendshipListFetchResponse> callback = isFollowersList ? followersFetchCb : followingFetchCb;
|
|
||||||
if (throwable != null) {
|
|
||||||
callback.onFailure(throwable);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
callback.onSuccess(response);
|
|
||||||
}), Dispatchers.getIO())
|
|
||||||
);
|
|
||||||
} else if (followersModels.size() == 0 || followingModels.size() == 0) {
|
|
||||||
binding.swipeRefreshLayout.setRefreshing(true);
|
|
||||||
Toast.makeText(getContext(), R.string.follower_start_compare, Toast.LENGTH_LONG).show();
|
|
||||||
friendshipRepository.getList(
|
|
||||||
!isFollowersList,
|
|
||||||
profileId,
|
|
||||||
null,
|
|
||||||
CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
|
|
||||||
final ServiceCallback<FriendshipListFetchResponse> callback = isFollowersList ? followingFetchCb : followersFetchCb;
|
|
||||||
if (throwable != null) {
|
|
||||||
callback.onFailure(throwable);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
callback.onSuccess(response);
|
|
||||||
}), Dispatchers.getIO()));
|
|
||||||
} else showCompare();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showCompare() {
|
|
||||||
allFollowing.addAll(followersModels);
|
|
||||||
allFollowing.retainAll(followingModels);
|
|
||||||
|
|
||||||
for (final FollowModel followModel : allFollowing) {
|
|
||||||
followersModels.remove(followModel);
|
|
||||||
followingModels.remove(followModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
allFollowing.trimToSize();
|
|
||||||
followersModels.trimToSize();
|
|
||||||
followingModels.trimToSize();
|
|
||||||
|
|
||||||
binding.swipeRefreshLayout.setRefreshing(false);
|
|
||||||
|
|
||||||
refreshAdapter(null, followingModels, followersModels, allFollowing);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreateOptionsMenu(@NonNull final Menu menu, final MenuInflater inflater) {
|
|
||||||
inflater.inflate(R.menu.follow, menu);
|
|
||||||
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 (TextUtils.isEmpty(query)) {
|
|
||||||
searching = false;
|
|
||||||
// refreshAdapter(followModels, followingModels, followersModels, allFollowing);
|
|
||||||
}
|
|
||||||
// else filter.filter(query.toLowerCase());
|
|
||||||
if (adapter != null) {
|
|
||||||
searching = true;
|
|
||||||
adapter.getFilter().filter(query);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
|
|
||||||
if (item.getItemId() != R.id.action_compare) return super.onOptionsItemSelected(item);
|
|
||||||
binding.rvFollow.setAdapter(null);
|
|
||||||
final Context context = getContext();
|
|
||||||
if (loading) Toast.makeText(context, R.string.follower_wait_to_load, Toast.LENGTH_LONG).show();
|
|
||||||
else if (isCompare) {
|
|
||||||
isCompare = false;
|
|
||||||
listFollows();
|
|
||||||
} else {
|
|
||||||
isCompare = true;
|
|
||||||
listCompare();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void refreshAdapter(final ArrayList<FollowModel> followModels,
|
|
||||||
final ArrayList<FollowModel> followingModels,
|
|
||||||
final ArrayList<FollowModel> followersModels,
|
|
||||||
final ArrayList<FollowModel> allFollowing) {
|
|
||||||
loading = false;
|
|
||||||
final ArrayList<ExpandableGroup> groups = new ArrayList<>(1);
|
|
||||||
|
|
||||||
if (isCompare && followingModels != null && followersModels != null && allFollowing != null) {
|
|
||||||
if (followingModels.size() > 0)
|
|
||||||
groups.add(new ExpandableGroup(resources.getString(R.string.followers_not_following, username), followingModels));
|
|
||||||
if (followersModels.size() > 0)
|
|
||||||
groups.add(new ExpandableGroup(resources.getString(R.string.followers_not_follower, namePost), followersModels));
|
|
||||||
if (allFollowing.size() > 0)
|
|
||||||
groups.add(new ExpandableGroup(resources.getString(R.string.followers_both_following), allFollowing));
|
|
||||||
} else if (followModels != null) {
|
|
||||||
groups.add(new ExpandableGroup(type, followModels));
|
|
||||||
} else return;
|
|
||||||
adapter = new FollowAdapter(clickListener, groups);
|
|
||||||
adapter.toggleGroup(0);
|
|
||||||
binding.rvFollow.setAdapter(adapter);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,260 @@
|
|||||||
|
package awais.instagrabber.fragments
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.Resources
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuInflater
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.Toast
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull
|
||||||
|
import androidx.annotation.Nullable
|
||||||
|
import androidx.appcompat.app.ActionBar
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.appcompat.widget.SearchView
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.navigation.fragment.NavHostFragment
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
|
|
||||||
|
import java.util.ArrayList
|
||||||
|
|
||||||
|
import awais.instagrabber.R
|
||||||
|
import awais.instagrabber.adapters.FollowAdapter
|
||||||
|
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader
|
||||||
|
import awais.instagrabber.databinding.FragmentFollowersViewerBinding
|
||||||
|
import awais.instagrabber.models.Resource
|
||||||
|
import awais.instagrabber.repositories.responses.User
|
||||||
|
import awais.instagrabber.utils.AppExecutors
|
||||||
|
import awais.instagrabber.viewmodels.FollowViewModel
|
||||||
|
import thoughtbot.expandableadapter.ExpandableGroup
|
||||||
|
|
||||||
|
class FollowViewerFragment : Fragment(), SwipeRefreshLayout.OnRefreshListener {
|
||||||
|
private val followModels: ArrayList<User> = ArrayList<User>()
|
||||||
|
private val followingModels: ArrayList<User> = ArrayList<User>()
|
||||||
|
private val followersModels: ArrayList<User> = ArrayList<User>()
|
||||||
|
private val allFollowing: ArrayList<User> = ArrayList<User>()
|
||||||
|
private val moreAvailable = true
|
||||||
|
private var isFollowersList = false
|
||||||
|
private var isCompare = false
|
||||||
|
private var shouldRefresh = true
|
||||||
|
private var searching = false
|
||||||
|
private var profileId: Long = 0
|
||||||
|
private var username: String? = null
|
||||||
|
private var namePost: String? = null
|
||||||
|
private var type = 0
|
||||||
|
private var root: SwipeRefreshLayout? = null
|
||||||
|
private var adapter: FollowAdapter? = null
|
||||||
|
private lateinit var lazyLoader: RecyclerLazyLoader
|
||||||
|
private lateinit var fragmentActivity: AppCompatActivity
|
||||||
|
private lateinit var viewModel: FollowViewModel
|
||||||
|
private lateinit var binding: FragmentFollowersViewerBinding
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
fragmentActivity = activity as AppCompatActivity
|
||||||
|
viewModel = ViewModelProvider(this).get(FollowViewModel::class.java)
|
||||||
|
setHasOptionsMenu(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
|
if (root != null) {
|
||||||
|
shouldRefresh = false
|
||||||
|
return root!!
|
||||||
|
}
|
||||||
|
binding = FragmentFollowersViewerBinding.inflate(layoutInflater)
|
||||||
|
root = binding.root
|
||||||
|
return root!!
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
if (!shouldRefresh) return
|
||||||
|
init()
|
||||||
|
shouldRefresh = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun init() {
|
||||||
|
val args = arguments ?: return
|
||||||
|
val fragmentArgs = FollowViewerFragmentArgs.fromBundle(args)
|
||||||
|
viewModel.userId.value = fragmentArgs.profileId
|
||||||
|
isFollowersList = fragmentArgs.isFollowersList
|
||||||
|
username = fragmentArgs.username
|
||||||
|
namePost = username
|
||||||
|
setTitle(username)
|
||||||
|
binding.swipeRefreshLayout.setOnRefreshListener(this)
|
||||||
|
if (isCompare) listCompare() else listFollows()
|
||||||
|
viewModel.fetch(isFollowersList, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
setTitle(username)
|
||||||
|
setSubtitle(type)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setTitle(title: String?) {
|
||||||
|
val actionBar: ActionBar = fragmentActivity.supportActionBar ?: return
|
||||||
|
actionBar.title = title
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setSubtitle(subtitleRes: Int) {
|
||||||
|
val actionBar: ActionBar = fragmentActivity.supportActionBar ?: return
|
||||||
|
actionBar.setSubtitle(subtitleRes)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRefresh() {
|
||||||
|
lazyLoader.resetState()
|
||||||
|
viewModel.clearProgress()
|
||||||
|
if (isCompare) listCompare()
|
||||||
|
else viewModel.fetch(isFollowersList, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun listFollows() {
|
||||||
|
viewModel.comparison.removeObservers(viewLifecycleOwner)
|
||||||
|
viewModel.status.removeObservers(viewLifecycleOwner)
|
||||||
|
type = if (isFollowersList) R.string.followers_type_followers else R.string.followers_type_following
|
||||||
|
setSubtitle(type)
|
||||||
|
val layoutManager = LinearLayoutManager(context)
|
||||||
|
lazyLoader = RecyclerLazyLoader(layoutManager, { _, totalItemsCount ->
|
||||||
|
binding.swipeRefreshLayout.isRefreshing = true
|
||||||
|
val liveData = if (searching) viewModel.search(isFollowersList)
|
||||||
|
else viewModel.fetch(isFollowersList, null)
|
||||||
|
liveData.observe(viewLifecycleOwner) {
|
||||||
|
binding.swipeRefreshLayout.isRefreshing = it.status != Resource.Status.SUCCESS
|
||||||
|
layoutManager.scrollToPosition(totalItemsCount)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
binding.rvFollow.addOnScrollListener(lazyLoader)
|
||||||
|
binding.rvFollow.layoutManager = layoutManager
|
||||||
|
viewModel.getList(isFollowersList).observe(viewLifecycleOwner) {
|
||||||
|
binding.swipeRefreshLayout.isRefreshing = false
|
||||||
|
refreshAdapter(it, null, null, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun listCompare() {
|
||||||
|
viewModel.getList(isFollowersList).removeObservers(viewLifecycleOwner)
|
||||||
|
binding.rvFollow.clearOnScrollListeners()
|
||||||
|
binding.swipeRefreshLayout.isRefreshing = true
|
||||||
|
setSubtitle(R.string.followers_compare)
|
||||||
|
viewModel.status.observe(viewLifecycleOwner) {}
|
||||||
|
viewModel.comparison.observe(viewLifecycleOwner) {
|
||||||
|
if (it != null) {
|
||||||
|
binding.swipeRefreshLayout.isRefreshing = false
|
||||||
|
refreshAdapter(null, it.first, it.second, it.third)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
|
inflater.inflate(R.menu.follow, menu)
|
||||||
|
val menuSearch = menu.findItem(R.id.action_search)
|
||||||
|
val searchView = menuSearch.actionView as SearchView
|
||||||
|
searchView.queryHint = resources.getString(R.string.action_search)
|
||||||
|
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||||
|
override fun onQueryTextSubmit(query: String): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onQueryTextChange(query: String): Boolean {
|
||||||
|
if (query.isNullOrEmpty()) {
|
||||||
|
if (!isCompare && searching) {
|
||||||
|
viewModel.setQuery(null, isFollowersList)
|
||||||
|
viewModel.getSearch().removeObservers(viewLifecycleOwner)
|
||||||
|
viewModel.getList(isFollowersList).observe(viewLifecycleOwner) {
|
||||||
|
refreshAdapter(it, null, null, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
searching = false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
searching = true
|
||||||
|
if (isCompare && adapter != null) {
|
||||||
|
adapter!!.filter.filter(query)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
viewModel.getList(isFollowersList).removeObservers(viewLifecycleOwner)
|
||||||
|
binding.swipeRefreshLayout.isRefreshing = true
|
||||||
|
viewModel.setQuery(query, isFollowersList)
|
||||||
|
viewModel.getSearch().observe(viewLifecycleOwner) {
|
||||||
|
binding.swipeRefreshLayout.isRefreshing = false
|
||||||
|
refreshAdapter(it, null, null, null)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
if (item.itemId != R.id.action_compare) return super.onOptionsItemSelected(item)
|
||||||
|
binding.rvFollow.adapter = null
|
||||||
|
if (isCompare) {
|
||||||
|
isCompare = false
|
||||||
|
listFollows()
|
||||||
|
} else {
|
||||||
|
isCompare = true
|
||||||
|
listCompare()
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun refreshAdapter(
|
||||||
|
followModels: List<User>?,
|
||||||
|
allFollowing: List<User>?,
|
||||||
|
followingModels: List<User>?,
|
||||||
|
followersModels: List<User>?
|
||||||
|
) {
|
||||||
|
val groups: ArrayList<ExpandableGroup> = ArrayList<ExpandableGroup>(1)
|
||||||
|
if (isCompare && followingModels != null && followersModels != null && allFollowing != null) {
|
||||||
|
if (followingModels.size > 0) groups.add(
|
||||||
|
ExpandableGroup(
|
||||||
|
getString(
|
||||||
|
R.string.followers_not_following,
|
||||||
|
username
|
||||||
|
), followingModels
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (followersModels.size > 0) groups.add(
|
||||||
|
ExpandableGroup(
|
||||||
|
getString(
|
||||||
|
R.string.followers_not_follower,
|
||||||
|
namePost
|
||||||
|
), followersModels
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (allFollowing.size > 0) groups.add(
|
||||||
|
ExpandableGroup(
|
||||||
|
getString(R.string.followers_both_following),
|
||||||
|
allFollowing
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else if (followModels != null) {
|
||||||
|
groups.add(ExpandableGroup(getString(type), followModels))
|
||||||
|
} else return
|
||||||
|
adapter = FollowAdapter({ v ->
|
||||||
|
val tag = v.tag
|
||||||
|
if (tag is User) {
|
||||||
|
val model = tag
|
||||||
|
val bundle = Bundle()
|
||||||
|
bundle.putString("username", model.username)
|
||||||
|
NavHostFragment.findNavController(this).navigate(R.id.action_global_profileFragment, bundle)
|
||||||
|
}
|
||||||
|
}, groups)
|
||||||
|
adapter!!.toggleGroup(0)
|
||||||
|
binding.rvFollow.adapter = adapter!!
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "FollowViewerFragment"
|
||||||
|
}
|
||||||
|
}
|
@ -51,6 +51,7 @@ import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
|
|||||||
import awais.instagrabber.models.PostsLayoutPreferences;
|
import awais.instagrabber.models.PostsLayoutPreferences;
|
||||||
import awais.instagrabber.models.enums.FavoriteType;
|
import awais.instagrabber.models.enums.FavoriteType;
|
||||||
import awais.instagrabber.models.enums.FollowingType;
|
import awais.instagrabber.models.enums.FollowingType;
|
||||||
|
//import awais.instagrabber.repositories.requests.StoryViewerOptions;
|
||||||
import awais.instagrabber.repositories.responses.Hashtag;
|
import awais.instagrabber.repositories.responses.Hashtag;
|
||||||
import awais.instagrabber.repositories.responses.Location;
|
import awais.instagrabber.repositories.responses.Location;
|
||||||
import awais.instagrabber.repositories.responses.Media;
|
import awais.instagrabber.repositories.responses.Media;
|
||||||
@ -64,6 +65,7 @@ import awais.instagrabber.utils.TextUtils;
|
|||||||
import awais.instagrabber.utils.Utils;
|
import awais.instagrabber.utils.Utils;
|
||||||
import awais.instagrabber.webservices.GraphQLRepository;
|
import awais.instagrabber.webservices.GraphQLRepository;
|
||||||
import awais.instagrabber.webservices.ServiceCallback;
|
import awais.instagrabber.webservices.ServiceCallback;
|
||||||
|
//import awais.instagrabber.webservices.StoriesRepository;
|
||||||
import awais.instagrabber.webservices.TagsService;
|
import awais.instagrabber.webservices.TagsService;
|
||||||
import kotlinx.coroutines.Dispatchers;
|
import kotlinx.coroutines.Dispatchers;
|
||||||
|
|
||||||
@ -80,9 +82,11 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
private String hashtag;
|
private String hashtag;
|
||||||
private Hashtag hashtagModel = null;
|
private Hashtag hashtagModel = null;
|
||||||
private ActionMode actionMode;
|
private ActionMode actionMode;
|
||||||
|
// private StoriesRepository storiesRepository;
|
||||||
private boolean isLoggedIn;
|
private boolean isLoggedIn;
|
||||||
private TagsService tagsService;
|
private TagsService tagsService;
|
||||||
private GraphQLRepository graphQLRepository;
|
private GraphQLRepository graphQLRepository;
|
||||||
|
// private boolean storiesFetching;
|
||||||
private Set<Media> selectedFeedModels;
|
private Set<Media> selectedFeedModels;
|
||||||
private PostsLayoutPreferences layoutPreferences = Utils.getPostsLayoutPreferences(Constants.PREF_HASHTAG_POSTS_LAYOUT);
|
private PostsLayoutPreferences layoutPreferences = Utils.getPostsLayoutPreferences(Constants.PREF_HASHTAG_POSTS_LAYOUT);
|
||||||
private LayoutHashtagDetailsBinding hashtagDetailsBinding;
|
private LayoutHashtagDetailsBinding hashtagDetailsBinding;
|
||||||
@ -116,7 +120,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
});
|
});
|
||||||
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
|
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onPostClick(final Media feedModel, final View profilePicView, final View mainPostImage) {
|
public void onPostClick(final Media feedModel) {
|
||||||
openPostDialog(feedModel, -1);
|
openPostDialog(feedModel, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,14 +180,14 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNameClick(final Media feedModel, final View profilePicView) {
|
public void onNameClick(final Media feedModel) {
|
||||||
final User user = feedModel.getUser();
|
final User user = feedModel.getUser();
|
||||||
if (user == null) return;
|
if (user == null) return;
|
||||||
navigateToProfile("@" + user.getUsername());
|
navigateToProfile("@" + user.getUsername());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onProfilePicClick(final Media feedModel, final View profilePicView) {
|
public void onProfilePicClick(final Media feedModel) {
|
||||||
final User user = feedModel.getUser();
|
final User user = feedModel.getUser();
|
||||||
if (user == null) return;
|
if (user == null) return;
|
||||||
navigateToProfile("@" + user.getUsername());
|
navigateToProfile("@" + user.getUsername());
|
||||||
@ -199,8 +203,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
Utils.openEmailAddress(getContext(), emailId);
|
Utils.openEmailAddress(getContext(), emailId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openPostDialog(@NonNull final Media feedModel,
|
private void openPostDialog(@NonNull final Media feedModel, final int position) {
|
||||||
final int position) {
|
|
||||||
if (opening) return;
|
if (opening) return;
|
||||||
final User user = feedModel.getUser();
|
final User user = feedModel.getUser();
|
||||||
if (user == null) return;
|
if (user == null) return;
|
||||||
@ -286,6 +289,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||||
isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0;
|
isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0;
|
||||||
tagsService = isLoggedIn ? TagsService.getInstance() : null;
|
tagsService = isLoggedIn ? TagsService.getInstance() : null;
|
||||||
|
// storiesRepository = isLoggedIn ? StoriesRepository.Companion.getInstance() : null;
|
||||||
graphQLRepository = isLoggedIn ? null : GraphQLRepository.Companion.getInstance();
|
graphQLRepository = isLoggedIn ? null : GraphQLRepository.Companion.getInstance();
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
}
|
}
|
||||||
@ -559,7 +563,9 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateSwipeRefreshState() {
|
private void updateSwipeRefreshState() {
|
||||||
binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching());
|
AppExecutors.INSTANCE.getMainThread().execute(() ->
|
||||||
|
binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void navigateToProfile(final String username) {
|
private void navigateToProfile(final String username) {
|
||||||
|
@ -48,6 +48,7 @@ import awais.instagrabber.db.repositories.FavoriteRepository;
|
|||||||
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
|
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
|
||||||
import awais.instagrabber.models.PostsLayoutPreferences;
|
import awais.instagrabber.models.PostsLayoutPreferences;
|
||||||
import awais.instagrabber.models.enums.FavoriteType;
|
import awais.instagrabber.models.enums.FavoriteType;
|
||||||
|
//import awais.instagrabber.repositories.requests.StoryViewerOptions;
|
||||||
import awais.instagrabber.repositories.responses.Location;
|
import awais.instagrabber.repositories.responses.Location;
|
||||||
import awais.instagrabber.repositories.responses.Media;
|
import awais.instagrabber.repositories.responses.Media;
|
||||||
import awais.instagrabber.repositories.responses.User;
|
import awais.instagrabber.repositories.responses.User;
|
||||||
@ -61,7 +62,7 @@ import awais.instagrabber.utils.Utils;
|
|||||||
import awais.instagrabber.webservices.GraphQLRepository;
|
import awais.instagrabber.webservices.GraphQLRepository;
|
||||||
import awais.instagrabber.webservices.LocationService;
|
import awais.instagrabber.webservices.LocationService;
|
||||||
import awais.instagrabber.webservices.ServiceCallback;
|
import awais.instagrabber.webservices.ServiceCallback;
|
||||||
import awais.instagrabber.webservices.StoriesRepository;
|
//import awais.instagrabber.webservices.StoriesRepository;
|
||||||
import kotlinx.coroutines.Dispatchers;
|
import kotlinx.coroutines.Dispatchers;
|
||||||
|
|
||||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||||
@ -78,11 +79,11 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||||||
private long locationId;
|
private long locationId;
|
||||||
private Location locationModel;
|
private Location locationModel;
|
||||||
private ActionMode actionMode;
|
private ActionMode actionMode;
|
||||||
private StoriesRepository storiesRepository;
|
// private StoriesRepository storiesRepository;
|
||||||
private GraphQLRepository graphQLRepository;
|
private GraphQLRepository graphQLRepository;
|
||||||
private LocationService locationService;
|
private LocationService locationService;
|
||||||
private boolean isLoggedIn;
|
private boolean isLoggedIn;
|
||||||
private boolean storiesFetching;
|
// private boolean storiesFetching;
|
||||||
private Set<Media> selectedFeedModels;
|
private Set<Media> selectedFeedModels;
|
||||||
private PostsLayoutPreferences layoutPreferences = Utils.getPostsLayoutPreferences(Constants.PREF_LOCATION_POSTS_LAYOUT);
|
private PostsLayoutPreferences layoutPreferences = Utils.getPostsLayoutPreferences(Constants.PREF_LOCATION_POSTS_LAYOUT);
|
||||||
private LayoutLocationDetailsBinding locationDetailsBinding;
|
private LayoutLocationDetailsBinding locationDetailsBinding;
|
||||||
@ -116,13 +117,13 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||||||
});
|
});
|
||||||
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
|
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onPostClick(final Media feedModel, final View profilePicView, final View mainPostImage) {
|
public void onPostClick(final Media feedModel) {
|
||||||
openPostDialog(feedModel, profilePicView, mainPostImage, -1);
|
openPostDialog(feedModel, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSliderClick(final Media feedModel, final int position) {
|
public void onSliderClick(final Media feedModel, final int position) {
|
||||||
openPostDialog(feedModel, null, null, position);
|
openPostDialog(feedModel, position);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -172,12 +173,12 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNameClick(final Media feedModel, final View profilePicView) {
|
public void onNameClick(final Media feedModel) {
|
||||||
navigateToProfile("@" + feedModel.getUser().getUsername());
|
navigateToProfile("@" + feedModel.getUser().getUsername());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onProfilePicClick(final Media feedModel, final View profilePicView) {
|
public void onProfilePicClick(final Media feedModel) {
|
||||||
navigateToProfile("@" + feedModel.getUser().getUsername());
|
navigateToProfile("@" + feedModel.getUser().getUsername());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,10 +192,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||||||
Utils.openEmailAddress(getContext(), emailId);
|
Utils.openEmailAddress(getContext(), emailId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openPostDialog(@NonNull final Media feedModel,
|
private void openPostDialog(@NonNull final Media feedModel, final int position) {
|
||||||
final View profilePicView,
|
|
||||||
final View mainPostImage,
|
|
||||||
final int position) {
|
|
||||||
if (opening) return;
|
if (opening) return;
|
||||||
final User user = feedModel.getUser();
|
final User user = feedModel.getUser();
|
||||||
if (user == null) return;
|
if (user == null) return;
|
||||||
@ -209,7 +207,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (media == null) return;
|
if (media == null) return;
|
||||||
openPostDialog(media, profilePicView, mainPostImage, position);
|
openPostDialog(media, position);
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
@ -280,7 +278,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||||||
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||||
isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0;
|
isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0;
|
||||||
locationService = isLoggedIn ? LocationService.getInstance() : null;
|
locationService = isLoggedIn ? LocationService.getInstance() : null;
|
||||||
storiesRepository = StoriesRepository.Companion.getInstance();
|
// storiesRepository = StoriesRepository.Companion.getInstance();
|
||||||
graphQLRepository = isLoggedIn ? null : GraphQLRepository.Companion.getInstance();
|
graphQLRepository = isLoggedIn ? null : GraphQLRepository.Companion.getInstance();
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
}
|
}
|
||||||
@ -584,7 +582,9 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateSwipeRefreshState() {
|
private void updateSwipeRefreshState() {
|
||||||
binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching() || storiesFetching);
|
AppExecutors.INSTANCE.getMainThread().execute(() ->
|
||||||
|
binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void navigateToProfile(final String username) {
|
private void navigateToProfile(final String username) {
|
||||||
|
@ -758,6 +758,8 @@ public class PostViewV2Fragment extends Fragment implements EditTextDialogFragme
|
|||||||
popupMenu.setOnMenuItemClickListener(item -> {
|
popupMenu.setOnMenuItemClickListener(item -> {
|
||||||
final int itemId = item.getItemId();
|
final int itemId = item.getItemId();
|
||||||
if (itemId == R.id.share_dm) {
|
if (itemId == R.id.share_dm) {
|
||||||
|
if (profileModel.isPrivate())
|
||||||
|
Toast.makeText(context, R.string.share_private_post, Toast.LENGTH_SHORT).show();
|
||||||
try {
|
try {
|
||||||
final NavDirections actionGlobalUserSearch = PostViewV2FragmentDirections
|
final NavDirections actionGlobalUserSearch = PostViewV2FragmentDirections
|
||||||
.actionToUserSearch()
|
.actionToUserSearch()
|
||||||
|
@ -39,6 +39,7 @@ import awais.instagrabber.models.enums.PostItemType;
|
|||||||
import awais.instagrabber.repositories.responses.Location;
|
import awais.instagrabber.repositories.responses.Location;
|
||||||
import awais.instagrabber.repositories.responses.Media;
|
import awais.instagrabber.repositories.responses.Media;
|
||||||
import awais.instagrabber.repositories.responses.User;
|
import awais.instagrabber.repositories.responses.User;
|
||||||
|
import awais.instagrabber.utils.AppExecutors;
|
||||||
import awais.instagrabber.utils.Constants;
|
import awais.instagrabber.utils.Constants;
|
||||||
import awais.instagrabber.utils.CookieUtils;
|
import awais.instagrabber.utils.CookieUtils;
|
||||||
import awais.instagrabber.utils.DownloadUtils;
|
import awais.instagrabber.utils.DownloadUtils;
|
||||||
@ -89,7 +90,7 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
|
|||||||
});
|
});
|
||||||
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
|
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onPostClick(final Media feedModel, final View profilePicView, final View mainPostImage) {
|
public void onPostClick(final Media feedModel) {
|
||||||
openPostDialog(feedModel, -1);
|
openPostDialog(feedModel, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,14 +150,12 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNameClick(final Media feedModel, final View profilePicView) {
|
public void onNameClick(final Media feedModel) {
|
||||||
final User user = feedModel.getUser();
|
navigateToProfile("@" + feedModel.getUser().getUsername());
|
||||||
if (user == null) return;
|
|
||||||
navigateToProfile("@" + user.getUsername());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onProfilePicClick(final Media feedModel, final View profilePicView) {
|
public void onProfilePicClick(final Media feedModel) {
|
||||||
final User user = feedModel.getUser();
|
final User user = feedModel.getUser();
|
||||||
if (user == null) return;
|
if (user == null) return;
|
||||||
navigateToProfile("@" + user.getUsername());
|
navigateToProfile("@" + user.getUsername());
|
||||||
@ -327,7 +326,9 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateSwipeRefreshState() {
|
private void updateSwipeRefreshState() {
|
||||||
binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching());
|
AppExecutors.INSTANCE.getMainThread().execute(() ->
|
||||||
|
binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void navigateToProfile(final String username) {
|
private void navigateToProfile(final String username) {
|
||||||
|
@ -17,10 +17,9 @@ import androidx.appcompat.view.ContextThemeWrapper
|
|||||||
import androidx.appcompat.widget.PopupMenu
|
import androidx.appcompat.widget.PopupMenu
|
||||||
import androidx.core.view.GestureDetectorCompat
|
import androidx.core.view.GestureDetectorCompat
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.MutableLiveData
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.lifecycle.SavedStateHandle
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
@ -40,9 +39,7 @@ import awais.instagrabber.models.enums.StoryPaginationType
|
|||||||
import awais.instagrabber.repositories.requests.StoryViewerOptions
|
import awais.instagrabber.repositories.requests.StoryViewerOptions
|
||||||
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient
|
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient
|
||||||
import awais.instagrabber.repositories.responses.stories.*
|
import awais.instagrabber.repositories.responses.stories.*
|
||||||
import awais.instagrabber.utils.Constants
|
|
||||||
import awais.instagrabber.utils.DownloadUtils.download
|
import awais.instagrabber.utils.DownloadUtils.download
|
||||||
import awais.instagrabber.utils.TextUtils.epochSecondToString
|
|
||||||
import awais.instagrabber.utils.ResponseBodyUtils
|
import awais.instagrabber.utils.ResponseBodyUtils
|
||||||
import awais.instagrabber.utils.Utils
|
import awais.instagrabber.utils.Utils
|
||||||
import awais.instagrabber.utils.extensions.TAG
|
import awais.instagrabber.utils.extensions.TAG
|
||||||
@ -78,7 +75,6 @@ class StoryViewerFragment : Fragment() {
|
|||||||
private var gestureDetector: GestureDetectorCompat? = null
|
private var gestureDetector: GestureDetectorCompat? = null
|
||||||
private val storiesRepository: StoriesRepository? = null
|
private val storiesRepository: StoriesRepository? = null
|
||||||
private val mediaRepository: MediaRepository? = null
|
private val mediaRepository: MediaRepository? = null
|
||||||
private var live: Broadcast? = null
|
|
||||||
private var menuProfile: MenuItem? = null
|
private var menuProfile: MenuItem? = null
|
||||||
private var profileVisible: Boolean = false
|
private var profileVisible: Boolean = false
|
||||||
private var player: SimpleExoPlayer? = null
|
private var player: SimpleExoPlayer? = null
|
||||||
@ -218,7 +214,7 @@ class StoryViewerFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
binding.storiesList.adapter = storiesAdapter
|
binding.storiesList.adapter = storiesAdapter
|
||||||
storiesViewModel.getCurrentStory().observe(fragmentActivity, {
|
storiesViewModel.getCurrentStory().observe(fragmentActivity, {
|
||||||
if (it?.items != null) {
|
if (it?.items != null && it.items.size > 1) {
|
||||||
val storyMedias = it.items.toMutableList()
|
val storyMedias = it.items.toMutableList()
|
||||||
val newItem = storyMedias.get(0)
|
val newItem = storyMedias.get(0)
|
||||||
newItem.isCurrentSlide = true
|
newItem.isCurrentSlide = true
|
||||||
@ -230,6 +226,7 @@ class StoryViewerFragment : Fragment() {
|
|||||||
else View.GONE
|
else View.GONE
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if (it?.items != null) storiesViewModel.setMedia(0)
|
||||||
binding.listToggle.isEnabled = false
|
binding.listToggle.isEnabled = false
|
||||||
binding.storiesList.visibility = View.GONE
|
binding.storiesList.visibility = View.GONE
|
||||||
}
|
}
|
||||||
@ -266,65 +263,28 @@ class StoryViewerFragment : Fragment() {
|
|||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
private fun setupListeners() {
|
private fun setupListeners() {
|
||||||
var liveModels: LiveData<List<Story>?>? = null
|
|
||||||
if (currentFeedStoryIndex >= 0) {
|
if (currentFeedStoryIndex >= 0) {
|
||||||
val type = options!!.type
|
val type = options!!.type
|
||||||
when (type) {
|
when (type) {
|
||||||
StoryViewerOptions.Type.HIGHLIGHT -> {
|
StoryViewerOptions.Type.HIGHLIGHT -> {
|
||||||
storiesViewModel.fetchHighlights(options!!.id)
|
storiesViewModel.fetchHighlights(options!!.id)
|
||||||
liveModels = storiesViewModel.getHighlights()
|
storiesViewModel.highlights.observe(fragmentActivity) {
|
||||||
|
setupMultipage(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
StoryViewerOptions.Type.FEED_STORY_POSITION -> {
|
StoryViewerOptions.Type.FEED_STORY_POSITION -> {
|
||||||
val feedStoriesViewModel = listViewModel as FeedStoriesViewModel?
|
val feedStoriesViewModel = listViewModel as FeedStoriesViewModel?
|
||||||
liveModels = feedStoriesViewModel!!.list
|
setupMultipage(feedStoriesViewModel!!.list.value)
|
||||||
}
|
}
|
||||||
StoryViewerOptions.Type.STORY_ARCHIVE -> {
|
StoryViewerOptions.Type.STORY_ARCHIVE -> {
|
||||||
val archivesViewModel = listViewModel as ArchivesViewModel?
|
val archivesViewModel = listViewModel as ArchivesViewModel?
|
||||||
liveModels = archivesViewModel!!.list
|
setupMultipage(archivesViewModel!!.list.value)
|
||||||
}
|
}
|
||||||
StoryViewerOptions.Type.USER -> {
|
StoryViewerOptions.Type.USER -> {
|
||||||
resetView()
|
resetView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (liveModels != null) liveModels.observe(viewLifecycleOwner, { models ->
|
|
||||||
storiesViewModel.getPagination().observe(fragmentActivity, {
|
|
||||||
if (models != null) {
|
|
||||||
when (it) {
|
|
||||||
StoryPaginationType.FORWARD -> {
|
|
||||||
if (currentFeedStoryIndex == models.size - 1)
|
|
||||||
Toast.makeText(
|
|
||||||
context,
|
|
||||||
R.string.no_more_stories,
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
else paginateStories(false, currentFeedStoryIndex == models.size - 2)
|
|
||||||
}
|
|
||||||
StoryPaginationType.BACKWARD -> {
|
|
||||||
if (currentFeedStoryIndex == 0)
|
|
||||||
Toast.makeText(
|
|
||||||
context,
|
|
||||||
R.string.no_more_stories,
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
else paginateStories(true, false)
|
|
||||||
}
|
|
||||||
StoryPaginationType.ERROR -> {
|
|
||||||
Toast.makeText(
|
|
||||||
context,
|
|
||||||
R.string.downloader_unknown_error,
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (models != null && !models.isEmpty()) {
|
|
||||||
binding.btnBackward.isEnabled = currentFeedStoryIndex != 0
|
|
||||||
binding.btnForward.isEnabled = currentFeedStoryIndex != models.size - 1
|
|
||||||
resetView()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
val context = context ?: return
|
val context = context ?: return
|
||||||
swipeEvent = SwipeEvent { isRightSwipe: Boolean ->
|
swipeEvent = SwipeEvent { isRightSwipe: Boolean ->
|
||||||
@ -357,9 +317,46 @@ class StoryViewerFragment : Fragment() {
|
|||||||
binding.imageViewer.setTapListener(simpleOnGestureListener)
|
binding.imageViewer.setTapListener(simpleOnGestureListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setupMultipage(models: List<Story>?) {
|
||||||
|
if (models == null) return
|
||||||
|
storiesViewModel.getPagination().observe(fragmentActivity, {
|
||||||
|
when (it) {
|
||||||
|
StoryPaginationType.FORWARD -> {
|
||||||
|
if (currentFeedStoryIndex == models.size - 1)
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
R.string.no_more_stories,
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
else paginateStories(false, currentFeedStoryIndex == models.size - 2)
|
||||||
|
}
|
||||||
|
StoryPaginationType.BACKWARD -> {
|
||||||
|
if (currentFeedStoryIndex == 0)
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
R.string.no_more_stories,
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
else paginateStories(true, false)
|
||||||
|
}
|
||||||
|
StoryPaginationType.ERROR -> {
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
R.string.downloader_unknown_error,
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (!models.isEmpty()) {
|
||||||
|
binding.btnBackward.isEnabled = currentFeedStoryIndex != 0
|
||||||
|
binding.btnForward.isEnabled = currentFeedStoryIndex != models.size - 1
|
||||||
|
resetView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun resetView() {
|
private fun resetView() {
|
||||||
val context = context ?: return
|
val context = context ?: return
|
||||||
live = null
|
|
||||||
if (menuProfile != null) menuProfile!!.isVisible = false
|
if (menuProfile != null) menuProfile!!.isVisible = false
|
||||||
binding.imageViewer.controller = null
|
binding.imageViewer.controller = null
|
||||||
releasePlayer()
|
releasePlayer()
|
||||||
@ -367,7 +364,7 @@ class StoryViewerFragment : Fragment() {
|
|||||||
var fetchOptions: StoryViewerOptions? = null
|
var fetchOptions: StoryViewerOptions? = null
|
||||||
when (type) {
|
when (type) {
|
||||||
StoryViewerOptions.Type.HIGHLIGHT -> {
|
StoryViewerOptions.Type.HIGHLIGHT -> {
|
||||||
val models = storiesViewModel.getHighlights().value
|
val models = storiesViewModel.highlights.value
|
||||||
if (models == null || models.isEmpty() || currentFeedStoryIndex >= models.size || currentFeedStoryIndex < 0) {
|
if (models == null || models.isEmpty() || currentFeedStoryIndex >= models.size || currentFeedStoryIndex < 0) {
|
||||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show()
|
||||||
return
|
return
|
||||||
@ -378,10 +375,15 @@ class StoryViewerFragment : Fragment() {
|
|||||||
val feedStoriesViewModel = listViewModel as FeedStoriesViewModel?
|
val feedStoriesViewModel = listViewModel as FeedStoriesViewModel?
|
||||||
val models = feedStoriesViewModel!!.list.value
|
val models = feedStoriesViewModel!!.list.value
|
||||||
if (models == null || currentFeedStoryIndex >= models.size || currentFeedStoryIndex < 0) return
|
if (models == null || currentFeedStoryIndex >= models.size || currentFeedStoryIndex < 0) return
|
||||||
val (_, _, _, _, user, _, _, _, _, _, _, broadcast) = models[currentFeedStoryIndex]
|
val userStory = models[currentFeedStoryIndex]
|
||||||
currentStoryUsername = user!!.username
|
currentStoryUsername = userStory.user!!.username
|
||||||
fetchOptions = StoryViewerOptions.forUser(user.pk, currentStoryUsername)
|
fetchOptions = StoryViewerOptions.forUser(userStory.user.pk, currentStoryUsername)
|
||||||
live = broadcast
|
val live = userStory.broadcast
|
||||||
|
if (live != null) {
|
||||||
|
storiesViewModel.setStory(userStory)
|
||||||
|
refreshLive(live)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
StoryViewerOptions.Type.STORY_ARCHIVE -> {
|
StoryViewerOptions.Type.STORY_ARCHIVE -> {
|
||||||
val archivesViewModel = listViewModel as ArchivesViewModel?
|
val archivesViewModel = listViewModel as ArchivesViewModel?
|
||||||
@ -404,11 +406,7 @@ class StoryViewerFragment : Fragment() {
|
|||||||
storiesViewModel.fetchSingleMedia(options!!.id)
|
storiesViewModel.fetchSingleMedia(options!!.id)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (live != null) {
|
storiesViewModel.fetchStory(fetchOptions).observe(viewLifecycleOwner, {
|
||||||
refreshLive()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
storiesViewModel.fetchStory(fetchOptions).observe(fragmentActivity, {
|
|
||||||
if (it.status == Resource.Status.ERROR) {
|
if (it.status == Resource.Status.ERROR) {
|
||||||
Toast.makeText(context, "Error: " + it.message, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "Error: " + it.message, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
@ -416,18 +414,14 @@ class StoryViewerFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
private fun refreshLive() {
|
private fun refreshLive(live: Broadcast) {
|
||||||
|
binding.btnDownload.isEnabled = false
|
||||||
|
binding.stickers.isEnabled = false
|
||||||
|
binding.listToggle.isEnabled = false
|
||||||
|
binding.btnShare.isEnabled = false
|
||||||
|
binding.btnReply.isEnabled = false
|
||||||
releasePlayer()
|
releasePlayer()
|
||||||
setupLive(live!!.dashPlaybackUrl ?: live!!.dashAbrPlaybackUrl ?: return)
|
setupLive(live.dashPlaybackUrl ?: live.dashAbrPlaybackUrl ?: return)
|
||||||
val actionBar = fragmentActivity.supportActionBar
|
|
||||||
actionBarSubtitle = epochSecondToString(live!!.publishedTime!!)
|
|
||||||
if (actionBar != null) {
|
|
||||||
try {
|
|
||||||
actionBar.setSubtitle(actionBarSubtitle)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(TAG, "refreshLive: ", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
@ -446,14 +440,19 @@ class StoryViewerFragment : Fragment() {
|
|||||||
binding.btnReply.isEnabled = currentStory.canReply
|
binding.btnReply.isEnabled = currentStory.canReply
|
||||||
if (itemType === MediaItemType.MEDIA_TYPE_VIDEO) setupVideo(url) else setupImage(url)
|
if (itemType === MediaItemType.MEDIA_TYPE_VIDEO) setupVideo(url) else setupImage(url)
|
||||||
|
|
||||||
// if (Utils.settingsHelper.getBoolean(MARK_AS_SEEN)) storiesRepository!!.seen(
|
if (options!!.type == StoryViewerOptions.Type.FEED_STORY_POSITION
|
||||||
// csrfToken,
|
&& Utils.settingsHelper.getBoolean(PreferenceKeys.MARK_AS_SEEN)) {
|
||||||
// userId,
|
val feedStoriesViewModel = listViewModel as FeedStoriesViewModel?
|
||||||
// deviceId,
|
storiesViewModel.markAsSeen(currentStory).observe(viewLifecycleOwner) { m ->
|
||||||
// currentStory!!.id!!,
|
if (m.status == Resource.Status.SUCCESS && m.data != null) {
|
||||||
// currentStory!!.takenAt,
|
val liveModels: MutableLiveData<List<Story>> = feedStoriesViewModel!!.list
|
||||||
// System.currentTimeMillis() / 1000
|
val models = liveModels.value
|
||||||
// )
|
val modelsCopy: MutableList<Story> = models!!.toMutableList()
|
||||||
|
modelsCopy.set(currentFeedStoryIndex, m.data)
|
||||||
|
liveModels.postValue(modelsCopy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun downloadStory() {
|
private fun downloadStory() {
|
||||||
@ -797,6 +796,13 @@ class StoryViewerFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun shareStoryViaDm() {
|
private fun shareStoryViaDm() {
|
||||||
|
val story = storiesViewModel.getCurrentStory().value ?: return
|
||||||
|
val context = context
|
||||||
|
if (story.user?.isPrivate == true && context != null) {
|
||||||
|
Toast.makeText(context, R.string.share_private_post, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
val actionBar = fragmentActivity.supportActionBar
|
||||||
|
if (actionBar != null) actionBar.subtitle = null
|
||||||
val actionGlobalUserSearch = StoryViewerFragmentDirections.actionToUserSearch().apply {
|
val actionGlobalUserSearch = StoryViewerFragmentDirections.actionToUserSearch().apply {
|
||||||
title = getString(R.string.share)
|
title = getString(R.string.share)
|
||||||
actionLabel = getString(R.string.send)
|
actionLabel = getString(R.string.send)
|
||||||
|
@ -52,6 +52,7 @@ import awais.instagrabber.repositories.responses.Location;
|
|||||||
import awais.instagrabber.repositories.responses.Media;
|
import awais.instagrabber.repositories.responses.Media;
|
||||||
import awais.instagrabber.repositories.responses.User;
|
import awais.instagrabber.repositories.responses.User;
|
||||||
import awais.instagrabber.repositories.responses.discover.TopicCluster;
|
import awais.instagrabber.repositories.responses.discover.TopicCluster;
|
||||||
|
import awais.instagrabber.utils.AppExecutors;
|
||||||
import awais.instagrabber.utils.Constants;
|
import awais.instagrabber.utils.Constants;
|
||||||
import awais.instagrabber.utils.DownloadUtils;
|
import awais.instagrabber.utils.DownloadUtils;
|
||||||
import awais.instagrabber.utils.ResponseBodyUtils;
|
import awais.instagrabber.utils.ResponseBodyUtils;
|
||||||
@ -99,7 +100,7 @@ public class TopicPostsFragment extends Fragment implements SwipeRefreshLayout.O
|
|||||||
});
|
});
|
||||||
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
|
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onPostClick(final Media feedModel, final View profilePicView, final View mainPostImage) {
|
public void onPostClick(final Media feedModel) {
|
||||||
openPostDialog(feedModel, -1);
|
openPostDialog(feedModel, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,12 +148,12 @@ public class TopicPostsFragment extends Fragment implements SwipeRefreshLayout.O
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNameClick(final Media feedModel, final View profilePicView) {
|
public void onNameClick(final Media feedModel) {
|
||||||
navigateToProfile("@" + feedModel.getUser().getUsername());
|
navigateToProfile("@" + feedModel.getUser().getUsername());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onProfilePicClick(final Media feedModel, final View profilePicView) {
|
public void onProfilePicClick(final Media feedModel) {
|
||||||
final User user = feedModel.getUser();
|
final User user = feedModel.getUser();
|
||||||
if (user == null) return;
|
if (user == null) return;
|
||||||
navigateToProfile("@" + user.getUsername());
|
navigateToProfile("@" + user.getUsername());
|
||||||
@ -377,7 +378,9 @@ public class TopicPostsFragment extends Fragment implements SwipeRefreshLayout.O
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateSwipeRefreshState() {
|
private void updateSwipeRefreshState() {
|
||||||
binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching());
|
AppExecutors.INSTANCE.getMainThread().execute(() ->
|
||||||
|
binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void navigateToProfile(final String username) {
|
private void navigateToProfile(final String username) {
|
||||||
|
@ -97,7 +97,7 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
|
|||||||
|
|
||||||
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
|
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onPostClick(final Media feedModel, final View profilePicView, final View mainPostImage) {
|
public void onPostClick(final Media feedModel) {
|
||||||
openPostDialog(feedModel, -1);
|
openPostDialog(feedModel, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,13 +157,13 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNameClick(final Media feedModel, final View profilePicView) {
|
public void onNameClick(final Media feedModel) {
|
||||||
if (feedModel.getUser() == null) return;
|
if (feedModel.getUser() == null) return;
|
||||||
navigateToProfile("@" + feedModel.getUser().getUsername());
|
navigateToProfile("@" + feedModel.getUser().getUsername());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onProfilePicClick(final Media feedModel, final View profilePicView) {
|
public void onProfilePicClick(final Media feedModel) {
|
||||||
if (feedModel.getUser() == null) return;
|
if (feedModel.getUser() == null) return;
|
||||||
navigateToProfile("@" + feedModel.getUser().getUsername());
|
navigateToProfile("@" + feedModel.getUser().getUsername());
|
||||||
}
|
}
|
||||||
@ -330,12 +330,6 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
|
|||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
binding.getRoot().postDelayed(feedStoriesAdapter::notifyDataSetChanged, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRefresh() {
|
public void onRefresh() {
|
||||||
binding.feedRecyclerView.refresh();
|
binding.feedRecyclerView.refresh();
|
||||||
@ -370,7 +364,9 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateSwipeRefreshState() {
|
private void updateSwipeRefreshState() {
|
||||||
binding.feedSwipeRefreshLayout.setRefreshing(binding.feedRecyclerView.isFetching() || storiesFetching);
|
AppExecutors.INSTANCE.getMainThread().execute(() ->
|
||||||
|
binding.feedSwipeRefreshLayout.setRefreshing(binding.feedRecyclerView.isFetching() || storiesFetching)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupFeedStories() {
|
private void setupFeedStories() {
|
||||||
@ -381,7 +377,7 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
|
|||||||
storiesRecyclerView = binding.header;
|
storiesRecyclerView = binding.header;
|
||||||
storiesRecyclerView.setLayoutManager(new LinearLayoutManager(context, RecyclerView.HORIZONTAL, false));
|
storiesRecyclerView.setLayoutManager(new LinearLayoutManager(context, RecyclerView.HORIZONTAL, false));
|
||||||
storiesRecyclerView.setAdapter(feedStoriesAdapter);
|
storiesRecyclerView.setAdapter(feedStoriesAdapter);
|
||||||
feedStoriesViewModel.getList().observe(getViewLifecycleOwner(), feedStoriesAdapter::submitList);
|
feedStoriesViewModel.getList().observe(fragmentActivity, feedStoriesAdapter::submitList);
|
||||||
fetchStories();
|
fetchStories();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,8 +397,6 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
|
|||||||
storiesFetching = false;
|
storiesFetching = false;
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
feedStoriesViewModel.getList().postValue((List<Story>) feedStoryModels);
|
feedStoriesViewModel.getList().postValue((List<Story>) feedStoryModels);
|
||||||
//noinspection unchecked
|
|
||||||
feedStoriesAdapter.submitList((List<Story>) feedStoryModels);
|
|
||||||
if (storyListMenu != null) storyListMenu.setVisible(true);
|
if (storyListMenu != null) storyListMenu.setVisible(true);
|
||||||
updateSwipeRefreshState();
|
updateSwipeRefreshState();
|
||||||
}), Dispatchers.getIO())
|
}), Dispatchers.getIO())
|
||||||
|
@ -90,15 +90,15 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
|
|||||||
private val bioDialogRequestCode = 102
|
private val bioDialogRequestCode = 102
|
||||||
private val translationDialogRequestCode = 103
|
private val translationDialogRequestCode = 103
|
||||||
private val feedItemCallback: FeedAdapterV2.FeedItemCallback = object : FeedAdapterV2.FeedItemCallback {
|
private val feedItemCallback: FeedAdapterV2.FeedItemCallback = object : FeedAdapterV2.FeedItemCallback {
|
||||||
override fun onPostClick(media: Media?, profilePicView: View?, mainPostImage: View?) {
|
override fun onPostClick(media: Media) {
|
||||||
openPostDialog(media ?: return, -1)
|
openPostDialog(media ?: return, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onProfilePicClick(media: Media?, profilePicView: View?) {
|
override fun onProfilePicClick(media: Media) {
|
||||||
navigateToProfile(media?.user?.username)
|
navigateToProfile(media?.user?.username)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNameClick(media: Media?, profilePicView: View?) {
|
override fun onNameClick(media: Media) {
|
||||||
navigateToProfile(media?.user?.username)
|
navigateToProfile(media?.user?.username)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -860,7 +860,11 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
|
|||||||
.setLifeCycleOwner(this)
|
.setLifeCycleOwner(this)
|
||||||
.setPostFetchService(ProfilePostFetchService(profile, currentUser != null))
|
.setPostFetchService(ProfilePostFetchService(profile, currentUser != null))
|
||||||
.setLayoutPreferences(layoutPreferences)
|
.setLayoutPreferences(layoutPreferences)
|
||||||
.addFetchStatusChangeListener { binding.swipeRefreshLayout.isRefreshing = it }
|
.addFetchStatusChangeListener {
|
||||||
|
AppExecutors.mainThread.execute {
|
||||||
|
binding.swipeRefreshLayout.isRefreshing = it
|
||||||
|
}
|
||||||
|
}
|
||||||
.setFeedItemCallback(feedItemCallback)
|
.setFeedItemCallback(feedItemCallback)
|
||||||
.setSelectionModeCallback(selectionModeCallback)
|
.setSelectionModeCallback(selectionModeCallback)
|
||||||
.init()
|
.init()
|
||||||
|
@ -240,6 +240,7 @@ public class SearchFragment extends Fragment implements SearchCategoryFragment.O
|
|||||||
switch (resource.status) {
|
switch (resource.status) {
|
||||||
case SUCCESS:
|
case SUCCESS:
|
||||||
viewModel.search("", type);
|
viewModel.search("", type);
|
||||||
|
viewModel.search("", FavoriteType.TOP);
|
||||||
liveData.removeObserver(this);
|
liveData.removeObserver(this);
|
||||||
break;
|
break;
|
||||||
case ERROR:
|
case ERROR:
|
||||||
|
@ -104,7 +104,7 @@ public class DownloadsPreferencesFragment extends BasePreferencesFragment {
|
|||||||
Utils.setupSelectedDir(context, data);
|
Utils.setupSelectedDir(context, data);
|
||||||
String path;
|
String path;
|
||||||
try {
|
try {
|
||||||
path = URLDecoder.decode(data.getData().toString(), StandardCharsets.UTF_8.toString());
|
path = URLDecoder.decode(data.getData().toString(), StandardCharsets.UTF_8.name());
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
path = data.getData().toString();
|
path = data.getData().toString();
|
||||||
}
|
}
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
package awais.instagrabber.models
|
|
||||||
|
|
||||||
import java.io.Serializable
|
|
||||||
|
|
||||||
class FollowModel(
|
|
||||||
val id: String,
|
|
||||||
val username: String,
|
|
||||||
val fullName: String,
|
|
||||||
val profilePicUrl: String
|
|
||||||
) : Serializable {
|
|
||||||
private var hasNextPage = false
|
|
||||||
get() = endCursor != null && field
|
|
||||||
|
|
||||||
var isShown = true
|
|
||||||
|
|
||||||
var endCursor: String? = null
|
|
||||||
private set
|
|
||||||
|
|
||||||
fun setPageCursor(hasNextPage: Boolean, endCursor: String?) {
|
|
||||||
this.endCursor = endCursor
|
|
||||||
this.hasNextPage = hasNextPage
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
|
|
||||||
other as FollowModel
|
|
||||||
|
|
||||||
if (id != other.id) return false
|
|
||||||
if (username != other.username) return false
|
|
||||||
if (fullName != other.fullName) return false
|
|
||||||
if (profilePicUrl != other.profilePicUrl) return false
|
|
||||||
if (isShown != other.isShown) return false
|
|
||||||
if (endCursor != other.endCursor) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = id.hashCode()
|
|
||||||
result = 31 * result + username.hashCode()
|
|
||||||
result = 31 * result + fullName.hashCode()
|
|
||||||
result = 31 * result + profilePicUrl.hashCode()
|
|
||||||
result = 31 * result + isShown.hashCode()
|
|
||||||
result = 31 * result + (endCursor?.hashCode() ?: 0)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
@ -64,8 +64,8 @@ enum class DirectItemType(val id: Int) : Serializable {
|
|||||||
private val map: MutableMap<Int, DirectItemType> = mutableMapOf()
|
private val map: MutableMap<Int, DirectItemType> = mutableMapOf()
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getId(id: Int): DirectItemType? {
|
fun getTypeFromId(id: Int): DirectItemType {
|
||||||
return map[id]
|
return map[id] ?: UNKNOWN
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getName(directItemType: DirectItemType): String? {
|
fun getName(directItemType: DirectItemType): String? {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package awais.instagrabber.repositories
|
package awais.instagrabber.repositories
|
||||||
|
|
||||||
import awais.instagrabber.repositories.responses.FriendshipChangeResponse
|
import awais.instagrabber.repositories.responses.FriendshipChangeResponse
|
||||||
|
import awais.instagrabber.repositories.responses.FriendshipListFetchResponse
|
||||||
import awais.instagrabber.repositories.responses.FriendshipRestrictResponse
|
import awais.instagrabber.repositories.responses.FriendshipRestrictResponse
|
||||||
import retrofit2.http.*
|
import retrofit2.http.*
|
||||||
|
|
||||||
@ -25,7 +26,7 @@ interface FriendshipService {
|
|||||||
@Path("userId") userId: Long,
|
@Path("userId") userId: Long,
|
||||||
@Path("type") type: String, // following or followers
|
@Path("type") type: String, // following or followers
|
||||||
@QueryMap(encoded = true) queryParams: Map<String, String>,
|
@QueryMap(encoded = true) queryParams: Map<String, String>,
|
||||||
): String
|
): FriendshipListFetchResponse
|
||||||
|
|
||||||
@FormUrlEncoded
|
@FormUrlEncoded
|
||||||
@POST("/api/v1/friendships/{action}/")
|
@POST("/api/v1/friendships/{action}/")
|
||||||
|
@ -1,27 +1,10 @@
|
|||||||
package awais.instagrabber.repositories.responses
|
package awais.instagrabber.repositories.responses
|
||||||
|
|
||||||
import awais.instagrabber.models.FollowModel
|
|
||||||
|
|
||||||
data class FriendshipListFetchResponse(
|
data class FriendshipListFetchResponse(
|
||||||
var nextMaxId: String?,
|
var nextMaxId: String?,
|
||||||
var status: String?,
|
var status: String?,
|
||||||
var items: List<FollowModel>?
|
var users: List<User>?
|
||||||
) {
|
) {
|
||||||
val isMoreAvailable: Boolean
|
val isMoreAvailable: Boolean
|
||||||
get() = !nextMaxId.isNullOrBlank()
|
get() = !nextMaxId.isNullOrBlank()
|
||||||
|
|
||||||
fun setNextMaxId(nextMaxId: String): FriendshipListFetchResponse {
|
|
||||||
this.nextMaxId = nextMaxId
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setStatus(status: String): FriendshipListFetchResponse {
|
|
||||||
this.status = status
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setItems(items: List<FollowModel>): FriendshipListFetchResponse {
|
|
||||||
this.items = items
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,9 +1,9 @@
|
|||||||
package awais.instagrabber.repositories.responses.notification
|
package awais.instagrabber.repositories.responses.notification
|
||||||
|
|
||||||
class NotificationCounts(val commentLikesCount: Int,
|
class NotificationCounts(val commentLikes: Int,
|
||||||
val userTagsCount: Int,
|
val usertags: Int,
|
||||||
val likesCount: Int,
|
val likes: Int,
|
||||||
val commentsCount: Int,
|
val comments: Int,
|
||||||
val relationshipsCount: Int,
|
val relationships: Int,
|
||||||
val pOYCount: Int,
|
val photosOfYou: Int,
|
||||||
val requestsCount: Int)
|
val requests: Int)
|
@ -11,7 +11,7 @@ data class Story(
|
|||||||
val latestReelMedia: Long? = null, // = timestamp
|
val latestReelMedia: Long? = null, // = timestamp
|
||||||
val mediaCount: Int? = null,
|
val mediaCount: Int? = null,
|
||||||
// for stories and highlights
|
// for stories and highlights
|
||||||
var seen: Long? = null,
|
val seen: Long? = null,
|
||||||
val user: User? = null,
|
val user: User? = null,
|
||||||
// for stories
|
// for stories
|
||||||
val muted: Boolean? = null,
|
val muted: Boolean? = null,
|
||||||
|
@ -54,10 +54,9 @@ public class ActivityCheckerService extends Service {
|
|||||||
public void onSuccess(final NotificationCounts result) {
|
public void onSuccess(final NotificationCounts result) {
|
||||||
try {
|
try {
|
||||||
if (result == null) return;
|
if (result == null) return;
|
||||||
final String notification = getNotificationString(result);
|
final List<String> notification = getNotificationString(result);
|
||||||
if (notification == null) return;
|
if (notification == null) return;
|
||||||
final String notificationString = getString(R.string.activity_count_prefix) + " " + notification + ".";
|
showNotification(notification);
|
||||||
showNotification(notificationString);
|
|
||||||
} finally {
|
} finally {
|
||||||
handler.postDelayed(runnable, DELAY_MILLIS);
|
handler.postDelayed(runnable, DELAY_MILLIS);
|
||||||
}
|
}
|
||||||
@ -88,42 +87,54 @@ public class ActivityCheckerService extends Service {
|
|||||||
handler.removeCallbacks(runnable);
|
handler.removeCallbacks(runnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getNotificationString(final NotificationCounts result) {
|
private List<String> getNotificationString(final NotificationCounts result) {
|
||||||
|
final List<String> toReturn = new ArrayList<>(2);
|
||||||
final List<String> list = new ArrayList<>();
|
final List<String> list = new ArrayList<>();
|
||||||
if (result.getRelationshipsCount() != 0) {
|
int count = 0;
|
||||||
list.add(getString(R.string.activity_count_relationship, result.getRelationshipsCount()));
|
if (result.getRelationships() != 0) {
|
||||||
|
list.add(getString(R.string.activity_count_relationship, result.getRelationships()));
|
||||||
|
count += result.getRelationships();
|
||||||
}
|
}
|
||||||
if (result.getRequestsCount() != 0) {
|
if (result.getRequests() != 0) {
|
||||||
list.add(getString(R.string.activity_count_requests, result.getRequestsCount()));
|
list.add(getString(R.string.activity_count_requests, result.getRequests()));
|
||||||
|
count += result.getRequests();
|
||||||
}
|
}
|
||||||
if (result.getUserTagsCount() != 0) {
|
if (result.getUsertags() != 0) {
|
||||||
list.add(getString(R.string.activity_count_usertags, result.getUserTagsCount()));
|
list.add(getString(R.string.activity_count_usertags, result.getUsertags()));
|
||||||
|
count += result.getUsertags();
|
||||||
}
|
}
|
||||||
if (result.getPOYCount() != 0) {
|
if (result.getPhotosOfYou() != 0) {
|
||||||
list.add(getString(R.string.activity_count_poy, result.getPOYCount()));
|
list.add(getString(R.string.activity_count_poy, result.getPhotosOfYou()));
|
||||||
|
count += result.getPhotosOfYou();
|
||||||
}
|
}
|
||||||
if (result.getCommentsCount() != 0) {
|
if (result.getComments() != 0) {
|
||||||
list.add(getString(R.string.activity_count_comments, result.getCommentsCount()));
|
list.add(getString(R.string.activity_count_comments, result.getComments()));
|
||||||
|
count += result.getComments();
|
||||||
}
|
}
|
||||||
if (result.getCommentLikesCount() != 0) {
|
if (result.getCommentLikes() != 0) {
|
||||||
list.add(getString(R.string.activity_count_commentlikes, result.getCommentLikesCount()));
|
list.add(getString(R.string.activity_count_commentlikes, result.getCommentLikes()));
|
||||||
|
count += result.getCommentLikes();
|
||||||
}
|
}
|
||||||
if (result.getLikesCount() != 0) {
|
if (result.getLikes() != 0) {
|
||||||
list.add(getString(R.string.activity_count_likes, result.getLikesCount()));
|
list.add(getString(R.string.activity_count_likes, result.getLikes()));
|
||||||
|
count += result.getLikes();
|
||||||
}
|
}
|
||||||
if (list.isEmpty()) return null;
|
if (list.isEmpty()) return null;
|
||||||
return TextUtils.join(", ", list);
|
toReturn.add(TextUtils.join(", ", list));
|
||||||
|
toReturn.add(getResources().getQuantityString(R.plurals.activity_count_total, count, count));
|
||||||
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showNotification(final String notificationString) {
|
private void showNotification(final List<String> notificationString) {
|
||||||
final Notification notification = new NotificationCompat.Builder(this, Constants.ACTIVITY_CHANNEL_ID)
|
final Notification notification = new NotificationCompat.Builder(this, Constants.ACTIVITY_CHANNEL_ID)
|
||||||
.setCategory(NotificationCompat.CATEGORY_STATUS)
|
.setCategory(NotificationCompat.CATEGORY_STATUS)
|
||||||
.setSmallIcon(R.drawable.ic_notif)
|
.setSmallIcon(R.drawable.ic_notif)
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
.setOnlyAlertOnce(true)
|
.setOnlyAlertOnce(true)
|
||||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||||
.setContentTitle(getString(R.string.action_notif))
|
.setContentTitle(notificationString.get(1))
|
||||||
.setContentText(notificationString)
|
.setContentText(notificationString.get(0))
|
||||||
|
.setStyle(new NotificationCompat.BigTextStyle().bigText(notificationString.get(0)))
|
||||||
.setContentIntent(getPendingIntent())
|
.setContentIntent(getPendingIntent())
|
||||||
.build();
|
.build();
|
||||||
notificationManager.notify(Constants.ACTIVITY_NOTIFICATION_ID, notification);
|
notificationManager.notify(Constants.ACTIVITY_NOTIFICATION_ID, notification);
|
||||||
|
@ -15,48 +15,23 @@ import java.io.FileDescriptor;
|
|||||||
|
|
||||||
public final class MediaUtils {
|
public final class MediaUtils {
|
||||||
private static final String TAG = MediaUtils.class.getSimpleName();
|
private static final String TAG = MediaUtils.class.getSimpleName();
|
||||||
private static final String[] PROJECTION_VIDEO = {
|
|
||||||
MediaStore.Video.Media.DURATION,
|
|
||||||
MediaStore.Video.Media.WIDTH,
|
|
||||||
MediaStore.Video.Media.HEIGHT,
|
|
||||||
MediaStore.Video.Media.SIZE
|
|
||||||
};
|
|
||||||
private static final String[] PROJECTION_AUDIO = {
|
|
||||||
MediaStore.Audio.Media.DURATION,
|
|
||||||
MediaStore.Audio.Media.SIZE
|
|
||||||
};
|
|
||||||
|
|
||||||
public static void getVideoInfo(@NonNull final ContentResolver contentResolver,
|
public static void getVideoInfo(@NonNull final ContentResolver contentResolver,
|
||||||
@NonNull final Uri uri,
|
@NonNull final Uri uri,
|
||||||
@NonNull final OnInfoLoadListener<VideoInfo> listener) {
|
@NonNull final OnInfoLoadListener<VideoInfo> listener) {
|
||||||
AppExecutors.INSTANCE.getTasksThread().submit(() -> {
|
getInfo(contentResolver, uri, listener, true);
|
||||||
try (Cursor cursor = MediaStore.Video.query(contentResolver, uri, PROJECTION_VIDEO)) {
|
|
||||||
if (cursor == null) {
|
|
||||||
listener.onLoad(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int durationColumn = cursor.getColumnIndex(MediaStore.Video.Media.DURATION);
|
|
||||||
int widthColumn = cursor.getColumnIndex(MediaStore.Video.Media.WIDTH);
|
|
||||||
int heightColumn = cursor.getColumnIndex(MediaStore.Video.Media.HEIGHT);
|
|
||||||
int sizeColumn = cursor.getColumnIndex(MediaStore.Video.Media.SIZE);
|
|
||||||
if (cursor.moveToNext()) {
|
|
||||||
listener.onLoad(new VideoInfo(
|
|
||||||
cursor.getLong(durationColumn),
|
|
||||||
cursor.getInt(widthColumn),
|
|
||||||
cursor.getInt(heightColumn),
|
|
||||||
cursor.getLong(sizeColumn)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(TAG, "getVideoInfo: ", e);
|
|
||||||
listener.onFailure(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void getVoiceInfo(@NonNull final ContentResolver contentResolver,
|
public static void getVoiceInfo(@NonNull final ContentResolver contentResolver,
|
||||||
@NonNull final Uri uri,
|
@NonNull final Uri uri,
|
||||||
@NonNull final OnInfoLoadListener<VideoInfo> listener) {
|
@NonNull final OnInfoLoadListener<VideoInfo> listener) {
|
||||||
|
getInfo(contentResolver, uri, listener, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void getInfo(@NonNull final ContentResolver contentResolver,
|
||||||
|
@NonNull final Uri uri,
|
||||||
|
@NonNull final OnInfoLoadListener<VideoInfo> listener,
|
||||||
|
@NonNull final Boolean isVideo) {
|
||||||
AppExecutors.INSTANCE.getTasksThread().submit(() -> {
|
AppExecutors.INSTANCE.getTasksThread().submit(() -> {
|
||||||
try (ParcelFileDescriptor parcelFileDescriptor = contentResolver.openFileDescriptor(uri, "r")) {
|
try (ParcelFileDescriptor parcelFileDescriptor = contentResolver.openFileDescriptor(uri, "r")) {
|
||||||
if (parcelFileDescriptor == null) {
|
if (parcelFileDescriptor == null) {
|
||||||
@ -68,6 +43,23 @@ public final class MediaUtils {
|
|||||||
mediaMetadataRetriever.setDataSource(fileDescriptor);
|
mediaMetadataRetriever.setDataSource(fileDescriptor);
|
||||||
String duration = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
|
String duration = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
|
||||||
if (TextUtils.isEmpty(duration)) duration = "0";
|
if (TextUtils.isEmpty(duration)) duration = "0";
|
||||||
|
if (isVideo) {
|
||||||
|
String width = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
|
||||||
|
if (TextUtils.isEmpty(width)) width = "1";
|
||||||
|
String height = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
|
||||||
|
if (TextUtils.isEmpty(height)) height = "1";
|
||||||
|
final Cursor cursor = contentResolver.query(uri, new String[]{MediaStore.Video.Media.SIZE}, null, null, null);
|
||||||
|
cursor.moveToFirst();
|
||||||
|
final long fileSize = cursor.getLong(0);
|
||||||
|
cursor.close();
|
||||||
|
listener.onLoad(new VideoInfo(
|
||||||
|
Long.parseLong(duration),
|
||||||
|
Integer.valueOf(width),
|
||||||
|
Integer.valueOf(height),
|
||||||
|
fileSize
|
||||||
|
));
|
||||||
|
return;
|
||||||
|
}
|
||||||
listener.onLoad(new VideoInfo(
|
listener.onLoad(new VideoInfo(
|
||||||
Long.parseLong(duration),
|
Long.parseLong(duration),
|
||||||
0,
|
0,
|
||||||
@ -75,7 +67,7 @@ public final class MediaUtils {
|
|||||||
0
|
0
|
||||||
));
|
));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "getVoiceInfo: ", e);
|
Log.e(TAG, "getInfo: ", e);
|
||||||
listener.onFailure(e);
|
listener.onFailure(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
package awais.instagrabber.viewmodels;
|
|
||||||
|
|
||||||
import androidx.lifecycle.MutableLiveData;
|
|
||||||
import androidx.lifecycle.ViewModel;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import awais.instagrabber.models.FollowModel;
|
|
||||||
|
|
||||||
public class FollowViewModel extends ViewModel {
|
|
||||||
private MutableLiveData<List<FollowModel>> list;
|
|
||||||
|
|
||||||
public MutableLiveData<List<FollowModel>> getList() {
|
|
||||||
if (list == null) {
|
|
||||||
list = new MutableLiveData<>();
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,167 @@
|
|||||||
|
package awais.instagrabber.viewmodels
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MediatorLiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import awais.instagrabber.models.Resource
|
||||||
|
import awais.instagrabber.repositories.responses.User
|
||||||
|
import awais.instagrabber.webservices.FriendshipRepository
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class FollowViewModel : ViewModel() {
|
||||||
|
// data
|
||||||
|
val userId = MutableLiveData<Long>()
|
||||||
|
private val followers = MutableLiveData<List<User>>()
|
||||||
|
private val followings = MutableLiveData<List<User>>()
|
||||||
|
private val searchResults = MutableLiveData<List<User>>()
|
||||||
|
|
||||||
|
// cursors
|
||||||
|
private val followersMaxId = MutableLiveData<String?>("")
|
||||||
|
private val followingMaxId = MutableLiveData<String?>("")
|
||||||
|
private val searchingMaxId = MutableLiveData<String?>("")
|
||||||
|
private val searchQuery = MutableLiveData<String?>()
|
||||||
|
|
||||||
|
// comparison
|
||||||
|
val status: LiveData<Pair<Boolean, Boolean>> = object : MediatorLiveData<Pair<Boolean, Boolean>>() {
|
||||||
|
init {
|
||||||
|
postValue(Pair(false, false))
|
||||||
|
addSource(followersMaxId) {
|
||||||
|
if (it == null) {
|
||||||
|
postValue(Pair(true, value!!.second))
|
||||||
|
}
|
||||||
|
else fetch(true, it)
|
||||||
|
}
|
||||||
|
addSource(followingMaxId) {
|
||||||
|
if (it == null) {
|
||||||
|
postValue(Pair(value!!.first, true))
|
||||||
|
}
|
||||||
|
else fetch(false, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val comparison: LiveData<Triple<List<User>, List<User>, List<User>>> =
|
||||||
|
object : MediatorLiveData<Triple<List<User>, List<User>, List<User>>>() {
|
||||||
|
init {
|
||||||
|
addSource(status) {
|
||||||
|
if (it.first && it.second) {
|
||||||
|
val followersList = followers.value!!
|
||||||
|
val followingList = followings.value!!
|
||||||
|
val allUsers: MutableList<User> = mutableListOf()
|
||||||
|
allUsers.addAll(followersList)
|
||||||
|
allUsers.addAll(followingList)
|
||||||
|
val followersMap = followersList.groupBy { it.pk }
|
||||||
|
val followingMap = followingList.groupBy { it.pk }
|
||||||
|
val mutual: MutableList<User> = mutableListOf()
|
||||||
|
val onlyFollowing: MutableList<User> = mutableListOf()
|
||||||
|
val onlyFollowers: MutableList<User> = mutableListOf()
|
||||||
|
allUsers.forEach {
|
||||||
|
val isFollowing = followingMap.get(it.pk) != null
|
||||||
|
val isFollower = followersMap.get(it.pk) != null
|
||||||
|
if (isFollowing && isFollower) mutual.add(it)
|
||||||
|
else if (isFollowing) onlyFollowing.add(it)
|
||||||
|
else if (isFollower) onlyFollowers.add(it)
|
||||||
|
}
|
||||||
|
postValue(Triple(mutual, onlyFollowing, onlyFollowers))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val friendshipRepository: FriendshipRepository by lazy { FriendshipRepository.getInstance() }
|
||||||
|
|
||||||
|
// fetch: supply max ID for continuous fetch
|
||||||
|
fun fetch(follower: Boolean, nextMaxId: String?): LiveData<Resource<Any?>> {
|
||||||
|
val data = MutableLiveData<Resource<Any?>>()
|
||||||
|
data.postValue(Resource.loading(null))
|
||||||
|
val maxId = if (follower) followersMaxId else followingMaxId
|
||||||
|
if (maxId.value == null && nextMaxId == null) data.postValue(Resource.success(null))
|
||||||
|
else if (userId.value == null) data.postValue(Resource.error("No user ID supplied!", null))
|
||||||
|
else viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val tempList = friendshipRepository.getList(
|
||||||
|
follower,
|
||||||
|
userId.value!!,
|
||||||
|
nextMaxId ?: maxId.value,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
if (!tempList.status.equals("ok")) {
|
||||||
|
data.postValue(Resource.error("Status not ok!", null))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (tempList.users != null) {
|
||||||
|
val liveData = if (follower) followers else followings
|
||||||
|
val currentList = if (liveData.value != null) liveData.value!!.toMutableList()
|
||||||
|
else mutableListOf()
|
||||||
|
currentList.addAll(tempList.users!!)
|
||||||
|
liveData.postValue(currentList.toList())
|
||||||
|
}
|
||||||
|
maxId.postValue(tempList.nextMaxId)
|
||||||
|
data.postValue(Resource.success(null))
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
data.postValue(Resource.error(e.message, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getList(follower: Boolean): LiveData<List<User>> {
|
||||||
|
return if (follower) followers else followings
|
||||||
|
}
|
||||||
|
|
||||||
|
fun search(follower: Boolean): LiveData<Resource<Any?>> {
|
||||||
|
val data = MutableLiveData<Resource<Any?>>()
|
||||||
|
data.postValue(Resource.loading(null))
|
||||||
|
val query = searchQuery.value
|
||||||
|
if (searchingMaxId.value == null) data.postValue(Resource.success(null))
|
||||||
|
else if (userId.value == null) data.postValue(Resource.error("No user ID supplied!", null))
|
||||||
|
else if (query.isNullOrEmpty()) data.postValue(Resource.error("No query supplied!", null))
|
||||||
|
else viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val tempList = friendshipRepository.getList(
|
||||||
|
follower,
|
||||||
|
userId.value!!,
|
||||||
|
searchingMaxId.value,
|
||||||
|
query
|
||||||
|
)
|
||||||
|
if (!tempList.status.equals("ok")) {
|
||||||
|
data.postValue(Resource.error("Status not ok!", null))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (tempList.users != null) {
|
||||||
|
val currentList = if (searchResults.value != null) searchResults.value!!.toMutableList()
|
||||||
|
else mutableListOf()
|
||||||
|
currentList.addAll(tempList.users!!)
|
||||||
|
searchResults.postValue(currentList.toList())
|
||||||
|
}
|
||||||
|
searchingMaxId.postValue(tempList.nextMaxId)
|
||||||
|
data.postValue(Resource.success(null))
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
data.postValue(Resource.error(e.message, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSearch(): LiveData<List<User>> {
|
||||||
|
return searchResults
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setQuery(query: String?, follower: Boolean) {
|
||||||
|
searchQuery.value = query
|
||||||
|
if (!query.isNullOrEmpty()) search(follower)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearProgress() {
|
||||||
|
followersMaxId.value = ""
|
||||||
|
followingMaxId.value = ""
|
||||||
|
searchingMaxId.value = ""
|
||||||
|
followings.value = listOf<User>()
|
||||||
|
followers.value = listOf<User>()
|
||||||
|
searchResults.value = listOf<User>()
|
||||||
|
}
|
||||||
|
}
|
@ -226,7 +226,7 @@ class ProfileFragmentViewModel(
|
|||||||
private suspend fun fetchUser(
|
private suspend fun fetchUser(
|
||||||
currentUser: User?,
|
currentUser: User?,
|
||||||
stateUsername: String,
|
stateUsername: String,
|
||||||
): User {
|
): User? {
|
||||||
if (currentUser != null) {
|
if (currentUser != null) {
|
||||||
// logged in
|
// logged in
|
||||||
val tempUser = userRepository.getUsernameInfo(stateUsername)
|
val tempUser = userRepository.getUsernameInfo(stateUsername)
|
||||||
|
@ -242,7 +242,7 @@ public class SearchFragmentViewModel extends AppStateViewModel {
|
|||||||
@Override
|
@Override
|
||||||
public void onFailure(@NonNull final Throwable t) {
|
public void onFailure(@NonNull final Throwable t) {
|
||||||
if (!TextUtils.isEmpty(tempQuery)) return;
|
if (!TextUtils.isEmpty(tempQuery)) return;
|
||||||
topResults.postValue(Resource.success(Collections.emptyList()));
|
liveData.postValue(Resource.success(Collections.emptyList()));
|
||||||
Log.e(TAG, "onFailure: ", t);
|
Log.e(TAG, "onFailure: ", t);
|
||||||
}
|
}
|
||||||
}, AppExecutors.INSTANCE.getMainThread());
|
}, AppExecutors.INSTANCE.getMainThread());
|
||||||
|
@ -60,21 +60,22 @@ class StoryFragmentViewModel : ViewModel() {
|
|||||||
private val mediaRepository: MediaRepository by lazy { MediaRepository.getInstance() }
|
private val mediaRepository: MediaRepository by lazy { MediaRepository.getInstance() }
|
||||||
|
|
||||||
// for highlights ONLY
|
// for highlights ONLY
|
||||||
private val highlights = MutableLiveData<List<Story>?>()
|
val highlights = MutableLiveData<List<Story>?>()
|
||||||
|
|
||||||
/* set functions */
|
/* set functions */
|
||||||
|
|
||||||
fun setStory(story: Story) {
|
fun setStory(story: Story) {
|
||||||
if (story.items == null || story.items.size == 0) {
|
|
||||||
pagination.postValue(StoryPaginationType.ERROR)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
currentStory.postValue(story)
|
currentStory.postValue(story)
|
||||||
storyTitle.postValue(story.title ?: story.user?.username)
|
storyTitle.postValue(story.title ?: story.user?.username)
|
||||||
if (story.broadcast != null) {
|
if (story.broadcast != null) {
|
||||||
date.postValue(story.dateTime)
|
date.postValue(story.dateTime)
|
||||||
type.postValue(MediaItemType.MEDIA_TYPE_LIVE)
|
type.postValue(MediaItemType.MEDIA_TYPE_LIVE)
|
||||||
pagination.postValue(StoryPaginationType.DO_NOTHING)
|
pagination.postValue(StoryPaginationType.DO_NOTHING)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (story.items == null || story.items.size == 0) {
|
||||||
|
pagination.postValue(StoryPaginationType.ERROR)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,10 +185,6 @@ class StoryFragmentViewModel : ViewModel() {
|
|||||||
|
|
||||||
/* get functions */
|
/* get functions */
|
||||||
|
|
||||||
fun getHighlights(): LiveData<List<Story>?> {
|
|
||||||
return highlights
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getCurrentStory(): LiveData<Story?> {
|
fun getCurrentStory(): LiveData<Story?> {
|
||||||
return currentStory
|
return currentStory
|
||||||
}
|
}
|
||||||
@ -472,4 +469,28 @@ class StoryFragmentViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun markAsSeen(storyMedia: StoryMedia): LiveData<Resource<Story?>> {
|
||||||
|
val data = MutableLiveData<Resource<Story?>>()
|
||||||
|
data.postValue(loading(null))
|
||||||
|
val oldStory = currentStory.value!!
|
||||||
|
if (oldStory.seen != null && oldStory.seen >= storyMedia.takenAt) data.postValue(success(null))
|
||||||
|
else viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
storiesRepository.seen(
|
||||||
|
csrfToken!!,
|
||||||
|
userId,
|
||||||
|
deviceId,
|
||||||
|
storyMedia.id,
|
||||||
|
storyMedia.takenAt,
|
||||||
|
System.currentTimeMillis() / 1000
|
||||||
|
)
|
||||||
|
val newStory = oldStory.copy(seen = storyMedia.takenAt)
|
||||||
|
data.postValue(success(newStory))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
data.postValue(error(e.message, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,15 +1,11 @@
|
|||||||
package awais.instagrabber.webservices
|
package awais.instagrabber.webservices
|
||||||
|
|
||||||
import awais.instagrabber.models.FollowModel
|
|
||||||
import awais.instagrabber.repositories.FriendshipService
|
import awais.instagrabber.repositories.FriendshipService
|
||||||
import awais.instagrabber.repositories.responses.FriendshipChangeResponse
|
import awais.instagrabber.repositories.responses.FriendshipChangeResponse
|
||||||
import awais.instagrabber.repositories.responses.FriendshipListFetchResponse
|
import awais.instagrabber.repositories.responses.FriendshipListFetchResponse
|
||||||
import awais.instagrabber.repositories.responses.FriendshipRestrictResponse
|
import awais.instagrabber.repositories.responses.FriendshipRestrictResponse
|
||||||
import awais.instagrabber.utils.Utils
|
import awais.instagrabber.utils.Utils
|
||||||
import awais.instagrabber.webservices.RetrofitFactory.retrofit
|
import awais.instagrabber.webservices.RetrofitFactory.retrofit
|
||||||
import org.json.JSONArray
|
|
||||||
import org.json.JSONException
|
|
||||||
import org.json.JSONObject
|
|
||||||
|
|
||||||
class FriendshipRepository(private val service: FriendshipService) {
|
class FriendshipRepository(private val service: FriendshipService) {
|
||||||
|
|
||||||
@ -113,43 +109,12 @@ class FriendshipRepository(private val service: FriendshipService) {
|
|||||||
follower: Boolean,
|
follower: Boolean,
|
||||||
targetUserId: Long,
|
targetUserId: Long,
|
||||||
maxId: String?,
|
maxId: String?,
|
||||||
|
query: String?
|
||||||
): FriendshipListFetchResponse {
|
): FriendshipListFetchResponse {
|
||||||
val queryMap = if (maxId != null) mapOf("max_id" to maxId) else emptyMap()
|
val queryMap: MutableMap<String, String> = mutableMapOf()
|
||||||
val response = service.getList(targetUserId, if (follower) "followers" else "following", queryMap)
|
if (!maxId.isNullOrEmpty()) queryMap.set("max_id", maxId)
|
||||||
return parseListResponse(response)
|
if (!query.isNullOrEmpty()) queryMap.set("query", query)
|
||||||
}
|
return service.getList(targetUserId, if (follower) "followers" else "following", queryMap.toMap())
|
||||||
|
|
||||||
@Throws(JSONException::class)
|
|
||||||
private fun parseListResponse(body: String): FriendshipListFetchResponse {
|
|
||||||
val root = JSONObject(body)
|
|
||||||
val nextMaxId = root.optString("next_max_id")
|
|
||||||
val status = root.optString("status")
|
|
||||||
val itemsJson = root.optJSONArray("users")
|
|
||||||
val items = parseItems(itemsJson)
|
|
||||||
return FriendshipListFetchResponse(
|
|
||||||
nextMaxId,
|
|
||||||
status,
|
|
||||||
items
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(JSONException::class)
|
|
||||||
private fun parseItems(items: JSONArray?): List<FollowModel> {
|
|
||||||
if (items == null) {
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
val followModels = mutableListOf<FollowModel>()
|
|
||||||
for (i in 0 until items.length()) {
|
|
||||||
val itemJson = items.optJSONObject(i) ?: continue
|
|
||||||
val followModel = FollowModel(
|
|
||||||
itemJson.getString("pk"),
|
|
||||||
itemJson.getString("username"),
|
|
||||||
itemJson.optString("full_name"),
|
|
||||||
itemJson.getString("profile_pic_url")
|
|
||||||
)
|
|
||||||
followModels.add(followModel)
|
|
||||||
}
|
|
||||||
return followModels
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -178,51 +178,59 @@ open class GraphQLRepository(private val service: GraphQLService) {
|
|||||||
// TODO convert string response to a response class
|
// TODO convert string response to a response class
|
||||||
open suspend fun fetchUser(
|
open suspend fun fetchUser(
|
||||||
username: String,
|
username: String,
|
||||||
): User {
|
): User? {
|
||||||
val response = service.getUser(username)
|
val response = service.getUser(username)
|
||||||
val body = JSONObject(response
|
try {
|
||||||
.split("<script type=\"text/javascript\">window._sharedData = ").get(1)
|
val body = JSONObject(
|
||||||
.split("</script>").get(0)
|
response
|
||||||
.trim().replace(Regex("\\};$"), "}"))
|
.split("<script type=\"text/javascript\">window._sharedData = ").get(1)
|
||||||
val userJson = body
|
.split("</script>").get(0)
|
||||||
.getJSONObject("entry_data")
|
.trim().replace(Regex("\\};$"), "}")
|
||||||
.getJSONArray("ProfilePage")
|
)
|
||||||
.getJSONObject(0)
|
val userJson = body
|
||||||
.getJSONObject("graphql")
|
.getJSONObject("entry_data")
|
||||||
.getJSONObject(Constants.EXTRAS_USER)
|
.getJSONArray("ProfilePage")
|
||||||
val isPrivate = userJson.getBoolean("is_private")
|
.getJSONObject(0)
|
||||||
val id = userJson.optLong(Constants.EXTRAS_ID, 0)
|
.getJSONObject("graphql")
|
||||||
val timelineMedia = userJson.getJSONObject("edge_owner_to_timeline_media")
|
.getJSONObject(Constants.EXTRAS_USER)
|
||||||
// if (timelineMedia.has("edges")) {
|
val isPrivate = userJson.getBoolean("is_private")
|
||||||
// final JSONArray edges = timelineMedia.getJSONArray("edges");
|
val id = userJson.optLong(Constants.EXTRAS_ID, 0)
|
||||||
// }
|
val timelineMedia = userJson.getJSONObject("edge_owner_to_timeline_media")
|
||||||
var url: String? = userJson.optString("external_url")
|
// if (timelineMedia.has("edges")) {
|
||||||
if (url.isNullOrBlank()) url = null
|
// final JSONArray edges = timelineMedia.getJSONArray("edges");
|
||||||
return User(
|
// }
|
||||||
id,
|
var url: String? = userJson.optString("external_url")
|
||||||
username,
|
if (url.isNullOrBlank()) url = null
|
||||||
userJson.getString("full_name"),
|
return User(
|
||||||
isPrivate,
|
id,
|
||||||
userJson.getString("profile_pic_url_hd"),
|
username,
|
||||||
userJson.getBoolean("is_verified"),
|
userJson.getString("full_name"),
|
||||||
friendshipStatus = FriendshipStatus(
|
|
||||||
userJson.optBoolean("followed_by_viewer"),
|
|
||||||
userJson.optBoolean("follows_viewer"),
|
|
||||||
userJson.optBoolean("blocked_by_viewer"),
|
|
||||||
false,
|
|
||||||
isPrivate,
|
isPrivate,
|
||||||
userJson.optBoolean("has_requested_viewer"),
|
userJson.getString("profile_pic_url_hd"),
|
||||||
userJson.optBoolean("requested_by_viewer"),
|
userJson.getBoolean("is_verified"),
|
||||||
false,
|
friendshipStatus = FriendshipStatus(
|
||||||
userJson.optBoolean("restricted_by_viewer"),
|
userJson.optBoolean("followed_by_viewer"),
|
||||||
false
|
userJson.optBoolean("follows_viewer"),
|
||||||
),
|
userJson.optBoolean("blocked_by_viewer"),
|
||||||
mediaCount = timelineMedia.getLong("count"),
|
false,
|
||||||
followerCount = userJson.getJSONObject("edge_followed_by").getLong("count"),
|
isPrivate,
|
||||||
followingCount = userJson.getJSONObject("edge_follow").getLong("count"),
|
userJson.optBoolean("has_requested_viewer"),
|
||||||
biography = userJson.getString("biography"),
|
userJson.optBoolean("requested_by_viewer"),
|
||||||
externalUrl = url,
|
false,
|
||||||
)
|
userJson.optBoolean("restricted_by_viewer"),
|
||||||
|
false
|
||||||
|
),
|
||||||
|
mediaCount = timelineMedia.getLong("count"),
|
||||||
|
followerCount = userJson.getJSONObject("edge_followed_by").getLong("count"),
|
||||||
|
followingCount = userJson.getJSONObject("edge_follow").getLong("count"),
|
||||||
|
biography = userJson.getString("biography"),
|
||||||
|
externalUrl = url,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
catch (e: Exception) {
|
||||||
|
Log.e(TAG, "fetchUser failed", e)
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO convert string response to a response class
|
// TODO convert string response to a response class
|
||||||
|
@ -3,13 +3,13 @@ package thoughtbot.expandableadapter;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import awais.instagrabber.models.FollowModel;
|
import awais.instagrabber.repositories.responses.User;
|
||||||
|
|
||||||
public class ExpandableGroup {
|
public class ExpandableGroup {
|
||||||
private final String title;
|
private final String title;
|
||||||
private final List<FollowModel> items;
|
private final List<User> items;
|
||||||
|
|
||||||
public ExpandableGroup(final String title, final List<FollowModel> items) {
|
public ExpandableGroup(final String title, final List<User> items) {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.items = items;
|
this.items = items;
|
||||||
}
|
}
|
||||||
@ -18,22 +18,13 @@ public class ExpandableGroup {
|
|||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FollowModel> getItems(final boolean filtered) {
|
public List<User> getItems() {
|
||||||
if (!filtered) return items;
|
return items;
|
||||||
final ArrayList<FollowModel> followModels = new ArrayList<>();
|
|
||||||
for (final FollowModel followModel : items) if (followModel.isShown()) followModels.add(followModel);
|
|
||||||
return followModels;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getItemCount(final boolean filtered) {
|
public int getItemCount() {
|
||||||
if (items != null) {
|
if (items != null) {
|
||||||
final int size = items.size();
|
return items.size();
|
||||||
if (filtered) {
|
|
||||||
int finalSize = 0;
|
|
||||||
for (int i = 0; i < size; ++i) if (items.get(i).isShown()) ++finalSize;
|
|
||||||
return finalSize;
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package thoughtbot.expandableadapter;
|
package thoughtbot.expandableadapter;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
@ -15,6 +16,13 @@ public final class ExpandableList {
|
|||||||
this.expandedGroupIndexes = new boolean[groupsSize];
|
this.expandedGroupIndexes = new boolean[groupsSize];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ExpandableList(@NonNull final ArrayList<ExpandableGroup> groups,
|
||||||
|
@Nullable final boolean[] expandedGroupIndexes) {
|
||||||
|
this.groups = groups;
|
||||||
|
this.groupsSize = groups.size();
|
||||||
|
this.expandedGroupIndexes = expandedGroupIndexes;
|
||||||
|
}
|
||||||
|
|
||||||
public int getVisibleItemCount() {
|
public int getVisibleItemCount() {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (int i = 0; i < groupsSize; i++) count = count + numberOfVisibleItemsInGroup(i);
|
for (int i = 0; i < groupsSize; i++) count = count + numberOfVisibleItemsInGroup(i);
|
||||||
@ -36,7 +44,7 @@ public final class ExpandableList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int numberOfVisibleItemsInGroup(final int group) {
|
private int numberOfVisibleItemsInGroup(final int group) {
|
||||||
return expandedGroupIndexes[group] ? groups.get(group).getItemCount(true) + 1 : 1;
|
return expandedGroupIndexes[group] ? groups.get(group).getItemCount() + 1 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getFlattenedGroupIndex(@NonNull final ExpandableListPosition listPosition) {
|
public int getFlattenedGroupIndex(@NonNull final ExpandableListPosition listPosition) {
|
||||||
|
@ -16,6 +16,5 @@
|
|||||||
android:paddingLeft="8dp"
|
android:paddingLeft="8dp"
|
||||||
android:paddingEnd="8dp"
|
android:paddingEnd="8dp"
|
||||||
android:paddingRight="8dp"
|
android:paddingRight="8dp"
|
||||||
app:layoutManager="LinearLayoutManager"
|
|
||||||
tools:listitem="@layout/item_follow" />
|
tools:listitem="@layout/item_follow" />
|
||||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
@ -1,113 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout 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:orientation="vertical">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/top_container"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/btnComments"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="?android:selectableItemBackground"
|
|
||||||
android:gravity="center"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:padding="4dp">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:padding="4dp"
|
|
||||||
app:srcCompat="@drawable/ic_outline_comments_24"
|
|
||||||
app:tint="?android:textColorPrimary" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
|
||||||
android:id="@+id/commentsCount"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:textAppearance="?attr/textAppearanceButton"
|
|
||||||
tools:text="690000" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/btnViews"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:gravity="center"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:padding="4dp">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:padding="4dp"
|
|
||||||
app:srcCompat="@drawable/ic_outline_views_24"
|
|
||||||
app:tint="?android:textColorPrimary" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
|
||||||
android:id="@+id/tvVideoViews"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:textAppearance="?attr/textAppearanceButton"
|
|
||||||
tools:text="690000" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
|
||||||
android:id="@+id/tvPostDate"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:padding="8dp"
|
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
|
|
||||||
tools:text="2020-01-01 12:00:00" />
|
|
||||||
|
|
||||||
<!--<androidx.appcompat.widget.AppCompatImageView-->
|
|
||||||
<!-- android:id="@+id/btnMute"-->
|
|
||||||
<!-- android:layout_width="wrap_content"-->
|
|
||||||
<!-- android:layout_height="match_parent"-->
|
|
||||||
<!-- android:background="?selectableItemBackgroundBorderless"-->
|
|
||||||
<!-- android:padding="4dp"-->
|
|
||||||
<!-- android:visibility="gone"-->
|
|
||||||
<!-- app:srcCompat="@drawable/ic_volume_up_24"-->
|
|
||||||
<!-- app:tint="?android:textColorPrimary"-->
|
|
||||||
<!-- tools:visibility="visible" />-->
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
|
||||||
android:id="@+id/btnDownload"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="?selectableItemBackgroundBorderless"
|
|
||||||
android:padding="4dp"
|
|
||||||
app:srcCompat="@drawable/ic_download"
|
|
||||||
app:tint="?android:textColorPrimary" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<awais.instagrabber.customviews.RamboTextViewV2
|
|
||||||
android:id="@+id/viewerCaption"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="?android:selectableItemBackground"
|
|
||||||
android:clickable="true"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:focusable="true"
|
|
||||||
android:maxLines="5"
|
|
||||||
android:paddingStart="8dp"
|
|
||||||
android:paddingLeft="8dp"
|
|
||||||
android:paddingEnd="8dp"
|
|
||||||
android:paddingRight="8dp"
|
|
||||||
android:paddingBottom="8dp"
|
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
|
||||||
tools:text="Bottom text with hashtags etc." />
|
|
||||||
</LinearLayout>
|
|
@ -1,13 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<include
|
<include layout="@layout/item_feed_top" />
|
||||||
android:id="@+id/item_feed_top"
|
|
||||||
layout="@layout/item_feed_top" />
|
|
||||||
|
|
||||||
<awais.instagrabber.customviews.drawee.ZoomableDraweeView
|
<awais.instagrabber.customviews.drawee.ZoomableDraweeView
|
||||||
android:id="@+id/imageViewer"
|
android:id="@+id/imageViewer"
|
||||||
@ -17,9 +15,9 @@
|
|||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
app:actualImageScaleType="fitCenter"
|
app:actualImageScaleType="fitCenter"
|
||||||
app:viewAspectRatio="1" />
|
app:viewAspectRatio="1"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/top_barrier"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/buttons_top_barrier"/>
|
||||||
|
|
||||||
<include
|
<include layout="@layout/layout_post_view_bottom" />
|
||||||
android:id="@+id/item_feed_bottom"
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
layout="@layout/item_feed_bottom" />
|
|
||||||
</LinearLayout>
|
|
@ -1,18 +1,18 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<include
|
<include layout="@layout/item_feed_top" />
|
||||||
android:id="@+id/item_feed_top"
|
|
||||||
layout="@layout/item_feed_top" />
|
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
|
android:id="@+id/post_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/top_barrier"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/buttons_top_barrier">
|
||||||
|
|
||||||
<androidx.viewpager2.widget.ViewPager2
|
<androidx.viewpager2.widget.ViewPager2
|
||||||
android:id="@+id/media_list"
|
android:id="@+id/media_list"
|
||||||
@ -39,7 +39,5 @@
|
|||||||
android:textColor="@android:color/white" />
|
android:textColor="@android:color/white" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
<include
|
<include layout="@layout/layout_post_view_bottom" />
|
||||||
android:id="@+id/item_feed_bottom"
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
layout="@layout/item_feed_bottom" />
|
|
||||||
</LinearLayout>
|
|
@ -1,63 +1,86 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/container"
|
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:padding="8dp">
|
|
||||||
|
|
||||||
<com.facebook.drawee.view.SimpleDraweeView
|
<awais.instagrabber.customviews.ProfilePicView
|
||||||
android:id="@+id/ivProfilePic"
|
android:id="@+id/profile_pic"
|
||||||
android:layout_width="@dimen/profile_pic_size_regular"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="@dimen/profile_pic_size_regular"
|
android:layout_height="wrap_content"
|
||||||
android:background="?selectableItemBackgroundBorderless"
|
android:layout_margin="12dp"
|
||||||
app:roundAsCircle="true" />
|
android:transitionName="profile_pic"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/top_barrier"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/title"
|
||||||
|
app:layout_constraintHorizontal_bias="0"
|
||||||
|
app:layout_constraintHorizontal_chainStyle="packed"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:size="regular" />
|
||||||
|
|
||||||
<RelativeLayout
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/infoContainer"
|
android:id="@+id/title"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:ellipsize="marquee"
|
||||||
android:animateLayoutChanges="true"
|
android:singleLine="true"
|
||||||
android:background="@null"
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
|
||||||
android:gravity="center"
|
app:layout_constrainedWidth="true"
|
||||||
android:orientation="vertical"
|
app:layout_constraintBottom_toTopOf="@id/subtitle"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/profile_pic"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/profile_pic"
|
||||||
|
tools:text="Username Username Username" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/subtitle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/profile_pic"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/title"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/title"
|
||||||
|
tools:text="Full name Full name Full name Full name Full name Full name Full name "
|
||||||
|
tools:visibility="gone" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Barrier
|
||||||
|
android:id="@+id/top_barrier"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:barrierAllowsGoneWidgets="true"
|
||||||
|
app:barrierDirection="bottom"/>
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/location"
|
||||||
|
style="?borderlessButtonStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:elevation="0dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:insetTop="0dp"
|
||||||
|
android:insetBottom="0dp"
|
||||||
|
android:maxWidth="200dp"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:minHeight="32dp"
|
||||||
android:paddingStart="8dp"
|
android:paddingStart="8dp"
|
||||||
android:paddingLeft="8dp"
|
|
||||||
android:paddingEnd="8dp"
|
android:paddingEnd="8dp"
|
||||||
android:paddingRight="8dp"
|
android:textAlignment="viewStart"
|
||||||
android:weightSum="2">
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
android:visibility="gone"
|
||||||
android:id="@+id/title"
|
app:backgroundTint="@color/black_a50"
|
||||||
android:layout_width="match_parent"
|
app:elevation="0dp"
|
||||||
android:layout_height="wrap_content"
|
app:icon="@drawable/ic_round_location_on_24"
|
||||||
android:gravity="center_vertical"
|
app:iconSize="16dp"
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
app:iconTint="@color/white"
|
||||||
tools:text="username" />
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/top_barrier"
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
app:rippleColor="@color/grey_600"
|
||||||
android:id="@+id/location"
|
tools:text="Location, Location, Location, Location, "
|
||||||
android:layout_width="match_parent"
|
tools:visibility="visible" />
|
||||||
android:layout_height="wrap_content"
|
</merge>
|
||||||
android:layout_below="@+id/title"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
|
|
||||||
android:textSize="15sp"
|
|
||||||
android:visibility="visible"
|
|
||||||
tools:text="location" />
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
<!--<androidx.appcompat.widget.AppCompatImageView-->
|
|
||||||
<!-- android:id="@+id/viewStoryPost"-->
|
|
||||||
<!-- android:layout_width="wrap_content"-->
|
|
||||||
<!-- android:layout_height="match_parent"-->
|
|
||||||
<!-- android:layout_gravity="center"-->
|
|
||||||
<!-- android:background="?selectableItemBackgroundBorderless"-->
|
|
||||||
<!-- app:srcCompat="@drawable/ic_open_in_new_24"-->
|
|
||||||
<!-- app:tint="?android:textColorPrimary" />-->
|
|
||||||
</LinearLayout>
|
|
@ -1,19 +1,20 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/videoHolder"
|
android:id="@+id/videoHolder"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<include
|
<include layout="@layout/item_feed_top" />
|
||||||
android:id="@+id/item_feed_top"
|
|
||||||
layout="@layout/item_feed_top" />
|
|
||||||
|
|
||||||
<include
|
<FrameLayout
|
||||||
android:id="@+id/video_post"
|
android:id="@+id/post_container"
|
||||||
layout="@layout/layout_video_player_with_thumbnail" />
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/buttons_top_barrier"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/top_barrier"
|
||||||
|
tools:layout_height="100dp" />
|
||||||
|
|
||||||
<include
|
<include layout="@layout/layout_post_view_bottom" />
|
||||||
android:id="@+id/item_feed_bottom"
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
layout="@layout/item_feed_bottom" />
|
|
||||||
</LinearLayout>
|
|
@ -1,105 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout 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:layout_marginTop="?attr/actionBarSize"
|
|
||||||
android:animateLayoutChanges="true"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:weightSum="3.2">
|
|
||||||
|
|
||||||
<include
|
|
||||||
android:id="@+id/topPanel"
|
|
||||||
layout="@layout/item_feed_top" />
|
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:id="@+id/container"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_weight="1.9">
|
|
||||||
|
|
||||||
<awais.instagrabber.customviews.helpers.NestedScrollableHost
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<androidx.viewpager2.widget.ViewPager2
|
|
||||||
android:id="@+id/mediaViewPager"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent" />
|
|
||||||
</awais.instagrabber.customviews.helpers.NestedScrollableHost>
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
|
||||||
android:id="@+id/mediaCounter"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_horizontal|bottom"
|
|
||||||
android:background="@drawable/rounder_corner_semi_black_bg"
|
|
||||||
android:gravity="center"
|
|
||||||
android:padding="5dp"
|
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
|
|
||||||
android:textColor="@android:color/white"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<ProgressBar
|
|
||||||
android:id="@+id/progressView"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="48dp"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<!-- Removing for now, will add in later versions -->
|
|
||||||
<!--<androidx.appcompat.widget.AppCompatImageView-->
|
|
||||||
<!-- android:id="@+id/ivToggleFullScreen"-->
|
|
||||||
<!-- android:layout_width="48dp"-->
|
|
||||||
<!-- android:layout_height="48dp"-->
|
|
||||||
<!-- android:layout_gravity="end|top"-->
|
|
||||||
<!-- android:background="?selectableItemBackgroundBorderless"-->
|
|
||||||
<!-- android:padding="4dp"-->
|
|
||||||
<!-- app:srcCompat="@drawable/ic_fullscreen"-->
|
|
||||||
<!-- app:tint="?android:textColorPrimary" />-->
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
<include
|
|
||||||
android:id="@+id/bottomPanel"
|
|
||||||
layout="@layout/item_feed_bottom"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_weight="1" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
|
||||||
android:id="@+id/postActions"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_weight="0.3"
|
|
||||||
android:background="#0000"
|
|
||||||
android:weightSum="2">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatButton
|
|
||||||
android:id="@+id/btnLike"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="6dp"
|
|
||||||
android:layout_marginLeft="8dp"
|
|
||||||
android:layout_marginRight="8dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:textColor="@color/btn_lightpink_text_color"
|
|
||||||
android:textSize="18sp"
|
|
||||||
app:backgroundTint="@color/btn_lightpink_background"
|
|
||||||
tools:text="@string/like" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatButton
|
|
||||||
android:id="@+id/btnBookmark"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentBottom="true"
|
|
||||||
android:layout_marginLeft="8dp"
|
|
||||||
android:layout_marginEnd="6dp"
|
|
||||||
android:layout_marginRight="8dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:text="@string/bookmark"
|
|
||||||
android:textColor="@color/btn_lightorange_text_color"
|
|
||||||
android:textSize="18sp"
|
|
||||||
app:backgroundTint="@color/btn_lightorange_background" />
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
|
||||||
</LinearLayout>
|
|
@ -253,7 +253,6 @@
|
|||||||
<string name="action_ayml">Suggested users</string>
|
<string name="action_ayml">Suggested users</string>
|
||||||
<string name="select_picture">Select Picture</string>
|
<string name="select_picture">Select Picture</string>
|
||||||
<string name="uploading">Uploading…</string>
|
<string name="uploading">Uploading…</string>
|
||||||
<string name="activity_count_prefix">You have:</string>
|
|
||||||
<string name="activity_count_relationship">%d follows</string>
|
<string name="activity_count_relationship">%d follows</string>
|
||||||
<string name="activity_count_comments">%d comments</string>
|
<string name="activity_count_comments">%d comments</string>
|
||||||
<string name="activity_count_commentlikes">%d comment likes</string>
|
<string name="activity_count_commentlikes">%d comment likes</string>
|
||||||
|
@ -237,7 +237,6 @@
|
|||||||
<string name="action_ayml">Usuaris suggerits</string>
|
<string name="action_ayml">Usuaris suggerits</string>
|
||||||
<string name="select_picture">Seleccionar imatge</string>
|
<string name="select_picture">Seleccionar imatge</string>
|
||||||
<string name="uploading">S\'està pujant…</string>
|
<string name="uploading">S\'està pujant…</string>
|
||||||
<string name="activity_count_prefix">Tens:</string>
|
|
||||||
<string name="activity_count_relationship">%d seguidors</string>
|
<string name="activity_count_relationship">%d seguidors</string>
|
||||||
<string name="activity_count_comments">%d comentaris</string>
|
<string name="activity_count_comments">%d comentaris</string>
|
||||||
<string name="activity_count_commentlikes">%d m\'agrades al comentari</string>
|
<string name="activity_count_commentlikes">%d m\'agrades al comentari</string>
|
||||||
|
@ -245,7 +245,6 @@
|
|||||||
<string name="action_ayml">Navrhovaní uživatelé</string>
|
<string name="action_ayml">Navrhovaní uživatelé</string>
|
||||||
<string name="select_picture">Vybrat obrázek</string>
|
<string name="select_picture">Vybrat obrázek</string>
|
||||||
<string name="uploading">Nahrávání…</string>
|
<string name="uploading">Nahrávání…</string>
|
||||||
<string name="activity_count_prefix">Máte:</string>
|
|
||||||
<string name="activity_count_relationship">%d sleduje</string>
|
<string name="activity_count_relationship">%d sleduje</string>
|
||||||
<string name="activity_count_comments">%d komentářů</string>
|
<string name="activity_count_comments">%d komentářů</string>
|
||||||
<string name="activity_count_commentlikes">%d lajků komentáře</string>
|
<string name="activity_count_commentlikes">%d lajků komentáře</string>
|
||||||
|
@ -237,7 +237,6 @@
|
|||||||
<string name="action_ayml">Vorgeschlagene Benutzer</string>
|
<string name="action_ayml">Vorgeschlagene Benutzer</string>
|
||||||
<string name="select_picture">Bild auswählen</string>
|
<string name="select_picture">Bild auswählen</string>
|
||||||
<string name="uploading">Hochladen…</string>
|
<string name="uploading">Hochladen…</string>
|
||||||
<string name="activity_count_prefix">Du hast:</string>
|
|
||||||
<string name="activity_count_relationship">%d Abonnenten</string>
|
<string name="activity_count_relationship">%d Abonnenten</string>
|
||||||
<string name="activity_count_comments">%d Kommentare</string>
|
<string name="activity_count_comments">%d Kommentare</string>
|
||||||
<string name="activity_count_commentlikes">%d gelikte Kommentare</string>
|
<string name="activity_count_commentlikes">%d gelikte Kommentare</string>
|
||||||
|
@ -237,7 +237,6 @@
|
|||||||
<string name="action_ayml">Προτεινόμενοι χρήστες</string>
|
<string name="action_ayml">Προτεινόμενοι χρήστες</string>
|
||||||
<string name="select_picture">Επιλογή εικόνας</string>
|
<string name="select_picture">Επιλογή εικόνας</string>
|
||||||
<string name="uploading">Μεταφόρτωση…</string>
|
<string name="uploading">Μεταφόρτωση…</string>
|
||||||
<string name="activity_count_prefix">Έχετε:</string>
|
|
||||||
<string name="activity_count_relationship">%d ακόλουθοι</string>
|
<string name="activity_count_relationship">%d ακόλουθοι</string>
|
||||||
<string name="activity_count_comments">%d σχόλια</string>
|
<string name="activity_count_comments">%d σχόλια</string>
|
||||||
<string name="activity_count_commentlikes">Το σχόλιο αρέσει σε %d</string>
|
<string name="activity_count_commentlikes">Το σχόλιο αρέσει σε %d</string>
|
||||||
|
@ -237,7 +237,6 @@
|
|||||||
<string name="action_ayml">Usuarios sugeridos</string>
|
<string name="action_ayml">Usuarios sugeridos</string>
|
||||||
<string name="select_picture">Seleccionar imagen</string>
|
<string name="select_picture">Seleccionar imagen</string>
|
||||||
<string name="uploading">Subiendo…</string>
|
<string name="uploading">Subiendo…</string>
|
||||||
<string name="activity_count_prefix">Tienes:</string>
|
|
||||||
<string name="activity_count_relationship">%d sigue</string>
|
<string name="activity_count_relationship">%d sigue</string>
|
||||||
<string name="activity_count_comments">%d comentarios</string>
|
<string name="activity_count_comments">%d comentarios</string>
|
||||||
<string name="activity_count_commentlikes">%d me gustas en comentarios</string>
|
<string name="activity_count_commentlikes">%d me gustas en comentarios</string>
|
||||||
|
@ -237,7 +237,6 @@
|
|||||||
<string name="action_ayml">Iradokitutako erabiltzaileak</string>
|
<string name="action_ayml">Iradokitutako erabiltzaileak</string>
|
||||||
<string name="select_picture">Hautatu irudia</string>
|
<string name="select_picture">Hautatu irudia</string>
|
||||||
<string name="uploading">Igotzen…</string>
|
<string name="uploading">Igotzen…</string>
|
||||||
<string name="activity_count_prefix">Duzuna:</string>
|
|
||||||
<string name="activity_count_relationship">%d jarraitzaile</string>
|
<string name="activity_count_relationship">%d jarraitzaile</string>
|
||||||
<string name="activity_count_comments">%d iruzkin</string>
|
<string name="activity_count_comments">%d iruzkin</string>
|
||||||
<string name="activity_count_commentlikes">%d iruzkin-atsegite</string>
|
<string name="activity_count_commentlikes">%d iruzkin-atsegite</string>
|
||||||
|
@ -238,7 +238,6 @@
|
|||||||
<string name="action_ayml">Suggested users</string>
|
<string name="action_ayml">Suggested users</string>
|
||||||
<string name="select_picture">انتخاب تصویر</string>
|
<string name="select_picture">انتخاب تصویر</string>
|
||||||
<string name="uploading">Uploading…</string>
|
<string name="uploading">Uploading…</string>
|
||||||
<string name="activity_count_prefix">شما باید:</string>
|
|
||||||
<string name="activity_count_relationship">%d دنبال کننده</string>
|
<string name="activity_count_relationship">%d دنبال کننده</string>
|
||||||
<string name="activity_count_comments">%d دیدگاه</string>
|
<string name="activity_count_comments">%d دیدگاه</string>
|
||||||
<string name="activity_count_commentlikes">%d پسند دیدگاه</string>
|
<string name="activity_count_commentlikes">%d پسند دیدگاه</string>
|
||||||
|
@ -237,7 +237,6 @@
|
|||||||
<string name="action_ayml">Utilisateurs suggérés</string>
|
<string name="action_ayml">Utilisateurs suggérés</string>
|
||||||
<string name="select_picture">Sélectionnez une image</string>
|
<string name="select_picture">Sélectionnez une image</string>
|
||||||
<string name="uploading">Envoi en cours…</string>
|
<string name="uploading">Envoi en cours…</string>
|
||||||
<string name="activity_count_prefix">Vous avez :</string>
|
|
||||||
<string name="activity_count_relationship">%d abonné(e)s</string>
|
<string name="activity_count_relationship">%d abonné(e)s</string>
|
||||||
<string name="activity_count_comments">%d commentaires</string>
|
<string name="activity_count_comments">%d commentaires</string>
|
||||||
<string name="activity_count_commentlikes">%d j\'aime(s) sur le commentaire</string>
|
<string name="activity_count_commentlikes">%d j\'aime(s) sur le commentaire</string>
|
||||||
|
@ -238,7 +238,6 @@
|
|||||||
<string name="action_ayml">सुझायें ऊपयोगकर्ता</string>
|
<string name="action_ayml">सुझायें ऊपयोगकर्ता</string>
|
||||||
<string name="select_picture">चित्र का चयन करें</string>
|
<string name="select_picture">चित्र का चयन करें</string>
|
||||||
<string name="uploading">अपलोड हो रहा है...</string>
|
<string name="uploading">अपलोड हो रहा है...</string>
|
||||||
<string name="activity_count_prefix">आपके पास है:</string>
|
|
||||||
<string name="activity_count_relationship">%d अनुगामी</string>
|
<string name="activity_count_relationship">%d अनुगामी</string>
|
||||||
<string name="activity_count_comments">%d टिप्पणियाँ</string>
|
<string name="activity_count_comments">%d टिप्पणियाँ</string>
|
||||||
<string name="activity_count_commentlikes">%d टिप्पणीयाँ पसन्दीत</string>
|
<string name="activity_count_commentlikes">%d टिप्पणीयाँ पसन्दीत</string>
|
||||||
|
@ -233,7 +233,6 @@
|
|||||||
<string name="action_ayml">Pengguna yang disarankan</string>
|
<string name="action_ayml">Pengguna yang disarankan</string>
|
||||||
<string name="select_picture">Pilih Gambar</string>
|
<string name="select_picture">Pilih Gambar</string>
|
||||||
<string name="uploading">Mengunggah…</string>
|
<string name="uploading">Mengunggah…</string>
|
||||||
<string name="activity_count_prefix">Anda memiliki:</string>
|
|
||||||
<string name="activity_count_relationship">%d mengikuti</string>
|
<string name="activity_count_relationship">%d mengikuti</string>
|
||||||
<string name="activity_count_comments">%d komentar</string>
|
<string name="activity_count_comments">%d komentar</string>
|
||||||
<string name="activity_count_commentlikes">%d suka komentar</string>
|
<string name="activity_count_commentlikes">%d suka komentar</string>
|
||||||
|
@ -237,7 +237,6 @@
|
|||||||
<string name="action_ayml">Utenti suggeriti</string>
|
<string name="action_ayml">Utenti suggeriti</string>
|
||||||
<string name="select_picture">Seleziona Immagine</string>
|
<string name="select_picture">Seleziona Immagine</string>
|
||||||
<string name="uploading">Caricamento…</string>
|
<string name="uploading">Caricamento…</string>
|
||||||
<string name="activity_count_prefix">Hai:</string>
|
|
||||||
<string name="activity_count_relationship">%d seguaci</string>
|
<string name="activity_count_relationship">%d seguaci</string>
|
||||||
<string name="activity_count_comments">%d commenti</string>
|
<string name="activity_count_comments">%d commenti</string>
|
||||||
<string name="activity_count_commentlikes">%d mi piace al commento</string>
|
<string name="activity_count_commentlikes">%d mi piace al commento</string>
|
||||||
|
@ -233,7 +233,6 @@
|
|||||||
<string name="action_ayml">おすすめのユーザー</string>
|
<string name="action_ayml">おすすめのユーザー</string>
|
||||||
<string name="select_picture">画像を選択</string>
|
<string name="select_picture">画像を選択</string>
|
||||||
<string name="uploading">アップロード中…</string>
|
<string name="uploading">アップロード中…</string>
|
||||||
<string name="activity_count_prefix">あなたのステータス:</string>
|
|
||||||
<string name="activity_count_relationship">%d 人のフォロワー</string>
|
<string name="activity_count_relationship">%d 人のフォロワー</string>
|
||||||
<string name="activity_count_comments">%d コメント</string>
|
<string name="activity_count_comments">%d コメント</string>
|
||||||
<string name="activity_count_commentlikes">%d 個のコメントへのいいね!</string>
|
<string name="activity_count_commentlikes">%d 個のコメントへのいいね!</string>
|
||||||
|
@ -233,7 +233,6 @@
|
|||||||
<string name="action_ayml">프로필 추천</string>
|
<string name="action_ayml">프로필 추천</string>
|
||||||
<string name="select_picture">사진 선택</string>
|
<string name="select_picture">사진 선택</string>
|
||||||
<string name="uploading">업로드 중…</string>
|
<string name="uploading">업로드 중…</string>
|
||||||
<string name="activity_count_prefix">You have:</string>
|
|
||||||
<string name="activity_count_relationship">%d follows</string>
|
<string name="activity_count_relationship">%d follows</string>
|
||||||
<string name="activity_count_comments">%d comments</string>
|
<string name="activity_count_comments">%d comments</string>
|
||||||
<string name="activity_count_commentlikes">%d comment likes</string>
|
<string name="activity_count_commentlikes">%d comment likes</string>
|
||||||
|
@ -237,7 +237,6 @@
|
|||||||
<string name="action_ayml">Препорачани кориснчки сметки</string>
|
<string name="action_ayml">Препорачани кориснчки сметки</string>
|
||||||
<string name="select_picture">Селектирај слика</string>
|
<string name="select_picture">Селектирај слика</string>
|
||||||
<string name="uploading">Се Прикачува…</string>
|
<string name="uploading">Се Прикачува…</string>
|
||||||
<string name="activity_count_prefix">Вие имате:</string>
|
|
||||||
<string name="activity_count_relationship">%d следачи</string>
|
<string name="activity_count_relationship">%d следачи</string>
|
||||||
<string name="activity_count_comments">%d коментари</string>
|
<string name="activity_count_comments">%d коментари</string>
|
||||||
<string name="activity_count_commentlikes">%d лајкови на коментари</string>
|
<string name="activity_count_commentlikes">%d лајкови на коментари</string>
|
||||||
|
@ -237,7 +237,6 @@
|
|||||||
<string name="action_ayml">Voorgestelde gebruikers</string>
|
<string name="action_ayml">Voorgestelde gebruikers</string>
|
||||||
<string name="select_picture">Selecteer Afbeelding</string>
|
<string name="select_picture">Selecteer Afbeelding</string>
|
||||||
<string name="uploading">Bezig met uploaden…</string>
|
<string name="uploading">Bezig met uploaden…</string>
|
||||||
<string name="activity_count_prefix">Je hebt:</string>
|
|
||||||
<string name="activity_count_relationship">%d volgers</string>
|
<string name="activity_count_relationship">%d volgers</string>
|
||||||
<string name="activity_count_comments">%d opmerkingen</string>
|
<string name="activity_count_comments">%d opmerkingen</string>
|
||||||
<string name="activity_count_commentlikes">%d opmerking-likes</string>
|
<string name="activity_count_commentlikes">%d opmerking-likes</string>
|
||||||
|
@ -237,7 +237,6 @@
|
|||||||
<string name="action_ayml">Suggested users</string>
|
<string name="action_ayml">Suggested users</string>
|
||||||
<string name="select_picture">Select Picture</string>
|
<string name="select_picture">Select Picture</string>
|
||||||
<string name="uploading">Uploading…</string>
|
<string name="uploading">Uploading…</string>
|
||||||
<string name="activity_count_prefix">You have:</string>
|
|
||||||
<string name="activity_count_relationship">%d follows</string>
|
<string name="activity_count_relationship">%d follows</string>
|
||||||
<string name="activity_count_comments">%d comments</string>
|
<string name="activity_count_comments">%d comments</string>
|
||||||
<string name="activity_count_commentlikes">%d comment likes</string>
|
<string name="activity_count_commentlikes">%d comment likes</string>
|
||||||
|
@ -245,7 +245,6 @@
|
|||||||
<string name="action_ayml">Proponowani użytkownicy</string>
|
<string name="action_ayml">Proponowani użytkownicy</string>
|
||||||
<string name="select_picture">Wybierz obraz</string>
|
<string name="select_picture">Wybierz obraz</string>
|
||||||
<string name="uploading">Przesyłanie…</string>
|
<string name="uploading">Przesyłanie…</string>
|
||||||
<string name="activity_count_prefix">Masz:</string>
|
|
||||||
<string name="activity_count_relationship">%d obserwujących</string>
|
<string name="activity_count_relationship">%d obserwujących</string>
|
||||||
<string name="activity_count_comments">%d komentarzy</string>
|
<string name="activity_count_comments">%d komentarzy</string>
|
||||||
<string name="activity_count_commentlikes">%d polubionych komentarzy</string>
|
<string name="activity_count_commentlikes">%d polubionych komentarzy</string>
|
||||||
|
@ -237,7 +237,6 @@
|
|||||||
<string name="action_ayml">Usuários sugeridos</string>
|
<string name="action_ayml">Usuários sugeridos</string>
|
||||||
<string name="select_picture">Selecionar imagem</string>
|
<string name="select_picture">Selecionar imagem</string>
|
||||||
<string name="uploading">Enviando…</string>
|
<string name="uploading">Enviando…</string>
|
||||||
<string name="activity_count_prefix">Você tem:</string>
|
|
||||||
<string name="activity_count_relationship">%d seguidores</string>
|
<string name="activity_count_relationship">%d seguidores</string>
|
||||||
<string name="activity_count_comments">%d comentários</string>
|
<string name="activity_count_comments">%d comentários</string>
|
||||||
<string name="activity_count_commentlikes">%d comentários curtidos</string>
|
<string name="activity_count_commentlikes">%d comentários curtidos</string>
|
||||||
|
@ -245,7 +245,6 @@
|
|||||||
<string name="action_ayml">Предлагаемые пользователи</string>
|
<string name="action_ayml">Предлагаемые пользователи</string>
|
||||||
<string name="select_picture">Выберите изображение</string>
|
<string name="select_picture">Выберите изображение</string>
|
||||||
<string name="uploading">Загрузка…</string>
|
<string name="uploading">Загрузка…</string>
|
||||||
<string name="activity_count_prefix">У вас есть:</string>
|
|
||||||
<string name="activity_count_relationship">%d подписано</string>
|
<string name="activity_count_relationship">%d подписано</string>
|
||||||
<string name="activity_count_comments">%d комментариев</string>
|
<string name="activity_count_comments">%d комментариев</string>
|
||||||
<string name="activity_count_commentlikes">%d симпатий к комментарию</string>
|
<string name="activity_count_commentlikes">%d симпатий к комментарию</string>
|
||||||
|
@ -245,7 +245,6 @@
|
|||||||
<string name="action_ayml">Používatelia ktorých možno poznáte</string>
|
<string name="action_ayml">Používatelia ktorých možno poznáte</string>
|
||||||
<string name="select_picture">Vybrať fotografiu</string>
|
<string name="select_picture">Vybrať fotografiu</string>
|
||||||
<string name="uploading">Nahráva sa…</string>
|
<string name="uploading">Nahráva sa…</string>
|
||||||
<string name="activity_count_prefix">Máš:</string>
|
|
||||||
<string name="activity_count_relationship">%d sledovaní</string>
|
<string name="activity_count_relationship">%d sledovaní</string>
|
||||||
<string name="activity_count_comments">%d komentárov</string>
|
<string name="activity_count_comments">%d komentárov</string>
|
||||||
<string name="activity_count_commentlikes">%d komentárov ktoré sa niekomu páčia</string>
|
<string name="activity_count_commentlikes">%d komentárov ktoré sa niekomu páčia</string>
|
||||||
|
@ -237,7 +237,6 @@
|
|||||||
<string name="action_ayml">Suggested users</string>
|
<string name="action_ayml">Suggested users</string>
|
||||||
<string name="select_picture">Välj bild</string>
|
<string name="select_picture">Välj bild</string>
|
||||||
<string name="uploading">Laddar upp…</string>
|
<string name="uploading">Laddar upp…</string>
|
||||||
<string name="activity_count_prefix">Du har:</string>
|
|
||||||
<string name="activity_count_relationship">%d följer</string>
|
<string name="activity_count_relationship">%d följer</string>
|
||||||
<string name="activity_count_comments">%d kommentarer</string>
|
<string name="activity_count_comments">%d kommentarer</string>
|
||||||
<string name="activity_count_commentlikes">%d gillade kommentarer</string>
|
<string name="activity_count_commentlikes">%d gillade kommentarer</string>
|
||||||
|
@ -237,7 +237,6 @@
|
|||||||
<string name="action_ayml">Önerilen kullanıcılar</string>
|
<string name="action_ayml">Önerilen kullanıcılar</string>
|
||||||
<string name="select_picture">Resim Seç</string>
|
<string name="select_picture">Resim Seç</string>
|
||||||
<string name="uploading">Yükleniyor…</string>
|
<string name="uploading">Yükleniyor…</string>
|
||||||
<string name="activity_count_prefix">Sahip olduğun:</string>
|
|
||||||
<string name="activity_count_relationship">%d takip</string>
|
<string name="activity_count_relationship">%d takip</string>
|
||||||
<string name="activity_count_comments">%d yorum</string>
|
<string name="activity_count_comments">%d yorum</string>
|
||||||
<string name="activity_count_commentlikes">%d yorum beğenisi</string>
|
<string name="activity_count_commentlikes">%d yorum beğenisi</string>
|
||||||
|
@ -233,7 +233,6 @@
|
|||||||
<string name="action_ayml">Người dùng được đề xuất</string>
|
<string name="action_ayml">Người dùng được đề xuất</string>
|
||||||
<string name="select_picture">Chọn hình ảnh</string>
|
<string name="select_picture">Chọn hình ảnh</string>
|
||||||
<string name="uploading">Đang tải lên…</string>
|
<string name="uploading">Đang tải lên…</string>
|
||||||
<string name="activity_count_prefix">Bạn có:</string>
|
|
||||||
<string name="activity_count_relationship">%d người theo dõi</string>
|
<string name="activity_count_relationship">%d người theo dõi</string>
|
||||||
<string name="activity_count_comments">%d bình luận</string>
|
<string name="activity_count_comments">%d bình luận</string>
|
||||||
<string name="activity_count_commentlikes">%d lượt thích bình luận</string>
|
<string name="activity_count_commentlikes">%d lượt thích bình luận</string>
|
||||||
|
@ -233,7 +233,6 @@
|
|||||||
<string name="action_ayml">推荐用户</string>
|
<string name="action_ayml">推荐用户</string>
|
||||||
<string name="select_picture">选择图片</string>
|
<string name="select_picture">选择图片</string>
|
||||||
<string name="uploading">上传中...</string>
|
<string name="uploading">上传中...</string>
|
||||||
<string name="activity_count_prefix">您有:</string>
|
|
||||||
<string name="activity_count_relationship">%d 位新粉丝</string>
|
<string name="activity_count_relationship">%d 位新粉丝</string>
|
||||||
<string name="activity_count_comments">%d 个评论回复</string>
|
<string name="activity_count_comments">%d 个评论回复</string>
|
||||||
<string name="activity_count_commentlikes">%d 个评论点赞</string>
|
<string name="activity_count_commentlikes">%d 个评论点赞</string>
|
||||||
|
@ -233,7 +233,6 @@
|
|||||||
<string name="action_ayml">推薦用戶</string>
|
<string name="action_ayml">推薦用戶</string>
|
||||||
<string name="select_picture">選擇圖片</string>
|
<string name="select_picture">選擇圖片</string>
|
||||||
<string name="uploading">上傳中…</string>
|
<string name="uploading">上傳中…</string>
|
||||||
<string name="activity_count_prefix">您有</string>
|
|
||||||
<string name="activity_count_relationship">%d 個追蹤者</string>
|
<string name="activity_count_relationship">%d 個追蹤者</string>
|
||||||
<string name="activity_count_comments">%d 個評論</string>
|
<string name="activity_count_comments">%d 個評論</string>
|
||||||
<string name="activity_count_commentlikes">%d 個評論的讚</string>
|
<string name="activity_count_commentlikes">%d 個評論的讚</string>
|
||||||
|
@ -242,7 +242,10 @@
|
|||||||
<string name="liability" translatable="false">This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.</string>
|
<string name="liability" translatable="false">This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.</string>
|
||||||
<string name="select_picture">Select Picture</string>
|
<string name="select_picture">Select Picture</string>
|
||||||
<string name="uploading">Uploading…</string>
|
<string name="uploading">Uploading…</string>
|
||||||
<string name="activity_count_prefix">You have:</string>
|
<plurals name="activity_count_total">
|
||||||
|
<item quantity="one">You have %d notification</item>
|
||||||
|
<item quantity="other">You have %d notifications</item>
|
||||||
|
</plurals>
|
||||||
<string name="activity_count_relationship">%d follows</string>
|
<string name="activity_count_relationship">%d follows</string>
|
||||||
<string name="activity_count_comments">%d comments</string>
|
<string name="activity_count_comments">%d comments</string>
|
||||||
<string name="activity_count_commentlikes">%d comment likes</string>
|
<string name="activity_count_commentlikes">%d comment likes</string>
|
||||||
@ -486,7 +489,7 @@
|
|||||||
<string name="crash_report_subject">Barinsta Crash Report</string>
|
<string name="crash_report_subject">Barinsta Crash Report</string>
|
||||||
<string name="crash_report_title">Select an email app to send crash logs</string>
|
<string name="crash_report_title">Select an email app to send crash logs</string>
|
||||||
<string name="not_found">Not found!</string>
|
<string name="not_found">Not found!</string>
|
||||||
<string name="rate_limit">Your IP has been rate limited by Instagram. Wait for an hour and try again. <a href=\"https://redd.it/msxlko\">Learn more.</a></string>
|
<string name="rate_limit">Your IP has been rate limited by Instagram. <a href=\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\">Learn more.</a></string>
|
||||||
<string name="skip_update">Skip this update</string>
|
<string name="skip_update">Skip this update</string>
|
||||||
<string name="on_latest_version">You\'re already on the latest version</string>
|
<string name="on_latest_version">You\'re already on the latest version</string>
|
||||||
<string name="tab_order">Screen order</string>
|
<string name="tab_order">Screen order</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user