full comment/caption/bio translation support, close #178

This commit is contained in:
Austin Huang 2020-12-21 21:22:38 -05:00
parent 7cd56080cd
commit 71264bef96
No known key found for this signature in database
GPG Key ID: 84C23AA04587A91F
12 changed files with 236 additions and 73 deletions

View File

@ -287,29 +287,25 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
&& (userIdFromCookie.equals(commentModel.getProfileModel().getId()) || userIdFromCookie.equals(userId))) {
commentDialogList = new String[]{
resources.getString(R.string.open_profile),
resources.getString(R.string.view_pfp),
// resources.getString(R.string.comment_viewer_copy_user),
resources.getString(R.string.comment_viewer_copy_comment),
resources.getString(R.string.comment_viewer_reply_comment),
commentModel.getLiked() ? resources.getString(R.string.comment_viewer_unlike_comment)
: resources.getString(R.string.comment_viewer_like_comment),
resources.getString(R.string.comment_viewer_translate_comment),
resources.getString(R.string.comment_viewer_delete_comment)
};
} else if (!TextUtils.isEmpty(cookie)) {
commentDialogList = new String[]{
resources.getString(R.string.open_profile),
resources.getString(R.string.view_pfp),
// resources.getString(R.string.comment_viewer_copy_user),
resources.getString(R.string.comment_viewer_copy_comment),
resources.getString(R.string.comment_viewer_reply_comment),
commentModel.getLiked() ? resources.getString(R.string.comment_viewer_unlike_comment)
: resources.getString(R.string.comment_viewer_like_comment),
resources.getString(R.string.comment_viewer_translate_comment)
};
} else {
commentDialogList = new String[]{
resources.getString(R.string.open_profile),
resources.getString(R.string.view_pfp),
// resources.getString(R.string.comment_viewer_copy_user),
resources.getString(R.string.comment_viewer_copy_comment)
};
}
@ -322,23 +318,10 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
case 0: // open profile
openProfile("@" + profileModel.getUsername());
break;
case 1: // view profile pic
final FragmentManager fragmentManager = getParentFragmentManager();
final ProfilePicDialogFragment fragment = new ProfilePicDialogFragment(profileModel.getId(),
profileModel.getUsername(),
profileModel.getHdProfilePic());
final FragmentTransaction ft = fragmentManager.beginTransaction();
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.add(fragment, "profilePicDialog")
.commit();
break;
// case 2: // copy username
// Utils.copyText(context, profileModel.getUsername());
// break;
case 2: // copy comment
case 1: // copy comment
Utils.copyText(context, "@" + profileModel.getUsername() + ": " + commentModel.getText());
break;
case 3: // reply to comment
case 2: // reply to comment
commentsAdapter.setSelected(commentModel);
String mention = "@" + profileModel.getUsername() + " ";
binding.commentText.setText(mention);
@ -350,7 +333,7 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
imm.showSoftInput(binding.commentText, 0);
}, 200);
break;
case 4: // like/unlike comment
case 3: // like/unlike comment
if (csrfToken == null) {
return;
}
@ -390,6 +373,28 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
}
});
break;
case 4: // translate comment
mediaService.translate(commentModel.getId(), "2", new ServiceCallback<String>() {
@Override
public void onSuccess(final String result) {
if (TextUtils.isEmpty(result)) {
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
return;
}
new AlertDialog.Builder(context)
.setTitle(username)
.setMessage(result)
.setPositiveButton(R.string.ok, null)
.show();
}
@Override
public void onFailure(final Throwable t) {
Log.e(TAG, "Error translating comment", t);
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
break;
case 5: // delete comment
final String userId = CookieUtils.getUserIdFromCookie(cookie);
if (userId == null) return;

View File

@ -250,15 +250,20 @@ public final class FollowViewerFragment extends Fragment implements SwipeRefresh
setSubtitle(R.string.followers_compare);
allFollowing.clear();
binding.swipeRefreshLayout.setRefreshing(true);
if (moreAvailable) friendshipService.getList(isFollowersList,
profileId,
endCursor,
isFollowersList ? followersFetchCb : followingFetchCb);
else if (followersModels.size() == 0 || followingModels.size() == 0)
if (moreAvailable) {
Toast.makeText(getContext(), R.string.follower_start_compare, Toast.LENGTH_LONG).show();
friendshipService.getList(isFollowersList,
profileId,
endCursor,
isFollowersList ? followersFetchCb : followingFetchCb);
}
else if (followersModels.size() == 0 || followingModels.size() == 0) {
Toast.makeText(getContext(), R.string.follower_start_compare, Toast.LENGTH_LONG).show();
friendshipService.getList(!isFollowersList,
profileId,
null,
isFollowersList ? followingFetchCb : followersFetchCb);
profileId,
null,
isFollowersList ? followingFetchCb : followersFetchCb);
}
else showCompare();
}

View File

@ -743,11 +743,33 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
binding.captionParent.getBackground().mutate().setAlpha((int) (128 + (128 * (slideOffset < 0 ? 0 : slideOffset))));
}
});
binding.caption.setOnClickListener(v -> {
binding.captionFrame.setOnClickListener(v -> {
if (bottomSheetBehavior == null) return;
if (bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) return;
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
});
if (TextUtils.isEmpty(feedModel.getCaptionId()))
binding.translateTitle.setVisibility(View.GONE);
else binding.translateTitle.setOnClickListener(v -> {
mediaService.translate(feedModel.getCaptionId(), "1", new ServiceCallback<String>() {
@Override
public void onSuccess(final String result) {
if (TextUtils.isEmpty(result)) {
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
return;
}
binding.translateTitle.setOnClickListener(null);
binding.translatedCaption.setVisibility(View.VISIBLE);
binding.translatedCaption.setText(result);
}
@Override
public void onFailure(final Throwable t) {
Log.e(TAG, "Error translating comment", t);
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
});
binding.captionToggle.setOnClickListener(v -> {
if (bottomSheetBehavior == null) return;
switch (bottomSheetBehavior.getState()) {

View File

@ -89,6 +89,7 @@ import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.viewmodels.HighlightsViewModel;
import awais.instagrabber.webservices.FriendshipService;
import awais.instagrabber.webservices.MediaService;
import awais.instagrabber.webservices.ServiceCallback;
import awais.instagrabber.webservices.StoriesService;
@ -113,6 +114,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
private Handler usernameSettingHandler;
private FriendshipService friendshipService;
private StoriesService storiesService;
private MediaService mediaService;
private boolean shouldRefresh = true;
private boolean hasStories = false;
private HighlightsAdapter highlightsAdapter;
@ -298,6 +300,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
fragmentActivity = (MainActivity) requireActivity();
friendshipService = FriendshipService.getInstance();
storiesService = StoriesService.getInstance();
mediaService = MediaService.getInstance();
accountRepository = AccountRepository.getInstance(AccountDataSource.getInstance(getContext()));
favoriteRepository = FavoriteRepository.getInstance(FavoriteDataSource.getInstance(getContext()));
setHasOptionsMenu(true);
@ -695,6 +698,51 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
.trim()));
profileDetailsBinding.mainBiography
.addOnURLClickListener(autoLinkItem -> Utils.openURL(getContext(), autoLinkItem.getOriginalText().trim()));
profileDetailsBinding.mainBiography.setOnClickListener(v -> {
String[] commentDialogList;
if (!TextUtils.isEmpty(cookie)) {
commentDialogList = new String[]{
getResources().getString(R.string.bio_copy),
getResources().getString(R.string.bio_translate)
};
} else {
commentDialogList = new String[]{
getResources().getString(R.string.bio_copy)
};
}
new AlertDialog.Builder(context)
.setItems(commentDialogList, (d,w) -> {
switch (w) {
case 0:
Utils.copyText(context, biography);
break;
case 1:
mediaService.translate(profileModel.getId(), "3", new ServiceCallback<String>() {
@Override
public void onSuccess(final String result) {
if (TextUtils.isEmpty(result)) {
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
return;
}
new AlertDialog.Builder(context)
.setTitle(profileModel.getUsername())
.setMessage(result)
.setPositiveButton(R.string.ok, null)
.show();
}
@Override
public void onFailure(final Throwable t) {
Log.e(TAG, "Error translating bio", t);
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
break;
}
})
.setNegativeButton(R.string.cancel, null)
.show();
});
profileDetailsBinding.mainBiography.setOnLongClickListener(v -> {
Utils.copyText(context, biography);
return true;

View File

@ -11,16 +11,12 @@ import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.utils.Utils;
public abstract class BasePostModel implements Serializable, Selectable {
protected String postId;
protected String displayUrl;
protected String shortCode;
protected String postId, displayUrl, shortCode, captionId;
protected CharSequence postCaption;
protected MediaItemType itemType;
protected boolean isSelected;
protected boolean isDownloaded;
protected boolean isSelected, isDownloaded;
protected long timestamp;
boolean liked;
boolean saved;
boolean liked, saved;
public boolean getLike() {
return liked;
@ -46,6 +42,10 @@ public abstract class BasePostModel implements Serializable, Selectable {
return postCaption;
}
public final String getCaptionId() {
return captionId;
}
public final String getShortCode() {
return shortCode;
}

View File

@ -24,7 +24,7 @@ public final class FeedModel extends PostModel {
private String displayUrl;
private String thumbnailUrl;
private String shortCode;
private String postCaption;
private String postCaption, captionId;
private long commentsCount;
private long timestamp;
private boolean liked;
@ -76,6 +76,11 @@ public final class FeedModel extends PostModel {
return this;
}
public Builder setCaptionId(final String captionId) {
this.captionId = captionId;
return this;
}
public Builder setCommentsCount(final long commentsCount) {
this.commentsCount = commentsCount;
return this;
@ -127,8 +132,8 @@ public final class FeedModel extends PostModel {
}
public FeedModel build() {
return new FeedModel(profileModel, itemType, viewCount, postId, displayUrl, thumbnailUrl, shortCode, postCaption, commentsCount,
timestamp, liked, bookmarked, likesCount, locationName, locationId, sliderItems, imageHeight, imageWidth);
return new FeedModel(profileModel, itemType, viewCount, postId, displayUrl, thumbnailUrl, shortCode, postCaption, captionId,
commentsCount, timestamp, liked, bookmarked, likesCount, locationName, locationId, sliderItems, imageHeight, imageWidth);
}
}
@ -140,6 +145,7 @@ public final class FeedModel extends PostModel {
final String thumbnailUrl,
final String shortCode,
final String postCaption,
final String captionId,
final long commentsCount,
final long timestamp,
final boolean liked,
@ -150,7 +156,7 @@ public final class FeedModel extends PostModel {
final List<PostChild> sliderItems,
final int imageHeight,
final int imageWidth) {
super(itemType, postId, displayUrl, thumbnailUrl, shortCode, postCaption, timestamp, liked, bookmarked);
super(itemType, postId, displayUrl, thumbnailUrl, shortCode, postCaption, captionId, timestamp, liked, bookmarked);
this.profileModel = profileModel;
this.commentsCount = commentsCount;
this.likesCount = likesCount;

View File

@ -19,6 +19,7 @@ public class PostModel extends BasePostModel {
final String thumbnailUrl,
final String shortCode,
final CharSequence postCaption,
final String captionId,
long timestamp,
boolean liked,
boolean bookmarked) {
@ -28,6 +29,7 @@ public class PostModel extends BasePostModel {
this.thumbnailUrl = thumbnailUrl;
this.shortCode = shortCode;
this.postCaption = postCaption;
this.captionId = captionId;
this.timestamp = timestamp;
this.liked = liked;
this.saved = bookmarked;

View File

@ -9,40 +9,38 @@ import retrofit2.http.GET;
import retrofit2.http.Header;
import retrofit2.http.POST;
import retrofit2.http.Path;
import retrofit2.http.QueryMap;
public interface MediaRepository {
@GET("/api/v1/media/{mediaId}/likers/")
Call<String> fetchLikes(@Header("User-Agent") final String userAgent,
@Path("mediaId") final String mediaId);
Call<String> fetchLikes(@Path("mediaId") final String mediaId);
@FormUrlEncoded
@POST("/api/v1/media/{mediaId}/{action}/")
Call<String> action(@Header("User-Agent") final String userAgent,
@Path("action") final String action,
Call<String> action(@Path("action") final String action,
@Path("mediaId") final String mediaId,
@FieldMap final Map<String, String> signedForm);
@FormUrlEncoded
@POST("/api/v1/media/{mediaId}/comment/")
Call<String> comment(@Header("User-Agent") final String userAgent,
@Path("mediaId") final String mediaId,
Call<String> comment(@Path("mediaId") final String mediaId,
@FieldMap final Map<String, String> signedForm);
@FormUrlEncoded
@POST("/api/v1/media/{mediaId}/comment/bulk_delete/")
Call<String> commentsBulkDelete(@Header("User-Agent") final String userAgent,
@Path("mediaId") final String mediaId,
Call<String> commentsBulkDelete(@Path("mediaId") final String mediaId,
@FieldMap final Map<String, String> signedForm);
@FormUrlEncoded
@POST("/api/v1/media/{commentId}/comment_like/")
Call<String> commentLike(@Header("User-Agent") final String userAgent,
@Path("commentId") final String commentId,
Call<String> commentLike(@Path("commentId") final String commentId,
@FieldMap final Map<String, String> signedForm);
@FormUrlEncoded
@POST("/api/v1/media/{commentId}/comment_unlike/")
Call<String> commentUnlike(@Header("User-Agent") final String userAgent,
@Path("commentId") final String commentId,
Call<String> commentUnlike(@Path("commentId") final String commentId,
@FieldMap final Map<String, String> signedForm);
@GET("/api/v1/language/translate/")
Call<String> translate(@QueryMap final Map<String, String> form);
}

View File

@ -640,6 +640,7 @@ public final class ResponseBodyUtils {
.setThumbnailUrl(mediaType != MediaItemType.MEDIA_TYPE_SLIDER ? ResponseBodyUtils.getLowQualityImage(itemJson) : null)
.setShortCode(itemJson.getString("code"))
.setPostCaption(captionJson != null ? captionJson.optString("text") : null)
.setCaptionId(captionJson != null ? captionJson.optString("pk") : null)
.setCommentsCount(itemJson.optInt("comment_count"))
.setTimestamp(itemJson.optLong("taken_at", -1))
.setLiked(itemJson.optBoolean("has_liked"))

View File

@ -86,7 +86,7 @@ public class MediaService extends BaseService {
form.put("_uuid", UUID.randomUUID().toString());
// form.put("radio_type", "wifi-none");
final Map<String, String> signedForm = Utils.sign(form);
final Call<String> request = repository.action(Constants.I_USER_AGENT, action, mediaId, signedForm);
final Call<String> request = repository.action(action, mediaId, signedForm);
request.enqueue(new Callback<String>() {
@Override
public void onResponse(@NonNull final Call<String> call,
@ -135,7 +135,7 @@ public class MediaService extends BaseService {
form.put("replied_to_comment_id", replyToCommentId);
}
final Map<String, String> signedForm = Utils.sign(form);
final Call<String> commentRequest = repository.comment(Constants.I_USER_AGENT, mediaId, signedForm);
final Call<String> commentRequest = repository.comment(mediaId, signedForm);
commentRequest.enqueue(new Callback<String>() {
@Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
@ -181,7 +181,7 @@ public class MediaService extends BaseService {
form.put("_uid", userId);
form.put("_uuid", UUID.randomUUID().toString());
final Map<String, String> signedForm = Utils.sign(form);
final Call<String> bulkDeleteRequest = repository.commentsBulkDelete(Constants.USER_AGENT, mediaId, signedForm);
final Call<String> bulkDeleteRequest = repository.commentsBulkDelete(mediaId, signedForm);
bulkDeleteRequest.enqueue(new Callback<String>() {
@Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
@ -217,7 +217,7 @@ public class MediaService extends BaseService {
// form.put("_uid", userId);
// form.put("_uuid", UUID.randomUUID().toString());
final Map<String, String> signedForm = Utils.sign(form);
final Call<String> commentLikeRequest = repository.commentLike(Constants.USER_AGENT, commentId, signedForm);
final Call<String> commentLikeRequest = repository.commentLike(commentId, signedForm);
commentLikeRequest.enqueue(new Callback<String>() {
@Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
@ -253,7 +253,7 @@ public class MediaService extends BaseService {
// form.put("_uid", userId);
// form.put("_uuid", UUID.randomUUID().toString());
final Map<String, String> signedForm = Utils.sign(form);
final Call<String> commentUnlikeRequest = repository.commentUnlike(Constants.USER_AGENT, commentId, signedForm);
final Call<String> commentUnlikeRequest = repository.commentUnlike(commentId, signedForm);
commentUnlikeRequest.enqueue(new Callback<String>() {
@Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
@ -283,7 +283,7 @@ public class MediaService extends BaseService {
public void fetchLikes(final String mediaId,
@NonNull final ServiceCallback<List<ProfileModel>> callback) {
final Call<String> likesRequest = repository.fetchLikes(Constants.I_USER_AGENT, mediaId);
final Call<String> likesRequest = repository.fetchLikes(mediaId);
likesRequest.enqueue(new Callback<String>() {
@Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
@ -324,4 +324,38 @@ public class MediaService extends BaseService {
}
});
}
public void translate(final String id,
final String type, // 1 caption 2 comment 3 bio
@NonNull final ServiceCallback<String> callback) {
final Map<String, String> form = new HashMap<>();
form.put("id", id);
form.put("type", type);
final Call<String> request = repository.translate(form);
request.enqueue(new Callback<String>() {
@Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
final String body = response.body();
if (body == null) {
Log.e(TAG, "Error occurred while translating");
callback.onSuccess(null);
return;
}
try {
final JSONObject jsonObject = new JSONObject(body);
final String translation = jsonObject.optString("translation");
callback.onSuccess(translation);
} catch (JSONException e) {
// Log.e(TAG, "Error parsing body", e);
callback.onFailure(e);
}
}
@Override
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
Log.e(TAG, "Error translating", t);
callback.onFailure(t);
}
});
}
}

View File

@ -159,20 +159,57 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@null">
<awais.instagrabber.customviews.RamboTextViewV2
android:id="@+id/caption"
<LinearLayout
android:id="@+id/captionFrame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="@null"
android:clickable="true"
android:focusable="true"
android:minHeight="100dp"
android:padding="16dp"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
android:textColor="@color/white"
tools:text="Text text text" />
android:layout_height="match_parent"
android:orientation="vertical">
<awais.instagrabber.customviews.RamboTextViewV2
android:id="@+id/caption"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="@null"
android:clickable="true"
android:focusable="true"
android:padding="16dp"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
android:textColor="@color/white"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/translateTitle"
tools:text="Text text text" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/translateTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:visibility="visible"
android:gravity="center_vertical"
android:padding="16dp"
app:layout_constraintTop_toBottomOf="@id/caption"
app:layout_constraintBottom_toTopOf="@id/translatedCaption"
android:text="@string/translate_caption"
android:textColor="?android:textColorSecondary"
android:textSize="16dp"/>
<awais.instagrabber.customviews.RamboTextViewV2
android:id="@+id/translatedCaption"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:visibility="gone"
android:background="@null"
android:clickable="true"
android:focusable="true"
android:padding="16dp"
app:layout_constraintTop_toBottomOf="@id/translateTitle"
app:layout_constraintBottom_toBottomOf="parent"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
android:textColor="@color/white"
tools:text="Text text text" />
</LinearLayout>
</ScrollView>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -112,6 +112,8 @@
<string name="unblock">Unblock</string>
<string name="restrict">Restrict</string>
<string name="unrestrict">Unrestrict</string>
<string name="bio_copy">Copy bio</string>
<string name="bio_translate">Translate bio</string>
<string name="status_mutual">Following each other</string>
<string name="status_following">Followed by you</string>
<string name="status_follower">Following you</string>
@ -195,6 +197,7 @@
<string name="comment_viewer_reply_comment">Reply to comment</string>
<string name="comment_viewer_like_comment">Like comment</string>
<string name="comment_viewer_unlike_comment">Unlike comment</string>
<string name="comment_viewer_translate_comment">Translate comment</string>
<string name="comment_viewer_delete_comment">Delete comment</string>
<string name="comment_send_empty_comment">No empty comments!</string>
<string name="comment_view_mention_user_search">Do you want to search the username?</string>
@ -288,6 +291,7 @@
<string name="apply">Apply</string>
<string name="save">Save</string>
<string name="caption">Caption</string>
<string name="translate_caption">Translate caption...</string>
<string name="player_timeline_desc">Video player timeline</string>
<string name="one_x" translatable="false">1x</string>
<string name="two_x" translatable="false">2x</string>
@ -323,6 +327,7 @@
<string name="show_grid_gap">Show grid gap</string>
<string name="disable_animation">Disable animation</string>
<string name="follower_wait_to_load">Please wait for the current task to complete first!</string>
<string name="follower_start_compare">Depending on user counts, this can take a while to load. Please be patient.</string>
<string name="post_not_found">Post not found!</string>
<string name="no_external_app_url">No app found which opens urls</string>
<plurals name="likes_count">