From 5d54567812f75e7156ecbdb16da2499ca468622d Mon Sep 17 00:00:00 2001 From: Harshad Vedartham Date: Mon, 8 Jan 2024 15:53:06 -0800 Subject: [PATCH] Additional change to how we do action buttons --- .../activity/DocumentShareIntoFragment.java | 223 +++++++++++------- .../markdown/MarkdownActionButtons.java | 16 ++ .../frontend/AttachLinkOrFileDialog.java | 48 ++-- .../markor/frontend/MarkorDialogFactory.java | 4 +- .../frontend/textview/TextViewUtils.java | 10 +- .../net/gsantner/opoc/format/GsTextUtils.java | 6 +- .../res/values/string-not_translatable.xml | 1 + .../main/res/xml/prefactions_share_into.xml | 5 + 8 files changed, 199 insertions(+), 114 deletions(-) diff --git a/app/src/main/java/net/gsantner/markor/activity/DocumentShareIntoFragment.java b/app/src/main/java/net/gsantner/markor/activity/DocumentShareIntoFragment.java index cfe4bbaa8d..56d129dfa8 100644 --- a/app/src/main/java/net/gsantner/markor/activity/DocumentShareIntoFragment.java +++ b/app/src/main/java/net/gsantner/markor/activity/DocumentShareIntoFragment.java @@ -11,16 +11,21 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.graphics.Typeface; import android.os.Bundle; import android.text.TextUtils; +import android.util.Pair; import android.util.Patterns; import android.util.TypedValue; import android.view.View; +import android.widget.EditText; +import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.fragment.app.FragmentTransaction; +import androidx.preference.CheckBoxPreference; import androidx.preference.Preference; import androidx.preference.PreferenceGroup; @@ -29,9 +34,11 @@ import net.gsantner.markor.format.FormatRegistry; import net.gsantner.markor.format.plaintext.PlaintextSyntaxHighlighter; import net.gsantner.markor.format.todotxt.TodoTxtTask; +import net.gsantner.markor.frontend.AttachLinkOrFileDialog; import net.gsantner.markor.frontend.NewFileDialog; import net.gsantner.markor.frontend.filebrowser.MarkorFileBrowserFactory; import net.gsantner.markor.frontend.textview.HighlightingEditor; +import net.gsantner.markor.frontend.textview.TextViewUtils; import net.gsantner.markor.model.AppSettings; import net.gsantner.markor.model.Document; import net.gsantner.markor.util.MarkorContextUtils; @@ -39,11 +46,15 @@ import net.gsantner.opoc.frontend.base.GsPreferenceFragmentBase; import net.gsantner.opoc.frontend.filebrowser.GsFileBrowserListAdapter; import net.gsantner.opoc.frontend.filebrowser.GsFileBrowserOptions; +import net.gsantner.opoc.util.GsCollectionUtils; +import net.gsantner.opoc.util.GsFileUtils; +import net.gsantner.opoc.wrapper.GsCallback; import net.gsantner.opoc.wrapper.GsTextWatcherAdapter; import java.io.File; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -53,11 +64,11 @@ public class DocumentShareIntoFragment extends MarkorBaseFragment { public static final String EXTRA_SHARED_TEXT = "EXTRA_SHARED_TEXT"; public static final String TEXT_TOKEN = "{{text}}"; - public static DocumentShareIntoFragment newInstance(Intent intent) { + public static DocumentShareIntoFragment newInstance(final Intent intent) { final DocumentShareIntoFragment f = new DocumentShareIntoFragment(); final Bundle args = new Bundle(); - final String sharedText = formatLink(intent.getStringExtra(Intent.EXTRA_SUBJECT), intent.getStringExtra(Intent.EXTRA_TEXT)); + final String sharedText = extractShareText(intent); final Object intentFile = intent.getSerializableExtra(Document.EXTRA_FILE); if (intentFile instanceof File && ((File) intentFile).isDirectory()) { @@ -70,8 +81,6 @@ public static DocumentShareIntoFragment newInstance(Intent intent) { } private File workingDir; - private HighlightingEditor _hlEditor; - private ShareIntoImportOptionsFragment _shareIntoImportOptionsFragment; public DocumentShareIntoFragment() { } @@ -84,36 +93,34 @@ protected int getLayoutResId() { @Override public void onViewCreated(final @NonNull View view, final Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - _hlEditor = view.findViewById(R.id.document__fragment__share_into__highlighting_editor); - _hlEditor.addTextChangedListener(GsTextWatcherAdapter.on((ctext, arg2, arg3, arg4) -> onTextChanged(ctext))); + HighlightingEditor _hlEditor = view.findViewById(R.id.document__fragment__share_into__highlighting_editor); final String sharedText = (getArguments() != null ? getArguments().getString(EXTRA_SHARED_TEXT, "") : "").trim(); + final ShareIntoImportOptionsFragment _shareIntoImportOptionsFragment; if (_savedInstanceState == null) { FragmentTransaction t = getChildFragmentManager().beginTransaction(); - _shareIntoImportOptionsFragment = ShareIntoImportOptionsFragment.newInstance(sharedText); + _shareIntoImportOptionsFragment = new ShareIntoImportOptionsFragment(); _shareIntoImportOptionsFragment.setWorkingDir(workingDir); t.replace(R.id.document__share_into__fragment__placeholder_fragment, _shareIntoImportOptionsFragment, ShareIntoImportOptionsFragment.TAG).commit(); } else { _shareIntoImportOptionsFragment = (ShareIntoImportOptionsFragment) getChildFragmentManager().findFragmentByTag(ShareIntoImportOptionsFragment.TAG); } - _hlEditor.setText(sharedText); + if (_shareIntoImportOptionsFragment != null) { + _shareIntoImportOptionsFragment.setEditor(_hlEditor); + } + _hlEditor.setTextSize(TypedValue.COMPLEX_UNIT_SP, _appSettings.getFontSize()); _hlEditor.setTypeface(Typeface.create(_appSettings.getFontFamily(), Typeface.NORMAL)); _hlEditor.setHighlighter(new PlaintextSyntaxHighlighter(_appSettings)); _hlEditor.setHighlightingEnabled(true); + _hlEditor.setText(sharedText); if (sharedText.isEmpty()) { _hlEditor.requestFocus(); } } - public void onTextChanged(CharSequence text) { - if (_shareIntoImportOptionsFragment != null) { - _shareIntoImportOptionsFragment.setText(text.toString()); - } - } - @Override public String getFragmentTag() { return FRAGMENT_TAG; @@ -127,27 +134,22 @@ public boolean onBackPressed() { public static class ShareIntoImportOptionsFragment extends GsPreferenceFragmentBase { public static final String TAG = "ShareIntoImportOptionsFragment"; - private static final String EXTRA_TEXT = Intent.EXTRA_TEXT; private File workingDir; + private EditText _editor = null; + @Override public boolean isDividerVisible() { return true; } - public static ShareIntoImportOptionsFragment newInstance(String sharedText) { - ShareIntoImportOptionsFragment f = new ShareIntoImportOptionsFragment(); - Bundle bundle = new Bundle(); - bundle.putString(EXTRA_TEXT, sharedText); - f.setArguments(bundle); - return f; - } - public void setWorkingDir(File dir) { workingDir = dir; } - private String _sharedText = ""; + public void setEditor(final EditText editor) { + _editor = editor; + } @Override public int getPreferenceResourceForInflation() { @@ -167,49 +169,29 @@ protected AppSettings getAppSettings(Context context) { @Override protected void afterOnCreate(Bundle savedInstances, Context context) { super.afterOnCreate(savedInstances, context); - if (getArguments() != null) { - _sharedText = getArguments().getString(EXTRA_TEXT, ""); - } - if (savedInstances != null) { - _sharedText = savedInstances.getString(EXTRA_TEXT, _sharedText); - } doUpdatePreferences(); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - if ((_sharedText.length() * 1.05) < 8200) { - outState.putString(EXTRA_TEXT, _sharedText); - } - } - public void setText(String text) { - _sharedText = text; - if (isAdded()) { - doUpdatePreferences(); + if (_editor != null) { + final CheckBoxPreference asLinkPref = (CheckBoxPreference) findPreference(R.string.pref_key__attach_as_link); + if (asLinkPref != null) { + asLinkPref.setVisible(!findLinksAndPaths(_editor.getText()).isEmpty()); + asLinkPref.setChecked(true); + _editor.addTextChangedListener(GsTextWatcherAdapter.on((ctext, arg2, arg3, arg4) -> + asLinkPref.setVisible(!findLinksAndPaths(ctext).isEmpty()))); + } } } - // Check set type and fallback to ext check - private boolean isTodoTxt(final Document doc) { - final int format = _appSettings.getDocumentFormat(doc.getPath(), FormatRegistry.FORMAT_UNKNOWN); - if (format != FormatRegistry.FORMAT_UNKNOWN) { - return format == FormatRegistry.FORMAT_TODOTXT; - } - return doc.getFormat() == FormatRegistry.FORMAT_TODOTXT; + private boolean shareAsLink() { + final CheckBoxPreference asLinkPref = (CheckBoxPreference) findPreference(R.string.pref_key__attach_as_link); + return asLinkPref != null && asLinkPref.isVisible() && asLinkPref.isEnabled() && asLinkPref.isChecked(); } private void appendToExistingDocumentAndClose(final File file, final boolean showEditor) { final Activity context = getActivity(); final Document document = new Document(file); - final String formatted; - if (isTodoTxt(document)) { - final String date = _appSettings.getDocumentAutoFormatEnabled(document.getPath()) ? TodoTxtTask.getToday() : ""; - formatted = date + " " + _sharedText.replaceAll("\\n+", " "); - } else { - formatted = formatShare(_sharedText); - } + final int format = _appSettings.getDocumentFormat(document.getPath(), document.getFormat()); + final String formatted = getFormatted(shareAsLink(), file, format); final String oldContent = document.loadContent(context); if (oldContent != null) { @@ -227,6 +209,83 @@ private void appendToExistingDocumentAndClose(final File file, final boolean sho } } + /** + * Iterates over each line of the text and checks if it is a path or link + * The whole line has to be a path or link to match. + * + * @param text Text to parse + * @return List of pairs. + * The first element of the pair is the start and end index of the link/path. + * Second element is true if it is a path, false if it is a link. + */ + public static List> findLinksAndPaths(final CharSequence text) { + final List> links = new ArrayList<>(); + GsTextUtils.forEachline(text, (line, start, end) -> { + start = TextViewUtils.getNextNonWhitespace(text, start); + end = TextViewUtils.getLastNonWhitespace(text, end) + 1; + if (start != -1 && end != -1 && start <= end) { + final String tl = text.subSequence(start, end).toString(); + final boolean hasSpaces = tl.contains(" ") || tl.contains("\\t"); + if (!hasSpaces && Patterns.WEB_URL.matcher(tl).matches()) { + links.add(Pair.create(new int[]{start, end}, false)); + } else { + try { + if (new File(tl.replace("%20", " ")).exists()) { + links.add(Pair.create(new int[]{start, end}, true)); + } + } catch (NullPointerException ignored) { + } + } + } + return true; + }); + return links; + } + + public static String getLinkTitle(final String link) { + final Matcher m = Patterns.WEB_URL.matcher(link); + if (m.matches()) { + final String title = m.group(4); + return (title != null && title.endsWith(".")) ? title.substring(0, title.length() - 1) : title; + } + return ""; + } + + private String getFormatted(final boolean asLink, final File src, final int format) { + final String text = _editor.getText().toString(); + String formatted = text; + if (asLink) { + final StringBuilder sb = new StringBuilder(text); + // Replace all links + final List> links = findLinksAndPaths(sb); + Collections.reverse(links); + for (final Pair link : links) { + final String linkText = text.substring(link.first[0], link.first[1]); + final String title, linkPath; + if (link.second) { + final File file = new File(linkText.replace("%20", " ")); + title = file.getName(); + linkPath = GsFileUtils.relativePath(src, file); + } else { + title = getLinkTitle(linkText); + linkPath = linkText; + } + final String fmtLine = AttachLinkOrFileDialog.formatLink(title, linkPath, format); + sb.replace(link.first[0], link.first[1], fmtLine); + } + + formatted = sb.toString(); + } + + formatted = formatShare(formatted); + + if (format == FormatRegistry.FORMAT_TODOTXT) { + formatted = TodoTxtTask.getToday() + " " + formatted.replaceAll("\\n", " "); + } + + return formatted; + } + private String formatShare(final String shared) { final Context context = getContext(); final String prefix = _appSettings.getShareIntoPrefix(); @@ -328,13 +387,14 @@ private void showInDocumentActivity(final Document document) { @SuppressWarnings({"ConstantConditions", "ConstantIfStatement"}) public Boolean onPreferenceClicked(Preference preference, String key, int keyId) { final Activity activity = getActivity(); + final String text = _editor.getText().toString(); final MarkorContextUtils shu = new MarkorContextUtils(activity); String tmps; boolean close = false; switch (keyId) { case R.string.pref_key__share_into__clipboard: { - shu.setClipboard(getContext(), _sharedText); + shu.setClipboard(getContext(), text); close = true; break; } @@ -357,19 +417,19 @@ public Boolean onPreferenceClicked(Preference preference, String key, int keyId) break; } case R.string.pref_key__share_into__open_in_browser: { - if ((tmps = GsTextUtils.tryExtractUrlAroundPos(_sharedText, _sharedText.length())) != null) { + if ((tmps = GsTextUtils.tryExtractUrlAroundPos(text, text.length())) != null) { _cu.openWebpageInExternalBrowser(getActivity(), tmps); close = true; } break; } case R.string.pref_key__share_into__reshare: { - shu.shareText(getActivity(), _sharedText, null); + shu.shareText(getActivity(), text, null); close = true; break; } case R.string.pref_key__share_into__calendar_event: { - if (shu.createCalendarAppointment(getActivity(), null, _sharedText, null)) { + if (shu.createCalendarAppointment(getActivity(), null, text, null)) { close = true; } else { Toast.makeText(getContext(), R.string.no_calendar_app_is_installed, Toast.LENGTH_SHORT).show(); @@ -394,7 +454,8 @@ public Boolean onPreferenceClicked(Preference preference, String key, int keyId) @Override public void doUpdatePreferences() { super.doUpdatePreferences(); - final boolean maybeHasWebUrl = _sharedText.contains("http://") || _sharedText.contains("https://"); + final String text = _editor.getText().toString(); + final boolean maybeHasWebUrl = text.contains("http://") || text.contains("https://"); setPreferenceVisible(R.string.pref_key__share_into__open_in_browser, maybeHasWebUrl); } @@ -410,36 +471,16 @@ private void addDocumentToPrefgroup(String filepath, final PreferenceGroup prefG } } - /** - * Convert text and link into a formatted link, if the text and string appear to be a link - * - * @param text Link description - * @param link Link url - * @return formatted URL of format [text](url) - */ - private static String formatLink(String text, String link) { - link = link == null ? "" : link; - text = text == null ? "" : text; - - final String formattedLink; - final Matcher linkMatch = Patterns.WEB_URL.matcher(link.trim()); - if (linkMatch.matches() && !link.trim().matches("\\s") && !text.trim().matches("\\s")) { - // Get a resonable default text if one is not present. group 4 is the domain name - try { - text = TextUtils.isEmpty(text) ? linkMatch.group(4).replaceAll("\\.$", "") : text; - } catch (IllegalStateException | IndexOutOfBoundsException e) { - text = ""; - } - - link = link.replaceAll("(?m)(?<=&|\\?)(utm_|source|si|__mk_|ref|sprefix|crid|partner|promo|ad_sub|gclid|fbclid|msclkid).*?(&|$|\\s|\\))", ""); + private static String sanitize(final String link) { + return link.replaceAll("(?m)(?<=&|\\?)(utm_|source|si|__mk_|ref|sprefix|crid|partner|promo|ad_sub|gclid|fbclid|msclkid).*?(&|$|\\s|\\))", ""); + } - formattedLink = String.format("[%s](%s )", - text.trim().replace("[", "\\[").replace("]", "\\]").replace("|", "/"), - link.trim().replace("(", "\\(").replace(")", "\\)") - ); - } else { - formattedLink = text + " " + link; + private static String extractShareText(final Intent intent) { + String link = intent.getStringExtra(Intent.EXTRA_TEXT); + link = link != null ? link.trim() : ""; + if (Patterns.WEB_URL.matcher(link).matches()) { + link = sanitize(link); } - return formattedLink; + return link; } } diff --git a/app/src/main/java/net/gsantner/markor/format/markdown/MarkdownActionButtons.java b/app/src/main/java/net/gsantner/markor/format/markdown/MarkdownActionButtons.java index 909e4e96b6..20c94ee39f 100644 --- a/app/src/main/java/net/gsantner/markor/format/markdown/MarkdownActionButtons.java +++ b/app/src/main/java/net/gsantner/markor/format/markdown/MarkdownActionButtons.java @@ -9,6 +9,7 @@ import android.annotation.SuppressLint; import android.content.Context; +import android.text.Editable; import android.view.KeyEvent; import androidx.annotation.NonNull; @@ -171,10 +172,25 @@ public boolean onActionClick(final @StringRes int action) { * @param delim - Delimiter to surround text with */ private void runLineSurroundAction(final Pattern pattern, final String delim) { + final int[] sel = TextViewUtils.getSelection(_hlEditor); + final String lineBefore = sel[0] == sel[1] ? TextViewUtils.getSelectedLines(_hlEditor, sel[0]) : null; + runRegexReplaceAction( new ReplacePattern(pattern, "$1$2$4$6"), new ReplacePattern(LINE_NONE, "$1$2" + delim + "$3" + delim + "$4") ); + + // This logic sets the cursor to the inside of the delimiters if the delimiters were empty + if (lineBefore != null) { + final String lineAfter = TextViewUtils.getSelectedLines(_hlEditor, sel[0]); + final String pair = delim + delim; + if (lineAfter.length() - lineBefore.length() == pair.length() && lineAfter.trim().endsWith(pair)) { + final Editable text = _hlEditor.getText(); + final int end = TextViewUtils.getLineEnd(text, sel[0]); + final int ns = TextViewUtils.getLastNonWhitespace(text, end) - delim.length(); + _hlEditor.setSelection(ns); + } + } } @SuppressLint("NonConstantResourceId") diff --git a/app/src/main/java/net/gsantner/markor/frontend/AttachLinkOrFileDialog.java b/app/src/main/java/net/gsantner/markor/frontend/AttachLinkOrFileDialog.java index 65c4a39520..7fc3c09094 100644 --- a/app/src/main/java/net/gsantner/markor/frontend/AttachLinkOrFileDialog.java +++ b/app/src/main/java/net/gsantner/markor/frontend/AttachLinkOrFileDialog.java @@ -27,6 +27,7 @@ import net.gsantner.markor.R; import net.gsantner.markor.format.FormatRegistry; import net.gsantner.markor.format.markdown.MarkdownSyntaxHighlighter; +import net.gsantner.markor.format.todotxt.TodoTxtTask; import net.gsantner.markor.frontend.filebrowser.MarkorFileBrowserFactory; import net.gsantner.markor.frontend.filesearch.FileSearchDialog; import net.gsantner.markor.frontend.filesearch.FileSearchEngine; @@ -36,7 +37,6 @@ import net.gsantner.markor.util.MarkorContextUtils; import net.gsantner.opoc.format.GsTextUtils; import net.gsantner.opoc.frontend.GsAudioRecordOmDialog; -import net.gsantner.opoc.frontend.filebrowser.GsFileBrowserListAdapter; import net.gsantner.opoc.frontend.filebrowser.GsFileBrowserOptions; import net.gsantner.opoc.util.GsFileUtils; import net.gsantner.opoc.wrapper.GsCallback; @@ -66,6 +66,8 @@ private static String getLinkFormat(final int textFormatId) { return "{{LINK|TITLE}}"; } else if (textFormatId == FormatRegistry.FORMAT_ASCIIDOC) { return "link:LINK[TITLE]"; + } else if (textFormatId == FormatRegistry.FORMAT_TODOTXT) { + return "TITLE:LINK"; } else { return "TITLE"; } @@ -113,7 +115,7 @@ public static void showInsertImageOrLinkDialog( } else { m = null; } - if (m != null && m.find()) { + if (m != null && m.find() && sel[0] >= m.start() && sel[1] <= m.end()) { inputPathName.setText(m.group(1)); inputPathUrl.setText((m.group(2))); sel[0] = m.start() + lineSel[0]; @@ -226,6 +228,33 @@ private static GsCallback.b2 getFilterForAction(final InsertType } } + public static String formatLink(String title, String path, final int textFormatId) { + return formatLink(title, path, textFormatId, InsertType.LINK_DIALOG); + } + + private static String formatLink(String title, String path, final int textFormatId, final InsertType action) { + title = title.trim().replace(")", "\\)"); + path = path.trim().replace(")", "\\)") + // Workaround for parser - cannot deal with spaces and have other entities problems + .replace(" ", "%20") + // Disable space encoding for Jekyll + .replace("{{%20site.baseurl%20}}", "{{ site.baseurl }}"); + + String newText = getTemplateForAction(action, textFormatId) + .replace("TITLE", title) + .replace("LINK", path); + + if (textFormatId == FormatRegistry.FORMAT_WIKITEXT && newText.endsWith("|]]")) { + newText = newText.replaceFirst("\\|]]$", "]]"); + } + + if (textFormatId == FormatRegistry.FORMAT_TODOTXT) { + newText = newText.replaceAll("\\n", " "); + } + + return newText; + } + private static void insertItem( final InsertType action, final int textFormatId, @@ -251,20 +280,7 @@ private static void insertItem( return; } - title = title.trim().replace(")", "\\)"); - path = path.trim().replace(")", "\\)") - // Workaround for parser - cannot deal with spaces and have other entities problems - .replace(" ", "%20") - // Disable space encoding for Jekyll - .replace("{{%20site.baseurl%20}}", "{{ site.baseurl }}"); - - String newText = getTemplateForAction(action, textFormatId) - .replace("TITLE", title) - .replace("LINK", path); - - if (textFormatId == FormatRegistry.FORMAT_WIKITEXT && newText.endsWith("|]]")) { - newText = newText.replaceFirst("\\|]]$", "]]"); - } + final String newText = formatLink(title, path, textFormatId, action); if (!newText.equals(text.subSequence(sel[0], sel[1]).toString())) { text.replace(sel[0], sel[1], newText); diff --git a/app/src/main/java/net/gsantner/markor/frontend/MarkorDialogFactory.java b/app/src/main/java/net/gsantner/markor/frontend/MarkorDialogFactory.java index 051d32fa40..58b24bd73e 100644 --- a/app/src/main/java/net/gsantner/markor/frontend/MarkorDialogFactory.java +++ b/app/src/main/java/net/gsantner/markor/frontend/MarkorDialogFactory.java @@ -145,7 +145,7 @@ public static void showSearchFilesDialog( FileSearchDialog.showDialog(activity, searchOptions -> { searchOptions.rootSearchDir = searchDir; FileSearchEngine.queueFileSearch(activity, searchOptions, searchResults -> - FileSearchResultSelectorDialog.showDialog(activity, searchResults, () -> callback)); + FileSearchResultSelectorDialog.showDialog(activity, searchResults, callback)); }); } } @@ -542,6 +542,7 @@ public static void showDocumentChecklistDialog( lines.add(line); indices.add(cs + start); } + return true; }); final DialogOptions dopt = new DialogOptions(); @@ -732,6 +733,7 @@ public static void showHeadlineDialog( if (level > 0) { headings.add(new Heading(level, text.subSequence(start, end), line)); } + return true; }); // List of levels present in text diff --git a/app/src/main/java/net/gsantner/markor/frontend/textview/TextViewUtils.java b/app/src/main/java/net/gsantner/markor/frontend/textview/TextViewUtils.java index 6bc15d3cc4..9b07785c84 100644 --- a/app/src/main/java/net/gsantner/markor/frontend/textview/TextViewUtils.java +++ b/app/src/main/java/net/gsantner/markor/frontend/textview/TextViewUtils.java @@ -99,10 +99,12 @@ public static int getLastNonWhitespace(final CharSequence s) { } public static int getLastNonWhitespace(final CharSequence s, final int end) { - for (int i = Math.min(s.length() - 1, end); i >= 0; i--) { - char c = s.charAt(i); - if (c != ' ' && c != '\t') { - return i; + if (s != null) { + for (int i = Math.min(s.length() - 1, end); i >= 0; i--) { + char c = s.charAt(i); + if (c != ' ' && c != '\t') { + return i; + } } } return -1; diff --git a/app/src/main/java/net/gsantner/opoc/format/GsTextUtils.java b/app/src/main/java/net/gsantner/opoc/format/GsTextUtils.java index b6f3d48dc1..bb6b7b6e76 100644 --- a/app/src/main/java/net/gsantner/opoc/format/GsTextUtils.java +++ b/app/src/main/java/net/gsantner/opoc/format/GsTextUtils.java @@ -320,12 +320,14 @@ public static List findChar(final CharSequence text, final char c, fina return posns; } - public static void forEachline(final CharSequence text, GsCallback.a3 callback) { + public static void forEachline(final CharSequence text, GsCallback.b3 callback) { final List ends = findChar(text, '\n'); int start = 0, i = 0; for (; i < ends.size(); i++) { final int end = ends.get(i); - callback.callback(i, start, end); + if (!callback.callback(i, start, end)) { + break; + }; start = end + 1; } callback.callback(i, start, text.length()); diff --git a/app/src/main/res/values/string-not_translatable.xml b/app/src/main/res/values/string-not_translatable.xml index 210be2163b..053fcef1fd 100644 --- a/app/src/main/res/values/string-not_translatable.xml +++ b/app/src/main/res/values/string-not_translatable.xml @@ -159,6 +159,7 @@ work. If not, see . pref_key__share_into__clipboard pref_key__share_into__reshare pref_key__share_into__calendar_event + pref_key__attach_as_link pref_key__more_info__settings pref_key__more_info__source_code pref_key__more_info__project_license diff --git a/app/src/main/res/xml/prefactions_share_into.xml b/app/src/main/res/xml/prefactions_share_into.xml index 7d1283ddcd..51de49796e 100644 --- a/app/src/main/res/xml/prefactions_share_into.xml +++ b/app/src/main/res/xml/prefactions_share_into.xml @@ -12,6 +12,11 @@ android:layout_height="match_parent" android:title="@string/share_to_arrow"> +