Commit 72bf9d92 authored by Yusuke Iwaki's avatar Yusuke Iwaki Committed by GitHub

Merge pull request #94 from RocketChat/markdown_link

render Markdown link
parents 7418dce9 57e89ce8
......@@ -13,6 +13,8 @@ import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
import android.widget.TextView;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import chat.rocket.android.widget.R;
public class InlineHightlighter {
......@@ -38,32 +40,47 @@ public class InlineHightlighter {
return new ForegroundColorSpan(Color.TRANSPARENT);
}
private static final Pattern HIGHLIGHT_PATTERN = Pattern.compile(
"(^|&gt;|[ >_*~])\\`([^`\\r\\n]+)\\`([<_*~]|\\B|\\b|$)", Pattern.MULTILINE);
private static CharSequence highlightInner(final Context context, final CharSequence text) {
final SpannableString s = new SpannableString(text);
final SpannableString inputText = new SpannableString(text);
Matcher matcher = HIGHLIGHT_PATTERN.matcher(inputText);
while (matcher.find()) {
setSpan(inputText, context,
matcher.start() + matcher.group(1).length(),
matcher.end() - matcher.group(3).length(),
1, 1);
}
return inputText;
}
final int length = text.length();
int highlightStart = length;
for (int i = 0; i < length; i++) {
char chr = text.charAt(i);
if (chr == '`') {
if (i > highlightStart) {
final int highlightEnd = i;
if (highlightStart + 1 < highlightEnd) {
s.setSpan(createTransparentSpan(), highlightStart, highlightStart + 1,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
for (CharacterStyle span : createCharStyles(context)) {
s.setSpan(span, highlightStart + 1, highlightEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
s.setSpan(createTransparentSpan(), highlightEnd, highlightEnd + 1,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
highlightStart = length;
} else {
highlightStart = i;
}
}
private static void setSpan(SpannableString inputText, Context context,
int start, int end, int markStartLen, int markEndLen) {
if (markStartLen > 0) {
inputText.setSpan(createTransparentSpan(),
start, start + markStartLen,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
return s;
CharacterStyle[] spans =
inputText.getSpans(start + markStartLen, end - markEndLen, CharacterStyle.class);
for (CharacterStyle span : spans) {
inputText.removeSpan(span);
}
for (CharacterStyle span : createCharStyles(context)) {
inputText.setSpan(span,
start + markStartLen, end - markEndLen,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if (markEndLen > 0) {
inputText.setSpan(createTransparentSpan(),
end - markEndLen, end,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
package chat.rocket.android.widget.helper;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.ClickableSpan;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import org.nibor.autolink.LinkExtractor;
import org.nibor.autolink.LinkSpan;
import org.nibor.autolink.LinkType;
......@@ -41,33 +33,10 @@ public class Linkify {
final int idx2 = link.getEndIndex();
final String url = text.subSequence(idx1, idx2).toString();
spannableString.setSpan(createLinkSpan(url), idx1, idx2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
spannableString.setSpan(MarkDown.createLinkSpan(url), idx1, idx2,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
return spannableString;
}
private static ClickableSpan createLinkSpan(final String url) {
return new ClickableSpan() {
@Override
public void onClick(View view) {
final Context context = view.getContext();
try {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
return;
} catch (Exception exception) {
}
try {
ClipboardManager clipboardManager =
(ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
clipboardManager.setPrimaryClip(ClipData.newPlainText("linkURL", url));
Toast.makeText(context, "Copied to clipboard", Toast.LENGTH_SHORT).show();
} catch (Exception exception) {
}
}
};
}
}
\ No newline at end of file
package chat.rocket.android.widget.helper;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.graphics.Typeface;
import android.net.Uri;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.ClickableSpan;
import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
......@@ -21,16 +29,56 @@ public class MarkDown {
*/
public static void apply(TextView textView) {
SpannableString text = new SpannableString(textView.getText());
removeImage(text);
highlightLink1(text);
highlightLink2(text);
bold(text);
italic(text);
strike(text);
textView.setText(text);
}
private static final Pattern IMAGE_PATTERN = Pattern.compile(
"!\\[([^\\]]+)\\]\\(((?:http|https):\\/\\/[^\\)]+)\\)", Pattern.MULTILINE);
private static void removeImage(SpannableString inputText) {
Matcher matcher = IMAGE_PATTERN.matcher(inputText);
while (matcher.find()) {
inputText.setSpan(new AbsoluteSizeSpan(0),
matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
private static final Pattern LINK_PATTERN = Pattern.compile(
"\\[([^\\]]+)\\]\\(((?:http|https):\\/\\/[^\\)]+)\\)", Pattern.MULTILINE);
private static void highlightLink1(SpannableString inputText) {
final Matcher matcher = LINK_PATTERN.matcher(inputText);
while (matcher.find()) {
ClickableSpan span = createLinkSpan(matcher.group(2));
setSpan(span, inputText,
matcher.start(), matcher.end(),
1, matcher.group(2).length() + 3);
}
}
private static final Pattern LINK_PATTERN2 = Pattern.compile(
"((?:<|&lt;))((?:http|https):\\/\\/[^\\|]+)\\|(.+?)((?=>|&gt;)(?:>|&gt;))", Pattern.MULTILINE);
private static void highlightLink2(SpannableString inputText) {
Matcher matcher = LINK_PATTERN2.matcher(inputText);
while (matcher.find()) {
ClickableSpan span = createLinkSpan(matcher.group(2));
setSpan(span, inputText,
matcher.start(), matcher.end(),
matcher.group(1).length() + matcher.group(2).length() + 1,
matcher.group(4).length());
}
}
private static final Pattern BOLD_PATTERN = Pattern.compile(
"(^|&gt;|[ >_~`])(\\*{1,2})[^\\*\\r\\n]+(\\*{1,2})([<_~`]|\\B|\\b|$)", Pattern.MULTILINE);
private static void bold(SpannableString inputText) {
Pattern boldPattern = Pattern.compile(
"(^|&gt;|[ >_~`])(\\*{1,2})[^\\*\\r\\n]+(\\*{1,2})([<_~`]|\\B|\\b|$)", Pattern.MULTILINE);
Matcher matcher = boldPattern.matcher(inputText);
Matcher matcher = BOLD_PATTERN.matcher(inputText);
while (matcher.find()) {
setSpan(new StyleSpan(Typeface.BOLD), inputText,
matcher.start() + matcher.group(1).length(),
......@@ -40,10 +88,10 @@ public class MarkDown {
}
}
private static final Pattern ITALIC_PATTERN = Pattern.compile(
"(^|&gt;|[ >*~`])(\\_)[^\\_\\r\\n]+(\\_)([<*~`]|\\B|\\b|$)", Pattern.MULTILINE);
private static void italic(SpannableString inputText) {
Pattern italicPattern = Pattern.compile(
"(^|&gt;|[ >*~`])(\\_)[^\\_\\r\\n]+(\\_)([<*~`]|\\B|\\b|$)", Pattern.MULTILINE);
Matcher matcher = italicPattern.matcher(inputText);
Matcher matcher = ITALIC_PATTERN.matcher(inputText);
while (matcher.find()) {
setSpan(new StyleSpan(Typeface.ITALIC), inputText,
matcher.start() + matcher.group(1).length(),
......@@ -53,10 +101,10 @@ public class MarkDown {
}
}
private static final Pattern STRIKE_PATTERN = Pattern.compile(
"(^|&gt;|[ >_*`])(\\~{1,2})[^~\\r\\n]+(\\~{1,2})([<_*`]|\\B|\\b|$)", Pattern.MULTILINE);
private static void strike(SpannableString inputText) {
Pattern strikePattern = Pattern.compile(
"(^|&gt;|[ >_*`])(\\~{1,2})[^~\\r\\n]+(\\~{1,2})([<_*`]|\\B|\\b|$)", Pattern.MULTILINE);
Matcher matcher = strikePattern.matcher(inputText);
Matcher matcher = STRIKE_PATTERN.matcher(inputText);
while (matcher.find()) {
setSpan(new StrikethroughSpan(), inputText,
matcher.start() + matcher.group(1).length(),
......@@ -84,4 +132,28 @@ public class MarkDown {
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
/*package*/ static ClickableSpan createLinkSpan(final String url) {
return new ClickableSpan() {
@Override
public void onClick(View view) {
final Context context = view.getContext();
try {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
return;
} catch (Exception exception) {
}
try {
ClipboardManager clipboardManager =
(ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
clipboardManager.setPrimaryClip(ClipData.newPlainText("linkURL", url));
Toast.makeText(context, "Copied to clipboard", Toast.LENGTH_SHORT).show();
} catch (Exception exception) {
}
}
};
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment