Inline html handling (no images)
This commit is contained in:
		
							parent
							
								
									e0c10c658b
								
							
						
					
					
						commit
						bf18b87420
					
				| @ -47,20 +47,6 @@ public class MainActivity extends Activity { | ||||
| 
 | ||||
|         final TextView textView = (TextView) findViewById(R.id.activity_main); | ||||
| 
 | ||||
| //        final Drawable drawable = getDrawable(R.mipmap.ic_launcher); | ||||
| ////        drawable.setBounds(0, 0, 16, 16); | ||||
| //        final SpannableStringBuilder builder = new SpannableStringBuilder(); | ||||
| //        for (int i = 0; i < 10; i++) { | ||||
| //            builder.append("text here and icon: \u00a0"); | ||||
| //            //noinspection WrongConstant | ||||
| //            builder.setSpan(new AsyncDrawableSpan(drawable, i % 3), builder.length() - 1, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); | ||||
| //            builder.append('\n'); | ||||
| //        } | ||||
| //        textView.setText(builder); | ||||
| // | ||||
| //        if (true) { | ||||
| //            return; | ||||
| //        } | ||||
| 
 | ||||
|         final Picasso picasso = new Picasso.Builder(this) | ||||
|                 .listener(new Picasso.Listener() { | ||||
| @ -78,7 +64,8 @@ public class MainActivity extends Activity { | ||||
|                 Scanner scanner = null; | ||||
|                 String md = null; | ||||
|                 try { | ||||
|                     stream = getAssets().open("scrollable.md"); | ||||
| //                    stream = getAssets().open("scrollable.md"); | ||||
|                     stream = getAssets().open("test.md"); | ||||
|                     scanner = new Scanner(stream).useDelimiter("\\A"); | ||||
|                     if (scanner.hasNext()) { | ||||
|                         md = scanner.next(); | ||||
|  | ||||
| @ -3,6 +3,7 @@ package ru.noties.markwon.renderer; | ||||
| import android.content.Context; | ||||
| import android.support.annotation.NonNull; | ||||
| 
 | ||||
| import ru.noties.markwon.renderer.html.SpannableHtmlParser; | ||||
| import ru.noties.markwon.spans.AsyncDrawable; | ||||
| import ru.noties.markwon.spans.LinkSpan; | ||||
| import ru.noties.markwon.spans.SpannableTheme; | ||||
| @ -22,12 +23,14 @@ public class SpannableConfiguration { | ||||
|     private final AsyncDrawable.Loader asyncDrawableLoader; | ||||
|     private final SyntaxHighlight syntaxHighlight; | ||||
|     private final LinkSpan.Resolver linkResolver; | ||||
|     private final SpannableHtmlParser htmlParser; | ||||
| 
 | ||||
|     private SpannableConfiguration(Builder builder) { | ||||
|         this.theme = builder.theme; | ||||
|         this.asyncDrawableLoader = builder.asyncDrawableLoader; | ||||
|         this.syntaxHighlight = builder.syntaxHighlight; | ||||
|         this.linkResolver = builder.linkResolver; | ||||
|         this.htmlParser = builder.htmlParser; | ||||
|     } | ||||
| 
 | ||||
|     public SpannableTheme theme() { | ||||
| @ -46,6 +49,10 @@ public class SpannableConfiguration { | ||||
|         return linkResolver; | ||||
|     } | ||||
| 
 | ||||
|     public SpannableHtmlParser htmlParser() { | ||||
|         return htmlParser; | ||||
|     } | ||||
| 
 | ||||
|     public static class Builder { | ||||
| 
 | ||||
|         private final Context context; | ||||
| @ -53,6 +60,7 @@ public class SpannableConfiguration { | ||||
|         private AsyncDrawable.Loader asyncDrawableLoader; | ||||
|         private SyntaxHighlight syntaxHighlight; | ||||
|         private LinkSpan.Resolver linkResolver; | ||||
|         private SpannableHtmlParser htmlParser; | ||||
| 
 | ||||
|         public Builder(Context context) { | ||||
|             this.context = context; | ||||
| @ -78,6 +86,11 @@ public class SpannableConfiguration { | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         public Builder htmlParser(SpannableHtmlParser htmlParser) { | ||||
|             this.htmlParser = htmlParser; | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         public SpannableConfiguration build() { | ||||
|             if (theme == null) { | ||||
|                 theme = SpannableTheme.create(context); | ||||
| @ -91,6 +104,9 @@ public class SpannableConfiguration { | ||||
|             if (linkResolver == null) { | ||||
|                 linkResolver = new LinkResolverDef(); | ||||
|             } | ||||
|             if (htmlParser == null) { | ||||
|                 htmlParser = SpannableHtmlParser.create(theme); | ||||
|             } | ||||
|             return new SpannableConfiguration(this); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -4,7 +4,6 @@ import android.support.annotation.NonNull; | ||||
| import android.text.SpannableStringBuilder; | ||||
| import android.text.Spanned; | ||||
| import android.text.style.StrikethroughSpan; | ||||
| import android.text.style.URLSpan; | ||||
| 
 | ||||
| import org.commonmark.ext.gfm.strikethrough.Strikethrough; | ||||
| import org.commonmark.node.AbstractVisitor; | ||||
| @ -17,6 +16,7 @@ import org.commonmark.node.FencedCodeBlock; | ||||
| import org.commonmark.node.HardLineBreak; | ||||
| import org.commonmark.node.Heading; | ||||
| import org.commonmark.node.HtmlBlock; | ||||
| import org.commonmark.node.HtmlInline; | ||||
| import org.commonmark.node.Image; | ||||
| import org.commonmark.node.Link; | ||||
| import org.commonmark.node.ListBlock; | ||||
| @ -29,7 +29,11 @@ import org.commonmark.node.StrongEmphasis; | ||||
| import org.commonmark.node.Text; | ||||
| import org.commonmark.node.ThematicBreak; | ||||
| 
 | ||||
| import java.util.ArrayDeque; | ||||
| import java.util.Deque; | ||||
| 
 | ||||
| import ru.noties.debug.Debug; | ||||
| import ru.noties.markwon.renderer.html.SpannableHtmlParser; | ||||
| import ru.noties.markwon.spans.AsyncDrawable; | ||||
| import ru.noties.markwon.spans.AsyncDrawableSpan; | ||||
| import ru.noties.markwon.spans.BlockQuoteSpan; | ||||
| @ -42,10 +46,14 @@ import ru.noties.markwon.spans.OrderedListItemSpan; | ||||
| import ru.noties.markwon.spans.StrongEmphasisSpan; | ||||
| import ru.noties.markwon.spans.ThematicBreakSpan; | ||||
| 
 | ||||
| // please do not reuse between different texts (due to the html handling) | ||||
| public class SpannableMarkdownVisitor extends AbstractVisitor { | ||||
| 
 | ||||
|     private static final String HTML_CONTENT = "<%1$s>%2$s</%1$s>"; | ||||
| 
 | ||||
|     private final SpannableConfiguration configuration; | ||||
|     private final SpannableStringBuilder builder; | ||||
|     private final Deque<HtmlInlineItem> htmlInlineItems; | ||||
| 
 | ||||
|     private int blockQuoteIndent; | ||||
|     private int listLevel; | ||||
| @ -56,17 +64,16 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { | ||||
|     ) { | ||||
|         this.configuration = configuration; | ||||
|         this.builder = builder; | ||||
|         this.htmlInlineItems = new ArrayDeque<>(2); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void visit(Text text) { | ||||
| //        Debug.i(text); | ||||
|         builder.append(text.getLiteral()); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void visit(StrongEmphasis strongEmphasis) { | ||||
| //        Debug.i(strongEmphasis); | ||||
|         final int length = builder.length(); | ||||
|         visitChildren(strongEmphasis); | ||||
|         setSpan(length, new StrongEmphasisSpan()); | ||||
| @ -74,7 +81,6 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { | ||||
| 
 | ||||
|     @Override | ||||
|     public void visit(Emphasis emphasis) { | ||||
| //        Debug.i(emphasis); | ||||
|         final int length = builder.length(); | ||||
|         visitChildren(emphasis); | ||||
|         setSpan(length, new EmphasisSpan()); | ||||
| @ -83,8 +89,6 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { | ||||
|     @Override | ||||
|     public void visit(BlockQuote blockQuote) { | ||||
| 
 | ||||
| //        Debug.i(blockQuote); | ||||
| 
 | ||||
|         newLine(); | ||||
|         if (blockQuoteIndent != 0) { | ||||
|             builder.append('\n'); | ||||
| @ -112,8 +116,6 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { | ||||
|     @Override | ||||
|     public void visit(Code code) { | ||||
| 
 | ||||
| //        Debug.i(code); | ||||
| 
 | ||||
|         final int length = builder.length(); | ||||
| 
 | ||||
|         // NB, in order to provide a _padding_ feeling code is wrapped inside two unbreakable spaces | ||||
| @ -163,7 +165,6 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { | ||||
|     } | ||||
| 
 | ||||
|     private void visitList(Node node) { | ||||
| //        Debug.i(node); | ||||
|         newLine(); | ||||
|         visitChildren(node); | ||||
|         newLine(); | ||||
| @ -175,15 +176,11 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { | ||||
|     @Override | ||||
|     public void visit(ListItem listItem) { | ||||
| 
 | ||||
| //        Debug.i(listItem); | ||||
| 
 | ||||
|         final int length = builder.length(); | ||||
| 
 | ||||
|         blockQuoteIndent += 1; | ||||
|         listLevel += 1; | ||||
| 
 | ||||
|         // todo, can be a bullet list & ordered list (with leading numbers... looks like we need to `draw` numbers... | ||||
| 
 | ||||
|         final Node parent = listItem.getParent(); | ||||
|         if (parent instanceof OrderedList) { | ||||
| 
 | ||||
| @ -223,8 +220,6 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { | ||||
|     @Override | ||||
|     public void visit(ThematicBreak thematicBreak) { | ||||
| 
 | ||||
| //        Debug.i(thematicBreak); | ||||
| 
 | ||||
|         newLine(); | ||||
| 
 | ||||
|         final int length = builder.length(); | ||||
| @ -238,8 +233,6 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { | ||||
|     @Override | ||||
|     public void visit(Heading heading) { | ||||
| 
 | ||||
| //        Debug.i(heading); | ||||
| 
 | ||||
|         newLine(); | ||||
| 
 | ||||
|         final int length = builder.length(); | ||||
| @ -258,21 +251,16 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { | ||||
| 
 | ||||
|     @Override | ||||
|     public void visit(SoftLineBreak softLineBreak) { | ||||
|         Debug.i(softLineBreak); | ||||
|         newLine(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void visit(HardLineBreak hardLineBreak) { | ||||
|         Debug.i(hardLineBreak); | ||||
|         newLine(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void visit(CustomNode customNode) { | ||||
| 
 | ||||
| //        Debug.i(customNode); | ||||
| 
 | ||||
|         if (customNode instanceof Strikethrough) { | ||||
|             final int length = builder.length(); | ||||
|             visitChildren(customNode); | ||||
| @ -287,8 +275,6 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { | ||||
| 
 | ||||
|         final boolean inTightList = isInTightList(paragraph); | ||||
| 
 | ||||
| //        Debug.i(paragraph, inTightList, listLevel); | ||||
| 
 | ||||
|         if (!inTightList) { | ||||
|             newLine(); | ||||
|         } | ||||
| @ -311,8 +297,6 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { | ||||
| 
 | ||||
|         visitChildren(image); | ||||
| 
 | ||||
|         // if image has no link, create it (to open in external app) | ||||
| 
 | ||||
|         // we must check if anything _was_ added, as we need at least one char to render | ||||
|         if (length == builder.length()) { | ||||
|             builder.append(' '); // breakable space | ||||
| @ -321,14 +305,17 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { | ||||
|         final Node parent = image.getParent(); | ||||
|         final boolean link = parent != null && parent instanceof Link; | ||||
| 
 | ||||
|         setSpan(length, new AsyncDrawableSpan( | ||||
|                 configuration.theme(), | ||||
|                 new AsyncDrawable( | ||||
|                         image.getDestination(), | ||||
|                         configuration.asyncDrawableLoader() | ||||
|                 ), | ||||
|                 AsyncDrawableSpan.ALIGN_BOTTOM, | ||||
|                 link) | ||||
|         setSpan( | ||||
|                 length, | ||||
|                 new AsyncDrawableSpan( | ||||
|                         configuration.theme(), | ||||
|                         new AsyncDrawable( | ||||
|                                 image.getDestination(), | ||||
|                                 configuration.asyncDrawableLoader() | ||||
|                         ), | ||||
|                         AsyncDrawableSpan.ALIGN_BOTTOM, | ||||
|                         link | ||||
|                 ) | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
| @ -339,6 +326,46 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { | ||||
|         super.visit(htmlBlock); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void visit(HtmlInline htmlInline) { | ||||
|         final SpannableHtmlParser htmlParser = configuration.htmlParser(); | ||||
|         final SpannableHtmlParser.Tag tag = htmlParser.parseTag(htmlInline.getLiteral()); | ||||
|         if (tag != null) { | ||||
|             if (tag.opening()) { | ||||
|                 // push in stack | ||||
|                 htmlInlineItems.push(new HtmlInlineItem(tag.name(), builder.length())); | ||||
|                 visitChildren(htmlInline); | ||||
|             } else { | ||||
|                 // pop last item | ||||
|                 if (htmlInlineItems.size() > 0) { | ||||
|                     final HtmlInlineItem item = htmlInlineItems.pop(); | ||||
|                     final int start = item.start; | ||||
|                     final Object span = htmlParser.handleTag(item.tag); | ||||
|                     if (span != null) { | ||||
|                         setSpan(start, span); | ||||
|                     } else { | ||||
|                         final String content = builder.subSequence(start, builder.length()).toString(); | ||||
|                         final String html = String.format(HTML_CONTENT, item.tag, content); | ||||
|                         final Object[] spans = htmlParser.htmlSpans(html); | ||||
|                         final int length = spans != null | ||||
|                                 ? spans.length | ||||
|                                 : 0; | ||||
|                         for (int i = 0; i < length; i++) { | ||||
|                             setSpan(start, spans[i]); | ||||
|                         } | ||||
|                     } | ||||
|                 } else { | ||||
|                     throw new IllegalStateException("Unexpected closing html tag: " + tag.name() | ||||
|                             + ", at position: " + builder.length()); | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             // let's add what we have | ||||
|             builder.append(htmlInline.getLiteral()); | ||||
|             visitChildren(htmlInline); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void visit(Link link) { | ||||
|         final int length = builder.length(); | ||||
| @ -369,6 +396,15 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     private static class HtmlInlineItem { | ||||
|         final String tag; | ||||
|         final int start; | ||||
|         HtmlInlineItem(String tag, int start) { | ||||
|             this.tag = tag; | ||||
|             this.start = start; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| //    private static String dump(Node node) { | ||||
| //        final StringBuilder builder = new StringBuilder(); | ||||
| //        node.accept(new DumpVisitor(builder)); | ||||
|  | ||||
| @ -0,0 +1,10 @@ | ||||
| package ru.noties.markwon.renderer.html; | ||||
| 
 | ||||
| import ru.noties.markwon.spans.StrongEmphasisSpan; | ||||
| 
 | ||||
| class BoldProvider implements SpannableHtmlParser.SpanProvider { | ||||
|     @Override | ||||
|     public Object provide() { | ||||
|         return new StrongEmphasisSpan(); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,10 @@ | ||||
| package ru.noties.markwon.renderer.html; | ||||
| 
 | ||||
| import ru.noties.markwon.spans.EmphasisSpan; | ||||
| 
 | ||||
| class ItalicsProvider implements SpannableHtmlParser.SpanProvider { | ||||
|     @Override | ||||
|     public Object provide() { | ||||
|         return new EmphasisSpan(); | ||||
|     } | ||||
| } | ||||
| @ -1,4 +1,4 @@ | ||||
| package ru.noties.markwon.renderer; | ||||
| package ru.noties.markwon.renderer.html; | ||||
| 
 | ||||
| import android.annotation.TargetApi; | ||||
| import android.os.Build; | ||||
| @ -10,11 +10,44 @@ import android.text.Spanned; | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
| 
 | ||||
| import ru.noties.markwon.spans.SpannableTheme; | ||||
| 
 | ||||
| @SuppressWarnings("WeakerAccess") | ||||
| public class SpannableHtmlParser { | ||||
| 
 | ||||
|     // we need to handle images independently (in order to parse alt, width, height, etc) | ||||
| 
 | ||||
|     // creates default parser | ||||
|     public static SpannableHtmlParser create(@NonNull SpannableTheme theme) { | ||||
|         return builderWithDefaults(theme) | ||||
|                 .build(); | ||||
|     } | ||||
| 
 | ||||
|     public static Builder builder() { | ||||
|         return new Builder(); | ||||
|     } | ||||
| 
 | ||||
|     public static Builder builderWithDefaults(@NonNull SpannableTheme theme) { | ||||
| 
 | ||||
|         final BoldProvider boldProvider = new BoldProvider(); | ||||
|         final ItalicsProvider italicsProvider = new ItalicsProvider(); | ||||
|         final StrikeProvider strikeProvider = new StrikeProvider(); | ||||
| 
 | ||||
|         return new Builder() | ||||
|                 .customTag("b", boldProvider) | ||||
|                 .customTag("strong", boldProvider) | ||||
|                 .customTag("i", italicsProvider) | ||||
|                 .customTag("em", italicsProvider) | ||||
|                 .customTag("cite", italicsProvider) | ||||
|                 .customTag("dfn", italicsProvider) | ||||
|                 .customTag("sup", new SuperScriptProvider(theme)) | ||||
|                 .customTag("sub", new SubScriptProvider(theme)) | ||||
|                 .customTag("u", new UnderlineProvider()) | ||||
|                 .customTag("del", strikeProvider) | ||||
|                 .customTag("s", strikeProvider) | ||||
|                 .customTag("strike", strikeProvider); | ||||
|     } | ||||
| 
 | ||||
|     // for simple tags without arguments | ||||
|     // <b>, <i>, etc | ||||
|     public interface SpanProvider { | ||||
| @ -25,15 +58,6 @@ public class SpannableHtmlParser { | ||||
|         Object[] getSpans(@NonNull String html); | ||||
|     } | ||||
| 
 | ||||
|     // creates default parser | ||||
|     public static SpannableHtmlParser create() { | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     public static Builder builder() { | ||||
|         return new Builder(); | ||||
|     } | ||||
| 
 | ||||
|     private final Map<String, SpanProvider> customTags; | ||||
|     private final HtmlParser parser; | ||||
| 
 | ||||
| @ -42,6 +66,47 @@ public class SpannableHtmlParser { | ||||
|         this.parser = builder.parser; | ||||
|     } | ||||
| 
 | ||||
|     @Nullable | ||||
|     public Tag parseTag(String html) { | ||||
| 
 | ||||
|         final Tag tag; | ||||
| 
 | ||||
|         final int length = html != null | ||||
|                 ? html.length() | ||||
|                 : 0; | ||||
| 
 | ||||
|         // absolutely minimum (`<i>`) | ||||
|         if (length < 3) { | ||||
|             tag = null; | ||||
|         } else { | ||||
|             final boolean closing = '<' == html.charAt(0) && '/' == html.charAt(1); | ||||
|             final String name = closing | ||||
|                     ? html.substring(2, length - 1) | ||||
|                     : html.substring(1, length - 1); | ||||
|             tag = new Tag(name, !closing); | ||||
|         } | ||||
| 
 | ||||
|         return tag; | ||||
|     } | ||||
| 
 | ||||
|     @Nullable | ||||
|     public Object handleTag(String tag) { | ||||
|         final Object out; | ||||
|         final SpanProvider provider = customTags.get(tag); | ||||
|         if (provider != null) { | ||||
|             out = provider.provide(); | ||||
|         } else { | ||||
|             out = null; | ||||
|         } | ||||
|         return out; | ||||
|     } | ||||
| 
 | ||||
|     @Nullable | ||||
|     public Object[] htmlSpans(String html) { | ||||
|         // todo, additional handling of: image & link | ||||
|         return parser.getSpans(html); | ||||
|     } | ||||
| 
 | ||||
|     public static class Builder { | ||||
| 
 | ||||
|         private final Map<String, SpanProvider> customTags = new HashMap<>(3); | ||||
| @ -52,20 +117,46 @@ public class SpannableHtmlParser { | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         public Builder setParser(@NonNull HtmlParser parser) { | ||||
|         public Builder parser(@NonNull HtmlParser parser) { | ||||
|             this.parser = parser; | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         public SpannableHtmlParser build() { | ||||
|             if (parser == null) { | ||||
|                 // todo, images.... | ||||
|                 parser = DefaultHtmlParser.create(null, null); | ||||
|             } | ||||
|             return new SpannableHtmlParser(this); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static class Tag { | ||||
| 
 | ||||
|         private final String name; | ||||
|         private final boolean opening; | ||||
| 
 | ||||
|         public Tag(String name, boolean opening) { | ||||
|             this.name = name; | ||||
|             this.opening = opening; | ||||
|         } | ||||
| 
 | ||||
|         public String name() { | ||||
|             return name; | ||||
|         } | ||||
| 
 | ||||
|         public boolean opening() { | ||||
|             return opening; | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public String toString() { | ||||
|             return "Tag{" + | ||||
|                     "name='" + name + '\'' + | ||||
|                     ", opening=" + opening + | ||||
|                     '}'; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static abstract class DefaultHtmlParser implements HtmlParser { | ||||
| 
 | ||||
|         public static DefaultHtmlParser create(@Nullable Html.ImageGetter imageGetter, @Nullable Html.TagHandler tagHandler) { | ||||
| @ -0,0 +1,10 @@ | ||||
| package ru.noties.markwon.renderer.html; | ||||
| 
 | ||||
| import android.text.style.StrikethroughSpan; | ||||
| 
 | ||||
| class StrikeProvider implements SpannableHtmlParser.SpanProvider { | ||||
|     @Override | ||||
|     public Object provide() { | ||||
|         return new StrikethroughSpan(); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,18 @@ | ||||
| package ru.noties.markwon.renderer.html; | ||||
| 
 | ||||
| import ru.noties.markwon.spans.SpannableTheme; | ||||
| import ru.noties.markwon.spans.SubScriptSpan; | ||||
| 
 | ||||
| class SubScriptProvider implements SpannableHtmlParser.SpanProvider { | ||||
| 
 | ||||
|     private final SpannableTheme theme; | ||||
| 
 | ||||
|     public SubScriptProvider(SpannableTheme theme) { | ||||
|         this.theme = theme; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public Object provide() { | ||||
|         return new SubScriptSpan(theme); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,18 @@ | ||||
| package ru.noties.markwon.renderer.html; | ||||
| 
 | ||||
| import ru.noties.markwon.spans.SpannableTheme; | ||||
| import ru.noties.markwon.spans.SuperScriptSpan; | ||||
| 
 | ||||
| class SuperScriptProvider implements SpannableHtmlParser.SpanProvider { | ||||
| 
 | ||||
|     private final SpannableTheme theme; | ||||
| 
 | ||||
|     SuperScriptProvider(SpannableTheme theme) { | ||||
|         this.theme = theme; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public Object provide() { | ||||
|         return new SuperScriptSpan(theme); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,11 @@ | ||||
| package ru.noties.markwon.renderer.html; | ||||
| 
 | ||||
| import android.text.style.UnderlineSpan; | ||||
| 
 | ||||
| class UnderlineProvider implements SpannableHtmlParser.SpanProvider { | ||||
| 
 | ||||
|     @Override | ||||
|     public Object provide() { | ||||
|         return new UnderlineSpan(); | ||||
|     } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Dimitry Ivanov
						Dimitry Ivanov