Browse Source

comments viewer improvement

1. Viewing parent comments is now paginated, no more long waits
2. Liking a comment will no longer refresh the entire comment list
3. The pink tint on liked comments from v18 is restored

also removed FeedStoriesFetcher which was deprecated by c24fd01
renovate/org.robolectric-robolectric-4.x
Austin Huang 4 years ago
parent
commit
3899b9adfa
No known key found for this signature in database GPG Key ID: 84C23AA04587A91F
  1. 16
      app/src/main/java/awais/instagrabber/adapters/CommentsAdapter.java
  2. 3
      app/src/main/java/awais/instagrabber/adapters/viewholder/comments/ChildCommentViewHolder.java
  3. 3
      app/src/main/java/awais/instagrabber/adapters/viewholder/comments/ParentCommentViewHolder.java
  4. 32
      app/src/main/java/awais/instagrabber/asyncs/CommentsFetcher.java
  5. 93
      app/src/main/java/awais/instagrabber/asyncs/FeedStoriesFetcher.java
  6. 70
      app/src/main/java/awais/instagrabber/fragments/CommentsViewerFragment.java
  7. 10
      app/src/main/java/awais/instagrabber/models/CommentModel.java
  8. 1
      app/src/main/res/values/color.xml

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

@ -83,8 +83,8 @@ public final class CommentsAdapter extends ListAdapter<CommentModel, RecyclerVie
}
};
private final CommentCallback commentCallback;
private CommentModel selected;
private int selectedIndex;
private CommentModel selected, toChangeLike;
private int selectedIndex, likedIndex;
public CommentsAdapter(final CommentCallback commentCallback) {
super(DIFF_CALLBACK);
@ -106,10 +106,12 @@ public final class CommentsAdapter extends ListAdapter<CommentModel, RecyclerVie
@Override
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {
final CommentModel commentModel = getItem(position);
CommentModel commentModel = getItem(position);
if (commentModel == null) return;
final int type = getItemViewType(position);
final boolean selected = this.selected != null && this.selected.getId().equals(commentModel.getId());
final boolean toLike = this.toChangeLike != null && this.toChangeLike.getId().equals(commentModel.getId());
if (toLike) commentModel = this.toChangeLike;
if (type == TYPE_PARENT) {
final ParentCommentViewHolder viewHolder = (ParentCommentViewHolder) holder;
viewHolder.bind(commentModel, selected, commentCallback);
@ -174,6 +176,14 @@ public final class CommentsAdapter extends ListAdapter<CommentModel, RecyclerVie
notifyItemChanged(selectedIndex);
}
public void setLiked(final CommentModel commentModel, final boolean liked) {
likedIndex = getCurrentList().indexOf(commentModel);
CommentModel newCommentModel = commentModel;
newCommentModel.setLiked(liked);
this.toChangeLike = newCommentModel;
notifyItemChanged(likedIndex);
}
public CommentModel getSelected() {
return selected;
}

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

@ -89,8 +89,7 @@ public final class ChildCommentViewHolder extends RecyclerView.ViewHolder {
public final void setLiked(final boolean liked) {
if (liked) {
// container.setBackgroundColor(0x40FF69B4);
return;
itemView.setBackgroundColor(itemView.getResources().getColor(R.color.comment_liked));
}
}
}

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

@ -89,8 +89,7 @@ public final class ParentCommentViewHolder extends RecyclerView.ViewHolder {
public final void setLiked(final boolean liked) {
if (liked) {
// container.setBackgroundColor(0x40FF69B4);
return;
itemView.setBackgroundColor(itemView.getResources().getColor(R.color.comment_liked));
}
}
}

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

@ -28,11 +28,12 @@ import static awais.instagrabber.utils.Utils.logCollector;
public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentModel>> {
private static final String TAG = "CommentsFetcher";
private final String shortCode;
private final String shortCode, endCursor;
private final FetchListener<List<CommentModel>> fetchListener;
public CommentsFetcher(final String shortCode, final FetchListener<List<CommentModel>> fetchListener) {
public CommentsFetcher(final String shortCode, final String endCursor, final FetchListener<List<CommentModel>> fetchListener) {
this.shortCode = shortCode;
this.endCursor = endCursor;
this.fetchListener = fetchListener;
}
@ -48,6 +49,7 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
https://www.instagram.com/graphql/query/?query_hash=51fdd02b67508306ad4484ff574a0b62&variables={"comment_id":"18100041898085322","first":50,"after":""}
*/
final List<CommentModel> commentModels = getParentComments();
if (commentModels != null) {
for (final CommentModel commentModel : commentModels) {
final List<CommentModel> childCommentModels = commentModel.getChildCommentModels();
if (childCommentModels != null) {
@ -60,6 +62,7 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
}
}
}
}
return commentModels;
}
@ -76,11 +79,10 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
@NonNull
private synchronized List<CommentModel> getChildComments(final String commentId) {
final List<CommentModel> commentModels = new ArrayList<>();
String endCursor = "";
while (endCursor != null) {
String childEndCursor = "";
while (childEndCursor != null) {
final String url = "https://www.instagram.com/graphql/query/?query_hash=51fdd02b67508306ad4484ff574a0b62&variables=" +
"{\"comment_id\":\"" + commentId + "\",\"first\":50,\"after\":\"" + endCursor + "\"}";
"{\"comment_id\":\"" + commentId + "\",\"first\":50,\"after\":\"" + childEndCursor + "\"}";
try {
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setUseCaches(false);
@ -93,8 +95,8 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
.getJSONObject("edge_threaded_comments");
final JSONObject pageInfo = data.getJSONObject("page_info");
endCursor = pageInfo.getString("end_cursor");
if (TextUtils.isEmpty(endCursor)) endCursor = null;
childEndCursor = pageInfo.getString("end_cursor");
if (TextUtils.isEmpty(childEndCursor)) childEndCursor = null;
final JSONArray childComments = data.optJSONArray("edges");
if (childComments != null) {
@ -152,17 +154,14 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
@NonNull
private synchronized List<CommentModel> getParentComments() {
final List<CommentModel> commentModels = new ArrayList<>();
String endCursor = "";
while (endCursor != null) {
final String url = "https://www.instagram.com/graphql/query/?query_hash=bc3296d1ce80a24b1b6e40b1e72903f5&variables=" +
"{\"shortcode\":\"" + shortCode + "\",\"first\":50,\"after\":\"" + endCursor + "\"}";
"{\"shortcode\":\"" + shortCode + "\",\"first\":50,\"after\":\"" + endCursor.replace("\"", "\\\"") + "\"}";
try {
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setUseCaches(false);
conn.connect();
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) break;
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) return null;
else {
final JSONObject parentComments = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("data")
.getJSONObject("shortcode_media")
@ -170,8 +169,7 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
"edge_media_to_parent_comment");
final JSONObject pageInfo = parentComments.getJSONObject("page_info");
endCursor = pageInfo.optString("end_cursor");
if (TextUtils.isEmpty(endCursor)) endCursor = null;
final String foundEndCursor = pageInfo.optString("end_cursor");
// final boolean containsToken = endCursor.contains("bifilter_token");
// if (!Utils.isEmpty(endCursor) && (containsToken || endCursor.contains("cached_comments_cursor"))) {
@ -219,6 +217,7 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
likedBy != null ? likedBy.optLong("count", 0) : 0,
comment.getBoolean("viewer_has_liked"),
profileModel);
commentModel.setPageCursor(!TextUtils.isEmpty(foundEndCursor), TextUtils.isEmpty(foundEndCursor) ? null : foundEndCursor);
JSONObject tempJsonObject;
final JSONArray childCommentsArray;
final int childCommentsLen;
@ -280,8 +279,7 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
logCollector.appendException(e, LogCollector.LogFile.ASYNC_COMMENTS_FETCHER, "getParentComments",
new Pair<>("commentModelsList.size", commentModels.size()));
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
break;
}
return null;
}
return commentModels;
}

93
app/src/main/java/awais/instagrabber/asyncs/FeedStoriesFetcher.java

@ -1,93 +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.FeedStoryModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.NetworkUtils;
import awaisomereport.LogCollector.LogFile;
import static awais.instagrabber.utils.Utils.logCollector;
public final class FeedStoriesFetcher extends AsyncTask<Void, Void, FeedStoryModel[]> {
private final FetchListener<FeedStoryModel[]> fetchListener;
public FeedStoriesFetcher(final FetchListener<FeedStoryModel[]> fetchListener) {
this.fetchListener = fetchListener;
}
@Override
protected FeedStoryModel[] doInBackground(final Void... voids) {
FeedStoryModel[] result = null;
String url = "https://www.instagram.com/graphql/query/?query_hash=b7b84d884400bc5aa7cfe12ae843a091&variables=" +
"{\"only_stories\":true,\"stories_prefetch\":false,\"stories_video_dash_manifest\":false}";
try {
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setInstanceFollowRedirects(false);
conn.setUseCaches(false);
conn.connect();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
final JSONArray feedStoriesReel = new JSONObject(NetworkUtils.readFromConnection(conn))
.getJSONObject("data")
.getJSONObject(Constants.EXTRAS_USER)
.getJSONObject("feed_reels_tray")
.getJSONObject("edge_reels_tray_to_reel")
.getJSONArray("edges");
conn.disconnect();
final int storiesLen = feedStoriesReel.length();
final FeedStoryModel[] feedStoryModels = new FeedStoryModel[storiesLen];
final String[] feedStoryIDs = new String[storiesLen];
for (int i = 0; i < storiesLen; ++i) {
final JSONObject node = feedStoriesReel.getJSONObject(i).getJSONObject("node");
final JSONObject user = node.getJSONObject(node.has("user") ? "user" : "owner");
final ProfileModel profileModel = new ProfileModel(false, false, false,
user.getString("id"),
user.getString("username"),
null, null, null,
user.getString("profile_pic_url"),
null, 0, 0, 0, false, false, false, false);
final String id = node.getString("id");
final boolean fullyRead = !node.isNull("seen") && node.getLong("seen") == node.getLong("latest_reel_media");
feedStoryIDs[i] = id;
feedStoryModels[i] = new FeedStoryModel(id, profileModel, fullyRead);
}
result = feedStoryModels;
}
conn.disconnect();
} catch (final Exception e) {
if (logCollector != null)
logCollector.appendException(e, LogFile.ASYNC_FEED_STORY_FETCHER, "doInBackground");
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
}
return result;
}
@Override
protected void onPreExecute() {
if (fetchListener != null) fetchListener.doBefore();
}
@Override
protected void onPostExecute(final FeedStoryModel[] result) {
if (fetchListener != null) fetchListener.onResult(result);
}
}

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

@ -32,11 +32,18 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.R;
import awais.instagrabber.adapters.CommentsAdapter;
import awais.instagrabber.asyncs.CommentsFetcher;
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
import awais.instagrabber.databinding.FragmentCommentsBinding;
import awais.instagrabber.dialogs.ProfilePicDialogFragment;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.CommentModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.utils.Constants;
@ -56,8 +63,9 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
private CommentsAdapter commentsAdapter;
private FragmentCommentsBinding binding;
private String shortCode;
private String userId;
private LinearLayoutManager layoutManager;
private RecyclerLazyLoader lazyLoader;
private String shortCode, userId, endCursor = null;
private Resources resources;
private InputMethodManager imm;
private AppCompatActivity fragmentActivity;
@ -65,8 +73,30 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
private boolean shouldRefresh = true;
private MediaService mediaService;
private String postId;
private AsyncTask<Void, Void, List<CommentModel>> currentlyRunning;
private CommentsViewModel commentsViewModel;
private final FetchListener<List<CommentModel>> fetchListener = new FetchListener<List<CommentModel>>() {
@Override
public void doBefore() {
binding.swipeRefreshLayout.setRefreshing(true);
}
@Override
public void onResult(final List<CommentModel> commentModels) {
endCursor = commentModels.get(0).getEndCursor();
if (commentModels != null && commentModels.size() > 0) {
List<CommentModel> list = commentsViewModel.getList().getValue();
list = list != null ? new LinkedList<>(list) : new LinkedList<>();
// final int oldSize = list != null ? list.size() : 0;
list.addAll(commentModels);
commentsViewModel.getList().postValue(list);
}
binding.swipeRefreshLayout.setRefreshing(false);
stopCurrentExecutor();
}
};
private final CommentsAdapter.CommentCallback commentCallback = new CommentsAdapter.CommentCallback() {
@Override
public void onClick(final CommentModel comment) {
@ -181,11 +211,11 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
@Override
public void onRefresh() {
binding.swipeRefreshLayout.setRefreshing(true);
new CommentsFetcher(shortCode, commentModels -> {
commentsViewModel.getList().postValue(commentModels);
binding.swipeRefreshLayout.setRefreshing(false);
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
endCursor = null;
lazyLoader.resetState();
commentsViewModel.getList().postValue(Collections.emptyList());
stopCurrentExecutor();
currentlyRunning = new CommentsFetcher(shortCode, "", fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private void init() {
@ -198,7 +228,8 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
binding.swipeRefreshLayout.setOnRefreshListener(this);
binding.swipeRefreshLayout.setRefreshing(true);
commentsViewModel = new ViewModelProvider(this).get(CommentsViewModel.class);
binding.rvComments.setLayoutManager(new LinearLayoutManager(getContext()));
layoutManager = new LinearLayoutManager(getContext());
binding.rvComments.setLayoutManager(layoutManager);
commentsAdapter = new CommentsAdapter(commentCallback);
binding.rvComments.setAdapter(commentsAdapter);
commentsViewModel.getList().observe(getViewLifecycleOwner(), commentsAdapter::submitList);
@ -226,6 +257,13 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
});
binding.commentField.setEndIconOnClickListener(newCommentListener);
}
lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
if (!TextUtils.isEmpty(endCursor))
currentlyRunning = new CommentsFetcher(shortCode, endCursor, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
endCursor = null;
});
binding.rvComments.addOnScrollListener(lazyLoader);
stopCurrentExecutor();
onRefresh();
}
@ -301,8 +339,6 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
Utils.copyText(context, "@" + profileModel.getUsername() + ": " + commentModel.getText());
break;
case 3: // reply to comment
// final View focus = binding.rvComments.findViewWithTag(commentModel);
// focus.setBackgroundColor(0x80888888);
commentsAdapter.setSelected(commentModel);
String mention = "@" + profileModel.getUsername() + " ";
binding.commentText.setText(mention);
@ -326,7 +362,7 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
return;
}
onRefresh();
commentsAdapter.setLiked(commentModel, true);
}
@Override
@ -344,7 +380,7 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
return;
}
onRefresh();
commentsAdapter.setLiked(commentModel, false);
}
@Override
@ -389,4 +425,14 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
final NavDirections action = CommentsViewerFragmentDirections.actionGlobalProfileFragment(username);
NavHostFragment.findNavController(this).navigate(action);
}
private void stopCurrentExecutor() {
if (currentlyRunning != null) {
try {
currentlyRunning.cancel(true);
} catch (final Exception e) {
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
}
}
}
}

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

@ -11,11 +11,10 @@ public class CommentModel {
private final ProfileModel profileModel;
private final String id;
private final String text;
private final long likes;
private long likes;
private final long timestamp;
private List<CommentModel> childCommentModels;
private final boolean liked;
private boolean hasNextPage;
private boolean liked, hasNextPage;
private String endCursor;
public CommentModel(final String id,
@ -53,6 +52,11 @@ public class CommentModel {
return liked;
}
public void setLiked(boolean liked) {
this.likes = liked ? likes + 1 : likes - 1;
this.liked = liked;
}
public ProfileModel getProfileModel() {
return profileModel;
}

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

@ -31,6 +31,7 @@
<color name="dm_profile_button_color">#efefef</color>
<color name="comment_selected">#888888</color>
<color name="comment_liked">#40FF69B4</color>
<color name="white">#FFFFFF</color>
<color name="white_a50">#80FFFFFF</color>

Loading…
Cancel
Save