diff --git a/README.md b/README.md index 25518070..dd397986 100644 --- a/README.md +++ b/README.md @@ -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_ Lorem ipsum dolor sit amet +--- + + ### Strong emphasis **Lorem ipsum dolor sit amet** @@ -66,6 +74,9 @@ __Lorem ipsum dolor sit amet__ Lorem ipsum dolor sit amet +--- + + ### Strike-through ~~Lorem ipsum dolor sit amet~~ @@ -97,9 +108,7 @@ __Lorem ipsum dolor sit amet__ click me - -### 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. OKA424342Y +--- + + ### 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 \ No newline at end of file +[library]: https://github.com/noties/Markwon/blob/master/library/README.md \ No newline at end of file diff --git a/app/src/main/assets/art/markwon_logo.png b/app/src/main/assets/art/markwon_logo.png new file mode 120000 index 00000000..16f26621 --- /dev/null +++ b/app/src/main/assets/art/markwon_logo.png @@ -0,0 +1 @@ +../../../../../art/markwon_logo.png \ No newline at end of file diff --git a/app/src/main/java/ru/noties/markwon/AppModule.java b/app/src/main/java/ru/noties/markwon/AppModule.java index 7d04a4db..09c5e230 100644 --- a/app/src/main/java/ru/noties/markwon/AppModule.java +++ b/app/src/main/java/ru/noties/markwon/AppModule.java @@ -42,6 +42,7 @@ class AppModule { return new OkHttpClient.Builder() .cache(new Cache(app.getCacheDir(), 1024L * 20)) .followRedirects(true) + .retryOnConnectionFailure(true) .build(); } diff --git a/app/src/main/java/ru/noties/markwon/MarkdownRenderer.java b/app/src/main/java/ru/noties/markwon/MarkdownRenderer.java index 1d0c4dc5..dbbe368a 100644 --- a/app/src/main/java/ru/noties/markwon/MarkdownRenderer.java +++ b/app/src/main/java/ru/noties/markwon/MarkdownRenderer.java @@ -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()); } diff --git a/art/markwon_logo.png b/art/markwon_logo.png new file mode 100644 index 00000000..817bacb4 Binary files /dev/null and b/art/markwon_logo.png differ diff --git a/library-image-loader/build.gradle b/library-image-loader/build.gradle index 68bfa8f1..847ef752 100644 --- a/library-image-loader/build.gradle +++ b/library-image-loader/build.gradle @@ -14,7 +14,6 @@ android { } dependencies { - compile project(':library') compile ANDROID_SVG compile ANDROID_GIF diff --git a/library-image-loader/src/main/java/ru/noties/markwon/il/AsyncDrawableLoader.java b/library-image-loader/src/main/java/ru/noties/markwon/il/AsyncDrawableLoader.java index c890baae..a9574d13 100644 --- a/library-image-loader/src/main/java/ru/noties/markwon/il/AsyncDrawableLoader.java +++ b/library-image-loader/src/main/java/ru/noties/markwon/il/AsyncDrawableLoader.java @@ -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,47 +104,32 @@ 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); - 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); - } else { - result = handleSimple(inputStream); - // just try to decode whatever it is - } - } finally { - try { - inputStream.close(); - } catch (IOException e) { - // no op - } - } + if (item != null + && item.inputStream != null) { + try { + 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(item.inputStream); + } + } finally { + try { + item.inputStream.close(); + } catch (IOException e) { + // no op } } } @@ -165,6 +157,102 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader { }); } + private Item fromFile(Uri uri) { + + final List 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; + } + } } diff --git a/library/build.gradle b/library/build.gradle index 043d931e..c384c1b6 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -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')) { diff --git a/library/src/main/java/ru/noties/markwon/UrlProcessorAndroidAssets.java b/library/src/main/java/ru/noties/markwon/UrlProcessorAndroidAssets.java new file mode 100644 index 00000000..82faf4e2 --- /dev/null +++ b/library/src/main/java/ru/noties/markwon/UrlProcessorAndroidAssets.java @@ -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; + } +} 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 8ac89cb0..757f735f 100644 --- a/library/src/main/java/ru/noties/markwon/renderer/SpannableMarkdownVisitor.java +++ b/library/src/main/java/ru/noties/markwon/renderer/SpannableMarkdownVisitor.java @@ -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,