diff --git a/library/src/main/java/ru/noties/markwon/ImageClickResolver.java b/library/src/main/java/ru/noties/markwon/ImageClickResolver.java new file mode 100644 index 00000000..202a31a3 --- /dev/null +++ b/library/src/main/java/ru/noties/markwon/ImageClickResolver.java @@ -0,0 +1,13 @@ +package ru.noties.markwon; + +import android.support.annotation.NonNull; +import android.view.View; + +/** + * @author pa.gulko zTrap (25.10.2017) + * @since 1.0.1 + */ +public interface ImageClickResolver { + + void resolve(View view, @NonNull String link); +} diff --git a/library/src/main/java/ru/noties/markwon/ImageClickResolverDef.java b/library/src/main/java/ru/noties/markwon/ImageClickResolverDef.java new file mode 100644 index 00000000..8d8a73aa --- /dev/null +++ b/library/src/main/java/ru/noties/markwon/ImageClickResolverDef.java @@ -0,0 +1,30 @@ +package ru.noties.markwon; + +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.provider.Browser; +import android.support.annotation.NonNull; +import android.util.Log; +import android.view.View; + +/** + * @author pa.gulko zTrap (25.10.2017) + * @since 1.0.1 + */ +public class ImageClickResolverDef implements ImageClickResolver { + + @Override + public void resolve(View view, @NonNull String link) { + final Uri uri = Uri.parse(link); + final Context context = view.getContext(); + final Intent intent = new Intent(Intent.ACTION_VIEW, uri); + intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()); + try { + context.startActivity(intent); + } catch (ActivityNotFoundException e) { + Log.w("LinkResolverDef", "Actvity was not found for intent, " + intent.toString()); + } + } +} diff --git a/library/src/main/java/ru/noties/markwon/SpannableConfiguration.java b/library/src/main/java/ru/noties/markwon/SpannableConfiguration.java index aa9b6f15..25f8c5d2 100644 --- a/library/src/main/java/ru/noties/markwon/SpannableConfiguration.java +++ b/library/src/main/java/ru/noties/markwon/SpannableConfiguration.java @@ -26,6 +26,7 @@ public class SpannableConfiguration { private final LinkSpan.Resolver linkResolver; private final UrlProcessor urlProcessor; private final SpannableHtmlParser htmlParser; + private final ImageClickResolver imageClickResolver; private SpannableConfiguration(Builder builder) { this.theme = builder.theme; @@ -34,6 +35,7 @@ public class SpannableConfiguration { this.linkResolver = builder.linkResolver; this.urlProcessor = builder.urlProcessor; this.htmlParser = builder.htmlParser; + this.imageClickResolver = builder.imageClickResolver; } public SpannableTheme theme() { @@ -60,6 +62,10 @@ public class SpannableConfiguration { return htmlParser; } + public ImageClickResolver imageClickResolver() { + return imageClickResolver; + } + public static class Builder { private final Context context; @@ -69,6 +75,7 @@ public class SpannableConfiguration { private LinkSpan.Resolver linkResolver; private UrlProcessor urlProcessor; private SpannableHtmlParser htmlParser; + private ImageClickResolver imageClickResolver; Builder(Context context) { this.context = context; @@ -104,6 +111,11 @@ public class SpannableConfiguration { return this; } + public Builder setImageClickResolver(ImageClickResolver imageClickResolver) { + this.imageClickResolver = imageClickResolver; + return this; + } + public SpannableConfiguration build() { if (theme == null) { theme = SpannableTheme.create(context); @@ -120,8 +132,12 @@ public class SpannableConfiguration { if (urlProcessor == null) { urlProcessor = new UrlProcessorNoOp(); } + if (imageClickResolver == null) { + imageClickResolver = new ImageClickResolverDef(); + } if (htmlParser == null) { - htmlParser = SpannableHtmlParser.create(theme, asyncDrawableLoader, urlProcessor, linkResolver); + htmlParser = SpannableHtmlParser.create(theme, asyncDrawableLoader, urlProcessor, + linkResolver, imageClickResolver); } return new SpannableConfiguration(this); } diff --git a/library/src/main/java/ru/noties/markwon/renderer/SpannableMarkdownVisitor.java b/library/src/main/java/ru/noties/markwon/renderer/SpannableMarkdownVisitor.java index f0bbf912..986d73cb 100644 --- a/library/src/main/java/ru/noties/markwon/renderer/SpannableMarkdownVisitor.java +++ b/library/src/main/java/ru/noties/markwon/renderer/SpannableMarkdownVisitor.java @@ -4,7 +4,9 @@ import android.support.annotation.NonNull; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextUtils; +import android.text.style.ClickableSpan; import android.text.style.StrikethroughSpan; +import android.view.View; import org.commonmark.ext.gfm.strikethrough.Strikethrough; import org.commonmark.ext.gfm.tables.TableBody; @@ -431,8 +433,12 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { ) ); - // todo, maybe, if image is not inside a link, we should make it clickable, so - // user can open it in external viewer? + setSpan(length, new ClickableSpan() { + @Override + public void onClick(View view) { + configuration.imageClickResolver().resolve(view, destination); + } + }); } @Override diff --git a/library/src/main/java/ru/noties/markwon/renderer/html/ImageProviderImpl.java b/library/src/main/java/ru/noties/markwon/renderer/html/ImageProviderImpl.java index 3e45caee..66236923 100644 --- a/library/src/main/java/ru/noties/markwon/renderer/html/ImageProviderImpl.java +++ b/library/src/main/java/ru/noties/markwon/renderer/html/ImageProviderImpl.java @@ -4,9 +4,12 @@ import android.support.annotation.NonNull; import android.text.SpannableString; import android.text.Spanned; import android.text.TextUtils; +import android.text.style.ClickableSpan; +import android.view.View; import java.util.Map; +import ru.noties.markwon.ImageClickResolver; import ru.noties.markwon.UrlProcessor; import ru.noties.markwon.spans.AsyncDrawable; import ru.noties.markwon.spans.AsyncDrawableSpan; @@ -17,14 +20,17 @@ class ImageProviderImpl implements SpannableHtmlParser.ImageProvider { private final SpannableTheme theme; private final AsyncDrawable.Loader loader; private final UrlProcessor urlProcessor; + private final ImageClickResolver imageClickResolver; ImageProviderImpl( @NonNull SpannableTheme theme, @NonNull AsyncDrawable.Loader loader, - @NonNull UrlProcessor urlProcessor) { + @NonNull UrlProcessor urlProcessor, + @NonNull ImageClickResolver imageClickResolver) { this.theme = theme; this.loader = loader; this.urlProcessor = urlProcessor; + this.imageClickResolver = imageClickResolver; } @Override @@ -53,6 +59,14 @@ class ImageProviderImpl implements SpannableHtmlParser.ImageProvider { final SpannableString string = new SpannableString(replacement); string.setSpan(span, 0, string.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + final ClickableSpan clickableSpan = new ClickableSpan() { + @Override + public void onClick(View view) { + imageClickResolver.resolve(view, destination); + } + }; + string.setSpan(clickableSpan, 0, string.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + spanned = string; } else { spanned = null; diff --git a/library/src/main/java/ru/noties/markwon/renderer/html/SpannableHtmlParser.java b/library/src/main/java/ru/noties/markwon/renderer/html/SpannableHtmlParser.java index af616330..8e5bfbc4 100644 --- a/library/src/main/java/ru/noties/markwon/renderer/html/SpannableHtmlParser.java +++ b/library/src/main/java/ru/noties/markwon/renderer/html/SpannableHtmlParser.java @@ -10,6 +10,8 @@ import android.text.Spanned; import java.util.HashMap; import java.util.Map; +import ru.noties.markwon.ImageClickResolver; +import ru.noties.markwon.ImageClickResolverDef; import ru.noties.markwon.LinkResolverDef; import ru.noties.markwon.UrlProcessor; import ru.noties.markwon.UrlProcessorNoOp; @@ -25,7 +27,8 @@ public class SpannableHtmlParser { @NonNull SpannableTheme theme, @NonNull AsyncDrawable.Loader loader ) { - return builderWithDefaults(theme, loader, null, null) + return builderWithDefaults(theme, loader, null, null, + null) .build(); } @@ -33,9 +36,10 @@ public class SpannableHtmlParser { @NonNull SpannableTheme theme, @NonNull AsyncDrawable.Loader loader, @NonNull UrlProcessor urlProcessor, - @NonNull LinkSpan.Resolver resolver + @NonNull LinkSpan.Resolver linkResolver, + @NonNull ImageClickResolver imageClickResolver ) { - return builderWithDefaults(theme, loader, urlProcessor, resolver) + return builderWithDefaults(theme, loader, urlProcessor, linkResolver, imageClickResolver) .build(); } @@ -44,22 +48,28 @@ public class SpannableHtmlParser { } public static Builder builderWithDefaults(@NonNull SpannableTheme theme) { - return builderWithDefaults(theme, null, null, null); + return builderWithDefaults(theme, null, null, null, + null); } public static Builder builderWithDefaults( @NonNull SpannableTheme theme, @Nullable AsyncDrawable.Loader asyncDrawableLoader, @Nullable UrlProcessor urlProcessor, - @Nullable LinkSpan.Resolver resolver + @Nullable LinkSpan.Resolver linkResolver, + @Nullable ImageClickResolver imageClickResolver ) { if (urlProcessor == null) { urlProcessor = new UrlProcessorNoOp(); } - if (resolver == null) { - resolver = new LinkResolverDef(); + if (linkResolver == null) { + linkResolver = new LinkResolverDef(); + } + + if (imageClickResolver == null) { + imageClickResolver = new ImageClickResolverDef(); } final BoldProvider boldProvider = new BoldProvider(); @@ -68,7 +78,8 @@ public class SpannableHtmlParser { final ImageProvider imageProvider; if (asyncDrawableLoader != null) { - imageProvider = new ImageProviderImpl(theme, asyncDrawableLoader, urlProcessor); + imageProvider = new ImageProviderImpl(theme, asyncDrawableLoader, urlProcessor, + imageClickResolver); } else { imageProvider = null; } @@ -86,7 +97,7 @@ public class SpannableHtmlParser { .simpleTag("del", strikeProvider) .simpleTag("s", strikeProvider) .simpleTag("strike", strikeProvider) - .simpleTag("a", new LinkProvider(theme, urlProcessor, resolver)) + .simpleTag("a", new LinkProvider(theme, urlProcessor, linkResolver)) .imageProvider(imageProvider); }