ImageDestinationProcessor (before UrlProcessor), limit usage to images only
This commit is contained in:
parent
21152f368f
commit
171b6d40a0
@ -9,6 +9,7 @@
|
|||||||
* Expose `enabledBlockTypes` in `CorePlugin`
|
* Expose `enabledBlockTypes` in `CorePlugin`
|
||||||
* Update `jlatexmath-android` dependency ([#225])
|
* Update `jlatexmath-android` dependency ([#225])
|
||||||
* Update `image-coil` module (Coil version `0.10.1`) ([#244])<br>Thanks to [@tylerbwong]
|
* Update `image-coil` module (Coil version `0.10.1`) ([#244])<br>Thanks to [@tylerbwong]
|
||||||
|
* Rename `UrlProcessor` to `ImageDestinationProcessor` (`io.noties.markwon.urlprocessor` -> `io.noties.markwon.image.destination`) and limit its usage to process **only** destination URL of images (was used to also process links before)
|
||||||
|
|
||||||
[#235]: https://github.com/noties/Markwon/issues/235
|
[#235]: https://github.com/noties/Markwon/issues/235
|
||||||
[#225]: https://github.com/noties/Markwon/issues/225
|
[#225]: https://github.com/noties/Markwon/issues/225
|
||||||
|
@ -5,15 +5,15 @@ import android.text.TextUtils;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import io.noties.markwon.urlprocessor.UrlProcessor;
|
import io.noties.markwon.image.destination.ImageDestinationProcessor;
|
||||||
import io.noties.markwon.urlprocessor.UrlProcessorRelativeToAbsolute;
|
import io.noties.markwon.image.destination.ImageDestinationProcessorRelativeToAbsolute;
|
||||||
|
|
||||||
class UrlProcessorInitialReadme implements UrlProcessor {
|
class ImageDestinationProcessorInitialReadme extends ImageDestinationProcessor {
|
||||||
|
|
||||||
private static final String GITHUB_BASE = "https://github.com/noties/Markwon/raw/master/";
|
private static final String GITHUB_BASE = "https://github.com/noties/Markwon/raw/master/";
|
||||||
|
|
||||||
private final UrlProcessorRelativeToAbsolute processor
|
private final ImageDestinationProcessorRelativeToAbsolute processor
|
||||||
= new UrlProcessorRelativeToAbsolute(GITHUB_BASE);
|
= new ImageDestinationProcessorRelativeToAbsolute(GITHUB_BASE);
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
@ -24,6 +24,8 @@ import io.noties.markwon.ext.tables.TablePlugin;
|
|||||||
import io.noties.markwon.ext.tasklist.TaskListPlugin;
|
import io.noties.markwon.ext.tasklist.TaskListPlugin;
|
||||||
import io.noties.markwon.html.HtmlPlugin;
|
import io.noties.markwon.html.HtmlPlugin;
|
||||||
import io.noties.markwon.image.ImagesPlugin;
|
import io.noties.markwon.image.ImagesPlugin;
|
||||||
|
import io.noties.markwon.image.destination.ImageDestinationProcessor;
|
||||||
|
import io.noties.markwon.image.destination.ImageDestinationProcessorRelativeToAbsolute;
|
||||||
import io.noties.markwon.image.file.FileSchemeHandler;
|
import io.noties.markwon.image.file.FileSchemeHandler;
|
||||||
import io.noties.markwon.image.gif.GifMediaDecoder;
|
import io.noties.markwon.image.gif.GifMediaDecoder;
|
||||||
import io.noties.markwon.image.network.OkHttpNetworkSchemeHandler;
|
import io.noties.markwon.image.network.OkHttpNetworkSchemeHandler;
|
||||||
@ -31,8 +33,6 @@ import io.noties.markwon.syntax.Prism4jTheme;
|
|||||||
import io.noties.markwon.syntax.Prism4jThemeDarkula;
|
import io.noties.markwon.syntax.Prism4jThemeDarkula;
|
||||||
import io.noties.markwon.syntax.Prism4jThemeDefault;
|
import io.noties.markwon.syntax.Prism4jThemeDefault;
|
||||||
import io.noties.markwon.syntax.SyntaxHighlightPlugin;
|
import io.noties.markwon.syntax.SyntaxHighlightPlugin;
|
||||||
import io.noties.markwon.urlprocessor.UrlProcessor;
|
|
||||||
import io.noties.markwon.urlprocessor.UrlProcessorRelativeToAbsolute;
|
|
||||||
import io.noties.prism4j.Prism4j;
|
import io.noties.prism4j.Prism4j;
|
||||||
|
|
||||||
@ActivityScope
|
@ActivityScope
|
||||||
@ -86,11 +86,11 @@ public class MarkdownRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void execute() {
|
private void execute() {
|
||||||
final UrlProcessor urlProcessor;
|
final ImageDestinationProcessor imageDestinationProcessor;
|
||||||
if (uri == null) {
|
if (uri == null) {
|
||||||
urlProcessor = new UrlProcessorInitialReadme();
|
imageDestinationProcessor = new ImageDestinationProcessorInitialReadme();
|
||||||
} else {
|
} else {
|
||||||
urlProcessor = new UrlProcessorRelativeToAbsolute(uri.toString());
|
imageDestinationProcessor = new ImageDestinationProcessorRelativeToAbsolute(uri.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
final Prism4jTheme prism4jTheme = isLightTheme
|
final Prism4jTheme prism4jTheme = isLightTheme
|
||||||
@ -119,7 +119,7 @@ public class MarkdownRenderer {
|
|||||||
.usePlugin(new AbstractMarkwonPlugin() {
|
.usePlugin(new AbstractMarkwonPlugin() {
|
||||||
@Override
|
@Override
|
||||||
public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
|
public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
|
||||||
builder.urlProcessor(urlProcessor);
|
builder.imageDestinationProcessor(imageDestinationProcessor);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.build();
|
.build();
|
||||||
|
@ -5,7 +5,7 @@ These are _configurable_ properties:
|
|||||||
* `AsyncDrawableLoader` (back here since <Badge text="4.0.0" />)
|
* `AsyncDrawableLoader` (back here since <Badge text="4.0.0" />)
|
||||||
* `SyntaxHighlight`
|
* `SyntaxHighlight`
|
||||||
* `LinkResolver` (since <Badge text="4.0.0" />, before — `LinkSpan.Resolver`)
|
* `LinkResolver` (since <Badge text="4.0.0" />, before — `LinkSpan.Resolver`)
|
||||||
* `UrlProcessor`
|
* `ImageDestinationProcessor` (since <Badge text="4.4.0" />, before — `UrlProcessor`)
|
||||||
* `ImageSizeResolver`
|
* `ImageSizeResolver`
|
||||||
|
|
||||||
:::tip
|
:::tip
|
||||||
@ -36,10 +36,11 @@ final Markwon markwon = Markwon.builder(context)
|
|||||||
.build();
|
.build();
|
||||||
```
|
```
|
||||||
|
|
||||||
Currently `Markwon` provides 3 implementations for loading images:
|
Currently `Markwon` provides 4 implementations for loading images:
|
||||||
* [markwon implementation](/docs/v4/image/) with SVG, GIF, data uri and android_assets support
|
* [markwon implementation](/docs/v4/image/) with SVG, GIF, data uri and android_assets support
|
||||||
* [based on Picasso](/docs/v4/image-picasso/)
|
* [based on Picasso](/docs/v4/image-picasso/)
|
||||||
* [based on Glide](/docs/v4/image-glide/)
|
* [based on Glide](/docs/v4/image-glide/)
|
||||||
|
* [base on Coil](/docs/v4/image-coil/)
|
||||||
|
|
||||||
## SyntaxHighlight
|
## SyntaxHighlight
|
||||||
|
|
||||||
@ -87,32 +88,32 @@ if there is none registered. if you wish to register own instance of a `Movement
|
|||||||
apply it directly to a TextView or use [MovementMethodPlugin](/docs/v4/core/movement-method-plugin.md)
|
apply it directly to a TextView or use [MovementMethodPlugin](/docs/v4/core/movement-method-plugin.md)
|
||||||
:::
|
:::
|
||||||
|
|
||||||
## UrlProcessor
|
## ImageDestinationProcessor
|
||||||
|
|
||||||
Process URLs in your markdown (for links and images). If not provided explicitly,
|
Process destinations (URLs) of images in your markdown. If not provided explicitly,
|
||||||
default **no-op** implementation will be used, which does not modify URLs (keeping them as-is).
|
default **no-op** implementation will be used, which does not modify URLs (keeping them as-is).
|
||||||
|
|
||||||
`Markwon` provides 2 implementations of `UrlProcessor`:
|
`Markwon` provides 2 implementations of `UrlProcessor`:
|
||||||
* `UrlProcessorRelativeToAbsolute`
|
* `ImageDestinationProcessorRelativeToAbsolute`
|
||||||
* `UrlProcessorAndroidAssets`
|
* `ImageDestinationProcessorAssets`
|
||||||
|
|
||||||
### UrlProcessorRelativeToAbsolute
|
### ImageDestinationProcessorRelativeToAbsolute
|
||||||
|
|
||||||
`UrlProcessorRelativeToAbsolute` can be used to make relative URL absolute. For example if an image is
|
`ImageDestinationProcessorRelativeToAbsolute` can be used to make relative URL absolute. For example if an image is
|
||||||
defined like this: `` and `UrlProcessorRelativeToAbsolute`
|
defined like this: `` and `ImageDestinationProcessorRelativeToAbsolute`
|
||||||
is created with `https://github.com/noties/Markwon/raw/master/` as the base:
|
is created with `https://github.com/noties/Markwon/raw/master/` as the base:
|
||||||
`new UrlProcessorRelativeToAbsolute("https://github.com/noties/Markwon/raw/master/")`,
|
`new ImageDestinationProcessorRelativeToAbsolute("https://github.com/noties/Markwon/raw/master/")`,
|
||||||
then final image will have `https://github.com/noties/Markwon/raw/master/art/image.JPG`
|
then final image will have `https://github.com/noties/Markwon/raw/master/art/image.JPG`
|
||||||
as the destination.
|
as the destination.
|
||||||
|
|
||||||
### UrlProcessorAndroidAssets
|
### ImageDestinationProcessorAssets
|
||||||
|
|
||||||
`UrlProcessorAndroidAssets` can be used to make processed links to point to Android assets folder.
|
`ImageDestinationProcessorAssets` can be used to make processed destinations to point to Android assets folder.
|
||||||
So an image: `` will have `file:///android_asset/art/image.JPG` as the
|
So an image: `` will have `file:///android_asset/art/image.JPG` as the
|
||||||
destination.
|
destination.
|
||||||
|
|
||||||
:::tip
|
:::tip
|
||||||
Please note that `UrlProcessorAndroidAssets` will process only URLs that have no `scheme` information,
|
Please note that `ImageDestinationProcessorAssets` will process only URLs that have no `scheme` information,
|
||||||
so a `./art/image.png` will become `file:///android_asset/art/image.JPG` whilst `https://so.me/where.png`
|
so a `./art/image.png` will become `file:///android_asset/art/image.JPG` whilst `https://so.me/where.png`
|
||||||
will be kept as-is.
|
will be kept as-is.
|
||||||
:::
|
:::
|
||||||
|
@ -6,15 +6,13 @@ import io.noties.markwon.core.MarkwonTheme;
|
|||||||
import io.noties.markwon.image.AsyncDrawableLoader;
|
import io.noties.markwon.image.AsyncDrawableLoader;
|
||||||
import io.noties.markwon.image.ImageSizeResolver;
|
import io.noties.markwon.image.ImageSizeResolver;
|
||||||
import io.noties.markwon.image.ImageSizeResolverDef;
|
import io.noties.markwon.image.ImageSizeResolverDef;
|
||||||
|
import io.noties.markwon.image.destination.ImageDestinationProcessor;
|
||||||
import io.noties.markwon.syntax.SyntaxHighlight;
|
import io.noties.markwon.syntax.SyntaxHighlight;
|
||||||
import io.noties.markwon.syntax.SyntaxHighlightNoOp;
|
import io.noties.markwon.syntax.SyntaxHighlightNoOp;
|
||||||
import io.noties.markwon.urlprocessor.UrlProcessor;
|
|
||||||
import io.noties.markwon.urlprocessor.UrlProcessorNoOp;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* since 3.0.0 renamed `SpannableConfiguration` -> `MarkwonConfiguration`
|
* since 3.0.0 renamed `SpannableConfiguration` -> `MarkwonConfiguration`
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("WeakerAccess")
|
|
||||||
public class MarkwonConfiguration {
|
public class MarkwonConfiguration {
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@ -26,7 +24,8 @@ public class MarkwonConfiguration {
|
|||||||
private final AsyncDrawableLoader asyncDrawableLoader;
|
private final AsyncDrawableLoader asyncDrawableLoader;
|
||||||
private final SyntaxHighlight syntaxHighlight;
|
private final SyntaxHighlight syntaxHighlight;
|
||||||
private final LinkResolver linkResolver;
|
private final LinkResolver linkResolver;
|
||||||
private final UrlProcessor urlProcessor;
|
// @since $nap;
|
||||||
|
private final ImageDestinationProcessor imageDestinationProcessor;
|
||||||
private final ImageSizeResolver imageSizeResolver;
|
private final ImageSizeResolver imageSizeResolver;
|
||||||
|
|
||||||
// @since 3.0.0
|
// @since 3.0.0
|
||||||
@ -37,7 +36,7 @@ public class MarkwonConfiguration {
|
|||||||
this.asyncDrawableLoader = builder.asyncDrawableLoader;
|
this.asyncDrawableLoader = builder.asyncDrawableLoader;
|
||||||
this.syntaxHighlight = builder.syntaxHighlight;
|
this.syntaxHighlight = builder.syntaxHighlight;
|
||||||
this.linkResolver = builder.linkResolver;
|
this.linkResolver = builder.linkResolver;
|
||||||
this.urlProcessor = builder.urlProcessor;
|
this.imageDestinationProcessor = builder.imageDestinationProcessor;
|
||||||
this.imageSizeResolver = builder.imageSizeResolver;
|
this.imageSizeResolver = builder.imageSizeResolver;
|
||||||
this.spansFactory = builder.spansFactory;
|
this.spansFactory = builder.spansFactory;
|
||||||
}
|
}
|
||||||
@ -62,9 +61,12 @@ public class MarkwonConfiguration {
|
|||||||
return linkResolver;
|
return linkResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since $nap;
|
||||||
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public UrlProcessor urlProcessor() {
|
public ImageDestinationProcessor imageDestinationProcessor() {
|
||||||
return urlProcessor;
|
return imageDestinationProcessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@ -87,7 +89,8 @@ public class MarkwonConfiguration {
|
|||||||
private AsyncDrawableLoader asyncDrawableLoader;
|
private AsyncDrawableLoader asyncDrawableLoader;
|
||||||
private SyntaxHighlight syntaxHighlight;
|
private SyntaxHighlight syntaxHighlight;
|
||||||
private LinkResolver linkResolver;
|
private LinkResolver linkResolver;
|
||||||
private UrlProcessor urlProcessor;
|
// @since $nap;
|
||||||
|
private ImageDestinationProcessor imageDestinationProcessor;
|
||||||
private ImageSizeResolver imageSizeResolver;
|
private ImageSizeResolver imageSizeResolver;
|
||||||
private MarkwonSpansFactory spansFactory;
|
private MarkwonSpansFactory spansFactory;
|
||||||
|
|
||||||
@ -115,9 +118,12 @@ public class MarkwonConfiguration {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since $nap;
|
||||||
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public Builder urlProcessor(@NonNull UrlProcessor urlProcessor) {
|
public Builder imageDestinationProcessor(@NonNull ImageDestinationProcessor imageDestinationProcessor) {
|
||||||
this.urlProcessor = urlProcessor;
|
this.imageDestinationProcessor = imageDestinationProcessor;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,8 +157,9 @@ public class MarkwonConfiguration {
|
|||||||
linkResolver = new LinkResolverDef();
|
linkResolver = new LinkResolverDef();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urlProcessor == null) {
|
// @since $nap;
|
||||||
urlProcessor = new UrlProcessorNoOp();
|
if (imageDestinationProcessor == null) {
|
||||||
|
imageDestinationProcessor = ImageDestinationProcessor.noOp();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (imageSizeResolver == null) {
|
if (imageSizeResolver == null) {
|
||||||
|
@ -320,7 +320,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
|
|||||||
final boolean link = parent instanceof Link;
|
final boolean link = parent instanceof Link;
|
||||||
|
|
||||||
final String destination = configuration
|
final String destination = configuration
|
||||||
.urlProcessor()
|
.imageDestinationProcessor()
|
||||||
.process(image.getDestination());
|
.process(image.getDestination());
|
||||||
|
|
||||||
final RenderProps props = visitor.renderProps();
|
final RenderProps props = visitor.renderProps();
|
||||||
@ -524,8 +524,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
|
|||||||
final int length = visitor.length();
|
final int length = visitor.length();
|
||||||
visitor.visitChildren(link);
|
visitor.visitChildren(link);
|
||||||
|
|
||||||
final MarkwonConfiguration configuration = visitor.configuration();
|
final String destination = link.getDestination();
|
||||||
final String destination = configuration.urlProcessor().process(link.getDestination());
|
|
||||||
|
|
||||||
CoreProps.LINK_DESTINATION.set(visitor.renderProps(), destination);
|
CoreProps.LINK_DESTINATION.set(visitor.renderProps(), destination);
|
||||||
|
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
package io.noties.markwon.image.destination;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process destination of image nodes
|
||||||
|
*
|
||||||
|
* @since $nap;
|
||||||
|
*/
|
||||||
|
public abstract class ImageDestinationProcessor {
|
||||||
|
@NonNull
|
||||||
|
public abstract String process(@NonNull String destination);
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static ImageDestinationProcessor noOp() {
|
||||||
|
return new NoOp();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class NoOp extends ImageDestinationProcessor {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String process(@NonNull String destination) {
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
package io.noties.markwon.image.destination;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link ImageDestinationProcessor} that treats all destinations <strong>without scheme</strong>
|
||||||
|
* information as pointing to the {@code assets} folder of an application. Please note that this
|
||||||
|
* processor only adds required {@code file:///android_asset/} prefix to destinations and
|
||||||
|
* actual image loading must take that into account (implement this functionality).
|
||||||
|
* <p>
|
||||||
|
* {@code FileSchemeHandler} from the {@code image} module supports asset images when created with
|
||||||
|
* {@code createWithAssets} factory method
|
||||||
|
*
|
||||||
|
* @since $nap;
|
||||||
|
*/
|
||||||
|
public class ImageDestinationProcessorAssets extends ImageDestinationProcessor {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static ImageDestinationProcessorAssets create(@Nullable ImageDestinationProcessor parent) {
|
||||||
|
return new ImageDestinationProcessorAssets(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
static final String MOCK = "https://android.asset/";
|
||||||
|
static final String BASE = "file:///android_asset/";
|
||||||
|
|
||||||
|
private final ImageDestinationProcessorRelativeToAbsolute assetsProcessor
|
||||||
|
= new ImageDestinationProcessorRelativeToAbsolute(MOCK);
|
||||||
|
|
||||||
|
private final ImageDestinationProcessor processor;
|
||||||
|
|
||||||
|
public ImageDestinationProcessorAssets() {
|
||||||
|
this(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImageDestinationProcessorAssets(@Nullable ImageDestinationProcessor parent) {
|
||||||
|
this.processor = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String process(@NonNull String destination) {
|
||||||
|
final String out;
|
||||||
|
final Uri uri = Uri.parse(destination);
|
||||||
|
if (TextUtils.isEmpty(uri.getScheme())) {
|
||||||
|
out = assetsProcessor.process(destination).replace(MOCK, BASE);
|
||||||
|
} else {
|
||||||
|
if (processor != null) {
|
||||||
|
out = processor.process(destination);
|
||||||
|
} else {
|
||||||
|
out = destination;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package io.noties.markwon.urlprocessor;
|
package io.noties.markwon.image.destination;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@ -6,15 +6,30 @@ import androidx.annotation.Nullable;
|
|||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
/**
|
||||||
public class UrlProcessorRelativeToAbsolute implements UrlProcessor {
|
* @since $nap;
|
||||||
|
*/
|
||||||
|
public class ImageDestinationProcessorRelativeToAbsolute extends ImageDestinationProcessor {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static ImageDestinationProcessorRelativeToAbsolute create(@NonNull String base) {
|
||||||
|
return new ImageDestinationProcessorRelativeToAbsolute(base);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ImageDestinationProcessorRelativeToAbsolute create(@NonNull URL base) {
|
||||||
|
return new ImageDestinationProcessorRelativeToAbsolute(base);
|
||||||
|
}
|
||||||
|
|
||||||
private final URL base;
|
private final URL base;
|
||||||
|
|
||||||
public UrlProcessorRelativeToAbsolute(@NonNull String base) {
|
public ImageDestinationProcessorRelativeToAbsolute(@NonNull String base) {
|
||||||
this.base = obtain(base);
|
this.base = obtain(base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ImageDestinationProcessorRelativeToAbsolute(@NonNull URL base) {
|
||||||
|
this.base = base;
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public String process(@NonNull String destination) {
|
public String process(@NonNull String destination) {
|
@ -1,8 +0,0 @@
|
|||||||
package io.noties.markwon.urlprocessor;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
public interface UrlProcessor {
|
|
||||||
@NonNull
|
|
||||||
String process(@NonNull String destination);
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
package io.noties.markwon.urlprocessor;
|
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Processor that will <em>assume</em> that an URL without scheme points to android assets folder.
|
|
||||||
* URL with a scheme will be processed by {@link #processor} (if it is specified) or returned `as-is`.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings({"unused", "WeakerAccess"})
|
|
||||||
public class UrlProcessorAndroidAssets implements UrlProcessor {
|
|
||||||
|
|
||||||
|
|
||||||
static final String MOCK = "https://android.asset/";
|
|
||||||
static final String BASE = "file:///android_asset/";
|
|
||||||
|
|
||||||
private final UrlProcessorRelativeToAbsolute assetsProcessor
|
|
||||||
= new UrlProcessorRelativeToAbsolute(MOCK);
|
|
||||||
|
|
||||||
private final UrlProcessor processor;
|
|
||||||
|
|
||||||
public UrlProcessorAndroidAssets() {
|
|
||||||
this(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UrlProcessorAndroidAssets(@Nullable UrlProcessor parent) {
|
|
||||||
this.processor = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public String process(@NonNull String destination) {
|
|
||||||
final String out;
|
|
||||||
final Uri uri = Uri.parse(destination);
|
|
||||||
if (TextUtils.isEmpty(uri.getScheme())) {
|
|
||||||
out = assetsProcessor.process(destination).replace(MOCK, BASE);
|
|
||||||
} else {
|
|
||||||
if (processor != null) {
|
|
||||||
out = processor.process(destination);
|
|
||||||
} else {
|
|
||||||
out = destination;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
package io.noties.markwon.urlprocessor;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
public class UrlProcessorNoOp implements UrlProcessor {
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public String process(@NonNull String destination) {
|
|
||||||
return destination;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
package io.noties.markwon.urlprocessor;
|
package io.noties.markwon.image.destination;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@ -6,18 +6,18 @@ import org.junit.runner.RunWith;
|
|||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.RobolectricTestRunner;
|
||||||
import org.robolectric.annotation.Config;
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
|
import static io.noties.markwon.image.destination.ImageDestinationProcessorAssets.BASE;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static io.noties.markwon.urlprocessor.UrlProcessorAndroidAssets.BASE;
|
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(RobolectricTestRunner.class)
|
||||||
@Config(manifest = Config.NONE)
|
@Config(manifest = Config.NONE)
|
||||||
public class UrlProcessorAndroidAssetsTest {
|
public class ImageDestinationProcessorAssetsTest {
|
||||||
|
|
||||||
private UrlProcessorAndroidAssets processor;
|
private ImageDestinationProcessorAssets processor;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void before() {
|
public void before() {
|
||||||
processor = new UrlProcessorAndroidAssets();
|
processor = new ImageDestinationProcessorAssets();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
@ -1,4 +1,4 @@
|
|||||||
package io.noties.markwon.urlprocessor;
|
package io.noties.markwon.image.destination;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@ -9,39 +9,39 @@ import static org.junit.Assert.*;
|
|||||||
|
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(RobolectricTestRunner.class)
|
||||||
@Config(manifest = Config.NONE)
|
@Config(manifest = Config.NONE)
|
||||||
public class UrlProcessorRelativeToAbsoluteTest {
|
public class ImageDestinationProcessorRelativeToAbsoluteTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void malformed_base_do_not_process() {
|
public void malformed_base_do_not_process() {
|
||||||
final UrlProcessorRelativeToAbsolute processor = new UrlProcessorRelativeToAbsolute("!@#$%^&*(");
|
final ImageDestinationProcessorRelativeToAbsolute processor = new ImageDestinationProcessorRelativeToAbsolute("!@#$%^&*(");
|
||||||
final String destination = "../hey.there.html";
|
final String destination = "../hey.there.html";
|
||||||
assertEquals(destination, processor.process(destination));
|
assertEquals(destination, processor.process(destination));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void access_root() {
|
public void access_root() {
|
||||||
final UrlProcessorRelativeToAbsolute processor = new UrlProcessorRelativeToAbsolute("https://ro.ot/hello/");
|
final ImageDestinationProcessorRelativeToAbsolute processor = new ImageDestinationProcessorRelativeToAbsolute("https://ro.ot/hello/");
|
||||||
final String url = "/index.html";
|
final String url = "/index.html";
|
||||||
assertEquals("https://ro.ot/index.html", processor.process(url));
|
assertEquals("https://ro.ot/index.html", processor.process(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void access_same_directory() {
|
public void access_same_directory() {
|
||||||
final UrlProcessorRelativeToAbsolute processor = new UrlProcessorRelativeToAbsolute("https://ro.ot/hello/");
|
final ImageDestinationProcessorRelativeToAbsolute processor = new ImageDestinationProcessorRelativeToAbsolute("https://ro.ot/hello/");
|
||||||
final String url = "./.htaccess";
|
final String url = "./.htaccess";
|
||||||
assertEquals("https://ro.ot/hello/.htaccess", processor.process(url));
|
assertEquals("https://ro.ot/hello/.htaccess", processor.process(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void asset_directory_up() {
|
public void asset_directory_up() {
|
||||||
final UrlProcessorRelativeToAbsolute processor = new UrlProcessorRelativeToAbsolute("http://ro.ot/first/second/");
|
final ImageDestinationProcessorRelativeToAbsolute processor = new ImageDestinationProcessorRelativeToAbsolute("http://ro.ot/first/second/");
|
||||||
final String url = "../cat.JPG";
|
final String url = "../cat.JPG";
|
||||||
assertEquals("http://ro.ot/first/cat.JPG", processor.process(url));
|
assertEquals("http://ro.ot/first/cat.JPG", processor.process(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void change_directory_inside_destination() {
|
public void change_directory_inside_destination() {
|
||||||
final UrlProcessorRelativeToAbsolute processor = new UrlProcessorRelativeToAbsolute("http://ro.ot/first/");
|
final ImageDestinationProcessorRelativeToAbsolute processor = new ImageDestinationProcessorRelativeToAbsolute("http://ro.ot/first/");
|
||||||
final String url = "../first/../second/./thi.rd";
|
final String url = "../first/../second/./thi.rd";
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"http://ro.ot/second/thi.rd",
|
"http://ro.ot/second/thi.rd",
|
||||||
@ -51,7 +51,7 @@ public class UrlProcessorRelativeToAbsoluteTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void with_query_arguments() {
|
public void with_query_arguments() {
|
||||||
final UrlProcessorRelativeToAbsolute processor = new UrlProcessorRelativeToAbsolute("http://ro.ot/first/");
|
final ImageDestinationProcessorRelativeToAbsolute processor = new ImageDestinationProcessorRelativeToAbsolute("http://ro.ot/first/");
|
||||||
final String url = "../index.php?ROOT=1";
|
final String url = "../index.php?ROOT=1";
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"http://ro.ot/index.php?ROOT=1",
|
"http://ro.ot/index.php?ROOT=1",
|
@ -62,7 +62,7 @@ public class ImageHandler extends SimpleTagHandler {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String destination = configuration.urlProcessor().process(src);
|
final String destination = configuration.imageDestinationProcessor().process(src);
|
||||||
final ImageSize imageSize = imageSizeParser.parse(tag.attributes());
|
final ImageSize imageSize = imageSizeParser.parse(tag.attributes());
|
||||||
|
|
||||||
// todo: replacement text is link... as we are not at block level
|
// todo: replacement text is link... as we are not at block level
|
||||||
|
@ -27,7 +27,8 @@ public class LinkHandler extends SimpleTagHandler {
|
|||||||
|
|
||||||
CoreProps.LINK_DESTINATION.set(
|
CoreProps.LINK_DESTINATION.set(
|
||||||
renderProps,
|
renderProps,
|
||||||
configuration.urlProcessor().process(destination));
|
destination
|
||||||
|
);
|
||||||
|
|
||||||
return spanFactory.getSpans(configuration, renderProps);
|
return spanFactory.getSpans(configuration, renderProps);
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,12 @@ package io.noties.markwon.image.file;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.AssetManager;
|
import android.content.res.AssetManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.webkit.MimeTypeMap;
|
import android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
@ -18,7 +19,6 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import io.noties.markwon.urlprocessor.UrlProcessorAndroidAssets;
|
|
||||||
import io.noties.markwon.image.ImageItem;
|
import io.noties.markwon.image.ImageItem;
|
||||||
import io.noties.markwon.image.SchemeHandler;
|
import io.noties.markwon.image.SchemeHandler;
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ public class FileSchemeHandler extends SchemeHandler {
|
|||||||
public static final String SCHEME = "file";
|
public static final String SCHEME = "file";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see UrlProcessorAndroidAssets
|
* @see io.noties.markwon.image.destination.ImageDestinationProcessorAssets
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public static FileSchemeHandler createWithAssets(@NonNull AssetManager assetManager) {
|
public static FileSchemeHandler createWithAssets(@NonNull AssetManager assetManager) {
|
||||||
@ -39,7 +39,7 @@ public class FileSchemeHandler extends SchemeHandler {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @see #createWithAssets(AssetManager)
|
* @see #createWithAssets(AssetManager)
|
||||||
* @see UrlProcessorAndroidAssets
|
* @see io.noties.markwon.image.destination.ImageDestinationProcessorAssets
|
||||||
* @since 4.0.0
|
* @since 4.0.0
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
|
@ -5,6 +5,7 @@ import android.net.Uri;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.style.ForegroundColorSpan;
|
import android.text.style.ForegroundColorSpan;
|
||||||
|
import android.view.View;
|
||||||
import android.widget.ScrollView;
|
import android.widget.ScrollView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
@ -20,6 +21,7 @@ import java.util.Collections;
|
|||||||
|
|
||||||
import io.noties.markwon.AbstractMarkwonPlugin;
|
import io.noties.markwon.AbstractMarkwonPlugin;
|
||||||
import io.noties.markwon.BlockHandlerDef;
|
import io.noties.markwon.BlockHandlerDef;
|
||||||
|
import io.noties.markwon.LinkResolverDef;
|
||||||
import io.noties.markwon.Markwon;
|
import io.noties.markwon.Markwon;
|
||||||
import io.noties.markwon.MarkwonConfiguration;
|
import io.noties.markwon.MarkwonConfiguration;
|
||||||
import io.noties.markwon.MarkwonSpansFactory;
|
import io.noties.markwon.MarkwonSpansFactory;
|
||||||
@ -153,7 +155,7 @@ public class BasicPluginsActivity extends ActivityWithMenuOptions {
|
|||||||
* <ul>
|
* <ul>
|
||||||
* <li>SyntaxHighlight</li>
|
* <li>SyntaxHighlight</li>
|
||||||
* <li>LinkSpan.Resolver</li>
|
* <li>LinkSpan.Resolver</li>
|
||||||
* <li>UrlProcessor</li>
|
* <li>ImageDestinationProcessor</li>
|
||||||
* <li>ImageSizeResolver</li>
|
* <li>ImageSizeResolver</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* <p>
|
* <p>
|
||||||
@ -173,12 +175,18 @@ public class BasicPluginsActivity extends ActivityWithMenuOptions {
|
|||||||
public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
|
public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
|
||||||
// for example if specified destination has no scheme info, we will
|
// for example if specified destination has no scheme info, we will
|
||||||
// _assume_ that it's network request and append HTTPS scheme
|
// _assume_ that it's network request and append HTTPS scheme
|
||||||
builder.urlProcessor(destination -> {
|
builder.linkResolver(new LinkResolverDef() {
|
||||||
final Uri uri = Uri.parse(destination);
|
@Override
|
||||||
if (TextUtils.isEmpty(uri.getScheme())) {
|
public void resolve(@NonNull View view, @NonNull String link) {
|
||||||
return "https://" + destination;
|
final String destination;
|
||||||
|
final Uri uri = Uri.parse(link);
|
||||||
|
if (TextUtils.isEmpty(uri.getScheme())) {
|
||||||
|
destination = "https://" + link;
|
||||||
|
} else {
|
||||||
|
destination = link;
|
||||||
|
}
|
||||||
|
super.resolve(view, destination);
|
||||||
}
|
}
|
||||||
return destination;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -34,6 +34,8 @@ import io.noties.markwon.MarkwonVisitor;
|
|||||||
import io.noties.markwon.core.CorePlugin;
|
import io.noties.markwon.core.CorePlugin;
|
||||||
import io.noties.markwon.html.HtmlPlugin;
|
import io.noties.markwon.html.HtmlPlugin;
|
||||||
import io.noties.markwon.image.ImagesPlugin;
|
import io.noties.markwon.image.ImagesPlugin;
|
||||||
|
import io.noties.markwon.image.destination.ImageDestinationProcessor;
|
||||||
|
import io.noties.markwon.image.destination.ImageDestinationProcessorRelativeToAbsolute;
|
||||||
import io.noties.markwon.image.file.FileSchemeHandler;
|
import io.noties.markwon.image.file.FileSchemeHandler;
|
||||||
import io.noties.markwon.image.network.OkHttpNetworkSchemeHandler;
|
import io.noties.markwon.image.network.OkHttpNetworkSchemeHandler;
|
||||||
import io.noties.markwon.image.svg.SvgMediaDecoder;
|
import io.noties.markwon.image.svg.SvgMediaDecoder;
|
||||||
@ -42,8 +44,6 @@ import io.noties.markwon.recycler.SimpleEntry;
|
|||||||
import io.noties.markwon.recycler.table.TableEntry;
|
import io.noties.markwon.recycler.table.TableEntry;
|
||||||
import io.noties.markwon.recycler.table.TableEntryPlugin;
|
import io.noties.markwon.recycler.table.TableEntryPlugin;
|
||||||
import io.noties.markwon.sample.R;
|
import io.noties.markwon.sample.R;
|
||||||
import io.noties.markwon.urlprocessor.UrlProcessor;
|
|
||||||
import io.noties.markwon.urlprocessor.UrlProcessorRelativeToAbsolute;
|
|
||||||
|
|
||||||
public class RecyclerActivity extends Activity {
|
public class RecyclerActivity extends Activity {
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ public class RecyclerActivity extends Activity {
|
|||||||
.usePlugin(new AbstractMarkwonPlugin() {
|
.usePlugin(new AbstractMarkwonPlugin() {
|
||||||
@Override
|
@Override
|
||||||
public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
|
public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
|
||||||
builder.urlProcessor(new UrlProcessorInitialReadme());
|
builder.imageDestinationProcessor(new ImageDestinationProcessorInitialReadme());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -182,12 +182,12 @@ public class RecyclerActivity extends Activity {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class UrlProcessorInitialReadme implements UrlProcessor {
|
private static class ImageDestinationProcessorInitialReadme extends ImageDestinationProcessor {
|
||||||
|
|
||||||
private static final String GITHUB_BASE = "https://github.com/noties/Markwon/raw/master/";
|
private static final String GITHUB_BASE = "https://github.com/noties/Markwon/raw/master/";
|
||||||
|
|
||||||
private final UrlProcessorRelativeToAbsolute processor
|
private final ImageDestinationProcessorRelativeToAbsolute processor
|
||||||
= new UrlProcessorRelativeToAbsolute(GITHUB_BASE);
|
= new ImageDestinationProcessorRelativeToAbsolute(GITHUB_BASE);
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
|
Loading…
x
Reference in New Issue
Block a user