Preparing 4.0.0 release

This commit is contained in:
Dimitry Ivanov 2019-06-26 17:57:33 +03:00
parent d65a1809ca
commit 386254f962
51 changed files with 504 additions and 149 deletions

View File

@ -1,9 +1,40 @@
# Changelog
# 3.0.2
# 4.0.0
* maven group-id change to `io.noties.markwon` (was `ru.noties.markwon`)
* package name change to `io.notier.markwon.*` (was `ru.noties.markwon.*`)
* androidx artifacts (#76)
* `Markwon#builder` does not require explicit `CorePlugin` (added automatically),
use `Markwon#builderNoCore()` to obtain a builder without `CorePlugin`
* Removed `Priority` abstraction and `MarkwonPlugin#priority` (use `MarkwonPlugin.Registry`)
* Removed `MarkwonPlugin#configureHtmlRenderer` (for configuration use `HtmlPlugin` directly)
* Removed `MarkwonPlugin#configureImages` (for configuration use `ImagesPlugin` directly)
* Added `MarkwonPlugin.Registry` and `MarkwonPlugin#configure(Registry)` method
* `CorePlugin#addOnTextAddedListener` (process raw text added)
* `ImageSizeResolver` signature change (accept `AsyncDrawable`)
* `LinkResolver` is now an independent entity (previously part of `LinkSpan`)
* `AsyncDrawableScheduler` can now be called multiple times without performance penalty
* `AsyncDrawable` now exposes its destination, image-size, last known dimensions (canvas, text-size)
* `AsyncDrawableLoader` signature change (accept `AsyncDrawable`)
* Add `LastLineSpacingSpan`
* Add `MarkwonConfiguration.Builder#asyncDrawableLoader` method
* `ImagesPlugin` removed from `core` artifact
(also removed `images-gif`, `images-okhttp` and `images-svg` artifacts and their plugins)
* `ImagesPlugin` exposes configuration (adding scheme-handler, media-decoder, etc)
* `ImagesPlugin` allows multiple images with the same source (URL)
* Add `PlaceholderProvider` and `ErrorHandler` to `ImagesPlugin`
* `GIF` and `SVG` media-decoders are automatically added to `ImagesPlugin` if required libraries are found in the classpath
* `ImageItem` is now abstract, has 2 implementations: `withResult`, `withDecodingNeeded`
* Add `images-glide`, `images-picasso`, `linkify`, `simple-ext` modules
* `JLatexMathPlugin` is now independent of `ImagesPlugin`
* Fix wrong `JLatexMathPlugin` formulas sizes (#138)
* `JLatexMathPlugin` has `backgroundProvider`, `executorService` configuration
* `HtmlPlugin` is self-contained (all configuration is moved in the plugin itself)
## 3.0.2
* Fix `latex` plugin (#136)
* Add `#create(Call.Factory)` factory method to `OkHttpImagesPlugin` (#129)
thanks to @ZacSweers
<br>Thanks to @ZacSweers
## 3.0.1
* Add `AsyncDrawableLoader.Builder#implementation` method (#109)
@ -64,7 +95,7 @@ to get the full picture of latest changes.
* Add SpannableBuilder#getSpans method
* Fix DataUri scheme handler in image-loader (#74)
* Introduced a "copy" builder for SpannableThem
Thanks @c-b-h
<br>Thanks @c-b-h
## 2.0.0
* Add `html-parser-api` and `html-parser-impl` modules
@ -90,7 +121,7 @@ to get the full picture of latest changes.
* Fix OrderedListItemSpan text position (baseline) (#55)
* Add softBreakAddsNewLine option for SpannableConfiguration (#54)
* Paragraph text can now explicitly be spanned (#58)
Thanks to @c-b-h
<br>Thanks to @c-b-h
* Fix table border color if odd background is specified (#56)
* Add table customizations (even and header rows)
@ -98,10 +129,10 @@ to get the full picture of latest changes.
* Update commonmark to 0.11.0 and android-gif to 1.2.14
* Add syntax highlight functionality (`library-syntax` module and `markwon-syntax` artifact)
* Add headingTypeface, headingTextSizes to SpannableTheme
Thanks to @edenman
<br>Thanks to @edenman
* Introduce `MediaDecoder` abstraction to `image-loader` module
* Introduce `SpannableFactory`
Thanks for idea to @c-b-h
<br>Thanks for idea to @c-b-h
* Update sample application to use syntax-highlight
* Update sample application to use clickable placeholder for GIF media
@ -116,12 +147,12 @@ to `ru.noties.markwon.renderer` package (one level up, previously `ru.noties.mar
* Change LinkSpan to extend URLSpan. Allow default linkColor (if not set explicitly)
* Fit an image without dimensions to canvas width (and keep ratio)
* Add support for separate color for code blocks (#37)
Thanks to @Arcnor
<br>Thanks to @Arcnor
## v1.0.4
* Fixes #28 (tables are not rendered when at the end of the markdown)
* Adds support for `indented code blocks`
Thanks to @dlew
<br>Thanks to @dlew
## v1.0.3
* Fixed ordered lists (when number width is greater than block margin)

View File

@ -35,13 +35,14 @@ features listed in [commonmark-spec] are supported
implementation "io.noties.markwon:core:${markwonVersion}"
```
Full list of available artifacts is present in the [install section](https://noties.github.io/Markwon/docs/v3/install.html)
Full list of available artifacts is present in the [install section](https://noties.github.io/Markwon/docs/v4/install.html)
of the [documentation] web-site.
Please visit [documentation] web-site for further reference.
> You can find previous version of Markwon in [2.x.x](https://github.com/noties/Markwon/tree/2.x.x) branch
> You can find previous version of Markwon in [2.x.x](https://github.com/noties/Markwon/tree/2.x.x)
and [3.x.x](https://github.com/noties/Markwon/tree/3.x.x) branches
## Supported markdown features:

View File

@ -1,16 +0,0 @@
* `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
* configure with registry
* removed priority
* images-plugin moved to standalone again
* removed MarkwonPlugin#configureHtmlRenderer -> now part of HtmlPlugin
* TagHandler now has `supportedTags()` method
* html is moved completely to html-plugin
* OnTextAddedListener (CorePlugin)
* ImageSizeResolver signature change (accept AsyncDrawable)
* JLatexMathPlugin builder has vertical & horizontal padding
* LinkResolver is now an independent entity (previously part of LinkSpan)

View File

@ -34,7 +34,6 @@ dependencies {
implementation project(':markwon-ext-tasklist')
implementation project(':markwon-html')
implementation project(':markwon-image')
implementation project(':markwon-linkify')
implementation project(':markwon-syntax-highlight')
deps.with {

View File

@ -1,3 +1,27 @@
# Image Glide
<MavenBadge4 :artifact="'image-glide'" />
Image loading based on `Glide` library
```java
final Markwon markwon = Markwon.builder(context)
// automatically create Glide instance
.usePlugin(GlideImagesPlugin.create(context))
// use supplied Glide instance
.usePlugin(GlideImagesPlugin.create(Glide.with(context)))
// if you need more control
.usePlugin(GlideImagesPlugin.create(new GlideImagesPlugin.GlideStore() {
@NonNull
@Override
public RequestBuilder<Drawable> load(@NonNull AsyncDrawable drawable) {
return Glide.with(context).load(drawable.getDestination());
}
@Override
public void cancel(@NonNull Target<?> target) {
Glide.with(context).clear(target);
}
}))
.build();
```

View File

@ -1,3 +1,32 @@
# Image Picasso
<MavenBadge4 :artifact="'image-picasso'" />
Image loading based on `Picasso` library
```java
final Markwon markwon = Markwon.builder(context)
// automatically create Picasso instance
.usePlugin(PicassoImagesPlugin.create(context))
// use provided picasso instance
.usePlugin(PicassoImagesPlugin.create(Picasso.get()))
// if you need more control
.usePlugin(PicassoImagesPlugin.create(new PicassoImagesPlugin.PicassoStore() {
@NonNull
@Override
public RequestCreator load(@NonNull AsyncDrawable drawable) {
return Picasso.get()
.load(drawable.getDestination())
// please note that drawable should be used as tag (not a destination)
// otherwise there won't be support for multiple images with the same URL
.tag(drawable);
}
@Override
public void cancel(@NonNull AsyncDrawable drawable) {
Picasso.get()
.cancelTag(drawable);
}
}))
.build();
```

View File

@ -194,15 +194,153 @@ introduce any unexpected behavior.
### GifMediaDecoder
Adds support for GIF media in markdown. If `pl.droidsonroids.gif:android-gif-drawable:*` dependency
is found in the classpath, then registration will happen automatically.
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(ImagesPlugin.create(new ImagesPlugin.ImagesConfigure() {
@Override
public void configureImages(@NonNull ImagesPlugin plugin) {
// autoplayGif controls if GIF should be automatically started
plugin.addMediaDecoder(GifMediaDecoder.create(/*autoplayGif*/false));
}
}))
.build();
```
### SvgMediaDecoder
Adds support for SVG media in markdown. If `com.caverock:androidsvg:*` dependency is found
in the classpath, then registration will happen automatically.
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(ImagesPlugin.create(new ImagesPlugin.ImagesConfigure() {
@Override
public void configureImages(@NonNull ImagesPlugin plugin) {
// uses supplied Resources
plugin.addMediaDecoder(SvgMediaDecoder.create(context.getResources()));
// uses Resources.getSystem()
plugin.addMediaDecoder(SvgMediaDecoder.create());
}
}))
.build();
```
### DefaultMediaDecoder
`DefaultMediaDecoder` _tries_ to decode supplied InputStream
as Bitmap (via `BitmapFactory.decodeStream(inputStream)`). This decoder is registered automatically.
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(ImagesPlugin.create(new ImagesPlugin.ImagesConfigure() {
@Override
public void configureImages(@NonNull ImagesPlugin plugin) {
// uses supplied Resources
plugin.defaultMediaDecoder(DefaultMediaDecoder.create(context.getResources()));
// uses Resources.getSystem()
plugin.defaultMediaDecoder(DefaultMediaDecoder.create());
}
}))
.build();
```
## AsyncDrawableScheduler
`AsyncDrawableScheduler` is used in order to give `AsyncDrawable` a way to invalidate `TextView`
that is holding it. A plugin that is dealing with `AsyncDrawable` should always call these methods:
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown) {
AsyncDrawableScheduler.unschedule(textView);
}
@Override
public void afterSetText(@NonNull TextView textView) {
AsyncDrawableScheduler.schedule(textView);
}
})
.build();
```
:::tip
Starting with <Badge text="4.0.0" /> multiple plugins can call `AsyncDrawableScheduler#schedule`
method without the penalty to process `AsyncDrawable` callbacks multiple times (internally caches
state which ensures that a `TextView` is processed only once the text has changed).
:::
## ErrorHandler
## Placeholder
An `ErrorHandler` can be used to receive an error that has happened during image loading
and (optionally) return an error drawable to be displayed instead
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(ImagesPlugin.create(new ImagesPlugin.ImagesConfigure() {
@Override
public void configureImages(@NonNull ImagesPlugin plugin) {
plugin.errorHandler(new ImagesPlugin.ErrorHandler() {
@Nullable
@Override
public Drawable handleError(@NonNull String url, @NonNull Throwable throwable) {
return null;
}
});
}
}))
.build();
```
## PlaceholderProvider
To display a placeholder during image loading `PlaceholderProvider` can be used:
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(ImagesPlugin.create(new ImagesPlugin.ImagesConfigure() {
@Override
public void configureImages(@NonNull ImagesPlugin plugin) {
plugin.placeholderProvider(new ImagesPlugin.PlaceholderProvider() {
@Nullable
@Override
public Drawable providePlaceholder(@NonNull AsyncDrawable drawable) {
return null;
}
});
}
}))
.build();
```
:::tip
If your placeholder drawable has _specific_ size which is not the same an image that is being loaded,
you can manually assign bounds to the placeholder:
```java
plugin.placeholderProvider(new ImagesPlugin.PlaceholderProvider() {
@Override
public Drawable providePlaceholder(@NonNull AsyncDrawable drawable) {
final ColorDrawable placeholder = new ColorDrawable(Color.BLUE);
// these bounds will be used to display a placeholder,
// so even if loading image has size `width=100%`, placeholder
// bounds won't be affected by it
placeholder.setBounds(0, 0, 48, 48);
return placeholder;
}
});
```
:::
---
:::tip
If you are using [html](/docs/v4/html/) you do not have to additionally setup

View File

@ -1,3 +1,31 @@
# Linkify
<MavenBadge4 :artifact="'linkify'" />
A plugin to automatically add links to your markdown. Currently autolinking works for:
* email (`me@web.com`)
* phone numbers (`+10000000`)
* web URLS
:::warning
`Linkify` plugin is based on `android.text.util.Linkify` which can lead to significant performance
drop due to its implementation based on regex.
:::
:::danger
Do not use `autolink` XML attribute on your `TextView` as it will remove
all links except autolinked ones ¯\\\_(ツ)_/¯
:::
```java
final Markwon markwon = Markwon.builder(context)
// will autolink all supported types
.usePlugin(LinkifyPlugin.create())
// the same as above
.usePlugin(LinkifyPlugin.create(
Linkify.EMAIL_ADDRESSES | Linkify.PHONE_NUMBERS | Linkify.WEB_URLS
))
// only emails
.usePlugin(LinkifyPlugin.create(Linkify.EMAIL_ADDRESSES))
.build();
```

View File

@ -2,5 +2,6 @@
* maven group-id is changed: `io.noties.markwon` (was `ru.noties.markwon`)
* root package-name is changed: `io.noties.markwon` (was `ru.noties.markwon`)
* `androidx` packages
todo

View File

@ -60,3 +60,11 @@ This extension will be applied to a text like this:
```md
@@we are inside different delimiter characters$$
```
:::warning
Space character cannot be used as a delimiter (from either side). So,
```java
plugin.addExtension(1, '@', ' ', /*spanFactory*/);
```
won't work for `@some-text ` text
:::

View File

@ -7,7 +7,7 @@ import androidx.annotation.NonNull;
/**
* @see LinkResolverDef
* @see MarkwonConfiguration.Builder#linkResolver(LinkResolver)
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
public interface LinkResolver {
void resolve(@NonNull View view, @NonNull String link);

View File

@ -47,14 +47,14 @@ public abstract class Markwon {
@NonNull
public static Builder builder(@NonNull Context context) {
return new MarkwonBuilderImpl(context)
// @since 4.0.0-SNAPSHOT add CorePlugin
// @since 4.0.0 add CorePlugin
.usePlugin(CorePlugin.create());
}
/**
* Factory method to obtain an instance of {@link Builder} without {@link CorePlugin}.
*
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public static Builder builderNoCore(@NonNull Context context) {

View File

@ -95,7 +95,7 @@ public class MarkwonConfiguration {
}
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public Builder asyncDrawableLoader(@NonNull AsyncDrawableLoader asyncDrawableLoader) {
@ -138,7 +138,7 @@ public class MarkwonConfiguration {
this.theme = theme;
this.spansFactory = spansFactory;
// @since 4.0.0-SNAPSHOT
// @since 4.0.0
if (asyncDrawableLoader == null) {
asyncDrawableLoader = AsyncDrawableLoader.noOp();
}

View File

@ -26,7 +26,7 @@ public interface MarkwonPlugin {
/**
* @see Registry#require(Class, Action)
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
interface Action<P extends MarkwonPlugin> {
void apply(@NonNull P p);
@ -34,7 +34,7 @@ public interface MarkwonPlugin {
/**
* @see #configure(Registry)
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
interface Registry {
@ -49,7 +49,7 @@ public interface MarkwonPlugin {
/**
* This method will be called before any other during {@link Markwon} instance construction.
*
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
void configure(@NonNull Registry registry);
@ -91,14 +91,6 @@ public interface MarkwonPlugin {
*/
void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder);
// /**
// * Configure {@link MarkwonHtmlRenderer} to add or remove HTML {@link TagHandler}s
// *
// * @see MarkwonHtmlRenderer
// * @see MarkwonHtmlRenderer.Builder
// */
// void configureHtmlRenderer(@NonNull MarkwonHtmlRenderer.Builder builder);
/**
* Process input markdown and return new string to be used in parsing stage further.
* Can be described as <code>pre-processing</code> of markdown String.

View File

@ -10,7 +10,7 @@ import java.util.Set;
import io.noties.markwon.core.CorePlugin;
// @since 4.0.0-SNAPSHOT
// @since 4.0.0
class RegistryImpl implements MarkwonPlugin.Registry {
private final List<MarkwonPlugin> origin;

View File

@ -58,7 +58,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
/**
* @see #addOnTextAddedListener(OnTextAddedListener)
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
public interface OnTextAddedListener {
@ -88,7 +88,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
return new CorePlugin();
}
// @since 4.0.0-SNAPSHOT
// @since 4.0.0
private final List<OnTextAddedListener> onTextAddedListeners = new ArrayList<>(0);
protected CorePlugin() {
@ -98,7 +98,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
* Can be useful to post-process text added. For example for auto-linking capabilities.
*
* @see OnTextAddedListener
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@SuppressWarnings("UnusedReturnValue")
@NonNull
@ -171,7 +171,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
visitor.builder().append(literal);
// @since 4.0.0-SNAPSHOT
// @since 4.0.0
if (!onTextAddedListeners.isEmpty()) {
// calculate the start position
final int length = visitor.length() - literal.length();
@ -262,7 +262,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
});
}
// @since 4.0.0-SNAPSHOT
// @since 4.0.0
// 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) {

View File

@ -8,7 +8,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Px;
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
public class LastLineSpacingSpan implements LineHeightSpan {

View File

@ -53,7 +53,7 @@ public class AsyncDrawable extends Drawable {
}
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@Nullable
public ImageSize getImageSize() {
@ -61,7 +61,7 @@ public class AsyncDrawable extends Drawable {
}
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public ImageSizeResolver getImageSizeResolver() {
@ -69,7 +69,7 @@ public class AsyncDrawable extends Drawable {
}
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
public boolean hasKnownDimentions() {
return canvasWidth > 0;
@ -77,7 +77,7 @@ public class AsyncDrawable extends Drawable {
/**
* @see #hasKnownDimentions()
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
public int getLastKnownCanvasWidth() {
return canvasWidth;
@ -85,7 +85,7 @@ public class AsyncDrawable extends Drawable {
/**
* @see #hasKnownDimentions()
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
public float getLastKnowTextSize() {
return textSize;
@ -262,7 +262,7 @@ public class AsyncDrawable extends Drawable {
if (hasResult()) {
out = result.getIntrinsicWidth();
} else {
// @since 4.0.0-SNAPSHOT, must not be zero in order to receive canvas dimensions
// @since 4.0.0, must not be zero in order to receive canvas dimensions
out = 1;
}
return out;
@ -274,7 +274,7 @@ public class AsyncDrawable extends Drawable {
if (hasResult()) {
out = result.getIntrinsicHeight();
} else {
// @since 4.0.0-SNAPSHOT, must not be zero in order to receive canvas dimensions
// @since 4.0.0, must not be zero in order to receive canvas dimensions
out = 1;
}
return out;

View File

@ -16,12 +16,12 @@ public abstract class AsyncDrawableLoader {
}
/**
* @since 3.1.0-SNAPSHOT
* @since 4.0.0
*/
public abstract void load(@NonNull AsyncDrawable drawable);
/**
* @since 3.1.0-SNAPSHOT
* @since 4.0.0
*/
public abstract void cancel(@NonNull AsyncDrawable drawable);

View File

@ -24,7 +24,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
// @since 4.0.0
final Integer lastTextHashCode =
(Integer) textView.getTag(R.id.markwon_drawables_scheduler_last_text_hashcode);
final int textHashCode = textView.getText().hashCode();
@ -69,7 +69,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
// @since 4.0.0
if (view.getTag(R.id.markwon_drawables_scheduler_last_text_hashcode) == null) {
return;
}
@ -99,7 +99,7 @@ public abstract class AsyncDrawableScheduler {
}
// we also could've tried the `nextSpanTransition`, but strangely it leads to worse performance
// then direct getSpans
// than direct getSpans
return ((Spanned) cs).getSpans(0, length, AsyncDrawableSpan.class);
}

View File

@ -12,7 +12,7 @@ import androidx.annotation.NonNull;
public abstract class ImageSizeResolver {
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public abstract Rect resolveImageSize(@NonNull AsyncDrawable drawable);

View File

@ -31,7 +31,7 @@ public class ImageSizeResolverDefTest {
@Test
public void correct_redirect() {
// @since 4.0.0-SNAPSHOT the main method is changed to accept AsyncDrawable
// @since 4.0.0 the main method is changed to accept AsyncDrawable
final ImageSizeResolverDef def = mock(ImageSizeResolverDef.class, Mockito.CALLS_REAL_METHODS);
final AsyncDrawable drawable = mock(AsyncDrawable.class);

View File

@ -36,7 +36,7 @@ import ru.noties.jlatexmath.JLatexMathDrawable;
public class JLatexMathPlugin extends AbstractMarkwonPlugin {
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
public interface BackgroundProvider {
@NonNull
@ -73,7 +73,7 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
private final float textSize;
// @since 4.0.0-SNAPSHOT
// @since 4.0.0
private final BackgroundProvider backgroundProvider;
@JLatexMathDrawable.Align
@ -81,13 +81,13 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
private final boolean fitCanvas;
// @since 4.0.0-SNAPSHOT
// @since 4.0.0
private final int paddingHorizontal;
// @since 4.0.0-SNAPSHOT
// @since 4.0.0
private final int paddingVertical;
// @since 4.0.0-SNAPSHOT
// @since 4.0.0
private final ExecutorService executorService;
Config(@NonNull Builder builder) {
@ -98,7 +98,7 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
this.paddingHorizontal = builder.paddingHorizontal;
this.paddingVertical = builder.paddingVertical;
// @since 4.0.0-SNAPSHOT
// @since 4.0.0
ExecutorService executorService = builder.executorService;
if (executorService == null) {
executorService = Executors.newCachedThreadPool();
@ -164,7 +164,7 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
private final float textSize;
// @since 4.0.0-SNAPSHOT
// @since 4.0.0
private BackgroundProvider backgroundProvider;
@JLatexMathDrawable.Align
@ -172,13 +172,13 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
private boolean fitCanvas = true;
// @since 4.0.0-SNAPSHOT
// @since 4.0.0
private int paddingHorizontal;
// @since 4.0.0-SNAPSHOT
// @since 4.0.0
private int paddingVertical;
// @since 4.0.0-SNAPSHOT
// @since 4.0.0
private ExecutorService executorService;
Builder(float textSize) {
@ -211,7 +211,7 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
}
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public Builder builder(@Px int paddingHorizontal, @Px int paddingVertical) {
@ -221,7 +221,7 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
}
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public Builder executorService(@NonNull ExecutorService executorService) {
@ -235,7 +235,7 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
}
}
// @since 4.0.0-SNAPSHOT
// @since 4.0.0
private static class JLatextAsyncDrawableLoader extends AsyncDrawableLoader {
private final Config config;
@ -317,7 +317,7 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
}
// we must make drawable fit canvas (if specified), but do not keep the ratio whilst scaling up
// @since 4.0.0-SNAPSHOT
// @since 4.0.0
private static class JLatexImageSizeResolver extends ImageSizeResolver {
private final boolean fitCanvas;

View File

@ -29,7 +29,7 @@ public class HtmlPlugin extends AbstractMarkwonPlugin {
/**
* @see #create(HtmlConfigure)
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
public interface HtmlConfigure {
void configureHtml(@NonNull HtmlPlugin plugin);
@ -41,7 +41,7 @@ public class HtmlPlugin extends AbstractMarkwonPlugin {
}
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public static HtmlPlugin create(@NonNull HtmlConfigure configure) {
@ -65,7 +65,7 @@ public class HtmlPlugin extends AbstractMarkwonPlugin {
/**
* @param allowNonClosedTags whether or not non-closed tags should be closed
* at the document end. By default `false`
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public HtmlPlugin allowNonClosedTags(boolean allowNonClosedTags) {
@ -74,7 +74,7 @@ public class HtmlPlugin extends AbstractMarkwonPlugin {
}
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public HtmlPlugin addHandler(@NonNull TagHandler tagHandler) {
@ -83,7 +83,7 @@ public class HtmlPlugin extends AbstractMarkwonPlugin {
}
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@Nullable
public TagHandler getHandler(@NonNull String tagName) {
@ -96,7 +96,7 @@ public class HtmlPlugin extends AbstractMarkwonPlugin {
* {@link TagHandlerNoOp} to no-op certain default tags.
*
* @see TagHandlerNoOp
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public HtmlPlugin excludeDefaults(boolean excludeDefaults) {
@ -107,7 +107,7 @@ public class HtmlPlugin extends AbstractMarkwonPlugin {
@Override
public void configureConfiguration(@NonNull MarkwonConfiguration.Builder configurationBuilder) {
// @since 4.0.0-SNAPSHOT we init internal html-renderer here (marks the end of configuration)
// @since 4.0.0 we init internal html-renderer here (marks the end of configuration)
final MarkwonHtmlRendererImpl.Builder builder = this.builder;

View File

@ -15,7 +15,7 @@ public abstract class TagHandler {
);
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public abstract Collection<String> supportedTags();

View File

@ -9,7 +9,7 @@ import java.util.Collections;
import io.noties.markwon.MarkwonVisitor;
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
public class TagHandlerNoOp extends TagHandler {

View File

@ -30,7 +30,7 @@ import io.noties.markwon.image.DrawableUtils;
import io.noties.markwon.image.ImageSpanFactory;
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
public class GlideImagesPlugin extends AbstractMarkwonPlugin {

View File

@ -30,7 +30,7 @@ import io.noties.markwon.image.DrawableUtils;
import io.noties.markwon.image.ImageSpanFactory;
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
public class PicassoImagesPlugin extends AbstractMarkwonPlugin {

View File

@ -28,7 +28,7 @@ class AsyncDrawableLoaderBuilder {
AsyncDrawableLoaderBuilder() {
// @since 4.0.0-SNAPSHOT
// @since 4.0.0
// okay, let's add supported schemes at the start, this would be : data-uri and default network
// we should not use file-scheme as it's a bit complicated to assume file usage (lack of permissions)
addSchemeHandler(DataUriSchemeHandler.create());

View File

@ -28,7 +28,7 @@ class AsyncDrawableLoaderImpl extends AsyncDrawableLoader {
private final Handler handler;
// @since 4.0.0-SNAPSHOT use a hash-map with a AsyncDrawable as key for multiple requests
// @since 4.0.0 use a hash-map with a AsyncDrawable as key for multiple requests
// for the same destination
private final Map<AsyncDrawable, Future<?>> requests = new HashMap<>(2);
@ -36,7 +36,7 @@ class AsyncDrawableLoaderImpl extends AsyncDrawableLoader {
this(builder, new Handler(Looper.getMainLooper()));
}
// @since 4.0.0-SNAPSHOT
// @since 4.0.0
@VisibleForTesting
AsyncDrawableLoaderImpl(@NonNull AsyncDrawableLoaderBuilder builder, @NonNull Handler handler) {
this.executorService = builder.executorService;
@ -144,7 +144,7 @@ class AsyncDrawableLoaderImpl extends AsyncDrawableLoader {
final Drawable out = drawable;
// @since 4.0.0-SNAPSHOT apply intrinsic bounds (but only if they are empty)
// @since 4.0.0 apply intrinsic bounds (but only if they are empty)
if (out != null) {
final Rect bounds = out.getBounds();
//noinspection ConstantConditions

View File

@ -17,7 +17,7 @@ public abstract class ImageItem {
*
* @see #withDecodingNeeded(String, InputStream)
* @see WithResult
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public static ImageItem withResult(@NonNull Drawable drawable) {
@ -29,7 +29,7 @@ public abstract class ImageItem {
*
* @see #withResult(Drawable)
* @see WithDecodingNeeded
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public static ImageItem withDecodingNeeded(
@ -43,31 +43,31 @@ public abstract class ImageItem {
}
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
public abstract boolean hasResult();
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
public abstract boolean hasDecodingNeeded();
/**
* @see #hasResult()
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public abstract WithResult getAsWithResult();
/**
* @see #hasDecodingNeeded()
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public abstract WithDecodingNeeded getAsWithDecodingNeeded();
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
public static class WithResult extends ImageItem {
@ -106,7 +106,7 @@ public abstract class ImageItem {
}
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
public static class WithDecodingNeeded extends ImageItem {

View File

@ -27,14 +27,14 @@ import io.noties.markwon.image.svg.SvgMediaDecoder;
public class ImagesPlugin extends AbstractMarkwonPlugin {
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
public interface ImagesConfigure {
void configureImages(@NonNull ImagesPlugin plugin);
}
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
public interface PlaceholderProvider {
@Nullable
@ -42,7 +42,7 @@ public class ImagesPlugin extends AbstractMarkwonPlugin {
}
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
public interface ErrorHandler {
@ -74,12 +74,12 @@ public class ImagesPlugin extends AbstractMarkwonPlugin {
private final AsyncDrawableLoaderBuilder builder;
// @since 4.0.0-SNAPSHOT
// @since 4.0.0
ImagesPlugin() {
this(new AsyncDrawableLoaderBuilder());
}
// @since 4.0.0-SNAPSHOT
// @since 4.0.0
@VisibleForTesting
ImagesPlugin(@NonNull AsyncDrawableLoaderBuilder builder) {
this.builder = builder;
@ -88,7 +88,7 @@ public class ImagesPlugin extends AbstractMarkwonPlugin {
/**
* Optional (by default new cached thread executor will be used)
*
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public ImagesPlugin executorService(@NonNull ExecutorService executorService) {
@ -102,7 +102,7 @@ public class ImagesPlugin extends AbstractMarkwonPlugin {
* @see FileSchemeHandler
* @see NetworkSchemeHandler
* @see OkHttpNetworkSchemeHandler
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public ImagesPlugin addSchemeHandler(@NonNull SchemeHandler schemeHandler) {
@ -114,7 +114,7 @@ public class ImagesPlugin extends AbstractMarkwonPlugin {
* @see DefaultMediaDecoder
* @see SvgMediaDecoder
* @see GifMediaDecoder
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public ImagesPlugin addMediaDecoder(@NonNull MediaDecoder mediaDecoder) {
@ -127,7 +127,7 @@ public class ImagesPlugin extends AbstractMarkwonPlugin {
* if you need to disable default-image-media-decoder specify here own no-op implementation or null.
*
* @see DefaultMediaDecoder
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public ImagesPlugin defaultMediaDecoder(@Nullable MediaDecoder mediaDecoder) {
@ -136,7 +136,7 @@ public class ImagesPlugin extends AbstractMarkwonPlugin {
}
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public ImagesPlugin removeSchemeHandler(@NonNull String scheme) {
@ -145,7 +145,7 @@ public class ImagesPlugin extends AbstractMarkwonPlugin {
}
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public ImagesPlugin removeMediaDecoder(@NonNull String contentType) {
@ -154,7 +154,7 @@ public class ImagesPlugin extends AbstractMarkwonPlugin {
}
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public ImagesPlugin placeholderProvider(@NonNull PlaceholderProvider placeholderProvider) {
@ -164,7 +164,7 @@ public class ImagesPlugin extends AbstractMarkwonPlugin {
/**
* @see ErrorHandler
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public ImagesPlugin errorHandler(@NonNull ErrorHandler errorHandler) {

View File

@ -27,7 +27,7 @@ public abstract class MediaDecoder {
);
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public abstract Collection<String> supportedTypes();

View File

@ -24,7 +24,7 @@ public abstract class SchemeHandler {
public abstract ImageItem handle(@NonNull String raw, @NonNull Uri uri);
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public abstract Collection<String> supportedSchemes();

View File

@ -40,7 +40,7 @@ public class FileSchemeHandler extends SchemeHandler {
/**
* @see #createWithAssets(AssetManager)
* @see UrlProcessorAndroidAssets
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public static FileSchemeHandler createWithAssets(@NonNull Context context) {

View File

@ -26,7 +26,7 @@ public class GifMediaDecoder extends MediaDecoder {
/**
* Creates a {@link GifMediaDecoder} with {@code autoPlayGif = true}
*
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public static GifMediaDecoder create() {
@ -43,7 +43,7 @@ public class GifMediaDecoder extends MediaDecoder {
protected GifMediaDecoder(boolean autoPlayGif) {
this.autoPlayGif = autoPlayGif;
// @since 4.0.0-SNAPSHOT
// @since 4.0.0
validate();
}

View File

@ -1,7 +1,7 @@
package io.noties.markwon.image.gif;
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
public abstract class GifSupport {

View File

@ -16,7 +16,7 @@ import okhttp3.Response;
import okhttp3.ResponseBody;
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
public class OkHttpNetworkSchemeHandler extends SchemeHandler {
@ -35,7 +35,7 @@ public class OkHttpNetworkSchemeHandler extends SchemeHandler {
}
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public static OkHttpNetworkSchemeHandler create(@NonNull Call.Factory factory) {
@ -44,7 +44,7 @@ public class OkHttpNetworkSchemeHandler extends SchemeHandler {
private static final String HEADER_CONTENT_TYPE = "Content-Type";
// @since 4.0.0-SNAPSHOT, previously just OkHttpClient
// @since 4.0.0, previously just OkHttpClient
private final Call.Factory factory;
@SuppressWarnings("WeakerAccess")

View File

@ -27,7 +27,7 @@ public class SvgMediaDecoder extends MediaDecoder {
/**
* @see #create(Resources)
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
@NonNull
public static SvgMediaDecoder create() {
@ -45,7 +45,7 @@ public class SvgMediaDecoder extends MediaDecoder {
SvgMediaDecoder(Resources resources) {
this.resources = resources;
// @since 4.0.0-SNAPSHOT
// @since 4.0.0
validate();
}

View File

@ -1,7 +1,7 @@
package io.noties.markwon.image.svg;
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
public abstract class SvgSupport {

View File

@ -1,9 +1,2 @@
# Linkify
Use this module (or take a hint from it) if you would need _linkify_ capabilities. Do not
use `TextView.setAutolinkMask` (or specify `autolink` in XML) because it will remove all
existing links and keep only the ones it creates.
Please note that usage of this plugin introduces significant performance drop due to not
optimal implementation of underlying `android.text.util.Linkify`. If you have any ideas of how
to improve this - PRs are welcome!

View File

@ -9,7 +9,7 @@ import java.util.List;
import io.noties.markwon.SpanFactory;
// @since 4.0.0-SNAPSHOT
// @since 4.0.0
class SimpleExtBuilder {
private final List<DelimiterProcessor> extensions = new ArrayList<>(2);

View File

@ -9,7 +9,7 @@ import org.commonmark.parser.delimiter.DelimiterRun;
import io.noties.markwon.SpanFactory;
// @since 4.0.0-SNAPSHOT
// @since 4.0.0
class SimpleExtDelimiterProcessor implements DelimiterProcessor {
private final char open;

View File

@ -7,7 +7,7 @@ import org.commonmark.node.Visitor;
import io.noties.markwon.SpanFactory;
// @since 4.0.0-SNAPSHOT
// @since 4.0.0
class SimpleExtNode extends CustomNode {
private final SpanFactory spanFactory;

View File

@ -11,7 +11,7 @@ import io.noties.markwon.SpanFactory;
import io.noties.markwon.SpannableBuilder;
/**
* @since 4.0.0-SNAPSHOT
* @since 4.0.0
*/
public class SimpleExtPlugin extends AbstractMarkwonPlugin {

View File

@ -26,6 +26,7 @@
<activity android:name="io.noties.markwon.sample.theme.ThemeActivity" />
<activity android:name=".html.HtmlActivity" />
<activity android:name=".simpleext.SimpleExtActivity" />
<activity android:name=".customextension2.CustomExtensionActivity2" />
</application>

View File

@ -21,6 +21,7 @@ import io.noties.markwon.Markwon;
import io.noties.markwon.sample.basicplugins.BasicPluginsActivity;
import io.noties.markwon.sample.core.CoreActivity;
import io.noties.markwon.sample.customextension.CustomExtensionActivity;
import io.noties.markwon.sample.customextension2.CustomExtensionActivity2;
import io.noties.markwon.sample.html.HtmlActivity;
import io.noties.markwon.sample.latex.LatexActivity;
import io.noties.markwon.sample.recycler.RecyclerActivity;
@ -107,6 +108,10 @@ public class MainActivity extends Activity {
activity = SimpleExtActivity.class;
break;
case CUSTOM_EXTENSION_2:
activity = CustomExtensionActivity2.class;
break;
default:
throw new IllegalStateException("No Activity is associated with sample-item: " + item);
}

View File

@ -17,7 +17,9 @@ public enum Sample {
HTML(R.string.sample_html),
SIMPLE_EXT(R.string.sample_simple_ext);
SIMPLE_EXT(R.string.sample_simple_ext),
CUSTOM_EXTENSION_2(R.string.sample_custom_extension_2);
private final int textResId;

View File

@ -22,13 +22,6 @@ public class IconPlugin extends AbstractMarkwonPlugin {
this.iconSpanProvider = iconSpanProvider;
}
// @NonNull
// @Override
// public Priority priority() {
// // define images dependency
// return Priority.after(ImagesPlugin.class);
// }
@Override
public void configureParser(@NonNull Parser.Builder builder) {
builder.customDelimiterProcessor(IconProcessor.create());

View File

@ -0,0 +1,123 @@
package io.noties.markwon.sample.customextension2;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.commonmark.node.Link;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import io.noties.markwon.AbstractMarkwonPlugin;
import io.noties.markwon.Markwon;
import io.noties.markwon.MarkwonConfiguration;
import io.noties.markwon.MarkwonVisitor;
import io.noties.markwon.RenderProps;
import io.noties.markwon.SpannableBuilder;
import io.noties.markwon.core.CorePlugin;
import io.noties.markwon.core.CoreProps;
import io.noties.markwon.sample.R;
public class CustomExtensionActivity2 extends Activity {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_text_view);
final TextView textView = findViewById(R.id.text_view);
// let's look for github special links:
// * `#1` - an issue or a pull request
// * `@user` link to a user
final Markwon markwon = Markwon.builder(this)
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configure(@NonNull Registry registry) {
registry.require(CorePlugin.class, corePlugin ->
corePlugin.addOnTextAddedListener(new GithubLinkifyRegexTextAddedListener()));
}
})
.build();
final String md = "# Custom Extension 2\n" +
"\n" +
"This is an issue #1\n" +
"Done by @noties";
markwon.setMarkdown(textView, md);
}
private static class GithubLinkifyRegexTextAddedListener implements CorePlugin.OnTextAddedListener {
private static final Pattern PATTERN = Pattern.compile("((#\\d+)|(@\\w+))", Pattern.MULTILINE);
@Override
public void onTextAdded(@NonNull MarkwonVisitor visitor, @NonNull String text, int start) {
final Matcher matcher = PATTERN.matcher(text);
String value;
String url;
int index;
while (matcher.find()) {
value = matcher.group(1);
// detect which one it is
if ('#' == value.charAt(0)) {
url = createIssueOrPullRequestLink(value.substring(1));
} else {
url = createUserLink(value.substring(1));
}
// it's important to use `start` value (represents start-index of `text` in the visitor)
index = start + matcher.start();
setLink(visitor, url, index, index + value.length());
}
}
@NonNull
private String createIssueOrPullRequestLink(@NonNull String number) {
// issues and pull-requests on github follow the same pattern and we
// cannot know for sure which one it is, but if we use issues for all types,
// github will automatically redirect to pull-request if it's the one which is opened
return "https://github.com/noties/Markwon/issues/" + number;
}
@NonNull
private String createUserLink(@NonNull String user) {
return "https://github.com/" + user;
}
private void setLink(@NonNull MarkwonVisitor visitor, @NonNull String destination, int start, int end) {
// might a simpler one, but it doesn't respect possible changes to links
// visitor.builder().setSpan(
// new LinkSpan(visitor.configuration().theme(), destination, visitor.configuration().linkResolver()),
// start,
// end
// );
// use default handlers for links
final MarkwonConfiguration configuration = visitor.configuration();
final RenderProps renderProps = visitor.renderProps();
CoreProps.LINK_DESTINATION.set(renderProps, destination);
SpannableBuilder.setSpans(
visitor.builder(),
configuration.spansFactory().require(Link.class).getSpans(configuration, renderProps),
start,
end
);
}
}
}

View File

@ -20,4 +20,7 @@
<string name="sample_simple_ext"># \# SimpleExt\n\nShows how to use SimpleExtPlugin module
to create own delimited parser extensions</string>
<string name="sample_custom_extension_2"># \# Custom extension 2\n\nAutomatically
convert `#1` and `@user` to Github links</string>
</resources>