Added ability to load from assets

This commit is contained in:
Dimitry Ivanov 2017-05-27 14:12:21 +03:00
parent 261f289329
commit 97f13926e0
10 changed files with 206 additions and 57 deletions

View File

@ -1,3 +1,5 @@
![logo](./art/markwon_logo.png)
# Markwon
[![maven|markwon](https://img.shields.io/maven-central/v/ru.noties/markwon.svg?label=maven%7Cmarkwon)](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%markwon%22)
@ -40,6 +42,9 @@ compile 'ru.noties:markwon-image-loader:1.0.0' // optional
* other inline html is rendered via (`Html.fromHtml(...)`)
---
### Emphasis
*Lorem ipsum dolor sit amet*
@ -55,6 +60,9 @@ _Lorem ipsum dolor sit amet_
<dfn>Lorem ipsum dolor sit amet</dfn>
---
### Strong emphasis
**Lorem ipsum dolor sit amet**
@ -66,6 +74,9 @@ __Lorem ipsum dolor sit amet__
<strong>Lorem ipsum dolor sit amet</strong>
---
### Strike-through
~~Lorem ipsum dolor sit amet~~
@ -97,9 +108,7 @@ __Lorem ipsum dolor sit amet__
<a href="https://github.com">click me</a>
### Images
// todo, normal ones & svg & gif
---
### Thematic break
@ -114,6 +123,9 @@ ___
>>> Lorem ipsum dolor sit amet
---
### Ordered lists
1. Lorem ipsum dolor sit amet
2. Lorem ipsum dolor sit amet
@ -123,6 +135,9 @@ ___
3. Lorem ipsum dolor sit amet
---
### Non-ordered lists
* Lorem ipsum dolor sit amet
* Lorem ipsum dolor sit amet
@ -132,6 +147,9 @@ ___
* Lorem ipsum dolor sit amet
---
### Inline code
`Lorem` ipsum dolor sit amet
Lorem `ipsum` dolor sit amet
@ -142,6 +160,9 @@ Lorem ipsum dolor sit `amet`
`Lorem ipsum dolor sit amet`
---
### Code block
```
Lorem ipsum dolor sit amet
@ -149,11 +170,16 @@ Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
```
---
### H.T.M.L.
<b>O</b><i>K<s>A</s><sup>42<sup>43<sub><b>42</b></sub></sup></sup><u>Y</u></i>
---
### Tables
Header #1 | Header #2 | Header #3
---: | :---: | :---
@ -173,4 +199,4 @@ long long long skjfs fgjsdfhj sf `dfk df` | sdsd,fklsdfklsdfklsdfkl sdfkl dsfjks
[github]: https://github.com
[commonmark-java]: https://github.com/atlassian/commonmark-java/blob/master/README.md
[library]: https://github.com/noties/Markwon/blob/master/README.md
[library]: https://github.com/noties/Markwon/blob/master/library/README.md

View File

@ -0,0 +1 @@
../../../../../art/markwon_logo.png

View File

@ -42,6 +42,7 @@ class AppModule {
return new OkHttpClient.Builder()
.cache(new Cache(app.getCacheDir(), 1024L * 20))
.followRedirects(true)
.retryOnConnectionFailure(true)
.build();
}

View File

@ -6,17 +6,11 @@ import android.os.Handler;
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.Future;
import javax.inject.Inject;
import ru.noties.markwon.renderer.SpannableRenderer;
import ru.noties.markwon.spans.AsyncDrawable;
@ActivityScope
@ -53,7 +47,7 @@ public class MarkdownRenderer {
final UrlProcessor urlProcessor;
if (uri == null) {
urlProcessor = null;
urlProcessor = new UrlProcessorAndroidAssets();
} else {
urlProcessor = new UrlProcessorRelativeToAbsolute(uri.toString());
}

BIN
art/markwon_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -14,7 +14,6 @@ android {
}
dependencies {
compile project(':library')
compile ANDROID_SVG
compile ANDROID_GIF

View File

@ -6,6 +6,7 @@ import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
@ -14,7 +15,11 @@ import android.text.TextUtils;
import com.caverock.androidsvg.SVG;
import com.caverock.androidsvg.SVGParseException;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
@ -46,6 +51,8 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
private static final String CONTENT_TYPE_SVG = "image/svg+xml";
private static final String CONTENT_TYPE_GIF = "image/gif";
private static final String FILE_ANDROID_ASSETS = "android_asset";
private final OkHttpClient client;
private final Resources resources;
private final ExecutorService executorService;
@ -97,50 +104,35 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
@Override
public void run() {
final Request request = new Request.Builder()
.url(destination)
.tag(destination)
.build();
final Item item;
Response response = null;
try {
response = client.newCall(request).execute();
} catch (IOException e) {
e.printStackTrace();
final Uri uri = Uri.parse(destination);
if ("file".equals(uri.getScheme())) {
item = fromFile(uri);
} else {
item = fromNetwork(destination);
}
Drawable result = null;
if (response != null) {
final ResponseBody body = response.body();
if (body != null) {
final InputStream inputStream = body.byteStream();
if (inputStream != null) {
final String contentType = response.header(HEADER_CONTENT_TYPE);
if (item != null
&& item.inputStream != null) {
try {
// svg can have `image/svg+xml;charset=...`
if (CONTENT_TYPE_SVG.equals(contentType)
|| (!TextUtils.isEmpty(contentType) && contentType.startsWith(CONTENT_TYPE_SVG))) {
// handle SVG
result = handleSvg(inputStream);
} else if (CONTENT_TYPE_GIF.equals(contentType)) {
// handle gif
result = handleGif(inputStream);
if (CONTENT_TYPE_SVG.equals(item.type)) {
result = handleSvg(item.inputStream);
} else if (CONTENT_TYPE_GIF.equals(item.type)) {
result = handleGif(item.inputStream);
} else {
result = handleSimple(inputStream);
// just try to decode whatever it is
result = handleSimple(item.inputStream);
}
} finally {
try {
inputStream.close();
item.inputStream.close();
} catch (IOException e) {
// no op
}
}
}
}
}
// if result is null, we assume it's an error
if (result == null) {
@ -165,6 +157,102 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
});
}
private Item fromFile(Uri uri) {
final List<String> segments = uri.getPathSegments();
if (segments == null
|| segments.size() == 0) {
// pointing to file & having no path segments is no use
return null;
}
final Item out;
final String type;
final InputStream inputStream;
final boolean assets = FILE_ANDROID_ASSETS.equals(segments.get(0));
final String lastSegment = uri.getLastPathSegment();
if (lastSegment.endsWith(".svg")) {
type = CONTENT_TYPE_SVG;
} else if (lastSegment.endsWith(".gif")) {
type = CONTENT_TYPE_GIF;
} else {
type = null;
}
if (assets) {
final StringBuilder path = new StringBuilder();
for (int i = 1, size = segments.size(); i < size; i++) {
if (i != 1) {
path.append('/');
}
path.append(segments.get(i));
}
// load assets
InputStream inner = null;
try {
inner = resources.getAssets().open(path.toString());
} catch (IOException e) {
e.printStackTrace();
}
inputStream = inner;
} else {
InputStream inner = null;
try {
inner = new BufferedInputStream(new FileInputStream(new File(uri.getPath())));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
inputStream = inner;
}
if (inputStream != null) {
out = new Item(type, inputStream);
} else {
out = null;
}
return out;
}
private Item fromNetwork(String destination) {
Item out = null;
final Request request = new Request.Builder()
.url(destination)
.tag(destination)
.build();
Response response = null;
try {
response = client.newCall(request).execute();
} catch (IOException e) {
e.printStackTrace();
}
if (response != null) {
final ResponseBody body = response.body();
if (body != null) {
final InputStream inputStream = body.byteStream();
if (inputStream != null) {
final String type;
final String contentType = response.header(HEADER_CONTENT_TYPE);
if (!TextUtils.isEmpty(contentType)
&& contentType.startsWith(CONTENT_TYPE_SVG)) {
type = CONTENT_TYPE_SVG;
} else {
type = contentType;
}
out = new Item(type, inputStream);
}
}
}
return out;
}
private Drawable handleSvg(InputStream stream) {
final Drawable out;
@ -292,4 +380,14 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
return new AsyncDrawableLoader(this);
}
}
private static class Item {
final String type;
final InputStream inputStream;
Item(String type, InputStream inputStream) {
this.type = type;
this.inputStream = inputStream;
}
}
}

View File

@ -18,8 +18,6 @@ dependencies {
compile COMMON_MARK
compile COMMON_MARK_STRIKETHROUGHT
compile COMMON_MARK_TABLE
compile 'ru.noties:debug:3.0.0@jar'
}
if (project.hasProperty('release')) {

View File

@ -0,0 +1,39 @@
package ru.noties.markwon;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
public class UrlProcessorAndroidAssets implements UrlProcessor {
private final UrlProcessorRelativeToAbsolute assetsProcessor
= new UrlProcessorRelativeToAbsolute("file:///android_asset/");
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);
} else {
if (processor != null) {
out = processor.process(destination);
} else {
out = destination;
}
}
return out;
}
}

View File

@ -38,7 +38,6 @@ import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import ru.noties.debug.Debug;
import ru.noties.markwon.SpannableConfiguration;
import ru.noties.markwon.renderer.html.SpannableHtmlParser;
import ru.noties.markwon.spans.AsyncDrawable;
@ -273,8 +272,6 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
@Override
public void visit(CustomNode customNode) {
// Log.e(null, String.valueOf(customNode));
if (customNode instanceof Strikethrough) {
final int length = builder.length();
@ -290,8 +287,6 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
final boolean handled;
Debug.i(node);
if (node instanceof TableBody) {
visitChildren(node);
tableRows = 0;
@ -306,8 +301,6 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
if (pendingTableRow != null) {
builder.append(' ');
Debug.i("adding a row: %d", tableRows);
final TableRowSpan span = new TableRowSpan(
configuration.theme(),
pendingTableRow,