Migrate Markdown Library.

Previously using a customized version of zzhoujay/Markdown, FastHub now
uses atlassian/commonmark-java, for better performance, and higher
control.
This commit is contained in:
Jedi Burrell 2017-05-17 18:32:21 -04:00
parent 318c784d5e
commit 870bef80db
22 changed files with 0 additions and 3092 deletions

View File

@ -3,17 +3,12 @@ package com.fastaccess.provider.markdown;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.Html;
import android.util.Log;
import android.webkit.MimeTypeMap;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import com.annimon.stream.IntStream;
import com.fastaccess.helper.InputHelper;
import com.fastaccess.helper.ViewHelper;
import com.fastaccess.provider.timeline.handler.DrawableGetter;
import com.zzhoujay.markdown.MarkDown;
import org.commonmark.node.Node;
import org.commonmark.parser.Parser;

View File

@ -1,38 +0,0 @@
package com.zzhoujay.markdown;
import android.text.Html;
import android.text.Spannable;
import android.text.Spanned;
import android.text.style.CharacterStyle;
import android.widget.TextView;
import com.zzhoujay.markdown.parser.StyleBuilderImpl;
import java.io.IOException;
/**
* Created by zhou on 16-6-25.
* Markdown解析器
*/
public class MarkDown {
public static Spanned fromMarkdown(String source, Html.ImageGetter imageGetter, TextView textView) {
MarkDownParser parser = new MarkDownParser(source, new StyleBuilderImpl(textView, imageGetter));
try {
return parser.parse();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static Spanned fromMarkdown(String source, TextView textView, int codeColor, int headerColor, Html.ImageGetter imageGetter) {
MarkDownParser parser = new MarkDownParser(source, new StyleBuilderImpl(textView, codeColor, headerColor, imageGetter));
try {
return parser.parse();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}

View File

@ -1,309 +0,0 @@
package com.zzhoujay.markdown;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import com.zzhoujay.markdown.parser.Line;
import com.zzhoujay.markdown.parser.LineQueue;
import com.zzhoujay.markdown.parser.QueueConsumer;
import com.zzhoujay.markdown.parser.StyleBuilder;
import com.zzhoujay.markdown.parser.Tag;
import com.zzhoujay.markdown.parser.TagHandler;
import com.zzhoujay.markdown.parser.TagHandlerImpl;
import com.zzhoujay.markdown.style.ScaleHeightSpan;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by zhou on 16-6-25.
*/
class MarkDownParser {
private BufferedReader reader;
private TagHandler tagHandler;
MarkDownParser(BufferedReader reader, StyleBuilder styleBuilder) {
this.reader = reader;
tagHandler = new TagHandlerImpl(styleBuilder);
}
MarkDownParser(InputStream inputStream, StyleBuilder styleBuilder) {
this(new BufferedReader(new InputStreamReader(inputStream)), styleBuilder);
}
MarkDownParser(String text, StyleBuilder styleBuilder) {
this(new BufferedReader(new StringReader(text)), styleBuilder);
}
public Spannable parse() throws IOException {
LineQueue queue = collect();
return parse(queue);
}
/**
* 收集 String -> LineQueue
*
* @return LineQueue
* @throws IOException
*/
private LineQueue collect() throws IOException {
String line;
Line root = null;
LineQueue queue = null;
while ((line = reader.readLine()) != null) {
if (!(tagHandler.imageId(line) || tagHandler.linkId(line))) {
Line l = new Line(line);
if (root == null) {
root = l;
queue = new LineQueue(root);
} else {
queue.append(l);
}
}
}
return queue;
}
/**
* 解析LineQueue
*
* @param queue
* LineQueue
* @return Spanned
*/
private Spannable parse(final LineQueue queue) {
tagHandler.setQueueProvider(new QueueConsumer.QueueProvider() {
@Override
public LineQueue getQueue() {
return queue;
}
});
removeCurrBlankLine(queue);
boolean notBlock;// 当前Line不是CodeBlock
do {
notBlock = queue.prevLine() != null && (queue.prevLine().getType() == Line.LINE_TYPE_OL
|| queue.prevLine().getType() == Line.LINE_TYPE_UL || queue.prevLine().getType() == Line.LINE_TYPE_CHECKED
|| queue.prevLine().getType() == Line.LINE_TYPE_UN_CHECKED)
&& (tagHandler.find(Tag.UL, queue.currLine()) || tagHandler.find(Tag.OL, queue.currLine())
|| tagHandler.find(Tag.TODO_CHECKED, queue.currLine()) || tagHandler.find(Tag.TODO_UNCHECKED, queue.currLine()));
if (!notBlock && (tagHandler.codeBlock1(queue.currLine()) || tagHandler.codeBlock2(queue.currLine()))) {
continue;
}
boolean isNewLine = tagHandler.find(Tag.NEW_LINE, queue.currLine()) || tagHandler.find(Tag.GAP, queue.currLine())
|| tagHandler.find(Tag.H, queue.currLine());
if (isNewLine) {
if (queue.nextLine() != null) handleQuotaRelevant(queue, true);
removeNextBlankLine(queue);
} else {
while (queue.nextLine() != null && !removeNextBlankLine(queue)) {
if (tagHandler.find(Tag.CODE_BLOCK_1, queue.nextLine()) || tagHandler.find(Tag.CODE_BLOCK_2, queue.nextLine()) ||
tagHandler.find(Tag.GAP, queue.nextLine()) || tagHandler.find(Tag.UL, queue.nextLine()) ||
tagHandler.find(Tag.OL, queue.nextLine()) || tagHandler.find(Tag.H, queue.nextLine())
|| tagHandler.find(Tag.TODO_CHECKED, queue.nextLine()) || tagHandler.find(Tag.TODO_UNCHECKED, queue.nextLine())) {
break;
}
if (handleQuotaRelevant(queue, false)) break;
}
removeNextBlankLine(queue);
}
// 解析style
if (tagHandler.gap(queue.currLine()) || tagHandler.quota(queue.currLine()) || tagHandler.ol(queue.currLine()) ||
tagHandler.ul(queue.currLine()) || tagHandler.h(queue.currLine())) {
continue;
}
queue.currLine().setStyle(SpannableStringBuilder.valueOf(queue.currLine().getSource()));
tagHandler.inline(queue.currLine());
} while (queue.next());
return merge(queue);
}
/**
* 处理Quota嵌套相关问题
*
* @param queue
* LineQueue
* @param onlyH
* 只处理Title相关的问题
* @return true已处理
*/
private boolean handleQuotaRelevant(LineQueue queue, boolean onlyH) {
int nextQuotaCount = tagHandler.findCount(Tag.QUOTA, queue.nextLine(), 1);
int currQuotaCount = tagHandler.findCount(Tag.QUOTA, queue.currLine(), 1);
if (nextQuotaCount > 0 && nextQuotaCount > currQuotaCount) {
return true;
} else {
String source = queue.nextLine().getSource();
if (nextQuotaCount > 0) {
source = source.replaceFirst("^\\s{0,3}(>\\s+){" + nextQuotaCount + "}", "");
}
if (currQuotaCount == nextQuotaCount) {
if (findH1_2(queue, currQuotaCount, source)) return true;
if (findH2_2(queue, currQuotaCount, source)) return true;
}
if (onlyH) {
return false;
}
if (tagHandler.find(Tag.UL, source) || tagHandler.find(Tag.OL, source)
|| tagHandler.find(Tag.TODO_CHECKED, source)
|| tagHandler.find(Tag.TODO_UNCHECKED, source)
|| tagHandler.find(Tag.H, source)) {
return true;
} else {
queue.currLine().setSource(queue.currLine().getSource() + ' ' + source);
queue.removeNextLine();
}
}
return false;
}
private boolean findH2_2(LineQueue queue, int currQuotaCount, String source) {
if (tagHandler.find(Tag.H2_2, source)) {
String currLineSource = queue.currLine().getSource();
Matcher m = Pattern.compile("^\\s{0,3}(>\\s+?){" + currQuotaCount + "}(.*)").matcher(queue.currLine().getSource());
String newCurrLineSource;
if (m.find()) {
int start = m.start(2);
int end = m.end(2);
newCurrLineSource = currLineSource.substring(0, start) + "## " + currLineSource.subSequence(start, end);
} else {
newCurrLineSource = "## " + currLineSource;
}
queue.currLine().setSource(newCurrLineSource);
queue.removeNextLine();
return true;
}
return false;
}
private boolean findH1_2(LineQueue queue, int currQuotaCount, String source) {
if (tagHandler.find(Tag.H1_2, source)) {
String currLineSource = queue.currLine().getSource();
Matcher m = Pattern.compile("^\\s{0,3}(>\\s+?){" + currQuotaCount + "}(.*)").matcher(currLineSource);
String newCurrLineSource;
if (m.find()) {
int start = m.start(2);
int end = m.end(2);
newCurrLineSource = currLineSource.substring(0, start) + "# " + currLineSource.subSequence(start, end);
} else {
newCurrLineSource = "# " + currLineSource;
}
queue.currLine().setSource(newCurrLineSource);
queue.removeNextLine();
return true;
}
return false;
}
/**
* 合并LineQueue -> Spanned
*
* @param queue
* LineQueue
* @return Spanned
*/
private Spannable merge(LineQueue queue) {
queue.reset();
SpannableStringBuilder builder = new SpannableStringBuilder();
do {
Line curr = queue.currLine();
Line next = queue.nextLine();
builder.append(curr.getStyle());
if (next == null) {
break;
}
builder.append('\n');
switch (curr.getType()) {
case Line.LINE_TYPE_QUOTA:
if (next.getType() != Line.LINE_TYPE_QUOTA) {
builder.append('\n');
}
break;
case Line.LINE_TYPE_UL:
if (next.getType() == Line.LINE_TYPE_UL)
builder.append(listMarginBottom());
builder.append('\n');
break;
case Line.LINE_TYPE_OL:
if (next.getType() == Line.LINE_TYPE_OL) {
builder.append(listMarginBottom());
}
builder.append('\n');
break;
case Line.LINE_TYPE_CHECKED:
if (next.getType() == Line.LINE_TYPE_OL) {
builder.append(listMarginBottom());
}
builder.append('\n');
break;
case Line.LINE_TYPE_UN_CHECKED:
if (next.getType() == Line.LINE_TYPE_OL) {
builder.append(listMarginBottom());
}
builder.append('\n');
break;
default:
builder.append('\n');
}
} while (queue.next());
return builder;
}
/**
* 从下个Line开始移除空Line
*
* @param queue
* LineQueue
* @return 是否移除了
*/
private boolean removeNextBlankLine(LineQueue queue) {
boolean flag = false;
while (queue.nextLine() != null) {
if (tagHandler.find(Tag.BLANK, queue.nextLine())) {
queue.removeNextLine();
flag = true;
} else {
break;
}
}
return flag;
}
/**
* 从当前行开始移除空Line
*
* @param queue
* LineQueue
* @return true移除了Line
*/
private boolean removeCurrBlankLine(LineQueue queue) {
boolean flag = false;
while (queue.currLine() != null) {
if (tagHandler.find(Tag.BLANK, queue.currLine())) {
queue.removeCurrLine();
flag = true;
} else {
break;
}
}
return flag;
}
private SpannableString listMarginBottom() {
SpannableString ss = new SpannableString(" ");
ss.setSpan(new ScaleHeightSpan(0.4f), 0, ss.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return ss;
}
}

View File

@ -1,412 +0,0 @@
package com.zzhoujay.markdown.parser;
import android.text.SpannableStringBuilder;
/**
* Created by zhou on 16-6-28.
* 代表每一行文本
*/
public class Line {
public static final int LINE_NORMAL = 0; // 普通文本
public static final int LINE_TYPE_QUOTA = 1; // 引用
public static final int LINE_TYPE_UL = 2; // 无序列表
public static final int LINE_TYPE_OL = 3; // 有序列表
public static final int LINE_TYPE_H1 = 4; // H1
public static final int LINE_TYPE_H2 = 5; // H2
public static final int LINE_TYPE_H3 = 6; // H3
public static final int LINE_TYPE_H4 = 7; // H4
public static final int LINE_TYPE_H5 = 8; // H5
public static final int LINE_TYPE_H6 = 9; // H6
public static final int LINE_TYPE_CODE_BLOCK_2 = 10;
public static final int LINE_TYPE_CODE_BLOCK_1 = 11;
public static final int LINE_TYPE_GAP = 12;
public static final int LINE_TYPE_CHECKED = 13;
public static final int LINE_TYPE_UN_CHECKED = 14;
public static final int HANDLE_BY_ROOT = 1;
private Line prev; // 前一个节点
private Line next; // 下一个节点
private Line parent; // 父节点
private Line child; // 子节点
private String source; // 源文本
private CharSequence style; // 样式
private int type; // 种类
private int count; // 数量
private int attr; // 属性
private int handle; // 0
private int data;
public Line(String source) {
this.source = source;
count = 1;
type = LINE_NORMAL;
}
private Line(Line line) {
this.source = line.source;
this.count = line.count;
this.attr = line.attr;
if (line.style != null) {
this.style = new SpannableStringBuilder(line.style);
}
this.type = line.type;
}
public void setSource(String source) {
this.source = source;
}
public String getSource() {
return source;
}
public void setStyle(CharSequence style) {
this.style = style;
}
public CharSequence getStyle() {
return style;
}
public void setType(int type) {
this.type = type;
}
public int getType() {
return type;
}
public void setCount(int count) {
this.count = count;
}
public int getCount() {
return count;
}
public int getHandle() {
return handle;
}
public void setHandle(int handle) {
this.handle = handle;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public void setAttr(int attr) {
this.attr = attr;
}
public int getAttr() {
return attr;
}
public Line get() {
return this;
}
public Line nextLine() {
return next;
}
public Line prevLine() {
return prev;
}
public Line childLine() {
return child;
}
public Line parentLine() {
return parent;
}
/**
* 添加为下一个节点
*
* @param line
* Line
* @return Line
*/
public Line addNext(Line line) {
if (line == null) {
next = null;
} else {
if (line.next != null) {
line.next.prev = null;
}
line.next = next;
if (next != null) {
next.prev = line;
}
if (line.prev != null) {
line.prev.next = null;
}
line.prev = this;
next = line;
if (child != null) {
child.addNext(line.child);
}
}
return line;
}
/**
* 添加为上一个节点
*
* @param line
* Line
* @return Line
*/
public Line addPrev(Line line) {
if (line == null) {
prev = null;
} else {
if (line.prev != null) {
line.prev.next = null;
}
line.prev = prev;
if (prev != null) {
prev.next = line;
}
if (line.next != null) {
line.next.prev = null;
}
line.next = this;
prev = line;
if (child != null) {
child.addPrev(line.child);
}
}
return line;
}
public Line add(Line line) {
return addNext(line);
}
/**
* 删除当前节点包括其子节点包会导致链表断开
*/
private void delete() {
if (child != null) {
child.delete();
}
if (prev != null) {
prev.next = null;
}
prev = null;
if (next != null) {
next.prev = null;
}
next = null;
}
/**
* 移除当前节点重新连上前后两个节点包括其子节点不会导致链表断开
*/
private void reduce() {
if (child != null) {
child.reduce();
}
if (prev != null) {
prev.next = next;
}
if (next != null) {
next.prev = prev;
}
next = null;
prev = null;
}
/**
* 移除当前节点并且不会导致主链表断开
*/
public void remove() {
if (parent == null) {
reduce();
} else {
delete();
}
}
/**
* 移除下一个节点
*
* @return this
*/
public Line removeNext() {
if (next != null) {
next.remove();
}
return this;
}
/**
* 移除上一行
*
* @return this
*/
public Line removePrev() {
if (prev != null) {
prev.remove();
}
return this;
}
/**
* 添加子节点并将子节点和前后节点的子节点建立连接
*
* @param line
* child
*/
public void addChild(Line line) {
if (child != null) {
child.parent = null;
}
child = line;
if (line.parent != null) {
line.parent.child = null;
}
line.parent = this;
attachChildToNext();
attachChildToPrev();
}
/**
* 将子节点和下一个节点之间建立连接
*/
public void attachChildToNext() {
if (child != null && next != null) {
if (child.next != null) {
child.next.prev = null;
}
child.next = next.child;
if (next.child != null) {
if (next.child.prev != null) {
next.child.prev.next = null;
}
next.child.prev = child;
}
child.attachChildToNext();
}
}
/**
* 将子节点和上一个节点之间建立连接
*/
public void attachChildToPrev() {
if (child != null && prev != null) {
if (child.prev != null) {
child.prev.next = null;
}
child.prev = prev.child;
if (prev.child != null) {
if (prev.child.next != null) {
prev.child.next.prev = null;
}
prev.child.next = child;
}
child.attachChildToPrev();
}
}
/**
* 将自身作为子节点添加到某个节点
*
* @param line
* parent
*/
public void attachToParent(Line line) {
line.addChild(this);
}
/**
* 从父节点上解绑
*/
public void unAttachFromParent() {
if (parent != null) {
delete();
parent.child = null;
}
parent = null;
}
/**
* 创建一个子节点
*
* @param src
* 子节点的source
* @return 子节点
*/
public Line createChild(String src) {
Line c = new Line(src);
addChild(c);
return c;
}
/**
* 将自身及父节点copy为下一个节点
*
* @return copy产生的对象
*/
public Line copyToNext() {
Line p = null;
if (parent != null) {
p = parent.copyToNext();
}
Line line = new Line(this);
if (p == null) {
line.next = next;
if (next != null) {
next.prev = line;
}
line.prev = this;
next = line;
} else {
p.addChild(line);
}
return line;
}
/**
* 将自身及父节点copy为上一个节点
*
* @return copy产生的对象
*/
public Line copyToPrev() {
Line p = null;
if (parent != null) {
p = parent.copyToPrev();
}
Line line = new Line(this);
if (p == null) {
line.prev = prev;
if (prev != null) {
prev.next = line;
}
line.next = this;
prev = this;
} else {
p.addChild(line);
}
return line;
}
@Override
public String toString() {
return source;
}
}

View File

@ -1,134 +0,0 @@
package com.zzhoujay.markdown.parser;
/**
* Created by zhou on 16-7-2.
*/
public class LineQueue {
private Line root; // 根节点
private Line curr; // 当前节点
private Line last; // 尾节点
public LineQueue(Line root) {
this.root = root;
curr = root;
last = root;
while (last.nextLine() != null) {
last = last.nextLine();
}
}
private LineQueue(LineQueue queue, Line curr) {
this.root = queue.root;
this.last = queue.last;
this.curr = curr;
}
public Line nextLine() {
return curr.nextLine();
}
public Line prevLine() {
return curr.prevLine();
}
public Line currLine() {
return curr;
}
public boolean next() {
if (curr.nextLine() == null) {
return false;
}
curr = curr.nextLine();
return true;
}
public boolean prev() {
if (curr.prevLine() == null) {
return false;
}
curr = currLine().prevLine();
return true;
}
public boolean end() {
return curr.nextLine() == null;
}
public boolean start() {
return curr == root;
}
public void append(Line line) {
last.add(line);
last = line;
}
public void insert(Line line) {
if (curr == last) {
append(line);
} else {
curr.addNext(line);
}
}
public Line removeCurrLine() {
Line tmp;
if (curr == last) {
tmp = last.prevLine();
} else {
tmp = curr.nextLine();
if (curr == root) {
root = tmp;
}
}
curr.remove();
Line r = curr;
curr = tmp;
return r;
}
public void removeNextLine() {
curr.removeNext();
}
public void removePrevLine() {
if (root == curr.prevLine()) {
root = curr;
}
curr.removePrev();
}
public LineQueue copy() {
return new LineQueue(this, curr);
}
public LineQueue copyNext() {
if (end()) {
return null;
}
return new LineQueue(this, curr.nextLine());
}
public void reset() {
curr = root;
}
@Override
public String toString() {
Line t = root;
StringBuilder sb = new StringBuilder();
int len = 0;
while (t != null) {
sb.append(t.toString()).append(",");
t = t.nextLine();
len++;
}
return "{" + sb.toString() + "}";
}
}

View File

@ -1,18 +0,0 @@
package com.zzhoujay.markdown.parser;
/**
* Created by zhou on 16-7-22.
* LineQueue的消费者
*/
public interface QueueConsumer {
void setQueueProvider(QueueProvider provider);
/**
* LineQueue提供器
*/
interface QueueProvider {
LineQueue getQueue();
}
}

View File

@ -1,59 +0,0 @@
package com.zzhoujay.markdown.parser;
import android.text.SpannableStringBuilder;
/**
* Created by zhou on 16-6-28.
* markdown各种样式的构建器
*/
public interface StyleBuilder {
SpannableStringBuilder em(CharSequence charSequence);
SpannableStringBuilder italic(CharSequence charSequence);
SpannableStringBuilder emItalic(CharSequence charSequence);
SpannableStringBuilder delete(CharSequence charSequence);
SpannableStringBuilder email(CharSequence charSequence);
SpannableStringBuilder link(CharSequence title, String link, String hint);
SpannableStringBuilder image(CharSequence title, String url, String hint);
SpannableStringBuilder code(CharSequence charSequence);
SpannableStringBuilder h1(CharSequence charSequence);
SpannableStringBuilder h2(CharSequence charSequence);
SpannableStringBuilder h3(CharSequence charSequence);
SpannableStringBuilder h4(CharSequence charSequence);
SpannableStringBuilder h5(CharSequence charSequence);
SpannableStringBuilder h6(CharSequence charSequence);
SpannableStringBuilder quota(CharSequence charSequence);
SpannableStringBuilder ul(CharSequence charSequence, int level);
SpannableStringBuilder ol(CharSequence charSequence, int level, int index);
SpannableStringBuilder ul2(CharSequence charSequence, int quotaLevel, int bulletLevel);
SpannableStringBuilder ol2(CharSequence charSequence, int quotaLevel, int bulletLevel, int index);
SpannableStringBuilder codeBlock(CharSequence... charSequence);
SpannableStringBuilder codeBlock(String code);
SpannableStringBuilder gap();
SpannableStringBuilder checked(CharSequence charSequence);
SpannableStringBuilder unChecked(CharSequence charSequence);
}

View File

@ -1,282 +0,0 @@
package com.zzhoujay.markdown.parser;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.text.Html;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.BackgroundColorSpan;
import android.text.style.BulletSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.ImageSpan;
import android.text.style.QuoteSpan;
import android.text.style.StrikethroughSpan;
import android.text.style.TypefaceSpan;
import android.util.Log;
import android.widget.TextView;
import com.zzhoujay.markdown.style.CodeBlockSpan;
import com.zzhoujay.markdown.style.EmailSpan;
import com.zzhoujay.markdown.style.FontSpan;
import com.zzhoujay.markdown.style.LinkSpan;
import com.zzhoujay.markdown.style.MarkDownBulletSpan;
import com.zzhoujay.markdown.style.MarkDownQuoteSpan;
import com.zzhoujay.markdown.style.QuotaBulletSpan;
import com.zzhoujay.markdown.style.UnderLineSpan;
import java.lang.ref.WeakReference;
/**
* Created by zhou on 16-6-28.
* StyleBuilderImpl
*/
public class StyleBuilderImpl implements StyleBuilder {
private int h1_color = Color.parseColor("#333333");
private static final int h6_color = Color.parseColor("#777777");
private static final int quota_color = Color.parseColor("#DDDDDD");
private int code_color = Color.parseColor("#F0F0F0");
private static final int link_color = Color.parseColor("#4078C0");
private static final int h_under_line_color = Color.parseColor("#eeeeee");
private static final float scale_h1 = 2.25f;
private static final float scale_h2 = 1.75f;
private static final float scale_h3 = 1.5f;
private static final float scale_h4 = 1.25f;
private static final float scale_h5 = 1, scale_h6 = 1;
private static final float scale_normal = 1;
private WeakReference<TextView> textViewWeakReference;
private Html.ImageGetter imageGetter;
public StyleBuilderImpl(TextView textView, int codeColor, int h1_color, Html.ImageGetter imageGetter) {
this.textViewWeakReference = new WeakReference<>(textView);
this.code_color = codeColor;
this.h1_color = h1_color;
this.imageGetter = imageGetter;
}
public StyleBuilderImpl(TextView textView, Html.ImageGetter imageGetter) {
this.textViewWeakReference = new WeakReference<>(textView);
this.imageGetter = imageGetter;
}
@Override
public SpannableStringBuilder em(CharSequence charSequence) {
SpannableStringBuilder builder = SpannableStringBuilder.valueOf(charSequence);
FontSpan fontSpan = new FontSpan(scale_normal, Typeface.BOLD, h1_color);
builder.setSpan(fontSpan, 0, charSequence.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return builder;
}
@Override
public SpannableStringBuilder italic(CharSequence charSequence) {
SpannableStringBuilder builder = SpannableStringBuilder.valueOf(charSequence);
FontSpan fontSpan = new FontSpan(scale_normal, Typeface.ITALIC, h1_color);
builder.setSpan(fontSpan, 0, charSequence.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return builder;
}
@Override
public SpannableStringBuilder emItalic(CharSequence charSequence) {
SpannableStringBuilder builder = SpannableStringBuilder.valueOf(charSequence);
FontSpan fontSpan = new FontSpan(scale_normal, Typeface.BOLD_ITALIC, h1_color);
builder.setSpan(fontSpan, 0, charSequence.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return builder;
}
@Override
public SpannableStringBuilder delete(CharSequence charSequence) {
SpannableStringBuilder builder = SpannableStringBuilder.valueOf(charSequence);
StrikethroughSpan span = new StrikethroughSpan();
ForegroundColorSpan colorSpan = new ForegroundColorSpan(h1_color);
builder.setSpan(colorSpan, 0, charSequence.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
builder.setSpan(span, 0, charSequence.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return builder;
}
@Override
public SpannableStringBuilder email(CharSequence charSequence) {
SpannableStringBuilder builder = SpannableStringBuilder.valueOf(charSequence);
EmailSpan emailSpan = new EmailSpan(charSequence.toString(), link_color);
builder.setSpan(emailSpan, 0, charSequence.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return builder;
}
@Override
public SpannableStringBuilder code(CharSequence charSequence) {
SpannableStringBuilder builder = SpannableStringBuilder.valueOf(charSequence);
builder.setSpan(new BackgroundColorSpan(code_color), 0, charSequence.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
builder.setSpan(new TypefaceSpan("monospace"), 0, charSequence.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return builder;
}
@Override
public SpannableStringBuilder h1(CharSequence charSequence) {
return hWithUnderLine(charSequence, scale_h1);
}
@Override
public SpannableStringBuilder h2(CharSequence charSequence) {
return hWithUnderLine(charSequence, scale_h2);
}
@Override
public SpannableStringBuilder h3(CharSequence charSequence) {
return h(charSequence, scale_h3, h1_color);
}
@Override
public SpannableStringBuilder h4(CharSequence charSequence) {
return h(charSequence, scale_h4, h1_color);
}
@Override
public SpannableStringBuilder h5(CharSequence charSequence) {
return h(charSequence, scale_h5, h1_color);
}
@Override
public SpannableStringBuilder h6(CharSequence charSequence) {
return h(charSequence, scale_h6, h6_color);
}
@Override
public SpannableStringBuilder quota(CharSequence charSequence) {
SpannableStringBuilder spannableStringBuilder = SpannableStringBuilder.valueOf(charSequence);
QuoteSpan span = new MarkDownQuoteSpan(quota_color);
spannableStringBuilder.setSpan(span, 0, spannableStringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableStringBuilder;
}
@Override
public SpannableStringBuilder ul(CharSequence charSequence, int level) {
SpannableStringBuilder spannableStringBuilder = SpannableStringBuilder.valueOf(charSequence);
BulletSpan bulletSpan = new MarkDownBulletSpan(level, h1_color, 0);
spannableStringBuilder.setSpan(bulletSpan, 0, spannableStringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableStringBuilder;
}
@Override
public SpannableStringBuilder ol(CharSequence charSequence, int level, int index) {
SpannableStringBuilder spannableStringBuilder = SpannableStringBuilder.valueOf(charSequence);
BulletSpan bulletSpan = new MarkDownBulletSpan(level, h1_color, index, textViewWeakReference.get());
spannableStringBuilder.setSpan(bulletSpan, 0, spannableStringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableStringBuilder;
}
@Override
public SpannableStringBuilder ul2(CharSequence charSequence, int quotaLevel, int bulletLevel) {
SpannableStringBuilder spannableStringBuilder = SpannableStringBuilder.valueOf(charSequence);
QuotaBulletSpan bulletSpan = new QuotaBulletSpan(quotaLevel, bulletLevel, quota_color, h1_color, 0, textViewWeakReference.get());
spannableStringBuilder.setSpan(bulletSpan, 0, spannableStringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableStringBuilder;
}
@Override
public SpannableStringBuilder ol2(CharSequence charSequence, int quotaLevel, int bulletLevel, int index) {
SpannableStringBuilder spannableStringBuilder = SpannableStringBuilder.valueOf(charSequence);
QuotaBulletSpan bulletSpan = new QuotaBulletSpan(quotaLevel, bulletLevel, quota_color, h1_color, index, textViewWeakReference.get());
spannableStringBuilder.setSpan(bulletSpan, 0, spannableStringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableStringBuilder;
}
@Override
public SpannableStringBuilder codeBlock(CharSequence... charSequence) {
SpannableStringBuilder builder = SpannableStringBuilder.valueOf("$");
CodeBlockSpan codeBlockSpan = new CodeBlockSpan(getTextViewRealWidth(), code_color, charSequence);
builder.setSpan(codeBlockSpan, 0, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
builder.setSpan(new TypefaceSpan("monospace"), 0, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return builder;
}
@Override
public SpannableStringBuilder codeBlock(String code) {
return codeBlock((CharSequence[]) code.split("\n"));
}
@Override
public SpannableStringBuilder link(CharSequence title, String link, String hint) {
SpannableStringBuilder builder = SpannableStringBuilder.valueOf(title);
LinkSpan linkSpan = new LinkSpan(link, link_color);
builder.setSpan(linkSpan, 0, title.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return builder;
}
@Override
public SpannableStringBuilder image(CharSequence title, String url, String hint) {
if (title == null || title.length() == 0) {
title = "$";
}
SpannableStringBuilder builder = SpannableStringBuilder.valueOf(title);
Drawable drawable = null;
if (imageGetter != null) {
drawable = imageGetter.getDrawable(url);
}
if (drawable == null) {
drawable = new ColorDrawable(Color.TRANSPARENT);
// builder.delete(0, builder.length());
// return builder;
}
ImageSpan imageSpan = new ImageSpan(drawable, url);
builder.setSpan(imageSpan, 0, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return builder;
}
@SuppressWarnings("WeakerAccess")
protected SpannableStringBuilder h(CharSequence charSequence, float s, int color) {
SpannableStringBuilder spannableStringBuilder = SpannableStringBuilder.valueOf(charSequence);
FontSpan fontSpan = new FontSpan(s, Typeface.BOLD, color);
spannableStringBuilder.setSpan(fontSpan, 0, spannableStringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableStringBuilder;
}
private SpannableStringBuilder hWithUnderLine(CharSequence charSequence, float s) {
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(charSequence);
int start = 0;
FontSpan fontSpan = new FontSpan(s, Typeface.BOLD, h1_color);
spannableStringBuilder.setSpan(fontSpan, 0, spannableStringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
Drawable underLine = new ColorDrawable(h_under_line_color);
UnderLineSpan underLineSpan = new UnderLineSpan(underLine, getTextViewRealWidth(), 5);
spannableStringBuilder.append('\n');
start += charSequence.length() + 1;
spannableStringBuilder.append("$");
spannableStringBuilder.setSpan(underLineSpan, start, spannableStringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableStringBuilder;
}
private int getTextViewRealWidth() {
TextView textView = textViewWeakReference.get();
if (textView != null) {
return textView.getWidth() - textView.getPaddingRight() - textView.getPaddingLeft();
}
return 0;
}
@Override
public SpannableStringBuilder gap() {
SpannableStringBuilder builder = new SpannableStringBuilder("$");
Drawable underLine = new ColorDrawable(h_under_line_color);
UnderLineSpan underLineSpan = new UnderLineSpan(underLine, getTextViewRealWidth(), 10);
builder.setSpan(underLineSpan, 0, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return builder;
}
@Override public SpannableStringBuilder checked(CharSequence charSequence) {
Log.e("checked()", "" + charSequence);
SpannableStringBuilder builder = new SpannableStringBuilder();
return builder.append("")
.append("\t")
.append(charSequence);
}
@Override public SpannableStringBuilder unChecked(CharSequence charSequence) {
Log.e("unChecked()", "" + charSequence);
SpannableStringBuilder builder = new SpannableStringBuilder();
return builder.append("")
.append("\t")
.append(charSequence);
}
}

View File

@ -1,41 +0,0 @@
package com.zzhoujay.markdown.parser;
/**
* Created by zhou on 16-7-10.
*/
public class Tag {
public static final int NORMAL = 0;
public static final int CODE_BLOCK_1 = 1;
public static final int CODE_BLOCK_2 = 2;
public static final int H1 = 3;
public static final int H2 = 4;
public static final int H3 = 24;
public static final int H4 = 5;
public static final int H5 = 6;
public static final int H6 = 7;
public static final int QUOTA = 8;
public static final int UL = 9;
public static final int OL = 10;
public static final int EM = 11;
public static final int ITALIC = 12;
public static final int EM_ITALIC = 13;
public static final int EMAIL = 14;
public static final int AUTO_LINK = 15;
public static final int DELETE = 16;
public static final int LINK = 17;
public static final int LINK2 = 18;
public static final int LINK_ID = 19;
public static final int IMAGE = 20;
public static final int IMAGE2 = 21;
public static final int IMAGE_ID = 22;
public static final int H = 23;
public static final int BLANK = 25;
public static final int NEW_LINE = 26;
public static final int GAP = 27;
public static final int H1_2 = 28;
public static final int H2_2 = 29;
public static final int CODE = 30;
public static final int TODO_CHECKED = 31;
public static final int TODO_UNCHECKED = 32;
}

View File

@ -1,47 +0,0 @@
package com.zzhoujay.markdown.parser;
/**
* Created by zhou on 16-7-10.
* TagFinder
*/
public interface TagFinder {
/**
* 检查对应tag是否存在
*
* @param tag Tag Id
* @param line line
* @return true存在false不存在
*/
boolean find(int tag, Line line);
/**
* 检查对应tag是否存在
*
* @param tag Tag Id
* @param line line
* @return true存在false不存在
*/
boolean find(int tag, String line);
/**
* 检查对应tag的个数
*
* @param tag tag id
* @param line line
* @param group group
* @return 对应tag的次数
*/
int findCount(int tag, Line line, int group);
/**
* 检查对应tag的个数
*
* @param tag tag id
* @param line line
* @param group group
* @return 对应tag的次数
*/
int findCount(int tag, String line, int group);
}

View File

@ -1,27 +0,0 @@
package com.zzhoujay.markdown.parser;
/**
* 获取Tag内容
*/
public interface TagGetter {
/**
* 获取Line特定Tag的内容
*
* @param tag tag
* @param line Line
* @param group group
* @return content
*/
CharSequence get(int tag, Line line, int group);
/**
* 获取line特定Tag的内容
*
* @param tag tag
* @param line line
* @param group group
* @return content
*/
CharSequence get(int tag, CharSequence line, int group);
}

View File

@ -1,67 +0,0 @@
package com.zzhoujay.markdown.parser;
/**
* Created by zhou on 16-7-10.
* TagHandler
*/
public interface TagHandler extends TagFinder, QueueConsumer, TagGetter {
boolean h(Line line);
boolean h1(Line line);
boolean h2(Line line);
boolean h3(Line line);
boolean h4(Line line);
boolean h5(Line line);
boolean h6(Line line);
boolean quota(Line line);
boolean ul(Line line);
boolean ol(Line line);
boolean todoChecked(Line line);
boolean todoUnChecked(Line line);
boolean gap(Line line);
boolean em(Line line);
boolean italic(Line line);
boolean emItalic(Line line);
boolean code(Line line);
boolean email(Line line);
boolean delete(Line line);
boolean autoLink(Line line);
boolean link(Line line);
boolean link2(Line line);
boolean linkId(String line);
boolean image(Line line);
boolean image2(Line line);
boolean imageId(String line);
boolean inline(Line line);
boolean codeBlock1(Line line);
boolean codeBlock2(Line line);
}

View File

@ -1,952 +0,0 @@
package com.zzhoujay.markdown.parser;
import android.text.SpannableStringBuilder;
import android.util.Pair;
import android.util.SparseArray;
import com.zzhoujay.markdown.style.CodeSpan;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by zhou on 16-7-10.
* TagHandlerImpl
*/
public class TagHandlerImpl implements TagHandler {
private static final Matcher matcherH1_2 = Pattern.compile("^\\s*=+\\s*$").matcher("");
private static final Matcher matcherH2_2 = Pattern.compile("^\\s*-+\\s*$").matcher("");
private static final Matcher matcherH = Pattern.compile("^\\s*#{1,6}\\s*([^#]*)(\\s*#)?").matcher("");
private static final Matcher matcherH1 = Pattern.compile("^\\s*#\\s*([^#]*)(\\s*#)?").matcher("");
private static final Matcher matcherH2 = Pattern.compile("^\\s*#{2}\\s*([^#]*)(\\s*#)?").matcher("");
private static final Matcher matcherH3 = Pattern.compile("^\\s*#{3}\\s*([^#]*)(\\s*#)?").matcher("");
private static final Matcher matcherH4 = Pattern.compile("^\\s*#{4}\\s*([^#]*)(\\s*#)?").matcher("");
private static final Matcher matcherH5 = Pattern.compile("^\\s*#{5}\\s*([^#]*)(\\s*#)?").matcher("");
private static final Matcher matcherH6 = Pattern.compile("^\\s*#{6}\\s*([^#]*)(\\s*#)?").matcher("");
private static final Matcher matcherQuota = Pattern.compile("^\\s{0,3}>\\s(.*)").matcher("");
private static final Matcher matcherUl = Pattern.compile("^\\s*[*+-]\\s+(.*)").matcher("");
private static final Matcher matcherOl = Pattern.compile("^\\s*\\d+\\.\\s+(.*)").matcher("");
private static final Matcher matcherUnChecked = Pattern.compile("-\\s\\[ ]+(.*)").matcher("");
private static final Matcher matcherChecked = Pattern.compile("-\\s\\[x]+(.*)").matcher("");
private static final Matcher matcherItalic = Pattern.compile("[^*_]*(([*_])([^*_].*?)\\2)").matcher("");
private static final Matcher matcherEm = Pattern.compile("[^*_]*(([*_])\\2([^*_].*?)\\2\\2)").matcher("");
private static final Matcher matcherEmItalic = Pattern.compile("[^*_]*(([*_])\\2\\2([^*_].*?)\\2\\2\\2)").matcher("");
private static final Matcher matcherDelete = Pattern.compile("[^~]*((~{2,4})([^~].*?)\\2)").matcher("");
private static final Matcher matcherCode = Pattern.compile("[^`]*((`+)([^`].*?)\\2)").matcher("");
private static final Matcher matcherLink = Pattern.compile(".*?(\\[\\s*(.*?)\\s*]\\(\\s*(\\S*?)(\\s+(['\"])(.*?)\\5)?\\s*?\\))").matcher("");
private static final Matcher matcherImage = Pattern.compile(".*?(!\\[\\s*(.*?)\\s*]\\(\\s*(\\S*?)(\\s+(['\"])(.*?)\\5)?\\s*?\\))").matcher("");
private static final Matcher matcherLink2 = Pattern.compile(".*?(\\[\\s*(.*?)\\s*]\\s*\\[\\s*(.*?)\\s*])").matcher("");
private static final Matcher matcherLinkId = Pattern.compile("^\\s*\\[\\s*(.*?)\\s*]:\\s*(\\S+?)(\\s+(['\"])(.*?)\\4)?\\s*$").matcher("");
private static final Matcher matcherImage2 = Pattern.compile(".*?(!\\[\\s*(.*?)\\s*]\\s*\\[\\s*(.*?)\\s*])").matcher("");
private static final Matcher matcherImageId = Pattern.compile("^\\s*!\\[\\s*(.*?)\\s*]:\\s*(\\S+?)(\\s+(['\"])(.*?)\\4)?\\s*$").matcher("");
private static final Matcher matcherEmail = Pattern.compile(".*?(<(\\S+@\\S+\\.\\S+)>).*?").matcher("");
private static final Matcher matcherAutoLink = Pattern.compile("((https|http|ftp|rtsp|mms)?://)[^\\s]+").matcher("");
private static final Matcher matcherEndSpace = Pattern.compile("(.*?) {2} *$").matcher("");
private static final Matcher matcherInlineSpace = Pattern.compile("\\S*(\\s+)\\S+").matcher("");
private static final Matcher matcherCodeBlock = Pattern.compile("^( {4}|\\t)(.*)").matcher("");
private static final Matcher matcherCodeBlock2 = Pattern.compile("^\\s*```").matcher("");
private static final Matcher matcherBlankLine = Pattern.compile("^\\s*$").matcher("");
private static final Matcher matcherGap = Pattern.compile("^\\s*([-*]\\s*){3,}$").matcher("");
private static final SparseArray<Matcher> matchers = new SparseArray<>();// matcher缓冲区
static {
matchers.put(Tag.CODE_BLOCK_1, matcherCodeBlock);
matchers.put(Tag.CODE_BLOCK_2, matcherCodeBlock2);
matchers.put(Tag.H1, matcherH1);
matchers.put(Tag.H2, matcherH2);
matchers.put(Tag.H3, matcherH3);
matchers.put(Tag.H4, matcherH4);
matchers.put(Tag.H5, matcherH5);
matchers.put(Tag.H6, matcherH6);
matchers.put(Tag.H, matcherH);
matchers.put(Tag.TODO_CHECKED, matcherChecked);
matchers.put(Tag.TODO_UNCHECKED, matcherUnChecked);
matchers.put(Tag.QUOTA, matcherQuota);
matchers.put(Tag.UL, matcherUl);
matchers.put(Tag.OL, matcherOl);
matchers.put(Tag.EM, matcherEm);
matchers.put(Tag.ITALIC, matcherItalic);
matchers.put(Tag.EM_ITALIC, matcherEmItalic);
matchers.put(Tag.EMAIL, matcherEmail);
matchers.put(Tag.AUTO_LINK, matcherAutoLink);
matchers.put(Tag.DELETE, matcherDelete);
matchers.put(Tag.LINK, matcherLink);
matchers.put(Tag.LINK2, matcherLink2);
matchers.put(Tag.LINK_ID, matcherLinkId);
matchers.put(Tag.IMAGE, matcherImage);
matchers.put(Tag.IMAGE2, matcherImage2);
matchers.put(Tag.IMAGE_ID, matcherImageId);
matchers.put(Tag.BLANK, matcherBlankLine);
matchers.put(Tag.NEW_LINE, matcherEndSpace);
matchers.put(Tag.GAP, matcherGap);
matchers.put(Tag.H1_2, matcherH1_2);
matchers.put(Tag.H2_2, matcherH2_2);
matchers.put(Tag.CODE, matcherCode);
}
private StyleBuilder styleBuilder;
private QueueProvider queueProvider;
private HashMap<String, Pair<String, String>> idLinkLinks;
private HashMap<String, Pair<String, String>> idImageUrl;
public TagHandlerImpl(StyleBuilder styleBuilder) {
this.styleBuilder = styleBuilder;
idImageUrl = new HashMap<>();
idLinkLinks = new HashMap<>();
}
@Override
public boolean h(Line line) {
return h6(line) || h5(line) || h4(line) || h3(line) || h2(line) || h1(line);
}
@Override
public boolean h1(Line line) {
Matcher matcher = obtain(Tag.H1, line.getSource());
if (matcher != null && matcher.find()) {
line.setType(Line.LINE_TYPE_H1);
line.setStyle(SpannableStringBuilder.valueOf(matcher.group(1)));
inline(line);
line.setStyle(styleBuilder.h1(line.getStyle()));
return true;
}
return false;
}
@Override
public boolean h2(Line line) {
Matcher matcher = obtain(Tag.H2, line.getSource());
if (matcher.find()) {
line.setType(Line.LINE_TYPE_H2);
line.setStyle(SpannableStringBuilder.valueOf(matcher.group(1)));
inline(line);
line.setStyle(styleBuilder.h2(line.getStyle()));
return true;
}
return false;
}
@Override
public boolean h3(Line line) {
Matcher matcher = obtain(Tag.H3, line.getSource());
if (matcher.find()) {
line.setType(Line.LINE_TYPE_H3);
line.setStyle(SpannableStringBuilder.valueOf(matcher.group(1)));
inline(line);
line.setStyle(styleBuilder.h3(line.getStyle()));
return true;
}
return false;
}
@Override
public boolean h4(Line line) {
Matcher matcher = obtain(Tag.H4, line.getSource());
if (matcher.find()) {
line.setType(Line.LINE_TYPE_H4);
line.setStyle(SpannableStringBuilder.valueOf(matcher.group(1)));
inline(line);
line.setStyle(styleBuilder.h4(line.getStyle()));
return true;
}
return false;
}
@Override
public boolean h5(Line line) {
Matcher matcher = obtain(Tag.H5, line.getSource());
if (matcher.find()) {
line.setType(Line.LINE_TYPE_H5);
line.setStyle(SpannableStringBuilder.valueOf(matcher.group(1)));
inline(line);
line.setStyle(styleBuilder.h5(line.getStyle()));
return true;
}
return false;
}
@Override
public boolean h6(Line line) {
Matcher matcher = obtain(Tag.H6, line.getSource());
if (matcher.find()) {
line.setType(Line.LINE_TYPE_H6);
line.setStyle(SpannableStringBuilder.valueOf(matcher.group(1)));
inline(line);
line.setStyle(styleBuilder.h6(line.getStyle()));
return true;
}
return false;
}
@Override
public boolean quota(Line line) {
LineQueue queue = queueProvider.getQueue();
line = line.get();
Matcher matcher = obtain(Tag.QUOTA, line.getSource());
if (matcher.find()) {
line.setType(Line.LINE_TYPE_QUOTA);
Line child = line.createChild(matcher.group(1));
line.attachChildToNext();
line.attachChildToPrev();
Line prev = queue.prevLine();
if (line.parentLine() == null && prev != null && prev.getType() == Line.LINE_TYPE_QUOTA) {
SpannableStringBuilder style = new SpannableStringBuilder(" ");
styleBuilder.quota(style);
while (prev.childLine() != null && prev.childLine().getType() == Line.LINE_TYPE_QUOTA) {
prev = prev.childLine();
styleBuilder.quota(style);
}
prev.copyToNext();
queue.prevLine().setStyle(style);
}
if (quota(child) || ul(child) || ol(child) || h(child)) {
if (child.getHandle() == Line.HANDLE_BY_ROOT) {
if (line.parentLine() == null) {
if (child.getData() == Line.LINE_TYPE_UL) {
line.setStyle(styleBuilder.ul2(child.getStyle(), findCount(Tag.QUOTA, line, 1) - 1, child.getAttr()));
} else {
line.setStyle(styleBuilder.ol2(child.getStyle(), findCount(Tag.QUOTA, line, 1) - 1, child.getAttr(), child.getCount()));
}
} else {
line.setData(child.getData());
line.setStyle(child.getStyle());
line.setAttr(child.getAttr());
line.setCount(child.getCount());
line.setHandle(Line.HANDLE_BY_ROOT);
}
return true;
}
} else {
child.setStyle(SpannableStringBuilder.valueOf(child.getSource()));
inline(child);
}
line.setStyle(styleBuilder.quota(child.getStyle()));
return true;
}
return false;
}
@Override
public boolean ul(Line line) {
return ul(line, 0);
}
private boolean ul(Line line, int level) {
if (line.getSource() != null && (line.getSource().startsWith("- [ ]") || line.getSource().startsWith("- [x]"))) {
if (line.getSource().startsWith("- [x]")) {
return todoChecked(line);
} else {
return todoUnChecked(line);
}
}
Matcher matcher = obtain(Tag.UL, line.getSource());
if (matcher.find()) {
line.setType(Line.LINE_TYPE_UL);
Line line1 = line.createChild(matcher.group(1));
line.setAttr(0);
Line parent = line.parentLine();
LineQueue queue = queueProvider.getQueue();
Line prev = line.prevLine();
boolean inQuota = queue.currLine().getType() == Line.LINE_TYPE_QUOTA;
if (inQuota) {
line.setHandle(Line.HANDLE_BY_ROOT);
line.setData(Line.LINE_TYPE_UL);
}
if (prev != null && (prev.getType() == Line.LINE_TYPE_OL || prev.getType() == Line.LINE_TYPE_UL)) {
if (level > 0) {
line.setAttr(level);
} else {
String m = line.getSource().substring(matcher.start(), matcher.start(1) - 2);
m = m.replaceAll("\\t", " ");
if (m.length() > prev.getAttr() * 2 + 1)
line.setAttr(prev.getAttr() + 1);
else
line.setAttr(m.length() / 2);
}
}
if (inQuota) {
line.setStyle(" ");
} else {
line.setStyle(styleBuilder.ul(" ", line.getAttr()));
}
if (find(Tag.UL, line1)) {
int nextLevel = line.getAttr() + 1;
line1.unAttachFromParent();
if (parent != null) {
Line p = parent.copyToNext();
p.addChild(line1);
queue.next();
ul(line1, nextLevel);
if (inQuota) {
while (p.parentLine() != null) {
p = p.parentLine();
}
p.setStyle(styleBuilder.ul2(line1.getStyle(), findCount(Tag.QUOTA, p, 1) - 1, line1.getAttr()));
} else {
while (p != null && p.getType() == Line.LINE_TYPE_QUOTA) {
p.setStyle(styleBuilder.quota(line1.getStyle()));
p = p.parentLine();
}
}
} else {
line.addNext(line1);
queue.next();
ul(queue.currLine(), nextLevel);
}
return true;
}
if (find(Tag.OL, line1)) {
int nextLevel = line.getAttr() + 1;
line1.unAttachFromParent();
if (parent != null) {
Line p = parent.copyToNext();
p.addChild(line1);
queue.next();
ol(line1, nextLevel);
if (inQuota) {
while (p.parentLine() != null) {
p = p.parentLine();
}
p.setStyle(styleBuilder.ol2(line1.getStyle(), findCount(Tag.QUOTA, p, 1) - 1, line1.getAttr(), line1.getCount()));
} else {
while (p != null && p.getType() == Line.LINE_TYPE_QUOTA) {
p.setStyle(styleBuilder.quota(line1.getStyle()));
p = p.parentLine();
}
}
} else {
line.addNext(line1);
queue.next();
ol(queue.currLine(), nextLevel);
}
return true;
}
CharSequence userText;
if (h(line1)) {
userText = line1.getStyle();
} else {
userText = line1.getSource();
}
SpannableStringBuilder builder;
if (userText instanceof SpannableStringBuilder) {
builder = (SpannableStringBuilder) userText;
} else {
builder = new SpannableStringBuilder(userText);
}
line.setStyle(builder);
inline(line);
if (!inQuota) {
line.setStyle(styleBuilder.ul(line.getStyle(), line.getAttr()));
}
return true;
}
return false;
}
@Override
public boolean ol(Line line) {
return ol(line, 0);
}
private boolean ol(Line line, int level) {
if (line.getSource() != null && (line.getSource().startsWith("- [ ]") || line.getSource().startsWith("- [x]"))) {
if (line.getSource().startsWith("- [x]")) {
return todoChecked(line);
} else {
return todoUnChecked(line);
}
}
Matcher matcher = obtain(Tag.OL, line.getSource());
if (matcher.find()) {
line.setType(Line.LINE_TYPE_OL);
Line line1 = new Line(matcher.group(1));
line.setAttr(0);
Line parent = line.parentLine();
LineQueue queue = queueProvider.getQueue();
Line prev = line.prevLine();
boolean inQuota = queue.currLine().getType() == Line.LINE_TYPE_QUOTA;
if (inQuota) {
line.setHandle(Line.HANDLE_BY_ROOT);
line.setData(Line.LINE_TYPE_OL);
}
if (prev != null && (prev.getType() == Line.LINE_TYPE_OL || prev.getType() == Line.LINE_TYPE_UL)) {
if (level > 0) {
line.setAttr(level);
} else {
String m = line.getSource().substring(matcher.start(), matcher.start(1) - 2);
m = m.replaceAll("\\t", " ");
if (m.length() > prev.getAttr() * 2 + 1)
line.setAttr(prev.getAttr() + 1);
else
line.setAttr(m.length() / 2);
}
}
if (prev != null && prev.getType() == Line.LINE_TYPE_OL && prev.getAttr() == line.getAttr()) {
line.setCount(prev.getCount() + 1);
} else {
line.setCount(1);
}
if (inQuota) {
line.setStyle(" ");
} else {
line.setStyle(styleBuilder.ol(" ", line.getAttr(), line.getCount()));
}
if (find(Tag.UL, line1)) {
int nextLevel = line.getAttr() + 1;
line1.unAttachFromParent();
if (parent != null) {
Line p = parent.copyToNext();
p.addChild(line1);
queue.next();
ul(line1, nextLevel);
if (inQuota) {
while (p.parentLine() != null) {
p = p.parentLine();
}
p.setStyle(styleBuilder.ul2(line1.getStyle(), findCount(Tag.QUOTA, p, 1) - 1, line1.getAttr()));
} else {
while (p != null && p.getType() == Line.LINE_TYPE_QUOTA) {
p.setStyle(styleBuilder.quota(line1.getStyle()));
p = p.parentLine();
}
}
} else {
line.addNext(line1);
queue.next();
ul(queue.currLine(), nextLevel);
}
return true;
}
if (find(Tag.OL, line1)) {
int nextLevel = line.getAttr() + 1;
line1.unAttachFromParent();
if (parent != null) {
Line p = parent.copyToNext();
p.addChild(line1);
queue.next();
ol(line1, nextLevel);
if (inQuota) {
while (p.parentLine() != null) {
p = p.parentLine();
}
p.setStyle(styleBuilder.ol2(line1.getStyle(), findCount(Tag.QUOTA, p, 1) - 1, line1.getAttr(), line1.getCount()));
} else {
while (p != null && p.getType() == Line.LINE_TYPE_QUOTA) {
p.setStyle(styleBuilder.quota(line1.getStyle()));
p = p.parentLine();
}
}
} else {
line.addNext(line1);
queue.next();
ol(queue.currLine(), nextLevel);
}
return true;
}
CharSequence userText;
if (h(line1)) {
userText = line1.getStyle();
} else {
userText = line1.getSource();
}
SpannableStringBuilder builder;
if (userText instanceof SpannableStringBuilder) {
builder = (SpannableStringBuilder) userText;
} else {
builder = new SpannableStringBuilder(userText);
}
line.setStyle(builder);
inline(line);
if (!inQuota) {
line.setStyle(styleBuilder.ol(line.getStyle(), line.getAttr(), line.getCount()));
}
return true;
}
return false;
}
@Override
public boolean gap(Line line) {
line = line.get();
Matcher matcher = obtain(Tag.GAP, line.getSource());
if (matcher.matches()) {
line.setType(Line.LINE_TYPE_GAP);
line.setStyle(styleBuilder.gap());
return true;
}
return false;
}
@Override
public boolean em(Line line) {
line = line.get();
SpannableStringBuilder builder = (SpannableStringBuilder) line.getStyle();
Matcher matcher = obtain(Tag.EM, builder);
while (matcher.find()) {
int start = matcher.start(1);
int end = matcher.end(1);
if (checkInCode(builder, start, end)) {
continue;
}
SpannableStringBuilder sb = (SpannableStringBuilder) builder.subSequence(matcher.start(3), matcher.end(3));
builder.delete(matcher.start(1), matcher.end(1));
builder.insert(matcher.start(1), styleBuilder.em(sb));
em(line);
return true;
}
return false;
}
@Override
public boolean italic(Line line) {
line = line.get();
SpannableStringBuilder builder = (SpannableStringBuilder) line.getStyle();
Matcher matcher = obtain(Tag.ITALIC, builder);
while (matcher.find()) {
int start = matcher.start(1);
int end = matcher.end(1);
if (checkInCode(builder, start, end)) {
continue;
}
SpannableStringBuilder sb = (SpannableStringBuilder) builder.subSequence(matcher.start(3), matcher.end(3));
builder.delete(matcher.start(1), matcher.end(1));
builder.insert(matcher.start(1), styleBuilder.italic(sb));
italic(line);
return true;
}
return false;
}
@Override
public boolean emItalic(Line line) {
line = line.get();
SpannableStringBuilder builder = (SpannableStringBuilder) line.getStyle();
Matcher matcher = obtain(Tag.EM_ITALIC, builder);
while (matcher.find()) {
int start = matcher.start(1);
int end = matcher.end(1);
if (checkInCode(builder, start, end)) {
continue;
}
SpannableStringBuilder sb = (SpannableStringBuilder) builder.subSequence(matcher.start(3), matcher.end(3));
builder.delete(matcher.start(1), matcher.end(1));
builder.insert(matcher.start(1), styleBuilder.emItalic(sb));
emItalic(line);
return true;
}
return false;
}
@Override
public boolean code(Line line) {
line = line.get();
SpannableStringBuilder builder = (SpannableStringBuilder) line.getStyle();
if (builder.toString().endsWith("```") || builder.toString().startsWith("```")) {
return codeBlock2(line);
}
Matcher matcher = obtain(Tag.CODE, builder);
if (matcher.find()) {
String content = matcher.group(3);
builder.delete(matcher.start(1), matcher.end(1));
builder.insert(matcher.start(1), styleBuilder.code(content));
code(line);
return true;
}
return false;
}
@Override
public boolean email(Line line) {
line = line.get();
SpannableStringBuilder builder = (SpannableStringBuilder) line.getStyle();
Matcher matcher = obtain(Tag.EMAIL, builder);
if (matcher.find()) {
SpannableStringBuilder sb = (SpannableStringBuilder) builder.subSequence(matcher.start(2), matcher.end(2));
builder.delete(matcher.start(1), matcher.end(1));
builder.insert(matcher.start(1), styleBuilder.email(sb));
email(line);
return true;
}
return false;
}
@Override
public boolean delete(Line line) {
line = line.get();
SpannableStringBuilder builder = (SpannableStringBuilder) line.getStyle();
Matcher matcher = obtain(Tag.DELETE, builder);
while (matcher.find()) {
int start = matcher.start(1);
int end = matcher.end(1);
if (checkInCode(builder, start, end)) {
continue;
}
SpannableStringBuilder sb = (SpannableStringBuilder) builder.subSequence(matcher.start(3), matcher.end(3));
builder.delete(matcher.start(1), matcher.end(1));
builder.insert(matcher.start(1), styleBuilder.delete(sb));
delete(line);
return true;
}
return false;
}
@Override
public boolean autoLink(Line line) {
line = line.get();
SpannableStringBuilder builder = (SpannableStringBuilder) line.getStyle();
Matcher matcher = obtain(Tag.AUTO_LINK, builder);
boolean m = false;
while (matcher.find()) {
String content = matcher.group();
builder.delete(matcher.start(), matcher.end());
builder.insert(matcher.start(), styleBuilder.link(content, content, ""));
m = true;
}
return m;
}
@Override
public boolean link(Line line) {
line = line.get();
SpannableStringBuilder builder = (SpannableStringBuilder) line.getStyle();
Matcher matcher = obtain(Tag.LINK, builder);
if (matcher.find()) {
String title = matcher.group(2);
String link = matcher.group(3);
String hint = matcher.group(6);
builder.delete(matcher.start(1), matcher.end(1));
builder.insert(matcher.start(1), styleBuilder.link(title, link, hint));
link(line);
return true;
}
return false;
}
@Override
public boolean link2(Line line) {
line = line.get();
SpannableStringBuilder builder = (SpannableStringBuilder) line.getStyle();
Matcher matcher = obtain(Tag.LINK2, builder);
if (matcher.find()) {
String title = matcher.group(2);
String id = matcher.group(3);
Pair<String, String> link = idLinkLinks.get(id);
if (link != null) {
builder.delete(matcher.start(1), matcher.end(1));
builder.insert(matcher.start(1), styleBuilder.link(title, link.first, link.second));
} else {
return false;
}
link2(line);
return true;
}
return false;
}
@Override
public boolean linkId(String line) {
Matcher matcher = obtain(Tag.LINK_ID, line);
if (matcher.find()) {
String id = matcher.group(1);
String link = matcher.group(2);
String hint = matcher.group(5);
idLinkLinks.put(id, new Pair<>(link, hint));
return true;
}
return false;
}
@Override
public boolean image(Line line) {
line = line.get();
SpannableStringBuilder builder = (SpannableStringBuilder) line.getStyle();
Matcher matcher = obtain(Tag.IMAGE, builder);
if (matcher.find()) {
String title = matcher.group(2);
String link = matcher.group(3);
String hint = matcher.group(6);
builder.delete(matcher.start(1), matcher.end(1));
builder.insert(matcher.start(1), styleBuilder.image(title, link, hint));
image(line);
return true;
}
return false;
}
@Override
public boolean image2(Line line) {
line = line.get();
SpannableStringBuilder builder = (SpannableStringBuilder) line.getStyle();
Matcher matcher = obtain(Tag.IMAGE2, builder);
if (matcher.find()) {
String title = matcher.group(2);
String id = matcher.group(3);
Pair<String, String> image = idImageUrl.get(id);
if (image != null) {
builder.delete(matcher.start(1), matcher.end(1));
builder.insert(matcher.start(1), styleBuilder.image(title, image.first, image.second));
} else {
return false;
}
image2(line);
return true;
}
return false;
}
@Override
public boolean imageId(String line) {
Matcher matcher = obtain(Tag.IMAGE_ID, line);
if (matcher.find()) {
String id = matcher.group(1);
String link = matcher.group(2);
String hint = matcher.group(5);
idImageUrl.put(id, new Pair<>(link, hint));
return true;
}
return false;
}
@Override
public boolean codeBlock1(Line line) {
Matcher matcher = obtain(Tag.CODE_BLOCK_1, line.getSource());
if (matcher.find()) {
String content = matcher.group(2);
LineQueue queue = queueProvider.getQueue();
Line next = queue.nextLine();
StringBuilder sb = new StringBuilder(content);
StringBuilder bsb = new StringBuilder();
while (next != null) {
CharSequence r = get(Tag.CODE_BLOCK_1, next, 2);
if (r == null) {
if (find(Tag.BLANK, next)) {
bsb.append(' ').append('\n');
} else {
break;
}
} else {
if (bsb.length() != 0) {
sb.append(bsb).append(r);
bsb.delete(0, sb.length());
} else {
sb.append('\n').append(r);
}
}
queue.removeNextLine();
next = queue.nextLine();
}
line.setType(Line.LINE_TYPE_CODE_BLOCK_1);
line.setStyle(styleBuilder.codeBlock(sb.toString()));
return true;
}
return false;
}
@Override
public boolean codeBlock2(Line line) {
if (find(Tag.CODE_BLOCK_2, line)) {
LineQueue queue = queueProvider.getQueue();
LineQueue nextQueue = queue.copy();
boolean find = false;
while (nextQueue.nextLine() != null) {
if (find(Tag.CODE_BLOCK_2, nextQueue.nextLine())) {
nextQueue.next();
removePrevBlankLine(nextQueue);
removeNextBlankLine(queue);
find = true;
break;
}
nextQueue.next();
}
if (find) {
StringBuilder sb = new StringBuilder();
queue.next();
queue.removePrevLine();
while (queue.currLine() != nextQueue.currLine()) {
sb.append(queue.currLine().getSource()).append('\n');
queue.next();
queue.removePrevLine();
}
removeNextBlankLine(nextQueue);
nextQueue.currLine().setType(Line.LINE_TYPE_CODE_BLOCK_2);
nextQueue.currLine().setStyle(styleBuilder.codeBlock(sb.toString()));
return true;
} else {
return false;
}
}
return false;
}
@Override public boolean todoChecked(Line line) {
Matcher matcher = obtain(Tag.TODO_CHECKED, line.getSource());
if (matcher != null && matcher.find()) {
line.setType(Line.LINE_TYPE_CHECKED);
line.setStyle(SpannableStringBuilder.valueOf(matcher.group(1)));
inline(line);
line.setStyle(styleBuilder.checked(line.getStyle()));
return true;
}
return false;
}
@Override public boolean todoUnChecked(Line line) {
Matcher matcher = obtain(Tag.TODO_UNCHECKED, line.getSource());
if (matcher != null && matcher.find()) {
line.setType(Line.LINE_TYPE_UN_CHECKED);
line.setStyle(SpannableStringBuilder.valueOf(matcher.group(1)));
inline(line);
line.setStyle(styleBuilder.unChecked(line.getStyle()));
return true;
}
return false;
}
@Override
public boolean inline(Line line) {
boolean flag = code(line);
flag = emItalic(line) || flag;
flag = em(line) || flag;
flag = italic(line) || flag;
flag = delete(line) || flag;
flag = email(line) || flag;
flag = image(line) || flag;
flag = image2(line) || flag;
flag = link(line) || flag;
flag = link2(line) || flag;
flag = autoLink(line) || flag;
return flag;
}
@Override
public boolean find(int tag, Line line) {
return line != null && find(tag, line.getSource());
}
@Override
public boolean find(int tag, String line) {
if (line == null) {
return false;
}
Matcher m = obtain(tag, line);
return m != null && m.find();
}
@Override
public int findCount(int tag, Line line, int group) {
return line == null ? 0 : findCount(tag, line.getSource(), group);
}
@Override
public int findCount(int tag, String line, int group) {
if (line == null) {
return 0;
}
Matcher matcher = obtain(tag, line);
if (matcher == null) {
return 0;
}
if (matcher.find()) {
return findCount(tag, matcher.group(group), group) + 1;
}
return 0;
}
private boolean checkInCode(SpannableStringBuilder builder, int start, int end) {
CodeSpan[] css = builder.getSpans(0, builder.length(), CodeSpan.class);
for (CodeSpan cs : css) {
int c_start = builder.getSpanStart(cs);
int c_end = builder.getSpanEnd(cs);
if (!(c_start >= end || c_end <= start)) {
return true;
}
}
return false;
}
private Matcher obtain(int tag, CharSequence src) {
Matcher m = matchers.get(tag, null);
if (m != null) {
m.reset(src);
}
return m;
}
public void setQueueProvider(QueueProvider queueProvider) {
this.queueProvider = queueProvider;
}
@Override
public CharSequence get(int tag, Line line, int group) {
return get(tag, line.getSource(), group);
}
@Override
public CharSequence get(int tag, CharSequence line, int group) {
Matcher m = obtain(tag, line);
return m.find() ? m.group(group) : null;
}
private void removeNextBlankLine(LineQueue queue) {
while (queue.nextLine() != null) {
if (find(Tag.BLANK, queue.nextLine())) {
queue.removeNextLine();
} else {
return;
}
}
}
private void removePrevBlankLine(LineQueue queue) {
while (queue.prevLine() != null) {
if (find(Tag.BLANK, queue.prevLine())) {
queue.removePrevLine();
} else {
return;
}
}
}
}

View File

@ -1,120 +0,0 @@
package com.zzhoujay.markdown.style;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.support.annotation.NonNull;
import android.text.style.LineHeightSpan;
import android.text.style.ReplacementSpan;
import android.util.Pair;
import java.util.ArrayList;
import java.util.List;
/**
* Created by zhou on 16-7-2.
* 代码块Span
*/
public class CodeBlockSpan extends ReplacementSpan implements LineHeightSpan {
private static final float radius = 10;
private static final int padding = 30;
private int width;
private Drawable drawable;
private int baseLine;
private int lineHeight;
private CharSequence[] ls;
private List<Pair<Integer, Integer>> lines;
public CodeBlockSpan(int width, int color, CharSequence... lines) {
this.width = width;
GradientDrawable g = new GradientDrawable();
g.setColor(color);
g.setCornerRadius(radius);
drawable = g;
this.ls = lines;
}
@Override public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
if (fm != null && lines == null) {
lines = new ArrayList<>();
for (CharSequence c : ls) {
lines.addAll(measureTextLine(c, 0, c.length(), paint));
}
}
return width;
}
@Override public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x,
int top, int y, int bottom, @NonNull Paint paint) {
drawable.setBounds((int) x, top, (int) x + width, bottom);
drawable.draw(canvas);
int lineNum = 0;
x = x + padding;
int i = baseLine + lineHeight / 2 + top;
for (Pair<Integer, Integer> line : lines) {
try {
CharSequence t = ls[lineNum];
canvas.drawText(t, line.first, line.second, x + padding, i, paint);
if (line.second >= t.length()) {
lineNum++;
}
i += lineHeight;
} catch (IndexOutOfBoundsException ignored) {}
}
}
private int getTextInLineLen(CharSequence text, int start, int end, Paint paint) {
int e = start;
while (paint.measureText(text, start, e) < width - padding * 2) {
e++;
if (e > end) {
break;
}
}
return e - 1;
}
private int getTextInLineLenInRange(CharSequence text, int start, int end, int rs, int re, Paint paint) {
int e = rs;
if (rs > end) {
return end;
}
try {
while (paint.measureText(text, start, e) < width - padding * 2) {
e++;
if (e > end || e > re) {
break;
}
}
} catch (Exception ignored) {}
return e - 1;
}
private List<Pair<Integer, Integer>> measureTextLine(CharSequence text, int start, int end, Paint paint) {
List<Pair<Integer, Integer>> lines = new ArrayList<>();
int l = getTextInLineLen(text, start, end, paint);
int count = l;
lines.add(new Pair<>(start, l));
while (l < end) {
int temp = l;
l = getTextInLineLenInRange(text, l, end, l + count - 4, l + count + 4, paint);
count = l - temp;
lines.add(new Pair<>(temp, l));
}
return lines;
}
@Override public void chooseHeight(CharSequence text, int start, int end, int spanstartv, int v, Paint.FontMetricsInt fm) {
int num = lines.size();
lineHeight = fm.bottom - fm.top;
baseLine = -fm.top;
fm.ascent = fm.top;
fm.bottom += num * lineHeight;
fm.descent = fm.bottom;
}
}

View File

@ -1,45 +0,0 @@
package com.zzhoujay.markdown.style;
import android.annotation.SuppressLint;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import android.text.TextPaint;
import android.text.style.URLSpan;
import android.util.Log;
import android.view.View;
/**
* Created by zhou on 16-7-30.
* EmailSpan
*/
@SuppressLint("ParcelCreator")
public class EmailSpan extends URLSpan {
private int color;
public EmailSpan(String email, int color) {
super(email);
this.color = color;
}
@Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
ds.setColor(color);
ds.setUnderlineText(false);
}
@Override
public void onClick(View widget) {
Intent intent = new Intent(Intent.ACTION_SENDTO);
intent.setData(Uri.parse("mailto:" + getURL()));
intent.putExtra(Intent.EXTRA_SUBJECT, "");
intent.putExtra(Intent.EXTRA_TEXT, "");
try {
widget.getContext().startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.w("URLSpan", "Actvity was not found for intent, " + intent.toString());
}
}
}

View File

@ -1,139 +0,0 @@
package com.zzhoujay.markdown.style;
import android.annotation.TargetApi;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Build;
import android.text.Layout;
import android.text.Spanned;
import android.text.style.BulletSpan;
import android.widget.TextView;
import com.zzhoujay.markdown.util.NumberKit;
import java.lang.ref.WeakReference;
/**
* Created by zhou on 16-6-25.
* 列表Span
*/
public class MarkDownBulletSpan extends BulletSpan {
private static final int tab = 40;
private static final int mGapWidth = 40;
private static final int BULLET_RADIUS = 6;
private final boolean mWantColor;
private final int mColor;
private final String index;
private int level = 0;
private int margin;
private static Path circleBulletPath = null;
private static Path rectBulletPath = null;
private WeakReference<TextView> textViewWeakReference;
public MarkDownBulletSpan(int l, int color, int pointIndex, TextView textView) {
super(mGapWidth, color);
level = l;
if (pointIndex > 0) {
if (level == 1) {
this.index = NumberKit.toRomanNumerals(pointIndex);
} else if (level >= 2) {
this.index = NumberKit.toABC(pointIndex - 1);
} else {
this.index = pointIndex + "";
}
} else {
index = null;
}
mWantColor = true;
mColor = color;
textViewWeakReference = new WeakReference<>(textView);
}
public MarkDownBulletSpan(int level, int color, int pointIndex) {
super(mGapWidth, color);
this.level = level;
if (pointIndex > 0) {
if (level == 1) {
this.index = NumberKit.toRomanNumerals(pointIndex);
} else if (level >= 2) {
this.index = NumberKit.toABC(pointIndex - 1);
} else {
this.index = pointIndex + "";
}
} else {
index = null;
}
mWantColor = true;
mColor = color;
}
@Override
public int getLeadingMargin(boolean first) {
TextView textView = textViewWeakReference != null ? textViewWeakReference.get() : null;
if (index != null && textView != null) {
margin = (int) (tab + (mGapWidth + textView.getPaint().measureText(index)) * (level + 1));
} else {
margin = (2 * BULLET_RADIUS + mGapWidth) * (level + 1) + tab;
}
return margin;
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@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 l) {
if (((Spanned) text).getSpanStart(this) == start) {
int oldcolor = 0;
if (mWantColor) {
oldcolor = p.getColor();
p.setColor(mColor);
}
if (index != null) {
c.drawText(index + '.', x - p.measureText(index) + margin - mGapWidth, baseline, p);
} else {
Paint.Style style = p.getStyle();
if (level == 1) {
p.setStyle(Paint.Style.STROKE);
} else {
p.setStyle(Paint.Style.FILL);
}
if (c.isHardwareAccelerated()) {
Path path;
if (level >= 2) {
if (rectBulletPath == null) {
rectBulletPath = new Path();
float w = 1.2f * BULLET_RADIUS;
rectBulletPath.addRect(-w, -w, w, w, Path.Direction.CW);
}
path = rectBulletPath;
} else {
if (circleBulletPath == null) {
circleBulletPath = new Path();
// Bullet is slightly better to avoid aliasing artifacts on mdpi devices.
circleBulletPath.addCircle(0.0f, 0.0f, 1.2f * BULLET_RADIUS, Path.Direction.CW);
}
path = circleBulletPath;
}
c.save();
c.translate(x + margin - mGapWidth, (top + bottom) / 2.0f);
c.drawPath(path, p);
c.restore();
} else {
c.drawCircle(x + margin - mGapWidth, (top + bottom) / 2.0f, BULLET_RADIUS, p);
}
p.setStyle(style);
}
if (mWantColor) {
p.setColor(oldcolor);
}
}
}
}

View File

@ -1,106 +0,0 @@
package com.zzhoujay.markdown.style;
import android.annotation.TargetApi;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Build;
import android.text.style.ReplacementSpan;
import com.zzhoujay.markdown.util.NumberKit;
/**
* Created by zhou on 16-7-3.
* 列表Span
*/
public class MarkDownInnerBulletSpan extends ReplacementSpan {
private static final int BULLET_RADIUS = 6;
private static final int tab = 40;
private static final int gap = 40;
private final int mColor;
private final String index;
private int margin;
private int level;
private static Path circleBulletPath = null;
private static Path rectBulletPath = null;
public MarkDownInnerBulletSpan(int level, int mColor, int index) {
this.mColor = mColor;
this.level = level;
if (index > 0) {
if (level == 1) {
this.index = NumberKit.toRomanNumerals(index) + '.';
} else if (level >= 2) {
this.index = NumberKit.toABC(index - 1) + '.';
} else {
this.index = index + ".";
}
} else {
this.index = null;
}
}
@Override
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
if (index == null) {
margin = tab + (gap + BULLET_RADIUS * 2) * (level + 1);
} else {
margin = (int) (tab + (gap + paint.measureText(index)) * (level + 1));
}
return (int) (margin + paint.measureText(text, start, end));
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
int oldcolor = paint.getColor();
paint.setColor(mColor);
// draw bullet
if (index != null) {
canvas.drawText(index, x + tab, y, paint);
} else {
Paint.Style style = paint.getStyle();
if (level == 1) {
paint.setStyle(Paint.Style.STROKE);
} else {
paint.setStyle(Paint.Style.FILL);
}
if (canvas.isHardwareAccelerated()) {
Path path;
if (level >= 2) {
if (rectBulletPath == null) {
rectBulletPath = new Path();
float w = 1.2f * BULLET_RADIUS;
rectBulletPath.addRect(-w, -w, w, w, Path.Direction.CW);
}
path = rectBulletPath;
} else {
if (circleBulletPath == null) {
circleBulletPath = new Path();
// Bullet is slightly better to avoid aliasing artifacts on mdpi devices.
circleBulletPath.addCircle(0.0f, 0.0f, 1.2f * BULLET_RADIUS, Path.Direction.CW);
}
path = circleBulletPath;
}
canvas.save();
canvas.translate(x + margin - gap, (top + bottom) / 2.0f);
canvas.drawPath(path, paint);
canvas.restore();
} else {
canvas.drawCircle(x + margin - gap, (top + bottom) / 2.0f, BULLET_RADIUS, paint);
}
paint.setStyle(style);
}
// drawText
canvas.drawText(text, start, end, x + margin, y, paint);
paint.setColor(oldcolor);
}
}

View File

@ -1,147 +0,0 @@
package com.zzhoujay.markdown.style;
import android.annotation.TargetApi;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Build;
import android.text.Layout;
import android.text.Spanned;
import android.text.style.QuoteSpan;
import android.widget.TextView;
import com.zzhoujay.markdown.util.NumberKit;
import java.lang.ref.WeakReference;
/**
* Created by zhou on 16-7-30.
*/
public class QuotaBulletSpan extends QuoteSpan {
private static final int tab = 40;
private static final int mGapWidth = 40;
private static final int BULLET_RADIUS = 6;
private static final int STRIPE_WIDTH = 15;
private static final int GAP_WIDTH = 40;
private static Path circleBulletPath = null;
private static Path rectBulletPath = null;
private final String index;
private int level = 0;
private int bulletColor;
private int margin;
private WeakReference<TextView> textViewWeakReference;
private int quotaLevel;
public QuotaBulletSpan(int quotaLevel, int bulletLevel, int quotaColor, int bulletColor, int pointIndex, TextView textView) {
super(quotaColor);
this.quotaLevel = quotaLevel;
this.level = bulletLevel;
if (pointIndex > 0) {
if (bulletLevel == 1) {
this.index = NumberKit.toRomanNumerals(pointIndex);
} else if (bulletLevel >= 2) {
this.index = NumberKit.toABC(pointIndex - 1);
} else {
this.index = pointIndex + "";
}
} else {
index = null;
}
this.bulletColor = bulletColor;
this.textViewWeakReference = new WeakReference<>(textView);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@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) {
// draw quota
Paint.Style style = p.getStyle();
int color = p.getColor();
p.setStyle(Paint.Style.FILL);
p.setColor(getColor());
int i = 0;
int quotaWidth = STRIPE_WIDTH + GAP_WIDTH;
while (i <= quotaLevel) {
int offset = i * quotaWidth;
c.drawRect(x + offset, top, x + offset + dir * STRIPE_WIDTH, bottom, p);
i++;
}
p.setStyle(style);
p.setColor(color);
// draw bullet
if (((Spanned) text).getSpanStart(this) == start) {
int oldColor;
oldColor = p.getColor();
p.setColor(bulletColor);
if (index != null) {
c.drawText(index + '.', x - p.measureText(index) + margin - mGapWidth, baseline, p);
} else {
style = p.getStyle();
if (level == 1) {
p.setStyle(Paint.Style.STROKE);
} else {
p.setStyle(Paint.Style.FILL);
}
if (c.isHardwareAccelerated()) {
Path path;
if (level >= 2) {
if (rectBulletPath == null) {
rectBulletPath = new Path();
float w = 1.2f * BULLET_RADIUS;
rectBulletPath.addRect(-w, -w, w, w, Path.Direction.CW);
}
path = rectBulletPath;
} else {
if (circleBulletPath == null) {
circleBulletPath = new Path();
// Bullet is slightly better to avoid aliasing artifacts on mdpi devices.
circleBulletPath.addCircle(0.0f, 0.0f, 1.2f * BULLET_RADIUS, Path.Direction.CW);
}
path = circleBulletPath;
}
c.save();
c.translate(x + margin - mGapWidth, (top + bottom) / 2.0f);
c.drawPath(path, p);
c.restore();
} else {
c.drawCircle(x + margin - mGapWidth, (top + bottom) / 2.0f, BULLET_RADIUS, p);
}
p.setStyle(style);
}
p.setColor(oldColor);
}
}
@Override
public int getLeadingMargin(boolean first) {
if (textViewWeakReference == null && margin != 0) {
return margin;
}
TextView textView = textViewWeakReference.get();
if (index != null && textView != null) {
margin = (int) (tab + (mGapWidth + textView.getPaint().measureText(index)) * (level + 1));
} else {
margin = (2 * BULLET_RADIUS + mGapWidth) * (level + 1) + tab;
}
int bulletMargin = (quotaLevel + 1) * (STRIPE_WIDTH + GAP_WIDTH);
margin += bulletMargin;
return margin;
}
}

View File

@ -1,26 +0,0 @@
package com.zzhoujay.markdown.style;
import android.graphics.Paint;
import android.text.style.LineHeightSpan;
/**
* Created by zhou on 16-7-2.
* ScaleHeightSpan
*/
public class ScaleHeightSpan implements LineHeightSpan {
private float scale;
public ScaleHeightSpan(float scale) {
this.scale = scale;
}
@Override
public void chooseHeight(CharSequence text, int start, int end, int spanstartv, int v, Paint.FontMetricsInt fm) {
fm.ascent *= scale;
fm.top *= scale;
fm.descent *= scale;
fm.bottom *= scale;
}
}

View File

@ -1,43 +0,0 @@
package com.zzhoujay.markdown.style;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.text.style.LineHeightSpan;
import android.text.style.ReplacementSpan;
/**
* Created by zhou on 16-7-2.
*/
public class UnderLineSpan extends ReplacementSpan implements LineHeightSpan {
private int height;
private int width;
private Drawable drawable;
public UnderLineSpan(Drawable drawable, int width, int height) {
this.height = height;
this.width = width;
this.drawable = drawable;
}
@Override
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
return (int) paint.measureText(text, start, end);
}
@Override
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
drawable.setBounds((int) x, bottom - height, (int) x + width, bottom);
drawable.draw(canvas);
}
@Override
public void chooseHeight(CharSequence text, int start, int end, int spanstartv, int v, Paint.FontMetricsInt fm) {
fm.top /= 3;
fm.ascent /= 3;
fm.bottom /= 3;
fm.descent /= 3;
}
}

View File

@ -1,25 +0,0 @@
package com.zzhoujay.markdown.util;
import android.graphics.Paint;
/**
* Created by zhou on 16-7-3.
*/
public class FontKit {
public static void scale(Paint.FontMetricsInt fm, float scale) {
fm.top *= scale;
fm.bottom *= scale;
fm.ascent *= scale;
fm.descent *= scale;
fm.leading *= scale;
}
public static void scaleTo(Paint.FontMetricsInt from, Paint.FontMetricsInt to, float scale) {
to.top = (int) (from.top * scale);
to.bottom = (int) (from.bottom * scale);
to.ascent = (int) (from.ascent * scale);
to.descent = (int) (from.descent * scale);
to.leading = (int) (from.leading * scale);
}
}

View File

@ -1,50 +0,0 @@
package com.zzhoujay.markdown.util;
/**
* created by zhou on 16-7-17.
*/
public class NumberKit {
private static final String[] digit = {"", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix"};
private static final String[] ten = {"", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc"};
private static final String[] hundreds = {"", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm"};
private static final String[] thousand = {"", "m", "mm", "mmm"};
private static final int ROMAN_MAX = 4996;
public static String toRomanNumerals(int num) {
while (num > ROMAN_MAX) {
num -= ROMAN_MAX;
}
String th = thousand[num / 1000];
num %= 1000;
String hu = hundreds[num / 100];
num %= 100;
String te = ten[num / 10];
num %= 10;
String di = digit[num];
return String.format("%s%s%s%s", th, hu, te, di);
}
public static String toABC(int num) {
int a = num / 26;
int b = num % 26;
StringBuilder sb = new StringBuilder();
if (a > 26) {
sb.append(toABC(a - 1)).append((char) (b + 'a'));
} else if (a == 0) {
sb.append((char) (b + 'a'));
} else {
sb.append((char) (a + 'a')).append((char) (b + 'a'));
}
return sb.toString();
// while (num < 0) {
// num += 26;
// }
// while (num > 26) {
// num -= 26;
// }
// return String.valueOf((char) (num + 'a'));
}
}