Working with realtive urls
This commit is contained in:
parent
9fbf5ff1b1
commit
250dd7677d
@ -97,6 +97,8 @@ Lorem ipsum `dolor` sit amet
|
|||||||
Lorem ipsum dolor `sit` amet
|
Lorem ipsum dolor `sit` amet
|
||||||
Lorem ipsum dolor sit `amet`
|
Lorem ipsum dolor sit `amet`
|
||||||
|
|
||||||
|
`Lorem ipsum dolor sit amet`
|
||||||
|
|
||||||
### Code block
|
### Code block
|
||||||
// todo syntax higlight
|
// todo syntax higlight
|
||||||
```
|
```
|
||||||
|
@ -55,8 +55,8 @@ class AppModule {
|
|||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
UrlProvider urlProvider() {
|
UriProcessor uriProcessor() {
|
||||||
return new UrlProviderImpl();
|
return new UriProcessorImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
12
app/src/main/java/ru/noties/markwon/CollectionUtils.java
Normal file
12
app/src/main/java/ru/noties/markwon/CollectionUtils.java
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package ru.noties.markwon;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public abstract class CollectionUtils {
|
||||||
|
|
||||||
|
public static boolean isEmpty(Collection<?> collection) {
|
||||||
|
return collection == null || collection.size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CollectionUtils() {}
|
||||||
|
}
|
@ -27,6 +27,9 @@ public class MainActivity extends Activity {
|
|||||||
@Inject
|
@Inject
|
||||||
Themes themes;
|
Themes themes;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
UriProcessor uriProcessor;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@ -38,9 +41,13 @@ public class MainActivity extends Activity {
|
|||||||
themes.apply(this);
|
themes.apply(this);
|
||||||
|
|
||||||
// how can we obtain SpannableConfiguration after theme was applied?
|
// how can we obtain SpannableConfiguration after theme was applied?
|
||||||
|
// as we inject `themes` we won't be able to inject configuration, as it requires theme set
|
||||||
|
|
||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
|
|
||||||
|
// we process additionally github urls, as if url has in path `blob`, we won't receive
|
||||||
|
// desired file, but instead rendered html
|
||||||
|
checkUri();
|
||||||
|
|
||||||
final AppBarItem.Renderer appBarRenderer
|
final AppBarItem.Renderer appBarRenderer
|
||||||
= new AppBarItem.Renderer(findViewById(R.id.app_bar), new View.OnClickListener() {
|
= new AppBarItem.Renderer(findViewById(R.id.app_bar), new View.OnClickListener() {
|
||||||
@ -59,7 +66,7 @@ public class MainActivity extends Activity {
|
|||||||
markdownLoader.load(uri(), new MarkdownLoader.OnMarkdownTextLoaded() {
|
markdownLoader.load(uri(), new MarkdownLoader.OnMarkdownTextLoaded() {
|
||||||
@Override
|
@Override
|
||||||
public void apply(String text) {
|
public void apply(String text) {
|
||||||
markdownRenderer.render(MainActivity.this, text, new MarkdownRenderer.MarkdownReadyListener() {
|
markdownRenderer.render(MainActivity.this, uri(), text, new MarkdownRenderer.MarkdownReadyListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onMarkdownReady(CharSequence markdown) {
|
public void onMarkdownReady(CharSequence markdown) {
|
||||||
Markwon.setText(textView, markdown);
|
Markwon.setText(textView, markdown);
|
||||||
@ -80,8 +87,6 @@ public class MainActivity extends Activity {
|
|||||||
|
|
||||||
final Uri uri = uri();
|
final Uri uri = uri();
|
||||||
|
|
||||||
Debug.i(uri);
|
|
||||||
|
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
title = uri.getLastPathSegment();
|
title = uri.getLastPathSegment();
|
||||||
subtitle = uri.toString();
|
subtitle = uri.toString();
|
||||||
@ -93,6 +98,13 @@ public class MainActivity extends Activity {
|
|||||||
return new AppBarItem.State(title, subtitle);
|
return new AppBarItem.State(title, subtitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkUri() {
|
||||||
|
final Uri uri = uri();
|
||||||
|
if (uri != null) {
|
||||||
|
getIntent().setData(uriProcessor.process(uri));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Uri uri() {
|
private Uri uri() {
|
||||||
final Intent intent = getIntent();
|
final Intent intent = getIntent();
|
||||||
return intent != null
|
return intent != null
|
||||||
|
@ -45,9 +45,6 @@ public class MarkdownLoader {
|
|||||||
@Inject
|
@Inject
|
||||||
OkHttpClient client;
|
OkHttpClient client;
|
||||||
|
|
||||||
@Inject
|
|
||||||
UrlProvider urlProvider;
|
|
||||||
|
|
||||||
private Future<?> task;
|
private Future<?> task;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@ -130,10 +127,8 @@ public class MarkdownLoader {
|
|||||||
|
|
||||||
private String loadExternalUrl(@NonNull Uri uri) {
|
private String loadExternalUrl(@NonNull Uri uri) {
|
||||||
|
|
||||||
final String url = urlProvider.provide(uri);
|
|
||||||
|
|
||||||
final Request request = new Request.Builder()
|
final Request request = new Request.Builder()
|
||||||
.url(url)
|
.url(uri.toString())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
Response response = null;
|
Response response = null;
|
||||||
|
@ -1,14 +1,23 @@
|
|||||||
package ru.noties.markwon;
|
package ru.noties.markwon;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.commonmark.ext.gfm.strikethrough.StrikethroughExtension;
|
||||||
|
import org.commonmark.node.Node;
|
||||||
|
import org.commonmark.parser.Parser;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import ru.noties.markwon.renderer.SpannableRenderer;
|
||||||
|
|
||||||
@ActivityScope
|
@ActivityScope
|
||||||
public class MarkdownRenderer {
|
public class MarkdownRenderer {
|
||||||
|
|
||||||
@ -31,15 +40,37 @@ public class MarkdownRenderer {
|
|||||||
MarkdownRenderer() {
|
MarkdownRenderer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void render(@NonNull final Context context, @NonNull final String markdown, @NonNull final MarkdownReadyListener listener) {
|
public void render(
|
||||||
|
@NonNull final Context context,
|
||||||
|
@Nullable final Uri uri,
|
||||||
|
@NonNull final String markdown,
|
||||||
|
@NonNull final MarkdownReadyListener listener) {
|
||||||
cancel();
|
cancel();
|
||||||
task = service.submit(new Runnable() {
|
task = service.submit(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
||||||
|
final UrlProcessor urlProcessor;
|
||||||
|
if (uri == null) {
|
||||||
|
urlProcessor = null;
|
||||||
|
} else {
|
||||||
|
urlProcessor = new UrlProcessorRelativeToAbsolute(uri.toString());
|
||||||
|
}
|
||||||
|
|
||||||
final SpannableConfiguration configuration = SpannableConfiguration.builder(context)
|
final SpannableConfiguration configuration = SpannableConfiguration.builder(context)
|
||||||
.asyncDrawableLoader(loader)
|
.asyncDrawableLoader(loader)
|
||||||
|
.urlProcessor(urlProcessor)
|
||||||
.build();
|
.build();
|
||||||
final CharSequence text = Markwon.markdown(configuration, markdown);
|
|
||||||
|
final Parser parser = Parser.builder()
|
||||||
|
.extensions(Collections.singleton(StrikethroughExtension.create()))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final Node node = parser.parse(markdown);
|
||||||
|
final SpannableRenderer renderer = new SpannableRenderer();
|
||||||
|
final CharSequence text = renderer.render(configuration, node);
|
||||||
|
|
||||||
|
// final CharSequence text = Markwon.markdown(configuration, markdown);
|
||||||
handler.post(new Runnable() {
|
handler.post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
9
app/src/main/java/ru/noties/markwon/UriProcessor.java
Normal file
9
app/src/main/java/ru/noties/markwon/UriProcessor.java
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package ru.noties.markwon;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
public interface UriProcessor {
|
||||||
|
Uri process(@NonNull Uri uri);
|
||||||
|
}
|
@ -5,25 +5,33 @@ import android.support.annotation.NonNull;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
class UrlProviderImpl implements UrlProvider {
|
class UriProcessorImpl implements UriProcessor {
|
||||||
|
|
||||||
private static final String GITHUB = "github.com";
|
private static final String GITHUB = "github.com";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String provide(@NonNull Uri uri) {
|
public Uri process(@NonNull final Uri uri) {
|
||||||
|
|
||||||
// hm... github, even having a README.md in path will return rendered HTML
|
// hm... github, even having a README.md in path will return rendered HTML
|
||||||
|
|
||||||
|
final Uri out;
|
||||||
|
|
||||||
if (GITHUB.equals(uri.getAuthority())) {
|
if (GITHUB.equals(uri.getAuthority())) {
|
||||||
|
|
||||||
final List<String> segments = uri.getPathSegments();
|
final List<String> segments = uri.getPathSegments();
|
||||||
if (segments != null
|
final int size = segments != null
|
||||||
&& segments.contains("blob")) {
|
? segments.size()
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
if (size > 0) {
|
||||||
|
|
||||||
// we need to modify the final uri
|
// we need to modify the final uri
|
||||||
final Uri.Builder builder = new Uri.Builder()
|
final Uri.Builder builder = new Uri.Builder()
|
||||||
.scheme(uri.getScheme())
|
.scheme(uri.getScheme())
|
||||||
.authority(uri.getAuthority())
|
.authority(uri.getAuthority())
|
||||||
.fragment(uri.getFragment())
|
.fragment(uri.getFragment())
|
||||||
.query(uri.getQuery());
|
.query(uri.getQuery());
|
||||||
|
|
||||||
for (String segment: segments) {
|
for (String segment: segments) {
|
||||||
final String part;
|
final String part;
|
||||||
if ("blob".equals(segment)) {
|
if ("blob".equals(segment)) {
|
||||||
@ -33,10 +41,14 @@ class UrlProviderImpl implements UrlProvider {
|
|||||||
}
|
}
|
||||||
builder.appendPath(part);
|
builder.appendPath(part);
|
||||||
}
|
}
|
||||||
uri = builder.build();
|
out = builder.build();
|
||||||
|
} else {
|
||||||
|
out = uri;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
out = uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
return uri.toString();
|
return out;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,8 +0,0 @@
|
|||||||
package ru.noties.markwon;
|
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
|
|
||||||
public interface UrlProvider {
|
|
||||||
String provide(@NonNull Uri uri);
|
|
||||||
}
|
|
@ -24,6 +24,7 @@ public class SpannableConfiguration {
|
|||||||
private final AsyncDrawable.Loader asyncDrawableLoader;
|
private final AsyncDrawable.Loader asyncDrawableLoader;
|
||||||
private final SyntaxHighlight syntaxHighlight;
|
private final SyntaxHighlight syntaxHighlight;
|
||||||
private final LinkSpan.Resolver linkResolver;
|
private final LinkSpan.Resolver linkResolver;
|
||||||
|
private final UrlProcessor urlProcessor;
|
||||||
private final SpannableHtmlParser htmlParser;
|
private final SpannableHtmlParser htmlParser;
|
||||||
|
|
||||||
private SpannableConfiguration(Builder builder) {
|
private SpannableConfiguration(Builder builder) {
|
||||||
@ -31,6 +32,7 @@ public class SpannableConfiguration {
|
|||||||
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.htmlParser = builder.htmlParser;
|
this.htmlParser = builder.htmlParser;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,6 +52,10 @@ public class SpannableConfiguration {
|
|||||||
return linkResolver;
|
return linkResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UrlProcessor urlProcessor() {
|
||||||
|
return urlProcessor;
|
||||||
|
}
|
||||||
|
|
||||||
public SpannableHtmlParser htmlParser() {
|
public SpannableHtmlParser htmlParser() {
|
||||||
return htmlParser;
|
return htmlParser;
|
||||||
}
|
}
|
||||||
@ -61,6 +67,7 @@ public class SpannableConfiguration {
|
|||||||
private AsyncDrawable.Loader asyncDrawableLoader;
|
private AsyncDrawable.Loader asyncDrawableLoader;
|
||||||
private SyntaxHighlight syntaxHighlight;
|
private SyntaxHighlight syntaxHighlight;
|
||||||
private LinkSpan.Resolver linkResolver;
|
private LinkSpan.Resolver linkResolver;
|
||||||
|
private UrlProcessor urlProcessor;
|
||||||
private SpannableHtmlParser htmlParser;
|
private SpannableHtmlParser htmlParser;
|
||||||
|
|
||||||
public Builder(Context context) {
|
public Builder(Context context) {
|
||||||
@ -87,6 +94,11 @@ public class SpannableConfiguration {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder urlProcessor(UrlProcessor urlProcessor) {
|
||||||
|
this.urlProcessor = urlProcessor;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Builder htmlParser(SpannableHtmlParser htmlParser) {
|
public Builder htmlParser(SpannableHtmlParser htmlParser) {
|
||||||
this.htmlParser = htmlParser;
|
this.htmlParser = htmlParser;
|
||||||
return this;
|
return this;
|
||||||
@ -105,8 +117,11 @@ public class SpannableConfiguration {
|
|||||||
if (linkResolver == null) {
|
if (linkResolver == null) {
|
||||||
linkResolver = new LinkResolverDef();
|
linkResolver = new LinkResolverDef();
|
||||||
}
|
}
|
||||||
|
if (urlProcessor == null) {
|
||||||
|
urlProcessor = new UrlProcessorNoOp();
|
||||||
|
}
|
||||||
if (htmlParser == null) {
|
if (htmlParser == null) {
|
||||||
htmlParser = SpannableHtmlParser.create(theme, asyncDrawableLoader);
|
htmlParser = SpannableHtmlParser.create(theme, asyncDrawableLoader, urlProcessor);
|
||||||
}
|
}
|
||||||
return new SpannableConfiguration(this);
|
return new SpannableConfiguration(this);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
package ru.noties.markwon;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
public interface UrlProcessor {
|
||||||
|
@NonNull
|
||||||
|
String process(@NonNull String destination);
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package ru.noties.markwon;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
public class UrlProcessorNoOp implements UrlProcessor {
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String process(@NonNull String destination) {
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package ru.noties.markwon;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
public class UrlProcessorRelativeToAbsolute implements UrlProcessor {
|
||||||
|
|
||||||
|
private final URL base;
|
||||||
|
|
||||||
|
public UrlProcessorRelativeToAbsolute(@NonNull String base) {
|
||||||
|
this.base = obtain(base);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String process(@NonNull String destination) {
|
||||||
|
|
||||||
|
String out = destination;
|
||||||
|
|
||||||
|
if (base != null) {
|
||||||
|
try {
|
||||||
|
final URL u = new URL(base, destination);
|
||||||
|
out = u.toString();
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static URL obtain(String base) {
|
||||||
|
try {
|
||||||
|
return new URL(base);
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -51,7 +51,7 @@ import ru.noties.markwon.spans.ThematicBreakSpan;
|
|||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings("WeakerAccess")
|
||||||
public class SpannableMarkdownVisitor extends AbstractVisitor {
|
public class SpannableMarkdownVisitor extends AbstractVisitor {
|
||||||
|
|
||||||
private static final String HTML_CONTENT = "<%1$s>%2$s</%1$s>";
|
private static final String HTML_CONTENT = "<%1$s>%2$s</%3$s>";
|
||||||
|
|
||||||
private final SpannableConfiguration configuration;
|
private final SpannableConfiguration configuration;
|
||||||
private final SpannableStringBuilder builder;
|
private final SpannableStringBuilder builder;
|
||||||
@ -253,7 +253,8 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(SoftLineBreak softLineBreak) {
|
public void visit(SoftLineBreak softLineBreak) {
|
||||||
newLine();
|
// at first here was a new line, but here should be a space char
|
||||||
|
builder.append(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -306,13 +307,14 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
|
|||||||
|
|
||||||
final Node parent = image.getParent();
|
final Node parent = image.getParent();
|
||||||
final boolean link = parent != null && parent instanceof Link;
|
final boolean link = parent != null && parent instanceof Link;
|
||||||
|
final String destination = configuration.urlProcessor().process(image.getDestination());
|
||||||
|
|
||||||
setSpan(
|
setSpan(
|
||||||
length,
|
length,
|
||||||
new AsyncDrawableSpan(
|
new AsyncDrawableSpan(
|
||||||
configuration.theme(),
|
configuration.theme(),
|
||||||
new AsyncDrawable(
|
new AsyncDrawable(
|
||||||
image.getDestination(),
|
destination,
|
||||||
configuration.asyncDrawableLoader()
|
configuration.asyncDrawableLoader()
|
||||||
),
|
),
|
||||||
AsyncDrawableSpan.ALIGN_BOTTOM,
|
AsyncDrawableSpan.ALIGN_BOTTOM,
|
||||||
@ -351,7 +353,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
|
|||||||
setSpan(item.start, span);
|
setSpan(item.start, span);
|
||||||
} else {
|
} else {
|
||||||
final String content = builder.subSequence(start, builder.length()).toString();
|
final String content = builder.subSequence(start, builder.length()).toString();
|
||||||
final String html = String.format(HTML_CONTENT, item.tag, content);
|
final String html = String.format(HTML_CONTENT, item.tag, content, tag.name());
|
||||||
final Object[] spans = htmlParser.htmlSpans(html);
|
final Object[] spans = htmlParser.htmlSpans(html);
|
||||||
final int length = spans != null
|
final int length = spans != null
|
||||||
? spans.length
|
? spans.length
|
||||||
@ -382,7 +384,8 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
|
|||||||
public void visit(Link link) {
|
public void visit(Link link) {
|
||||||
final int length = builder.length();
|
final int length = builder.length();
|
||||||
visitChildren(link);
|
visitChildren(link);
|
||||||
setSpan(length, new LinkSpan(configuration.theme(), link.getDestination(), configuration.linkResolver()));
|
final String destination = configuration.urlProcessor().process(link.getDestination());
|
||||||
|
setSpan(length, new LinkSpan(configuration.theme(), destination, configuration.linkResolver()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setSpan(int start, @NonNull Object span) {
|
private void setSpan(int start, @NonNull Object span) {
|
||||||
|
@ -2,20 +2,30 @@ package ru.noties.markwon.renderer.html;
|
|||||||
|
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
|
|
||||||
|
import ru.noties.markwon.UrlProcessor;
|
||||||
import ru.noties.markwon.spans.AsyncDrawable;
|
import ru.noties.markwon.spans.AsyncDrawable;
|
||||||
|
|
||||||
class HtmlImageGetter implements Html.ImageGetter {
|
class HtmlImageGetter implements Html.ImageGetter {
|
||||||
|
|
||||||
private final AsyncDrawable.Loader loader;
|
private final AsyncDrawable.Loader loader;
|
||||||
|
private final UrlProcessor urlProcessor;
|
||||||
|
|
||||||
HtmlImageGetter(@NonNull AsyncDrawable.Loader loader) {
|
HtmlImageGetter(@NonNull AsyncDrawable.Loader loader, @Nullable UrlProcessor urlProcessor) {
|
||||||
this.loader = loader;
|
this.loader = loader;
|
||||||
|
this.urlProcessor = urlProcessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Drawable getDrawable(String source) {
|
public Drawable getDrawable(String source) {
|
||||||
return new AsyncDrawable(source, loader);
|
final String destination;
|
||||||
|
if (urlProcessor == null) {
|
||||||
|
destination = source;
|
||||||
|
} else {
|
||||||
|
destination = urlProcessor.process(source);
|
||||||
|
}
|
||||||
|
return new AsyncDrawable(destination, loader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,8 @@ import java.util.HashSet;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import ru.noties.debug.Debug;
|
||||||
|
import ru.noties.markwon.UrlProcessor;
|
||||||
import ru.noties.markwon.spans.AsyncDrawable;
|
import ru.noties.markwon.spans.AsyncDrawable;
|
||||||
import ru.noties.markwon.spans.SpannableTheme;
|
import ru.noties.markwon.spans.SpannableTheme;
|
||||||
|
|
||||||
@ -22,8 +24,20 @@ public class SpannableHtmlParser {
|
|||||||
// we need to handle images independently (in order to parse alt, width, height, etc)
|
// we need to handle images independently (in order to parse alt, width, height, etc)
|
||||||
|
|
||||||
// creates default parser
|
// creates default parser
|
||||||
public static SpannableHtmlParser create(@NonNull SpannableTheme theme, @NonNull AsyncDrawable.Loader loader) {
|
public static SpannableHtmlParser create(
|
||||||
return builderWithDefaults(theme, loader)
|
@NonNull SpannableTheme theme,
|
||||||
|
@NonNull AsyncDrawable.Loader loader
|
||||||
|
) {
|
||||||
|
return builderWithDefaults(theme, loader, null)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SpannableHtmlParser create(
|
||||||
|
@NonNull SpannableTheme theme,
|
||||||
|
@NonNull AsyncDrawable.Loader loader,
|
||||||
|
@NonNull UrlProcessor urlProcessor
|
||||||
|
) {
|
||||||
|
return builderWithDefaults(theme, loader, urlProcessor)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,7 +47,8 @@ public class SpannableHtmlParser {
|
|||||||
|
|
||||||
public static Builder builderWithDefaults(
|
public static Builder builderWithDefaults(
|
||||||
@NonNull SpannableTheme theme,
|
@NonNull SpannableTheme theme,
|
||||||
@Nullable AsyncDrawable.Loader asyncDrawableLoader
|
@Nullable AsyncDrawable.Loader asyncDrawableLoader,
|
||||||
|
@Nullable UrlProcessor urlProcessor
|
||||||
) {
|
) {
|
||||||
|
|
||||||
final BoldProvider boldProvider = new BoldProvider();
|
final BoldProvider boldProvider = new BoldProvider();
|
||||||
@ -42,7 +57,7 @@ public class SpannableHtmlParser {
|
|||||||
|
|
||||||
final HtmlParser parser;
|
final HtmlParser parser;
|
||||||
if (asyncDrawableLoader != null) {
|
if (asyncDrawableLoader != null) {
|
||||||
parser = DefaultHtmlParser.create(new HtmlImageGetter(asyncDrawableLoader), null);
|
parser = DefaultHtmlParser.create(new HtmlImageGetter(asyncDrawableLoader, urlProcessor), null);
|
||||||
} else {
|
} else {
|
||||||
parser = DefaultHtmlParser.create(null, null);
|
parser = DefaultHtmlParser.create(null, null);
|
||||||
}
|
}
|
||||||
@ -71,9 +86,12 @@ public class SpannableHtmlParser {
|
|||||||
|
|
||||||
public interface HtmlParser {
|
public interface HtmlParser {
|
||||||
Object[] getSpans(@NonNull String html);
|
Object[] getSpans(@NonNull String html);
|
||||||
|
|
||||||
Spanned parse(@NonNull String html);
|
Spanned parse(@NonNull String html);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final String LINK_START = "<a ";
|
||||||
|
|
||||||
private final Map<String, SpanProvider> customTags;
|
private final Map<String, SpanProvider> customTags;
|
||||||
private final Set<String> voidTags;
|
private final Set<String> voidTags;
|
||||||
private final HtmlParser parser;
|
private final HtmlParser parser;
|
||||||
@ -97,7 +115,7 @@ public class SpannableHtmlParser {
|
|||||||
if (length < 3) {
|
if (length < 3) {
|
||||||
tag = null;
|
tag = null;
|
||||||
} else {
|
} else {
|
||||||
// okay, we will consider a tag a void one if it's in our void list tag or if it ends with `/>`
|
// okay, we will consider a tag a void one if it's in our void list tag
|
||||||
final boolean closing = '<' == html.charAt(0) && '/' == html.charAt(1);
|
final boolean closing = '<' == html.charAt(0) && '/' == html.charAt(1);
|
||||||
final boolean voidTag;
|
final boolean voidTag;
|
||||||
if (closing) {
|
if (closing) {
|
||||||
@ -144,10 +162,14 @@ public class SpannableHtmlParser {
|
|||||||
@Nullable
|
@Nullable
|
||||||
public Object[] htmlSpans(String html) {
|
public Object[] htmlSpans(String html) {
|
||||||
// todo, additional handling of: image & link
|
// todo, additional handling of: image & link
|
||||||
|
Debug.i("html: %s", html);
|
||||||
return parser.getSpans(html);
|
return parser.getSpans(html);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this is called when we encounter `void` tag
|
||||||
|
// `img` is a void tag
|
||||||
public Spanned html(String html) {
|
public Spanned html(String html) {
|
||||||
|
Debug.i("html: %s", html);
|
||||||
return parser.parse(html);
|
return parser.parse(html);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user