Skip to content

Instantly share code, notes, and snippets.

@tpb1908
Created March 22, 2017 10:33
Show Gist options
  • Save tpb1908/570f2a8aae1e01f4931a5d77be16e839 to your computer and use it in GitHub Desktop.
Save tpb1908/570f2a8aae1e01f4931a5d77be16e839 to your computer and use it in GitHub Desktop.
Extract attributes from XmlReader in Android HtmlTagHandler
public class HtmlTagHandler implements Html.TagHandler {
private static final String TAG = HtmlTagHandler.class.getSimpleName();
@Override
public void handleTag(final boolean opening, final String tag, Editable output, final XMLReader xmlReader) {
if(opening) {
if(tag.equalsIgnoreCase("a")) {
start(output, new A(getAttribute("href", xmlReader, "error.com")));
}
} else {
if(tag.equalsIgnoreCase("a")) {
A obj = getLast(output, A.class);
// start of the tag
int where = output.getSpanStart(obj);
// end of the tag
int len = output.length();
//TODO-
//Do something with your attribute
}
}
}
private void start(Editable output, Object mark) {
final int len = output.length();
output.setSpan(mark, len, len, Spannable.SPAN_MARK_MARK);
}
/**
* Modified from {@link android.text.Html}
*/
private void end(Editable output, Class kind, boolean paragraphStyle, Object... replaces) {
Object obj = getLast(output, kind);
// start of the tag
int where = output.getSpanStart(obj);
// end of the tag
int len = output.length();
output.removeSpan(obj);
if(where != len) {
int thisLen = len;
// paragraph styles like AlignmentSpan need to end with a new line!
if(paragraphStyle) {
output.append("\n");
thisLen++;
}
for(Object replace : replaces) {
output.setSpan(replace, where, thisLen, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
/**
* Returns the text contained within a span and deletes it from the output string
*/
private CharSequence extractSpanText(Editable output, Class kind) {
final Object obj = getLast(output, kind);
// start of the tag
final int where = output.getSpanStart(obj);
// end of the tag
final int len = output.length();
final CharSequence extractedSpanText = output.subSequence(where, len);
output.delete(where, len);
return extractedSpanText;
}
private static <T> T getLast(Editable text, Class<T> kind) {
final T[] objs = text.getSpans(0, text.length(), kind);
if(objs.length == 0) {
return null;
} else {
for(int i = objs.length; i > 0; i--) {
if(text.getSpanFlags(objs[i - 1]) == Spannable.SPAN_MARK_MARK) {
return objs[i - 1];
}
}
return null;
}
}
private static String getAttribute(@NonNull String attr, @NonNull XMLReader reader, String defaultAttr) {
try {
final Field elementField = reader.getClass().getDeclaredField("theNewElement");
elementField.setAccessible(true);
final Object element = elementField.get(reader);
final Field attsField = element.getClass().getDeclaredField("theAtts");
attsField.setAccessible(true);
final Object atts = attsField.get(element);
final Field dataField = atts.getClass().getDeclaredField("data");
dataField.setAccessible(true);
final String[] data = (String[]) dataField.get(atts);
final Field lengthField = atts.getClass().getDeclaredField("length");
lengthField.setAccessible(true);
final int len = (Integer) lengthField.get(atts);
for(int i = 0; i < len; i++) {
if(attr.equals(data[i * 5 + 1])) {
return data[i * 5 + 4];
}
}
} catch(Exception e) {
Log.e(TAG, "handleTag: ", e);
}
return defaultAttr;
}
private static class A {
String href;
A(String href) {
this.href = href;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment