From 71264bef966c040ab892828f01e519a9117ee6c0 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Mon, 21 Dec 2020 21:22:38 -0500 Subject: [PATCH] full comment/caption/bio translation support, close #178 --- .../fragments/CommentsViewerFragment.java | 49 ++++++++------- .../fragments/FollowViewerFragment.java | 21 ++++--- .../fragments/PostViewV2Fragment.java | 24 ++++++- .../fragments/main/ProfileFragment.java | 48 ++++++++++++++ .../instagrabber/models/BasePostModel.java | 14 ++--- .../awais/instagrabber/models/FeedModel.java | 14 +++-- .../awais/instagrabber/models/PostModel.java | 2 + .../repositories/MediaRepository.java | 22 +++---- .../instagrabber/utils/ResponseBodyUtils.java | 1 + .../webservices/MediaService.java | 46 ++++++++++++-- app/src/main/res/layout/dialog_post_view.xml | 63 +++++++++++++++---- app/src/main/res/values/strings.xml | 5 ++ 12 files changed, 236 insertions(+), 73 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/fragments/CommentsViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/CommentsViewerFragment.java index f39b2ea1..2ddc1b7a 100644 --- a/app/src/main/java/awais/instagrabber/fragments/CommentsViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/CommentsViewerFragment.java @@ -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() { + @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; diff --git a/app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.java index 1ccb3464..4da57ca4 100644 --- a/app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.java @@ -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(); } diff --git a/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java b/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java index 84fea98e..fbc821c2 100644 --- a/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java @@ -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() { + @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()) { diff --git a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java index 7c2b4843..9edb51f6 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java @@ -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() { + @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; diff --git a/app/src/main/java/awais/instagrabber/models/BasePostModel.java b/app/src/main/java/awais/instagrabber/models/BasePostModel.java index 38f5f8fd..4f8486d3 100755 --- a/app/src/main/java/awais/instagrabber/models/BasePostModel.java +++ b/app/src/main/java/awais/instagrabber/models/BasePostModel.java @@ -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; } diff --git a/app/src/main/java/awais/instagrabber/models/FeedModel.java b/app/src/main/java/awais/instagrabber/models/FeedModel.java index d9178640..f4e51664 100755 --- a/app/src/main/java/awais/instagrabber/models/FeedModel.java +++ b/app/src/main/java/awais/instagrabber/models/FeedModel.java @@ -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 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; diff --git a/app/src/main/java/awais/instagrabber/models/PostModel.java b/app/src/main/java/awais/instagrabber/models/PostModel.java index e74924d7..75bb6753 100755 --- a/app/src/main/java/awais/instagrabber/models/PostModel.java +++ b/app/src/main/java/awais/instagrabber/models/PostModel.java @@ -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; diff --git a/app/src/main/java/awais/instagrabber/repositories/MediaRepository.java b/app/src/main/java/awais/instagrabber/repositories/MediaRepository.java index f8e42a21..525a666f 100644 --- a/app/src/main/java/awais/instagrabber/repositories/MediaRepository.java +++ b/app/src/main/java/awais/instagrabber/repositories/MediaRepository.java @@ -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 fetchLikes(@Header("User-Agent") final String userAgent, - @Path("mediaId") final String mediaId); + Call fetchLikes(@Path("mediaId") final String mediaId); @FormUrlEncoded @POST("/api/v1/media/{mediaId}/{action}/") - Call action(@Header("User-Agent") final String userAgent, - @Path("action") final String action, + Call action(@Path("action") final String action, @Path("mediaId") final String mediaId, @FieldMap final Map signedForm); @FormUrlEncoded @POST("/api/v1/media/{mediaId}/comment/") - Call comment(@Header("User-Agent") final String userAgent, - @Path("mediaId") final String mediaId, + Call comment(@Path("mediaId") final String mediaId, @FieldMap final Map signedForm); @FormUrlEncoded @POST("/api/v1/media/{mediaId}/comment/bulk_delete/") - Call commentsBulkDelete(@Header("User-Agent") final String userAgent, - @Path("mediaId") final String mediaId, + Call commentsBulkDelete(@Path("mediaId") final String mediaId, @FieldMap final Map signedForm); @FormUrlEncoded @POST("/api/v1/media/{commentId}/comment_like/") - Call commentLike(@Header("User-Agent") final String userAgent, - @Path("commentId") final String commentId, + Call commentLike(@Path("commentId") final String commentId, @FieldMap final Map signedForm); @FormUrlEncoded @POST("/api/v1/media/{commentId}/comment_unlike/") - Call commentUnlike(@Header("User-Agent") final String userAgent, - @Path("commentId") final String commentId, + Call commentUnlike(@Path("commentId") final String commentId, @FieldMap final Map signedForm); + + @GET("/api/v1/language/translate/") + Call translate(@QueryMap final Map form); } diff --git a/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java b/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java index c9029a00..8739f0c9 100644 --- a/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java +++ b/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java @@ -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")) diff --git a/app/src/main/java/awais/instagrabber/webservices/MediaService.java b/app/src/main/java/awais/instagrabber/webservices/MediaService.java index f41c14cd..3e0c8a04 100644 --- a/app/src/main/java/awais/instagrabber/webservices/MediaService.java +++ b/app/src/main/java/awais/instagrabber/webservices/MediaService.java @@ -86,7 +86,7 @@ public class MediaService extends BaseService { form.put("_uuid", UUID.randomUUID().toString()); // form.put("radio_type", "wifi-none"); final Map signedForm = Utils.sign(form); - final Call request = repository.action(Constants.I_USER_AGENT, action, mediaId, signedForm); + final Call request = repository.action(action, mediaId, signedForm); request.enqueue(new Callback() { @Override public void onResponse(@NonNull final Call call, @@ -135,7 +135,7 @@ public class MediaService extends BaseService { form.put("replied_to_comment_id", replyToCommentId); } final Map signedForm = Utils.sign(form); - final Call commentRequest = repository.comment(Constants.I_USER_AGENT, mediaId, signedForm); + final Call commentRequest = repository.comment(mediaId, signedForm); commentRequest.enqueue(new Callback() { @Override public void onResponse(@NonNull final Call call, @NonNull final Response response) { @@ -181,7 +181,7 @@ public class MediaService extends BaseService { form.put("_uid", userId); form.put("_uuid", UUID.randomUUID().toString()); final Map signedForm = Utils.sign(form); - final Call bulkDeleteRequest = repository.commentsBulkDelete(Constants.USER_AGENT, mediaId, signedForm); + final Call bulkDeleteRequest = repository.commentsBulkDelete(mediaId, signedForm); bulkDeleteRequest.enqueue(new Callback() { @Override public void onResponse(@NonNull final Call call, @NonNull final Response response) { @@ -217,7 +217,7 @@ public class MediaService extends BaseService { // form.put("_uid", userId); // form.put("_uuid", UUID.randomUUID().toString()); final Map signedForm = Utils.sign(form); - final Call commentLikeRequest = repository.commentLike(Constants.USER_AGENT, commentId, signedForm); + final Call commentLikeRequest = repository.commentLike(commentId, signedForm); commentLikeRequest.enqueue(new Callback() { @Override public void onResponse(@NonNull final Call call, @NonNull final Response response) { @@ -253,7 +253,7 @@ public class MediaService extends BaseService { // form.put("_uid", userId); // form.put("_uuid", UUID.randomUUID().toString()); final Map signedForm = Utils.sign(form); - final Call commentUnlikeRequest = repository.commentUnlike(Constants.USER_AGENT, commentId, signedForm); + final Call commentUnlikeRequest = repository.commentUnlike(commentId, signedForm); commentUnlikeRequest.enqueue(new Callback() { @Override public void onResponse(@NonNull final Call call, @NonNull final Response response) { @@ -283,7 +283,7 @@ public class MediaService extends BaseService { public void fetchLikes(final String mediaId, @NonNull final ServiceCallback> callback) { - final Call likesRequest = repository.fetchLikes(Constants.I_USER_AGENT, mediaId); + final Call likesRequest = repository.fetchLikes(mediaId); likesRequest.enqueue(new Callback() { @Override public void onResponse(@NonNull final Call call, @NonNull final Response 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 callback) { + final Map form = new HashMap<>(); + form.put("id", id); + form.put("type", type); + final Call request = repository.translate(form); + request.enqueue(new Callback() { + @Override + public void onResponse(@NonNull final Call call, @NonNull final Response 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 call, @NonNull final Throwable t) { + Log.e(TAG, "Error translating", t); + callback.onFailure(t); + } + }); + } } diff --git a/app/src/main/res/layout/dialog_post_view.xml b/app/src/main/res/layout/dialog_post_view.xml index b0a4227c..f1f220ad 100644 --- a/app/src/main/res/layout/dialog_post_view.xml +++ b/app/src/main/res/layout/dialog_post_view.xml @@ -159,20 +159,57 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:background="@null"> - - + android:layout_height="match_parent" + android:orientation="vertical"> + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b82e1caf..728d29d6 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -112,6 +112,8 @@ Unblock Restrict Unrestrict + Copy bio + Translate bio Following each other Followed by you Following you @@ -195,6 +197,7 @@ Reply to comment Like comment Unlike comment + Translate comment Delete comment No empty comments! Do you want to search the username? @@ -288,6 +291,7 @@ Apply Save Caption + Translate caption... Video player timeline 1x 2x @@ -323,6 +327,7 @@ Show grid gap Disable animation Please wait for the current task to complete first! + Depending on user counts, this can take a while to load. Please be patient. Post not found! No app found which opens urls