mirror of
https://github.com/KokaKiwi/BarInsta
synced 2024-11-22 14:47:29 +00:00
redo follower/ing viewer
Follower/ing viewer now uses the i endpoint (which returns more users at once) as well as caching (less requests needed) so its response time has decreased significantly (to 1/3 in my case)
This commit is contained in:
parent
519d46858b
commit
bf24f56843
@ -1,111 +0,0 @@
|
|||||||
// package awais.instagrabber.adapters;
|
|
||||||
//
|
|
||||||
// import android.content.Context;
|
|
||||||
// import android.view.LayoutInflater;
|
|
||||||
// import android.view.View;
|
|
||||||
// import android.view.ViewGroup;
|
|
||||||
//
|
|
||||||
// import androidx.annotation.NonNull;
|
|
||||||
// import androidx.recyclerview.widget.DiffUtil;
|
|
||||||
// import androidx.recyclerview.widget.ListAdapter;
|
|
||||||
//
|
|
||||||
// import awais.instagrabber.adapters.viewholder.feed.FeedItemViewHolder;
|
|
||||||
// import awais.instagrabber.adapters.viewholder.feed.FeedPhotoViewHolder;
|
|
||||||
// import awais.instagrabber.adapters.viewholder.feed.FeedSliderViewHolder;
|
|
||||||
// import awais.instagrabber.adapters.viewholder.feed.FeedVideoViewHolder;
|
|
||||||
// import awais.instagrabber.customviews.RamboTextView;
|
|
||||||
// import awais.instagrabber.databinding.ItemFeedPhotoBinding;
|
|
||||||
// import awais.instagrabber.databinding.ItemFeedSliderBinding;
|
|
||||||
// import awais.instagrabber.databinding.ItemFeedVideoBinding;
|
|
||||||
// import awais.instagrabber.interfaces.MentionClickListener;
|
|
||||||
// import awais.instagrabber.models.FeedModel;
|
|
||||||
// import awais.instagrabber.models.enums.MediaItemType;
|
|
||||||
// import awais.instagrabber.utils.Utils;
|
|
||||||
//
|
|
||||||
// public final class FeedAdapter extends ListAdapter<FeedModel, FeedItemViewHolder> {
|
|
||||||
// private static final String TAG = "FeedAdapter";
|
|
||||||
// private final View.OnClickListener clickListener;
|
|
||||||
// private final MentionClickListener mentionClickListener;
|
|
||||||
// private final View.OnLongClickListener longClickListener = v -> {
|
|
||||||
// final Object tag;
|
|
||||||
// if (v instanceof RamboTextView && (tag = v.getTag()) instanceof FeedModel)
|
|
||||||
// Utils.copyText(v.getContext(), ((FeedModel) tag).getPostCaption());
|
|
||||||
// return true;
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// private static final DiffUtil.ItemCallback<FeedModel> diffCallback = new DiffUtil.ItemCallback<FeedModel>() {
|
|
||||||
// @Override
|
|
||||||
// public boolean areItemsTheSame(@NonNull final FeedModel oldItem, @NonNull final FeedModel newItem) {
|
|
||||||
// return oldItem.getPostId().equals(newItem.getPostId());
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public boolean areContentsTheSame(@NonNull final FeedModel oldItem, @NonNull final FeedModel newItem) {
|
|
||||||
// return oldItem.getPostId().equals(newItem.getPostId());
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// public FeedAdapter(final View.OnClickListener clickListener,
|
|
||||||
// final MentionClickListener mentionClickListener) {
|
|
||||||
// super(diffCallback);
|
|
||||||
// // private final static String ellipsize = "… more";
|
|
||||||
// this.clickListener = clickListener;
|
|
||||||
// this.mentionClickListener = mentionClickListener;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @NonNull
|
|
||||||
// @Override
|
|
||||||
// public FeedItemViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
|
|
||||||
// final Context context = parent.getContext();
|
|
||||||
// final LayoutInflater layoutInflater = LayoutInflater.from(context);
|
|
||||||
// final MediaItemType type = MediaItemType.valueOf(viewType);
|
|
||||||
// switch (type) {
|
|
||||||
// case MEDIA_TYPE_VIDEO: {
|
|
||||||
// final ItemFeedVideoBinding binding = ItemFeedVideoBinding.inflate(layoutInflater, parent, false);
|
|
||||||
// return new FeedVideoViewHolder(binding, mentionClickListener, clickListener, longClickListener);
|
|
||||||
// }
|
|
||||||
// case MEDIA_TYPE_SLIDER: {
|
|
||||||
// final ItemFeedSliderBinding binding = ItemFeedSliderBinding.inflate(layoutInflater, parent, false);
|
|
||||||
// return new FeedSliderViewHolder(binding, mentionClickListener, clickListener, longClickListener);
|
|
||||||
// }
|
|
||||||
// case MEDIA_TYPE_IMAGE:
|
|
||||||
// default: {
|
|
||||||
// final ItemFeedPhotoBinding binding = ItemFeedPhotoBinding.inflate(layoutInflater, parent, false);
|
|
||||||
// return new FeedPhotoViewHolder(binding, mentionClickListener, clickListener, longClickListener);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public void onBindViewHolder(@NonNull final FeedItemViewHolder viewHolder, final int position) {
|
|
||||||
// final FeedModel feedModel = getItem(position);
|
|
||||||
// if (feedModel == null) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// feedModel.setPosition(position);
|
|
||||||
// viewHolder.bind(feedModel, (feedModel1, view, postImage) -> {});
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public int getItemViewType(final int position) {
|
|
||||||
// return getItem(position).getItemType().getId();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public void onViewAttachedToWindow(@NonNull final FeedItemViewHolder holder) {
|
|
||||||
// super.onViewAttachedToWindow(holder);
|
|
||||||
// // Log.d(TAG, "attached holder: " + holder);
|
|
||||||
// if (!(holder instanceof FeedSliderViewHolder)) return;
|
|
||||||
// final FeedSliderViewHolder feedSliderViewHolder = (FeedSliderViewHolder) holder;
|
|
||||||
// feedSliderViewHolder.startPlayingVideo();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public void onViewDetachedFromWindow(@NonNull final FeedItemViewHolder holder) {
|
|
||||||
// super.onViewDetachedFromWindow(holder);
|
|
||||||
// // Log.d(TAG, "detached holder: " + holder);
|
|
||||||
// if (!(holder instanceof FeedSliderViewHolder)) return;
|
|
||||||
// final FeedSliderViewHolder feedSliderViewHolder = (FeedSliderViewHolder) holder;
|
|
||||||
// feedSliderViewHolder.stopPlayingVideo();
|
|
||||||
// }
|
|
||||||
// }
|
|
@ -1,101 +0,0 @@
|
|||||||
package awais.instagrabber.asyncs;
|
|
||||||
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import org.json.JSONArray;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.URL;
|
|
||||||
|
|
||||||
import awais.instagrabber.BuildConfig;
|
|
||||||
import awais.instagrabber.interfaces.FetchListener;
|
|
||||||
import awais.instagrabber.models.FollowModel;
|
|
||||||
import awais.instagrabber.utils.Constants;
|
|
||||||
import awais.instagrabber.utils.NetworkUtils;
|
|
||||||
import awaisomereport.LogCollector;
|
|
||||||
|
|
||||||
import static awais.instagrabber.utils.Utils.logCollector;
|
|
||||||
|
|
||||||
public final class FollowFetcher extends AsyncTask<Void, Void, FollowModel[]> {
|
|
||||||
private final String endCursor, id;
|
|
||||||
private final boolean isFollowers;
|
|
||||||
private final FetchListener<FollowModel[]> fetchListener;
|
|
||||||
|
|
||||||
public FollowFetcher(final String id, final boolean isFollowers, final FetchListener<FollowModel[]> fetchListener) {
|
|
||||||
this.id = id;
|
|
||||||
this.endCursor = "";
|
|
||||||
this.isFollowers = isFollowers;
|
|
||||||
this.fetchListener = fetchListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FollowFetcher(final String id, final boolean isFollowers, final String endCursor, final FetchListener<FollowModel[]> fetchListener) {
|
|
||||||
this.id = id;
|
|
||||||
this.endCursor = endCursor == null ? "" : endCursor;
|
|
||||||
this.isFollowers = isFollowers;
|
|
||||||
this.fetchListener = fetchListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPreExecute() {
|
|
||||||
if (fetchListener != null) fetchListener.doBefore();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected FollowModel[] doInBackground(final Void... voids) {
|
|
||||||
FollowModel[] result = null;
|
|
||||||
final String url = "https://www.instagram.com/graphql/query/?query_id=" + (isFollowers ? "17851374694183129" : "17874545323001329")
|
|
||||||
+ "&id=" + id + "&first=50&after=" + endCursor;
|
|
||||||
|
|
||||||
try {
|
|
||||||
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
|
|
||||||
conn.setInstanceFollowRedirects(false);
|
|
||||||
conn.setUseCaches(false);
|
|
||||||
conn.connect();
|
|
||||||
|
|
||||||
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
|
|
||||||
final JSONObject data = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("data")
|
|
||||||
.getJSONObject(Constants.EXTRAS_USER).getJSONObject(isFollowers ? "edge_followed_by" : "edge_follow");
|
|
||||||
|
|
||||||
final String endCursor;
|
|
||||||
final boolean hasNextPage;
|
|
||||||
|
|
||||||
final JSONObject pageInfo = data.getJSONObject("page_info");
|
|
||||||
if (pageInfo.has("has_next_page")) {
|
|
||||||
hasNextPage = pageInfo.getBoolean("has_next_page");
|
|
||||||
endCursor = hasNextPage ? pageInfo.getString("end_cursor") : null;
|
|
||||||
} else {
|
|
||||||
hasNextPage = false;
|
|
||||||
endCursor = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final JSONArray edges = data.getJSONArray("edges");
|
|
||||||
final FollowModel[] models = new FollowModel[edges.length()];
|
|
||||||
for (int i = 0; i < models.length; ++i) {
|
|
||||||
final JSONObject followNode = edges.getJSONObject(i).getJSONObject("node");
|
|
||||||
models[i] = new FollowModel(followNode.getString(Constants.EXTRAS_ID), followNode.getString(Constants.EXTRAS_USERNAME),
|
|
||||||
followNode.getString("full_name"), followNode.getString("profile_pic_url"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (models[models.length - 1] != null)
|
|
||||||
models[models.length - 1].setPageCursor(hasNextPage, endCursor);
|
|
||||||
|
|
||||||
result = models;
|
|
||||||
}
|
|
||||||
|
|
||||||
conn.disconnect();
|
|
||||||
} catch (final Exception e) {
|
|
||||||
if (logCollector != null)
|
|
||||||
logCollector.appendException(e, LogCollector.LogFile.ASYNC_FOLLOW_FETCHER, "doInBackground");
|
|
||||||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(final FollowModel[] result) {
|
|
||||||
if (fetchListener != null) fetchListener.onResult(result);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,6 @@
|
|||||||
package awais.instagrabber.fragments;
|
package awais.instagrabber.fragments;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@ -10,6 +11,7 @@ import android.view.MenuInflater;
|
|||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@ -26,15 +28,20 @@ import java.util.Arrays;
|
|||||||
import awais.instagrabber.BuildConfig;
|
import awais.instagrabber.BuildConfig;
|
||||||
import awais.instagrabber.R;
|
import awais.instagrabber.R;
|
||||||
import awais.instagrabber.adapters.FollowAdapter;
|
import awais.instagrabber.adapters.FollowAdapter;
|
||||||
import awais.instagrabber.asyncs.FollowFetcher;
|
|
||||||
import awais.instagrabber.databinding.FragmentFollowersViewerBinding;
|
import awais.instagrabber.databinding.FragmentFollowersViewerBinding;
|
||||||
import awais.instagrabber.interfaces.FetchListener;
|
|
||||||
import awais.instagrabber.models.FollowModel;
|
import awais.instagrabber.models.FollowModel;
|
||||||
|
import awais.instagrabber.repositories.responses.FriendshipRepoChangeRootResponse;
|
||||||
|
import awais.instagrabber.repositories.responses.FriendshipRepoListFetchResponse;
|
||||||
|
import awais.instagrabber.utils.Constants;
|
||||||
|
import awais.instagrabber.utils.CookieUtils;
|
||||||
import awais.instagrabber.utils.TextUtils;
|
import awais.instagrabber.utils.TextUtils;
|
||||||
|
import awais.instagrabber.webservices.FriendshipService;
|
||||||
|
import awais.instagrabber.webservices.ServiceCallback;
|
||||||
import awaisomereport.LogCollector;
|
import awaisomereport.LogCollector;
|
||||||
import thoughtbot.expandableadapter.ExpandableGroup;
|
import thoughtbot.expandableadapter.ExpandableGroup;
|
||||||
|
|
||||||
import static awais.instagrabber.utils.Utils.logCollector;
|
import static awais.instagrabber.utils.Utils.logCollector;
|
||||||
|
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||||
|
|
||||||
public final class FollowViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
public final class FollowViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
||||||
private static final String TAG = "FollowViewerFragment";
|
private static final String TAG = "FollowViewerFragment";
|
||||||
@ -44,7 +51,7 @@ public final class FollowViewerFragment extends Fragment implements SwipeRefresh
|
|||||||
private final ArrayList<FollowModel> followersModels = new ArrayList<>();
|
private final ArrayList<FollowModel> followersModels = new ArrayList<>();
|
||||||
private final ArrayList<FollowModel> allFollowing = new ArrayList<>();
|
private final ArrayList<FollowModel> allFollowing = new ArrayList<>();
|
||||||
|
|
||||||
private boolean isFollowersList, isCompare = false;
|
private boolean isFollowersList, isCompare = false, loading = false;
|
||||||
private String profileId, username, namePost, type;
|
private String profileId, username, namePost, type;
|
||||||
private Resources resources;
|
private Resources resources;
|
||||||
private FollowModel model;
|
private FollowModel model;
|
||||||
@ -53,12 +60,14 @@ public final class FollowViewerFragment extends Fragment implements SwipeRefresh
|
|||||||
private FragmentFollowersViewerBinding binding;
|
private FragmentFollowersViewerBinding binding;
|
||||||
private AsyncTask<Void, Void, FollowModel[]> currentlyExecuting;
|
private AsyncTask<Void, Void, FollowModel[]> currentlyExecuting;
|
||||||
private SwipeRefreshLayout root;
|
private SwipeRefreshLayout root;
|
||||||
|
private FriendshipService friendshipService;
|
||||||
private boolean shouldRefresh = true;
|
private boolean shouldRefresh = true;
|
||||||
private AppCompatActivity fragmentActivity;
|
private AppCompatActivity fragmentActivity;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
friendshipService = FriendshipService.getInstance();
|
||||||
fragmentActivity = (AppCompatActivity) getActivity();
|
fragmentActivity = (AppCompatActivity) getActivity();
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
}
|
}
|
||||||
@ -135,58 +144,99 @@ public final class FollowViewerFragment extends Fragment implements SwipeRefresh
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void listFollows() {
|
private void listFollows() {
|
||||||
stopCurrentExecutor();
|
loading = true;
|
||||||
type = resources.getString(isFollowersList ? R.string.followers_type_followers : R.string.followers_type_following);
|
type = resources.getString(isFollowersList ? R.string.followers_type_followers : R.string.followers_type_following);
|
||||||
setSubtitle(type);
|
setSubtitle(type);
|
||||||
followModels.clear();
|
followModels.clear();
|
||||||
final FetchListener<FollowModel[]> fetchListener = new FetchListener<FollowModel[]>() {
|
final ServiceCallback<FriendshipRepoListFetchResponse> cb = new ServiceCallback<FriendshipRepoListFetchResponse>() {
|
||||||
@Override
|
@Override
|
||||||
public void doBefore() {
|
public void onSuccess(final FriendshipRepoListFetchResponse result) {
|
||||||
binding.swipeRefreshLayout.setRefreshing(true);
|
if (result == null) {
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResult(final FollowModel[] result) {
|
|
||||||
if (result == null) binding.swipeRefreshLayout.setRefreshing(false);
|
|
||||||
else {
|
|
||||||
followModels.addAll(Arrays.asList(result));
|
|
||||||
|
|
||||||
final FollowModel model = result[result.length - 1];
|
|
||||||
if (model != null && model.hasNextPage()) {
|
|
||||||
stopCurrentExecutor();
|
|
||||||
currentlyExecuting = new FollowFetcher(profileId, isFollowersList, model.getEndCursor(), this)
|
|
||||||
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
model.setPageCursor(false, null);
|
|
||||||
} else {
|
|
||||||
binding.swipeRefreshLayout.setRefreshing(false);
|
binding.swipeRefreshLayout.setRefreshing(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
followModels.addAll(result.getItems());
|
||||||
|
if (result.isMoreAvailable()) {
|
||||||
|
friendshipService.getList(isFollowersList, profileId, result.getNextMaxId(), this);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
binding.swipeRefreshLayout.setRefreshing(false);
|
||||||
|
if (isFollowersList) followersModels.addAll(followModels);
|
||||||
|
else followingModels.addAll(followModels);
|
||||||
refreshAdapter(followModels, null, null, null);
|
refreshAdapter(followModels, null, null, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(final Throwable t) {
|
||||||
|
binding.swipeRefreshLayout.setRefreshing(false);
|
||||||
|
Log.e(TAG, "Error fetching list (single)", t);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
currentlyExecuting = new FollowFetcher(profileId, isFollowersList, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
binding.swipeRefreshLayout.setRefreshing(true);
|
||||||
|
friendshipService.getList(isFollowersList, profileId, null, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void listCompare() {
|
private void listCompare() {
|
||||||
stopCurrentExecutor();
|
loading = true;
|
||||||
setSubtitle(R.string.followers_compare);
|
setSubtitle(R.string.followers_compare);
|
||||||
allFollowing.clear();
|
allFollowing.clear();
|
||||||
followersModels.clear();
|
followersModels.clear();
|
||||||
followingModels.clear();
|
followingModels.clear();
|
||||||
final FetchListener<FollowModel[]> followingFetchListener = new FetchListener<FollowModel[]>() {
|
final ServiceCallback<FriendshipRepoListFetchResponse> followingFetchCb = new ServiceCallback<FriendshipRepoListFetchResponse>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResult(final FollowModel[] result) {
|
public void onSuccess(final FriendshipRepoListFetchResponse result) {
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
followingModels.addAll(Arrays.asList(result));
|
followingModels.addAll(result.getItems());
|
||||||
|
|
||||||
final FollowModel model = result[result.length - 1];
|
if (result.isMoreAvailable()) {
|
||||||
if (model != null && model.hasNextPage()) {
|
friendshipService.getList(false, profileId, result.getNextMaxId(), this);
|
||||||
stopCurrentExecutor();
|
|
||||||
currentlyExecuting = new FollowFetcher(profileId, false, model.getEndCursor(), this)
|
|
||||||
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
model.setPageCursor(false, null);
|
|
||||||
} else {
|
} else {
|
||||||
|
showCompare();
|
||||||
|
}
|
||||||
|
} else binding.swipeRefreshLayout.setRefreshing(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(final Throwable t) {
|
||||||
|
binding.swipeRefreshLayout.setRefreshing(false);
|
||||||
|
Log.e(TAG, "Error fetching list (double, following)", t);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
final ServiceCallback<FriendshipRepoListFetchResponse> followersFetchCb = new ServiceCallback<FriendshipRepoListFetchResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final FriendshipRepoListFetchResponse result) {
|
||||||
|
if (result != null) {
|
||||||
|
followersModels.addAll(result.getItems());
|
||||||
|
if (result.isMoreAvailable()) {
|
||||||
|
friendshipService.getList(true, profileId, result.getNextMaxId(), this);
|
||||||
|
} else if (followingModels.size() == 0) {
|
||||||
|
friendshipService.getList(false, profileId, null, followingFetchCb);
|
||||||
|
} else {
|
||||||
|
showCompare();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(final Throwable t) {
|
||||||
|
binding.swipeRefreshLayout.setRefreshing(false);
|
||||||
|
Log.e(TAG, "Error fetching list (double, follower)", t);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
binding.swipeRefreshLayout.setRefreshing(true);
|
||||||
|
if (followersModels.size() == 0) {
|
||||||
|
friendshipService.getList(true, profileId, null, followersFetchCb);
|
||||||
|
}
|
||||||
|
else if (followingModels.size() == 0) {
|
||||||
|
friendshipService.getList(false, profileId, null, followingFetchCb);
|
||||||
|
}
|
||||||
|
else showCompare();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showCompare() {
|
||||||
allFollowing.addAll(followersModels);
|
allFollowing.addAll(followersModels);
|
||||||
allFollowing.retainAll(followingModels);
|
allFollowing.retainAll(followingModels);
|
||||||
|
|
||||||
@ -203,35 +253,6 @@ public final class FollowViewerFragment extends Fragment implements SwipeRefresh
|
|||||||
|
|
||||||
refreshAdapter(null, followingModels, followersModels, allFollowing);
|
refreshAdapter(null, followingModels, followersModels, allFollowing);
|
||||||
}
|
}
|
||||||
} else binding.swipeRefreshLayout.setRefreshing(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
final FetchListener<FollowModel[]> followersFetchListener = new FetchListener<FollowModel[]>() {
|
|
||||||
@Override
|
|
||||||
public void doBefore() {
|
|
||||||
binding.swipeRefreshLayout.setRefreshing(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResult(final FollowModel[] result) {
|
|
||||||
if (result != null) {
|
|
||||||
followersModels.addAll(Arrays.asList(result));
|
|
||||||
final FollowModel model = result[result.length - 1];
|
|
||||||
if (model == null || !model.hasNextPage()) {
|
|
||||||
stopCurrentExecutor();
|
|
||||||
currentlyExecuting = new FollowFetcher(profileId, false, followingFetchListener)
|
|
||||||
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
} else {
|
|
||||||
stopCurrentExecutor();
|
|
||||||
currentlyExecuting = new FollowFetcher(profileId, true, model.getEndCursor(), this)
|
|
||||||
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
model.setPageCursor(false, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
currentlyExecuting = new FollowFetcher(profileId, true, followersFetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(@NonNull final Menu menu, final MenuInflater inflater) {
|
public void onCreateOptionsMenu(@NonNull final Menu menu, final MenuInflater inflater) {
|
||||||
@ -322,9 +343,16 @@ public final class FollowViewerFragment extends Fragment implements SwipeRefresh
|
|||||||
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
|
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
|
||||||
if (item.getItemId() != R.id.action_compare) return super.onOptionsItemSelected(item);
|
if (item.getItemId() != R.id.action_compare) return super.onOptionsItemSelected(item);
|
||||||
binding.rvFollow.setAdapter(null);
|
binding.rvFollow.setAdapter(null);
|
||||||
if (isCompare) listFollows();
|
final Context context = getContext();
|
||||||
else listCompare();
|
if (loading) Toast.makeText(context, R.string.follower_wait_to_load, Toast.LENGTH_LONG).show();
|
||||||
|
else if (isCompare) {
|
||||||
|
listFollows();
|
||||||
isCompare = !isCompare;
|
isCompare = !isCompare;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
listCompare();
|
||||||
|
isCompare = !isCompare;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,6 +360,7 @@ public final class FollowViewerFragment extends Fragment implements SwipeRefresh
|
|||||||
final ArrayList<FollowModel> followingModels,
|
final ArrayList<FollowModel> followingModels,
|
||||||
final ArrayList<FollowModel> followersModels,
|
final ArrayList<FollowModel> followersModels,
|
||||||
final ArrayList<FollowModel> allFollowing) {
|
final ArrayList<FollowModel> allFollowing) {
|
||||||
|
loading = false;
|
||||||
final ArrayList<ExpandableGroup> groups = new ArrayList<>(1);
|
final ArrayList<ExpandableGroup> groups = new ArrayList<>(1);
|
||||||
|
|
||||||
if (isCompare) {
|
if (isCompare) {
|
||||||
@ -349,18 +378,4 @@ public final class FollowViewerFragment extends Fragment implements SwipeRefresh
|
|||||||
adapter.toggleGroup(0);
|
adapter.toggleGroup(0);
|
||||||
binding.rvFollow.setAdapter(adapter);
|
binding.rvFollow.setAdapter(adapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stopCurrentExecutor() {
|
|
||||||
if (currentlyExecuting != null) {
|
|
||||||
try {
|
|
||||||
currentlyExecuting.cancel(true);
|
|
||||||
} catch (final Exception e) {
|
|
||||||
if (logCollector != null)
|
|
||||||
logCollector.appendException(e, LogCollector.LogFile.MAIN_HELPER, "stopCurrentExecutor");
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
Log.e(TAG, "", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -612,7 +612,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (!profileModel.isReallyPrivate()) {
|
if (!profileModel.isReallyPrivate() && isLoggedIn) {
|
||||||
binding.mainFollowing.setClickable(true);
|
binding.mainFollowing.setClickable(true);
|
||||||
binding.mainFollowers.setClickable(true);
|
binding.mainFollowers.setClickable(true);
|
||||||
|
|
||||||
|
@ -3,13 +3,16 @@ package awais.instagrabber.repositories;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import awais.instagrabber.repositories.responses.FriendshipRepoChangeRootResponse;
|
import awais.instagrabber.repositories.responses.FriendshipRepoChangeRootResponse;
|
||||||
|
import awais.instagrabber.repositories.responses.FriendshipRepoListFetchResponse;
|
||||||
import awais.instagrabber.repositories.responses.FriendshipRepoRestrictRootResponse;
|
import awais.instagrabber.repositories.responses.FriendshipRepoRestrictRootResponse;
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.http.FieldMap;
|
import retrofit2.http.FieldMap;
|
||||||
import retrofit2.http.FormUrlEncoded;
|
import retrofit2.http.FormUrlEncoded;
|
||||||
|
import retrofit2.http.GET;
|
||||||
import retrofit2.http.Header;
|
import retrofit2.http.Header;
|
||||||
import retrofit2.http.POST;
|
import retrofit2.http.POST;
|
||||||
import retrofit2.http.Path;
|
import retrofit2.http.Path;
|
||||||
|
import retrofit2.http.QueryMap;
|
||||||
|
|
||||||
public interface FriendshipRepository {
|
public interface FriendshipRepository {
|
||||||
|
|
||||||
@ -25,4 +28,10 @@ public interface FriendshipRepository {
|
|||||||
Call<FriendshipRepoRestrictRootResponse> toggleRestrict(@Header("User-Agent") String userAgent,
|
Call<FriendshipRepoRestrictRootResponse> toggleRestrict(@Header("User-Agent") String userAgent,
|
||||||
@Path("action") String action,
|
@Path("action") String action,
|
||||||
@FieldMap Map<String, String> form);
|
@FieldMap Map<String, String> form);
|
||||||
|
|
||||||
|
@GET("/api/v1/friendships/{userId}/{type}/")
|
||||||
|
Call<String> getList(@Header("User-Agent") String userAgent,
|
||||||
|
@Path("userId") String userId,
|
||||||
|
@Path("type") String type, // following or followers
|
||||||
|
@QueryMap(encoded = true) Map<String, String> queryParams);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,79 @@
|
|||||||
|
package awais.instagrabber.repositories.responses;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import awais.instagrabber.models.FollowModel;
|
||||||
|
import awais.instagrabber.utils.TextUtils;
|
||||||
|
|
||||||
|
public class FriendshipRepoListFetchResponse {
|
||||||
|
private String nextMaxId;
|
||||||
|
private String status;
|
||||||
|
private List<FollowModel> items;
|
||||||
|
|
||||||
|
public FriendshipRepoListFetchResponse(final String nextMaxId,
|
||||||
|
final String status,
|
||||||
|
final List<FollowModel> items) {
|
||||||
|
this.nextMaxId = nextMaxId;
|
||||||
|
this.status = status;
|
||||||
|
this.items = items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMoreAvailable() {
|
||||||
|
return !TextUtils.isEmpty(nextMaxId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNextMaxId() {
|
||||||
|
return nextMaxId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FriendshipRepoListFetchResponse setNextMaxId(final String nextMaxId) {
|
||||||
|
this.nextMaxId = nextMaxId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FriendshipRepoListFetchResponse setStatus(final String status) {
|
||||||
|
this.status = status;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<FollowModel> getItems() {
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FriendshipRepoListFetchResponse setItems(final List<FollowModel> items) {
|
||||||
|
this.items = items;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
final FriendshipRepoListFetchResponse that = (FriendshipRepoListFetchResponse) o;
|
||||||
|
return Objects.equals(nextMaxId, that.nextMaxId) &&
|
||||||
|
Objects.equals(status, that.status) &&
|
||||||
|
Objects.equals(items, that.items);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(nextMaxId, status, items);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "FriendshipRepoListFetchResponse{" +
|
||||||
|
"nextMaxId='" + nextMaxId + '\'' +
|
||||||
|
", status='" + status + '\'' +
|
||||||
|
", items=" + items +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,28 @@
|
|||||||
package awais.instagrabber.webservices;
|
package awais.instagrabber.webservices;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import awais.instagrabber.models.FollowModel;
|
||||||
import awais.instagrabber.repositories.FriendshipRepository;
|
import awais.instagrabber.repositories.FriendshipRepository;
|
||||||
import awais.instagrabber.repositories.responses.FriendshipRepoChangeRootResponse;
|
import awais.instagrabber.repositories.responses.FriendshipRepoChangeRootResponse;
|
||||||
|
import awais.instagrabber.repositories.responses.FriendshipRepoListFetchResponse;
|
||||||
import awais.instagrabber.repositories.responses.FriendshipRepoRestrictRootResponse;
|
import awais.instagrabber.repositories.responses.FriendshipRepoRestrictRootResponse;
|
||||||
import awais.instagrabber.utils.Constants;
|
import awais.instagrabber.utils.Constants;
|
||||||
|
import awais.instagrabber.utils.TextUtils;
|
||||||
import awais.instagrabber.utils.Utils;
|
import awais.instagrabber.utils.Utils;
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.Callback;
|
import retrofit2.Callback;
|
||||||
@ -139,4 +152,79 @@ public class FriendshipService extends BaseService {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// log in required
|
||||||
|
public void getList(final boolean follower,
|
||||||
|
final String targetUserId,
|
||||||
|
final String maxId,
|
||||||
|
final ServiceCallback<FriendshipRepoListFetchResponse> callback) {
|
||||||
|
final Map<String, String> queryMap = new HashMap<>();
|
||||||
|
queryMap.put("max_id", maxId == null ? "" : maxId);
|
||||||
|
final Call<String> request = repository.getList(Constants.I_USER_AGENT,
|
||||||
|
targetUserId,
|
||||||
|
follower ? "followers" : "following",
|
||||||
|
queryMap);
|
||||||
|
request.enqueue(new Callback<String>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
||||||
|
try {
|
||||||
|
if (callback == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final String body = response.body();
|
||||||
|
if (TextUtils.isEmpty(body)) {
|
||||||
|
|
||||||
|
callback.onSuccess(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final FriendshipRepoListFetchResponse friendshipListFetchResponse = parseListResponse(body);
|
||||||
|
callback.onSuccess(friendshipListFetchResponse);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
Log.e(TAG, "onResponse", e);
|
||||||
|
callback.onFailure(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onFailure(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private FriendshipRepoListFetchResponse parseListResponse(@NonNull final String body) throws JSONException {
|
||||||
|
final JSONObject root = new JSONObject(body);
|
||||||
|
final String nextMaxId = root.optString("next_max_id");
|
||||||
|
final String status = root.optString("status");
|
||||||
|
final JSONArray itemsJson = root.optJSONArray("users");
|
||||||
|
final List<FollowModel> items = parseItems(itemsJson);
|
||||||
|
return new FriendshipRepoListFetchResponse(
|
||||||
|
nextMaxId,
|
||||||
|
status,
|
||||||
|
items
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<FollowModel> parseItems(final JSONArray items) throws JSONException {
|
||||||
|
if (items == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
final List<FollowModel> followModels = new ArrayList<>();
|
||||||
|
for (int i = 0; i < items.length(); i++) {
|
||||||
|
final JSONObject itemJson = items.optJSONObject(i);
|
||||||
|
if (itemJson == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final FollowModel followModel = new FollowModel(itemJson.getString("pk"),
|
||||||
|
itemJson.getString("username"),
|
||||||
|
itemJson.optString("full_name"),
|
||||||
|
itemJson.getString("profile_pic_url"));
|
||||||
|
if (followModel != null) {
|
||||||
|
followModels.add(followModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return followModels;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -328,6 +328,7 @@
|
|||||||
<string name="corners">Corners</string>
|
<string name="corners">Corners</string>
|
||||||
<string name="show_grid_gap">Show grid gap</string>
|
<string name="show_grid_gap">Show grid gap</string>
|
||||||
<string name="disable_animation">Disable animation</string>
|
<string name="disable_animation">Disable animation</string>
|
||||||
|
<string name="follower_wait_to_load">Please wait for the current task to complete first!</string>
|
||||||
<plurals name="likes_count">
|
<plurals name="likes_count">
|
||||||
<item quantity="one">%d like</item>
|
<item quantity="one">%d like</item>
|
||||||
<item quantity="other">%d likes</item>
|
<item quantity="other">%d likes</item>
|
||||||
|
Loading…
Reference in New Issue
Block a user