diff --git a/app/build.gradle b/app/build.gradle
index 3c747a17..8078c22c 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -18,5 +18,7 @@ dependencies {
compile project(':library-renderer')
compile 'ru.noties:debug:3.0.0@jar'
compile 'com.squareup.picasso:picasso:2.5.2'
+ compile 'com.caverock:androidsvg:1.2.1'
+ compile 'pl.droidsonroids.gif:android-gif-drawable:1.2.7'
compile 'com.squareup.okhttp3:okhttp:3.8.0'
}
diff --git a/app/src/androidTest/java/ru/noties/markwon/ExampleInstrumentedTest.java b/app/src/androidTest/java/ru/noties/markwon/ExampleInstrumentedTest.java
deleted file mode 100644
index fac6b350..00000000
--- a/app/src/androidTest/java/ru/noties/markwon/ExampleInstrumentedTest.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package ru.noties.markwon;
-
-import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static org.junit.Assert.*;
-
-/**
- * Instrumentation test, which will execute on an Android device.
- *
- * @see Testing documentation
- */
-@RunWith(AndroidJUnit4.class)
-public class ExampleInstrumentedTest {
- @Test
- public void useAppContext() throws Exception {
- // Context of the app under test.
- Context appContext = InstrumentationRegistry.getTargetContext();
-
- assertEquals("ru.noties.markwon", appContext.getPackageName());
- }
-}
diff --git a/app/src/main/assets/scrollable.md b/app/src/main/assets/scrollable.md
index 80310183..b6b67ecb 100644
--- a/app/src/main/assets/scrollable.md
+++ b/app/src/main/assets/scrollable.md
@@ -1,4 +1,4 @@
-
+
[](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%22scrollable%22)
@@ -11,7 +11,7 @@
All GIFs here are taken from `sample` application module.
-
+
*Serving suggestion
diff --git a/app/src/main/java/ru/noties/markwon/AsyncDrawableLoader.java b/app/src/main/java/ru/noties/markwon/AsyncDrawableLoader.java
new file mode 100644
index 00000000..4a3f931f
--- /dev/null
+++ b/app/src/main/java/ru/noties/markwon/AsyncDrawableLoader.java
@@ -0,0 +1,126 @@
+package ru.noties.markwon;
+
+import android.graphics.Picture;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.PictureDrawable;
+import android.net.Uri;
+import android.support.annotation.NonNull;
+import android.widget.TextView;
+
+import com.caverock.androidsvg.SVG;
+import com.squareup.picasso.Picasso;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import okhttp3.OkHttpClient;
+import pl.droidsonroids.gif.GifDrawable;
+import ru.noties.debug.Debug;
+import ru.noties.markwon.spans.AsyncDrawable;
+
+public class AsyncDrawableLoader implements AsyncDrawable.Loader {
+
+ private final TextView view;
+ private final Picasso picasso;
+ private final OkHttpClient client;
+ private final ExecutorService executorService;
+ private final Map> requests;
+
+ // sh*t..
+ public AsyncDrawableLoader(TextView view) {
+ this.view = view;
+ this.picasso = new Picasso.Builder(view.getContext())
+ .listener(new Picasso.Listener() {
+ @Override
+ public void onImageLoadFailed(Picasso picasso, Uri uri, Exception exception) {
+ Debug.e(exception, picasso, uri);
+ }
+ })
+ .build();
+ this.client = new OkHttpClient();
+ this.executorService = Executors.newCachedThreadPool();
+ this.requests = new HashMap<>(3);
+ }
+
+ @Override
+ public void load(@NonNull String destination, @NonNull AsyncDrawable drawable) {
+
+ Debug.i("destination: %s", destination);
+
+ if (destination.endsWith(".svg")) {
+ // load svg
+ requests.put(destination, loadSvg(destination, drawable));
+ } else if (destination.endsWith(".gif")) {
+ requests.put(destination, loadGif(destination, drawable));
+ } else {
+ picasso
+ .load(destination)
+ .tag(destination)
+ .into(new TextViewTarget(view, drawable));
+ }
+ }
+
+ @Override
+ public void cancel(@NonNull String destination) {
+ Debug.i("destination: %s", destination);
+ picasso.cancelTag(destination);
+
+ final Future> future = requests.get(destination);
+ if (future != null) {
+ future.cancel(true);
+ }
+ }
+
+ private Future> loadSvg(final String destination, final AsyncDrawable asyncDrawable) {
+ return executorService.submit(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ final URL url = new URL(destination);
+ final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ final InputStream inputStream = connection.getInputStream();
+ final SVG svg = SVG.getFromInputStream(inputStream);
+ final Picture picture = svg.renderToPicture();
+ final Drawable drawable = new PictureDrawable(picture);
+ drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
+ asyncDrawable.setResult(drawable);
+
+ } catch (Throwable t) {
+ Debug.e(t);
+ }
+ }
+ });
+ }
+
+ private Future> loadGif(final String destination, final AsyncDrawable asyncDrawable) {
+ return executorService.submit(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ final URL url = new URL(destination);
+ final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ final InputStream inputStream = connection.getInputStream();
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ final byte[] buffer = new byte[1024 * 8];
+ int read;
+ while ((read = inputStream.read(buffer, 0, buffer.length)) != -1) {
+ baos.write(buffer, 0, read);
+ }
+ final GifDrawable drawable = new GifDrawable(baos.toByteArray());
+ drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
+ asyncDrawable.setResult(drawable);
+ } catch (Throwable t) {
+ Debug.e(t);
+ }
+ }
+ });
+ }
+}
diff --git a/app/src/main/java/ru/noties/markwon/MainActivity.java b/app/src/main/java/ru/noties/markwon/MainActivity.java
index 2aec835e..96e811a1 100644
--- a/app/src/main/java/ru/noties/markwon/MainActivity.java
+++ b/app/src/main/java/ru/noties/markwon/MainActivity.java
@@ -3,7 +3,6 @@ package ru.noties.markwon;
import android.app.Activity;
import android.os.Bundle;
import android.os.SystemClock;
-import android.support.annotation.NonNull;
import android.text.method.LinkMovementMethod;
import android.widget.TextView;
@@ -33,15 +32,7 @@ public class MainActivity extends Activity {
final TextView textView = (TextView) findViewById(R.id.activity_main);
-//
-// final Picasso picasso = new Picasso.Builder(this)
-// .listener(new Picasso.Listener() {
-// @Override
-// public void onImageLoadFailed(Picasso picasso, Uri uri, Exception exception) {
-// Debug.i(exception, uri);
-// }
-// })
-// .build();
+ final AsyncDrawable.Loader loader = new AsyncDrawableLoader(textView);
new Thread(new Runnable() {
@Override
@@ -75,17 +66,7 @@ public class MainActivity extends Activity {
final long start = SystemClock.uptimeMillis();
final SpannableConfiguration configuration = SpannableConfiguration.builder(MainActivity.this)
- .asyncDrawableLoader(new AsyncDrawable.Loader() {
- @Override
- public void load(@NonNull String destination, @NonNull AsyncDrawable drawable) {
- Debug.i("destination: %s, drawable: %s", destination, drawable);
- }
-
- @Override
- public void cancel(@NonNull String destination) {
- Debug.i("destination: %s", destination);
- }
- })
+ .asyncDrawableLoader(loader)
.build();
final CharSequence text = Markwon.markdown(configuration, md);
diff --git a/app/src/main/java/ru/noties/markwon/Markwon.java b/app/src/main/java/ru/noties/markwon/Markwon.java
deleted file mode 100644
index 2a76c84c..00000000
--- a/app/src/main/java/ru/noties/markwon/Markwon.java
+++ /dev/null
@@ -1,7 +0,0 @@
-//package ru.noties.markwon;
-//
-//public class Markwon {
-//
-// // todo, annotation processor to PRE_COMPILE markdown!! no... multiple lnguages and you are out, forget about it
-// // view for debugging (to view in preview) x3!
-//}
diff --git a/app/src/main/java/ru/noties/markwon/SpannableRenderer.java b/app/src/main/java/ru/noties/markwon/SpannableRenderer.java
deleted file mode 100644
index aee93ac4..00000000
--- a/app/src/main/java/ru/noties/markwon/SpannableRenderer.java
+++ /dev/null
@@ -1,444 +0,0 @@
-//package ru.noties.markwon;
-//
-//import android.graphics.Canvas;
-//import android.graphics.ColorFilter;
-//import android.graphics.drawable.Drawable;
-//import android.os.Handler;
-//import android.os.Looper;
-//import android.support.annotation.IntRange;
-//import android.support.annotation.NonNull;
-//import android.support.annotation.Nullable;
-//import android.text.Html;
-//import android.text.SpannableStringBuilder;
-//import android.text.Spanned;
-//import android.text.style.AbsoluteSizeSpan;
-//import android.text.style.StrikethroughSpan;
-//import android.text.style.URLSpan;
-//
-//import org.commonmark.ext.gfm.strikethrough.Strikethrough;
-//import org.commonmark.node.AbstractVisitor;
-//import org.commonmark.node.BlockQuote;
-//import org.commonmark.node.BulletList;
-//import org.commonmark.node.Code;
-//import org.commonmark.node.CustomBlock;
-//import org.commonmark.node.CustomNode;
-//import org.commonmark.node.Document;
-//import org.commonmark.node.Emphasis;
-//import org.commonmark.node.FencedCodeBlock;
-//import org.commonmark.node.HardLineBreak;
-//import org.commonmark.node.Heading;
-//import org.commonmark.node.HtmlBlock;
-//import org.commonmark.node.HtmlInline;
-//import org.commonmark.node.Image;
-//import org.commonmark.node.IndentedCodeBlock;
-//import org.commonmark.node.Link;
-//import org.commonmark.node.ListItem;
-//import org.commonmark.node.Node;
-//import org.commonmark.node.OrderedList;
-//import org.commonmark.node.Paragraph;
-//import org.commonmark.node.SoftLineBreak;
-//import org.commonmark.node.StrongEmphasis;
-//import org.commonmark.node.Text;
-//import org.commonmark.node.ThematicBreak;
-//import org.commonmark.renderer.Renderer;
-//
-//import java.util.ArrayDeque;
-//import java.util.Arrays;
-//import java.util.Deque;
-//
-//import ru.noties.debug.Debug;
-//import ru.noties.markwon.spans.BlockQuoteSpan;
-//import ru.noties.markwon.spans.CodeSpan;
-//import ru.noties.markwon.spans.AsyncDrawableSpan;
-//import ru.noties.markwon.spans.EmphasisSpan;
-//import ru.noties.markwon.spans.BulletListItemSpan;
-//import ru.noties.markwon.spans.StrongEmphasisSpan;
-//import ru.noties.markwon.spans.SubScriptSpan;
-//import ru.noties.markwon.spans.SuperScriptSpan;
-//import ru.noties.markwon.spans.ThematicBreakSpan;
-//
-//public class SpannableRenderer implements Renderer {
-//
-// // todo, util to extract all drawables and attach to textView (gif, animations, lazyLoading, etc)
-//
-// @Override
-// public void render(Node node, Appendable output) {
-//
-// }
-//
-// @Override
-// public String render(Node node) {
-// // hm.. doesn't make sense to render to string
-// throw null;
-// }
-//
-// public CharSequence _render(Node node) {
-// final SpannableStringBuilder builder = new SpannableStringBuilder();
-// node.accept(new SpannableNodeRenderer(builder));
-// return builder;
-// }
-//
-// private static class SpannableNodeRenderer extends AbstractVisitor {
-//
-//// private static final float[] HEADING_SIZES = {
-//// 1.5f, 1.4f, 1.3f, 1.2f, 1.1f, 1f,
-//// };
-//
-// private final SpannableStringBuilder builder;
-//
-// private int blockQuoteIndent;
-// private int listLevel;
-//
-// SpannableNodeRenderer(SpannableStringBuilder builder) {
-// this.builder = builder;
-// }
-//
-// @Override
-// public void visit(HardLineBreak hardLineBreak) {
-// // todo
-// Debug.i(hardLineBreak);
-// }
-//
-// @Override
-// public void visit(Text text) {
-// builder.append(text.getLiteral());
-// }
-//
-// @Override
-// public void visit(StrongEmphasis strongEmphasis) {
-// final int length = builder.length();
-// visitChildren(strongEmphasis);
-// builder.setSpan(new StrongEmphasisSpan(), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-// }
-//
-// @Override
-// public void visit(Emphasis emphasis) {
-// final int length = builder.length();
-// visitChildren(emphasis);
-// builder.setSpan(new EmphasisSpan(), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-// }
-//
-// @Override
-// public void visit(IndentedCodeBlock indentedCodeBlock) {
-// // todo
-// Debug.i(indentedCodeBlock);
-// }
-//
-// @Override
-// public void visit(BlockQuote blockQuote) {
-// builder.append('\n');
-// final int length = builder.length();
-// blockQuoteIndent += 1;
-// visitChildren(blockQuote);
-// builder.setSpan(new BlockQuoteSpan(blockQuoteIndent), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-// blockQuoteIndent -= 1;
-// }
-//
-// @Override
-// public void visit(Code code) {
-// final int length = builder.length();
-// builder.append(code.getLiteral());
-//// builder.setSpan(new ForegroundColorSpan(0xff00ff00), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-// builder.setSpan(new CodeSpan(false, length, builder.length()), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-// }
-//
-// @Override
-// public void visit(BulletList bulletList) {
-// Debug.i(bulletList, bulletList.getBulletMarker());
-// visitChildren(bulletList);
-// }
-//
-// @Override
-// public void visit(ListItem listItem) {
-// Debug.i(listItem);
-//// builder.append('\n');
-// if (builder.charAt(builder.length() - 1) != '\n') {
-// builder.append('\n');
-// }
-// final int length = builder.length();
-// blockQuoteIndent += 1;
-// listLevel += 1;
-// visitChildren(listItem);
-//// builder.setSpan(new BulletSpan(4, 0xff0000ff), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-// builder.setSpan(new BulletListItemSpan(blockQuoteIndent, listLevel > 1, length), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-// blockQuoteIndent -= 1;
-// listLevel -= 1;
-// }
-//
-// @Override
-// public void visit(ThematicBreak thematicBreak) {
-// final int length = builder.length();
-// builder.append('\n')
-// .append(' '); // without space it won't render
-// builder.setSpan(new ThematicBreakSpan(), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-// builder.append('\n');
-// }
-//
-// @Override
-// public void visit(OrderedList orderedList) {
-// Debug.i(orderedList, orderedList.getDelimiter(), orderedList.getStartNumber());
-// // todo, ordering numbers
-// super.visit(orderedList);
-// }
-//
-// @Override
-// public void visit(SoftLineBreak softLineBreak) {
-// Debug.i(softLineBreak);
-// }
-//
-// @Override
-// public void visit(Heading heading) {
-// Debug.i(heading);
-// if (builder.length() != 0 && builder.charAt(builder.length() - 1) != '\n') {
-// builder.append('\n');
-// }
-// final int length = builder.length();
-// visitChildren(heading);
-// final int max = 120;
-// final int one = 20; // total is 6
-// final int size = max - ((heading.getLevel() - 1) * one);
-// builder.setSpan(new AbsoluteSizeSpan(size), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-// builder.append('\n');
-// }
-//
-// @Override
-// public void visit(FencedCodeBlock fencedCodeBlock) {
-// builder.append('\n');
-// final int length = builder.length();
-// builder.append(fencedCodeBlock.getLiteral());
-// builder.setSpan(new CodeSpan(true, length, builder.length() - 1), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-// }
-//
-// @Override
-// public void visit(Paragraph paragraph) {
-// Debug.i(paragraph);
-// if (listLevel == 0
-// && blockQuoteIndent == 0) {
-// builder.append('\n')
-// .append('\n');
-// }
-// visitChildren(paragraph);
-//
-// if (listLevel == 0
-// && blockQuoteIndent == 0) {
-// builder.append('\n')
-// .append('\n');
-// }
-// }
-//
-//// private int htmlStart = -1;
-// private final Deque htmlStack = new ArrayDeque<>();
-//
-// private static class HtmlInlineItem {
-//
-// final int start;
-// final String tag;
-//
-// private HtmlInlineItem(int start, String tag) {
-// this.start = start;
-// this.tag = tag;
-// }
-// }
-//
-// @Override
-// public void visit(HtmlInline htmlInline) {
-//
-//// Debug.i(htmlInline, htmlStart);
-//// Debug.i(htmlInline.getLiteral(), htmlInline.toString());
-//
-// // okay, it's seems that we desperately need to understand if it's opening tag or closing
-//
-// final HtmlTag tag = parseTag(htmlInline.getLiteral());
-//
-// Debug.i(htmlInline.getLiteral(), tag);
-//
-// if (tag != null) {
-// Debug.i("tag: %s, closing: %s", tag.tag, tag.closing);
-// if (!tag.closing) {
-// htmlStack.push(new HtmlInlineItem(builder.length(), tag.tag));
-// visitChildren(htmlInline);
-// } else {
-// final HtmlInlineItem item = htmlStack.pop();
-// final int start = item.start;
-// final int end = builder.length();
-// // here, additionally, we can render some tags ourselves (sup/sub)
-// if ("sup".equals(item.tag)) {
-// builder.setSpan(new SuperScriptSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-// } else if("sub".equals(item.tag)) {
-// builder.setSpan(new SubScriptSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-// } else if("del".equals(item.tag)) {
-// // weird, but `Html` class does not return a spannable for `o`
-// // seems like a bug
-// builder.setSpan(new StrikethroughSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-// } else {
-// final String html = "<" + item.tag + ">" + (builder.subSequence(start, end).toString()) + "" + item.tag + ">";
-// final Spanned spanned = Html.fromHtml(html);
-// final Object[] spans = spanned.getSpans(0, spanned.length(), Object.class);
-//
-// Debug.i("html: %s, start: %d, end: %d, spans: %s", html, start, end, Arrays.toString(spans));
-//
-// if (spans != null
-// && spans.length > 0) {
-// for (Object span: spans) {
-// Debug.i(span);
-// builder.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-// }
-// }
-// }
-// }
-// } else {
-// super.visit(htmlInline);
-// }
-// }
-//
-// private static class HtmlTag {
-// final String tag;
-// final boolean closing;
-// HtmlTag(String tag, boolean closing) {
-// this.tag = tag;
-// this.closing = closing;
-// }
-// @Override
-// public String toString() {
-// return "HtmlTag{" +
-// "tag='" + tag + '\'' +
-// ", closing=" + closing +
-// '}';
-// }
-// }
-//
-// private static HtmlTag parseTag(String in) {
-//
-// final HtmlTag out;
-//
-// final int length = in != null
-// ? in.length()
-// : 0;
-//
-// Debug.i(in, length);
-//
-// if (length == 0 || length < 3) {
-// out = null;
-// } else {
-//
-// final boolean closing = '<' == in.charAt(0) && '/' == in.charAt(1);
-// final String tag = closing
-// ? in.substring(2, in.length() - 1)
-// : in.substring(1, in.length() - 1);
-// out = new HtmlTag(tag, closing);
-// }
-//
-// return out;
-// }
-//
-// @Override
-// public void visit(HtmlBlock htmlBlock) {
-// // interestring thing... what is it also?
-// Debug.i(htmlBlock);
-// }
-//
-// @Override
-// public void visit(CustomBlock customBlock) {
-// // not supported, what is it anyway?
-// Debug.i(customBlock);
-// }
-//
-// @Override
-// public void visit(Document document) {
-// // the whole document, no need to do anything
-// Debug.i(document);
-// super.visit(document);
-// }
-//
-// @Override
-// public void visit(Link link) {
-// Debug.i(link);
-// final int length = builder.length();
-// visitChildren(link);
-// builder.setSpan(new URLSpan(link.getDestination()), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-// }
-//
-// @Override
-// public void visit(Image image) {
-// // not supported... maybe for now?
-// Debug.i(image);
-// final int length = builder.length();
-// super.visit(image);
-//
-//// final int length = builder.length();
-// final TestDrawable drawable = new TestDrawable();
-// final AsyncDrawableSpan span = new AsyncDrawableSpan(drawable);
-// builder.append(" ");
-// builder.setSpan(span, length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-// }
-//
-// @Override
-// public void visit(CustomNode customNode) {
-//
-// Debug.i(customNode);
-//
-// if (customNode instanceof Strikethrough) {
-// final int length = builder.length();
-// visitChildren(customNode);
-// builder.setSpan(new StrikethroughSpan(), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-// } else {
-// super.visit(customNode);
-// }
-// }
-// }
-//
-//
-// private static class TestDrawable extends Drawable {
-//
-// private final Handler handler = new Handler(Looper.getMainLooper());
-// private boolean called;
-//
-// TestDrawable() {
-// setBounds(0, 0, 50, 50);
-// }
-//
-// @Override
-// public void draw(@NonNull final Canvas canvas) {
-// canvas.clipRect(getBounds());
-// if (!called) {
-// canvas.drawColor(0xFF00ff00);
-// handler.removeCallbacksAndMessages(null);
-// handler.postDelayed(new Runnable() {
-// @Override
-// public void run() {
-// called = true;
-// setBounds(0, 0, 400, 400);
-// invalidateSelf();
-// }
-// }, 2000L);
-// } else {
-// canvas.drawColor(0xFFff0000);
-// }
-// }
-//
-// @Override
-// public void setAlpha(@IntRange(from = 0, to = 255) int alpha) {
-//
-// }
-//
-// @Override
-// public void setColorFilter(@Nullable ColorFilter colorFilter) {
-//
-// }
-//
-// @Override
-// public int getOpacity() {
-// return 0;
-// }
-//
-// @Override
-// public int getIntrinsicWidth() {
-// return called ? 400 : 50;
-// }
-//
-// @Override
-// public int getIntrinsicHeight() {
-// return called ? 400 : 50;
-// }
-// }
-//}
diff --git a/app/src/main/java/ru/noties/markwon/TextViewTarget.java b/app/src/main/java/ru/noties/markwon/TextViewTarget.java
new file mode 100644
index 00000000..c491beee
--- /dev/null
+++ b/app/src/main/java/ru/noties/markwon/TextViewTarget.java
@@ -0,0 +1,74 @@
+package ru.noties.markwon;
+
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.widget.TextView;
+
+import com.squareup.picasso.Picasso;
+import com.squareup.picasso.Target;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import ru.noties.markwon.spans.AsyncDrawable;
+
+public class TextViewTarget implements Target {
+
+ private final TextView view;
+ private final AsyncDrawable asyncDrawable;
+
+ public TextViewTarget(TextView view, AsyncDrawable asyncDrawable) {
+ this.view = view;
+ this.asyncDrawable = asyncDrawable;
+
+ attach();
+ }
+
+ @Override
+ public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
+ if (bitmap != null) {
+ final Drawable drawable = new BitmapDrawable(view.getResources(), bitmap);
+ drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
+ asyncDrawable.setResult(drawable);
+ }
+ detach();
+ }
+
+ @Override
+ public void onBitmapFailed(Drawable errorDrawable) {
+ if (errorDrawable != null) {
+ asyncDrawable.setResult(errorDrawable);
+ }
+ detach();
+ }
+
+ @Override
+ public void onPrepareLoad(Drawable placeHolderDrawable) {
+ if (placeHolderDrawable != null) {
+ asyncDrawable.setResult(placeHolderDrawable);
+ }
+ }
+
+ private void attach() {
+
+ // amazing stuff here, in order to keep this target alive (picasso stores target in a WeakReference)
+ // we need to do this
+
+ //noinspection unchecked
+ List list = (List) view.getTag(R.id.amazing);
+ if (list == null) {
+ list = new ArrayList<>(2);
+ view.setTag(R.id.amazing, list);
+ }
+ list.add(this);
+ }
+
+ private void detach() {
+ //noinspection unchecked
+ final List list = (List) view.getTag(R.id.amazing);
+ if (list != null) {
+ list.remove(this);
+ }
+ }
+}
diff --git a/app/src/main/java/ru/noties/markwon/spans2/BlockQuoteSpan.java b/app/src/main/java/ru/noties/markwon/spans2/BlockQuoteSpan.java
deleted file mode 100644
index 68937347..00000000
--- a/app/src/main/java/ru/noties/markwon/spans2/BlockQuoteSpan.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package ru.noties.markwon.spans2;
-
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.text.Layout;
-import android.text.style.LeadingMarginSpan;
-import ru.noties.debug.Debug;
-
-public class BlockQuoteSpan implements LeadingMarginSpan {
-
- private final int indent;
-
- public BlockQuoteSpan(int indent) {
- this.indent = indent;
- }
-
- @Override
- public int getLeadingMargin(boolean first) {
- return 24;
- }
-
- @Override
- public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, int top, int baseline, int bottom, CharSequence text, int start, int end, boolean first, Layout layout) {
-// Debug.i("x: %d, dir: %d, top: %d, baseline: %d, bottom: %d, first: %s",
-// x, dir, top, baseline, bottom, first
-// );
-
- final int save = c.save();
- try {
- final int left = 24 * (indent - 1);
-// final RectF rectF = new RectF(0, 0, 16, 16);
- final Rect rect = new Rect(left, top, left + 8, bottom);
- final Paint paint = new Paint();
- paint.setStyle(Paint.Style.FILL);
- paint.setColor(0xFFf0f0f0);
- c.drawRect(rect, paint);
-// c.translate(x, .0F);
-// c.drawOval(rectF, paint);
- } finally {
- c.restoreToCount(save);
- }
- }
-}
diff --git a/app/src/main/java/ru/noties/markwon/spans2/CodeSpan.java b/app/src/main/java/ru/noties/markwon/spans2/CodeSpan.java
deleted file mode 100644
index 485592b3..00000000
--- a/app/src/main/java/ru/noties/markwon/spans2/CodeSpan.java
+++ /dev/null
@@ -1,270 +0,0 @@
-package ru.noties.markwon.spans2;
-
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.support.annotation.IntRange;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.text.style.ReplacementSpan;
-
-import ru.noties.debug.Debug;
-
-// we will use Replacement span because code blocks cannot contain other markdown
-// so we will render the string (not a charSequence with possible metric affecting spans)
-public class CodeSpan extends ReplacementSpan/* implements LeadingMarginSpan*/ {
-
- private final boolean multiline;
- private final int start;
- private final int end;
-
- private final Rect rect = new Rect();
- private final Rect borderRect = new Rect();
- private final Paint paint = new Paint();
-
- public CodeSpan(boolean multiline, int start, int end) {
- this.multiline = multiline;
- this.start = start;
- this.end = end;
-
- paint.setStyle(Paint.Style.FILL);
- }
-
- @Override
- public int getSize(
- @NonNull Paint paint,
- CharSequence text,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end,
- @Nullable Paint.FontMetricsInt fm
- ) {
-
- final CharSequence cs = text.subSequence(start, end);
- final int width = 32 + (int) (paint.measureText(cs, 0, cs.length()) + .5F);
-
-// final StaticLayout layout = new StaticLayout(cs, new TextPaint(paint), 10000, Layout.Alignment.ALIGN_NORMAL, 1.F, .0F, false);
-// final float width = layout.getLineWidth(0);
-// final int out = 32 + (int) (width + .5F);
-
-// Debug.i("text: %s, width: %s", cs, width);
-
- if (fm != null) {
- // we add a padding top & bottom
- Debug.i("a: %s, d: %s, t: %s, b: %s", fm.ascent, fm.descent, fm.top, fm.bottom);
- final float ratio = .62F; // golden ratio
- fm.ascent = fm.ascent - 8;
- fm.descent = (int) (-fm.ascent * ratio);
- fm.top = fm.ascent;
- fm.bottom = fm.descent;
- }
-
- return width;
- }
-
- @Override
- public void draw(
- @NonNull Canvas canvas,
- CharSequence text,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end,
- float x,
- int top,
- int y,
- int bottom,
- @NonNull Paint paint
- ) {
-
- final int left = (int) (x + .5F);
-
- final int right;
- if (multiline) {
- right = canvas.getWidth();
- } else {
- final int width = (16 * 2) + (int) (paint.measureText(text, start, end) + .5F);
- right = left + width;
- }
-
- rect.set(left, top, right, bottom);
-
-
- // okay, draw background first
- drawBackground(canvas);
-
- // then, if any, draw borders
- drawBorders(canvas, this.start == start, this.end == end);
-
- // draw text
- // y center position
- final int b = bottom - ((bottom - top) / 2) - (int) ((paint.descent() + paint.ascent()) / 2);
-// if (config.textColor != 0) {
-// // we will use Paint object that is used to draw all the text (textSize, textColor, typeface, etc)
-// paint.setColor(config.textColor);
-// }
- canvas.drawText(text, start, end, x + 16, b, paint);
-
-
-// Debug.i("text: %s, x: %s, top: %s, y: %s, bottom: %s", text.subSequence(start, end), x, top, y, bottom);
-//
-// final CharSequence cs = text.subSequence(start, end);
-//
-// final int width = 32 + (int) (paint.measureText(cs, 0, cs.length()) + .5F);
-//
-// final int left = (int) (x + .5F);
-// final int right = multiline
-// ? canvas.getWidth()
-// : left + width;
-//
-// final Rect rect = new Rect(
-// left,
-// top,
-// right,
-// bottom
-// );
-//
-// final Paint p = new Paint();
-// p.setStyle(Paint.Style.FILL);
-// p.setColor(0x80ff0000);
-// canvas.drawRect(rect, p);
-//
-// // y center position
-// final int b = bottom - ((bottom - top) / 2) - (int) ((paint.descent() + paint.ascent()) / 2);
-// p.setColor(0xFF000000);
-// canvas.drawText(cs, 0, cs.length(), x + 16, b, paint);
- }
-
- private void drawBackground(Canvas canvas) {
-// final int color = config.backgroundColor;
-// if (color != 0) {
- paint.setColor(0x40ff0000);
- canvas.drawRect(rect, paint);
-// }
- }
-
- private void drawBorders(Canvas canvas, boolean top, boolean bottom) {
-
- final int color = 0xFFff0000;
- final int width = 4;
-// if (color == 0
-// || width == 0) {
-// return;
-// }
-
- paint.setColor(color);
-
- // left and right are always drawn
-
- // LEFT
- borderRect.set(rect.left, rect.top, rect.left + width, rect.bottom);
- canvas.drawRect(borderRect, paint);
-
- // RIGHT
- borderRect.set(rect.right - width, rect.top, rect.right, rect.bottom);
- canvas.drawRect(borderRect, paint);
-
- // TOP
- if (top) {
- borderRect.set(rect.left, rect.top, rect.right, rect.top + width);
- canvas.drawRect(borderRect, paint);
- }
-
- // BOTTOM
- if (bottom) {
- borderRect.set(rect.left, rect.bottom - width, rect.right, rect.bottom);
- canvas.drawRect(borderRect, paint);
- }
- }
-
-
-// @Override
-// public int getLeadingMargin(boolean first) {
-// return 1;
-// }
-//
-// @Override
-// public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, int top, int baseline, int bottom, CharSequence text, int start, int end, boolean first, Layout layout) {
-//// Debug.i("x: %d, top: %d, bottom: %d", x, top, bottom);
-//
-//// Debug.i("this: [%d, %d], came: [%d, %d]", this.start, this.end, start, end);
-// Debug.i("x: %d, canvas: [%d-%d], text: %s", x, c.getWidth(), c.getHeight(), (text.subSequence(start, end)));
-//
-// // the thing is... if we do not draw, then text won't be drawn also
-// final Rect rect = new Rect();
-//
-// final Paint paint = new Paint();
-// paint.setStyle(Paint.Style.FILL);
-// paint.setColor(0xffcccccc);
-//
-// rect.set(x, top, c.getWidth(), bottom);
-// c.drawRect(rect, paint);
-//
-// if (this.start == start) {
-// this.top = top;
-//
-//// final int save = c.save();
-//// try {
-//// c.drawColor(0x00ffffff);
-//// } finally {
-//// c.restoreToCount(save);
-//// }
-//
-//// c.drawColor(0x00ffffff);
-// }
-//
-// if (this.end == end) {
-// // draw borders
-// final Rect r = new Rect(x + 1, this.top, c.getWidth() - x, bottom);
-// final Paint pa = new Paint();
-// pa.setStyle(Paint.Style.STROKE);
-// pa.setColor(0xff999999);
-// c.drawRect(r, pa);
-// }
-//// rect.inset((int) paint.getStrokeWidth(), (int) paint.getStrokeWidth());
-//// paint.setStyle(Paint.Style.STROKE);
-//// paint.setColor(0xff333333);
-//// c.drawRect(rect, paint);
-// }
-
-// @Override
-// public void chooseHeight(CharSequence text, int start, int end, int spanstartv, int v, Paint.FontMetricsInt fm) {
-//// int ht = mDrawable.getIntrinsicHeight();
-////
-//// int need = ht - (v + fm.descent - fm.ascent - istartv);
-//// if (need > 0)
-//// fm.descent += need;
-////
-//// need = ht - (v + fm.bottom - fm.top - istartv);
-//// if (need > 0)
-//// fm.bottom += need;
-////
-//
-//// final int lineOffset = v - spanstartv;
-//// final int desired = 128;
-//// final int currentLineHeight = -fm.ascent + fm.descent;
-//// final float ratio = (float) desired / currentLineHeight;
-////
-//// Debug.i("fm, came: %s", fm);
-//// Debug.i("lineOffset: %d, current: %d, ratio: %s", lineOffset, currentLineHeight, ratio);
-////
-//// fm.ascent = (int) (ratio * fm.ascent + .5F);
-//// fm.descent = (int) (ratio * fm.descent + .5F);
-////
-//// Debug.i("fm, out: %s", fm);
-//
-//// Debug.i("top: %d, bottom: %d, ascent: %d, descent: %d", fm.top, fm.bottom, fm.ascent, fm.descent);
-//// Debug.i("lineHeight: %d, v: %d, spanstartv: %d", lineOffset, v, spanstartv);
-////
-//// final int h = 128;
-//// final int descentNeed = h - (v + fm.descent - fm.ascent - spanstartv);
-//// if (descentNeed > 0) {
-//// fm.ascent -= descentNeed / 2;
-//// fm.descent += descentNeed / 2;
-//// }
-//// final int bottomNeed = h - (v + fm.bottom - fm.top - spanstartv);
-//// if (bottomNeed > 0) {
-//// fm.top -= bottomNeed;
-//// fm.bottom += bottomNeed;
-//// }
-////
-//// Debug.i("out, ascent: %d, descent: %d, bottom: %d", fm.ascent, fm.descent, fm.bottom);
-// }
-}
diff --git a/app/src/main/java/ru/noties/markwon/spans2/DrawableSpan.java b/app/src/main/java/ru/noties/markwon/spans2/DrawableSpan.java
deleted file mode 100644
index 5da2fbce..00000000
--- a/app/src/main/java/ru/noties/markwon/spans2/DrawableSpan.java
+++ /dev/null
@@ -1,97 +0,0 @@
-package ru.noties.markwon.spans2;
-
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.IntDef;
-import android.support.annotation.IntRange;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.text.style.ReplacementSpan;
-
-public class DrawableSpan extends ReplacementSpan {
-
- @IntDef({ ALIGN_BOTTOM, ALIGN_BASELINE, ALIGN_CENTER })
- @interface Alignment {}
-
- public static final int ALIGN_BOTTOM = 0;
- public static final int ALIGN_BASELINE = 1;
- public static final int ALIGN_CENTER = 2;
-
- private final Drawable drawable;
- private final int alignment;
-
- public DrawableSpan(@NonNull Drawable drawable) {
- this(drawable, ALIGN_BOTTOM);
- }
-
- public DrawableSpan(@NonNull Drawable drawable, @Alignment int alignment) {
- this.drawable = drawable;
- this.alignment = alignment;
-
- // additionally set intrinsic bounds if empty
- final Rect rect = drawable.getBounds();
- if (rect.isEmpty()) {
- drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
- }
- }
-
- @Override
- public int getSize(
- @NonNull Paint paint,
- CharSequence text,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end,
- @Nullable Paint.FontMetricsInt fm) {
-
- final Rect rect = drawable.getBounds();
-
- if (fm != null) {
- fm.ascent = -rect.bottom;
- fm.descent = 0;
-
- fm.top = fm.ascent;
- fm.bottom = 0;
- }
-
- return rect.right;
- }
-
- @Override
- public void draw(
- @NonNull Canvas canvas,
- CharSequence text,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end,
- float x,
- int top,
- int y,
- int bottom,
- @NonNull Paint paint) {
-
- final Drawable drawable = this.drawable;
-
- final int b = bottom - drawable.getBounds().bottom;
-
- final int save = canvas.save();
- try {
- final int translationY;
- if (ALIGN_CENTER == alignment) {
- translationY = (int) (b / 2.F + .5F);
- } else if (ALIGN_BASELINE == alignment) {
- translationY = b - paint.getFontMetricsInt().descent;
- } else {
- translationY = b;
- }
- canvas.translate(x, translationY);
- drawable.draw(canvas);
- } finally {
- canvas.restoreToCount(save);
- }
- }
-
- public Drawable getDrawable() {
- return drawable;
- }
-}
diff --git a/app/src/main/java/ru/noties/markwon/spans2/DrawableSpanUtils.java b/app/src/main/java/ru/noties/markwon/spans2/DrawableSpanUtils.java
deleted file mode 100644
index 8b3fe20b..00000000
--- a/app/src/main/java/ru/noties/markwon/spans2/DrawableSpanUtils.java
+++ /dev/null
@@ -1,116 +0,0 @@
-package ru.noties.markwon.spans2;
-
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.SystemClock;
-import android.support.annotation.NonNull;
-import android.text.Spanned;
-import android.view.View;
-import android.widget.TextView;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import ru.noties.debug.Debug;
-
-public class DrawableSpanUtils {
-
- // this method is not completely valid because DynamicDrawableSpan stores
- // a drawable in weakReference & it could easily be freed, thus we might need
- // to re-schedule a new one, but we have no means to do it
- public static void scheduleDrawables(@NonNull final TextView textView) {
-
- final CharSequence cs = textView.getText();
- final int length = cs != null
- ? cs.length()
- : 0;
- if (length == 0 || !(cs instanceof Spanned)) {
- return;
- }
-
- final Object[] spans = ((Spanned) cs).getSpans(0, length, Object.class);
- if (spans != null
- && spans.length > 0) {
-
- final List list = new ArrayList<>(2);
-
- for (Object span: spans) {
- if (span instanceof DrawableSpan) {
- list.add(((DrawableSpan) span).getDrawable());
- }
- }
-
- if (list.size() > 0) {
- textView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View v) {
- // can it happen that the same view first detached & them attached with all previous content? hm..
- // no op for now
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) {
- // remove callbacks...
- textView.removeOnAttachStateChangeListener(this);
- for (Drawable drawable: list) {
- drawable.setCallback(null);
- }
- }
- });
-
- for (Drawable drawable: list) {
- drawable.setCallback(new DrawableCallbackImpl(textView, drawable.getBounds()));
- }
- }
- }
- }
-
- private DrawableSpanUtils() {}
-
- private static class DrawableCallbackImpl implements Drawable.Callback {
-
- private final TextView view;
- private Rect previousBounds;
-
- DrawableCallbackImpl(TextView view, Rect initialBounds) {
- this.view = view;
- this.previousBounds = new Rect(initialBounds);
- }
-
- @Override
- public void invalidateDrawable(@NonNull Drawable who) {
-
- // okay... teh thing is IF we do not change bounds size, normal invalidate would do
- // but if the size has changed, then we need to update the whole layout...
-
- final Rect rect = who.getBounds();
-
- if (!previousBounds.equals(rect)) {
- // the only method that seems to work when bounds have changed
- view.setText(view.getText());
- previousBounds = new Rect(rect);
- } else {
- // if bounds are the same then simple invalidate would do
- final int scrollX = view.getScrollX();
- final int scrollY = view.getScrollY();
- view.postInvalidate(
- scrollX + rect.left,
- scrollY + rect.top,
- scrollX + rect.right,
- scrollY + rect.bottom
- );
- }
- }
-
- @Override
- public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
- final long delay = when - SystemClock.uptimeMillis();
- view.postDelayed(what, delay);
- }
-
- @Override
- public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
- view.removeCallbacks(what);
- }
- }
-}
diff --git a/app/src/main/java/ru/noties/markwon/spans2/EmphasisSpan.java b/app/src/main/java/ru/noties/markwon/spans2/EmphasisSpan.java
deleted file mode 100644
index bcd8804b..00000000
--- a/app/src/main/java/ru/noties/markwon/spans2/EmphasisSpan.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package ru.noties.markwon.spans2;
-
-import android.text.TextPaint;
-import android.text.style.MetricAffectingSpan;
-
-public class EmphasisSpan extends MetricAffectingSpan {
-
- @Override
- public void updateMeasureState(TextPaint p) {
- p.setTextSkewX(-0.25f);
- }
-
- @Override
- public void updateDrawState(TextPaint tp) {
- tp.setTextSkewX(-0.25f);
- }
-}
diff --git a/app/src/main/java/ru/noties/markwon/spans2/ListItemSpan.java b/app/src/main/java/ru/noties/markwon/spans2/ListItemSpan.java
deleted file mode 100644
index 5b81952d..00000000
--- a/app/src/main/java/ru/noties/markwon/spans2/ListItemSpan.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package ru.noties.markwon.spans2;
-
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.RectF;
-import android.text.Layout;
-import android.text.style.LeadingMarginSpan;
-import ru.noties.debug.Debug;
-
-public class ListItemSpan implements LeadingMarginSpan {
-
- private final int blockIndent;
- private final boolean nested;
- private final int start;
-
- public ListItemSpan(int blockIndent, boolean nested, int start) {
- this.blockIndent = blockIndent;
- this.nested = nested;
- this.start = start;
- }
-
- @Override
- public int getLeadingMargin(boolean first) {
- return 36;
- }
-
- @Override
- public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, int top, int baseline, int bottom, CharSequence text, int start, int end, boolean first, Layout layout) {
-// Debug.i("x: %d, dir: %d, top: %d, baseline: %d, bottom: %d, first: %s",
-// x, dir, top, baseline, bottom, first
-// );
-
- // if there was a line break, we don't need to draw it
- if (this.start != start) {
- return;
- }
-
- final int save = c.save();
- try {
- final int left = 24 * (blockIndent - 1) + (first ? 12 : 0);
- final RectF rectF = new RectF(left, top, left + 16, bottom);
- final Paint paint = new Paint();
- paint.setStyle(nested ? Paint.Style.STROKE : Paint.Style.FILL);
- paint.setColor(0xFFff0000);
- c.drawOval(rectF, paint);
- } finally {
- c.restoreToCount(save);
- }
- }
-}
diff --git a/app/src/main/java/ru/noties/markwon/spans2/StrongEmphasisSpan.java b/app/src/main/java/ru/noties/markwon/spans2/StrongEmphasisSpan.java
deleted file mode 100644
index 0ec3e8b7..00000000
--- a/app/src/main/java/ru/noties/markwon/spans2/StrongEmphasisSpan.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package ru.noties.markwon.spans2;
-
-import android.text.TextPaint;
-import android.text.style.MetricAffectingSpan;
-
-public class StrongEmphasisSpan extends MetricAffectingSpan {
-
- @Override
- public void updateMeasureState(TextPaint p) {
- p.setFakeBoldText(true);
- }
-
- @Override
- public void updateDrawState(TextPaint tp) {
- tp.setFakeBoldText(true);
- }
-}
diff --git a/app/src/main/java/ru/noties/markwon/spans2/SubSpan.java b/app/src/main/java/ru/noties/markwon/spans2/SubSpan.java
deleted file mode 100644
index 991219c6..00000000
--- a/app/src/main/java/ru/noties/markwon/spans2/SubSpan.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package ru.noties.markwon.spans2;
-
-import android.text.TextPaint;
-import android.text.style.MetricAffectingSpan;
-
-public class SubSpan extends MetricAffectingSpan {
-
- @Override
- public void updateDrawState(TextPaint tp) {
- tp.setTextSize(tp.getTextSize() * .75F);
- tp.baselineShift -= (int) (tp.ascent() / 2);
- }
-
- @Override
- public void updateMeasureState(TextPaint tp) {
- tp.setTextSize(tp.getTextSize() * .75F);
- tp.baselineShift -= (int) (tp.ascent() / 2);
- }
-}
diff --git a/app/src/main/java/ru/noties/markwon/spans2/SupSpan.java b/app/src/main/java/ru/noties/markwon/spans2/SupSpan.java
deleted file mode 100644
index 044efc70..00000000
--- a/app/src/main/java/ru/noties/markwon/spans2/SupSpan.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package ru.noties.markwon.spans2;
-
-import android.text.TextPaint;
-import android.text.style.MetricAffectingSpan;
-
-public class SupSpan extends MetricAffectingSpan {
-
- @Override
- public void updateDrawState(TextPaint tp) {
- tp.setTextSize(tp.getTextSize() * .75F);
- tp.baselineShift += (int) (tp.ascent() / 2);
- }
-
- @Override
- public void updateMeasureState(TextPaint tp) {
- tp.setTextSize(tp.getTextSize() * .75F);
- tp.baselineShift += (int) (tp.ascent() / 2);
- }
-}
diff --git a/app/src/main/java/ru/noties/markwon/spans2/ThematicBreakSpan.java b/app/src/main/java/ru/noties/markwon/spans2/ThematicBreakSpan.java
deleted file mode 100644
index 731c9628..00000000
--- a/app/src/main/java/ru/noties/markwon/spans2/ThematicBreakSpan.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package ru.noties.markwon.spans2;
-
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.text.Layout;
-import android.text.style.LeadingMarginSpan;
-
-public class ThematicBreakSpan implements LeadingMarginSpan {
-
- @Override
- public int getLeadingMargin(boolean first) {
- return 1;
- }
-
- @Override
- public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, int top, int baseline, int bottom, CharSequence text, int start, int end, boolean first, Layout layout) {
- final int middle = (bottom - top) / 2;
- final Rect rect = new Rect(0, top + middle - 2, c.getWidth(), top + middle + 2);
- final Paint paint = new Paint();
- paint.setStyle(Paint.Style.FILL);
- paint.setColor(0x80000000);
- c.drawRect(rect, paint);
- }
-}
diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml
new file mode 100644
index 00000000..edca2c81
--- /dev/null
+++ b/app/src/main/res/values/ids.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/test/java/ru/noties/markwon/ExampleUnitTest.java b/app/src/test/java/ru/noties/markwon/ExampleUnitTest.java
deleted file mode 100644
index 10bd839a..00000000
--- a/app/src/test/java/ru/noties/markwon/ExampleUnitTest.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package ru.noties.markwon;
-
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * @see Testing documentation
- */
-public class ExampleUnitTest {
- @Test
- public void addition_isCorrect() throws Exception {
- assertEquals(4, 2 + 2);
- }
-}
\ No newline at end of file
diff --git a/library-renderer/src/main/java/ru/noties/markwon/DrawablesScheduler.java b/library-renderer/src/main/java/ru/noties/markwon/DrawablesScheduler.java
index c385b749..e5f92524 100644
--- a/library-renderer/src/main/java/ru/noties/markwon/DrawablesScheduler.java
+++ b/library-renderer/src/main/java/ru/noties/markwon/DrawablesScheduler.java
@@ -2,6 +2,7 @@ package ru.noties.markwon;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.Looper;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.text.Spanned;
@@ -13,6 +14,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import ru.noties.debug.Debug;
import ru.noties.markwon.spans.AsyncDrawable;
import ru.noties.markwon.spans.AsyncDrawableSpan;
@@ -21,6 +23,7 @@ abstract class DrawablesScheduler {
static void schedule(@NonNull final TextView textView) {
final List list = extract(textView);
+ Debug.i(list);
if (list.size() > 0) {
textView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
@@ -36,13 +39,15 @@ abstract class DrawablesScheduler {
});
for (AsyncDrawable d : list) {
- d.setCallback2(new DrawableCallbackImpl(textView, d.getBounds()));
+ Debug.i(d);
+ d.setCallback2(new DrawableCallbackImpl(textView, null, d.getBounds()));
}
}
}
// must be called when text manually changed in TextView
static void unschedule(@NonNull TextView view) {
+ Debug.i();
for (AsyncDrawable d : extract(view)) {
d.setCallback2(null);
}
@@ -93,38 +98,61 @@ abstract class DrawablesScheduler {
private DrawablesScheduler() {
}
+ private interface CoordinatesProvider {
+ int getX();
+ int getY();
+ }
+
private static class DrawableCallbackImpl implements Drawable.Callback {
private final TextView view;
+ private final CoordinatesProvider coordinatesProvider;
private Rect previousBounds;
- DrawableCallbackImpl(TextView view, Rect initialBounds) {
+ DrawableCallbackImpl(TextView view, CoordinatesProvider provider, Rect initialBounds) {
this.view = view;
+ this.coordinatesProvider = provider;
this.previousBounds = new Rect(initialBounds);
}
@Override
- public void invalidateDrawable(@NonNull Drawable who) {
+ public void invalidateDrawable(@NonNull final Drawable who) {
+
+ if (Looper.myLooper() != Looper.getMainLooper()) {
+ view.post(new Runnable() {
+ @Override
+ public void run() {
+ invalidateDrawable(who);
+ }
+ });
+ return;
+ }
+
+ final Rect rect = who.getBounds();
// okay... teh thing is IF we do not change bounds size, normal invalidate would do
// but if the size has changed, then we need to update the whole layout...
- final Rect rect = who.getBounds();
-
if (!previousBounds.equals(rect)) {
// the only method that seems to work when bounds have changed
view.setText(view.getText());
previousBounds = new Rect(rect);
} else {
- // if bounds are the same then simple invalidate would do
- final int scrollX = view.getScrollX();
- final int scrollY = view.getScrollY();
- view.postInvalidate(
- scrollX + rect.left,
- scrollY + rect.top,
- scrollX + rect.right,
- scrollY + rect.bottom
- );
+// // if bounds are the same then simple invalidate would do
+// if (coordinatesProvider != null) {
+// final int x = coordinatesProvider.getX();
+// final int y = coordinatesProvider.getY();
+// view.postInvalidate(
+// x + rect.left,
+// y + rect.top,
+// x + rect.right,
+// y + rect.bottom
+// );
+// } else {
+// // else all we can do is request full re-draw... maybe system is smart enough not re-draw what is not on screen?
+// view.postInvalidate();
+// }
+ view.postInvalidate();
}
}
@@ -139,4 +167,23 @@ abstract class DrawablesScheduler {
view.removeCallbacks(what);
}
}
+
+ private static class AsyncDrawableCoordinatesProvider implements CoordinatesProvider {
+
+ private final AsyncDrawableSpan span;
+
+ private AsyncDrawableCoordinatesProvider(AsyncDrawableSpan span) {
+ this.span = span;
+ }
+
+ @Override
+ public int getX() {
+ return span.lastKnownDrawX();
+ }
+
+ @Override
+ public int getY() {
+ return span.lastKnownDrawY();
+ }
+ }
}
diff --git a/library-renderer/src/main/java/ru/noties/markwon/spans/AsyncDrawable.java b/library-renderer/src/main/java/ru/noties/markwon/spans/AsyncDrawable.java
index ab90e754..b5d0fe8c 100644
--- a/library-renderer/src/main/java/ru/noties/markwon/spans/AsyncDrawable.java
+++ b/library-renderer/src/main/java/ru/noties/markwon/spans/AsyncDrawable.java
@@ -20,6 +20,7 @@ public class AsyncDrawable extends Drawable {
private final Loader loader;
private Drawable result;
+ private Callback callback;
public AsyncDrawable(@NonNull String destination, @NonNull Loader loader) {
this.destination = destination;
@@ -45,6 +46,7 @@ public class AsyncDrawable extends Drawable {
// yeah
public void setCallback2(@Nullable Callback callback) {
+ this.callback = callback;
super.setCallback(callback);
// if not null -> means we are attached
@@ -58,7 +60,7 @@ public class AsyncDrawable extends Drawable {
}
}
- public void setResult(Drawable result) {
+ public void setResult(@NonNull Drawable result) {
// if we have previous one, detach it
if (this.result != null) {
@@ -66,7 +68,7 @@ public class AsyncDrawable extends Drawable {
}
this.result = result;
- this.result.setCallback(getCallback());
+ this.result.setCallback(callback);
// should we copy the data here? like bounds etc?
// if we are async and we load some image from some source
diff --git a/library-renderer/src/main/java/ru/noties/markwon/spans/AsyncDrawableSpan.java b/library-renderer/src/main/java/ru/noties/markwon/spans/AsyncDrawableSpan.java
index 637c0e95..c2321850 100644
--- a/library-renderer/src/main/java/ru/noties/markwon/spans/AsyncDrawableSpan.java
+++ b/library-renderer/src/main/java/ru/noties/markwon/spans/AsyncDrawableSpan.java
@@ -25,6 +25,9 @@ public class AsyncDrawableSpan extends ReplacementSpan {
private final int alignment;
private final boolean replacementTextIsLink;
+ private int lastKnownDrawX;
+ private int lastKnownDrawY;
+
public AsyncDrawableSpan(@NonNull SpannableTheme theme, @NonNull AsyncDrawable drawable) {
this(theme, drawable, ALIGN_BOTTOM);
}
@@ -105,6 +108,9 @@ public class AsyncDrawableSpan extends ReplacementSpan {
int bottom,
@NonNull Paint paint) {
+ this.lastKnownDrawX = (int) (x + .5F);
+ this.lastKnownDrawY = y;
+
final AsyncDrawable drawable = this.drawable;
if (drawable.hasResult()) {
@@ -144,4 +150,12 @@ public class AsyncDrawableSpan extends ReplacementSpan {
public AsyncDrawable getDrawable() {
return drawable;
}
+
+ public int lastKnownDrawX() {
+ return lastKnownDrawX;
+ }
+
+ public int lastKnownDrawY() {
+ return lastKnownDrawY;
+ }
}
diff --git a/library-view/build.gradle b/library-view/build.gradle
deleted file mode 100644
index fa1bfd9d..00000000
--- a/library-view/build.gradle
+++ /dev/null
@@ -1,19 +0,0 @@
-apply plugin: 'com.android.library'
-
-android {
-
- compileSdkVersion TARGET_SDK
- buildToolsVersion BUILD_TOOLS
-
- defaultConfig {
- minSdkVersion MIN_SDK
- targetSdkVersion TARGET_SDK
- versionCode 1
- versionName version
- }
-}
-
-dependencies {
- compile project(':library-renderer')
- compile SUPPORT_ANNOTATIONS
-}
\ No newline at end of file
diff --git a/library-view/src/main/AndroidManifest.xml b/library-view/src/main/AndroidManifest.xml
deleted file mode 100644
index f763152f..00000000
--- a/library-view/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/library-view/src/main/java/ru/noties/markwon/view/MarkdownTextView.java b/library-view/src/main/java/ru/noties/markwon/view/MarkdownTextView.java
deleted file mode 100644
index a229a859..00000000
--- a/library-view/src/main/java/ru/noties/markwon/view/MarkdownTextView.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package ru.noties.markwon.view;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.os.Build;
-import android.support.annotation.Nullable;
-import android.util.AttributeSet;
-import android.widget.TextView;
-
-import org.commonmark.ext.gfm.strikethrough.StrikethroughExtension;
-import org.commonmark.node.Node;
-import org.commonmark.parser.Parser;
-
-import java.util.Collections;
-
-import ru.noties.markwon.SpannableConfiguration;
-import ru.noties.markwon.renderer.SpannableRenderer;
-
-public class MarkdownTextView extends TextView {
-
- private Parser parser;
- private SpannableRenderer renderer;
- private SpannableConfiguration configuration;
-
- public MarkdownTextView(Context context) {
- super(context);
- }
-
- public MarkdownTextView(Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- }
-
- public MarkdownTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
- public MarkdownTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- @Override
- public void setText(CharSequence text, BufferType type) {
- if (parser == null) {
- parser = Parser.builder()
- .extensions(Collections.singletonList(StrikethroughExtension.create()))
- .build();
- }
- if (renderer == null) {
- renderer = new SpannableRenderer();
- }
- if (configuration == null) {
- configuration = SpannableConfiguration.create(getContext());
- }
- final Node node = parser.parse(text.toString());
- final CharSequence cs = renderer.render(configuration, node);
- super.setText(cs, type);
- }
-}
diff --git a/settings.gradle b/settings.gradle
index 330339ba..43603efd 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1 @@
-include ':app', ':library-renderer', ':library-view'
+include ':app', ':library-renderer'