Add image loading plugin based on picasso library
This commit is contained in:
parent
e35d3ad044
commit
19091b5675
@ -71,7 +71,8 @@ ext {
|
||||
'prism4j' : 'ru.noties:prism4j:1.1.0',
|
||||
'debug' : 'ru.noties:debug:3.0.0@jar',
|
||||
'adapt' : 'ru.noties:adapt:1.1.0',
|
||||
'dagger' : "com.google.dagger:dagger:$daggerVersion"
|
||||
'dagger' : "com.google.dagger:dagger:$daggerVersion",
|
||||
'picasso' : "com.squareup.picasso:picasso:2.71828"
|
||||
]
|
||||
|
||||
deps['annotationProcessor'] = [
|
||||
|
@ -14,6 +14,7 @@ import org.commonmark.node.Emphasis;
|
||||
import org.commonmark.node.FencedCodeBlock;
|
||||
import org.commonmark.node.HardLineBreak;
|
||||
import org.commonmark.node.Heading;
|
||||
import org.commonmark.node.Image;
|
||||
import org.commonmark.node.IndentedCodeBlock;
|
||||
import org.commonmark.node.Link;
|
||||
import org.commonmark.node.ListBlock;
|
||||
@ -30,6 +31,8 @@ import ru.noties.markwon.AbstractMarkwonPlugin;
|
||||
import ru.noties.markwon.MarkwonConfiguration;
|
||||
import ru.noties.markwon.MarkwonSpansFactory;
|
||||
import ru.noties.markwon.MarkwonVisitor;
|
||||
import ru.noties.markwon.RenderProps;
|
||||
import ru.noties.markwon.SpanFactory;
|
||||
import ru.noties.markwon.core.factory.BlockQuoteSpanFactory;
|
||||
import ru.noties.markwon.core.factory.CodeBlockSpanFactory;
|
||||
import ru.noties.markwon.core.factory.CodeSpanFactory;
|
||||
@ -40,6 +43,7 @@ import ru.noties.markwon.core.factory.ListItemSpanFactory;
|
||||
import ru.noties.markwon.core.factory.StrongEmphasisSpanFactory;
|
||||
import ru.noties.markwon.core.factory.ThematicBreakSpanFactory;
|
||||
import ru.noties.markwon.core.spans.OrderedListItemSpan;
|
||||
import ru.noties.markwon.image.ImageProps;
|
||||
|
||||
/**
|
||||
* @see CoreProps
|
||||
@ -64,6 +68,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
|
||||
code(builder);
|
||||
fencedCodeBlock(builder);
|
||||
indentedCodeBlock(builder);
|
||||
image(builder);
|
||||
bulletList(builder);
|
||||
orderedList(builder);
|
||||
listItem(builder);
|
||||
@ -197,6 +202,53 @@ public class CorePlugin extends AbstractMarkwonPlugin {
|
||||
});
|
||||
}
|
||||
|
||||
// @since 4.0.0-SNAPSHOT
|
||||
// his method is moved from ImagesPlugin. Alternative implementations must set SpanFactory
|
||||
// for Image node in order for this visitor to function
|
||||
private static void image(MarkwonVisitor.Builder builder) {
|
||||
builder.on(Image.class, new MarkwonVisitor.NodeVisitor<Image>() {
|
||||
@Override
|
||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Image image) {
|
||||
|
||||
// if there is no image spanFactory, ignore
|
||||
final SpanFactory spanFactory = visitor.configuration().spansFactory().get(Image.class);
|
||||
if (spanFactory == null) {
|
||||
visitor.visitChildren(image);
|
||||
return;
|
||||
}
|
||||
|
||||
final int length = visitor.length();
|
||||
|
||||
visitor.visitChildren(image);
|
||||
|
||||
// we must check if anything _was_ added, as we need at least one char to render
|
||||
if (length == visitor.length()) {
|
||||
visitor.builder().append('\uFFFC');
|
||||
}
|
||||
|
||||
final MarkwonConfiguration configuration = visitor.configuration();
|
||||
|
||||
final Node parent = image.getParent();
|
||||
final boolean link = parent instanceof Link;
|
||||
|
||||
final String destination = configuration
|
||||
.urlProcessor()
|
||||
.process(image.getDestination());
|
||||
|
||||
final RenderProps props = visitor.renderProps();
|
||||
|
||||
// apply image properties
|
||||
// Please note that we explicitly set IMAGE_SIZE to null as we do not clear
|
||||
// properties after we applied span (we could though)
|
||||
ImageProps.DESTINATION.set(props, destination);
|
||||
ImageProps.REPLACEMENT_TEXT_IS_LINK.set(props, link);
|
||||
ImageProps.IMAGE_SIZE.set(props, null);
|
||||
|
||||
visitor.setSpans(length, spanFactory.getSpans(configuration, props));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static void visitCodeBlock(
|
||||
@NonNull MarkwonVisitor visitor,
|
||||
|
@ -261,7 +261,8 @@ public class AsyncDrawable extends Drawable {
|
||||
if (hasResult()) {
|
||||
out = result.getIntrinsicWidth();
|
||||
} else {
|
||||
out = 0;
|
||||
// @since 4.0.0-SNAPSHOT, must not be zero in order to receive canvas dimensions
|
||||
out = 1;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@ -272,7 +273,8 @@ public class AsyncDrawable extends Drawable {
|
||||
if (hasResult()) {
|
||||
out = result.getIntrinsicHeight();
|
||||
} else {
|
||||
out = 0;
|
||||
// @since 4.0.0-SNAPSHOT, must not be zero in order to receive canvas dimensions
|
||||
out = 1;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@ -290,4 +292,16 @@ public class AsyncDrawable extends Drawable {
|
||||
? imageSizeResolver.resolveImageSize(imageSize, result.getBounds(), canvasWidth, textSize)
|
||||
: result.getBounds();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AsyncDrawable{" +
|
||||
"destination='" + destination + '\'' +
|
||||
", imageSize=" + imageSize +
|
||||
", result=" + result +
|
||||
", canvasWidth=" + canvasWidth +
|
||||
", textSize=" + textSize +
|
||||
", waitingForDimensions=" + waitingForDimensions +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ public abstract class AsyncDrawableScheduler {
|
||||
// hm... we need the same thing for unschedule then... we can check if last hash is !null,
|
||||
// if it's not -> unschedule, else ignore
|
||||
|
||||
// @since 4.0.0-SNAPSHOT
|
||||
final Integer lastTextHashCode =
|
||||
(Integer) textView.getTag(R.id.markwon_drawables_scheduler_last_text_hashcode);
|
||||
final int textHashCode = textView.getText().hashCode();
|
||||
@ -67,6 +68,7 @@ public abstract class AsyncDrawableScheduler {
|
||||
// must be called when text manually changed in TextView
|
||||
public static void unschedule(@NonNull TextView view) {
|
||||
|
||||
// @since 4.0.0-SNAPSHOT
|
||||
if (view.getTag(R.id.markwon_drawables_scheduler_last_text_hashcode) == null) {
|
||||
return;
|
||||
}
|
||||
|
21
markwon-image-picasso/build.gradle
Normal file
21
markwon-image-picasso/build.gradle
Normal file
@ -0,0 +1,21 @@
|
||||
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 {
|
||||
api project(':markwon-core')
|
||||
api deps['picasso']
|
||||
}
|
||||
|
||||
registerArtifact(this)
|
4
markwon-image-picasso/gradle.properties
Normal file
4
markwon-image-picasso/gradle.properties
Normal file
@ -0,0 +1,4 @@
|
||||
POM_NAME=Image
|
||||
POM_ARTIFACT_ID=image-picasso
|
||||
POM_DESCRIPTION=Markwon image loading module (based on Picasso library)
|
||||
POM_PACKAGING=aar
|
1
markwon-image-picasso/src/main/AndroidManifest.xml
Normal file
1
markwon-image-picasso/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1 @@
|
||||
<manifest package="ru.noties.markwon.image.picasso" />
|
@ -0,0 +1,177 @@
|
||||
package ru.noties.markwon.image.picasso;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.Spanned;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.squareup.picasso.Picasso;
|
||||
import com.squareup.picasso.RequestCreator;
|
||||
import com.squareup.picasso.Target;
|
||||
|
||||
import org.commonmark.node.Image;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import ru.noties.markwon.AbstractMarkwonPlugin;
|
||||
import ru.noties.markwon.MarkwonConfiguration;
|
||||
import ru.noties.markwon.MarkwonSpansFactory;
|
||||
import ru.noties.markwon.image.AsyncDrawable;
|
||||
import ru.noties.markwon.image.AsyncDrawableLoader;
|
||||
import ru.noties.markwon.image.AsyncDrawableScheduler;
|
||||
import ru.noties.markwon.image.DrawableUtils;
|
||||
import ru.noties.markwon.image.ImageSpanFactory;
|
||||
|
||||
/**
|
||||
* @since 4.0.0-SNAPSHOT
|
||||
*/
|
||||
public class PicassoImagesPlugin extends AbstractMarkwonPlugin {
|
||||
|
||||
public interface PicassoStore {
|
||||
|
||||
@NonNull
|
||||
RequestCreator load(@NonNull AsyncDrawable drawable);
|
||||
|
||||
void cancel(@NonNull AsyncDrawable drawable);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static PicassoImagesPlugin create(@NonNull Context context) {
|
||||
return create(new Picasso.Builder(context).build());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static PicassoImagesPlugin create(@NonNull final Picasso picasso) {
|
||||
return create(new PicassoStore() {
|
||||
@NonNull
|
||||
@Override
|
||||
public RequestCreator load(@NonNull AsyncDrawable drawable) {
|
||||
return picasso.load(drawable.getDestination())
|
||||
.tag(drawable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel(@NonNull AsyncDrawable drawable) {
|
||||
picasso.cancelTag(drawable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static PicassoImagesPlugin create(@NonNull PicassoStore picassoStore) {
|
||||
return new PicassoImagesPlugin(picassoStore);
|
||||
}
|
||||
|
||||
private final PicassoAsyncDrawableLoader picassoAsyncDrawableLoader;
|
||||
|
||||
PicassoImagesPlugin(@NonNull PicassoStore picassoStore) {
|
||||
this.picassoAsyncDrawableLoader = new PicassoAsyncDrawableLoader(picassoStore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
|
||||
builder.asyncDrawableLoader(picassoAsyncDrawableLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {
|
||||
builder.setFactory(Image.class, new ImageSpanFactory());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown) {
|
||||
AsyncDrawableScheduler.unschedule(textView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSetText(@NonNull TextView textView) {
|
||||
AsyncDrawableScheduler.schedule(textView);
|
||||
}
|
||||
|
||||
private static class PicassoAsyncDrawableLoader extends AsyncDrawableLoader {
|
||||
|
||||
private final PicassoStore picassoStore;
|
||||
private final Map<AsyncDrawable, AsyncDrawableTarget> cache = new HashMap<>(2);
|
||||
|
||||
PicassoAsyncDrawableLoader(@NonNull PicassoStore picassoStore) {
|
||||
this.picassoStore = picassoStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(@NonNull AsyncDrawable drawable) {
|
||||
|
||||
// we must store hard-reference to target (otherwise it will be garbage-collected
|
||||
// ad picasso internally stores a target in a weak-reference)
|
||||
final AsyncDrawableTarget target = new AsyncDrawableTarget(drawable);
|
||||
cache.put(drawable, target);
|
||||
|
||||
picassoStore.load(drawable)
|
||||
.into(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel(@NonNull AsyncDrawable drawable) {
|
||||
|
||||
cache.remove(drawable);
|
||||
|
||||
picassoStore.cancel(drawable);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Drawable placeholder(@NonNull AsyncDrawable drawable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private class AsyncDrawableTarget implements Target {
|
||||
|
||||
private final AsyncDrawable drawable;
|
||||
|
||||
AsyncDrawableTarget(@NonNull AsyncDrawable drawable) {
|
||||
this.drawable = drawable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
|
||||
if (cache.remove(drawable) != null) {
|
||||
if (drawable.isAttached() && bitmap != null) {
|
||||
final BitmapDrawable bitmapDrawable = new BitmapDrawable(Resources.getSystem(), bitmap);
|
||||
DrawableUtils.applyIntrinsicBounds(bitmapDrawable);
|
||||
drawable.setResult(bitmapDrawable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBitmapFailed(Exception e, Drawable errorDrawable) {
|
||||
if (cache.remove(drawable) != null) {
|
||||
if (errorDrawable != null
|
||||
&& drawable.isAttached()) {
|
||||
DrawableUtils.applyIntrinsicBoundsIfEmpty(errorDrawable);
|
||||
drawable.setResult(errorDrawable);
|
||||
}
|
||||
}
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepareLoad(Drawable placeHolderDrawable) {
|
||||
if (placeHolderDrawable != null
|
||||
&& canDeliver()) {
|
||||
DrawableUtils.applyIntrinsicBoundsIfEmpty(placeHolderDrawable);
|
||||
drawable.setResult(placeHolderDrawable);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canDeliver() {
|
||||
return drawable.isAttached() && cache.containsKey(drawable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -73,13 +73,17 @@ class AsyncDrawableLoaderBuilder {
|
||||
|
||||
isBuilt = true;
|
||||
|
||||
// if we have no schemeHandlers -> we cannot show anything
|
||||
// OR if we have no media decoders
|
||||
if (schemeHandlers.size() == 0
|
||||
|| (mediaDecoders.size() == 0 && defaultMediaDecoder == null)) {
|
||||
// we must have schemeHandlers registered (we will provide
|
||||
// default media decoder if it's absent)
|
||||
if (schemeHandlers.size() == 0) {
|
||||
return new AsyncDrawableLoaderNoOp();
|
||||
}
|
||||
|
||||
// @since 4.0.0-SNAPSHOT
|
||||
if (defaultMediaDecoder == null) {
|
||||
defaultMediaDecoder = DefaultImageMediaDecoder.create();
|
||||
}
|
||||
|
||||
if (executorService == null) {
|
||||
executorService = Executors.newCachedThreadPool();
|
||||
}
|
||||
|
@ -7,17 +7,12 @@ import android.text.Spanned;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.commonmark.node.Image;
|
||||
import org.commonmark.node.Link;
|
||||
import org.commonmark.node.Node;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import ru.noties.markwon.AbstractMarkwonPlugin;
|
||||
import ru.noties.markwon.MarkwonConfiguration;
|
||||
import ru.noties.markwon.MarkwonSpansFactory;
|
||||
import ru.noties.markwon.MarkwonVisitor;
|
||||
import ru.noties.markwon.RenderProps;
|
||||
import ru.noties.markwon.SpanFactory;
|
||||
|
||||
public class ImagesPlugin extends AbstractMarkwonPlugin {
|
||||
|
||||
@ -147,51 +142,6 @@ public class ImagesPlugin extends AbstractMarkwonPlugin {
|
||||
builder.setFactory(Image.class, new ImageSpanFactory());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) {
|
||||
builder.on(Image.class, new MarkwonVisitor.NodeVisitor<Image>() {
|
||||
@Override
|
||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Image image) {
|
||||
|
||||
// if there is no image spanFactory, ignore
|
||||
final SpanFactory spanFactory = visitor.configuration().spansFactory().get(Image.class);
|
||||
if (spanFactory == null) {
|
||||
visitor.visitChildren(image);
|
||||
return;
|
||||
}
|
||||
|
||||
final int length = visitor.length();
|
||||
|
||||
visitor.visitChildren(image);
|
||||
|
||||
// we must check if anything _was_ added, as we need at least one char to render
|
||||
if (length == visitor.length()) {
|
||||
visitor.builder().append('\uFFFC');
|
||||
}
|
||||
|
||||
final MarkwonConfiguration configuration = visitor.configuration();
|
||||
|
||||
final Node parent = image.getParent();
|
||||
final boolean link = parent instanceof Link;
|
||||
|
||||
final String destination = configuration
|
||||
.urlProcessor()
|
||||
.process(image.getDestination());
|
||||
|
||||
final RenderProps props = visitor.renderProps();
|
||||
|
||||
// apply image properties
|
||||
// Please note that we explicitly set IMAGE_SIZE to null as we do not clear
|
||||
// properties after we applied span (we could though)
|
||||
ImageProps.DESTINATION.set(props, destination);
|
||||
ImageProps.REPLACEMENT_TEXT_IS_LINK.set(props, link);
|
||||
ImageProps.IMAGE_SIZE.set(props, null);
|
||||
|
||||
visitor.setSpans(length, spanFactory.getSpans(configuration, props));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown) {
|
||||
AsyncDrawableScheduler.unschedule(textView);
|
||||
|
@ -1,5 +1,6 @@
|
||||
package ru.noties.markwon.image.file;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
@ -35,6 +36,16 @@ public class FileSchemeHandler extends SchemeHandler {
|
||||
return new FileSchemeHandler(assetManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #createWithAssets(AssetManager)
|
||||
* @see ru.noties.markwon.urlprocessor.UrlProcessorAndroidAssets
|
||||
* @since 4.0.0-SNAPSHOT
|
||||
*/
|
||||
@NonNull
|
||||
public static FileSchemeHandler createWithAssets(@NonNull Context context) {
|
||||
return new FileSchemeHandler(context.getAssets());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static FileSchemeHandler create() {
|
||||
return new FileSchemeHandler(null);
|
||||
|
@ -44,6 +44,8 @@ dependencies {
|
||||
implementation project(':markwon-recycler')
|
||||
implementation project(':markwon-recycler-table')
|
||||
|
||||
implementation project(':markwon-image-picasso')
|
||||
|
||||
deps.with {
|
||||
implementation it['support-recycler-view']
|
||||
implementation it['okhttp']
|
||||
|
@ -2,6 +2,8 @@ package ru.noties.markwon.sample.recycler;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
@ -10,6 +12,9 @@ import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.squareup.picasso.Picasso;
|
||||
import com.squareup.picasso.RequestCreator;
|
||||
|
||||
import org.commonmark.ext.gfm.tables.TableBlock;
|
||||
import org.commonmark.node.FencedCodeBlock;
|
||||
|
||||
@ -26,11 +31,8 @@ import ru.noties.markwon.MarkwonConfiguration;
|
||||
import ru.noties.markwon.MarkwonVisitor;
|
||||
import ru.noties.markwon.core.CorePlugin;
|
||||
import ru.noties.markwon.html.HtmlPlugin;
|
||||
import ru.noties.markwon.image.DefaultImageMediaDecoder;
|
||||
import ru.noties.markwon.image.ImagesPlugin;
|
||||
import ru.noties.markwon.image.file.FileSchemeHandler;
|
||||
import ru.noties.markwon.image.network.OkHttpNetworkSchemeHandler;
|
||||
import ru.noties.markwon.image.svg.SvgMediaDecoder;
|
||||
import ru.noties.markwon.image.AsyncDrawable;
|
||||
import ru.noties.markwon.image.picasso.PicassoImagesPlugin;
|
||||
import ru.noties.markwon.recycler.MarkwonAdapter;
|
||||
import ru.noties.markwon.recycler.SimpleEntry;
|
||||
import ru.noties.markwon.recycler.table.TableEntry;
|
||||
@ -77,13 +79,13 @@ public class RecyclerActivity extends Activity {
|
||||
private static Markwon markwon(@NonNull Context context) {
|
||||
return Markwon.builder(context)
|
||||
.usePlugin(CorePlugin.create())
|
||||
.usePlugin(ImagesPlugin.create(plugin -> {
|
||||
plugin
|
||||
.addSchemeHandler(FileSchemeHandler.createWithAssets(context.getAssets()))
|
||||
.addSchemeHandler(OkHttpNetworkSchemeHandler.create())
|
||||
.addMediaDecoder(SvgMediaDecoder.create())
|
||||
.defaultMediaDecoder(DefaultImageMediaDecoder.create());
|
||||
}))
|
||||
// .usePlugin(ImagesPlugin.create(plugin -> {
|
||||
// plugin
|
||||
// .addSchemeHandler(FileSchemeHandler.createWithAssets(context))
|
||||
// .addSchemeHandler(OkHttpNetworkSchemeHandler.create())
|
||||
// .addMediaDecoder(SvgMediaDecoder.create());
|
||||
// }))
|
||||
.usePlugin(PicassoImagesPlugin.create(context))
|
||||
// important to use TableEntryPlugin instead of TablePlugin
|
||||
.usePlugin(TableEntryPlugin.create(context))
|
||||
.usePlugin(HtmlPlugin.create())
|
||||
|
@ -7,6 +7,7 @@ include ':app', ':sample',
|
||||
':markwon-ext-tasklist',
|
||||
':markwon-html',
|
||||
':markwon-image',
|
||||
':markwon-image-picasso',
|
||||
':markwon-recycler',
|
||||
':markwon-recycler-table',
|
||||
':markwon-syntax-highlight',
|
||||
|
Loading…
x
Reference in New Issue
Block a user