Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ public class AppUtils {
public static final int HOT_FACTOR = 3;
private static final String HOST_ITEM = "item";
private static final String HOST_USER = "user";
private static final HtmlCodeTagHandler CODE_TAG_HANDLER = new HtmlCodeTagHandler();

public static void openWebUrlExternal(Context context, @Nullable WebItem item,
String url, @Nullable CustomTabsSession session) {
Expand Down Expand Up @@ -184,14 +185,16 @@ public static CharSequence fromHtml(String htmlText, boolean compact) {
if (TextUtils.isEmpty(htmlText)) {
return null;
}
CharSequence spanned;
final CharSequence spanned;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//noinspection InlinedApi
spanned = Html.fromHtml(htmlText, compact ?
Html.FROM_HTML_MODE_COMPACT : Html.FROM_HTML_MODE_LEGACY);
Html.FROM_HTML_MODE_COMPACT : Html.FROM_HTML_MODE_LEGACY,
null,
CODE_TAG_HANDLER);
} else {
//noinspection deprecation
spanned = Html.fromHtml(htmlText);
spanned = Html.fromHtml(htmlText, null, CODE_TAG_HANDLER);
}
return trim(spanned);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package io.github.hidroh.materialistic;

import android.text.Editable;
import android.text.Html;
import android.text.Spannable;
import android.text.Spanned;
import android.text.style.TypefaceSpan;

import org.xml.sax.XMLReader;

/**
* TagHandler that 'knows' how to handle <code>code<code> tags.
* This handler can be used in the Html.fromHtml method to support formatting of code tags using the monospace typeface.
*
* Hackernews returns <code>&lt;pre&gt;&lt;code&gt;</code> blocks, but we are only interested in the inner code.
* The pre tag is then ignored by the Html.fromHtml default tag handler.
*/
public class HtmlCodeTagHandler implements Html.TagHandler {
@Override
public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {
if (tag.equalsIgnoreCase("code")) {
if (opening) {
start(output, new Monospace());
} else {
end(output, Monospace.class, new TypefaceSpan("monospace"));
}
}
}

// This is copied from android.text.HtmlToSpannedConverter#start
private static void start(Editable text, Object mark) {
int len = text.length();
text.setSpan(mark, len, len, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
}

// This is copied from android.text.HtmlToSpannedConverter#end
private static void end(Editable text, Class kind, Object repl) {
Object obj = getLast(text, kind);
if (obj != null) {
setSpanFromMark(text, obj, repl);
}
}

// This is copied from android.text.HtmlToSpannedConverter#setSpanFromMark
private static void setSpanFromMark(Spannable text, Object mark, Object... spans) {
int where = text.getSpanStart(mark);
text.removeSpan(mark);
int len = text.length();
if (where != len) {
for (Object span : spans) {
text.setSpan(span, where, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}

// This is copied from android.text.HtmlToSpannedConverter#getLast
private static <T> T getLast(Spanned text, Class<T> kind) {
/*
* This knows that the last returned object from getSpans()
* will be the most recently added.
*/
T[] objs = text.getSpans(0, text.length(), kind);
if (objs.length == 0) {
return null;
} else {
return objs[objs.length - 1];
}
}
// This is copied from android.text.HtmlToSpannedConverter.Monospace
private static class Monospace {}
}
26 changes: 26 additions & 0 deletions app/src/test/java/io/github/hidroh/materialistic/AppUtilsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.content.LocalBroadcastManager;
import android.text.SpannedString;
import android.text.format.DateUtils;
import android.text.style.TypefaceSpan;
import android.view.ContextThemeWrapper;
import android.view.MotionEvent;
import android.view.View;
Expand Down Expand Up @@ -239,6 +241,30 @@ public void testTrimHtmlWhitespaces() {
assertThat(textView).hasTextString("paragraph");
}

@Test
public void testPreformattedTextHasMonospaceTypeface() {
TextView textView = new TextView(context);
textView.setText(AppUtils.fromHtml("<pre><code>val x = myCode()</code></pre>"));
assertThat(textView).hasTextString("val x = myCode()");

SpannedString view = (SpannedString) textView.getText();
TypefaceSpan[] spans = view.getSpans(0, view.length(), TypefaceSpan.class);
assertThat(spans.length).isEqualTo(1);
assertThat(spans[0].getFamily()).isEqualTo("monospace");
}

@Test
public void testPreformattedTextHasMultipleMonospaceTypeface() {
TextView textView = new TextView(context);
textView.setText(AppUtils.fromHtml("<pre><code>val x = myCode()</code></pre><p>some more text<br/></p><pre><code>val y = myCode()</code></pre><p>And more text"));

SpannedString view = (SpannedString) textView.getText();
TypefaceSpan[] spans = view.getSpans(0, view.length(), TypefaceSpan.class);
assertThat(spans.length).isEqualTo(2);
assertThat(spans[0].getFamily()).isEqualTo("monospace");
assertThat(spans[1].getFamily()).isEqualTo("monospace");
}

@Test
public void testOpenExternalUrlNoConnection() {
shadowOf((ConnectivityManager) context
Expand Down