Update jlatex plugin to be independent of images
This commit is contained in:
		
							parent
							
								
									64af306e53
								
							
						
					
					
						commit
						e35d3ad044
					
				| @ -1,2 +1,6 @@ | ||||
| * `Markwon.builder` won't require CorePlugin registration (it is done automatically) | ||||
|   to create a builder without CorePlugin - use `Markwon#builderNoCore` | ||||
| * `JLatex` plugin now is not dependent on ImagesPlugin | ||||
|   also accepts a ExecutorService (optional, by default cachedThreadPool is used) | ||||
| * AsyncDrawableScheduler now can be called by multiple plugins without penalty | ||||
|   internally caches latest state and skips scheduling if drawables are already processed | ||||
| @ -5,23 +5,37 @@ import android.graphics.drawable.Drawable; | ||||
| import android.os.Looper; | ||||
| import android.os.SystemClock; | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.text.Spanned; | ||||
| import android.text.style.DynamicDrawableSpan; | ||||
| import android.view.View; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import ru.noties.markwon.renderer.R; | ||||
| 
 | ||||
| public abstract class AsyncDrawableScheduler { | ||||
| 
 | ||||
|     public static void schedule(@NonNull final TextView textView) { | ||||
| 
 | ||||
|         final List<AsyncDrawable> list = extract(textView); | ||||
|         if (list.size() > 0) { | ||||
|         // we need a simple check if current text has already scheduled drawables | ||||
|         // we need this in order to allow multiple calls to schedule (different plugins | ||||
|         // might use AsyncDrawable), but we do not want to repeat the task | ||||
|         // | ||||
|         // hm... we need the same thing for unschedule then... we can check if last hash is !null, | ||||
|         // if it's not -> unschedule, else ignore | ||||
| 
 | ||||
|         final Integer lastTextHashCode = | ||||
|                 (Integer) textView.getTag(R.id.markwon_drawables_scheduler_last_text_hashcode); | ||||
|         final int textHashCode = textView.getText().hashCode(); | ||||
|         if (lastTextHashCode != null | ||||
|                 && lastTextHashCode == textHashCode) { | ||||
|             return; | ||||
|         } | ||||
|         textView.setTag(R.id.markwon_drawables_scheduler_last_text_hashcode, textHashCode); | ||||
| 
 | ||||
| 
 | ||||
|         final AsyncDrawableSpan[] spans = extractSpans(textView); | ||||
|         if (spans != null | ||||
|                 && spans.length > 0) { | ||||
| 
 | ||||
|             if (textView.getTag(R.id.markwon_drawables_scheduler) == null) { | ||||
|                 final View.OnAttachStateChangeListener listener = new View.OnAttachStateChangeListener() { | ||||
| @ -41,7 +55,10 @@ public abstract class AsyncDrawableScheduler { | ||||
|                 textView.setTag(R.id.markwon_drawables_scheduler, listener); | ||||
|             } | ||||
| 
 | ||||
|             for (AsyncDrawable drawable : list) { | ||||
|             AsyncDrawable drawable; | ||||
| 
 | ||||
|             for (AsyncDrawableSpan span : spans) { | ||||
|                 drawable = span.getDrawable(); | ||||
|                 drawable.setCallback2(new DrawableCallbackImpl(textView, drawable.getBounds())); | ||||
|             } | ||||
|         } | ||||
| @ -49,57 +66,39 @@ public abstract class AsyncDrawableScheduler { | ||||
| 
 | ||||
|     // must be called when text manually changed in TextView | ||||
|     public static void unschedule(@NonNull TextView view) { | ||||
|         for (AsyncDrawable drawable : extract(view)) { | ||||
|             drawable.setCallback2(null); | ||||
| 
 | ||||
|         if (view.getTag(R.id.markwon_drawables_scheduler_last_text_hashcode) == null) { | ||||
|             return; | ||||
|         } | ||||
|         view.setTag(R.id.markwon_drawables_scheduler_last_text_hashcode, null); | ||||
| 
 | ||||
| 
 | ||||
|         final AsyncDrawableSpan[] spans = extractSpans(view); | ||||
|         if (spans != null | ||||
|                 && spans.length > 0) { | ||||
|             for (AsyncDrawableSpan span : spans) { | ||||
|                 span.getDrawable().setCallback2(null); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static List<AsyncDrawable> extract(@NonNull TextView view) { | ||||
|     @Nullable | ||||
|     private static AsyncDrawableSpan[] extractSpans(@NonNull TextView textView) { | ||||
| 
 | ||||
|         final List<AsyncDrawable> list; | ||||
| 
 | ||||
|         final CharSequence cs = view.getText(); | ||||
|         final CharSequence cs = textView.getText(); | ||||
|         final int length = cs != null | ||||
|                 ? cs.length() | ||||
|                 : 0; | ||||
| 
 | ||||
|         if (length == 0 || !(cs instanceof Spanned)) { | ||||
|             //noinspection unchecked | ||||
|             list = Collections.EMPTY_LIST; | ||||
|         } else { | ||||
| 
 | ||||
|             final List<AsyncDrawable> drawables = new ArrayList<>(2); | ||||
| 
 | ||||
|             final Spanned spanned = (Spanned) cs; | ||||
|             final AsyncDrawableSpan[] asyncDrawableSpans = spanned.getSpans(0, length, AsyncDrawableSpan.class); | ||||
|             if (asyncDrawableSpans != null | ||||
|                     && asyncDrawableSpans.length > 0) { | ||||
|                 for (AsyncDrawableSpan span : asyncDrawableSpans) { | ||||
|                     drawables.add(span.getDrawable()); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             final DynamicDrawableSpan[] dynamicDrawableSpans = spanned.getSpans(0, length, DynamicDrawableSpan.class); | ||||
|             if (dynamicDrawableSpans != null | ||||
|                     && dynamicDrawableSpans.length > 0) { | ||||
|                 for (DynamicDrawableSpan span : dynamicDrawableSpans) { | ||||
|                     final Drawable d = span.getDrawable(); | ||||
|                     if (d != null | ||||
|                             && d instanceof AsyncDrawable) { | ||||
|                         drawables.add((AsyncDrawable) d); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if (drawables.size() == 0) { | ||||
|                 //noinspection unchecked | ||||
|                 list = Collections.EMPTY_LIST; | ||||
|             } else { | ||||
|                 list = drawables; | ||||
|             } | ||||
|         if (length == 0 | ||||
|                 || !(cs instanceof Spanned)) { | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|         return list; | ||||
|         // we also could've tried the `nextSpanTransition`, but strangely it leads to worse performance | ||||
|         // then direct getSpans | ||||
| 
 | ||||
|         return ((Spanned) cs).getSpans(0, length, AsyncDrawableSpan.class); | ||||
|     } | ||||
| 
 | ||||
|     private AsyncDrawableScheduler() { | ||||
|  | ||||
| @ -2,5 +2,6 @@ | ||||
| <resources> | ||||
| 
 | ||||
|     <item name="markwon_drawables_scheduler" type="id" /> | ||||
|     <item name="markwon_drawables_scheduler_last_text_hashcode" type="id" /> | ||||
| 
 | ||||
| </resources> | ||||
| @ -16,6 +16,7 @@ android { | ||||
| dependencies { | ||||
| 
 | ||||
|     api project(':markwon-core') | ||||
|     api project(':markwon-image') | ||||
|     api deps['jlatexmath-android'] | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,24 +1,31 @@ | ||||
| package ru.noties.markwon.ext.latex; | ||||
| 
 | ||||
| import android.graphics.drawable.Drawable; | ||||
| import android.net.Uri; | ||||
| import android.os.Handler; | ||||
| import android.os.Looper; | ||||
| import android.os.SystemClock; | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.support.annotation.Px; | ||||
| import android.text.Spanned; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import org.commonmark.node.Image; | ||||
| import org.commonmark.parser.Parser; | ||||
| 
 | ||||
| import java.io.ByteArrayInputStream; | ||||
| import java.io.InputStream; | ||||
| import java.io.UnsupportedEncodingException; | ||||
| import java.util.Scanner; | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
| import java.util.concurrent.ExecutorService; | ||||
| import java.util.concurrent.Executors; | ||||
| import java.util.concurrent.Future; | ||||
| 
 | ||||
| import ru.noties.jlatexmath.JLatexMathDrawable; | ||||
| import ru.noties.markwon.AbstractMarkwonPlugin; | ||||
| import ru.noties.markwon.MarkwonConfiguration; | ||||
| import ru.noties.markwon.MarkwonVisitor; | ||||
| import ru.noties.markwon.RenderProps; | ||||
| import ru.noties.markwon.image.AsyncDrawable; | ||||
| import ru.noties.markwon.image.AsyncDrawableLoader; | ||||
| import ru.noties.markwon.image.AsyncDrawableScheduler; | ||||
| import ru.noties.markwon.image.AsyncDrawableSpan; | ||||
| import ru.noties.markwon.image.ImageSize; | ||||
| 
 | ||||
| /** | ||||
| @ -65,27 +72,29 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin { | ||||
| 
 | ||||
|         private final int padding; | ||||
| 
 | ||||
|         // @since 4.0.0-SNAPSHOT | ||||
|         private final ExecutorService executorService; | ||||
| 
 | ||||
|         Config(@NonNull Builder builder) { | ||||
|             this.textSize = builder.textSize; | ||||
|             this.background = builder.background; | ||||
|             this.align = builder.align; | ||||
|             this.fitCanvas = builder.fitCanvas; | ||||
|             this.padding = builder.padding; | ||||
| 
 | ||||
|             // @since 4.0.0-SNAPSHOT | ||||
|             ExecutorService executorService = builder.executorService; | ||||
|             if (executorService == null) { | ||||
|                 executorService = Executors.newCachedThreadPool(); | ||||
|             } | ||||
|             this.executorService = executorService; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     public static String makeDestination(@NonNull String latex) { | ||||
|         return SCHEME + "://" + latex; | ||||
|     } | ||||
| 
 | ||||
|     private static final String SCHEME = "jlatexmath"; | ||||
|     private static final String CONTENT_TYPE = "text/jlatexmath"; | ||||
| 
 | ||||
|     private final Config config; | ||||
|     private final JLatextAsyncDrawableLoader jLatextAsyncDrawableLoader; | ||||
| 
 | ||||
|     JLatexMathPlugin(@NonNull Config config) { | ||||
|         this.config = config; | ||||
|         this.jLatextAsyncDrawableLoader = new JLatextAsyncDrawableLoader(config); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
| @ -102,70 +111,36 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin { | ||||
|                 final String latex = jLatexMathBlock.latex(); | ||||
| 
 | ||||
|                 final int length = visitor.length(); | ||||
| 
 | ||||
|                 visitor.builder().append(latex); | ||||
| 
 | ||||
|                 final RenderProps renderProps = visitor.renderProps(); | ||||
|                 final MarkwonConfiguration configuration = visitor.configuration(); | ||||
| 
 | ||||
|                 ImageProps.DESTINATION.set(renderProps, makeDestination(latex)); | ||||
|                 ImageProps.REPLACEMENT_TEXT_IS_LINK.set(renderProps, false); | ||||
|                 ImageProps.IMAGE_SIZE.set(renderProps, new ImageSize(new ImageSize.Dimension(100, "%"), null)); | ||||
|                 final AsyncDrawableSpan span = new AsyncDrawableSpan( | ||||
|                         configuration.theme(), | ||||
|                         new AsyncDrawable( | ||||
|                                 latex, | ||||
|                                 jLatextAsyncDrawableLoader, | ||||
|                                 configuration.imageSizeResolver(), | ||||
|                                 new ImageSize( | ||||
|                                         new ImageSize.Dimension(100, "%"), | ||||
|                                         null)), | ||||
|                         AsyncDrawableSpan.ALIGN_BOTTOM, | ||||
|                         false); | ||||
| 
 | ||||
|                 visitor.setSpansForNode(Image.class, length); | ||||
|                 visitor.setSpans(length, span); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void configureImages(@NonNull AsyncDrawableLoader.Builder builder) { | ||||
|         builder | ||||
|                 .addSchemeHandler(SCHEME, new SchemeHandler() { | ||||
|                     @Nullable | ||||
|                     @Override | ||||
|                     public ImageItem handle(@NonNull String raw, @NonNull Uri uri) { | ||||
| 
 | ||||
|                         ImageItem item = null; | ||||
| 
 | ||||
|                         try { | ||||
|                             final byte[] bytes = raw.substring(SCHEME.length()).getBytes("UTF-8"); | ||||
|                             item = new ImageItem( | ||||
|                                     CONTENT_TYPE, | ||||
|                                     new ByteArrayInputStream(bytes)); | ||||
|                         } catch (UnsupportedEncodingException e) { | ||||
|                             e.printStackTrace(); | ||||
|                         } | ||||
| 
 | ||||
|                         return item; | ||||
|                     } | ||||
|                 }) | ||||
|                 .addMediaDecoder(CONTENT_TYPE, new MediaDecoder() { | ||||
|                     @Nullable | ||||
|                     @Override | ||||
|                     public Drawable decode(@NonNull InputStream inputStream) { | ||||
| 
 | ||||
|                         final Scanner scanner = new Scanner(inputStream, "UTF-8").useDelimiter("\\A"); | ||||
|                         final String latex = scanner.hasNext() | ||||
|                                 ? scanner.next() | ||||
|                                 : null; | ||||
| 
 | ||||
|                         if (latex == null) { | ||||
|                             return null; | ||||
|                         } | ||||
| 
 | ||||
|                         return JLatexMathDrawable.builder(latex) | ||||
|                                 .textSize(config.textSize) | ||||
|                                 .background(config.background) | ||||
|                                 .align(config.align) | ||||
|                                 .fitCanvas(config.fitCanvas) | ||||
|                                 .padding(config.padding) | ||||
|                                 .build(); | ||||
|                     } | ||||
|                 }); | ||||
|     public void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown) { | ||||
|         AsyncDrawableScheduler.unschedule(textView); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public Priority priority() { | ||||
|         return Priority.after(ImagesPlugin.class); | ||||
|     public void afterSetText(@NonNull TextView textView) { | ||||
|         AsyncDrawableScheduler.schedule(textView); | ||||
|     } | ||||
| 
 | ||||
|     public static class Builder { | ||||
| @ -181,6 +156,9 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin { | ||||
| 
 | ||||
|         private int padding; | ||||
| 
 | ||||
|         // @since 4.0.0-SNAPSHOT | ||||
|         private ExecutorService executorService; | ||||
| 
 | ||||
|         Builder(float textSize) { | ||||
|             this.textSize = textSize; | ||||
|         } | ||||
| @ -209,9 +187,95 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin { | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * @since 4.0.0-SNAPSHOT | ||||
|          */ | ||||
|         @NonNull | ||||
|         public Builder executorService(@NonNull ExecutorService executorService) { | ||||
|             this.executorService = executorService; | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         @NonNull | ||||
|         public Config build() { | ||||
|             return new Config(this); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // @since 4.0.0-SNAPSHOT | ||||
|     private static class JLatextAsyncDrawableLoader extends AsyncDrawableLoader { | ||||
| 
 | ||||
|         private final Config config; | ||||
|         private final Handler handler = new Handler(Looper.getMainLooper()); | ||||
|         private final Map<AsyncDrawable, Future<?>> cache = new HashMap<>(3); | ||||
| 
 | ||||
|         JLatextAsyncDrawableLoader(@NonNull Config config) { | ||||
|             this.config = config; | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public void load(@NonNull final AsyncDrawable drawable) { | ||||
| 
 | ||||
|             // this method must be called from main-thread only (thus synchronization can be skipped) | ||||
| 
 | ||||
|             // check for currently running tasks associated with provided drawable | ||||
|             final Future<?> future = cache.get(drawable); | ||||
| 
 | ||||
|             // if it's present -> proceed with new execution | ||||
|             // as asyncDrawable is immutable, it won't have destination changed (so there is no need | ||||
|             // to cancel any started tasks) | ||||
|             if (future == null) { | ||||
| 
 | ||||
|                 cache.put(drawable, config.executorService.submit(new Runnable() { | ||||
|                     @Override | ||||
|                     public void run() { | ||||
| 
 | ||||
|                         // create JLatexMathDrawable | ||||
|                         final JLatexMathDrawable jLatexMathDrawable = | ||||
|                                 JLatexMathDrawable.builder(drawable.getDestination()) | ||||
|                                         .textSize(config.textSize) | ||||
|                                         .background(config.background) | ||||
|                                         .align(config.align) | ||||
|                                         .fitCanvas(config.fitCanvas) | ||||
|                                         .padding(config.padding) | ||||
|                                         .build(); | ||||
| 
 | ||||
|                         // we must post to handler, but also have a way to identify the drawable | ||||
|                         // for which we are posting (in case of cancellation) | ||||
|                         handler.postAtTime(new Runnable() { | ||||
|                             @Override | ||||
|                             public void run() { | ||||
|                                 // remove entry from cache (it will be present if task is not cancelled) | ||||
|                                 if (cache.remove(drawable) != null | ||||
|                                         && drawable.isAttached()) { | ||||
|                                     drawable.setResult(jLatexMathDrawable); | ||||
|                                 } | ||||
| 
 | ||||
|                             } | ||||
|                         }, drawable, SystemClock.uptimeMillis()); | ||||
|                     } | ||||
|                 })); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public void cancel(@NonNull AsyncDrawable drawable) { | ||||
| 
 | ||||
|             // this method also must be called from main thread only | ||||
| 
 | ||||
|             final Future<?> future = cache.remove(drawable); | ||||
|             if (future != null) { | ||||
|                 future.cancel(true); | ||||
|             } | ||||
| 
 | ||||
|             // remove all callbacks (via runnable) and messages posted for this drawable | ||||
|             handler.removeCallbacksAndMessages(drawable); | ||||
|         } | ||||
| 
 | ||||
|         @Nullable | ||||
|         @Override | ||||
|         public Drawable placeholder(@NonNull AsyncDrawable drawable) { | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -50,6 +50,7 @@ dependencies { | ||||
|         implementation it['prism4j'] | ||||
|         implementation it['debug'] | ||||
|         implementation it['adapt'] | ||||
|         implementation it['android-svg'] | ||||
|     } | ||||
| 
 | ||||
|     deps['annotationProcessor'].with { | ||||
|  | ||||
| @ -19,9 +19,8 @@ import ru.noties.markwon.MarkwonConfiguration; | ||||
| import ru.noties.markwon.MarkwonPlugin; | ||||
| import ru.noties.markwon.MarkwonSpansFactory; | ||||
| import ru.noties.markwon.MarkwonVisitor; | ||||
| import ru.noties.markwon.movement.MovementMethodPlugin; | ||||
| import ru.noties.markwon.core.MarkwonTheme; | ||||
| import ru.noties.markwon.image.AsyncDrawableLoader; | ||||
| import ru.noties.markwon.movement.MovementMethodPlugin; | ||||
| 
 | ||||
| public class BasicPluginsActivity extends Activity { | ||||
| 
 | ||||
| @ -168,25 +167,25 @@ public class BasicPluginsActivity extends Activity { | ||||
|         final String markdown = ""; | ||||
| 
 | ||||
|         final Markwon markwon = Markwon.builder(this) | ||||
|                 .usePlugin(ImagesPlugin.create(this)) | ||||
|                 .usePlugin(new AbstractMarkwonPlugin() { | ||||
|                     @Override | ||||
|                     public void configureImages(@NonNull AsyncDrawableLoader.Builder builder) { | ||||
|                         // we can have a custom SchemeHandler | ||||
|                         // here we will just use networkSchemeHandler to redirect call | ||||
|                         builder.addSchemeHandler("myownscheme", new SchemeHandler() { | ||||
| 
 | ||||
|                             final NetworkSchemeHandler networkSchemeHandler = NetworkSchemeHandler.create(); | ||||
| 
 | ||||
|                             @Nullable | ||||
|                             @Override | ||||
|                             public ImageItem handle(@NonNull String raw, @NonNull Uri uri) { | ||||
|                                 raw = raw.replace("myownscheme", "https"); | ||||
|                                 return networkSchemeHandler.handle(raw, Uri.parse(raw)); | ||||
|                             } | ||||
|                         }); | ||||
|                     } | ||||
|                 }) | ||||
| //                .usePlugin(ImagesPlugin.create(this)) | ||||
| //                .usePlugin(new AbstractMarkwonPlugin() { | ||||
| //                    @Override | ||||
| //                    public void configureImages(@NonNull AsyncDrawableLoader.Builder builder) { | ||||
| //                        // we can have a custom SchemeHandler | ||||
| //                        // here we will just use networkSchemeHandler to redirect call | ||||
| //                        builder.addSchemeHandler("myownscheme", new SchemeHandler() { | ||||
| // | ||||
| //                            final NetworkSchemeHandler networkSchemeHandler = NetworkSchemeHandler.create(); | ||||
| // | ||||
| //                            @Nullable | ||||
| //                            @Override | ||||
| //                            public ImageItem handle(@NonNull String raw, @NonNull Uri uri) { | ||||
| //                                raw = raw.replace("myownscheme", "https"); | ||||
| //                                return networkSchemeHandler.handle(raw, Uri.parse(raw)); | ||||
| //                            } | ||||
| //                        }); | ||||
| //                    } | ||||
| //                }) | ||||
|                 .build(); | ||||
| 
 | ||||
|         markwon.setMarkdown(textView, markdown); | ||||
|  | ||||
| @ -26,7 +26,7 @@ public class CustomExtensionActivity extends Activity { | ||||
|         // `usePlugin` call | ||||
|         final Markwon markwon = Markwon.builder(this) | ||||
|                 // try commenting out this line to see runtime dependency resolution | ||||
|                 .usePlugin(ImagesPlugin.create(this)) | ||||
| //                .usePlugin(ImagesPlugin.create(this)) | ||||
|                 .usePlugin(IconPlugin.create(IconSpanProvider.create(this, 0))) | ||||
|                 .build(); | ||||
| 
 | ||||
|  | ||||
| @ -21,12 +21,12 @@ public class IconPlugin extends AbstractMarkwonPlugin { | ||||
|         this.iconSpanProvider = iconSpanProvider; | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public Priority priority() { | ||||
|         // define images dependency | ||||
|         return Priority.after(ImagesPlugin.class); | ||||
|     } | ||||
| //    @NonNull | ||||
| //    @Override | ||||
| //    public Priority priority() { | ||||
| //        // define images dependency | ||||
| //        return Priority.after(ImagesPlugin.class); | ||||
| //    } | ||||
| 
 | ||||
|     @Override | ||||
|     public void configureParser(@NonNull Parser.Builder builder) { | ||||
|  | ||||
| @ -42,7 +42,7 @@ public class LatexActivity extends Activity { | ||||
|                 + latex + "$$\n\n something like **this**"; | ||||
| 
 | ||||
|         final Markwon markwon = Markwon.builder(this) | ||||
|                 .usePlugin(ImagesPlugin.create(this)) | ||||
| //                .usePlugin(ImagesPlugin.create(this)) | ||||
|                 .usePlugin(JLatexMathPlugin.create(textView.getTextSize())) | ||||
|                 .build(); | ||||
| 
 | ||||
|  | ||||
| @ -26,7 +26,11 @@ 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.svg.SvgPlugin; | ||||
| 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.recycler.MarkwonAdapter; | ||||
| import ru.noties.markwon.recycler.SimpleEntry; | ||||
| import ru.noties.markwon.recycler.table.TableEntry; | ||||
| @ -73,8 +77,13 @@ public class RecyclerActivity extends Activity { | ||||
|     private static Markwon markwon(@NonNull Context context) { | ||||
|         return Markwon.builder(context) | ||||
|                 .usePlugin(CorePlugin.create()) | ||||
|                 .usePlugin(ImagesPlugin.createWithAssets(context)) | ||||
|                 .usePlugin(SvgPlugin.create(context.getResources())) | ||||
|                 .usePlugin(ImagesPlugin.create(plugin -> { | ||||
|                     plugin | ||||
|                             .addSchemeHandler(FileSchemeHandler.createWithAssets(context.getAssets())) | ||||
|                             .addSchemeHandler(OkHttpNetworkSchemeHandler.create()) | ||||
|                             .addMediaDecoder(SvgMediaDecoder.create()) | ||||
|                             .defaultMediaDecoder(DefaultImageMediaDecoder.create()); | ||||
|                 })) | ||||
|                 // important to use TableEntryPlugin instead of TablePlugin | ||||
|                 .usePlugin(TableEntryPlugin.create(context)) | ||||
|                 .usePlugin(HtmlPlugin.create()) | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Dimitry Ivanov
						Dimitry Ivanov