From 4f414eac37a8bfce85e662cd458c4e192384eb9e Mon Sep 17 00:00:00 2001 From: Docile-Alligator <25734209+Docile-Alligator@users.noreply.github.com> Date: Thu, 19 Sep 2024 13:23:28 -0400 Subject: Sending comments with a Giphy gif is available. --- app/build.gradle | 2 + .../activities/CommentActivity.java | 54 ++++++++++++++++-- .../activities/PostPollActivity.java | 35 ------------ .../MarkdownBottomBarRecyclerViewAdapter.java | 19 ++++++- .../infinityforreddit/comment/SendComment.java | 7 ++- .../infinityforreddit/markdown/GiphyGifBlock.java | 13 +++++ .../markdown/GiphyGifBlockParser.java | 65 ++++++++++++++++++++++ .../infinityforreddit/markdown/GiphyGifPlugin.java | 28 ++++++++++ .../infinityforreddit/markdown/MarkdownUtils.java | 20 +++++++ .../markdown/RichTextJSONConverter.java | 32 +++++++++++ .../infinityforreddit/thing/GiphyGif.java | 8 +++ .../infinityforreddit/utils/APIUtils.java | 1 + app/src/main/res/drawable/ic_gif_24dp.xml | 9 +++ 13 files changed, 247 insertions(+), 46 deletions(-) create mode 100644 app/src/main/java/ml/docilealligator/infinityforreddit/markdown/GiphyGifBlock.java create mode 100644 app/src/main/java/ml/docilealligator/infinityforreddit/markdown/GiphyGifBlockParser.java create mode 100644 app/src/main/java/ml/docilealligator/infinityforreddit/markdown/GiphyGifPlugin.java create mode 100644 app/src/main/java/ml/docilealligator/infinityforreddit/thing/GiphyGif.java create mode 100644 app/src/main/res/drawable/ic_gif_24dp.xml diff --git a/app/build.gradle b/app/build.gradle index bc8789c7..0e5cf24e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -191,6 +191,8 @@ dependencies { implementation 'androidx.core:core-splashscreen:1.0.1' + implementation 'com.giphy.sdk:ui:2.3.15' + /**** Builds and flavors ****/ // debugImplementation because LeakCanary should only run in debug builds. diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/CommentActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/CommentActivity.java index ff0d3519..f83322e8 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/CommentActivity.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/CommentActivity.java @@ -25,6 +25,10 @@ import androidx.core.content.FileProvider; import com.bumptech.glide.Glide; import com.bumptech.glide.RequestManager; import com.bumptech.glide.request.RequestOptions; +import com.giphy.sdk.core.models.Media; +import com.giphy.sdk.ui.GPHContentType; +import com.giphy.sdk.ui.Giphy; +import com.giphy.sdk.ui.views.GiphyDialogFragment; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.snackbar.Snackbar; @@ -46,11 +50,9 @@ import io.noties.markwon.MarkwonConfiguration; import io.noties.markwon.MarkwonPlugin; import io.noties.markwon.core.MarkwonTheme; import jp.wasabeef.glide.transformations.RoundedCornersTransformation; -import ml.docilealligator.infinityforreddit.network.AnyAccountAccessTokenAuthenticator; import ml.docilealligator.infinityforreddit.Infinity; import ml.docilealligator.infinityforreddit.R; import ml.docilealligator.infinityforreddit.RedditDataRoomDatabase; -import ml.docilealligator.infinityforreddit.thing.UploadedImage; import ml.docilealligator.infinityforreddit.account.Account; import ml.docilealligator.infinityforreddit.adapters.MarkdownBottomBarRecyclerViewAdapter; import ml.docilealligator.infinityforreddit.bottomsheetfragments.AccountChooserBottomSheetFragment; @@ -69,13 +71,17 @@ import ml.docilealligator.infinityforreddit.markdown.EmotePlugin; import ml.docilealligator.infinityforreddit.markdown.ImageAndGifEntry; import ml.docilealligator.infinityforreddit.markdown.ImageAndGifPlugin; import ml.docilealligator.infinityforreddit.markdown.MarkdownUtils; +import ml.docilealligator.infinityforreddit.network.AnyAccountAccessTokenAuthenticator; +import ml.docilealligator.infinityforreddit.thing.GiphyGif; +import ml.docilealligator.infinityforreddit.thing.UploadedImage; +import ml.docilealligator.infinityforreddit.utils.APIUtils; import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils; import ml.docilealligator.infinityforreddit.utils.Utils; import okhttp3.ConnectionPool; import okhttp3.OkHttpClient; import retrofit2.Retrofit; -public class CommentActivity extends BaseActivity implements UploadImageEnabledActivity, AccountChooserBottomSheetFragment.AccountChooserListener { +public class CommentActivity extends BaseActivity implements UploadImageEnabledActivity, AccountChooserBottomSheetFragment.AccountChooserListener, GiphyDialogFragment.GifSelectionListener { public static final String EXTRA_COMMENT_PARENT_TITLE_KEY = "ECPTK"; public static final String EXTRA_COMMENT_PARENT_BODY_KEY = "ECPBK"; @@ -124,6 +130,7 @@ public class CommentActivity extends BaseActivity implements UploadImageEnabledA private boolean isReplying; private Uri capturedImageUri; private ArrayList uploadedImages = new ArrayList<>(); + private GiphyGif giphyGif; private Menu mMenu; /** @@ -282,7 +289,7 @@ public class CommentActivity extends BaseActivity implements UploadImageEnabledA } MarkdownBottomBarRecyclerViewAdapter adapter = new MarkdownBottomBarRecyclerViewAdapter( - mCustomThemeWrapper, true, + mCustomThemeWrapper, true, true, new MarkdownBottomBarRecyclerViewAdapter.ItemClickListener() { @Override public void onClick(int item) { @@ -300,6 +307,11 @@ public class CommentActivity extends BaseActivity implements UploadImageEnabledA fragment.setArguments(arguments); fragment.show(getSupportFragmentManager(), fragment.getTag()); } + + @Override + public void onSelectGiphyGif() { + GiphyDialogFragment.Companion.newInstance().show(getSupportFragmentManager(), "giphy_dialog"); + } }); binding.commentMarkdownBottomBarRecyclerView.setLayoutManager(new LinearLayoutManagerBugFixed(this, @@ -313,6 +325,8 @@ public class CommentActivity extends BaseActivity implements UploadImageEnabledA binding.commentCommentEditText.requestFocus(); Utils.showKeyboard(this, new Handler(), binding.commentCommentEditText); + + Giphy.INSTANCE.configure(this, APIUtils.GIPHY_GIF_API_KEY); } private void loadCurrentAccount() { @@ -438,7 +452,7 @@ public class CommentActivity extends BaseActivity implements UploadImageEnabledA .build()) .build(); SendComment.sendComment(this, mExecutor, new Handler(), binding.commentCommentEditText.getText().toString(), - parentFullname, parentDepth, uploadedImages, newAuthenticatorOauthRetrofit, selectedAccount, + parentFullname, parentDepth, uploadedImages, giphyGif, newAuthenticatorOauthRetrofit, selectedAccount, new SendComment.SendCommentListener() { @Override public void sendCommentSuccess(Comment comment) { @@ -599,4 +613,32 @@ public class CommentActivity extends BaseActivity implements UploadImageEnabledA binding.commentAccountNameTextView.setText(selectedAccount.getAccountName()); } } -} + + @Override + public void didSearchTerm(@NonNull String s) { + + } + + @Override + public void onGifSelected(@NonNull Media media, @Nullable String s, @NonNull GPHContentType gphContentType) { + this.giphyGif = new GiphyGif(media.getId()); + + int start = Math.max(binding.commentCommentEditText.getSelectionStart(), 0); + int end = Math.max(binding.commentCommentEditText.getSelectionEnd(), 0); + int realStart = Math.min(start, end); + if (realStart > 0 && binding.commentCommentEditText.getText().toString().charAt(realStart - 1) != '\n') { + binding.commentCommentEditText.getText().replace(realStart, Math.max(start, end), + "\n![gif](" + giphyGif.id + ")\n", + 0, "\n![gif]()\n".length() + giphyGif.id.length()); + } else { + binding.commentCommentEditText.getText().replace(realStart, Math.max(start, end), + "![gif](" + giphyGif.id + ")\n", + 0, "![gif]()\n".length() + giphyGif.id.length()); + } + } + + @Override + public void onDismissed(@NonNull GPHContentType gphContentType) { + + } +} \ No newline at end of file diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/PostPollActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/PostPollActivity.java index 7bd24b47..09c6de41 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/PostPollActivity.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/PostPollActivity.java @@ -620,41 +620,6 @@ public class PostPollActivity extends BaseActivity implements FlairBottomSheetFr mPostingSnackbar.show(); - /*Intent intent = new Intent(this, SubmitPostService.class); - intent.putExtra(SubmitPostService.EXTRA_ACCOUNT, selectedAccount); - intent.putExtra(SubmitPostService.EXTRA_SUBREDDIT_NAME, subredditName); - intent.putExtra(SubmitPostService.EXTRA_POST_TYPE, SubmitPostService.EXTRA_POST_TYPE_POLL); - - PollPayload payload; - if (!binding.postContentEditTextPostPollActivity.getText().toString().isEmpty()) { - if (uploadedImages.isEmpty()) { - payload = new PollPayload(subredditName, binding.postTitleEditTextPostPollActivity.getText().toString(), - optionList.toArray(new String[0]), (int) binding.votingLengthSliderPostPollActivity.getValue(), isNSFW, isSpoiler, flair, - null, binding.postContentEditTextPostPollActivity.getText().toString(), - binding.receivePostReplyNotificationsSwitchMaterialPostPollActivity.isChecked(), - subredditIsUser ? "profile" : "subreddit"); - } else { - try { - payload = new PollPayload(subredditName, binding.postTitleEditTextPostPollActivity.getText().toString(), - optionList.toArray(new String[0]), (int) binding.votingLengthSliderPostPollActivity.getValue(), isNSFW, isSpoiler, flair, - new RichTextJSONConverter().constructRichTextJSON(this, binding.postContentEditTextPostPollActivity.getText().toString(), uploadedImages), - null, binding.receivePostReplyNotificationsSwitchMaterialPostPollActivity.isChecked(), - subredditIsUser ? "profile" : "subreddit"); - } catch (JSONException e) { - Snackbar.make(binding.coordinatorLayoutPostPollActivity, R.string.convert_to_richtext_json_failed, Snackbar.LENGTH_SHORT).show(); - return; - } - } - } else { - payload = new PollPayload(subredditName, binding.postTitleEditTextPostPollActivity.getText().toString(), - optionList.toArray(new String[0]), (int) binding.votingLengthSliderPostPollActivity.getValue(), isNSFW, isSpoiler, flair, - binding.receivePostReplyNotificationsSwitchMaterialPostPollActivity.isChecked(), - subredditIsUser ? "profile" : "subreddit"); - } - intent.putExtra(SubmitPostService.EXTRA_POLL_PAYLOAD, new Gson().toJson(payload)); - - ContextCompat.startForegroundService(this, intent);*/ - PersistableBundle extras = new PersistableBundle(); extras.putString(SubmitPostService.EXTRA_ACCOUNT, selectedAccount.getJSONModel()); extras.putString(SubmitPostService.EXTRA_SUBREDDIT_NAME, subredditName); diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/MarkdownBottomBarRecyclerViewAdapter.java b/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/MarkdownBottomBarRecyclerViewAdapter.java index 73405851..4b57a3e7 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/MarkdownBottomBarRecyclerViewAdapter.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/MarkdownBottomBarRecyclerViewAdapter.java @@ -31,28 +31,38 @@ public class MarkdownBottomBarRecyclerViewAdapter extends RecyclerView.Adapter uploadedImages, + List uploadedImages, @Nullable GiphyGif giphyGif, Retrofit newAuthenticatorOauthRetrofit, Account account, SendCommentListener sendCommentListener) { Map headers = APIUtils.getOAuthHeader(account.getAccessToken()); Map params = new HashMap<>(); params.put(APIUtils.API_TYPE_KEY, "json"); params.put(APIUtils.RETURN_RTJSON_KEY, "true"); - if (!uploadedImages.isEmpty()) { + if (!uploadedImages.isEmpty() || giphyGif != null) { try { - params.put(APIUtils.RICHTEXT_JSON_KEY, new RichTextJSONConverter().constructRichTextJSON(context, commentMarkdown, uploadedImages)); + params.put(APIUtils.RICHTEXT_JSON_KEY, new RichTextJSONConverter().constructRichTextJSON(context, commentMarkdown, uploadedImages, giphyGif)); params.put(APIUtils.TEXT_KEY, ""); } catch (JSONException e) { sendCommentListener.sendCommentFailed(context.getString(R.string.convert_to_richtext_json_failed)); diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/GiphyGifBlock.java b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/GiphyGifBlock.java new file mode 100644 index 00000000..3f48d147 --- /dev/null +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/GiphyGifBlock.java @@ -0,0 +1,13 @@ +package ml.docilealligator.infinityforreddit.markdown; + +import org.commonmark.node.CustomBlock; + +import ml.docilealligator.infinityforreddit.thing.GiphyGif; + +public class GiphyGifBlock extends CustomBlock { + public GiphyGif giphyGif; + + public GiphyGifBlock(GiphyGif giphyGif) { + this.giphyGif = giphyGif; + } +} diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/GiphyGifBlockParser.java b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/GiphyGifBlockParser.java new file mode 100644 index 00000000..784a590a --- /dev/null +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/GiphyGifBlockParser.java @@ -0,0 +1,65 @@ +package ml.docilealligator.infinityforreddit.markdown; + +import androidx.annotation.Nullable; + +import org.commonmark.node.Block; +import org.commonmark.parser.block.AbstractBlockParser; +import org.commonmark.parser.block.AbstractBlockParserFactory; +import org.commonmark.parser.block.BlockContinue; +import org.commonmark.parser.block.BlockStart; +import org.commonmark.parser.block.MatchedBlockParser; +import org.commonmark.parser.block.ParserState; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import ml.docilealligator.infinityforreddit.thing.GiphyGif; + +public class GiphyGifBlockParser extends AbstractBlockParser { + private final GiphyGifBlock giphyGifBlock; + + GiphyGifBlockParser(GiphyGif giphyGif) { + this.giphyGifBlock = new GiphyGifBlock(giphyGif); + } + + @Override + public Block getBlock() { + return giphyGifBlock; + } + + @Override + public BlockContinue tryContinue(ParserState parserState) { + return null; + } + + public static class Factory extends AbstractBlockParserFactory { + private final Pattern pattern = Pattern.compile("!\\[gif]\\(giphy\\|\\w+\\|downsized\\)"); + @Nullable + private GiphyGif giphyGif; + + public Factory(@Nullable GiphyGif giphyGif) { + this.giphyGif = giphyGif; + } + + @Override + public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) { + if (giphyGif == null) { + return BlockStart.none(); + } + + String line = state.getLine().toString(); + Matcher matcher = pattern.matcher(line); + if (matcher.find()) { + int startIndex = line.lastIndexOf('('); + if (startIndex > 0) { + int endIndex = line.indexOf(')', startIndex); + String id = line.substring(startIndex + 1, endIndex); + if (giphyGif.id.equals(id)) { + return BlockStart.of(new GiphyGifBlockParser(giphyGif)); + } + } + } + return BlockStart.none(); + } + } +} diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/GiphyGifPlugin.java b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/GiphyGifPlugin.java new file mode 100644 index 00000000..b76fff58 --- /dev/null +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/GiphyGifPlugin.java @@ -0,0 +1,28 @@ +package ml.docilealligator.infinityforreddit.markdown; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.commonmark.parser.Parser; + +import io.noties.markwon.AbstractMarkwonPlugin; +import ml.docilealligator.infinityforreddit.thing.GiphyGif; + +public class GiphyGifPlugin extends AbstractMarkwonPlugin { + private final GiphyGifBlockParser.Factory factory; + + public GiphyGifPlugin(@Nullable GiphyGif giphyGif) { + this.factory = new GiphyGifBlockParser.Factory(giphyGif); + } + + @NonNull + @Override + public String processMarkdown(@NonNull String markdown) { + return super.processMarkdown(markdown); + } + + @Override + public void configureParser(@NonNull Parser.Builder builder) { + builder.customBlockParserFactory(factory); + } +} diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/MarkdownUtils.java b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/MarkdownUtils.java index 1421a00f..e6bb5120 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/MarkdownUtils.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/MarkdownUtils.java @@ -76,6 +76,26 @@ public class MarkdownUtils { .build(); } + @NonNull + public static Markwon createContentSubmissionRedditMarkwon(@NonNull Context context, + @NonNull UploadedImagePlugin uploadedImagePlugin, + @NonNull GiphyGifPlugin giphyGifPlugin) { + return Markwon.builder(context) + .usePlugin(MarkwonInlineParserPlugin.create(plugin -> { + plugin.excludeInlineProcessor(HtmlInlineProcessor.class); + plugin.excludeInlineProcessor(BangInlineProcessor.class); + })) + .usePlugin(SuperscriptPlugin.create()) + .usePlugin(SpoilerParserPlugin.create(0, 0)) + .usePlugin(RedditHeadingPlugin.create()) + .usePlugin(StrikethroughPlugin.create()) + .usePlugin(LinkifyPlugin.create(Linkify.WEB_URLS)) + .usePlugin(giphyGifPlugin) + .usePlugin(uploadedImagePlugin) + .usePlugin(TableEntryPlugin.create(context)) + .build(); + } + @NonNull public static Markwon createContentPreviewRedditMarkwon(@NonNull Context context, @NonNull MarkwonPlugin miscPlugin, diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/RichTextJSONConverter.java b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/RichTextJSONConverter.java index 3ea511be..6e2826a6 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/RichTextJSONConverter.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/RichTextJSONConverter.java @@ -47,6 +47,7 @@ import java.util.Stack; import io.noties.markwon.Markwon; import io.noties.markwon.MarkwonReducer; +import ml.docilealligator.infinityforreddit.thing.GiphyGif; import ml.docilealligator.infinityforreddit.thing.UploadedImage; public class RichTextJSONConverter implements Visitor { @@ -69,6 +70,7 @@ public class RichTextJSONConverter implements Visitor { private static final String SPOILER_E = "spoilertext"; private static final String TABLE_E = "table"; private static final String IMAGE_E = "img"; + private static final String GIF_E = "gif"; private static final String TYPE = "e"; private static final String CONTENT = "c"; @@ -126,6 +128,25 @@ public class RichTextJSONConverter implements Visitor { return richText.toString(); } + public String constructRichTextJSON(Context context, String markdown, + List uploadedImages, @Nullable GiphyGif giphyGif) throws JSONException { + UploadedImagePlugin uploadedImagePlugin = new UploadedImagePlugin(); + uploadedImagePlugin.setUploadedImages(uploadedImages); + Markwon markwon = MarkdownUtils.createContentSubmissionRedditMarkwon( + context, uploadedImagePlugin, new GiphyGifPlugin(giphyGif)); + + List nodes = MarkwonReducer.directChildren().reduce(markwon.parse(markdown)); + + JSONObject richText = new JSONObject(); + + for (Node n : nodes) { + n.accept(this); + } + + richText.put(DOCUMENT, document); + return richText.toString(); + } + public JSONObject constructRichTextJSON(List nodes) throws JSONException { JSONObject richText = new JSONObject(); @@ -579,6 +600,17 @@ public class RichTextJSONConverter implements Visitor { nodeJSON.put(IMAGE_ID, ((UploadedImageBlock) customBlock).uploadeImage.imageUrlOrKey); nodeJSON.put(CONTENT, ((UploadedImageBlock) customBlock).uploadeImage.getCaption()); + document.put(nodeJSON); + } catch (JSONException e) { + throw new RuntimeException(e); + } + } else if (customBlock instanceof GiphyGifBlock) { + //Nothing is allowed inside this block. + try { + JSONObject nodeJSON = new JSONObject(); + nodeJSON.put(TYPE, GIF_E); + nodeJSON.put(IMAGE_ID, ((GiphyGifBlock) customBlock).giphyGif.id); + document.put(nodeJSON); } catch (JSONException e) { throw new RuntimeException(e); diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/thing/GiphyGif.java b/app/src/main/java/ml/docilealligator/infinityforreddit/thing/GiphyGif.java new file mode 100644 index 00000000..ef0b7ff0 --- /dev/null +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/thing/GiphyGif.java @@ -0,0 +1,8 @@ +package ml.docilealligator.infinityforreddit.thing; + +public class GiphyGif { + public final String id; + public GiphyGif(String id) { + this.id = "giphy|" + id + "|downsized"; + } +} diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/utils/APIUtils.java b/app/src/main/java/ml/docilealligator/infinityforreddit/utils/APIUtils.java index 9db267a4..d561629a 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/utils/APIUtils.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/utils/APIUtils.java @@ -32,6 +32,7 @@ public class APIUtils { public static final String IMGUR_CLIENT_ID = "Client-ID cc671794e0ab397"; public static final String REDGIFS_CLIENT_ID = "1828d0bcc93-15ac-bde6-0005-d2ecbe8daab3"; public static final String REDGIFS_CLIENT_SECRET = "TJBlw7jRXW65NAGgFBtgZHu97WlzRXHYybK81sZ9dLM="; + public static final String GIPHY_GIF_API_KEY = ""; public static final String RESPONSE_TYPE_KEY = "response_type"; public static final String RESPONSE_TYPE = "code"; public static final String STATE_KEY = "state"; diff --git a/app/src/main/res/drawable/ic_gif_24dp.xml b/app/src/main/res/drawable/ic_gif_24dp.xml new file mode 100644 index 00000000..00646422 --- /dev/null +++ b/app/src/main/res/drawable/ic_gif_24dp.xml @@ -0,0 +1,9 @@ + + + -- cgit v1.2.3