1
0
mirror of https://github.com/KokaKiwi/BarInsta synced 2024-11-26 08:37:29 +00:00

profile viewer improvement

1. restore tagged posts access for anons
2. chip-ify profile viewer, bring it to consistency with tag/loc viewers
3. add a following/er status chip
4. pluralize "post(s)" and "follower(s)"
5. correct favourited string
This commit is contained in:
Austin Huang 2020-12-19 21:38:21 -05:00
parent 8240829fa8
commit 4d6ac5d293
No known key found for this signature in database
GPG Key ID: 84C23AA04587A91F
18 changed files with 301 additions and 296 deletions

View File

@ -122,6 +122,7 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
false,
false,
false,
false,
false);
final JSONObject likedBy = childComment.optJSONObject("edge_liked_by");
@ -207,6 +208,7 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
false,
false,
false,
false,
false);
final JSONObject likedBy = comment.optJSONObject("edge_liked_by");
@ -256,6 +258,7 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
false,
false,
false,
false,
false);
tempJsonObject = childComment.optJSONObject("edge_liked_by");

View File

@ -65,6 +65,7 @@ public final class PostFetcher extends AsyncTask<Void, Void, FeedModel> {
owner.optInt("edge_followed_by"),
-1,
owner.optBoolean("followed_by_viewer"),
false,
owner.optBoolean("restricted_by_viewer"),
owner.optBoolean("blocked_by_viewer"),
owner.optBoolean("requested_by_viewer")

View File

@ -72,6 +72,7 @@ public final class ProfileFetcher extends AsyncTask<Void, Void, ProfileModel> {
user.getJSONObject("edge_followed_by").getLong("count"),
user.getJSONObject("edge_follow").getLong("count"),
user.optBoolean("followed_by_viewer"),
user.optBoolean("follows_viewer"),
user.optBoolean("restricted_by_viewer"),
user.optBoolean("blocked_by_viewer"),
user.optBoolean("requested_by_viewer"));

View File

@ -50,7 +50,7 @@ public class SavedPostFetchService implements PostFetcher.PostFetchService {
profileService.fetchLiked(nextMaxId, callback);
break;
case TAGGED:
profileService.fetchTagged(profileId, nextMaxId, callback);
profileService.fetchTagged(profileId, 30, nextMaxId, callback);
break;
case SAVED:
default:

View File

@ -513,7 +513,9 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
}));
hashtagDetailsBinding.mainHashtagImage.setImageURI(hashtagModel.getSdProfilePic());
final String postCount = String.valueOf(hashtagModel.getPostCount());
final SpannableStringBuilder span = new SpannableStringBuilder(getString(R.string.main_posts_count_inline, postCount));
final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline,
hashtagModel.getPostCount() > 2000000000L ? 2000000000 : hashtagModel.getPostCount().intValue(),
postCount));
span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0);
span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0);
hashtagDetailsBinding.mainTagPostCount.setText(span);

View File

@ -400,8 +400,9 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
// binding.swipeRefreshLayout.setRefreshing(true);
locationDetailsBinding.mainLocationImage.setImageURI(locationModel.getSdProfilePic());
final String postCount = String.valueOf(locationModel.getPostCount());
final SpannableStringBuilder span = new SpannableStringBuilder(getString(R.string.main_posts_count_inline,
postCount));
final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline,
locationModel.getPostCount() > 2000000000L ? 2000000000 : locationModel.getPostCount().intValue(),
postCount));
span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0);
span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0);
locationDetailsBinding.mainLocPostCount.setText(span);

View File

@ -370,10 +370,10 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
}
if (item.getItemId() == R.id.restrict) {
if (!isLoggedIn) return false;
final String action = profileModel.getRestricted() ? "Unrestrict" : "Restrict";
final String action = profileModel.isRestricted() ? "Unrestrict" : "Restrict";
friendshipService.toggleRestrict(
profileModel.getId(),
!profileModel.getRestricted(),
!profileModel.isRestricted(),
CookieUtils.getCsrfTokenFromCookie(cookie),
new ServiceCallback<FriendshipRepoRestrictRootResponse>() {
@Override
@ -392,7 +392,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
if (item.getItemId() == R.id.block) {
final String userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
if (!isLoggedIn) return false;
if (profileModel.getBlocked()) {
if (profileModel.isBlocked()) {
friendshipService.unblock(
userIdFromCookie,
profileModel.getId(),
@ -571,44 +571,85 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
fetchStoryAndHighlights(profileId);
}
setupButtons(profileId, myId);
if (!profileId.equals(myId)) {
profileDetailsBinding.favCb.setVisibility(View.VISIBLE);
favoriteRepository.getFavorite(username.substring(1), FavoriteType.USER, new RepositoryCallback<Favorite>() {
@Override
public void onSuccess(final Favorite result) {
profileDetailsBinding.favCb.setChecked(true);
profileDetailsBinding.favCb.setButtonDrawable(R.drawable.ic_star_check_24);
}
profileDetailsBinding.favChip.setVisibility(View.VISIBLE);
final FavoriteRepository favoriteRepository = FavoriteRepository.getInstance(FavoriteDataSource.getInstance(getContext()));
favoriteRepository.getFavorite(profileModel.getUsername(), FavoriteType.USER, new RepositoryCallback<Favorite>() {
@Override
public void onSuccess(final Favorite result) {
profileDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24);
profileDetailsBinding.favChip.setText(R.string.added_to_favs);
}
@Override
public void onDataNotAvailable() {
profileDetailsBinding.favCb.setChecked(false);
profileDetailsBinding.favCb.setButtonDrawable(R.drawable.ic_outline_star_plus_24);
}
});
} else {
profileDetailsBinding.favCb.setVisibility(View.GONE);
}
@Override
public void onDataNotAvailable() {
profileDetailsBinding.favChip.setChipIconResource(R.drawable.ic_outline_star_plus_24);
profileDetailsBinding.favChip.setText(R.string.add_to_favorites);
}
});
profileDetailsBinding.favChip.setOnClickListener(
v -> favoriteRepository.getFavorite(profileModel.getUsername(), FavoriteType.USER, new RepositoryCallback<Favorite>() {
@Override
public void onSuccess(final Favorite result) {
favoriteRepository.deleteFavorite(profileModel.getUsername(), FavoriteType.USER, new RepositoryCallback<Void>() {
@Override
public void onSuccess(final Void result) {
profileDetailsBinding.favChip.setText(R.string.add_to_favorites);
profileDetailsBinding.favChip.setChipIconResource(R.drawable.ic_outline_star_plus_24);
showSnackbar(getString(R.string.removed_from_favs));
}
@Override
public void onDataNotAvailable() {}
});
}
@Override
public void onDataNotAvailable() {
final String finalUsername = username.startsWith("@") ? username.substring(1) : username;
favoriteRepository.insertOrUpdateFavorite(new Favorite(
-1,
finalUsername,
FavoriteType.USER,
profileModel.getName(),
profileModel.getSdProfilePic(),
new Date()
), new RepositoryCallback<Void>() {
@Override
public void onSuccess(final Void result) {
profileDetailsBinding.favChip.setText(R.string.added_to_favs);
profileDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24);
showSnackbar(getString(R.string.added_to_favs));
}
@Override
public void onDataNotAvailable() {}
});
}
}));
profileDetailsBinding.mainProfileImage.setImageURI(profileModel.getHdProfilePic());
final long followersCount = profileModel.getFollowersCount();
final long followingCount = profileModel.getFollowingCount();
final Long followersCount = profileModel.getFollowersCount();
final Long followingCount = profileModel.getFollowingCount();
final String postCount = String.valueOf(profileModel.getPostCount());
SpannableStringBuilder span = new SpannableStringBuilder(getString(R.string.main_posts_count,
postCount));
SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline,
profileModel.getPostCount() > 2000000000L ? 2000000000 : profileModel.getPostCount().intValue(),
postCount));
span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0);
span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0);
profileDetailsBinding.mainPostCount.setText(span);
profileDetailsBinding.mainPostCount.setVisibility(View.VISIBLE);
final String followersCountStr = String.valueOf(followersCount);
final int followersCountStrLen = followersCountStr.length();
span = new SpannableStringBuilder(getString(R.string.main_posts_followers,
followersCountStr));
span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_followers,
followersCount > 2000000000L ? 2000000000 : followersCount.intValue(),
followersCountStr));
span.setSpan(new RelativeSizeSpan(1.2f), 0, followersCountStrLen, 0);
span.setSpan(new StyleSpan(Typeface.BOLD), 0, followersCountStrLen, 0);
profileDetailsBinding.mainFollowers.setText(span);
profileDetailsBinding.mainFollowers.setVisibility(View.VISIBLE);
final String followingCountStr = String.valueOf(followingCount);
final int followingCountStrLen = followingCountStr.length();
@ -617,6 +658,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
span.setSpan(new RelativeSizeSpan(1.2f), 0, followingCountStrLen, 0);
span.setSpan(new StyleSpan(Typeface.BOLD), 0, followingCountStrLen, 0);
profileDetailsBinding.mainFollowing.setText(span);
profileDetailsBinding.mainFollowing.setVisibility(View.VISIBLE);
profileDetailsBinding.mainFullName.setText(TextUtils.isEmpty(profileModel.getName()) ? profileModel.getUsername()
: profileModel.getName());
@ -701,10 +743,25 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
profileDetailsBinding.btnLiked.setVisibility(View.GONE);
profileDetailsBinding.btnDM.setVisibility(View.VISIBLE); // maybe there is a judgment mechanism?
profileDetailsBinding.btnFollow.setVisibility(View.VISIBLE);
if (profileModel.getFollowing()) {
if (profileModel.isFollowing() || profileModel.isFollower()) {
profileDetailsBinding.mainStatus.setVisibility(View.VISIBLE);
if (!profileModel.isFollowing()) {
profileDetailsBinding.mainStatus.setChipBackgroundColor(getResources().getColorStateList(R.color.blue_800));
profileDetailsBinding.mainStatus.setText(R.string.status_follower);
}
else if (!profileModel.isFollower()) {
profileDetailsBinding.mainStatus.setChipBackgroundColor(getResources().getColorStateList(R.color.deep_orange_800));
profileDetailsBinding.mainStatus.setText(R.string.status_following);
}
else {
profileDetailsBinding.mainStatus.setChipBackgroundColor(getResources().getColorStateList(R.color.green_800));
profileDetailsBinding.mainStatus.setText(R.string.status_mutual);
}
}
if (profileModel.isFollowing()) {
profileDetailsBinding.btnFollow.setText(R.string.unfollow);
profileDetailsBinding.btnFollow.setIconResource(R.drawable.ic_outline_person_add_disabled_24);
} else if (profileModel.getRequested()) {
} else if (profileModel.isRequested()) {
profileDetailsBinding.btnFollow.setText(R.string.cancel);
profileDetailsBinding.btnFollow.setIconResource(R.drawable.ic_outline_person_add_disabled_24);
} else {
@ -713,7 +770,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
}
if (restrictMenuItem != null) {
restrictMenuItem.setVisible(true);
if (profileModel.getRestricted()) {
if (profileModel.isRestricted()) {
restrictMenuItem.setTitle(R.string.unrestrict);
} else {
restrictMenuItem.setTitle(R.string.restrict);
@ -722,7 +779,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
profileDetailsBinding.btnTagged.setVisibility(profileModel.isReallyPrivate() ? View.GONE : View.VISIBLE);
if (blockMenuItem != null) {
blockMenuItem.setVisible(true);
if (profileModel.getBlocked()) {
if (profileModel.isBlocked()) {
blockMenuItem.setTitle(R.string.unblock);
} else {
blockMenuItem.setTitle(R.string.block);
@ -732,7 +789,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
}
if (!profileModel.isReallyPrivate() && restrictMenuItem != null) {
restrictMenuItem.setVisible(true);
if (profileModel.getRestricted()) {
if (profileModel.isRestricted()) {
restrictMenuItem.setTitle(R.string.unrestrict);
} else {
restrictMenuItem.setTitle(R.string.restrict);
@ -771,9 +828,34 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
}
private void setupCommonListeners() {
final Context context = getContext();
final String userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
profileDetailsBinding.btnFollow.setOnClickListener(v -> {
if (profileModel.getFollowing() || profileModel.getRequested()) {
if (profileModel.isFollowing() && profileModel.isPrivate()) {
new AlertDialog.Builder(context)
.setTitle(R.string.priv_acc)
.setMessage(R.string.priv_acc_confirm)
.setPositiveButton(R.string.confirm, (d, w) ->
friendshipService.unfollow(
userIdFromCookie,
profileModel.getId(),
CookieUtils.getCsrfTokenFromCookie(cookie),
new ServiceCallback<FriendshipRepoChangeRootResponse>() {
@Override
public void onSuccess(final FriendshipRepoChangeRootResponse result) {
// Log.d(TAG, "Unfollow success: " + result);
onRefresh();
}
@Override
public void onFailure(final Throwable t) {
Log.e(TAG, "Error unfollowing", t);
}
}))
.setNegativeButton(R.string.cancel, null)
.show();
}
else if (profileModel.isFollowing() || profileModel.isRequested()) {
friendshipService.unfollow(
userIdFromCookie,
profileModel.getId(),
@ -860,66 +942,12 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
}
showProfilePicDialog();
};
final Context context = getContext();
if (context == null) return;
new AlertDialog.Builder(context)
.setItems(options, profileDialogListener)
.setNegativeButton(R.string.cancel, null)
.show();
});
profileDetailsBinding.favCb.setOnCheckedChangeListener((buttonView, isChecked) -> {
// do not do anything if state matches the db, as listener is set before profile details are set
final Context context = getContext();
if (context == null) return;
final String finalUsername = username.startsWith("@") ? username.substring(1) : username;
favoriteRepository.getFavorite(finalUsername, FavoriteType.USER, new RepositoryCallback<Favorite>() {
@Override
public void onSuccess(final Favorite result) {
if (isChecked) return; // already a fav
buttonView.setVisibility(View.GONE);
profileDetailsBinding.favProgress.setVisibility(View.VISIBLE);
favoriteRepository.deleteFavorite(finalUsername, FavoriteType.USER, new RepositoryCallback<Void>() {
@Override
public void onSuccess(final Void result) {
profileDetailsBinding.favCb.setButtonDrawable(R.drawable.ic_outline_star_plus_24);
profileDetailsBinding.favProgress.setVisibility(View.GONE);
profileDetailsBinding.favCb.setVisibility(View.VISIBLE);
showSnackbar(getString(R.string.removed_from_favs));
}
@Override
public void onDataNotAvailable() {}
});
}
@Override
public void onDataNotAvailable() {
if (!isChecked) return; // not in fav already
buttonView.setVisibility(View.GONE);
profileDetailsBinding.favProgress.setVisibility(View.VISIBLE);
final Favorite model = new Favorite(
-1,
finalUsername,
FavoriteType.USER,
profileModel.getName(),
profileModel.getSdProfilePic(),
new Date()
);
favoriteRepository.insertOrUpdateFavorite(model, new RepositoryCallback<Void>() {
@Override
public void onSuccess(final Void result) {
profileDetailsBinding.favCb.setButtonDrawable(R.drawable.ic_star_check_24);
profileDetailsBinding.favProgress.setVisibility(View.GONE);
profileDetailsBinding.favCb.setVisibility(View.VISIBLE);
showSnackbar(getString(R.string.added_to_favs));
}
@Override
public void onDataNotAvailable() {}
});
}
});
});
}
private void showSnackbar(final String message) {

View File

@ -29,7 +29,7 @@ public final class HashtagModel implements Serializable {
return sdProfilePic;
}
public long getPostCount() { return postCount; }
public Long getPostCount() { return postCount; }
public boolean getFollowing() { return following; }
}

View File

@ -59,5 +59,5 @@ public final class LocationModel implements Serializable {
return sdProfilePic;
}
public long getPostCount() { return postCount; }
public Long getPostCount() { return postCount; }
}

View File

@ -3,15 +3,15 @@ package awais.instagrabber.models;
import java.io.Serializable;
public final class ProfileModel implements Serializable {
private final boolean isPrivate, reallyPrivate, isVerified, following, restricted, blocked, requested;
private final boolean isPrivate, reallyPrivate, isVerified, following, follower, restricted, blocked, requested;
private final long postCount, followersCount, followingCount;
private final String id, username, name, biography, url, sdProfilePic, hdProfilePic;
public ProfileModel(final boolean isPrivate, final boolean reallyPrivate,
final boolean isVerified, final String id, final String username, final String name, final String biography,
final String url, final String sdProfilePic, final String hdProfilePic, final long postCount,
final long followersCount, final long followingCount, final boolean following, final boolean restricted,
final boolean blocked, final boolean requested) {
final long followersCount, final long followingCount, final boolean following, final boolean follower,
final boolean restricted, final boolean blocked, final boolean requested) {
this.isPrivate = isPrivate;
this.reallyPrivate = reallyPrivate;
this.isVerified = isVerified;
@ -26,21 +26,22 @@ public final class ProfileModel implements Serializable {
this.followersCount = followersCount;
this.followingCount = followingCount;
this.following = following;
this.follower = follower;
this.restricted = restricted;
this.blocked = blocked;
this.requested = requested;
}
public static ProfileModel getDefaultProfileModel() {
return new ProfileModel(false, false, false, null, null, null, null, null, null, null, 0, 0, 0, false, false, false, false);
return new ProfileModel(false, false, false, null, null, null, null, null, null, null, 0, 0, 0, false, false, false, false, false);
}
public static ProfileModel getDefaultProfileModel(final String userId) {
return new ProfileModel(false, false, false, userId, null, null, null, null, null, null, 0, 0, 0, false, false, false, false);
return new ProfileModel(false, false, false, userId, null, null, null, null, null, null, 0, 0, 0, false, false, false, false, false);
}
public static ProfileModel getDefaultProfileModel(final String userId, final String username) {
return new ProfileModel(false, false, false, userId, username, null, null, null, null, null, 0, 0, 0, false, false, false, false);
return new ProfileModel(false, false, false, userId, username, null, null, null, null, null, 0, 0, 0, false, false, false, false, false);
}
public boolean isPrivate() {
@ -83,31 +84,35 @@ public final class ProfileModel implements Serializable {
return hdProfilePic;
}
public long getPostCount() {
public Long getPostCount() {
return postCount;
}
public long getFollowersCount() {
public Long getFollowersCount() {
return followersCount;
}
public long getFollowingCount() {
public Long getFollowingCount() {
return followingCount;
}
public boolean getFollowing() {
public boolean isFollowing() {
return following;
}
public boolean getRestricted() {
public boolean isFollower() {
return follower;
}
public boolean isRestricted() {
return restricted;
}
public boolean getBlocked() {
public boolean isBlocked() {
return blocked;
}
public boolean getRequested() {
public boolean isRequested() {
return requested;
}
}

View File

@ -20,7 +20,4 @@ public interface ProfileRepository {
@GET("/api/v1/feed/liked/")
Call<String> fetchLiked(@QueryMap Map<String, String> queryParams);
@GET("/api/v1/usertags/{profileId}/feed/")
Call<String> fetchTagged(@Path("profileId") final String profileId, @QueryMap Map<String, String> queryParams);
}

View File

@ -203,7 +203,7 @@ public final class ResponseBodyUtils {
userObj.getString("full_name"),
null, null,
userObj.getString("profile_pic_url"),
null, 0, 0, 0, false, false, false, false);
null, 0, 0, 0, false, false, false, false, false);
}
final MediaItemType mediaType = getMediaItemType(mediaObj.optInt("media_type", -1));
@ -291,7 +291,7 @@ public final class ResponseBodyUtils {
userObject.getString("full_name"),
null, null,
userObject.getString("profile_pic_url"),
null, 0, 0, 0, false, false, false, false);
null, 0, 0, 0, false, false, false, false, false);
}
final ProfileModel[] leftuserModels = new ProfileModel[leftusersLen];
@ -305,7 +305,7 @@ public final class ResponseBodyUtils {
userObject.getString("full_name"),
null, null,
userObject.getString("profile_pic_url"),
null, 0, 0, 0, false, false, false, false);
null, 0, 0, 0, false, false, false, false, false);
}
final Long[] adminIDs = new Long[adminsLen];
@ -470,7 +470,7 @@ public final class ResponseBodyUtils {
profile.getString("full_name"),
null, null,
profile.getString("profile_pic_url"),
null, 0, 0, 0, false, false, false, false);
null, 0, 0, 0, false, false, false, false, false);
}
break;
@ -622,6 +622,7 @@ public final class ResponseBodyUtils {
0,
0,
following,
false,
restricted,
false,
requested);
@ -705,6 +706,7 @@ public final class ResponseBodyUtils {
false,
false,
false,
false,
false);
}
JSONObject tempJsonObject = feedItem.optJSONObject("edge_media_preview_comment");

View File

@ -165,6 +165,7 @@ public class DiscoverService extends BaseService {
false,
false,
false,
false,
false);
}
final String resourceUrl = ResponseBodyUtils.getHighQualityImage(coverMediaJson);

View File

@ -89,7 +89,6 @@ public class ProfileService extends BaseService {
});
}
public void fetchPosts(final ProfileModel profileModel,
final int postsPerPage,
final String cursor,
@ -156,135 +155,18 @@ public class ProfileService extends BaseService {
}
final JSONArray edges = mediaPosts.getJSONArray("edges");
for (int i = 0; i < edges.length(); ++i) {
final JSONObject mediaNode = edges.getJSONObject(i).getJSONObject("node");
final String mediaType = mediaNode.optString("__typename");
if (mediaType.isEmpty() || "GraphSuggestedUserFeedUnit".equals(mediaType))
final JSONObject itemJson = edges.optJSONObject(i);
if (itemJson == null) {
continue;
final boolean isVideo = mediaNode.getBoolean("is_video");
final long videoViews = mediaNode.optLong("video_view_count", 0);
final String displayUrl = mediaNode.optString("display_url");
if (TextUtils.isEmpty(displayUrl)) continue;
final String resourceUrl;
if (isVideo) {
resourceUrl = mediaNode.getString("video_url");
} else {
resourceUrl = mediaNode.has("display_resources") ? ResponseBodyUtils.getHighQualityImage(mediaNode) : displayUrl;
}
JSONObject tempJsonObject = mediaNode.optJSONObject("edge_media_to_comment");
final long commentsCount = tempJsonObject != null ? tempJsonObject.optLong("count") : 0;
tempJsonObject = mediaNode.optJSONObject("edge_media_preview_like");
final long likesCount = tempJsonObject != null ? tempJsonObject.optLong("count") : 0;
tempJsonObject = mediaNode.optJSONObject("edge_media_to_caption");
final JSONArray captions = tempJsonObject != null ? tempJsonObject.getJSONArray("edges") : null;
String captionText = null;
if (captions != null && captions.length() > 0) {
if ((tempJsonObject = captions.optJSONObject(0)) != null &&
(tempJsonObject = tempJsonObject.optJSONObject("node")) != null) {
captionText = tempJsonObject.getString("text");
}
final FeedModel feedModel = ResponseBodyUtils.parseGraphQLItem(itemJson);
if (feedModel != null) {
feedModels.add(feedModel);
}
final JSONObject location = mediaNode.optJSONObject("location");
// Log.d(TAG, "location: " + (location == null ? null : location.toString()));
String locationId = null;
String locationName = null;
if (location != null) {
locationName = location.optString("name");
if (location.has("id")) {
locationId = location.getString("id");
} else if (location.has("pk")) {
locationId = location.getString("pk");
}
// Log.d(TAG, "locationId: " + locationId);
}
int height = 0;
int width = 0;
final JSONObject dimensions = mediaNode.optJSONObject("dimensions");
if (dimensions != null) {
height = dimensions.optInt("height");
width = dimensions.optInt("width");
}
String thumbnailUrl = null;
try {
thumbnailUrl = mediaNode.getJSONArray("display_resources")
.getJSONObject(0)
.getString("src");
} catch (JSONException ignored) {}
final FeedModel.Builder builder = new FeedModel.Builder()
.setProfileModel(profileModel)
.setItemType(isVideo ? MediaItemType.MEDIA_TYPE_VIDEO
: MediaItemType.MEDIA_TYPE_IMAGE)
.setViewCount(videoViews)
.setPostId(mediaNode.getString(Constants.EXTRAS_ID))
.setDisplayUrl(resourceUrl)
.setThumbnailUrl(thumbnailUrl != null ? thumbnailUrl : displayUrl)
.setShortCode(mediaNode.getString(Constants.EXTRAS_SHORTCODE))
.setPostCaption(captionText)
.setCommentsCount(commentsCount)
.setTimestamp(mediaNode.optLong("taken_at_timestamp", -1))
.setLiked(mediaNode.getBoolean("viewer_has_liked"))
.setBookmarked(mediaNode.getBoolean("viewer_has_saved"))
.setLikesCount(likesCount)
.setLocationName(locationName)
.setLocationId(locationId)
.setImageHeight(height)
.setImageWidth(width);
final boolean isSlider = "GraphSidecar".equals(mediaType) && mediaNode.has("edge_sidecar_to_children");
if (isSlider) {
builder.setItemType(MediaItemType.MEDIA_TYPE_SLIDER);
final JSONObject sidecar = mediaNode.optJSONObject("edge_sidecar_to_children");
if (sidecar != null) {
final JSONArray children = sidecar.optJSONArray("edges");
if (children != null) {
final List<PostChild> sliderItems = getSliderItems(children);
builder.setSliderItems(sliderItems);
}
}
}
final FeedModel feedModel = builder.build();
feedModels.add(feedModel);
}
return new PostsFetchResponse(feedModels, hasNextPage, endCursor);
}
@NonNull
private List<PostChild> getSliderItems(final JSONArray children) throws JSONException {
final List<PostChild> sliderItems = new ArrayList<>();
for (int j = 0; j < children.length(); ++j) {
final JSONObject childNode = children.optJSONObject(j).getJSONObject("node");
final boolean isChildVideo = childNode.optBoolean("is_video");
int height = 0;
int width = 0;
final JSONObject dimensions = childNode.optJSONObject("dimensions");
if (dimensions != null) {
height = dimensions.optInt("height");
width = dimensions.optInt("width");
}
String thumbnailUrl = null;
try {
thumbnailUrl = childNode.getJSONArray("display_resources")
.getJSONObject(0)
.getString("src");
} catch (JSONException ignored) {}
final PostChild sliderItem = new PostChild.Builder()
.setItemType(isChildVideo ? MediaItemType.MEDIA_TYPE_VIDEO
: MediaItemType.MEDIA_TYPE_IMAGE)
.setPostId(childNode.getString(Constants.EXTRAS_ID))
.setDisplayUrl(isChildVideo ? childNode.getString("video_url")
: childNode.getString("display_url"))
.setThumbnailUrl(thumbnailUrl != null ? thumbnailUrl
: childNode.getString("display_url"))
.setVideoViews(childNode.optLong("video_view_count", 0))
.setHeight(height)
.setWidth(width)
.build();
// Log.d(TAG, "getSliderItems: sliderItem: " + sliderItem);
sliderItems.add(sliderItem);
}
return sliderItems;
}
public void fetchSaved(final String maxId,
final ServiceCallback<SavedPostsFetchResponse> callback) {
final ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
@ -358,13 +240,17 @@ public class ProfileService extends BaseService {
}
public void fetchTagged(final String profileId,
final String maxId,
final int postsPerPage,
final String cursor,
final ServiceCallback<SavedPostsFetchResponse> callback) {
final ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
if (!TextUtils.isEmpty(maxId)) {
builder.put("max_id", maxId);
}
final Call<String> request = repository.fetchTagged(profileId, builder.build());
final Map<String, String> queryMap = new HashMap<>();
queryMap.put("query_hash", "31fe64d9463cbbe58319dced405c6206");
queryMap.put("variables", "{" +
"\"id\":\"" + profileId + "\"," +
"\"first\":" + postsPerPage + "," +
"\"after\":\"" + (cursor == null ? "" : cursor) + "\"" +
"}");
final Call<String> request = wwwRepository.fetch(queryMap);
request.enqueue(new Callback<String>() {
@Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
@ -377,7 +263,7 @@ public class ProfileService extends BaseService {
callback.onSuccess(null);
return;
}
final SavedPostsFetchResponse savedPostsFetchResponse = parseSavedPostsResponse(body, false);
final SavedPostsFetchResponse savedPostsFetchResponse = parseTaggedPostsResponse(body);
callback.onSuccess(savedPostsFetchResponse);
} catch (JSONException e) {
Log.e(TAG, "onResponse", e);
@ -411,6 +297,41 @@ public class ProfileService extends BaseService {
);
}
@NonNull
private SavedPostsFetchResponse parseTaggedPostsResponse(@NonNull final String body)
throws JSONException {
final List<FeedModel> feedModels = new ArrayList<>();
final JSONObject timelineFeed = new JSONObject(body)
.getJSONObject("data")
.getJSONObject(Constants.EXTRAS_USER)
.getJSONObject("edge_user_to_photos_of_you");
final String endCursor;
final boolean hasNextPage;
final JSONObject pageInfo = timelineFeed.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 feedItems = timelineFeed.getJSONArray("edges");
for (int i = 0; i < feedItems.length(); ++i) {
final JSONObject itemJson = feedItems.optJSONObject(i);
if (itemJson == null) {
continue;
}
final FeedModel feedModel = ResponseBodyUtils.parseGraphQLItem(itemJson);
if (feedModel != null) {
feedModels.add(feedModel);
}
}
return new SavedPostsFetchResponse(hasNextPage, endCursor, timelineFeed.getInt("count"), "ok", feedModels);
}
private List<FeedModel> parseItems(final JSONArray items, final boolean isInMedia) throws JSONException {
if (items == null) {
return Collections.emptyList();

View File

@ -123,7 +123,7 @@ public class StoriesService extends BaseService {
user.getString("username"),
null, null, null,
user.getString("profile_pic_url"),
null, 0, 0, 0, false, false, false, false);
null, 0, 0, 0, false, false, false, false, false);
final String id = node.getString("id");
final boolean fullyRead = !node.isNull("seen") && node.getLong("seen") == node.getLong("latest_reel_media");
feedStoryModels.add(new FeedStoryModel(id, profileModel, fullyRead));

View File

@ -18,45 +18,96 @@
app:layout_constraintEnd_toStartOf="@id/mainPostCount"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="@id/fav_chip"
tools:background="@mipmap/ic_launcher" />
<androidx.appcompat.widget.AppCompatTextView
<com.google.android.material.chip.Chip
android:id="@+id/mainPostCount"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_width="wrap_content"
android:layout_height="@dimen/profile_chip_size"
android:layout_marginStart="4dp"
android:clickable="false"
android:visibility="gone"
tools:visibility="visible"
android:gravity="center"
android:textAppearance="@style/TextAppearance.AppCompat"
app:layout_constraintBottom_toBottomOf="@id/mainProfileImage"
app:layout_constraintEnd_toStartOf="@id/mainFollowers"
app:layout_constraintBottom_toTopOf="@id/mainFollowers"
app:layout_constraintStart_toEndOf="@id/mainProfileImage"
app:layout_constraintTop_toTopOf="parent"
tools:text="35\nPosts" />
app:layout_constraintTop_toTopOf="@id/mainProfileImage"
tools:text="35 Posts" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/mainFollowers"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="?selectableItemBackgroundBorderless"
<com.google.android.material.chip.Chip
android:id="@+id/mainStatus"
android:layout_width="wrap_content"
android:layout_height="@dimen/profile_chip_size"
android:layout_marginStart="4dp"
android:clickable="false"
android:visibility="gone"
tools:visibility="visible"
android:gravity="center"
android:textAppearance="@style/TextAppearance.AppCompat"
app:layout_constraintBottom_toBottomOf="@id/mainProfileImage"
app:layout_constraintEnd_toStartOf="@id/mainFollowing"
app:layout_constraintBottom_toTopOf="@id/mainFollowers"
app:layout_constraintStart_toEndOf="@id/mainPostCount"
app:layout_constraintTop_toTopOf="parent"
tools:text="68\nFollowers" />
app:layout_constraintTop_toTopOf="@id/mainPostCount"
tools:text="omg what do u expect" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/mainFollowing"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="?selectableItemBackgroundBorderless"
<com.google.android.material.chip.Chip
android:id="@+id/mainFollowers"
android:layout_width="wrap_content"
android:layout_height="@dimen/profile_chip_size"
android:layout_marginStart="4dp"
android:clickable="false"
android:visibility="gone"
tools:visibility="visible"
android:gravity="center"
android:textAppearance="@style/TextAppearance.AppCompat"
app:layout_constraintBottom_toBottomOf="@id/mainProfileImage"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/fav_chip"
app:layout_constraintStart_toEndOf="@id/mainProfileImage"
app:layout_constraintTop_toBottomOf="@id/mainPostCount"
app:rippleColor="@color/grey_400"
tools:text="10 Followers" />
<com.google.android.material.chip.Chip
android:id="@+id/mainFollowing"
android:layout_width="wrap_content"
android:layout_height="@dimen/profile_chip_size"
android:layout_marginStart="4dp"
android:clickable="false"
android:visibility="gone"
tools:visibility="visible"
android:gravity="center"
app:layout_constraintBottom_toTopOf="@id/fav_chip"
app:layout_constraintStart_toEndOf="@id/mainFollowers"
app:layout_constraintTop_toTopOf="parent"
tools:text="64\nFollowing" />
app:layout_constraintTop_toBottomOf="@id/mainPostCount"
app:rippleColor="@color/grey_400"
tools:text="10 Following" />
<com.google.android.material.chip.Chip
android:id="@+id/fav_chip"
android:layout_width="wrap_content"
android:layout_height="@dimen/profile_chip_size"
android:layout_marginStart="8dp"
android:text="@string/add_to_favorites"
android:visibility="gone"
app:chipIcon="@drawable/ic_outline_star_plus_24"
app:chipIconTint="@color/yellow_800"
app:layout_constraintBottom_toBottomOf="@id/mainProfileImage"
app:layout_constraintStart_toEndOf="@id/mainProfileImage"
app:layout_constraintTop_toBottomOf="@id/mainFollowers"
app:rippleColor="@color/yellow_400"
tools:visibility="visible" />
<com.google.android.material.chip.Chip
android:id="@+id/btnTagged"
android:layout_width="wrap_content"
android:layout_height="@dimen/profile_chip_size"
android:layout_marginStart="4dp"
android:text="@string/tagged"
android:visibility="gone"
app:chipIcon="@drawable/ic_outline_person_pin_24"
app:chipIconTint="@color/deep_orange_800"
app:layout_constraintBottom_toBottomOf="@id/fav_chip"
app:layout_constraintStart_toEndOf="@id/fav_chip"
app:layout_constraintTop_toBottomOf="@id/mainFollowers"
app:rippleColor="@color/deep_orange_400"
tools:visibility="visible" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/mainFullName"
@ -70,7 +121,7 @@
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/mainProfileImage"
app:layout_constraintTop_toBottomOf="@id/fav_chip"
tools:text="Austin Huang" />
<androidx.appcompat.widget.AppCompatImageView
@ -84,7 +135,7 @@
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/mainFullName"
app:layout_constraintStart_toEndOf="@id/mainFullName"
app:layout_constraintTop_toBottomOf="@id/mainProfileImage"
app:layout_constraintTop_toBottomOf="@id/fav_chip"
app:srcCompat="@drawable/verified"
tools:visibility="visible" />
@ -151,29 +202,12 @@
app:iconGravity="top"
app:iconTint="@color/deep_purple_200"
app:layout_constraintBottom_toTopOf="@id/highlights_barrier"
app:layout_constraintEnd_toStartOf="@id/btnTagged"
app:layout_constraintEnd_toStartOf="@id/btnSaved"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/mainUrl"
app:rippleColor="@color/purple_200"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnTagged"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/tagged"
android:textColor="@color/deep_orange_600"
android:visibility="gone"
app:icon="@drawable/ic_outline_person_pin_24"
app:iconGravity="top"
app:iconTint="@color/deep_orange_600"
app:layout_constraintBottom_toTopOf="@id/highlights_barrier"
app:layout_constraintEnd_toStartOf="@id/btnSaved"
app:layout_constraintStart_toEndOf="@id/btnFollow"
app:layout_constraintTop_toBottomOf="@id/mainUrl"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnSaved"
style="@style/Widget.MaterialComponents.Button.TextButton"
@ -187,7 +221,7 @@
app:iconTint="@color/blue_700"
app:layout_constraintBottom_toTopOf="@id/highlights_barrier"
app:layout_constraintEnd_toStartOf="@id/btnLiked"
app:layout_constraintStart_toEndOf="@id/btnTagged"
app:layout_constraintStart_toEndOf="@id/btnFollow"
app:layout_constraintTop_toBottomOf="@id/mainUrl"
app:rippleColor="@color/blue_A400"
tools:visibility="visible" />

View File

@ -4,6 +4,7 @@
<dimen name="private_page_margins">@dimen/profile_picture_size</dimen>
<dimen name="profile_picture_size">90dp</dimen>
<dimen name="profile_chip_size">40dp</dimen>
<dimen name="image_size_40">40dp</dimen>
<dimen name="profile_pic_size_tiny">24dp</dimen>

View File

@ -45,10 +45,15 @@
<string name="instadp_settings">Use Instadp for high definition profile pictures</string>
<string name="import_export">Import/Export</string>
<string name="select_language">Language</string>
<string name="main_posts_count">%s\nPosts</string>
<string name="main_posts_count_inline">%s Posts</string>
<string name="main_posts_followers">%s\nFollowers</string>
<string name="main_posts_following">%s\nFollowing</string>
<plurals name="main_posts_count_inline">
<item quantity="one">%s Post</item>
<item quantity="other">%s Posts</item>
</plurals>
<plurals name="main_posts_followers">
<item quantity="one">%s Follower</item>
<item quantity="other">%s Followers</item>
</plurals>
<string name="main_posts_following">%s Following </string>
<string name="post_viewer_autoplay_video">Autoplay videos</string>
<string name="post_viewer_muted_autoplay">Always mute videos</string>
<string name="post_viewer_download_dialog_title">Select what to download</string>
@ -107,6 +112,9 @@
<string name="unblock">Unblock</string>
<string name="restrict">Restrict</string>
<string name="unrestrict">Unrestrict</string>
<string name="status_mutual">Following each other</string>
<string name="status_following">Followed by you</string>
<string name="status_follower">Following you</string>
<string name="map">Map</string>
<string name="dialog_export_btn_export">Export</string>
<string name="dialog_export_btn_import">Import</string>