Loading app/src/main/java/it/niedermann/owncloud/notes/edit/NotePreviewFragment.java +17 −4 Original line number Diff line number Diff line Loading @@ -35,9 +35,13 @@ import com.yydcdut.markdown.syntax.text.TextFactory; import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.databinding.FragmentNotePreviewBinding; import it.niedermann.owncloud.notes.persistence.NotesDatabase; import it.niedermann.owncloud.notes.shared.model.DBNote; import it.niedermann.owncloud.notes.shared.util.MarkDownUtil; import it.niedermann.owncloud.notes.shared.util.NoteLinksUtils; import it.niedermann.owncloud.notes.shared.util.SSOUtil; import it.niedermann.owncloud.notes.shared.util.text.NoteLinksProcessor; import it.niedermann.owncloud.notes.shared.util.text.TextProcessorChain; import it.niedermann.owncloud.notes.shared.util.text.WwwLinksProcessor; import static it.niedermann.owncloud.notes.shared.util.DisplayUtils.searchAndColor; import static it.niedermann.owncloud.notes.shared.util.MarkDownUtil.CHECKBOX_CHECKED_MINUS; Loading @@ -46,7 +50,6 @@ import static it.niedermann.owncloud.notes.shared.util.MarkDownUtil.CHECKBOX_UNC import static it.niedermann.owncloud.notes.shared.util.MarkDownUtil.CHECKBOX_UNCHECKED_STAR; import static it.niedermann.owncloud.notes.shared.util.MarkDownUtil.parseCompat; import static it.niedermann.owncloud.notes.shared.util.NoteLinksUtils.extractNoteRemoteId; import static it.niedermann.owncloud.notes.shared.util.NoteLinksUtils.replaceNoteLinksWithDummyUrls; import static it.niedermann.owncloud.notes.shared.util.NoteUtil.getFontSizeFromPreferences; public class NotePreviewFragment extends SearchableBaseNoteFragment implements OnRefreshListener { Loading Loading @@ -165,11 +168,13 @@ public class NotePreviewFragment extends SearchableBaseNoteFragment implements O } }) .build()); TextProcessorChain chain = defaultTextProcessorChain(note); try { binding.singleNoteContent.setText(parseCompat(markdownProcessor, replaceNoteLinksWithDummyUrls(note.getContent(), db.getRemoteIds(note.getAccountId())))); binding.singleNoteContent.setText(parseCompat(markdownProcessor, chain.apply(note.getContent()))); } catch (StringIndexOutOfBoundsException e) { // Workaround for RxMarkdown: https://github.com/stefan-niedermann/nextcloud-notes/issues/668 binding.singleNoteContent.setText(replaceNoteLinksWithDummyUrls(note.getContent(), db.getRemoteIds(note.getAccountId()))); binding.singleNoteContent.setText(chain.apply(note.getContent())); Toast.makeText(binding.singleNoteContent.getContext(), R.string.could_not_load_preview_two_digit_numbered_list, Toast.LENGTH_LONG).show(); e.printStackTrace(); } Loading Loading @@ -205,11 +210,12 @@ public class NotePreviewFragment extends SearchableBaseNoteFragment implements O if (db.getNoteServerSyncHelper().isSyncPossible() && SSOUtil.isConfigured(getContext())) { binding.swiperefreshlayout.setRefreshing(true); try { TextProcessorChain chain = defaultTextProcessorChain(note); SingleSignOnAccount ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(requireContext()); db.getNoteServerSyncHelper().addCallbackPull(ssoAccount, () -> { note = db.getNote(note.getAccountId(), note.getId()); changedText = note.getContent(); binding.singleNoteContent.setText(parseCompat(markdownProcessor, replaceNoteLinksWithDummyUrls(note.getContent(), db.getRemoteIds(note.getAccountId())))); binding.singleNoteContent.setText(parseCompat(markdownProcessor, chain.apply(note.getContent()))); binding.swiperefreshlayout.setRefreshing(false); }); db.getNoteServerSyncHelper().scheduleSync(ssoAccount, false); Loading @@ -227,4 +233,11 @@ public class NotePreviewFragment extends SearchableBaseNoteFragment implements O super.applyBrand(mainColor, textColor); binding.singleNoteContent.setHighlightColor(getTextHighlightBackgroundColor(requireContext(), mainColor, colorPrimary, colorAccent)); } private TextProcessorChain defaultTextProcessorChain(DBNote note) { TextProcessorChain chain = new TextProcessorChain(); chain.add(new NoteLinksProcessor(db.getRemoteIds(note.getAccountId()))); chain.add(new WwwLinksProcessor()); return chain; } } app/src/main/java/it/niedermann/owncloud/notes/shared/util/NoteLinksUtils.java +6 −46 Original line number Diff line number Diff line package it.niedermann.owncloud.notes.shared.util; import android.text.TextUtils; import androidx.annotation.VisibleForTesting; import java.util.HashSet; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import it.niedermann.owncloud.notes.shared.util.text.NoteLinksProcessor; public class NoteLinksUtils { @VisibleForTesting static final String RELATIVE_LINK_WORKAROUND_PREFIX = "https://nextcloudnotes/notes/"; private static final String linksThatLookLikeNoteLinksRegEx = "\\[[^]]*]\\((\\d+)\\)"; private static final String replaceNoteRemoteIdsRegEx = "\\[([^\\]]*)\\]\\((%s)\\)"; /** * Replaces all links to other notes of the form `[<link-text>](<note-file-id>)` * in the markdown string with links to a dummy url. * * Why is this needed? * See discussion in issue #623 * * @return Markdown with all note-links replaced with dummy-url-links */ public static String replaceNoteLinksWithDummyUrls(String markdown, Set<String> existingNoteRemoteIds) { Pattern noteLinkCandidates = Pattern.compile(linksThatLookLikeNoteLinksRegEx); Matcher matcher = noteLinkCandidates.matcher(markdown); Set<String> noteRemoteIdsToReplace = new HashSet<>(); while (matcher.find()) { String presumedNoteId = matcher.group(1); if (existingNoteRemoteIds.contains(presumedNoteId)) { noteRemoteIdsToReplace.add(presumedNoteId); } } String noteRemoteIdsCondition = TextUtils.join("|", noteRemoteIdsToReplace); Pattern replacePattern = Pattern.compile(String.format(replaceNoteRemoteIdsRegEx, noteRemoteIdsCondition)); Matcher replaceMatcher = replacePattern.matcher(markdown); return replaceMatcher.replaceAll(String.format("[$1](%s$2)", RELATIVE_LINK_WORKAROUND_PREFIX)); } /** * Tests if the given link is a note-link (which was transformed in {@link #replaceNoteLinksWithDummyUrls}) or not * Tests if the given link is a note-link (which was transformed in {@link it.niedermann.owncloud.notes.shared.util.text.NoteLinksProcessor}) or not * * @param link Link under test * @return true if the link is a note-link */ public static boolean isNoteLink(String link) { return link.startsWith(RELATIVE_LINK_WORKAROUND_PREFIX); return link.startsWith(NoteLinksProcessor.RELATIVE_LINK_WORKAROUND_PREFIX); } /** * Extracts the remoteId back from links that were transformed in {@link #replaceNoteLinksWithDummyUrls}. * Extracts the remoteId back from links that were transformed in {@link it.niedermann.owncloud.notes.shared.util.text.NoteLinksProcessor}. * * @param link Link that was transformed in {@link #replaceNoteLinksWithDummyUrls} * @param link Link that was transformed in {@link it.niedermann.owncloud.notes.shared.util.text.NoteLinksProcessor} * @return the remoteId of the linked note */ public static long extractNoteRemoteId(String link) { return Long.parseLong(link.replace(RELATIVE_LINK_WORKAROUND_PREFIX, "")); return Long.parseLong(link.replace(NoteLinksProcessor.RELATIVE_LINK_WORKAROUND_PREFIX, "")); } } app/src/main/java/it/niedermann/owncloud/notes/shared/util/text/NoteLinksProcessor.java 0 → 100644 +57 −0 Original line number Diff line number Diff line package it.niedermann.owncloud.notes.shared.util.text; import android.text.TextUtils; import java.util.HashSet; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import androidx.annotation.VisibleForTesting; public class NoteLinksProcessor extends TextProcessor { public static final String RELATIVE_LINK_WORKAROUND_PREFIX = "https://nextcloudnotes/notes/"; @VisibleForTesting private static final String linksThatLookLikeNoteLinksRegEx = "\\[[^]]*]\\((\\d+)\\)"; private static final String replaceNoteRemoteIdsRegEx = "\\[([^\\]]*)\\]\\((%s)\\)"; private Set<String> existingNoteRemoteIds; public NoteLinksProcessor(Set<String> existingNoteRemoteIds) { this.existingNoteRemoteIds = existingNoteRemoteIds; } /** * Replaces all links to other notes of the form `[<link-text>](<note-file-id>)` * in the markdown string with links to a dummy url. * * Why is this needed? * See discussion in issue #623 * * @return Markdown with all note-links replaced with dummy-url-links */ @Override public String process(String s) { return replaceNoteLinksWithDummyUrls(s, existingNoteRemoteIds); } private static String replaceNoteLinksWithDummyUrls(String markdown, Set<String> existingNoteRemoteIds) { Pattern noteLinkCandidates = Pattern.compile(linksThatLookLikeNoteLinksRegEx); Matcher matcher = noteLinkCandidates.matcher(markdown); Set<String> noteRemoteIdsToReplace = new HashSet<>(); while (matcher.find()) { String presumedNoteId = matcher.group(1); if (existingNoteRemoteIds.contains(presumedNoteId)) { noteRemoteIdsToReplace.add(presumedNoteId); } } String noteRemoteIdsCondition = TextUtils.join("|", noteRemoteIdsToReplace); Pattern replacePattern = Pattern.compile(String.format(replaceNoteRemoteIdsRegEx, noteRemoteIdsCondition)); Matcher replaceMatcher = replacePattern.matcher(markdown); return replaceMatcher.replaceAll(String.format("[$1](%s$2)", RELATIVE_LINK_WORKAROUND_PREFIX)); } } app/src/main/java/it/niedermann/owncloud/notes/shared/util/text/TextProcessor.java 0 → 100644 +10 −0 Original line number Diff line number Diff line package it.niedermann.owncloud.notes.shared.util.text; abstract public class TextProcessor { /** * Applies a specified transformation on a text string and returns the updated string. * @param s Text to transform * @return Transformed text */ abstract public String process(String s); } No newline at end of file app/src/main/java/it/niedermann/owncloud/notes/shared/util/text/TextProcessorChain.java 0 → 100644 +12 −0 Original line number Diff line number Diff line package it.niedermann.owncloud.notes.shared.util.text; import java.util.LinkedList; public class TextProcessorChain extends LinkedList<TextProcessor> { public String apply(String s) { for (TextProcessor textProcessor : this) { s = textProcessor.process(s); } return s; } } Loading
app/src/main/java/it/niedermann/owncloud/notes/edit/NotePreviewFragment.java +17 −4 Original line number Diff line number Diff line Loading @@ -35,9 +35,13 @@ import com.yydcdut.markdown.syntax.text.TextFactory; import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.databinding.FragmentNotePreviewBinding; import it.niedermann.owncloud.notes.persistence.NotesDatabase; import it.niedermann.owncloud.notes.shared.model.DBNote; import it.niedermann.owncloud.notes.shared.util.MarkDownUtil; import it.niedermann.owncloud.notes.shared.util.NoteLinksUtils; import it.niedermann.owncloud.notes.shared.util.SSOUtil; import it.niedermann.owncloud.notes.shared.util.text.NoteLinksProcessor; import it.niedermann.owncloud.notes.shared.util.text.TextProcessorChain; import it.niedermann.owncloud.notes.shared.util.text.WwwLinksProcessor; import static it.niedermann.owncloud.notes.shared.util.DisplayUtils.searchAndColor; import static it.niedermann.owncloud.notes.shared.util.MarkDownUtil.CHECKBOX_CHECKED_MINUS; Loading @@ -46,7 +50,6 @@ import static it.niedermann.owncloud.notes.shared.util.MarkDownUtil.CHECKBOX_UNC import static it.niedermann.owncloud.notes.shared.util.MarkDownUtil.CHECKBOX_UNCHECKED_STAR; import static it.niedermann.owncloud.notes.shared.util.MarkDownUtil.parseCompat; import static it.niedermann.owncloud.notes.shared.util.NoteLinksUtils.extractNoteRemoteId; import static it.niedermann.owncloud.notes.shared.util.NoteLinksUtils.replaceNoteLinksWithDummyUrls; import static it.niedermann.owncloud.notes.shared.util.NoteUtil.getFontSizeFromPreferences; public class NotePreviewFragment extends SearchableBaseNoteFragment implements OnRefreshListener { Loading Loading @@ -165,11 +168,13 @@ public class NotePreviewFragment extends SearchableBaseNoteFragment implements O } }) .build()); TextProcessorChain chain = defaultTextProcessorChain(note); try { binding.singleNoteContent.setText(parseCompat(markdownProcessor, replaceNoteLinksWithDummyUrls(note.getContent(), db.getRemoteIds(note.getAccountId())))); binding.singleNoteContent.setText(parseCompat(markdownProcessor, chain.apply(note.getContent()))); } catch (StringIndexOutOfBoundsException e) { // Workaround for RxMarkdown: https://github.com/stefan-niedermann/nextcloud-notes/issues/668 binding.singleNoteContent.setText(replaceNoteLinksWithDummyUrls(note.getContent(), db.getRemoteIds(note.getAccountId()))); binding.singleNoteContent.setText(chain.apply(note.getContent())); Toast.makeText(binding.singleNoteContent.getContext(), R.string.could_not_load_preview_two_digit_numbered_list, Toast.LENGTH_LONG).show(); e.printStackTrace(); } Loading Loading @@ -205,11 +210,12 @@ public class NotePreviewFragment extends SearchableBaseNoteFragment implements O if (db.getNoteServerSyncHelper().isSyncPossible() && SSOUtil.isConfigured(getContext())) { binding.swiperefreshlayout.setRefreshing(true); try { TextProcessorChain chain = defaultTextProcessorChain(note); SingleSignOnAccount ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(requireContext()); db.getNoteServerSyncHelper().addCallbackPull(ssoAccount, () -> { note = db.getNote(note.getAccountId(), note.getId()); changedText = note.getContent(); binding.singleNoteContent.setText(parseCompat(markdownProcessor, replaceNoteLinksWithDummyUrls(note.getContent(), db.getRemoteIds(note.getAccountId())))); binding.singleNoteContent.setText(parseCompat(markdownProcessor, chain.apply(note.getContent()))); binding.swiperefreshlayout.setRefreshing(false); }); db.getNoteServerSyncHelper().scheduleSync(ssoAccount, false); Loading @@ -227,4 +233,11 @@ public class NotePreviewFragment extends SearchableBaseNoteFragment implements O super.applyBrand(mainColor, textColor); binding.singleNoteContent.setHighlightColor(getTextHighlightBackgroundColor(requireContext(), mainColor, colorPrimary, colorAccent)); } private TextProcessorChain defaultTextProcessorChain(DBNote note) { TextProcessorChain chain = new TextProcessorChain(); chain.add(new NoteLinksProcessor(db.getRemoteIds(note.getAccountId()))); chain.add(new WwwLinksProcessor()); return chain; } }
app/src/main/java/it/niedermann/owncloud/notes/shared/util/NoteLinksUtils.java +6 −46 Original line number Diff line number Diff line package it.niedermann.owncloud.notes.shared.util; import android.text.TextUtils; import androidx.annotation.VisibleForTesting; import java.util.HashSet; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import it.niedermann.owncloud.notes.shared.util.text.NoteLinksProcessor; public class NoteLinksUtils { @VisibleForTesting static final String RELATIVE_LINK_WORKAROUND_PREFIX = "https://nextcloudnotes/notes/"; private static final String linksThatLookLikeNoteLinksRegEx = "\\[[^]]*]\\((\\d+)\\)"; private static final String replaceNoteRemoteIdsRegEx = "\\[([^\\]]*)\\]\\((%s)\\)"; /** * Replaces all links to other notes of the form `[<link-text>](<note-file-id>)` * in the markdown string with links to a dummy url. * * Why is this needed? * See discussion in issue #623 * * @return Markdown with all note-links replaced with dummy-url-links */ public static String replaceNoteLinksWithDummyUrls(String markdown, Set<String> existingNoteRemoteIds) { Pattern noteLinkCandidates = Pattern.compile(linksThatLookLikeNoteLinksRegEx); Matcher matcher = noteLinkCandidates.matcher(markdown); Set<String> noteRemoteIdsToReplace = new HashSet<>(); while (matcher.find()) { String presumedNoteId = matcher.group(1); if (existingNoteRemoteIds.contains(presumedNoteId)) { noteRemoteIdsToReplace.add(presumedNoteId); } } String noteRemoteIdsCondition = TextUtils.join("|", noteRemoteIdsToReplace); Pattern replacePattern = Pattern.compile(String.format(replaceNoteRemoteIdsRegEx, noteRemoteIdsCondition)); Matcher replaceMatcher = replacePattern.matcher(markdown); return replaceMatcher.replaceAll(String.format("[$1](%s$2)", RELATIVE_LINK_WORKAROUND_PREFIX)); } /** * Tests if the given link is a note-link (which was transformed in {@link #replaceNoteLinksWithDummyUrls}) or not * Tests if the given link is a note-link (which was transformed in {@link it.niedermann.owncloud.notes.shared.util.text.NoteLinksProcessor}) or not * * @param link Link under test * @return true if the link is a note-link */ public static boolean isNoteLink(String link) { return link.startsWith(RELATIVE_LINK_WORKAROUND_PREFIX); return link.startsWith(NoteLinksProcessor.RELATIVE_LINK_WORKAROUND_PREFIX); } /** * Extracts the remoteId back from links that were transformed in {@link #replaceNoteLinksWithDummyUrls}. * Extracts the remoteId back from links that were transformed in {@link it.niedermann.owncloud.notes.shared.util.text.NoteLinksProcessor}. * * @param link Link that was transformed in {@link #replaceNoteLinksWithDummyUrls} * @param link Link that was transformed in {@link it.niedermann.owncloud.notes.shared.util.text.NoteLinksProcessor} * @return the remoteId of the linked note */ public static long extractNoteRemoteId(String link) { return Long.parseLong(link.replace(RELATIVE_LINK_WORKAROUND_PREFIX, "")); return Long.parseLong(link.replace(NoteLinksProcessor.RELATIVE_LINK_WORKAROUND_PREFIX, "")); } }
app/src/main/java/it/niedermann/owncloud/notes/shared/util/text/NoteLinksProcessor.java 0 → 100644 +57 −0 Original line number Diff line number Diff line package it.niedermann.owncloud.notes.shared.util.text; import android.text.TextUtils; import java.util.HashSet; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import androidx.annotation.VisibleForTesting; public class NoteLinksProcessor extends TextProcessor { public static final String RELATIVE_LINK_WORKAROUND_PREFIX = "https://nextcloudnotes/notes/"; @VisibleForTesting private static final String linksThatLookLikeNoteLinksRegEx = "\\[[^]]*]\\((\\d+)\\)"; private static final String replaceNoteRemoteIdsRegEx = "\\[([^\\]]*)\\]\\((%s)\\)"; private Set<String> existingNoteRemoteIds; public NoteLinksProcessor(Set<String> existingNoteRemoteIds) { this.existingNoteRemoteIds = existingNoteRemoteIds; } /** * Replaces all links to other notes of the form `[<link-text>](<note-file-id>)` * in the markdown string with links to a dummy url. * * Why is this needed? * See discussion in issue #623 * * @return Markdown with all note-links replaced with dummy-url-links */ @Override public String process(String s) { return replaceNoteLinksWithDummyUrls(s, existingNoteRemoteIds); } private static String replaceNoteLinksWithDummyUrls(String markdown, Set<String> existingNoteRemoteIds) { Pattern noteLinkCandidates = Pattern.compile(linksThatLookLikeNoteLinksRegEx); Matcher matcher = noteLinkCandidates.matcher(markdown); Set<String> noteRemoteIdsToReplace = new HashSet<>(); while (matcher.find()) { String presumedNoteId = matcher.group(1); if (existingNoteRemoteIds.contains(presumedNoteId)) { noteRemoteIdsToReplace.add(presumedNoteId); } } String noteRemoteIdsCondition = TextUtils.join("|", noteRemoteIdsToReplace); Pattern replacePattern = Pattern.compile(String.format(replaceNoteRemoteIdsRegEx, noteRemoteIdsCondition)); Matcher replaceMatcher = replacePattern.matcher(markdown); return replaceMatcher.replaceAll(String.format("[$1](%s$2)", RELATIVE_LINK_WORKAROUND_PREFIX)); } }
app/src/main/java/it/niedermann/owncloud/notes/shared/util/text/TextProcessor.java 0 → 100644 +10 −0 Original line number Diff line number Diff line package it.niedermann.owncloud.notes.shared.util.text; abstract public class TextProcessor { /** * Applies a specified transformation on a text string and returns the updated string. * @param s Text to transform * @return Transformed text */ abstract public String process(String s); } No newline at end of file
app/src/main/java/it/niedermann/owncloud/notes/shared/util/text/TextProcessorChain.java 0 → 100644 +12 −0 Original line number Diff line number Diff line package it.niedermann.owncloud.notes.shared.util.text; import java.util.LinkedList; public class TextProcessorChain extends LinkedList<TextProcessor> { public String apply(String s) { for (TextProcessor textProcessor : this) { s = textProcessor.process(s); } return s; } }