Skip to content

Instantly share code, notes, and snippets.

@Shusshu
Created October 4, 2016 07:53
Show Gist options
  • Save Shusshu/b4c0d5634e69adf9a22fa694cd853f28 to your computer and use it in GitHub Desktop.
Save Shusshu/b4c0d5634e69adf9a22fa694cd853f28 to your computer and use it in GitHub Desktop.
import android.content.Context;
import android.graphics.BlurMaskFilter;
import android.graphics.Color;
import android.graphics.EmbossMaskFilter;
import android.graphics.Typeface;
import android.net.Uri;
import android.text.Layout;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.ImageSpan;
import android.text.style.QuoteSpan;
import android.text.style.RelativeSizeSpan;
import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
import android.text.style.URLSpan;
import android.text.style.UnderlineSpan;
import android.text.style.AlignmentSpan;
import android.text.style.BulletSpan;
import android.text.style.MaskFilterSpan;
import android.text.style.SubscriptSpan;
import android.text.style.SuperscriptSpan;
import android.view.Gravity;
import com.binaryfork.spanny.Spanny;
import org.primeframework.transformer.domain.Document;
import org.primeframework.transformer.domain.Node;
import org.primeframework.transformer.domain.TagNode;
import org.primeframework.transformer.domain.TextNode;
import org.primeframework.transformer.service.TransformException;
import org.primeframework.transformer.service.TransformerSpanny;
/**
* BBCode to Spanny Transformer For a downgraded version of https://github.com/inversoft/prime-transformer (Java 8 to Java 7)
[bullet]First point[/bullet][BR]
[bullet]Second point[/bullet][BR]
[bullet=30]A[/bullet][BR]
[bullet=30]B[/bullet][BR]
[quote]Quote me if you can I'm writing so much text that I can be quoted for ages. Yes[/quote][BR]
[code]public method() static url variable + ; ... ++a - b = c;[/code][BR]
[blur]Blur this[/blur][BR]
[emboss][b]bold emboss this[/b][/emboss][BR]
[b][i]Bold italic[/i][/b][BR]
[s]strike[/s][BR]
[u]underline[/u][BR]
[style relsize=2.2]Bigger text[/style][BR]
32[superscript]2[/superscript] + x[BR]
[align=center]Align me if you can I'm writing so much text that I can be quoted for ages. Yes[/align][BR]
[align=opposite]Align me if you can I'm writing so much text that I can be quoted for ages. Yes[/align][BR]
[align=normal]Align me if you can I'm writing so much text that I can be quoted for ages. Yes[/align][BR]
[url=https://www.google.be]Google LINK[/url][BR]
[phone=+32466666666]0466666666[/phone]
*/
public class BBCodeToSpanTransformer implements TransformerSpanny {
public static final String TAG_BOLD = "b";
public static final String TAG_LABEL = "l";
public static final String TAG_STRIKE = "s";
public static final String TAG_ITALIC = "i";
public static final String TAG_UNDERLINE = "u";
public static final String TAG_URL = "url";
public static final String TAG_PHONE = "phone";
public static final String TAG_IMG = "img";
public static final String TAG_CODE = "code";
public static final String TAG_QUOTE = "quote";
public static final String TAG_STYLE = "style";
public static final String TAG_COLOR = "color";
public static final String TAG_FONT_FAMILY = "font-family";
public static final String TAG_BLUR = "blur";
public static final String TAG_BULLET = "bullet";
public static final String TAG_EMBOSS = "emboss";
public static final String TAG_SUPERSCRIPT = "superscript";
public static final String TAG_SUBSCRIPT = "subscript";
public static final String TAG_FONT = "font";
public static final String TAG_ALIGN = "align";
public static final String ALIGN_CENTER = "center";
public static final String ALIGN_NORMAL = "normal";
public static final String ALIGN_OPPOSITE = "opposite";
public static final String ATTR_BLUR = "blur";
public static final String ATTR_EMBOSS = "emboss";
public static final String ATTR_SUPERSCRIPT = "superscript";
public static final String ATTR_SUBSCRIPT = "subscript";
public static final String ATTR_COLOR = "color";
public static final String ATTR_SIZE = "size";
public static final String ATTR_ABS_SIZE = "abssize";
public static final String ATTR_REL_SIZE = "relsize";
public static final String ATTR_BG_COLOR = "bgcolor";
public static final String ATTR_FG_COLOR = "fgcolor";
public static final String ATTR_FONT_FAMILY = "font-family";
private Context context;
public BBCodeToSpanTransformer(Context context) {
this.context = context;
}
@Override
public Spanny transform(
Document document,
boolean transform)
throws TransformException {
return recurse(document, transform);
}
protected Spanny recurse(
Node node,
boolean transform)
throws TransformException {
Spanny spanny = new Spanny();
if (node instanceof TextNode) {
TextNode textNode = (TextNode) node;
String text = textNode.getBody();
spanny.append(text);
} else if (node instanceof Document) {
Document document = (Document) node;
for (Node child : document.children) {
spanny.append(recurse(child, transform));
}
} else if (node instanceof TagNode) {
TagNode tagNode = (TagNode) node;
String tagName = tagNode.getName().toLowerCase();
if (transform) {
// Transform the children first
Spanny childBuild = new Spanny();
for (Node child : tagNode.children) {
childBuild.append(recurse(child, transform));
}
transformToSpanny(spanny, tagName, tagNode, childBuild);
} else {
spanny.append(tagNode.getRawString());
}
} else {
throw new TransformException("Invalid node class [" + node.getClass() + "]");
}
return spanny;
}
protected void transformToSpanny(
final Spanny spanny,
final String tagName,
final TagNode tagNode,
CharSequence body) {
boolean tagUnknown = false;
String attribute = tagNode.attribute;
Object span = null;
String url;
switch (tagName) {
case TAG_LABEL:
body = "\n" + body + "\n";
span = new StyleSpan(Typeface.BOLD);
break;
case TAG_BOLD:
span = new StyleSpan(Typeface.BOLD);
break;
case TAG_ITALIC:
span = new StyleSpan(Typeface.ITALIC);
break;
case TAG_UNDERLINE:
span = new UnderlineSpan();
break;
case TAG_PHONE:
url = attribute == null || attribute.isEmpty() ? "tel:" + body : "tel:" + attribute;
span = new URLSpan(url);
break;
case TAG_URL:
url = attribute == null || attribute.isEmpty() ? body.toString() : attribute;
span = new URLSpan(url);
break;
case TAG_STRIKE:
span = new StrikethroughSpan();
break;
case TAG_IMG:
url = attribute == null || attribute.isEmpty() ? body.toString() : attribute;
if (!canProcess(url)) {
return;
}
span = new ImageSpan(context, Uri.parse(processUrl(url)));
break;
case TAG_CODE:
span = new QuoteSpan();
break;
case TAG_QUOTE:
span = new QuoteSpan();
break;
case TAG_FONT_FAMILY:
if (attribute != null && !attribute.isEmpty()) {
span = new TypefaceSpan(attribute);
}
break;
case TAG_STYLE:
if (!tagNode.attributes.isEmpty()) {
Object[] spans = new Object[tagNode.attributes.size()];
int i = 0;
for (String attrName : tagNode.attributes.keySet()) {
span = createStyleSpan(attrName, tagNode.attributes.get(attrName));
if (span == null) {
continue;
}
spans[i] = span;
i++;
}
spanny.append(body, spans);
} else {
styleSingleAttributeBehavior(spanny, tagName, tagNode, body);
}
break;
case TAG_COLOR:
span = new ForegroundColorSpan(Color.parseColor(attribute));
break;
case TAG_BLUR:
span = makeBlur();
break;
case TAG_BULLET:
if (tagNode.attribute != null && !tagNode.attribute.isEmpty()) {
try {
span = new BulletSpan(Integer.valueOf(tagNode.attribute));
} catch (NumberFormatException ex) {
span = new BulletSpan();
}
} else {
span = new BulletSpan();
}
break;
case TAG_ALIGN:
Layout.Alignment alignment = Layout.Alignment.ALIGN_CENTER;
if (tagNode.attribute != null) {
switch (tagNode.attribute) {
case ALIGN_CENTER:
alignment = Layout.Alignment.ALIGN_CENTER;
break;
case ALIGN_NORMAL:
alignment = Layout.Alignment.ALIGN_NORMAL;
break;
case ALIGN_OPPOSITE:
alignment = Layout.Alignment.ALIGN_OPPOSITE;
break;
default:
alignment = Layout.Alignment.ALIGN_CENTER;
}
}
span = new AlignmentSpan.Standard(alignment);
break;
case TAG_EMBOSS:
span = makeEmboss();
break;
case TAG_SUPERSCRIPT:
span = new SuperscriptSpan();
break;
case TAG_SUBSCRIPT:
span = new SubscriptSpan();
break;
case TAG_FONT:
if (tagNode.attribute != null && !tagNode.attribute.isEmpty()) {
try {
Typeface font = Typeface.createFromAsset(getContext().getAssets(), tagNode.attribute);
if (font != null) {
span = new CustomTypefaceSpan(font);
}
} catch (Exception e) {
//Do nothing
}
}
break;
default:
tagUnknown = true;
}
if (tagNode.attributes.isEmpty() && !tagUnknown) {
spanny.append(body, span);
}
}
/**
* Intended to be available for a child class to change the default behavior
*
* @param spanny
* @param tagName
* @param tagNode
* @param body
*/
protected void styleSingleAttributeBehavior(
final Spanny spanny,
final String tagName,
final TagNode tagNode,
CharSequence body) {
}
protected Object createStyleSpan(
final String attrName,
final String attrValue) {
switch (attrName) {
case ATTR_COLOR:
case ATTR_FG_COLOR:
return new ForegroundColorSpan(Color.parseColor(attrValue));
case ATTR_BG_COLOR:
return new BackgroundColorSpan(Color.parseColor(attrValue));
case ATTR_SIZE:
case ATTR_ABS_SIZE:
return new AbsoluteSizeSpan(Integer.parseInt(attrValue), true);
case ATTR_REL_SIZE:
return new RelativeSizeSpan(Float.parseFloat(attrValue));
case ATTR_FONT_FAMILY:
return new TypefaceSpan(attrValue);
case ATTR_BLUR:
return makeBlur();
case ATTR_EMBOSS:
return makeEmboss();
case ATTR_SUPERSCRIPT:
return new SuperscriptSpan();
case ATTR_SUBSCRIPT:
return new SubscriptSpan();
default:
return null;
}
}
private Object makeBlur() {
return new MaskFilterSpan(new BlurMaskFilter(
getContext().getResources().getDisplayMetrics().density * 2, BlurMaskFilter.Blur.NORMAL));
}
private Object makeEmboss() {
return new MaskFilterSpan(new EmbossMaskFilter(new float[] { 1, 1, 1 }, 0.4f, 6, 3.5f));
}
/**
* Intended to be available for a child class to change the default behavior
* By default this transformer does not support network calls as they need to be done outside the main thread
*
* @param url
* @return
*/
protected boolean canProcess(final String url) {
return !url.startsWith("http");
}
/**
* Intended to be available for a child class to change the default behavior
* By default this transformer does not support network calls as they need to be done outside the main thread
*
* @param url
* @return
*/
protected String processUrl(final String url) {
return url;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment