diff options
author | Stefan Niedermann <info@niedermann.it> | 2020-12-17 18:02:26 +0300 |
---|---|---|
committer | Niedermann IT-Dienstleistungen <stefan-niedermann@users.noreply.github.com> | 2020-12-17 18:14:29 +0300 |
commit | c69257424bdb319d45c6b48c63fe9cbe734f71a1 (patch) | |
tree | df390a018d62ac6d240aa5c967064110c6de8de6 | |
parent | 6d3377f62b21c97780391603d388c343ed11a8ca (diff) |
Performance improvements
Signed-off-by: Stefan Niedermann <info@niedermann.it>
4 files changed, 116 insertions, 138 deletions
diff --git a/markdown/src/androidTest/java/it/niedermann/android/markdown/MarkwonMarkdownUtilTest.java b/markdown/src/androidTest/java/it/niedermann/android/markdown/MarkwonMarkdownUtilTest.java index 254e3f8e4..026b4fbad 100644 --- a/markdown/src/androidTest/java/it/niedermann/android/markdown/MarkwonMarkdownUtilTest.java +++ b/markdown/src/androidTest/java/it/niedermann/android/markdown/MarkwonMarkdownUtilTest.java @@ -1,5 +1,7 @@ package it.niedermann.android.markdown; +import android.text.SpannableStringBuilder; + import androidx.test.ext.junit.runners.AndroidJUnit4; import junit.framework.TestCase; @@ -131,131 +133,143 @@ public class MarkwonMarkdownUtilTest extends TestCase { @Test public void testTogglePunctuation() { - StringBuilder builder; + SpannableStringBuilder builder; // Add italic - builder = new StringBuilder("Lorem ipsum dolor sit amet."); + builder = new SpannableStringBuilder("Lorem ipsum dolor sit amet."); assertEquals(13, MarkwonMarkdownUtil.togglePunctuation(builder, 6, 11, "*")); assertEquals("Lorem *ipsum* dolor sit amet.", builder.toString()); // Remove italic - builder = new StringBuilder("Lorem *ipsum* dolor sit amet."); + builder = new SpannableStringBuilder("Lorem *ipsum* dolor sit amet."); assertEquals(11, MarkwonMarkdownUtil.togglePunctuation(builder, 7, 12, "*")); assertEquals("Lorem ipsum dolor sit amet.", builder.toString()); // Add bold - builder = new StringBuilder("Lorem ipsum dolor sit amet."); + builder = new SpannableStringBuilder("Lorem ipsum dolor sit amet."); assertEquals(15, MarkwonMarkdownUtil.togglePunctuation(builder, 6, 11, "**")); assertEquals("Lorem **ipsum** dolor sit amet.", builder.toString()); // Remove bold - builder = new StringBuilder("Lorem **ipsum** dolor sit amet."); + builder = new SpannableStringBuilder("Lorem **ipsum** dolor sit amet."); assertEquals(11, MarkwonMarkdownUtil.togglePunctuation(builder, 8, 13, "**")); assertEquals("Lorem ipsum dolor sit amet.", builder.toString()); // Add strike - builder = new StringBuilder("Lorem ipsum dolor sit amet."); + builder = new SpannableStringBuilder("Lorem ipsum dolor sit amet."); assertEquals(15, MarkwonMarkdownUtil.togglePunctuation(builder, 6, 11, "~~")); assertEquals("Lorem ~~ipsum~~ dolor sit amet.", builder.toString()); // Remove strike - builder = new StringBuilder("Lorem ~~ipsum~~ dolor sit amet."); + builder = new SpannableStringBuilder("Lorem ~~ipsum~~ dolor sit amet."); assertEquals(11, MarkwonMarkdownUtil.togglePunctuation(builder, 8, 13, "~~")); assertEquals("Lorem ipsum dolor sit amet.", builder.toString()); // Add italic at first position - builder = new StringBuilder("Lorem ipsum dolor sit amet."); + builder = new SpannableStringBuilder("Lorem ipsum dolor sit amet."); assertEquals(7, MarkwonMarkdownUtil.togglePunctuation(builder, 0, 5, "*")); assertEquals("*Lorem* ipsum dolor sit amet.", builder.toString()); // Remove italic from first position - builder = new StringBuilder("*Lorem* ipsum dolor sit amet."); + builder = new SpannableStringBuilder("*Lorem* ipsum dolor sit amet."); assertEquals(5, MarkwonMarkdownUtil.togglePunctuation(builder, 1, 6, "*")); assertEquals("Lorem ipsum dolor sit amet.", builder.toString()); // Add italic at last position - builder = new StringBuilder("Lorem ipsum dolor sit amet."); + builder = new SpannableStringBuilder("Lorem ipsum dolor sit amet."); assertEquals(29, MarkwonMarkdownUtil.togglePunctuation(builder, 22, 27, "*")); assertEquals("Lorem ipsum dolor sit *amet.*", builder.toString()); // Remove italic from last position - builder = new StringBuilder("Lorem ipsum dolor sit *amet.*"); + builder = new SpannableStringBuilder("Lorem ipsum dolor sit *amet.*"); assertEquals(27, MarkwonMarkdownUtil.togglePunctuation(builder, 23, 28, "*")); assertEquals("Lorem ipsum dolor sit amet.", builder.toString()); // Text is not directly surrounded by punctuation but contains it // Do nothing when the same punctuation is contained only one time - builder = new StringBuilder("Lorem *ipsum dolor sit amet."); + builder = new SpannableStringBuilder("Lorem *ipsum dolor sit amet."); assertEquals(28, MarkwonMarkdownUtil.togglePunctuation(builder, 0, 28, "*")); assertEquals("Lorem *ipsum dolor sit amet.", builder.toString()); // Do nothing when the same punctuation is contained only one time - builder = new StringBuilder("Lorem **ipsum dolor sit amet."); + builder = new SpannableStringBuilder("Lorem **ipsum dolor sit amet."); assertEquals(29, MarkwonMarkdownUtil.togglePunctuation(builder, 0, 29, "**")); assertEquals("Lorem **ipsum dolor sit amet.", builder.toString()); // Remove containing punctuation - builder = new StringBuilder("Lorem *ipsum* dolor sit amet."); + builder = new SpannableStringBuilder("Lorem *ipsum* dolor sit amet."); assertEquals(11, MarkwonMarkdownUtil.togglePunctuation(builder, 6, 13, "*")); assertEquals("Lorem ipsum dolor sit amet.", builder.toString()); // Remove containing punctuation - builder = new StringBuilder("Lorem *ipsum* dolor sit amet."); + builder = new SpannableStringBuilder("Lorem *ipsum* dolor sit amet."); assertEquals(27, MarkwonMarkdownUtil.togglePunctuation(builder, 0, 29, "*")); assertEquals("Lorem ipsum dolor sit amet.", builder.toString()); // Remove multiple containing punctuations - builder = new StringBuilder("Lorem *ipsum* dolor *sit* amet."); + builder = new SpannableStringBuilder("Lorem *ipsum* dolor *sit* amet."); assertEquals(27, MarkwonMarkdownUtil.togglePunctuation(builder, 0, 31, "*")); assertEquals("Lorem ipsum dolor sit amet.", builder.toString()); // Special use-case: toggle from italic to bold and back // TODO Toggle italic on bold text -// builder = new StringBuilder("Lorem **ipsum** dolor sit amet."); +// builder = new SpannableStringBuilder("Lorem **ipsum** dolor sit amet."); // assertEquals(17, MarkwonMarkdownUtil.togglePunctuation(builder, 8, 13, "*")); // assertEquals("Lorem ***ipsum*** dolor sit amet.", builder.toString()); // TODO Toggle bold on italic text -// builder = new StringBuilder("Lorem *ipsum* dolor sit amet."); +// builder = new SpannableStringBuilder("Lorem *ipsum* dolor sit amet."); // assertEquals(17, MarkwonMarkdownUtil.togglePunctuation(builder, 7, 12, "**")); // assertEquals("Lorem ***ipsum*** dolor sit amet.", builder.toString()); // TODO Toggle bold to italic -// builder = new StringBuilder("Lorem **ipsum** dolor sit amet."); +// builder = new SpannableStringBuilder("Lorem **ipsum** dolor sit amet."); // assertEquals(29, MarkwonMarkdownUtil.togglePunctuation(builder, 0, 31, "*")); // assertEquals("Lorem *ipsum* dolor sit amet.", builder.toString()); // TODO Toggle multiple bold parts to italic -// builder = new StringBuilder("Lorem **ipsum** dolor **sit** amet."); +// builder = new SpannableStringBuilder("Lorem **ipsum** dolor **sit** amet."); // assertEquals(31, MarkwonMarkdownUtil.togglePunctuation(builder, 0, 35, "*")); // assertEquals("Lorem *ipsum* dolor *sit* amet.", builder.toString()); } @Test public void testInsertLink() { - StringBuilder builder; + SpannableStringBuilder builder; // Add link without clipboardUrl to normal text - builder = new StringBuilder("Lorem ipsum dolor sit amet."); + builder = new SpannableStringBuilder("Lorem ipsum dolor sit amet."); assertEquals(14, MarkwonMarkdownUtil.insertLink(builder, 6, 11, null)); assertEquals("Lorem [ipsum]() dolor sit amet.", builder.toString()); // Add link without clipboardUrl to url - builder = new StringBuilder("Lorem https://example.com dolor sit amet."); + builder = new SpannableStringBuilder("Lorem https://example.com dolor sit amet."); assertEquals(7, MarkwonMarkdownUtil.insertLink(builder, 6, 25, null)); assertEquals("Lorem [](https://example.com) dolor sit amet.", builder.toString()); + // TODO if space character on one side, make place around it, if not, find word to link it? + // Add link without clipboardUrl to empty selection + builder = new SpannableStringBuilder("Lorem ipsum dolor sit amet."); + assertEquals(13, MarkwonMarkdownUtil.insertLink(builder, 12, 12, null)); + assertEquals("Lorem ipsum []()dolor sit amet.", builder.toString()); + // Add link with clipboardUrl to normal text - builder = new StringBuilder("Lorem ipsum dolor sit amet."); + builder = new SpannableStringBuilder("Lorem ipsum dolor sit amet."); assertEquals(33, MarkwonMarkdownUtil.insertLink(builder, 6, 11, "https://example.com")); assertEquals("Lorem [ipsum](https://example.com) dolor sit amet.", builder.toString()); // Add link with clipboardUrl to url - builder = new StringBuilder("Lorem https://example.com dolor sit amet."); + builder = new SpannableStringBuilder("Lorem https://example.com dolor sit amet."); assertEquals(46, MarkwonMarkdownUtil.insertLink(builder, 6, 25, "https://example.de")); assertEquals("Lorem [https://example.com](https://example.de) dolor sit amet.", builder.toString()); + + // TODO if space character on one side, make place around it, if not, find word to link it? + // Add link with clipboardUrl to empty selection + builder = new SpannableStringBuilder("Lorem ipsum dolor sit amet."); + assertEquals(13, MarkwonMarkdownUtil.insertLink(builder, 12, 12, "https://example.com")); + assertEquals("Lorem ipsum [](https://example.com)dolor sit amet.", builder.toString()); } @Test diff --git a/markdown/src/main/java/it/niedermann/android/markdown/markwon/MarkwonMarkdownUtil.java b/markdown/src/main/java/it/niedermann/android/markdown/markwon/MarkwonMarkdownUtil.java index 8d71ce8e2..d875ba507 100644 --- a/markdown/src/main/java/it/niedermann/android/markdown/markwon/MarkwonMarkdownUtil.java +++ b/markdown/src/main/java/it/niedermann/android/markdown/markwon/MarkwonMarkdownUtil.java @@ -1,6 +1,7 @@ package it.niedermann.android.markdown.markwon; import android.content.Context; +import android.text.Editable; import android.text.TextUtils; import androidx.annotation.NonNull; @@ -113,35 +114,35 @@ public class MarkwonMarkdownUtil { } /** - * Modifies the {@param builder} and adds the given {@param punctuation} from + * Modifies the {@param editable} and adds the given {@param punctuation} from * {@param selectionStart} to {@param selectionEnd} or removes the {@param punctuation} in case * it already is around the selected part. * * @return the new cursor position */ - public static int togglePunctuation(@NonNull StringBuilder builder, int selectionStart, int selectionEnd, @NonNull String punctuation) { + public static int togglePunctuation(@NonNull Editable editable, int selectionStart, int selectionEnd, @NonNull String punctuation) { switch (punctuation) { case "**": case "__": case "*": case "_": case "~~": { - final String text = builder.toString(); + final String text = editable.toString(); final boolean selectionIsSurroundedByPunctuation = selectionIsSurroundedByPunctuation(text, selectionStart, selectionEnd, punctuation); if (selectionIsSurroundedByPunctuation) { - builder.delete(selectionEnd, selectionEnd + punctuation.length()); - builder.delete(selectionStart - punctuation.length(), selectionStart); + editable.delete(selectionEnd, selectionEnd + punctuation.length()); + editable.delete(selectionStart - punctuation.length(), selectionStart); return selectionEnd - punctuation.length(); } else { final int containedPunctuationCount = getContainedPunctuationCount(text, selectionStart, selectionEnd, punctuation); if (containedPunctuationCount == 0) { - builder.insert(selectionEnd, punctuation); - builder.insert(selectionStart, punctuation); + editable.insert(selectionEnd, punctuation); + editable.insert(selectionStart, punctuation); return selectionEnd + punctuation.length() * 2; } else if (containedPunctuationCount % 2 > 0) { return selectionEnd; } else { - removeContainingPunctuation(builder, selectionStart, selectionEnd, punctuation); + removeContainingPunctuation(editable, selectionStart, selectionEnd, punctuation); return selectionEnd - containedPunctuationCount * punctuation.length(); } } @@ -152,34 +153,38 @@ public class MarkwonMarkdownUtil { } /** - * Inserts a link into the given {@param builder} from {@param selectionStart} to {@param selectionEnd} and uses the {@param clipboardUrl} if available. + * Inserts a link into the given {@param editable} from {@param selectionStart} to {@param selectionEnd} and uses the {@param clipboardUrl} if available. * * @return the new cursor position */ - public static int insertLink(@NonNull StringBuilder builder, int selectionStart, int selectionEnd, @Nullable String clipboardUrl) { - final CharSequence text = builder.toString(); - final boolean textToFormatIsLink = TextUtils.indexOf(text.subSequence(selectionStart, selectionEnd), "http") == 0; - if (textToFormatIsLink) { - if (clipboardUrl == null) { - builder.insert(selectionEnd, ")"); - builder.insert(selectionStart, "[]("); - } else { - builder.insert(selectionEnd, "](" + clipboardUrl + ")"); - builder.insert(selectionStart, "["); - selectionEnd += clipboardUrl.length(); - } + public static int insertLink(@NonNull Editable editable, int selectionStart, int selectionEnd, @Nullable String clipboardUrl) { + if (selectionStart == selectionEnd) { + editable.insert(selectionStart, "[](" + (clipboardUrl == null ? "" : clipboardUrl) + ")"); + return selectionStart + 1; } else { - if (clipboardUrl == null) { - builder.insert(selectionEnd, "]()"); + final boolean textToFormatIsLink = TextUtils.indexOf(editable.subSequence(selectionStart, selectionEnd), "http") == 0; + if (textToFormatIsLink) { + if (clipboardUrl == null) { + editable.insert(selectionEnd, ")"); + editable.insert(selectionStart, "[]("); + } else { + editable.insert(selectionEnd, "](" + clipboardUrl + ")"); + editable.insert(selectionStart, "["); + selectionEnd += clipboardUrl.length(); + } } else { - builder.insert(selectionEnd, "](" + clipboardUrl + ")"); - selectionEnd += clipboardUrl.length(); + if (clipboardUrl == null) { + editable.insert(selectionEnd, "]()"); + } else { + editable.insert(selectionEnd, "](" + clipboardUrl + ")"); + selectionEnd += clipboardUrl.length(); + } + editable.insert(selectionStart, "["); } - builder.insert(selectionStart, "["); + return textToFormatIsLink && clipboardUrl == null + ? selectionStart + 1 + : selectionEnd + 3; } - return textToFormatIsLink && clipboardUrl == null - ? selectionStart + 1 - : selectionEnd + 3; } /** @@ -206,11 +211,11 @@ public class MarkwonMarkdownUtil { return counter; } - private static void removeContainingPunctuation(@NonNull StringBuilder builder, int start, int end, @NonNull String punctuation) { - final Matcher matcher = Pattern.compile(Pattern.quote(punctuation)).matcher(builder.toString().subSequence(start, end)); + private static void removeContainingPunctuation(@NonNull Editable editable, int start, int end, @NonNull String punctuation) { + final Matcher matcher = Pattern.compile(Pattern.quote(punctuation)).matcher(editable.toString().subSequence(start, end)); int countDeletedPunctuations = 0; while (matcher.find()) { - builder.delete(start + matcher.start() - countDeletedPunctuations * punctuation.length(), start + matcher.end() - countDeletedPunctuations * punctuation.length()); + editable.delete(start + matcher.start() - countDeletedPunctuations * punctuation.length(), start + matcher.end() - countDeletedPunctuations * punctuation.length()); countDeletedPunctuations++; } } diff --git a/markdown/src/main/java/it/niedermann/android/markdown/markwon/format/ContextBasedFormattingCallback.java b/markdown/src/main/java/it/niedermann/android/markdown/markwon/format/ContextBasedFormattingCallback.java index 6bed4b1db..2918b1314 100644 --- a/markdown/src/main/java/it/niedermann/android/markdown/markwon/format/ContextBasedFormattingCallback.java +++ b/markdown/src/main/java/it/niedermann/android/markdown/markwon/format/ContextBasedFormattingCallback.java @@ -1,7 +1,6 @@ package it.niedermann.android.markdown.markwon.format; -import android.text.SpannableStringBuilder; -import android.text.TextUtils; +import android.text.Editable; import android.util.Log; import android.view.ActionMode; import android.view.Menu; @@ -9,6 +8,7 @@ import android.view.MenuItem; import it.niedermann.android.markdown.R; import it.niedermann.android.markdown.markwon.MarkwonMarkdownEditor; +import it.niedermann.android.markdown.markwon.MarkwonMarkdownUtil; import it.niedermann.android.markdown.markwon.model.EListType; import it.niedermann.android.util.ClipboardUtil; @@ -54,70 +54,26 @@ public class ContextBasedFormattingCallback implements ActionMode.Callback { @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - int itemId = item.getItemId(); - if (itemId == R.id.checkbox) { - insertCheckbox(); - return true; - } else if (itemId == R.id.link) { - insertLink(); - return true; - } - return false; - } - - private void insertCheckbox() { - final CharSequence text = editText.getText(); - if (text == null) { - editText.setMarkdownString(EListType.DASH.checkboxUncheckedWithTrailingSpace); - editText.setSelection(EListType.DASH.checkboxUncheckedWithTrailingSpace.length()); - } else { - final int originalCursorPosition = editText.getSelectionStart(); - final int startOfLine = getStartOfLine(text, originalCursorPosition); - Log.i(TAG, "Inserting checkbox at position " + startOfLine); - final CharSequence part1 = text.subSequence(0, startOfLine); - final CharSequence part2 = text.subSequence(startOfLine, text.length()); - editText.setMarkdownString(TextUtils.concat(part1, EListType.DASH.checkboxUncheckedWithTrailingSpace, part2)); - editText.setSelection(originalCursorPosition + EListType.DASH.checkboxUncheckedWithTrailingSpace.length()); - } - } - - private void insertLink() { - final CharSequence text = editText.getText(); - final SpannableStringBuilder ssb = new SpannableStringBuilder(text); - final boolean textToFormatIsLink; - final int start; - int end; - if (text == null) { - start = end = 0; - textToFormatIsLink = false; - } else { - start = text.length(); - end = start; - textToFormatIsLink = TextUtils.indexOf(text.subSequence(start, end), "http") == 0; - } + final Editable editable = editText.getText(); + if (editable != null) { + final int itemId = item.getItemId(); + final int cursorPosition = editText.getSelectionStart(); - if (textToFormatIsLink) { - Log.i(TAG, "Inserting link description for position " + start + " to " + end); - ssb.insert(end, ")"); - ssb.insert(start, "[]("); - } else { - String clipboardURL = ClipboardUtil.INSTANCE.getClipboardURLorNull(editText.getContext()); - if (clipboardURL != null) { - Log.i(TAG, "Inserting link from clipboard at position " + start + " to " + end + ": " + clipboardURL); - ssb.insert(end, "](" + clipboardURL + ")"); - end += clipboardURL.length(); - } else { - Log.i(TAG, "Inserting empty link for position " + start + " to " + end); - ssb.insert(end, "]()"); + if (itemId == R.id.checkbox) { + editable.insert(getStartOfLine(editable, cursorPosition), EListType.DASH.checkboxUncheckedWithTrailingSpace); + editText.setMarkdownStringModel(editable); + editText.setSelection(cursorPosition + EListType.DASH.checkboxUncheckedWithTrailingSpace.length()); + return true; + } else if (itemId == R.id.link) { + final int newSelection = MarkwonMarkdownUtil.insertLink(editable, cursorPosition, cursorPosition, ClipboardUtil.INSTANCE.getClipboardURLorNull(editText.getContext())); + editText.setMarkdownStringModel(editable); + editText.setSelection(newSelection); + return true; } - ssb.insert(start, "["); - } - editText.setMarkdownString(ssb); - if (textToFormatIsLink) { - editText.setSelection(start + 1); } else { - editText.setSelection(end + 3); // after <end>]( + Log.e(TAG, "Editable is null"); } + return false; } @Override diff --git a/markdown/src/main/java/it/niedermann/android/markdown/markwon/format/ContextBasedRangeFormattingCallback.java b/markdown/src/main/java/it/niedermann/android/markdown/markwon/format/ContextBasedRangeFormattingCallback.java index 6cca65e43..94c614c90 100644 --- a/markdown/src/main/java/it/niedermann/android/markdown/markwon/format/ContextBasedRangeFormattingCallback.java +++ b/markdown/src/main/java/it/niedermann/android/markdown/markwon/format/ContextBasedRangeFormattingCallback.java @@ -69,26 +69,29 @@ public class ContextBasedRangeFormattingCallback implements ActionMode.Callback @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { final Editable editable = editText.getText(); - final StringBuilder ssb = new StringBuilder(editable == null ? "" : editable.toString()); - final int itemId = item.getItemId(); - final int start = editText.getSelectionStart(); - int end = editText.getSelectionEnd(); + if (editable != null) { + final int itemId = item.getItemId(); + final int start = editText.getSelectionStart(); + final int end = editText.getSelectionEnd(); - if (itemId == R.id.bold) { - final int newSelection = MarkwonMarkdownUtil.togglePunctuation(ssb, start, end, "**"); - editText.setMarkdownString(ssb); - editText.setSelection(newSelection); - return true; - } else if (itemId == R.id.italic) { - final int newSelection = MarkwonMarkdownUtil.togglePunctuation(ssb, start, end, "*"); - editText.setMarkdownString(ssb); - editText.setSelection(newSelection); - return true; - } else if (itemId == R.id.link) { - final int newSelection = MarkwonMarkdownUtil.insertLink(ssb, start, end, ClipboardUtil.INSTANCE.getClipboardURLorNull(editText.getContext())); - editText.setMarkdownString(ssb); - editText.setSelection(newSelection); - return true; + if (itemId == R.id.bold) { + final int newSelection = MarkwonMarkdownUtil.togglePunctuation(editable, start, end, "**"); + editText.setMarkdownStringModel(editable); + editText.setSelection(newSelection); + return true; + } else if (itemId == R.id.italic) { + final int newSelection = MarkwonMarkdownUtil.togglePunctuation(editable, start, end, "*"); + editText.setMarkdownStringModel(editable); + editText.setSelection(newSelection); + return true; + } else if (itemId == R.id.link) { + final int newSelection = MarkwonMarkdownUtil.insertLink(editable, start, end, ClipboardUtil.INSTANCE.getClipboardURLorNull(editText.getContext())); + editText.setMarkdownStringModel(editable); + editText.setSelection(newSelection); + return true; + } + } else { + Log.e(TAG, "Editable is null"); } return false; } |