Move all html entities to markwon-html module
This commit is contained in:
		
							parent
							
								
									2efd12f020
								
							
						
					
					
						commit
						27ed17aaff
					
				@ -77,10 +77,6 @@ public class MarkdownRenderer {
 | 
				
			|||||||
                        ? prism4jThemeDefault
 | 
					                        ? prism4jThemeDefault
 | 
				
			||||||
                        : prism4JThemeDarkula;
 | 
					                        : prism4JThemeDarkula;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//                final int background = isLightTheme
 | 
					 | 
				
			||||||
//                        ? prism4jTheme.background()
 | 
					 | 
				
			||||||
//                        : 0x0Fffffff;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                final Markwon2 markwon2 = Markwon2.builder(context)
 | 
					                final Markwon2 markwon2 = Markwon2.builder(context)
 | 
				
			||||||
                        .use(CorePlugin.create())
 | 
					                        .use(CorePlugin.create())
 | 
				
			||||||
                        .use(ImagesPlugin.createWithAssets(context))
 | 
					                        .use(ImagesPlugin.createWithAssets(context))
 | 
				
			||||||
 | 
				
			|||||||
@ -1,23 +0,0 @@
 | 
				
			|||||||
apply plugin: 'com.android.library'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
android {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    compileSdkVersion config['compile-sdk']
 | 
					 | 
				
			||||||
    buildToolsVersion config['build-tools']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    defaultConfig {
 | 
					 | 
				
			||||||
        minSdkVersion config['min-sdk']
 | 
					 | 
				
			||||||
        targetSdkVersion config['target-sdk']
 | 
					 | 
				
			||||||
        versionCode 1
 | 
					 | 
				
			||||||
        versionName version
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
dependencies {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    deps.with {
 | 
					 | 
				
			||||||
        api it['support-annotations']
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
registerArtifact(this)
 | 
					 | 
				
			||||||
@ -1,3 +0,0 @@
 | 
				
			|||||||
POM_NAME=Markwon
 | 
					 | 
				
			||||||
POM_ARTIFACT_ID=markwon-html-parser-api
 | 
					 | 
				
			||||||
POM_PACKAGING=aar
 | 
					 | 
				
			||||||
@ -1 +0,0 @@
 | 
				
			|||||||
<manifest package="ru.noties.markwon.html.api" />
 | 
					 | 
				
			||||||
@ -15,7 +15,7 @@ android {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
dependencies {
 | 
					dependencies {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    api project(':markwon-html-parser-api')
 | 
					    api project(':markwon')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    deps.with {
 | 
					    deps.with {
 | 
				
			||||||
        api it['support-annotations']
 | 
					        api it['support-annotations']
 | 
				
			||||||
@ -25,6 +25,7 @@ dependencies {
 | 
				
			|||||||
    deps.test.with {
 | 
					    deps.test.with {
 | 
				
			||||||
        testImplementation it['junit']
 | 
					        testImplementation it['junit']
 | 
				
			||||||
        testImplementation it['robolectric']
 | 
					        testImplementation it['robolectric']
 | 
				
			||||||
 | 
					        testImplementation it['ix-java']
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
package ru.noties.markwon.renderer.html2;
 | 
					package ru.noties.markwon.html.impl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
import android.support.annotation.Nullable;
 | 
					import android.support.annotation.Nullable;
 | 
				
			||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
package ru.noties.markwon.renderer.html2;
 | 
					package ru.noties.markwon.html.impl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -3,7 +3,7 @@ package ru.noties.markwon.html.impl;
 | 
				
			|||||||
import android.support.annotation.NonNull;
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
import android.support.annotation.Nullable;
 | 
					import android.support.annotation.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ru.noties.markwon.html.api.HtmlTag;
 | 
					import ru.noties.markwon.html.HtmlTag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * This class will be used to append some text to output in order to
 | 
					 * This class will be used to append some text to output in order to
 | 
				
			||||||
@ -7,7 +7,7 @@ import java.util.Collections;
 | 
				
			|||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ru.noties.markwon.html.api.HtmlTag;
 | 
					import ru.noties.markwon.html.HtmlTag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
abstract class HtmlTagImpl implements HtmlTag {
 | 
					abstract class HtmlTagImpl implements HtmlTag {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -14,10 +14,10 @@ import java.util.Locale;
 | 
				
			|||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
import java.util.Set;
 | 
					import java.util.Set;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ru.noties.markwon.html.api.HtmlTag;
 | 
					import ru.noties.markwon.html.HtmlTag;
 | 
				
			||||||
import ru.noties.markwon.html.api.HtmlTag.Block;
 | 
					import ru.noties.markwon.html.HtmlTag.Block;
 | 
				
			||||||
import ru.noties.markwon.html.api.HtmlTag.Inline;
 | 
					import ru.noties.markwon.html.HtmlTag.Inline;
 | 
				
			||||||
import ru.noties.markwon.html.api.MarkwonHtmlParser;
 | 
					import ru.noties.markwon.html.MarkwonHtmlParser;
 | 
				
			||||||
import ru.noties.markwon.html.impl.jsoup.nodes.Attribute;
 | 
					import ru.noties.markwon.html.impl.jsoup.nodes.Attribute;
 | 
				
			||||||
import ru.noties.markwon.html.impl.jsoup.nodes.Attributes;
 | 
					import ru.noties.markwon.html.impl.jsoup.nodes.Attributes;
 | 
				
			||||||
import ru.noties.markwon.html.impl.jsoup.parser.CharacterReader;
 | 
					import ru.noties.markwon.html.impl.jsoup.parser.CharacterReader;
 | 
				
			||||||
@ -0,0 +1,167 @@
 | 
				
			|||||||
 | 
					package ru.noties.markwon.html.impl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
 | 
					import android.support.annotation.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Collections;
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Locale;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import ru.noties.markwon.MarkwonConfiguration;
 | 
				
			||||||
 | 
					import ru.noties.markwon.SpannableBuilder;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.HtmlTag;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.MarkwonHtmlParser;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.MarkwonHtmlRenderer;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.TagHandler;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.impl.tag.BlockquoteHandler;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.impl.tag.EmphasisHandler;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.impl.tag.HeadingHandler;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.impl.tag.ImageHandler;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.impl.tag.LinkHandler;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.impl.tag.ListHandler;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.impl.tag.StrikeHandler;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.impl.tag.StrongEmphasisHandler;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.impl.tag.SubScriptHandler;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.impl.tag.SuperScriptHandler;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.impl.tag.UnderlineHandler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class MarkwonHtmlRendererImpl extends MarkwonHtmlRenderer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNull
 | 
				
			||||||
 | 
					    public static MarkwonHtmlRendererImpl create() {
 | 
				
			||||||
 | 
					        return builderWithDefaults().build();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNull
 | 
				
			||||||
 | 
					    public static Builder builderWithDefaults() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final EmphasisHandler emphasisHandler = new EmphasisHandler();
 | 
				
			||||||
 | 
					        final StrongEmphasisHandler strongEmphasisHandler = new StrongEmphasisHandler();
 | 
				
			||||||
 | 
					        final StrikeHandler strikeHandler = new StrikeHandler();
 | 
				
			||||||
 | 
					        final UnderlineHandler underlineHandler = new UnderlineHandler();
 | 
				
			||||||
 | 
					        final ListHandler listHandler = new ListHandler();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return builder()
 | 
				
			||||||
 | 
					                .handler("i", emphasisHandler)
 | 
				
			||||||
 | 
					                .handler("em", emphasisHandler)
 | 
				
			||||||
 | 
					                .handler("cite", emphasisHandler)
 | 
				
			||||||
 | 
					                .handler("dfn", emphasisHandler)
 | 
				
			||||||
 | 
					                .handler("b", strongEmphasisHandler)
 | 
				
			||||||
 | 
					                .handler("strong", strongEmphasisHandler)
 | 
				
			||||||
 | 
					                .handler("sup", new SuperScriptHandler())
 | 
				
			||||||
 | 
					                .handler("sub", new SubScriptHandler())
 | 
				
			||||||
 | 
					                .handler("u", underlineHandler)
 | 
				
			||||||
 | 
					                .handler("ins", underlineHandler)
 | 
				
			||||||
 | 
					                .handler("del", strikeHandler)
 | 
				
			||||||
 | 
					                .handler("s", strikeHandler)
 | 
				
			||||||
 | 
					                .handler("strike", strikeHandler)
 | 
				
			||||||
 | 
					                .handler("a", new LinkHandler())
 | 
				
			||||||
 | 
					                .handler("ul", listHandler)
 | 
				
			||||||
 | 
					                .handler("ol", listHandler)
 | 
				
			||||||
 | 
					                .handler("img", ImageHandler.create())
 | 
				
			||||||
 | 
					                .handler("blockquote", new BlockquoteHandler())
 | 
				
			||||||
 | 
					                .handler("h1", new HeadingHandler(1))
 | 
				
			||||||
 | 
					                .handler("h2", new HeadingHandler(2))
 | 
				
			||||||
 | 
					                .handler("h3", new HeadingHandler(3))
 | 
				
			||||||
 | 
					                .handler("h4", new HeadingHandler(4))
 | 
				
			||||||
 | 
					                .handler("h5", new HeadingHandler(5))
 | 
				
			||||||
 | 
					                .handler("h6", new HeadingHandler(6));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNull
 | 
				
			||||||
 | 
					    public static Builder builder() {
 | 
				
			||||||
 | 
					        return new Builder();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final float SCRIPT_DEF_TEXT_SIZE_RATIO = .75F;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final Map<String, TagHandler> tagHandlers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private MarkwonHtmlRendererImpl(@NonNull Map<String, TagHandler> tagHandlers) {
 | 
				
			||||||
 | 
					        this.tagHandlers = tagHandlers;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void render(
 | 
				
			||||||
 | 
					            @NonNull final MarkwonConfiguration configuration,
 | 
				
			||||||
 | 
					            @NonNull final SpannableBuilder builder,
 | 
				
			||||||
 | 
					            @NonNull MarkwonHtmlParser parser) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final int end;
 | 
				
			||||||
 | 
					        if (!configuration.htmlAllowNonClosedTags()) {
 | 
				
			||||||
 | 
					            end = HtmlTag.NO_END;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            end = builder.length();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        parser.flushInlineTags(end, new MarkwonHtmlParser.FlushAction<HtmlTag.Inline>() {
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void apply(@NonNull List<HtmlTag.Inline> tags) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                TagHandler handler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                for (HtmlTag.Inline inline : tags) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // if tag is not closed -> do not render
 | 
				
			||||||
 | 
					                    if (!inline.isClosed()) {
 | 
				
			||||||
 | 
					                        continue;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    handler = tagHandler(inline.name());
 | 
				
			||||||
 | 
					                    if (handler != null) {
 | 
				
			||||||
 | 
					                        handler.handle(configuration, builder, inline);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        parser.flushBlockTags(end, new MarkwonHtmlParser.FlushAction<HtmlTag.Block>() {
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void apply(@NonNull List<HtmlTag.Block> tags) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                TagHandler handler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                for (HtmlTag.Block block : tags) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (!block.isClosed()) {
 | 
				
			||||||
 | 
					                        continue;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    handler = tagHandler(block.name());
 | 
				
			||||||
 | 
					                    if (handler != null) {
 | 
				
			||||||
 | 
					                        handler.handle(configuration, builder, block);
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        // see if any of children can be handled
 | 
				
			||||||
 | 
					                        apply(block.children());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        parser.reset();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Nullable
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public TagHandler tagHandler(@NonNull String tagName) {
 | 
				
			||||||
 | 
					        return tagHandlers.get(tagName);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static class Builder {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private final Map<String, TagHandler> tagHandlers = new HashMap<>(2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @NonNull
 | 
				
			||||||
 | 
					        public Builder handler(@NonNull String tagName, @NonNull TagHandler tagHandler) {
 | 
				
			||||||
 | 
					            tagHandlers.put(tagName.toLowerCase(Locale.US), tagHandler);
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @NonNull
 | 
				
			||||||
 | 
					        public MarkwonHtmlRendererImpl build() {
 | 
				
			||||||
 | 
					            return new MarkwonHtmlRendererImpl(Collections.unmodifiableMap(tagHandlers));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					package ru.noties.markwon.html.impl.span;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
 | 
					import android.text.TextPaint;
 | 
				
			||||||
 | 
					import android.text.style.MetricAffectingSpan;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.impl.MarkwonHtmlRendererImpl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class SubScriptSpan extends MetricAffectingSpan {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void updateDrawState(TextPaint tp) {
 | 
				
			||||||
 | 
					        apply(tp);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void updateMeasureState(@NonNull TextPaint tp) {
 | 
				
			||||||
 | 
					        apply(tp);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void apply(TextPaint paint) {
 | 
				
			||||||
 | 
					        paint.setTextSize(paint.getTextSize() * MarkwonHtmlRendererImpl.SCRIPT_DEF_TEXT_SIZE_RATIO);
 | 
				
			||||||
 | 
					        paint.baselineShift -= (int) (paint.ascent() / 2);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					package ru.noties.markwon.html.impl.span;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
 | 
					import android.text.TextPaint;
 | 
				
			||||||
 | 
					import android.text.style.MetricAffectingSpan;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.impl.MarkwonHtmlRendererImpl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class SuperScriptSpan extends MetricAffectingSpan {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void updateDrawState(TextPaint tp) {
 | 
				
			||||||
 | 
					        apply(tp);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void updateMeasureState(@NonNull TextPaint tp) {
 | 
				
			||||||
 | 
					        apply(tp);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void apply(TextPaint paint) {
 | 
				
			||||||
 | 
					        paint.setTextSize(paint.getTextSize() * MarkwonHtmlRendererImpl.SCRIPT_DEF_TEXT_SIZE_RATIO);
 | 
				
			||||||
 | 
					        paint.baselineShift += (int) (paint.ascent() / 2);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,10 +1,11 @@
 | 
				
			|||||||
package ru.noties.markwon.renderer.html2.tag;
 | 
					package ru.noties.markwon.html.impl.tag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ru.noties.markwon.SpannableBuilder;
 | 
					 | 
				
			||||||
import ru.noties.markwon.MarkwonConfiguration;
 | 
					import ru.noties.markwon.MarkwonConfiguration;
 | 
				
			||||||
import ru.noties.markwon.html.api.HtmlTag;
 | 
					import ru.noties.markwon.SpannableBuilder;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.HtmlTag;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.TagHandler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class BlockquoteHandler extends TagHandler {
 | 
					public class BlockquoteHandler extends TagHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1,10 +1,10 @@
 | 
				
			|||||||
package ru.noties.markwon.renderer.html2.tag;
 | 
					package ru.noties.markwon.html.impl.tag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
import android.support.annotation.Nullable;
 | 
					import android.support.annotation.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ru.noties.markwon.MarkwonConfiguration;
 | 
					import ru.noties.markwon.MarkwonConfiguration;
 | 
				
			||||||
import ru.noties.markwon.html.api.HtmlTag;
 | 
					import ru.noties.markwon.html.HtmlTag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class EmphasisHandler extends SimpleTagHandler {
 | 
					public class EmphasisHandler extends SimpleTagHandler {
 | 
				
			||||||
    @Nullable
 | 
					    @Nullable
 | 
				
			||||||
@ -1,10 +1,10 @@
 | 
				
			|||||||
package ru.noties.markwon.renderer.html2.tag;
 | 
					package ru.noties.markwon.html.impl.tag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
import android.support.annotation.Nullable;
 | 
					import android.support.annotation.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ru.noties.markwon.MarkwonConfiguration;
 | 
					import ru.noties.markwon.MarkwonConfiguration;
 | 
				
			||||||
import ru.noties.markwon.html.api.HtmlTag;
 | 
					import ru.noties.markwon.html.HtmlTag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class HeadingHandler extends SimpleTagHandler {
 | 
					public class HeadingHandler extends SimpleTagHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
package ru.noties.markwon.renderer.html2.tag;
 | 
					package ru.noties.markwon.html.impl.tag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
import android.support.annotation.Nullable;
 | 
					import android.support.annotation.Nullable;
 | 
				
			||||||
@ -7,9 +7,9 @@ import android.text.TextUtils;
 | 
				
			|||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ru.noties.markwon.MarkwonConfiguration;
 | 
					import ru.noties.markwon.MarkwonConfiguration;
 | 
				
			||||||
import ru.noties.markwon.html.api.HtmlTag;
 | 
					import ru.noties.markwon.html.HtmlTag;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.impl.CssInlineStyleParser;
 | 
				
			||||||
import ru.noties.markwon.renderer.ImageSize;
 | 
					import ru.noties.markwon.renderer.ImageSize;
 | 
				
			||||||
import ru.noties.markwon.renderer.html2.CssInlineStyleParser;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class ImageHandler extends SimpleTagHandler {
 | 
					public class ImageHandler extends SimpleTagHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
package ru.noties.markwon.renderer.html2.tag;
 | 
					package ru.noties.markwon.html.impl.tag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
import android.support.annotation.Nullable;
 | 
					import android.support.annotation.Nullable;
 | 
				
			||||||
@ -7,9 +7,9 @@ import android.text.TextUtils;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.impl.CssInlineStyleParser;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.impl.CssProperty;
 | 
				
			||||||
import ru.noties.markwon.renderer.ImageSize;
 | 
					import ru.noties.markwon.renderer.ImageSize;
 | 
				
			||||||
import ru.noties.markwon.renderer.html2.CssInlineStyleParser;
 | 
					 | 
				
			||||||
import ru.noties.markwon.renderer.html2.CssProperty;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ImageSizeParserImpl implements ImageHandler.ImageSizeParser {
 | 
					class ImageSizeParserImpl implements ImageHandler.ImageSizeParser {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1,11 +1,11 @@
 | 
				
			|||||||
package ru.noties.markwon.renderer.html2.tag;
 | 
					package ru.noties.markwon.html.impl.tag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
import android.support.annotation.Nullable;
 | 
					import android.support.annotation.Nullable;
 | 
				
			||||||
import android.text.TextUtils;
 | 
					import android.text.TextUtils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ru.noties.markwon.MarkwonConfiguration;
 | 
					import ru.noties.markwon.MarkwonConfiguration;
 | 
				
			||||||
import ru.noties.markwon.html.api.HtmlTag;
 | 
					import ru.noties.markwon.html.HtmlTag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class LinkHandler extends SimpleTagHandler {
 | 
					public class LinkHandler extends SimpleTagHandler {
 | 
				
			||||||
    @Nullable
 | 
					    @Nullable
 | 
				
			||||||
@ -1,10 +1,11 @@
 | 
				
			|||||||
package ru.noties.markwon.renderer.html2.tag;
 | 
					package ru.noties.markwon.html.impl.tag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ru.noties.markwon.MarkwonConfiguration;
 | 
					import ru.noties.markwon.MarkwonConfiguration;
 | 
				
			||||||
import ru.noties.markwon.SpannableBuilder;
 | 
					import ru.noties.markwon.SpannableBuilder;
 | 
				
			||||||
import ru.noties.markwon.html.api.HtmlTag;
 | 
					import ru.noties.markwon.html.HtmlTag;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.TagHandler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class ListHandler extends TagHandler {
 | 
					public class ListHandler extends TagHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1,11 +1,12 @@
 | 
				
			|||||||
package ru.noties.markwon.renderer.html2.tag;
 | 
					package ru.noties.markwon.html.impl.tag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
import android.support.annotation.Nullable;
 | 
					import android.support.annotation.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ru.noties.markwon.SpannableBuilder;
 | 
					 | 
				
			||||||
import ru.noties.markwon.MarkwonConfiguration;
 | 
					import ru.noties.markwon.MarkwonConfiguration;
 | 
				
			||||||
import ru.noties.markwon.html.api.HtmlTag;
 | 
					import ru.noties.markwon.SpannableBuilder;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.HtmlTag;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.TagHandler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public abstract class SimpleTagHandler extends TagHandler {
 | 
					public abstract class SimpleTagHandler extends TagHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1,10 +1,11 @@
 | 
				
			|||||||
package ru.noties.markwon.renderer.html2.tag;
 | 
					package ru.noties.markwon.html.impl.tag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ru.noties.markwon.MarkwonConfiguration;
 | 
					import ru.noties.markwon.MarkwonConfiguration;
 | 
				
			||||||
import ru.noties.markwon.SpannableBuilder;
 | 
					import ru.noties.markwon.SpannableBuilder;
 | 
				
			||||||
import ru.noties.markwon.html.api.HtmlTag;
 | 
					import ru.noties.markwon.html.HtmlTag;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.TagHandler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class StrikeHandler extends TagHandler {
 | 
					public class StrikeHandler extends TagHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1,10 +1,10 @@
 | 
				
			|||||||
package ru.noties.markwon.renderer.html2.tag;
 | 
					package ru.noties.markwon.html.impl.tag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
import android.support.annotation.Nullable;
 | 
					import android.support.annotation.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ru.noties.markwon.MarkwonConfiguration;
 | 
					import ru.noties.markwon.MarkwonConfiguration;
 | 
				
			||||||
import ru.noties.markwon.html.api.HtmlTag;
 | 
					import ru.noties.markwon.html.HtmlTag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class StrongEmphasisHandler extends SimpleTagHandler {
 | 
					public class StrongEmphasisHandler extends SimpleTagHandler {
 | 
				
			||||||
    @Nullable
 | 
					    @Nullable
 | 
				
			||||||
@ -1,15 +1,16 @@
 | 
				
			|||||||
package ru.noties.markwon.renderer.html2.tag;
 | 
					package ru.noties.markwon.html.impl.tag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
import android.support.annotation.Nullable;
 | 
					import android.support.annotation.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ru.noties.markwon.MarkwonConfiguration;
 | 
					import ru.noties.markwon.MarkwonConfiguration;
 | 
				
			||||||
import ru.noties.markwon.html.api.HtmlTag;
 | 
					import ru.noties.markwon.html.HtmlTag;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.impl.span.SubScriptSpan;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class SubScriptHandler extends SimpleTagHandler {
 | 
					public class SubScriptHandler extends SimpleTagHandler {
 | 
				
			||||||
    @Nullable
 | 
					    @Nullable
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull HtmlTag tag) {
 | 
					    public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull HtmlTag tag) {
 | 
				
			||||||
        return configuration.factory().subScript(configuration.theme());
 | 
					        return new SubScriptSpan();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -1,15 +1,16 @@
 | 
				
			|||||||
package ru.noties.markwon.renderer.html2.tag;
 | 
					package ru.noties.markwon.html.impl.tag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
import android.support.annotation.Nullable;
 | 
					import android.support.annotation.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ru.noties.markwon.MarkwonConfiguration;
 | 
					import ru.noties.markwon.MarkwonConfiguration;
 | 
				
			||||||
import ru.noties.markwon.html.api.HtmlTag;
 | 
					import ru.noties.markwon.html.HtmlTag;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.impl.span.SuperScriptSpan;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class SuperScriptHandler extends SimpleTagHandler {
 | 
					public class SuperScriptHandler extends SimpleTagHandler {
 | 
				
			||||||
    @Nullable
 | 
					    @Nullable
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull HtmlTag tag) {
 | 
					    public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull HtmlTag tag) {
 | 
				
			||||||
        return configuration.factory().superScript(configuration.theme());
 | 
					        return new SuperScriptSpan();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -1,10 +1,12 @@
 | 
				
			|||||||
package ru.noties.markwon.renderer.html2.tag;
 | 
					package ru.noties.markwon.html.impl.tag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
 | 
					import android.text.style.UnderlineSpan;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ru.noties.markwon.SpannableBuilder;
 | 
					 | 
				
			||||||
import ru.noties.markwon.MarkwonConfiguration;
 | 
					import ru.noties.markwon.MarkwonConfiguration;
 | 
				
			||||||
import ru.noties.markwon.html.api.HtmlTag;
 | 
					import ru.noties.markwon.SpannableBuilder;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.HtmlTag;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.TagHandler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class UnderlineHandler extends TagHandler {
 | 
					public class UnderlineHandler extends TagHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -23,7 +25,7 @@ public class UnderlineHandler extends TagHandler {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        SpannableBuilder.setSpans(
 | 
					        SpannableBuilder.setSpans(
 | 
				
			||||||
                builder,
 | 
					                builder,
 | 
				
			||||||
                configuration.factory().underline(),
 | 
					                new UnderlineSpan(),
 | 
				
			||||||
                tag.start(),
 | 
					                tag.start(),
 | 
				
			||||||
                tag.end()
 | 
					                tag.end()
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
@ -0,0 +1,239 @@
 | 
				
			|||||||
 | 
					package ru.noties.markwon.html.impl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.junit.Before;
 | 
				
			||||||
 | 
					import org.junit.Test;
 | 
				
			||||||
 | 
					import org.junit.runner.RunWith;
 | 
				
			||||||
 | 
					import org.robolectric.RobolectricTestRunner;
 | 
				
			||||||
 | 
					import org.robolectric.annotation.Config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import ix.Ix;
 | 
				
			||||||
 | 
					import ix.IxFunction;
 | 
				
			||||||
 | 
					import ru.noties.markwon.test.TestUtils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static org.junit.Assert.assertEquals;
 | 
				
			||||||
 | 
					import static org.junit.Assert.assertNotNull;
 | 
				
			||||||
 | 
					import static ru.noties.markwon.test.TestUtils.with;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@RunWith(RobolectricTestRunner.class)
 | 
				
			||||||
 | 
					@Config(manifest = Config.NONE)
 | 
				
			||||||
 | 
					public class CssInlineStyleParserTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private CssInlineStyleParser.Impl impl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Before
 | 
				
			||||||
 | 
					    public void before() {
 | 
				
			||||||
 | 
					        impl = new CssInlineStyleParser.Impl();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void simple_single_pair() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final String input = "key: value;";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final List<CssProperty> list = listProperties(input);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertEquals(1, list.size());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with(list.get(0), new TestUtils.Action<CssProperty>() {
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void apply(@NonNull CssProperty cssProperty) {
 | 
				
			||||||
 | 
					                assertEquals("key", cssProperty.key());
 | 
				
			||||||
 | 
					                assertEquals("value", cssProperty.value());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void simple_two_pairs() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final String input = "key1: value1; key2: value2;";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final List<CssProperty> list = listProperties(input);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertEquals(2, list.size());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with(list.get(0), new TestUtils.Action<CssProperty>() {
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void apply(@NonNull CssProperty cssProperty) {
 | 
				
			||||||
 | 
					                assertEquals("key1", cssProperty.key());
 | 
				
			||||||
 | 
					                assertEquals("value1", cssProperty.value());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with(list.get(1), new TestUtils.Action<CssProperty>() {
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void apply(@NonNull CssProperty cssProperty) {
 | 
				
			||||||
 | 
					                assertEquals("key2", cssProperty.key());
 | 
				
			||||||
 | 
					                assertEquals("value2", cssProperty.value());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void one_pair_eof() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final String input = "key: value";
 | 
				
			||||||
 | 
					        final List<CssProperty> list = listProperties(input);
 | 
				
			||||||
 | 
					        assertEquals(1, list.size());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with(list.get(0), new TestUtils.Action<CssProperty>() {
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void apply(@NonNull CssProperty cssProperty) {
 | 
				
			||||||
 | 
					                assertEquals("key", cssProperty.key());
 | 
				
			||||||
 | 
					                assertEquals("value", cssProperty.value());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void one_pair_eof_whitespaces() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final String input = "key: value         \n\n\t";
 | 
				
			||||||
 | 
					        final List<CssProperty> list = listProperties(input);
 | 
				
			||||||
 | 
					        assertEquals(1, list.size());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with(list.get(0), new TestUtils.Action<CssProperty>() {
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void apply(@NonNull CssProperty cssProperty) {
 | 
				
			||||||
 | 
					                assertEquals("key", cssProperty.key());
 | 
				
			||||||
 | 
					                assertEquals("value", cssProperty.value());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void white_spaces() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final String input = "\n\n\n\t    \t key1 \n\n\n\t  : \n\n\n\n   \t value1    \n\n\n\n    ; \n key2\n : \n value2 \n ; ";
 | 
				
			||||||
 | 
					        final List<CssProperty> list = listProperties(input);
 | 
				
			||||||
 | 
					        assertEquals(2, list.size());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with(list.get(0), new TestUtils.Action<CssProperty>() {
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void apply(@NonNull CssProperty cssProperty) {
 | 
				
			||||||
 | 
					                assertEquals("key1", cssProperty.key());
 | 
				
			||||||
 | 
					                assertEquals("value1", cssProperty.value());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with(list.get(1), new TestUtils.Action<CssProperty>() {
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void apply(@NonNull CssProperty cssProperty) {
 | 
				
			||||||
 | 
					                assertEquals("key2", cssProperty.key());
 | 
				
			||||||
 | 
					                assertEquals("value2", cssProperty.value());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void list_of_keys() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final String input = "key1 key2 key3 key4";
 | 
				
			||||||
 | 
					        final List<CssProperty> list = listProperties(input);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertEquals(0, list.size());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void list_of_keys_and_value() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final String input = "key1 key2 key3 key4: value4";
 | 
				
			||||||
 | 
					        final List<CssProperty> list = listProperties(input);
 | 
				
			||||||
 | 
					        assertEquals(1, list.size());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with(list.get(0), new TestUtils.Action<CssProperty>() {
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void apply(@NonNull CssProperty cssProperty) {
 | 
				
			||||||
 | 
					                assertEquals("key4", cssProperty.key());
 | 
				
			||||||
 | 
					                assertEquals("value4", cssProperty.value());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void list_of_keys_separated_by_semi_colon() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final String input = "key1;key2;key3;key4;";
 | 
				
			||||||
 | 
					        final List<CssProperty> list = listProperties(input);
 | 
				
			||||||
 | 
					        assertEquals(0, list.size());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void key_value_with_invalid_between() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final String input = "key1: value1; key2 key3: value3;";
 | 
				
			||||||
 | 
					        final List<CssProperty> list = listProperties(input);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertEquals(2, list.size());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with(list.get(0), new TestUtils.Action<CssProperty>() {
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void apply(@NonNull CssProperty cssProperty) {
 | 
				
			||||||
 | 
					                assertEquals("key1", cssProperty.key());
 | 
				
			||||||
 | 
					                assertEquals("value1", cssProperty.value());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with(list.get(1), new TestUtils.Action<CssProperty>() {
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void apply(@NonNull CssProperty cssProperty) {
 | 
				
			||||||
 | 
					                assertEquals("key3", cssProperty.key());
 | 
				
			||||||
 | 
					                assertEquals("value3", cssProperty.value());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void css_functions() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final Map<String, String> map = new HashMap<String, String>() {{
 | 
				
			||||||
 | 
					            put("attr", "\" (\" attr(href) \")\"");
 | 
				
			||||||
 | 
					            put("calc", "calc(100% - 100px)");
 | 
				
			||||||
 | 
					            put("cubic-bezier", "cubic-bezier(0.1, 0.7, 1.0, 0.1)");
 | 
				
			||||||
 | 
					            put("hsl", "hsl(120,100%,50%)");
 | 
				
			||||||
 | 
					            put("hsla", "hsla(120,100%,50%,0.3)");
 | 
				
			||||||
 | 
					            put("linear-gradient", "linear-gradient(red, yellow, blue)");
 | 
				
			||||||
 | 
					            put("radial-gradient", "radial-gradient(red, green, blue)");
 | 
				
			||||||
 | 
					            put("repeating-linear-gradient", "repeating-linear-gradient(red, yellow 10%, green 20%)");
 | 
				
			||||||
 | 
					            put("repeating-radial-gradient", "repeating-radial-gradient(red, yellow 10%, green 15%)");
 | 
				
			||||||
 | 
					            put("rgb", "rgb(255,0,0)");
 | 
				
			||||||
 | 
					            put("rgba", "rgba(255,0,0,0.3)");
 | 
				
			||||||
 | 
					            put("var", "var(--some-variable)");
 | 
				
			||||||
 | 
					            put("url", "url(\"url.gif\")");
 | 
				
			||||||
 | 
					        }};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final StringBuilder builder = new StringBuilder();
 | 
				
			||||||
 | 
					        for (Map.Entry<String, String> entry: map.entrySet()) {
 | 
				
			||||||
 | 
					            builder.append(entry.getKey())
 | 
				
			||||||
 | 
					                    .append(':')
 | 
				
			||||||
 | 
					                    .append(entry.getValue())
 | 
				
			||||||
 | 
					                    .append(';');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (CssProperty cssProperty: impl.parse(builder.toString())) {
 | 
				
			||||||
 | 
					            final String value = map.remove(cssProperty.key());
 | 
				
			||||||
 | 
					            assertNotNull(cssProperty.key(), value);
 | 
				
			||||||
 | 
					            assertEquals(cssProperty.key(), value, cssProperty.value());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertEquals(0, map.size());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNull
 | 
				
			||||||
 | 
					    private List<CssProperty> listProperties(@NonNull String input) {
 | 
				
			||||||
 | 
					        return Ix.from(impl.parse(input))
 | 
				
			||||||
 | 
					                .map(new IxFunction<CssProperty, CssProperty>() {
 | 
				
			||||||
 | 
					                    @Override
 | 
				
			||||||
 | 
					                    public CssProperty apply(CssProperty cssProperty) {
 | 
				
			||||||
 | 
					                        return cssProperty.mutate();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .toList();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,186 @@
 | 
				
			|||||||
 | 
					package ru.noties.markwon.html.impl.tag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
 | 
					import android.support.annotation.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.junit.Before;
 | 
				
			||||||
 | 
					import org.junit.Test;
 | 
				
			||||||
 | 
					import org.junit.runner.RunWith;
 | 
				
			||||||
 | 
					import org.robolectric.RobolectricTestRunner;
 | 
				
			||||||
 | 
					import org.robolectric.annotation.Config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Collections;
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import ru.noties.markwon.renderer.ImageSize;
 | 
				
			||||||
 | 
					import ru.noties.markwon.renderer.html2.CssInlineStyleParser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static org.junit.Assert.assertEquals;
 | 
				
			||||||
 | 
					import static org.junit.Assert.assertNotNull;
 | 
				
			||||||
 | 
					import static org.junit.Assert.assertNull;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@RunWith(RobolectricTestRunner.class)
 | 
				
			||||||
 | 
					@Config(manifest = Config.NONE)
 | 
				
			||||||
 | 
					public class ImageSizeParserImplTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final float DELTA = 1e-7F;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private ImageSizeParserImpl impl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Before
 | 
				
			||||||
 | 
					    public void before() {
 | 
				
			||||||
 | 
					        impl = new ImageSizeParserImpl(CssInlineStyleParser.create());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void nothing() {
 | 
				
			||||||
 | 
					        assertNull(impl.parse(Collections.<String, String>emptyMap()));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void width_height_from_style() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final String style = "width: 123; height: 321";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertImageSize(
 | 
				
			||||||
 | 
					                new ImageSize(dimension(123, null), dimension(321, null)),
 | 
				
			||||||
 | 
					                impl.parse(Collections.singletonMap("style", style))
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void style_has_higher_priority_width() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // if property is found in styles, do not lookup raw attribute
 | 
				
			||||||
 | 
					        final Map<String, String> attributes = new HashMap<String, String>() {{
 | 
				
			||||||
 | 
					            put("style", "width: 43");
 | 
				
			||||||
 | 
					            put("width", "991");
 | 
				
			||||||
 | 
					        }};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertImageSize(
 | 
				
			||||||
 | 
					                new ImageSize(dimension(43, null), null),
 | 
				
			||||||
 | 
					                impl.parse(attributes)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void style_has_higher_priority_height() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // if property is found in styles, do not lookup raw attribute
 | 
				
			||||||
 | 
					        final Map<String, String> attributes = new HashMap<String, String>() {{
 | 
				
			||||||
 | 
					            put("style", "height: 177");
 | 
				
			||||||
 | 
					            put("height", "8");
 | 
				
			||||||
 | 
					        }};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertImageSize(
 | 
				
			||||||
 | 
					                new ImageSize(null, dimension(177, null)),
 | 
				
			||||||
 | 
					                impl.parse(attributes)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void width_style_height_attributes() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final Map<String, String> attributes = new HashMap<String, String>() {{
 | 
				
			||||||
 | 
					            put("style", "width: 99");
 | 
				
			||||||
 | 
					            put("height", "7");
 | 
				
			||||||
 | 
					        }};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertImageSize(
 | 
				
			||||||
 | 
					                new ImageSize(dimension(99, null), dimension(7, null)),
 | 
				
			||||||
 | 
					                impl.parse(attributes)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void height_style_width_attributes() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final Map<String, String> attributes = new HashMap<String, String>() {{
 | 
				
			||||||
 | 
					            put("style", "height: 15");
 | 
				
			||||||
 | 
					            put("width", "88");
 | 
				
			||||||
 | 
					        }};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertImageSize(
 | 
				
			||||||
 | 
					                new ImageSize(dimension(88, null), dimension(15, null)),
 | 
				
			||||||
 | 
					                impl.parse(attributes)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void non_empty_styles_width_height_attributes() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final Map<String, String> attributes = new HashMap<String, String>() {{
 | 
				
			||||||
 | 
					            put("style", "key1: value1; width0: 123; height0: 99");
 | 
				
			||||||
 | 
					            put("width", "40");
 | 
				
			||||||
 | 
					            put("height", "77");
 | 
				
			||||||
 | 
					        }};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertImageSize(
 | 
				
			||||||
 | 
					                new ImageSize(dimension(40, null), dimension(77, null)),
 | 
				
			||||||
 | 
					                impl.parse(attributes)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void dimension_units() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final Map<String, ImageSize.Dimension> map = new HashMap<String, ImageSize.Dimension>() {{
 | 
				
			||||||
 | 
					            put("100", dimension(100, null));
 | 
				
			||||||
 | 
					            put("100%", dimension(100, "%"));
 | 
				
			||||||
 | 
					            put("1%", dimension(1, "%"));
 | 
				
			||||||
 | 
					            put("0.2em", dimension(0.2F, "em"));
 | 
				
			||||||
 | 
					            put("155px", dimension(155, "px"));
 | 
				
			||||||
 | 
					            put("67blah", dimension(67, "blah"));
 | 
				
			||||||
 | 
					            put("-1", dimension(-1, null));
 | 
				
			||||||
 | 
					            put("-0.01pt", dimension(-0.01F, "pt"));
 | 
				
			||||||
 | 
					        }};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (Map.Entry<String, ImageSize.Dimension> entry : map.entrySet()) {
 | 
				
			||||||
 | 
					            assertDimension(entry.getKey(), entry.getValue(), impl.dimension(entry.getKey()));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void bad_dimension() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final String[] dimensions = {
 | 
				
			||||||
 | 
					                "calc(5px + 10rem)",
 | 
				
			||||||
 | 
					                "whataver6",
 | 
				
			||||||
 | 
					                "165 165",
 | 
				
			||||||
 | 
					                "!@#$%^&*(%"
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (String dimension : dimensions) {
 | 
				
			||||||
 | 
					            assertNull(dimension, impl.dimension(dimension));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static void assertImageSize(@Nullable ImageSize expected, @Nullable ImageSize actual) {
 | 
				
			||||||
 | 
					        if (expected == null) {
 | 
				
			||||||
 | 
					            assertNull(actual);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            assertNotNull(actual);
 | 
				
			||||||
 | 
					            assertDimension("width", expected.width, actual.width);
 | 
				
			||||||
 | 
					            assertDimension("height", expected.height, actual.height);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static void assertDimension(
 | 
				
			||||||
 | 
					            @NonNull String name,
 | 
				
			||||||
 | 
					            @Nullable ImageSize.Dimension expected,
 | 
				
			||||||
 | 
					            @Nullable ImageSize.Dimension actual) {
 | 
				
			||||||
 | 
					        if (expected == null) {
 | 
				
			||||||
 | 
					            assertNull(name, actual);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            assertNotNull(name, actual);
 | 
				
			||||||
 | 
					            assertEquals(name, expected.value, actual.value, DELTA);
 | 
				
			||||||
 | 
					            assertEquals(name, expected.unit, actual.unit);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNull
 | 
				
			||||||
 | 
					    private static ImageSize.Dimension dimension(float value, @Nullable String unit) {
 | 
				
			||||||
 | 
					        return new ImageSize.Dimension(value, unit);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,37 +0,0 @@
 | 
				
			|||||||
apply plugin: 'com.android.library'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
android {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    compileSdkVersion config['compile-sdk']
 | 
					 | 
				
			||||||
    buildToolsVersion config['build-tools']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    defaultConfig {
 | 
					 | 
				
			||||||
        minSdkVersion config['min-sdk']
 | 
					 | 
				
			||||||
        targetSdkVersion config['target-sdk']
 | 
					 | 
				
			||||||
        versionCode 1
 | 
					 | 
				
			||||||
        versionName version
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    lintOptions {
 | 
					 | 
				
			||||||
        // okio....
 | 
					 | 
				
			||||||
        disable 'InvalidPackage'
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
dependencies {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    api project(':markwon')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    deps.with {
 | 
					 | 
				
			||||||
        api it['android-svg']
 | 
					 | 
				
			||||||
        api it['android-gif']
 | 
					 | 
				
			||||||
        api it['okhttp']
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    deps['test'].with {
 | 
					 | 
				
			||||||
        testImplementation it['junit']
 | 
					 | 
				
			||||||
        testImplementation it['robolectric']
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
registerArtifact(this)
 | 
					 | 
				
			||||||
@ -1,3 +0,0 @@
 | 
				
			|||||||
POM_NAME=Markwon-Image-Loader
 | 
					 | 
				
			||||||
POM_ARTIFACT_ID=markwon-image-loader
 | 
					 | 
				
			||||||
POM_PACKAGING=aar
 | 
					 | 
				
			||||||
@ -1 +0,0 @@
 | 
				
			|||||||
<manifest package="ru.noties.markwon.il" />
 | 
					 | 
				
			||||||
@ -1,405 +0,0 @@
 | 
				
			|||||||
package ru.noties.markwon.il;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import android.content.res.Resources;
 | 
					 | 
				
			||||||
import android.graphics.drawable.Drawable;
 | 
					 | 
				
			||||||
import android.net.Uri;
 | 
					 | 
				
			||||||
import android.os.Handler;
 | 
					 | 
				
			||||||
import android.os.Looper;
 | 
					 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					 | 
				
			||||||
import android.support.annotation.Nullable;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.io.IOException;
 | 
					 | 
				
			||||||
import java.io.InputStream;
 | 
					 | 
				
			||||||
import java.lang.ref.WeakReference;
 | 
					 | 
				
			||||||
import java.util.ArrayList;
 | 
					 | 
				
			||||||
import java.util.HashMap;
 | 
					 | 
				
			||||||
import java.util.List;
 | 
					 | 
				
			||||||
import java.util.Map;
 | 
					 | 
				
			||||||
import java.util.concurrent.ExecutorService;
 | 
					 | 
				
			||||||
import java.util.concurrent.Executors;
 | 
					 | 
				
			||||||
import java.util.concurrent.Future;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import okhttp3.OkHttpClient;
 | 
					 | 
				
			||||||
import ru.noties.markwon.image.AsyncDrawable;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public class AsyncDrawableLoader implements AsyncDrawable.Loader {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @NonNull
 | 
					 | 
				
			||||||
    public static AsyncDrawableLoader create() {
 | 
					 | 
				
			||||||
        return builder().build();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @NonNull
 | 
					 | 
				
			||||||
    public static AsyncDrawableLoader.Builder builder() {
 | 
					 | 
				
			||||||
        return new Builder();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private final ExecutorService executorService;
 | 
					 | 
				
			||||||
    private final Handler mainThread;
 | 
					 | 
				
			||||||
    private final Drawable errorDrawable;
 | 
					 | 
				
			||||||
    private final Map<String, SchemeHandler> schemeHandlers;
 | 
					 | 
				
			||||||
    private final List<MediaDecoder> mediaDecoders;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private final Map<String, Future<?>> requests;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    AsyncDrawableLoader(Builder builder) {
 | 
					 | 
				
			||||||
        this.executorService = builder.executorService;
 | 
					 | 
				
			||||||
        this.mainThread = new Handler(Looper.getMainLooper());
 | 
					 | 
				
			||||||
        this.errorDrawable = builder.errorDrawable;
 | 
					 | 
				
			||||||
        this.schemeHandlers = builder.schemeHandlers;
 | 
					 | 
				
			||||||
        this.mediaDecoders = builder.mediaDecoders;
 | 
					 | 
				
			||||||
        this.requests = new HashMap<>(3);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public void load(@NonNull String destination, @NonNull AsyncDrawable drawable) {
 | 
					 | 
				
			||||||
        // if drawable is not a link -> show loading placeholder...
 | 
					 | 
				
			||||||
        requests.put(destination, execute(destination, drawable));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public void cancel(@NonNull String destination) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        final Future<?> request = requests.remove(destination);
 | 
					 | 
				
			||||||
        if (request != null) {
 | 
					 | 
				
			||||||
            request.cancel(true);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (SchemeHandler schemeHandler : schemeHandlers.values()) {
 | 
					 | 
				
			||||||
            schemeHandler.cancel(destination);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private Future<?> execute(@NonNull final String destination, @NonNull AsyncDrawable drawable) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        final WeakReference<AsyncDrawable> reference = new WeakReference<AsyncDrawable>(drawable);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // todo: should we cancel pending request for the same destination?
 | 
					 | 
				
			||||||
        //      we _could_ but there is possibility that one resource is request in multiple places
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // todo: error handing (simply applying errorDrawable is not a good solution
 | 
					 | 
				
			||||||
        //      as reason for an error is unclear (no scheme handler, no input data, error decoding, etc)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // todo: more efficient ImageMediaDecoder... BitmapFactory.decodeStream is a bit not optimal
 | 
					 | 
				
			||||||
        //      for big images for sure. We _could_ introduce internal Drawable that will check for
 | 
					 | 
				
			||||||
        //      image bounds (but we will need to cache inputStream in order to inspect and optimize
 | 
					 | 
				
			||||||
        //      input image...)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return executorService.submit(new Runnable() {
 | 
					 | 
				
			||||||
            @Override
 | 
					 | 
				
			||||||
            public void run() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                final ImageItem item;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                final Uri uri = Uri.parse(destination);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                final SchemeHandler schemeHandler = schemeHandlers.get(uri.getScheme());
 | 
					 | 
				
			||||||
                if (schemeHandler != null) {
 | 
					 | 
				
			||||||
                    item = schemeHandler.handle(destination, uri);
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    item = null;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                final InputStream inputStream = item != null
 | 
					 | 
				
			||||||
                        ? item.inputStream()
 | 
					 | 
				
			||||||
                        : null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                Drawable result = null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (inputStream != null) {
 | 
					 | 
				
			||||||
                    try {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        final String fileName = item.fileName();
 | 
					 | 
				
			||||||
                        final MediaDecoder mediaDecoder = fileName != null
 | 
					 | 
				
			||||||
                                ? mediaDecoderFromFile(fileName)
 | 
					 | 
				
			||||||
                                : mediaDecoderFromContentType(item.contentType());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        if (mediaDecoder != null) {
 | 
					 | 
				
			||||||
                            result = mediaDecoder.decode(inputStream);
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    } finally {
 | 
					 | 
				
			||||||
                        try {
 | 
					 | 
				
			||||||
                            inputStream.close();
 | 
					 | 
				
			||||||
                        } catch (IOException e) {
 | 
					 | 
				
			||||||
                            // ignored
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // if result is null, we assume it's an error
 | 
					 | 
				
			||||||
                if (result == null) {
 | 
					 | 
				
			||||||
                    result = errorDrawable;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (result != null) {
 | 
					 | 
				
			||||||
                    final Drawable out = result;
 | 
					 | 
				
			||||||
                    mainThread.post(new Runnable() {
 | 
					 | 
				
			||||||
                        @Override
 | 
					 | 
				
			||||||
                        public void run() {
 | 
					 | 
				
			||||||
                            final AsyncDrawable asyncDrawable = reference.get();
 | 
					 | 
				
			||||||
                            if (asyncDrawable != null && asyncDrawable.isAttached()) {
 | 
					 | 
				
			||||||
                                asyncDrawable.setResult(out);
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                requests.remove(destination);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    private MediaDecoder mediaDecoderFromFile(@NonNull String fileName) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        MediaDecoder out = null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (MediaDecoder mediaDecoder : mediaDecoders) {
 | 
					 | 
				
			||||||
            if (mediaDecoder.canDecodeByFileName(fileName)) {
 | 
					 | 
				
			||||||
                out = mediaDecoder;
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return out;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    private MediaDecoder mediaDecoderFromContentType(@Nullable String contentType) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        MediaDecoder out = null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (MediaDecoder mediaDecoder : mediaDecoders) {
 | 
					 | 
				
			||||||
            if (mediaDecoder.canDecodeByContentType(contentType)) {
 | 
					 | 
				
			||||||
                out = mediaDecoder;
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return out;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // todo: as now we have different layers of abstraction (for scheme handling and media decoding)
 | 
					 | 
				
			||||||
    //      we no longer should add dependencies implicitly, it would be way better to allow adding
 | 
					 | 
				
			||||||
    //      multiple artifacts (file, data, network, svg, gif)... at least, maybe we can extract API
 | 
					 | 
				
			||||||
    //      for this module (without implementations), but keep _all-in_ (fat) artifact with all of these.
 | 
					 | 
				
			||||||
    public static class Builder {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /**
 | 
					 | 
				
			||||||
         * @deprecated 2.0.0 add {@link NetworkSchemeHandler} directly
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        @Deprecated
 | 
					 | 
				
			||||||
        private OkHttpClient client;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /**
 | 
					 | 
				
			||||||
         * @deprecated 2.0.0 construct {@link MediaDecoder} and {@link SchemeHandler} appropriately
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        @Deprecated
 | 
					 | 
				
			||||||
        private Resources resources;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private ExecutorService executorService;
 | 
					 | 
				
			||||||
        private Drawable errorDrawable;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // @since 1.1.0
 | 
					 | 
				
			||||||
        private final List<MediaDecoder> mediaDecoders = new ArrayList<>(3);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // @since 2.0.0
 | 
					 | 
				
			||||||
        private final Map<String, SchemeHandler> schemeHandlers = new HashMap<>(3);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /**
 | 
					 | 
				
			||||||
         * @deprecated 2.0.0 add {@link NetworkSchemeHandler} directly
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        @NonNull
 | 
					 | 
				
			||||||
        @Deprecated
 | 
					 | 
				
			||||||
        public Builder client(@NonNull OkHttpClient client) {
 | 
					 | 
				
			||||||
            this.client = client;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /**
 | 
					 | 
				
			||||||
         * Supplied resources argument will be used to open files from assets directory
 | 
					 | 
				
			||||||
         * and to create default {@link MediaDecoder}\'s which require resources instance
 | 
					 | 
				
			||||||
         *
 | 
					 | 
				
			||||||
         * @return self
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        @NonNull
 | 
					 | 
				
			||||||
        public Builder resources(@NonNull Resources resources) {
 | 
					 | 
				
			||||||
            this.resources = resources;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        @NonNull
 | 
					 | 
				
			||||||
        public Builder executorService(@NonNull ExecutorService executorService) {
 | 
					 | 
				
			||||||
            this.executorService = executorService;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        @NonNull
 | 
					 | 
				
			||||||
        public Builder errorDrawable(@NonNull Drawable errorDrawable) {
 | 
					 | 
				
			||||||
            this.errorDrawable = errorDrawable;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /**
 | 
					 | 
				
			||||||
         * @since 2.0.0
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        @SuppressWarnings("UnusedReturnValue")
 | 
					 | 
				
			||||||
        @NonNull
 | 
					 | 
				
			||||||
        public Builder addSchemeHandler(@NonNull SchemeHandler schemeHandler) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            SchemeHandler previous;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            for (String scheme : schemeHandler.schemes()) {
 | 
					 | 
				
			||||||
                previous = schemeHandlers.put(scheme, schemeHandler);
 | 
					 | 
				
			||||||
                if (previous != null) {
 | 
					 | 
				
			||||||
                    throw new IllegalStateException(String.format("Multiple scheme handlers handle " +
 | 
					 | 
				
			||||||
                            "the same scheme: `%s`, %s %s", scheme, previous, schemeHandler));
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /**
 | 
					 | 
				
			||||||
         * @see #addMediaDecoder(MediaDecoder)
 | 
					 | 
				
			||||||
         * @see #addMediaDecoders(MediaDecoder...)
 | 
					 | 
				
			||||||
         * @see #addMediaDecoders(Iterable)
 | 
					 | 
				
			||||||
         * @since 1.1.0
 | 
					 | 
				
			||||||
         * @deprecated 2.0.0
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        @Deprecated
 | 
					 | 
				
			||||||
        @NonNull
 | 
					 | 
				
			||||||
        public Builder mediaDecoders(@NonNull List<MediaDecoder> mediaDecoders) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // previously it was clearing before adding
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            for (MediaDecoder mediaDecoder : mediaDecoders) {
 | 
					 | 
				
			||||||
                this.mediaDecoders.add(requireNonNull(mediaDecoder));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /**
 | 
					 | 
				
			||||||
         * @see #addMediaDecoder(MediaDecoder)
 | 
					 | 
				
			||||||
         * @see #addMediaDecoders(MediaDecoder...)
 | 
					 | 
				
			||||||
         * @see #addMediaDecoders(Iterable)
 | 
					 | 
				
			||||||
         * @since 1.1.0
 | 
					 | 
				
			||||||
         * @deprecated 2.0.0
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        @NonNull
 | 
					 | 
				
			||||||
        @Deprecated
 | 
					 | 
				
			||||||
        public Builder mediaDecoders(MediaDecoder... mediaDecoders) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // previously it was clearing before adding
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            final int length = mediaDecoders != null
 | 
					 | 
				
			||||||
                    ? mediaDecoders.length
 | 
					 | 
				
			||||||
                    : 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (length > 0) {
 | 
					 | 
				
			||||||
                for (int i = 0; i < length; i++) {
 | 
					 | 
				
			||||||
                    this.mediaDecoders.add(requireNonNull(mediaDecoders[i]));
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /**
 | 
					 | 
				
			||||||
         * @see SvgMediaDecoder
 | 
					 | 
				
			||||||
         * @see GifMediaDecoder
 | 
					 | 
				
			||||||
         * @see ImageMediaDecoder
 | 
					 | 
				
			||||||
         * @since 2.0.0
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        @NonNull
 | 
					 | 
				
			||||||
        public Builder addMediaDecoder(@NonNull MediaDecoder mediaDecoder) {
 | 
					 | 
				
			||||||
            mediaDecoders.add(mediaDecoder);
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /**
 | 
					 | 
				
			||||||
         * @see SvgMediaDecoder
 | 
					 | 
				
			||||||
         * @see GifMediaDecoder
 | 
					 | 
				
			||||||
         * @see ImageMediaDecoder
 | 
					 | 
				
			||||||
         * @since 2.0.0
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        @NonNull
 | 
					 | 
				
			||||||
        public Builder addMediaDecoders(@NonNull Iterable<MediaDecoder> mediaDecoders) {
 | 
					 | 
				
			||||||
            for (MediaDecoder mediaDecoder : mediaDecoders) {
 | 
					 | 
				
			||||||
                this.mediaDecoders.add(requireNonNull(mediaDecoder));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /**
 | 
					 | 
				
			||||||
         * @see SvgMediaDecoder
 | 
					 | 
				
			||||||
         * @see GifMediaDecoder
 | 
					 | 
				
			||||||
         * @see ImageMediaDecoder
 | 
					 | 
				
			||||||
         * @since 2.0.0
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        @NonNull
 | 
					 | 
				
			||||||
        public Builder addMediaDecoders(MediaDecoder... mediaDecoders) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            final int length = mediaDecoders != null
 | 
					 | 
				
			||||||
                    ? mediaDecoders.length
 | 
					 | 
				
			||||||
                    : 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (length > 0) {
 | 
					 | 
				
			||||||
                for (int i = 0; i < length; i++) {
 | 
					 | 
				
			||||||
                    this.mediaDecoders.add(requireNonNull(mediaDecoders[i]));
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        @NonNull
 | 
					 | 
				
			||||||
        public AsyncDrawableLoader build() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // I think we should deprecate this...
 | 
					 | 
				
			||||||
            if (resources == null) {
 | 
					 | 
				
			||||||
                resources = Resources.getSystem();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (executorService == null) {
 | 
					 | 
				
			||||||
                // @since 2.0.0 we are using newCachedThreadPool instead
 | 
					 | 
				
			||||||
                // of `okHttpClient.dispatcher().executorService()`
 | 
					 | 
				
			||||||
                executorService = Executors.newCachedThreadPool();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // @since 2.0.0
 | 
					 | 
				
			||||||
            // put default scheme handlers (to mimic previous behavior)
 | 
					 | 
				
			||||||
            // remove in 3.0.0 with plugins
 | 
					 | 
				
			||||||
            if (schemeHandlers.size() == 0) {
 | 
					 | 
				
			||||||
                if (client == null) {
 | 
					 | 
				
			||||||
                    client = new OkHttpClient();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                addSchemeHandler(NetworkSchemeHandler.create(client));
 | 
					 | 
				
			||||||
                addSchemeHandler(FileSchemeHandler.createWithAssets(resources.getAssets()));
 | 
					 | 
				
			||||||
                addSchemeHandler(DataUriSchemeHandler.create());
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // add default media decoders if not specified
 | 
					 | 
				
			||||||
            // remove in 3.0.0 with plugins
 | 
					 | 
				
			||||||
            if (mediaDecoders.size() == 0) {
 | 
					 | 
				
			||||||
                mediaDecoders.add(SvgMediaDecoder.create(resources));
 | 
					 | 
				
			||||||
                mediaDecoders.add(GifMediaDecoder.create(true));
 | 
					 | 
				
			||||||
                mediaDecoders.add(ImageMediaDecoder.create(resources));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return new AsyncDrawableLoader(this);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // @since 2.0.0
 | 
					 | 
				
			||||||
    @NonNull
 | 
					 | 
				
			||||||
    private static <T> T requireNonNull(@Nullable T t) {
 | 
					 | 
				
			||||||
        if (t == null) {
 | 
					 | 
				
			||||||
            throw new NullPointerException();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return t;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,60 +0,0 @@
 | 
				
			|||||||
package ru.noties.markwon.il;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import android.support.annotation.Nullable;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public class DataUri {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private final String contentType;
 | 
					 | 
				
			||||||
    private final boolean base64;
 | 
					 | 
				
			||||||
    private final String data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public DataUri(@Nullable String contentType, boolean base64, @Nullable String data) {
 | 
					 | 
				
			||||||
        this.contentType = contentType;
 | 
					 | 
				
			||||||
        this.base64 = base64;
 | 
					 | 
				
			||||||
        this.data = data;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    public String contentType() {
 | 
					 | 
				
			||||||
        return contentType;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public boolean base64() {
 | 
					 | 
				
			||||||
        return base64;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    public String data() {
 | 
					 | 
				
			||||||
        return data;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public String toString() {
 | 
					 | 
				
			||||||
        return "DataUri{" +
 | 
					 | 
				
			||||||
                "contentType='" + contentType + '\'' +
 | 
					 | 
				
			||||||
                ", base64=" + base64 +
 | 
					 | 
				
			||||||
                ", data='" + data + '\'' +
 | 
					 | 
				
			||||||
                '}';
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public boolean equals(Object o) {
 | 
					 | 
				
			||||||
        if (this == o) return true;
 | 
					 | 
				
			||||||
        if (o == null || getClass() != o.getClass()) return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        DataUri dataUri = (DataUri) o;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (base64 != dataUri.base64) return false;
 | 
					 | 
				
			||||||
        if (contentType != null ? !contentType.equals(dataUri.contentType) : dataUri.contentType != null)
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        return data != null ? data.equals(dataUri.data) : dataUri.data == null;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public int hashCode() {
 | 
					 | 
				
			||||||
        int result = contentType != null ? contentType.hashCode() : 0;
 | 
					 | 
				
			||||||
        result = 31 * result + (base64 ? 1 : 0);
 | 
					 | 
				
			||||||
        result = 31 * result + (data != null ? data.hashCode() : 0);
 | 
					 | 
				
			||||||
        return result;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,41 +0,0 @@
 | 
				
			|||||||
package ru.noties.markwon.il;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					 | 
				
			||||||
import android.support.annotation.Nullable;
 | 
					 | 
				
			||||||
import android.text.TextUtils;
 | 
					 | 
				
			||||||
import android.util.Base64;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public abstract class DataUriDecoder {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    public abstract byte[] decode(@NonNull DataUri dataUri);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @NonNull
 | 
					 | 
				
			||||||
    public static DataUriDecoder create() {
 | 
					 | 
				
			||||||
        return new Impl();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    static class Impl extends DataUriDecoder {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        @Nullable
 | 
					 | 
				
			||||||
        @Override
 | 
					 | 
				
			||||||
        public byte[] decode(@NonNull DataUri dataUri) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            final String data = dataUri.data();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (!TextUtils.isEmpty(data)) {
 | 
					 | 
				
			||||||
                try {
 | 
					 | 
				
			||||||
                    if (dataUri.base64()) {
 | 
					 | 
				
			||||||
                        return Base64.decode(data.getBytes("UTF-8"), Base64.DEFAULT);
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        return data.getBytes("UTF-8");
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                } catch (Throwable t) {
 | 
					 | 
				
			||||||
                    return null;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                return null;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,79 +0,0 @@
 | 
				
			|||||||
package ru.noties.markwon.il;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					 | 
				
			||||||
import android.support.annotation.Nullable;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public abstract class DataUriParser {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    public abstract DataUri parse(@NonNull String input);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @NonNull
 | 
					 | 
				
			||||||
    public static DataUriParser create() {
 | 
					 | 
				
			||||||
        return new Impl();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    static class Impl extends DataUriParser {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        @Nullable
 | 
					 | 
				
			||||||
        @Override
 | 
					 | 
				
			||||||
        public DataUri parse(@NonNull String input) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            final int index = input.indexOf(',');
 | 
					 | 
				
			||||||
            // we expect exactly one comma
 | 
					 | 
				
			||||||
            if (index < 0) {
 | 
					 | 
				
			||||||
                return null;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            final String contentType;
 | 
					 | 
				
			||||||
            final boolean base64;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (index > 0) {
 | 
					 | 
				
			||||||
                final String part = input.substring(0, index);
 | 
					 | 
				
			||||||
                final String[] parts = part.split(";");
 | 
					 | 
				
			||||||
                final int length = parts.length;
 | 
					 | 
				
			||||||
                if (length > 0) {
 | 
					 | 
				
			||||||
                    // if one: either content-type or base64
 | 
					 | 
				
			||||||
                    if (length == 1) {
 | 
					 | 
				
			||||||
                        final String value = parts[0];
 | 
					 | 
				
			||||||
                        if ("base64".equals(value)) {
 | 
					 | 
				
			||||||
                            contentType = null;
 | 
					 | 
				
			||||||
                            base64 = true;
 | 
					 | 
				
			||||||
                        } else {
 | 
					 | 
				
			||||||
                            contentType = value.indexOf('/') > -1
 | 
					 | 
				
			||||||
                                    ? value
 | 
					 | 
				
			||||||
                                    : null;
 | 
					 | 
				
			||||||
                            base64 = false;
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        contentType = parts[0].indexOf('/') > -1
 | 
					 | 
				
			||||||
                                ? parts[0]
 | 
					 | 
				
			||||||
                                : null;
 | 
					 | 
				
			||||||
                        base64 = "base64".equals(parts[length - 1]);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    contentType = null;
 | 
					 | 
				
			||||||
                    base64 = false;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                contentType = null;
 | 
					 | 
				
			||||||
                base64 = false;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            final String data;
 | 
					 | 
				
			||||||
            if (index < input.length()) {
 | 
					 | 
				
			||||||
                final String value = input.substring(index + 1, input.length()).replaceAll("\n", "");
 | 
					 | 
				
			||||||
                if (value.length() == 0) {
 | 
					 | 
				
			||||||
                    data = null;
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    data = value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                data = null;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return new DataUri(contentType, base64, data);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,74 +0,0 @@
 | 
				
			|||||||
package ru.noties.markwon.il;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import android.net.Uri;
 | 
					 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					 | 
				
			||||||
import android.support.annotation.Nullable;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.io.ByteArrayInputStream;
 | 
					 | 
				
			||||||
import java.util.Collection;
 | 
					 | 
				
			||||||
import java.util.Collections;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * @since 2.0.0
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public class DataUriSchemeHandler extends SchemeHandler {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @NonNull
 | 
					 | 
				
			||||||
    public static DataUriSchemeHandler create() {
 | 
					 | 
				
			||||||
        return new DataUriSchemeHandler(DataUriParser.create(), DataUriDecoder.create());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private static final String START = "data:";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private final DataUriParser uriParser;
 | 
					 | 
				
			||||||
    private final DataUriDecoder uriDecoder;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @SuppressWarnings("WeakerAccess")
 | 
					 | 
				
			||||||
    DataUriSchemeHandler(@NonNull DataUriParser uriParser, @NonNull DataUriDecoder uriDecoder) {
 | 
					 | 
				
			||||||
        this.uriParser = uriParser;
 | 
					 | 
				
			||||||
        this.uriDecoder = uriDecoder;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public ImageItem handle(@NonNull String raw, @NonNull Uri uri) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (!raw.startsWith(START)) {
 | 
					 | 
				
			||||||
            return null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        String part = raw.substring(START.length());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // this part is added to support `data://` with which this functionality was released
 | 
					 | 
				
			||||||
        if (part.startsWith("//")) {
 | 
					 | 
				
			||||||
            part = part.substring(2);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        final DataUri dataUri = uriParser.parse(part);
 | 
					 | 
				
			||||||
        if (dataUri == null) {
 | 
					 | 
				
			||||||
            return null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        final byte[] bytes = uriDecoder.decode(dataUri);
 | 
					 | 
				
			||||||
        if (bytes == null) {
 | 
					 | 
				
			||||||
            return null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return new ImageItem(
 | 
					 | 
				
			||||||
                dataUri.contentType(),
 | 
					 | 
				
			||||||
                new ByteArrayInputStream(bytes),
 | 
					 | 
				
			||||||
                null
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public void cancel(@NonNull String raw) {
 | 
					 | 
				
			||||||
        // no op
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @NonNull
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public Collection<String> schemes() {
 | 
					 | 
				
			||||||
        return Collections.singleton("data");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,13 +0,0 @@
 | 
				
			|||||||
package ru.noties.markwon.il;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import android.graphics.drawable.Drawable;
 | 
					 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
abstract class DrawableUtils {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    static void intrinsicBounds(@NonNull Drawable drawable) {
 | 
					 | 
				
			||||||
        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private DrawableUtils() {}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,109 +0,0 @@
 | 
				
			|||||||
package ru.noties.markwon.il;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import android.content.res.AssetManager;
 | 
					 | 
				
			||||||
import android.net.Uri;
 | 
					 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					 | 
				
			||||||
import android.support.annotation.Nullable;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.io.BufferedInputStream;
 | 
					 | 
				
			||||||
import java.io.File;
 | 
					 | 
				
			||||||
import java.io.FileInputStream;
 | 
					 | 
				
			||||||
import java.io.FileNotFoundException;
 | 
					 | 
				
			||||||
import java.io.IOException;
 | 
					 | 
				
			||||||
import java.io.InputStream;
 | 
					 | 
				
			||||||
import java.util.Collection;
 | 
					 | 
				
			||||||
import java.util.Collections;
 | 
					 | 
				
			||||||
import java.util.List;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * @since 2.0.0
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public class FileSchemeHandler extends SchemeHandler {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @NonNull
 | 
					 | 
				
			||||||
    public static FileSchemeHandler createWithAssets(@NonNull AssetManager assetManager) {
 | 
					 | 
				
			||||||
        return new FileSchemeHandler(assetManager);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @NonNull
 | 
					 | 
				
			||||||
    public static FileSchemeHandler create() {
 | 
					 | 
				
			||||||
        return new FileSchemeHandler(null);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private static final String FILE_ANDROID_ASSETS = "android_asset";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    private final AssetManager assetManager;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @SuppressWarnings("WeakerAccess")
 | 
					 | 
				
			||||||
    FileSchemeHandler(@Nullable AssetManager assetManager) {
 | 
					 | 
				
			||||||
        this.assetManager = assetManager;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public ImageItem handle(@NonNull String raw, @NonNull Uri uri) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        final List<String> segments = uri.getPathSegments();
 | 
					 | 
				
			||||||
        if (segments == null
 | 
					 | 
				
			||||||
                || segments.size() == 0) {
 | 
					 | 
				
			||||||
            // pointing to file & having no path segments is no use
 | 
					 | 
				
			||||||
            return null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        final ImageItem out;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        InputStream inputStream = null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        final boolean assets = FILE_ANDROID_ASSETS.equals(segments.get(0));
 | 
					 | 
				
			||||||
        final String fileName = uri.getLastPathSegment();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (assets) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // no handling of assets here if we have no assetsManager
 | 
					 | 
				
			||||||
            if (assetManager != null) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                final StringBuilder path = new StringBuilder();
 | 
					 | 
				
			||||||
                for (int i = 1, size = segments.size(); i < size; i++) {
 | 
					 | 
				
			||||||
                    if (i != 1) {
 | 
					 | 
				
			||||||
                        path.append('/');
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    path.append(segments.get(i));
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                // load assets
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                try {
 | 
					 | 
				
			||||||
                    inputStream = assetManager.open(path.toString());
 | 
					 | 
				
			||||||
                } catch (IOException e) {
 | 
					 | 
				
			||||||
                    e.printStackTrace();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            try {
 | 
					 | 
				
			||||||
                inputStream = new BufferedInputStream(new FileInputStream(new File(uri.getPath())));
 | 
					 | 
				
			||||||
            } catch (FileNotFoundException e) {
 | 
					 | 
				
			||||||
                e.printStackTrace();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (inputStream != null) {
 | 
					 | 
				
			||||||
            out = new ImageItem(fileName, inputStream, fileName);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            out = null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return out;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public void cancel(@NonNull String raw) {
 | 
					 | 
				
			||||||
        // no op
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @NonNull
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public Collection<String> schemes() {
 | 
					 | 
				
			||||||
        return Collections.singleton("file");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,91 +0,0 @@
 | 
				
			|||||||
package ru.noties.markwon.il;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import android.graphics.drawable.Drawable;
 | 
					 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					 | 
				
			||||||
import android.support.annotation.Nullable;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.io.ByteArrayOutputStream;
 | 
					 | 
				
			||||||
import java.io.IOException;
 | 
					 | 
				
			||||||
import java.io.InputStream;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import pl.droidsonroids.gif.GifDrawable;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * @since 1.1.0
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
@SuppressWarnings("WeakerAccess")
 | 
					 | 
				
			||||||
public class GifMediaDecoder extends MediaDecoder {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected static final String CONTENT_TYPE_GIF = "image/gif";
 | 
					 | 
				
			||||||
    protected static final String FILE_EXTENSION_GIF = ".gif";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @NonNull
 | 
					 | 
				
			||||||
    public static GifMediaDecoder create(boolean autoPlayGif) {
 | 
					 | 
				
			||||||
        return new GifMediaDecoder(autoPlayGif);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private final boolean autoPlayGif;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected GifMediaDecoder(boolean autoPlayGif) {
 | 
					 | 
				
			||||||
        this.autoPlayGif = autoPlayGif;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public boolean canDecodeByContentType(@Nullable String contentType) {
 | 
					 | 
				
			||||||
        return CONTENT_TYPE_GIF.equals(contentType);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public boolean canDecodeByFileName(@NonNull String fileName) {
 | 
					 | 
				
			||||||
        return fileName.endsWith(FILE_EXTENSION_GIF);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public Drawable decode(@NonNull InputStream inputStream) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Drawable out = null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        final byte[] bytes = readBytes(inputStream);
 | 
					 | 
				
			||||||
        if (bytes != null) {
 | 
					 | 
				
			||||||
            try {
 | 
					 | 
				
			||||||
                out = newGifDrawable(bytes);
 | 
					 | 
				
			||||||
                DrawableUtils.intrinsicBounds(out);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (!autoPlayGif) {
 | 
					 | 
				
			||||||
                    ((GifDrawable) out).pause();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            } catch (IOException e) {
 | 
					 | 
				
			||||||
                e.printStackTrace();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return out;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @NonNull
 | 
					 | 
				
			||||||
    protected Drawable newGifDrawable(@NonNull byte[] bytes) throws IOException {
 | 
					 | 
				
			||||||
        return new GifDrawable(bytes);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    protected static byte[] readBytes(@NonNull InputStream stream) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        byte[] out = null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
 | 
					 | 
				
			||||||
            final int length = 1024 * 8;
 | 
					 | 
				
			||||||
            final byte[] buffer = new byte[length];
 | 
					 | 
				
			||||||
            int read;
 | 
					 | 
				
			||||||
            while ((read = stream.read(buffer, 0, length)) != -1) {
 | 
					 | 
				
			||||||
                outputStream.write(buffer, 0, read);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            out = outputStream.toByteArray();
 | 
					 | 
				
			||||||
        } catch (IOException e) {
 | 
					 | 
				
			||||||
            e.printStackTrace();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return out;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,39 +0,0 @@
 | 
				
			|||||||
package ru.noties.markwon.il;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import android.support.annotation.Nullable;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.io.InputStream;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * @since 2.0.0
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public class ImageItem {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private final String contentType;
 | 
					 | 
				
			||||||
    private final InputStream inputStream;
 | 
					 | 
				
			||||||
    private final String fileName;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public ImageItem(
 | 
					 | 
				
			||||||
            @Nullable String contentType,
 | 
					 | 
				
			||||||
            @Nullable InputStream inputStream,
 | 
					 | 
				
			||||||
            @Nullable String fileName) {
 | 
					 | 
				
			||||||
        this.contentType = contentType;
 | 
					 | 
				
			||||||
        this.inputStream = inputStream;
 | 
					 | 
				
			||||||
        this.fileName = fileName;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    public String contentType() {
 | 
					 | 
				
			||||||
        return contentType;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    public InputStream inputStream() {
 | 
					 | 
				
			||||||
        return inputStream;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    public String fileName() {
 | 
					 | 
				
			||||||
        return fileName;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,60 +0,0 @@
 | 
				
			|||||||
package ru.noties.markwon.il;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import android.content.res.Resources;
 | 
					 | 
				
			||||||
import android.graphics.Bitmap;
 | 
					 | 
				
			||||||
import android.graphics.BitmapFactory;
 | 
					 | 
				
			||||||
import android.graphics.drawable.BitmapDrawable;
 | 
					 | 
				
			||||||
import android.graphics.drawable.Drawable;
 | 
					 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					 | 
				
			||||||
import android.support.annotation.Nullable;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.io.InputStream;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * This class can be used as the last {@link MediaDecoder} to _try_ to handle all rest cases.
 | 
					 | 
				
			||||||
 * Here we just assume that supplied InputStream is of image type and try to decode it.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @since 1.1.0
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public class ImageMediaDecoder extends MediaDecoder {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @NonNull
 | 
					 | 
				
			||||||
    public static ImageMediaDecoder create(@NonNull Resources resources) {
 | 
					 | 
				
			||||||
        return new ImageMediaDecoder(resources);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private final Resources resources;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @SuppressWarnings("WeakerAccess")
 | 
					 | 
				
			||||||
    ImageMediaDecoder(Resources resources) {
 | 
					 | 
				
			||||||
        this.resources = resources;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public boolean canDecodeByContentType(@Nullable String contentType) {
 | 
					 | 
				
			||||||
        return true;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public boolean canDecodeByFileName(@NonNull String fileName) {
 | 
					 | 
				
			||||||
        return true;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public Drawable decode(@NonNull InputStream inputStream) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        final Drawable out;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // absolutely not optimal... thing
 | 
					 | 
				
			||||||
        final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
 | 
					 | 
				
			||||||
        if (bitmap != null) {
 | 
					 | 
				
			||||||
            out = new BitmapDrawable(resources, bitmap);
 | 
					 | 
				
			||||||
            DrawableUtils.intrinsicBounds(out);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            out = null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return out;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,20 +0,0 @@
 | 
				
			|||||||
package ru.noties.markwon.il;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import android.graphics.drawable.Drawable;
 | 
					 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					 | 
				
			||||||
import android.support.annotation.Nullable;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.io.InputStream;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * @since 1.1.0
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public abstract class MediaDecoder {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public abstract boolean canDecodeByContentType(@Nullable String contentType);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public abstract boolean canDecodeByFileName(@NonNull String fileName);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    public abstract Drawable decode(@NonNull InputStream inputStream);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,89 +0,0 @@
 | 
				
			|||||||
package ru.noties.markwon.il;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import android.net.Uri;
 | 
					 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					 | 
				
			||||||
import android.support.annotation.Nullable;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.io.IOException;
 | 
					 | 
				
			||||||
import java.io.InputStream;
 | 
					 | 
				
			||||||
import java.util.Arrays;
 | 
					 | 
				
			||||||
import java.util.Collection;
 | 
					 | 
				
			||||||
import java.util.List;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import okhttp3.Call;
 | 
					 | 
				
			||||||
import okhttp3.OkHttpClient;
 | 
					 | 
				
			||||||
import okhttp3.Request;
 | 
					 | 
				
			||||||
import okhttp3.Response;
 | 
					 | 
				
			||||||
import okhttp3.ResponseBody;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * @since 2.0.0
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public class NetworkSchemeHandler extends SchemeHandler {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @NonNull
 | 
					 | 
				
			||||||
    public static NetworkSchemeHandler create(@NonNull OkHttpClient client) {
 | 
					 | 
				
			||||||
        return new NetworkSchemeHandler(client);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private static final String HEADER_CONTENT_TYPE = "Content-Type";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private final OkHttpClient client;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @SuppressWarnings("WeakerAccess")
 | 
					 | 
				
			||||||
    NetworkSchemeHandler(@NonNull OkHttpClient client) {
 | 
					 | 
				
			||||||
        this.client = client;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public ImageItem handle(@NonNull String raw, @NonNull Uri uri) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        ImageItem out = null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        final Request request = new Request.Builder()
 | 
					 | 
				
			||||||
                .url(raw)
 | 
					 | 
				
			||||||
                .tag(raw)
 | 
					 | 
				
			||||||
                .build();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Response response = null;
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            response = client.newCall(request).execute();
 | 
					 | 
				
			||||||
        } catch (IOException e) {
 | 
					 | 
				
			||||||
            e.printStackTrace();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (response != null) {
 | 
					 | 
				
			||||||
            final ResponseBody body = response.body();
 | 
					 | 
				
			||||||
            if (body != null) {
 | 
					 | 
				
			||||||
                final InputStream inputStream = body.byteStream();
 | 
					 | 
				
			||||||
                if (inputStream != null) {
 | 
					 | 
				
			||||||
                    final String contentType = response.header(HEADER_CONTENT_TYPE);
 | 
					 | 
				
			||||||
                    out = new ImageItem(contentType, inputStream, null);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return out;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public void cancel(@NonNull String raw) {
 | 
					 | 
				
			||||||
        final List<Call> calls = client.dispatcher().queuedCalls();
 | 
					 | 
				
			||||||
        if (calls != null) {
 | 
					 | 
				
			||||||
            for (Call call : calls) {
 | 
					 | 
				
			||||||
                if (!call.isCanceled()) {
 | 
					 | 
				
			||||||
                    if (raw.equals(call.request().tag())) {
 | 
					 | 
				
			||||||
                        call.cancel();
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @NonNull
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public Collection<String> schemes() {
 | 
					 | 
				
			||||||
        return Arrays.asList("http", "https");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,25 +0,0 @@
 | 
				
			|||||||
package ru.noties.markwon.il;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import android.net.Uri;
 | 
					 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					 | 
				
			||||||
import android.support.annotation.Nullable;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.util.Collection;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * @since 2.0.0
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public abstract class SchemeHandler {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    public abstract ImageItem handle(@NonNull String raw, @NonNull Uri uri);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public abstract void cancel(@NonNull String raw);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Will be called only once during initialization, should return schemes that are
 | 
					 | 
				
			||||||
     * handled by this handler
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    @NonNull
 | 
					 | 
				
			||||||
    public abstract Collection<String> schemes();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,81 +0,0 @@
 | 
				
			|||||||
package ru.noties.markwon.il;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import android.content.res.Resources;
 | 
					 | 
				
			||||||
import android.graphics.Bitmap;
 | 
					 | 
				
			||||||
import android.graphics.Canvas;
 | 
					 | 
				
			||||||
import android.graphics.drawable.BitmapDrawable;
 | 
					 | 
				
			||||||
import android.graphics.drawable.Drawable;
 | 
					 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					 | 
				
			||||||
import android.support.annotation.Nullable;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.caverock.androidsvg.SVG;
 | 
					 | 
				
			||||||
import com.caverock.androidsvg.SVGParseException;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.io.InputStream;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * @since 1.1.0
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public class SvgMediaDecoder extends MediaDecoder {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private static final String CONTENT_TYPE_SVG = "image/svg+xml";
 | 
					 | 
				
			||||||
    private static final String FILE_EXTENSION_SVG = ".svg";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @NonNull
 | 
					 | 
				
			||||||
    public static SvgMediaDecoder create(@NonNull Resources resources) {
 | 
					 | 
				
			||||||
        return new SvgMediaDecoder(resources);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private final Resources resources;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @SuppressWarnings("WeakerAccess")
 | 
					 | 
				
			||||||
    SvgMediaDecoder(Resources resources) {
 | 
					 | 
				
			||||||
        this.resources = resources;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public boolean canDecodeByContentType(@Nullable String contentType) {
 | 
					 | 
				
			||||||
        return contentType != null && contentType.startsWith(CONTENT_TYPE_SVG);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public boolean canDecodeByFileName(@NonNull String fileName) {
 | 
					 | 
				
			||||||
        return fileName.endsWith(FILE_EXTENSION_SVG);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public Drawable decode(@NonNull InputStream inputStream) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        final Drawable out;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        SVG svg = null;
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            svg = SVG.getFromInputStream(inputStream);
 | 
					 | 
				
			||||||
        } catch (SVGParseException e) {
 | 
					 | 
				
			||||||
            e.printStackTrace();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (svg == null) {
 | 
					 | 
				
			||||||
            out = null;
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            final float w = svg.getDocumentWidth();
 | 
					 | 
				
			||||||
            final float h = svg.getDocumentHeight();
 | 
					 | 
				
			||||||
            final float density = resources.getDisplayMetrics().density;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            final int width = (int) (w * density + .5F);
 | 
					 | 
				
			||||||
            final int height = (int) (h * density + .5F);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);
 | 
					 | 
				
			||||||
            final Canvas canvas = new Canvas(bitmap);
 | 
					 | 
				
			||||||
            canvas.scale(density, density);
 | 
					 | 
				
			||||||
            svg.renderToCanvas(canvas);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            out = new BitmapDrawable(resources, bitmap);
 | 
					 | 
				
			||||||
            DrawableUtils.intrinsicBounds(out);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return out;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,119 +0,0 @@
 | 
				
			|||||||
package ru.noties.markwon.il;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import org.junit.Before;
 | 
					 | 
				
			||||||
import org.junit.Test;
 | 
					 | 
				
			||||||
import org.junit.runner.RunWith;
 | 
					 | 
				
			||||||
import org.robolectric.RobolectricTestRunner;
 | 
					 | 
				
			||||||
import org.robolectric.annotation.Config;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.util.LinkedHashMap;
 | 
					 | 
				
			||||||
import java.util.Map;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import static org.junit.Assert.assertEquals;
 | 
					 | 
				
			||||||
import static org.junit.Assert.assertNull;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@RunWith(RobolectricTestRunner.class)
 | 
					 | 
				
			||||||
@Config(manifest = Config.NONE)
 | 
					 | 
				
			||||||
public class DataUriParserTest {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private DataUriParser.Impl impl;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Before
 | 
					 | 
				
			||||||
    public void before() {
 | 
					 | 
				
			||||||
        impl = new DataUriParser.Impl();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Test
 | 
					 | 
				
			||||||
    public void test() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        final Map<String, DataUri> data = new LinkedHashMap<String, DataUri>() {{
 | 
					 | 
				
			||||||
            put(",", new DataUri(null, false, null));
 | 
					 | 
				
			||||||
            put("image/svg+xml;base64,!@#$%^&*(", new DataUri("image/svg+xml", true, "!@#$%^&*("));
 | 
					 | 
				
			||||||
            put("text/vnd-example+xyz;foo=bar;base64,R0lGODdh", new DataUri("text/vnd-example+xyz", true, "R0lGODdh"));
 | 
					 | 
				
			||||||
            put("text/plain;charset=UTF-8;page=21,the%20data:1234,5678", new DataUri("text/plain", false, "the%20data:1234,5678"));
 | 
					 | 
				
			||||||
        }};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (Map.Entry<String, DataUri> entry : data.entrySet()) {
 | 
					 | 
				
			||||||
            assertEquals(entry.getKey(), entry.getValue(), impl.parse(entry.getKey()));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Test
 | 
					 | 
				
			||||||
    public void data_new_lines_are_ignored() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        final String input = "image/png;base64,iVBORw0KGgoAAA\n" +
 | 
					 | 
				
			||||||
                "ANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4\n" +
 | 
					 | 
				
			||||||
                "//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU\n" +
 | 
					 | 
				
			||||||
                "5ErkJggg==";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        assertEquals(
 | 
					 | 
				
			||||||
                new DataUri("image/png", true, "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="),
 | 
					 | 
				
			||||||
                impl.parse(input)
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Test
 | 
					 | 
				
			||||||
    public void no_comma_returns_null() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        final String[] inputs = {
 | 
					 | 
				
			||||||
                "",
 | 
					 | 
				
			||||||
                "what-ever",
 | 
					 | 
				
			||||||
                ";;;;;;;",
 | 
					 | 
				
			||||||
                "some crazy data"
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (String input : inputs) {
 | 
					 | 
				
			||||||
            assertNull(input, impl.parse(input));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Test
 | 
					 | 
				
			||||||
    public void two_commas() {
 | 
					 | 
				
			||||||
        final String input = ",,"; // <- second one would be considered data...
 | 
					 | 
				
			||||||
        assertEquals(
 | 
					 | 
				
			||||||
                input,
 | 
					 | 
				
			||||||
                new DataUri(null, false, ","),
 | 
					 | 
				
			||||||
                impl.parse(input)
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Test
 | 
					 | 
				
			||||||
    public void more_commas() {
 | 
					 | 
				
			||||||
        final String input = "first,second,third"; // <- first is just a value (will be ignored)
 | 
					 | 
				
			||||||
        assertEquals(
 | 
					 | 
				
			||||||
                input,
 | 
					 | 
				
			||||||
                new DataUri(null, false, "second,third"),
 | 
					 | 
				
			||||||
                impl.parse(input)
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Test
 | 
					 | 
				
			||||||
    public void base64_no_content_type() {
 | 
					 | 
				
			||||||
        final String input = ";base64,12345";
 | 
					 | 
				
			||||||
        assertEquals(
 | 
					 | 
				
			||||||
                input,
 | 
					 | 
				
			||||||
                new DataUri(null, true, "12345"),
 | 
					 | 
				
			||||||
                impl.parse(input)
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Test
 | 
					 | 
				
			||||||
    public void not_base64_no_content_type() {
 | 
					 | 
				
			||||||
        final String input = ",qweRTY";
 | 
					 | 
				
			||||||
        assertEquals(
 | 
					 | 
				
			||||||
                input,
 | 
					 | 
				
			||||||
                new DataUri(null, false, "qweRTY"),
 | 
					 | 
				
			||||||
                impl.parse(input)
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Test
 | 
					 | 
				
			||||||
    public void content_type_data_no_base64() {
 | 
					 | 
				
			||||||
        final String input = "image/png,aSdFg";
 | 
					 | 
				
			||||||
        assertEquals(
 | 
					 | 
				
			||||||
                input,
 | 
					 | 
				
			||||||
                new DataUri("image/png", false, "aSdFg"),
 | 
					 | 
				
			||||||
                impl.parse(input)
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,112 +0,0 @@
 | 
				
			|||||||
package ru.noties.markwon.il;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import android.net.Uri;
 | 
					 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import org.junit.Before;
 | 
					 | 
				
			||||||
import org.junit.Test;
 | 
					 | 
				
			||||||
import org.junit.runner.RunWith;
 | 
					 | 
				
			||||||
import org.robolectric.RobolectricTestRunner;
 | 
					 | 
				
			||||||
import org.robolectric.annotation.Config;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.io.InputStream;
 | 
					 | 
				
			||||||
import java.util.HashMap;
 | 
					 | 
				
			||||||
import java.util.Map;
 | 
					 | 
				
			||||||
import java.util.Scanner;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import static org.junit.Assert.assertEquals;
 | 
					 | 
				
			||||||
import static org.junit.Assert.assertNotNull;
 | 
					 | 
				
			||||||
import static org.junit.Assert.assertNull;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@RunWith(RobolectricTestRunner.class)
 | 
					 | 
				
			||||||
@Config(manifest = Config.NONE)
 | 
					 | 
				
			||||||
public class DataUriSchemeHandlerTest {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private DataUriSchemeHandler handler;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Before
 | 
					 | 
				
			||||||
    public void before() {
 | 
					 | 
				
			||||||
        handler = DataUriSchemeHandler.create();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Test
 | 
					 | 
				
			||||||
    public void scheme_specific_part_is_empty() {
 | 
					 | 
				
			||||||
        assertNull(handler.handle("data:", Uri.parse("data:")));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Test
 | 
					 | 
				
			||||||
    public void data_uri_is_empty() {
 | 
					 | 
				
			||||||
        assertNull(handler.handle("data://whatever", Uri.parse("data://whatever")));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Test
 | 
					 | 
				
			||||||
    public void no_data() {
 | 
					 | 
				
			||||||
        assertNull(handler.handle("data://,", Uri.parse("data://,")));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Test
 | 
					 | 
				
			||||||
    public void correct() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        final class Item {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            final String contentType;
 | 
					 | 
				
			||||||
            final String data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Item(String contentType, String data) {
 | 
					 | 
				
			||||||
                this.contentType = contentType;
 | 
					 | 
				
			||||||
                this.data = data;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        final Map<String, Item> expected = new HashMap<String, Item>() {{
 | 
					 | 
				
			||||||
            put("data://text/plain;,123", new Item("text/plain", "123"));
 | 
					 | 
				
			||||||
            put("data://image/svg+xml;base64,MTIz", new Item("image/svg+xml", "123"));
 | 
					 | 
				
			||||||
        }};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (Map.Entry<String, Item> entry : expected.entrySet()) {
 | 
					 | 
				
			||||||
            final ImageItem item = handler.handle(entry.getKey(), Uri.parse(entry.getKey()));
 | 
					 | 
				
			||||||
            assertNotNull(entry.getKey(), item);
 | 
					 | 
				
			||||||
            assertEquals(entry.getKey(), entry.getValue().contentType, item.contentType());
 | 
					 | 
				
			||||||
            assertEquals(entry.getKey(), entry.getValue().data, readStream(item.inputStream()));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Test
 | 
					 | 
				
			||||||
    public void correct_real() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        final class Item {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            final String contentType;
 | 
					 | 
				
			||||||
            final String data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Item(String contentType, String data) {
 | 
					 | 
				
			||||||
                this.contentType = contentType;
 | 
					 | 
				
			||||||
                this.data = data;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        final Map<String, Item> expected = new HashMap<String, Item>() {{
 | 
					 | 
				
			||||||
            put("data:text/plain;,123", new Item("text/plain", "123"));
 | 
					 | 
				
			||||||
            put("data:image/svg+xml;base64,MTIz", new Item("image/svg+xml", "123"));
 | 
					 | 
				
			||||||
        }};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (Map.Entry<String, Item> entry : expected.entrySet()) {
 | 
					 | 
				
			||||||
            final ImageItem item = handler.handle(entry.getKey(), Uri.parse(entry.getKey()));
 | 
					 | 
				
			||||||
            assertNotNull(entry.getKey(), item);
 | 
					 | 
				
			||||||
            assertEquals(entry.getKey(), entry.getValue().contentType, item.contentType());
 | 
					 | 
				
			||||||
            assertEquals(entry.getKey(), entry.getValue().data, readStream(item.inputStream()));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @NonNull
 | 
					 | 
				
			||||||
    private static String readStream(@NonNull InputStream stream) {
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            final Scanner scanner = new Scanner(stream, "UTF-8").useDelimiter("\\A");
 | 
					 | 
				
			||||||
            return scanner.hasNext()
 | 
					 | 
				
			||||||
                    ? scanner.next()
 | 
					 | 
				
			||||||
                    : "";
 | 
					 | 
				
			||||||
        } catch (Throwable t) {
 | 
					 | 
				
			||||||
            throw new RuntimeException(t);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -16,6 +16,10 @@ public class Prism4jThemeDarkula extends Prism4jThemeBase {
 | 
				
			|||||||
        return new Prism4jThemeDarkula(0xFF2d2d2d);
 | 
					        return new Prism4jThemeDarkula(0xFF2d2d2d);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @param background color
 | 
				
			||||||
 | 
					     * @since 3.0.0
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    @NonNull
 | 
					    @NonNull
 | 
				
			||||||
    public static Prism4jThemeDarkula create(@ColorInt int background) {
 | 
					    public static Prism4jThemeDarkula create(@ColorInt int background) {
 | 
				
			||||||
        return new Prism4jThemeDarkula(background);
 | 
					        return new Prism4jThemeDarkula(background);
 | 
				
			||||||
 | 
				
			|||||||
@ -15,9 +15,6 @@ android {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
dependencies {
 | 
					dependencies {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    api project(':markwon-html-parser-api')
 | 
					 | 
				
			||||||
    api project(':markwon-html-parser-impl')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    deps.with {
 | 
					    deps.with {
 | 
				
			||||||
        api it['support-annotations']
 | 
					        api it['support-annotations']
 | 
				
			||||||
        api it['commonmark']
 | 
					        api it['commonmark']
 | 
				
			||||||
 | 
				
			|||||||
@ -3,12 +3,12 @@ package ru.noties.markwon;
 | 
				
			|||||||
import android.content.Context;
 | 
					import android.content.Context;
 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ru.noties.markwon.html.api.MarkwonHtmlParser;
 | 
					import ru.noties.markwon.html.MarkwonHtmlParser;
 | 
				
			||||||
 | 
					import ru.noties.markwon.html.MarkwonHtmlRenderer;
 | 
				
			||||||
import ru.noties.markwon.image.AsyncDrawableLoader;
 | 
					import ru.noties.markwon.image.AsyncDrawableLoader;
 | 
				
			||||||
import ru.noties.markwon.image.AsyncDrawableLoaderNoOp;
 | 
					import ru.noties.markwon.image.AsyncDrawableLoaderNoOp;
 | 
				
			||||||
import ru.noties.markwon.renderer.ImageSizeResolver;
 | 
					import ru.noties.markwon.renderer.ImageSizeResolver;
 | 
				
			||||||
import ru.noties.markwon.renderer.ImageSizeResolverDef;
 | 
					import ru.noties.markwon.renderer.ImageSizeResolverDef;
 | 
				
			||||||
import ru.noties.markwon.renderer.html2.MarkwonHtmlRenderer;
 | 
					 | 
				
			||||||
import ru.noties.markwon.spans.LinkSpan;
 | 
					import ru.noties.markwon.spans.LinkSpan;
 | 
				
			||||||
import ru.noties.markwon.spans.MarkwonTheme;
 | 
					import ru.noties.markwon.spans.MarkwonTheme;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -37,7 +37,6 @@ public class MarkwonConfiguration {
 | 
				
			|||||||
    private final UrlProcessor urlProcessor;
 | 
					    private final UrlProcessor urlProcessor;
 | 
				
			||||||
    private final ImageSizeResolver imageSizeResolver;
 | 
					    private final ImageSizeResolver imageSizeResolver;
 | 
				
			||||||
    private final SpannableFactory factory; // @since 1.1.0
 | 
					    private final SpannableFactory factory; // @since 1.1.0
 | 
				
			||||||
    private final boolean softBreakAddsNewLine; // @since 1.1.1
 | 
					 | 
				
			||||||
    private final MarkwonHtmlParser htmlParser; // @since 2.0.0
 | 
					    private final MarkwonHtmlParser htmlParser; // @since 2.0.0
 | 
				
			||||||
    private final MarkwonHtmlRenderer htmlRenderer; // @since 2.0.0
 | 
					    private final MarkwonHtmlRenderer htmlRenderer; // @since 2.0.0
 | 
				
			||||||
    private final boolean htmlAllowNonClosedTags; // @since 2.0.0
 | 
					    private final boolean htmlAllowNonClosedTags; // @since 2.0.0
 | 
				
			||||||
@ -50,7 +49,6 @@ public class MarkwonConfiguration {
 | 
				
			|||||||
        this.urlProcessor = builder.urlProcessor;
 | 
					        this.urlProcessor = builder.urlProcessor;
 | 
				
			||||||
        this.imageSizeResolver = builder.imageSizeResolver;
 | 
					        this.imageSizeResolver = builder.imageSizeResolver;
 | 
				
			||||||
        this.factory = builder.factory;
 | 
					        this.factory = builder.factory;
 | 
				
			||||||
        this.softBreakAddsNewLine = builder.softBreakAddsNewLine;
 | 
					 | 
				
			||||||
        this.htmlParser = builder.htmlParser;
 | 
					        this.htmlParser = builder.htmlParser;
 | 
				
			||||||
        this.htmlRenderer = builder.htmlRenderer;
 | 
					        this.htmlRenderer = builder.htmlRenderer;
 | 
				
			||||||
        this.htmlAllowNonClosedTags = builder.htmlAllowNonClosedTags;
 | 
					        this.htmlAllowNonClosedTags = builder.htmlAllowNonClosedTags;
 | 
				
			||||||
@ -99,15 +97,6 @@ public class MarkwonConfiguration {
 | 
				
			|||||||
        return factory;
 | 
					        return factory;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * @return a flag indicating if soft break should be treated as a hard
 | 
					 | 
				
			||||||
     * break and thus adding a new line instead of adding a white space
 | 
					 | 
				
			||||||
     * @since 1.1.1
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public boolean softBreakAddsNewLine() {
 | 
					 | 
				
			||||||
        return softBreakAddsNewLine;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * @since 2.0.0
 | 
					     * @since 2.0.0
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
@ -143,7 +132,6 @@ public class MarkwonConfiguration {
 | 
				
			|||||||
        private UrlProcessor urlProcessor;
 | 
					        private UrlProcessor urlProcessor;
 | 
				
			||||||
        private ImageSizeResolver imageSizeResolver;
 | 
					        private ImageSizeResolver imageSizeResolver;
 | 
				
			||||||
        private SpannableFactory factory; // @since 1.1.0
 | 
					        private SpannableFactory factory; // @since 1.1.0
 | 
				
			||||||
        private boolean softBreakAddsNewLine; // @since 1.1.1
 | 
					 | 
				
			||||||
        private MarkwonHtmlParser htmlParser; // @since 2.0.0
 | 
					        private MarkwonHtmlParser htmlParser; // @since 2.0.0
 | 
				
			||||||
        private MarkwonHtmlRenderer htmlRenderer; // @since 2.0.0
 | 
					        private MarkwonHtmlRenderer htmlRenderer; // @since 2.0.0
 | 
				
			||||||
        private boolean htmlAllowNonClosedTags; // @since 2.0.0
 | 
					        private boolean htmlAllowNonClosedTags; // @since 2.0.0
 | 
				
			||||||
@ -161,7 +149,6 @@ public class MarkwonConfiguration {
 | 
				
			|||||||
            this.urlProcessor = configuration.urlProcessor;
 | 
					            this.urlProcessor = configuration.urlProcessor;
 | 
				
			||||||
            this.imageSizeResolver = configuration.imageSizeResolver;
 | 
					            this.imageSizeResolver = configuration.imageSizeResolver;
 | 
				
			||||||
            this.factory = configuration.factory;
 | 
					            this.factory = configuration.factory;
 | 
				
			||||||
            this.softBreakAddsNewLine = configuration.softBreakAddsNewLine;
 | 
					 | 
				
			||||||
            this.htmlParser = configuration.htmlParser;
 | 
					            this.htmlParser = configuration.htmlParser;
 | 
				
			||||||
            this.htmlRenderer = configuration.htmlRenderer;
 | 
					            this.htmlRenderer = configuration.htmlRenderer;
 | 
				
			||||||
            this.htmlAllowNonClosedTags = configuration.htmlAllowNonClosedTags;
 | 
					            this.htmlAllowNonClosedTags = configuration.htmlAllowNonClosedTags;
 | 
				
			||||||
@ -203,19 +190,6 @@ public class MarkwonConfiguration {
 | 
				
			|||||||
            return this;
 | 
					            return this;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /**
 | 
					 | 
				
			||||||
         * @param softBreakAddsNewLine a flag indicating if soft break should be treated as a hard
 | 
					 | 
				
			||||||
         *                             break and thus adding a new line instead of adding a white space
 | 
					 | 
				
			||||||
         * @return self
 | 
					 | 
				
			||||||
         * @see <a href="https://spec.commonmark.org/0.28/#soft-line-breaks" > spec </a >
 | 
					 | 
				
			||||||
         * @since 1.1.1
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        @NonNull
 | 
					 | 
				
			||||||
        public Builder softBreakAddsNewLine(boolean softBreakAddsNewLine) {
 | 
					 | 
				
			||||||
            this.softBreakAddsNewLine = softBreakAddsNewLine;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /**
 | 
					        /**
 | 
				
			||||||
         * @since 2.0.0
 | 
					         * @since 2.0.0
 | 
				
			||||||
         */
 | 
					         */
 | 
				
			||||||
@ -276,17 +250,12 @@ public class MarkwonConfiguration {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            // @since 2.0.0
 | 
					            // @since 2.0.0
 | 
				
			||||||
            if (htmlParser == null) {
 | 
					            if (htmlParser == null) {
 | 
				
			||||||
                try {
 | 
					 | 
				
			||||||
                    // if impl artifact was excluded -> fallback to no-op implementation
 | 
					 | 
				
			||||||
                    htmlParser = ru.noties.markwon.html.impl.MarkwonHtmlParserImpl.create();
 | 
					 | 
				
			||||||
                } catch (Throwable t) {
 | 
					 | 
				
			||||||
                htmlParser = MarkwonHtmlParser.noOp();
 | 
					                htmlParser = MarkwonHtmlParser.noOp();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // @since 2.0.0
 | 
					            // @since 2.0.0
 | 
				
			||||||
            if (htmlRenderer == null) {
 | 
					            if (htmlRenderer == null) {
 | 
				
			||||||
                htmlRenderer = MarkwonHtmlRenderer.create();
 | 
					                htmlRenderer = MarkwonHtmlRenderer.noOp();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return new MarkwonConfiguration(this);
 | 
					            return new MarkwonConfiguration(this);
 | 
				
			||||||
 | 
				
			|||||||
@ -63,16 +63,4 @@ public interface SpannableFactory {
 | 
				
			|||||||
            @NonNull MarkwonTheme theme,
 | 
					            @NonNull MarkwonTheme theme,
 | 
				
			||||||
            @NonNull String destination,
 | 
					            @NonNull String destination,
 | 
				
			||||||
            @NonNull LinkSpan.Resolver resolver);
 | 
					            @NonNull LinkSpan.Resolver resolver);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Currently used by HTML parser
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    Object superScript(@NonNull MarkwonTheme theme);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Currently used by HTML parser
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    Object subScript(@NonNull MarkwonTheme theme);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Currently used by HTML parser
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    Object underline();
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -118,21 +118,4 @@ public class SpannableFactoryDef implements SpannableFactory {
 | 
				
			|||||||
    public Object link(@NonNull MarkwonTheme theme, @NonNull String destination, @NonNull LinkSpan.Resolver resolver) {
 | 
					    public Object link(@NonNull MarkwonTheme theme, @NonNull String destination, @NonNull LinkSpan.Resolver resolver) {
 | 
				
			||||||
        return new LinkSpan(theme, destination, resolver);
 | 
					        return new LinkSpan(theme, destination, resolver);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public Object superScript(@NonNull MarkwonTheme theme) {
 | 
					 | 
				
			||||||
        return new SuperScriptSpan(theme);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public Object subScript(@NonNull MarkwonTheme theme) {
 | 
					 | 
				
			||||||
        return new SubScriptSpan(theme);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public Object underline() {
 | 
					 | 
				
			||||||
        return new UnderlineSpan();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
package ru.noties.markwon.html.api;
 | 
					package ru.noties.markwon.html;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
import android.support.annotation.Nullable;
 | 
					import android.support.annotation.Nullable;
 | 
				
			||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
package ru.noties.markwon.html.api;
 | 
					package ru.noties.markwon.html;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -34,7 +34,7 @@ public abstract class MarkwonHtmlParser {
 | 
				
			|||||||
     *                       If you wish to keep them open (do not force close at the end of a
 | 
					     *                       If you wish to keep them open (do not force close at the end of a
 | 
				
			||||||
     *                       document pass here {@link HtmlTag#NO_END}. Later non-closed tags
 | 
					     *                       document pass here {@link HtmlTag#NO_END}. Later non-closed tags
 | 
				
			||||||
     *                       can be detected by calling {@link HtmlTag#isClosed()}
 | 
					     *                       can be detected by calling {@link HtmlTag#isClosed()}
 | 
				
			||||||
     * @param action         {@link FlushAction} to be called with resulting tags ({@link ru.noties.markwon.html.api.HtmlTag.Inline})
 | 
					     * @param action         {@link FlushAction} to be called with resulting tags ({@link HtmlTag.Inline})
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public abstract void flushInlineTags(
 | 
					    public abstract void flushInlineTags(
 | 
				
			||||||
            int documentLength,
 | 
					            int documentLength,
 | 
				
			||||||
@ -49,7 +49,7 @@ public abstract class MarkwonHtmlParser {
 | 
				
			|||||||
     *                       If you wish to keep them open (do not force close at the end of a
 | 
					     *                       If you wish to keep them open (do not force close at the end of a
 | 
				
			||||||
     *                       document pass here {@link HtmlTag#NO_END}. Later non-closed tags
 | 
					     *                       document pass here {@link HtmlTag#NO_END}. Later non-closed tags
 | 
				
			||||||
     *                       can be detected by calling {@link HtmlTag#isClosed()}
 | 
					     *                       can be detected by calling {@link HtmlTag#isClosed()}
 | 
				
			||||||
     * @param action         {@link FlushAction} to be called with resulting tags ({@link ru.noties.markwon.html.api.HtmlTag.Block})
 | 
					     * @param action         {@link FlushAction} to be called with resulting tags ({@link HtmlTag.Block})
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public abstract void flushBlockTags(
 | 
					    public abstract void flushBlockTags(
 | 
				
			||||||
            int documentLength,
 | 
					            int documentLength,
 | 
				
			||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
package ru.noties.markwon.html.api;
 | 
					package ru.noties.markwon.html;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					package ru.noties.markwon.html;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
 | 
					import android.support.annotation.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import ru.noties.markwon.MarkwonConfiguration;
 | 
				
			||||||
 | 
					import ru.noties.markwon.SpannableBuilder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @since 2.0.0
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public abstract class MarkwonHtmlRenderer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @since 3.0.0
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @NonNull
 | 
				
			||||||
 | 
					    public static MarkwonHtmlRenderer noOp() {
 | 
				
			||||||
 | 
					        return new MarkwonHtmlRendererNoOp();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public abstract void render(
 | 
				
			||||||
 | 
					            @NonNull MarkwonConfiguration configuration,
 | 
				
			||||||
 | 
					            @NonNull SpannableBuilder builder,
 | 
				
			||||||
 | 
					            @NonNull MarkwonHtmlParser parser
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Nullable
 | 
				
			||||||
 | 
					    public abstract TagHandler tagHandler(@NonNull String tagName);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					package ru.noties.markwon.html;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
 | 
					import android.support.annotation.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import ru.noties.markwon.MarkwonConfiguration;
 | 
				
			||||||
 | 
					import ru.noties.markwon.SpannableBuilder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MarkwonHtmlRendererNoOp extends MarkwonHtmlRenderer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void render(@NonNull MarkwonConfiguration configuration, @NonNull SpannableBuilder builder, @NonNull MarkwonHtmlParser parser) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Nullable
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public TagHandler tagHandler(@NonNull String tagName) {
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,10 +1,9 @@
 | 
				
			|||||||
package ru.noties.markwon.renderer.html2.tag;
 | 
					package ru.noties.markwon.html;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ru.noties.markwon.MarkwonConfiguration;
 | 
					import ru.noties.markwon.MarkwonConfiguration;
 | 
				
			||||||
import ru.noties.markwon.SpannableBuilder;
 | 
					import ru.noties.markwon.SpannableBuilder;
 | 
				
			||||||
import ru.noties.markwon.html.api.HtmlTag;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
public abstract class TagHandler {
 | 
					public abstract class TagHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1,101 +0,0 @@
 | 
				
			|||||||
package ru.noties.markwon.renderer.html2;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					 | 
				
			||||||
import android.support.annotation.Nullable;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.util.Collections;
 | 
					 | 
				
			||||||
import java.util.HashMap;
 | 
					 | 
				
			||||||
import java.util.Locale;
 | 
					 | 
				
			||||||
import java.util.Map;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import ru.noties.markwon.SpannableBuilder;
 | 
					 | 
				
			||||||
import ru.noties.markwon.MarkwonConfiguration;
 | 
					 | 
				
			||||||
import ru.noties.markwon.html.api.MarkwonHtmlParser;
 | 
					 | 
				
			||||||
import ru.noties.markwon.renderer.html2.tag.BlockquoteHandler;
 | 
					 | 
				
			||||||
import ru.noties.markwon.renderer.html2.tag.EmphasisHandler;
 | 
					 | 
				
			||||||
import ru.noties.markwon.renderer.html2.tag.HeadingHandler;
 | 
					 | 
				
			||||||
import ru.noties.markwon.renderer.html2.tag.ImageHandler;
 | 
					 | 
				
			||||||
import ru.noties.markwon.renderer.html2.tag.LinkHandler;
 | 
					 | 
				
			||||||
import ru.noties.markwon.renderer.html2.tag.ListHandler;
 | 
					 | 
				
			||||||
import ru.noties.markwon.renderer.html2.tag.StrikeHandler;
 | 
					 | 
				
			||||||
import ru.noties.markwon.renderer.html2.tag.StrongEmphasisHandler;
 | 
					 | 
				
			||||||
import ru.noties.markwon.renderer.html2.tag.SubScriptHandler;
 | 
					 | 
				
			||||||
import ru.noties.markwon.renderer.html2.tag.SuperScriptHandler;
 | 
					 | 
				
			||||||
import ru.noties.markwon.renderer.html2.tag.TagHandler;
 | 
					 | 
				
			||||||
import ru.noties.markwon.renderer.html2.tag.UnderlineHandler;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * @since 2.0.0
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public abstract class MarkwonHtmlRenderer {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public abstract void render(
 | 
					 | 
				
			||||||
            @NonNull MarkwonConfiguration configuration,
 | 
					 | 
				
			||||||
            @NonNull SpannableBuilder builder,
 | 
					 | 
				
			||||||
            @NonNull MarkwonHtmlParser parser
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    public abstract TagHandler tagHandler(@NonNull String tagName);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @NonNull
 | 
					 | 
				
			||||||
    public static MarkwonHtmlRenderer create() {
 | 
					 | 
				
			||||||
        return builderWithDefaults().build();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @NonNull
 | 
					 | 
				
			||||||
    public static Builder builderWithDefaults() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        final EmphasisHandler emphasisHandler = new EmphasisHandler();
 | 
					 | 
				
			||||||
        final StrongEmphasisHandler strongEmphasisHandler = new StrongEmphasisHandler();
 | 
					 | 
				
			||||||
        final StrikeHandler strikeHandler = new StrikeHandler();
 | 
					 | 
				
			||||||
        final UnderlineHandler underlineHandler = new UnderlineHandler();
 | 
					 | 
				
			||||||
        final ListHandler listHandler = new ListHandler();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return builder()
 | 
					 | 
				
			||||||
                .handler("i", emphasisHandler)
 | 
					 | 
				
			||||||
                .handler("em", emphasisHandler)
 | 
					 | 
				
			||||||
                .handler("cite", emphasisHandler)
 | 
					 | 
				
			||||||
                .handler("dfn", emphasisHandler)
 | 
					 | 
				
			||||||
                .handler("b", strongEmphasisHandler)
 | 
					 | 
				
			||||||
                .handler("strong", strongEmphasisHandler)
 | 
					 | 
				
			||||||
                .handler("sup", new SuperScriptHandler())
 | 
					 | 
				
			||||||
                .handler("sub", new SubScriptHandler())
 | 
					 | 
				
			||||||
                .handler("u", underlineHandler)
 | 
					 | 
				
			||||||
                .handler("ins", underlineHandler)
 | 
					 | 
				
			||||||
                .handler("del", strikeHandler)
 | 
					 | 
				
			||||||
                .handler("s", strikeHandler)
 | 
					 | 
				
			||||||
                .handler("strike", strikeHandler)
 | 
					 | 
				
			||||||
                .handler("a", new LinkHandler())
 | 
					 | 
				
			||||||
                .handler("ul", listHandler)
 | 
					 | 
				
			||||||
                .handler("ol", listHandler)
 | 
					 | 
				
			||||||
                .handler("img", ImageHandler.create())
 | 
					 | 
				
			||||||
                .handler("blockquote", new BlockquoteHandler())
 | 
					 | 
				
			||||||
                .handler("h1", new HeadingHandler(1))
 | 
					 | 
				
			||||||
                .handler("h2", new HeadingHandler(2))
 | 
					 | 
				
			||||||
                .handler("h3", new HeadingHandler(3))
 | 
					 | 
				
			||||||
                .handler("h4", new HeadingHandler(4))
 | 
					 | 
				
			||||||
                .handler("h5", new HeadingHandler(5))
 | 
					 | 
				
			||||||
                .handler("h6", new HeadingHandler(6));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @NonNull
 | 
					 | 
				
			||||||
    public static Builder builder() {
 | 
					 | 
				
			||||||
        return new Builder();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static class Builder {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private final Map<String, TagHandler> tagHandlers = new HashMap<>(2);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public Builder handler(@NonNull String tagName, @NonNull TagHandler tagHandler) {
 | 
					 | 
				
			||||||
            tagHandlers.put(tagName.toLowerCase(Locale.US), tagHandler);
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        @NonNull
 | 
					 | 
				
			||||||
        public MarkwonHtmlRenderer build() {
 | 
					 | 
				
			||||||
            return new MarkwonHtmlRendererImpl(Collections.unmodifiableMap(tagHandlers));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,88 +0,0 @@
 | 
				
			|||||||
package ru.noties.markwon.renderer.html2;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import android.support.annotation.NonNull;
 | 
					 | 
				
			||||||
import android.support.annotation.Nullable;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.util.List;
 | 
					 | 
				
			||||||
import java.util.Map;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import ru.noties.markwon.SpannableBuilder;
 | 
					 | 
				
			||||||
import ru.noties.markwon.MarkwonConfiguration;
 | 
					 | 
				
			||||||
import ru.noties.markwon.html.api.HtmlTag;
 | 
					 | 
				
			||||||
import ru.noties.markwon.html.api.MarkwonHtmlParser;
 | 
					 | 
				
			||||||
import ru.noties.markwon.renderer.html2.tag.TagHandler;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class MarkwonHtmlRendererImpl extends MarkwonHtmlRenderer {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private final Map<String, TagHandler> tagHandlers;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    MarkwonHtmlRendererImpl(@NonNull Map<String, TagHandler> tagHandlers) {
 | 
					 | 
				
			||||||
        this.tagHandlers = tagHandlers;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public void render(
 | 
					 | 
				
			||||||
            @NonNull final MarkwonConfiguration configuration,
 | 
					 | 
				
			||||||
            @NonNull final SpannableBuilder builder,
 | 
					 | 
				
			||||||
            @NonNull MarkwonHtmlParser parser) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        final int end;
 | 
					 | 
				
			||||||
        if (!configuration.htmlAllowNonClosedTags()) {
 | 
					 | 
				
			||||||
            end = HtmlTag.NO_END;
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            end = builder.length();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        parser.flushInlineTags(end, new MarkwonHtmlParser.FlushAction<HtmlTag.Inline>() {
 | 
					 | 
				
			||||||
            @Override
 | 
					 | 
				
			||||||
            public void apply(@NonNull List<HtmlTag.Inline> tags) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                TagHandler handler;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                for (HtmlTag.Inline inline : tags) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    // if tag is not closed -> do not render
 | 
					 | 
				
			||||||
                    if (!inline.isClosed()) {
 | 
					 | 
				
			||||||
                        continue;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    handler = tagHandler(inline.name());
 | 
					 | 
				
			||||||
                    if (handler != null) {
 | 
					 | 
				
			||||||
                        handler.handle(configuration, builder, inline);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        parser.flushBlockTags(end, new MarkwonHtmlParser.FlushAction<HtmlTag.Block>() {
 | 
					 | 
				
			||||||
            @Override
 | 
					 | 
				
			||||||
            public void apply(@NonNull List<HtmlTag.Block> tags) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                TagHandler handler;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                for (HtmlTag.Block block : tags) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (!block.isClosed()) {
 | 
					 | 
				
			||||||
                        continue;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    handler = tagHandler(block.name());
 | 
					 | 
				
			||||||
                    if (handler != null) {
 | 
					 | 
				
			||||||
                        handler.handle(configuration, builder, block);
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        // see if any of children can be handled
 | 
					 | 
				
			||||||
                        apply(block.children());
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        parser.reset();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public TagHandler tagHandler(@NonNull String tagName) {
 | 
					 | 
				
			||||||
        return tagHandlers.get(tagName);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -7,7 +7,7 @@ import ru.noties.markwon.SpannableFactory;
 | 
				
			|||||||
import ru.noties.markwon.SyntaxHighlight;
 | 
					import ru.noties.markwon.SyntaxHighlight;
 | 
				
			||||||
import ru.noties.markwon.UrlProcessor;
 | 
					import ru.noties.markwon.UrlProcessor;
 | 
				
			||||||
import ru.noties.markwon.html.api.MarkwonHtmlParser;
 | 
					import ru.noties.markwon.html.api.MarkwonHtmlParser;
 | 
				
			||||||
import ru.noties.markwon.renderer.html2.MarkwonHtmlRenderer;
 | 
					import ru.noties.markwon.html.MarkwonHtmlRenderer;
 | 
				
			||||||
import ru.noties.markwon.image.AsyncDrawable;
 | 
					import ru.noties.markwon.image.AsyncDrawable;
 | 
				
			||||||
import ru.noties.markwon.spans.LinkSpan;
 | 
					import ru.noties.markwon.spans.LinkSpan;
 | 
				
			||||||
import ru.noties.markwon.spans.MarkwonTheme;
 | 
					import ru.noties.markwon.spans.MarkwonTheme;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,3 +1,3 @@
 | 
				
			|||||||
rootProject.name = 'MarkwonProject'
 | 
					rootProject.name = 'MarkwonProject'
 | 
				
			||||||
include ':app', ':markwon', ':markwon-view', ':sample-custom-extension', ':sample-latex-math', ':markwon-image-svg', ':markwon-image-gif',
 | 
					include ':app', ':markwon', ':markwon-view', ':sample-custom-extension', ':sample-latex-math', ':markwon-image-svg', ':markwon-image-gif',
 | 
				
			||||||
        ':markwon-syntax-highlight', ':markwon-html-parser-api', ':markwon-html-parser-impl'
 | 
					        ':markwon-syntax-highlight', ':markwon-html'
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user