From bfc362ef6225d1764801378c5923e67d9ca5e5f3 Mon Sep 17 00:00:00 2001 From: Thomas Date: Thu, 14 Dec 2023 16:55:49 +0100 Subject: [PATCH] Automatically split long copied/pasted messages into threads --- .../mastodon/activities/ComposeActivity.java | 5 +- .../mastodon/helper/ComposeHelper.java | 98 +++++++++++ .../mastodon/ui/drawer/ComposeAdapter.java | 163 +++++++++--------- 3 files changed, 182 insertions(+), 84 deletions(-) create mode 100644 app/src/main/java/app/fedilab/android/mastodon/helper/ComposeHelper.java diff --git a/app/src/main/java/app/fedilab/android/mastodon/activities/ComposeActivity.java b/app/src/main/java/app/fedilab/android/mastodon/activities/ComposeActivity.java index a1ce331f0..cf9c72f5c 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/activities/ComposeActivity.java +++ b/app/src/main/java/app/fedilab/android/mastodon/activities/ComposeActivity.java @@ -774,12 +774,15 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana } @Override - public void onItemDraftAdded(int position) { + public void onItemDraftAdded(int position, String initialContent) { Status status = new Status(); status.id = Helper.generateIdString(); status.mentions = statusList.get(position - 1).mentions; status.visibility = statusList.get(position - 1).visibility; + if(initialContent != null) { + status.text = initialContent; + } final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ComposeActivity.this); boolean unlistedReplies = sharedpreferences.getBoolean(getString(R.string.SET_UNLISTED_REPLIES), true); if (status.visibility.equalsIgnoreCase("public") && unlistedReplies) { diff --git a/app/src/main/java/app/fedilab/android/mastodon/helper/ComposeHelper.java b/app/src/main/java/app/fedilab/android/mastodon/helper/ComposeHelper.java new file mode 100644 index 000000000..faec45de5 --- /dev/null +++ b/app/src/main/java/app/fedilab/android/mastodon/helper/ComposeHelper.java @@ -0,0 +1,98 @@ +package app.fedilab.android.mastodon.helper; +/* Copyright 2023 Thomas Schneider + * + * This file is a part of Fedilab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Fedilab; if not, + * see . */ + +import static app.fedilab.android.mastodon.helper.Helper.mentionLongPattern; +import static app.fedilab.android.mastodon.helper.Helper.mentionPattern; + +import java.util.ArrayList; +import java.util.regex.Matcher; + +public class ComposeHelper { + + + /** + * Allows to split the toot by dot "." for sentences - adds number at the end automatically + * + * @param content String initial content + * @param maxChars int the max chars per toot + * @return ArrayList split toot + */ + public static ArrayList splitToots(String content, int maxChars) { + String[] splitContent = content.split("\\s"); + + + ArrayList mentions = new ArrayList<>(); + int mentionLength = 0; + StringBuilder mentionString = new StringBuilder(); + Matcher matcher = mentionLongPattern.matcher(content); + while (matcher.find()) { + String mentionLong = matcher.group(1); + if (!mentions.contains(mentionLong)) { + mentions.add(mentionLong); + } + } + matcher = mentionPattern.matcher(content); + while (matcher.find()) { + String mention = matcher.group(1); + if (!mentions.contains(mention)) { + mentions.add(mention); + } + } + for (String mention : mentions) { + mentionString.append(mention).append(" "); + } + mentionLength = mentionString.length() + 1; + int maxCharsPerMessage = maxChars - mentionLength; + int totalCurrent = 0; + ArrayList reply = new ArrayList<>(); + int index = 0; + for (String s : splitContent) { + if ((totalCurrent + s.length() + 1) < maxCharsPerMessage) { + totalCurrent += (s.length() + 1); + } else { + if (content.length() > totalCurrent && totalCurrent > 0) { + String tempContent = content.substring(0, (totalCurrent)); + content = content.substring(totalCurrent); + + reply.add(index, tempContent); + index++; + totalCurrent = s.length() + 1; + } + } + } + if (totalCurrent > 0) { + reply.add(index, content); + } + if (reply.size() > 1) { + int i = 0; + for (String r : reply) { + if (mentions.size() > 0) { + String tmpMention = mentionString.toString(); + for (String mention : mentions) { + if (r.contains(mention)) { + tmpMention = tmpMention.replace(mention, ""); + } + } + reply.set(i, r + " " + tmpMention); + } else { + reply.set(i, r); + } + i++; + } + } + return reply; + } +} diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/ComposeAdapter.java b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/ComposeAdapter.java index ce1227e56..ab73f1a02 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/ComposeAdapter.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/ComposeAdapter.java @@ -89,6 +89,7 @@ import java.util.Date; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; +import java.util.Objects; import java.util.Random; import java.util.Set; import java.util.regex.Matcher; @@ -117,6 +118,7 @@ import app.fedilab.android.mastodon.client.entities.app.Languages; import app.fedilab.android.mastodon.client.entities.app.Quotes; import app.fedilab.android.mastodon.client.entities.app.StatusDraft; import app.fedilab.android.mastodon.exception.DBException; +import app.fedilab.android.mastodon.helper.ComposeHelper; import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.mastodon.helper.MastodonHelper; import app.fedilab.android.mastodon.helper.ThemeHelper; @@ -467,7 +469,7 @@ public class ComposeAdapter extends RecyclerView.Adapter { - manageDrafts.onItemDraftAdded(statusList.size()); + manageDrafts.onItemDraftAdded(statusList.size(), null); buttonVisibility(holder); }); } @@ -475,7 +477,7 @@ public class ComposeAdapter extends RecyclerView.Adapter { - manageDrafts.onItemDraftAdded(statusList.size()); + manageDrafts.onItemDraftAdded(statusList.size(), null); buttonVisibility(holder); }); } @@ -523,6 +525,7 @@ public class ComposeAdapter extends RecyclerView.Adapter max_car) { + proceedToSplit[0] = true; + ArrayList splitText = ComposeHelper.splitToots(s.toString(), max_car); + int statusListSize = statusList.size(); + int i = 0; + for(String message: splitText) { + if(i==0) { + i++; + continue; + } + manageDrafts.onItemDraftAdded(statusListSize+(i-1), message); + buttonVisibility(holder); + i++; + } + } } @Override public void afterTextChanged(Editable s) { + String contentString = s.toString(); + if(proceedToSplit[0]) { + int max_car = MastodonHelper.getInstanceMaxChars(context); + ArrayList splitText = ComposeHelper.splitToots(contentString, max_car); + contentString = splitText.get(0); + } int currentLength = MastodonHelper.countLength(holder); if (promptDraftListener != null) { promptDraftListener.promptDraft(); @@ -553,22 +579,23 @@ public class ComposeAdapter extends RecyclerView.Adapter mentions = new ArrayList<>(); String mentionPattern = "@[a-z0-9_]+(@[a-z0-9.\\-]+[a-z0-9]+)?"; final Pattern mPattern = Pattern.compile(mentionPattern, Pattern.CASE_INSENSITIVE); @@ -634,8 +661,8 @@ public class ComposeAdapter extends RecyclerView.Adapter mentions = new ArrayList<>(); String mentionPattern = "@[a-z0-9_]+(@[a-z0-9.\\-]+[a-z0-9]+)?"; final Pattern mPattern = Pattern.compile(mentionPattern, Pattern.CASE_INSENSITIVE); @@ -694,37 +721,35 @@ public class ComposeAdapter extends RecyclerView.Adapter s.toString().length()) { + if (currentCursorPosition[0] - (searchLength[0] - 1) < 0 || currentCursorPosition[0] == 0 || currentCursorPosition[0] > contentString.length()) { updateCharacterCount(holder); return; } - Matcher mathsPatterns = Helper.mathsComposePattern.matcher((s.toString())); + Matcher mathsPatterns = Helper.mathsComposePattern.matcher((contentString)); if (mathsPatterns.find()) { if (holder.binding.laTexViewContainer.getChildCount() == 0) { MathJaxConfig mathJaxConfig = new MathJaxConfig(); mathJaxConfig.setAutomaticLinebreaks(true); mathJaxConfig.setInput(TeX); switch (context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) { - case Configuration.UI_MODE_NIGHT_YES: - mathJaxConfig.setTextColor("white"); - break; - case Configuration.UI_MODE_NIGHT_NO: - mathJaxConfig.setTextColor("black"); - break; + case Configuration.UI_MODE_NIGHT_YES -> + mathJaxConfig.setTextColor("white"); + case Configuration.UI_MODE_NIGHT_NO -> + mathJaxConfig.setTextColor("black"); } statusList.get(holder.getBindingAdapterPosition()).mathJaxView = new MathJaxView(context, mathJaxConfig); holder.binding.laTexViewContainer.addView(statusList.get(holder.getBindingAdapterPosition()).mathJaxView); holder.binding.laTexViewContainer.setVisibility(View.VISIBLE); } if (statusList.get(holder.getBindingAdapterPosition()).mathJaxView != null) { - statusList.get(holder.getBindingAdapterPosition()).mathJaxView.setInputText(s.toString()); + statusList.get(holder.getBindingAdapterPosition()).mathJaxView.setInputText(contentString); } } else { @@ -733,7 +758,7 @@ public class ComposeAdapter extends RecyclerView.Adapter { holder.binding.buttonVisibility.setIconResource(R.drawable.ic_compose_visibility_public); statusDraft.visibility = MastodonHelper.visibility.PUBLIC.name(); - break; - case "unlisted": + } + case "unlisted" -> { holder.binding.buttonVisibility.setIconResource(R.drawable.ic_compose_visibility_unlisted); statusDraft.visibility = MastodonHelper.visibility.UNLISTED.name(); - break; - case "private": + } + case "private" -> { holder.binding.buttonVisibility.setIconResource(R.drawable.ic_compose_visibility_private); statusDraft.visibility = MastodonHelper.visibility.PRIVATE.name(); - break; - case "direct": + } + case "direct" -> { holder.binding.buttonVisibility.setIconResource(R.drawable.ic_compose_visibility_direct); statusDraft.visibility = MastodonHelper.visibility.DIRECT.name(); - break; + } } holder.binding.visibilityPanel.setOnTouchListener((view, motionEvent) -> true); @@ -1582,7 +1607,10 @@ public class ComposeAdapter extends RecyclerView.Adapter { if (inputContentInfo != null) { Uri uri = inputContentInfo.getContentUri(); @@ -1694,7 +1722,6 @@ public class ComposeAdapter extends RecyclerView.Adapter composePollBinding.pollDuration.setSelection(0); + case 1800 -> composePollBinding.pollDuration.setSelection(1); + case 3600 -> composePollBinding.pollDuration.setSelection(2); + case 21600 -> composePollBinding.pollDuration.setSelection(3); + case 86400 -> composePollBinding.pollDuration.setSelection(4); + case 259200 -> composePollBinding.pollDuration.setSelection(5); + case 604800 -> composePollBinding.pollDuration.setSelection(6); } if (statusDraft.poll.multiple) composePollBinding.pollType.check(R.id.poll_type_multiple); @@ -1980,40 +1993,24 @@ public class ComposeAdapter extends RecyclerView.Adapter 300; + case 1 -> 1800; + case 2 -> 3600; + case 3 -> 21600; + case 4 -> 86400; + case 5 -> 259200; + case 6 -> 604800; + default -> 864000; + }; statusDraft.poll.expire_in = expire; if (promptDraftListener != null) { promptDraftListener.promptDraft(); @@ -2094,7 +2091,7 @@ public class ComposeAdapter extends RecyclerView.Adapter