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; ...@@ -13,6 +13,8 @@ import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan; import android.text.style.TypefaceSpan;
import android.widget.TextView; import android.widget.TextView;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import chat.rocket.android.widget.R; import chat.rocket.android.widget.R;
public class InlineHightlighter { public class InlineHightlighter {
...@@ -38,32 +40,47 @@ public class InlineHightlighter { ...@@ -38,32 +40,47 @@ public class InlineHightlighter {
return new ForegroundColorSpan(Color.TRANSPARENT); 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) { 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(); private static void setSpan(SpannableString inputText, Context context,
int highlightStart = length; int start, int end, int markStartLen, int markEndLen) {
for (int i = 0; i < length; i++) { if (markStartLen > 0) {
char chr = text.charAt(i); inputText.setSpan(createTransparentSpan(),
if (chr == '`') { start, start + markStartLen,
if (i > highlightStart) { Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
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;
}
}
} }
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; 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.SpannableString;
import android.text.Spanned; import android.text.Spanned;
import android.text.style.ClickableSpan;
import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import org.nibor.autolink.LinkExtractor; import org.nibor.autolink.LinkExtractor;
import org.nibor.autolink.LinkSpan; import org.nibor.autolink.LinkSpan;
import org.nibor.autolink.LinkType; import org.nibor.autolink.LinkType;
...@@ -41,33 +33,10 @@ public class Linkify { ...@@ -41,33 +33,10 @@ public class Linkify {
final int idx2 = link.getEndIndex(); final int idx2 = link.getEndIndex();
final String url = text.subSequence(idx1, idx2).toString(); 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; 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; 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.graphics.Typeface;
import android.net.Uri;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.Spanned; import android.text.Spanned;
import android.text.style.AbsoluteSizeSpan; import android.text.style.AbsoluteSizeSpan;
import android.text.style.ClickableSpan;
import android.text.style.StrikethroughSpan; import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan; import android.text.style.StyleSpan;
import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
...@@ -21,16 +29,56 @@ public class MarkDown { ...@@ -21,16 +29,56 @@ public class MarkDown {
*/ */
public static void apply(TextView textView) { public static void apply(TextView textView) {
SpannableString text = new SpannableString(textView.getText()); SpannableString text = new SpannableString(textView.getText());
removeImage(text);
highlightLink1(text);
highlightLink2(text);
bold(text); bold(text);
italic(text); italic(text);
strike(text); strike(text);
textView.setText(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) { private static void bold(SpannableString inputText) {
Pattern boldPattern = Pattern.compile( Matcher matcher = BOLD_PATTERN.matcher(inputText);
"(^|&gt;|[ >_~`])(\\*{1,2})[^\\*\\r\\n]+(\\*{1,2})([<_~`]|\\B|\\b|$)", Pattern.MULTILINE);
Matcher matcher = boldPattern.matcher(inputText);
while (matcher.find()) { while (matcher.find()) {
setSpan(new StyleSpan(Typeface.BOLD), inputText, setSpan(new StyleSpan(Typeface.BOLD), inputText,
matcher.start() + matcher.group(1).length(), matcher.start() + matcher.group(1).length(),
...@@ -40,10 +88,10 @@ public class MarkDown { ...@@ -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) { private static void italic(SpannableString inputText) {
Pattern italicPattern = Pattern.compile( Matcher matcher = ITALIC_PATTERN.matcher(inputText);
"(^|&gt;|[ >*~`])(\\_)[^\\_\\r\\n]+(\\_)([<*~`]|\\B|\\b|$)", Pattern.MULTILINE);
Matcher matcher = italicPattern.matcher(inputText);
while (matcher.find()) { while (matcher.find()) {
setSpan(new StyleSpan(Typeface.ITALIC), inputText, setSpan(new StyleSpan(Typeface.ITALIC), inputText,
matcher.start() + matcher.group(1).length(), matcher.start() + matcher.group(1).length(),
...@@ -53,10 +101,10 @@ public class MarkDown { ...@@ -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) { private static void strike(SpannableString inputText) {
Pattern strikePattern = Pattern.compile( Matcher matcher = STRIKE_PATTERN.matcher(inputText);
"(^|&gt;|[ >_*`])(\\~{1,2})[^~\\r\\n]+(\\~{1,2})([<_*`]|\\B|\\b|$)", Pattern.MULTILINE);
Matcher matcher = strikePattern.matcher(inputText);
while (matcher.find()) { while (matcher.find()) {
setSpan(new StrikethroughSpan(), inputText, setSpan(new StrikethroughSpan(), inputText,
matcher.start() + matcher.group(1).length(), matcher.start() + matcher.group(1).length(),
...@@ -84,4 +132,28 @@ public class MarkDown { ...@@ -84,4 +132,28 @@ public class MarkDown {
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 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