mirror of
https://github.com/samsonjs/media.git
synced 2026-03-29 10:05:48 +00:00
Add AbsoluteSizeSpan to SpannedHtmlConverter
PiperOrigin-RevId: 309390205
This commit is contained in:
parent
fa0178d043
commit
eb7c14704f
3 changed files with 90 additions and 25 deletions
|
|
@ -19,6 +19,7 @@ package com.google.android.exoplayer2.ui;
|
|||
import android.graphics.Typeface;
|
||||
import android.text.Html;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.AbsoluteSizeSpan;
|
||||
import android.text.style.BackgroundColorSpan;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.text.style.StyleSpan;
|
||||
|
|
@ -66,8 +67,12 @@ import java.util.regex.Pattern;
|
|||
* <li>WebView/Chromium (the intended destination of this HTML) gracefully handles overlapping
|
||||
* tags and usually renders the same result as spanned text in a TextView.
|
||||
* </ul>
|
||||
*
|
||||
* @param text The (possibly span-styled) text to convert to HTML.
|
||||
* @param displayDensity The screen density of the device. WebView treats 1 CSS px as one Android
|
||||
* dp, so to convert size values from Android px to CSS px we need to know the screen density.
|
||||
*/
|
||||
public static String convert(@Nullable CharSequence text) {
|
||||
public static String convert(@Nullable CharSequence text, float displayDensity) {
|
||||
if (text == null) {
|
||||
return "";
|
||||
}
|
||||
|
|
@ -75,7 +80,7 @@ import java.util.regex.Pattern;
|
|||
return escapeHtml(text);
|
||||
}
|
||||
Spanned spanned = (Spanned) text;
|
||||
SparseArray<Transition> spanTransitions = findSpanTransitions(spanned);
|
||||
SparseArray<Transition> spanTransitions = findSpanTransitions(spanned, displayDensity);
|
||||
|
||||
StringBuilder html = new StringBuilder(spanned.length());
|
||||
int previousTransition = 0;
|
||||
|
|
@ -100,11 +105,12 @@ import java.util.regex.Pattern;
|
|||
return html.toString();
|
||||
}
|
||||
|
||||
private static SparseArray<Transition> findSpanTransitions(Spanned spanned) {
|
||||
private static SparseArray<Transition> findSpanTransitions(
|
||||
Spanned spanned, float displayDensity) {
|
||||
SparseArray<Transition> spanTransitions = new SparseArray<>();
|
||||
|
||||
for (Object span : spanned.getSpans(0, spanned.length(), Object.class)) {
|
||||
@Nullable String openingTag = getOpeningTag(span);
|
||||
@Nullable String openingTag = getOpeningTag(span, displayDensity);
|
||||
@Nullable String closingTag = getClosingTag(span);
|
||||
int spanStart = spanned.getSpanStart(span);
|
||||
int spanEnd = spanned.getSpanEnd(span);
|
||||
|
|
@ -120,7 +126,7 @@ import java.util.regex.Pattern;
|
|||
}
|
||||
|
||||
@Nullable
|
||||
private static String getOpeningTag(Object span) {
|
||||
private static String getOpeningTag(Object span, float displayDensity) {
|
||||
if (span instanceof ForegroundColorSpan) {
|
||||
ForegroundColorSpan colorSpan = (ForegroundColorSpan) span;
|
||||
return Util.formatInvariant(
|
||||
|
|
@ -132,6 +138,13 @@ import java.util.regex.Pattern;
|
|||
HtmlUtils.toCssRgba(colorSpan.getBackgroundColor()));
|
||||
} else if (span instanceof HorizontalTextInVerticalContextSpan) {
|
||||
return "<span style='text-combine-upright:all;'>";
|
||||
} else if (span instanceof AbsoluteSizeSpan) {
|
||||
AbsoluteSizeSpan absoluteSizeSpan = (AbsoluteSizeSpan) span;
|
||||
float sizeCssPx =
|
||||
absoluteSizeSpan.getDip()
|
||||
? absoluteSizeSpan.getSize()
|
||||
: absoluteSizeSpan.getSize() / displayDensity;
|
||||
return Util.formatInvariant("<span style='font-size:%.2fpx;'>", sizeCssPx);
|
||||
} else if (span instanceof TypefaceSpan) {
|
||||
@Nullable String fontFamily = ((TypefaceSpan) span).getFamily();
|
||||
return fontFamily != null
|
||||
|
|
@ -171,7 +184,8 @@ import java.util.regex.Pattern;
|
|||
private static String getClosingTag(Object span) {
|
||||
if (span instanceof ForegroundColorSpan
|
||||
|| span instanceof BackgroundColorSpan
|
||||
|| span instanceof HorizontalTextInVerticalContextSpan) {
|
||||
|| span instanceof HorizontalTextInVerticalContextSpan
|
||||
|| span instanceof AbsoluteSizeSpan) {
|
||||
return "</span>";
|
||||
} else if (span instanceof TypefaceSpan) {
|
||||
@Nullable String fontFamily = ((TypefaceSpan) span).getFamily();
|
||||
|
|
|
|||
|
|
@ -309,7 +309,9 @@ import java.util.List;
|
|||
horizontalTranslatePercent,
|
||||
verticalTranslatePercent))
|
||||
.append(Util.formatInvariant("<span style=\"background-color:%s;\">", backgroundColorCss))
|
||||
.append(SpannedToHtmlConverter.convert(cue.text))
|
||||
.append(
|
||||
SpannedToHtmlConverter.convert(
|
||||
cue.text, getContext().getResources().getDisplayMetrics().density))
|
||||
.append("</span>")
|
||||
.append("</div>");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,21 +22,31 @@ import android.graphics.Color;
|
|||
import android.graphics.Typeface;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.AbsoluteSizeSpan;
|
||||
import android.text.style.BackgroundColorSpan;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.text.style.TypefaceSpan;
|
||||
import android.text.style.UnderlineSpan;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.android.exoplayer2.text.span.HorizontalTextInVerticalContextSpan;
|
||||
import com.google.android.exoplayer2.text.span.RubySpan;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
/** Tests for {@link SpannedToHtmlConverter}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class SpannedToHtmlConverterTest {
|
||||
|
||||
private final float displayDensity;
|
||||
|
||||
public SpannedToHtmlConverterTest() {
|
||||
displayDensity =
|
||||
ApplicationProvider.getApplicationContext().getResources().getDisplayMetrics().density;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convert_supportsForegroundColorSpan() {
|
||||
SpannableString spanned = new SpannableString("String with colored section");
|
||||
|
|
@ -46,7 +56,7 @@ public class SpannedToHtmlConverterTest {
|
|||
"String with colored".length(),
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
String html = SpannedToHtmlConverter.convert(spanned);
|
||||
String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
|
||||
|
||||
assertThat(html)
|
||||
.isEqualTo("String with <span style='color:rgba(64,32,16,0.200);'>colored</span> section");
|
||||
|
|
@ -61,7 +71,7 @@ public class SpannedToHtmlConverterTest {
|
|||
"String with highlighted".length(),
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
String html = SpannedToHtmlConverter.convert(spanned);
|
||||
String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
|
||||
|
||||
assertThat(html)
|
||||
.isEqualTo(
|
||||
|
|
@ -78,7 +88,7 @@ public class SpannedToHtmlConverterTest {
|
|||
"Vertical text with 123".length(),
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
String html = SpannedToHtmlConverter.convert(spanned);
|
||||
String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
|
||||
|
||||
assertThat(html)
|
||||
.isEqualTo(
|
||||
|
|
@ -86,6 +96,40 @@ public class SpannedToHtmlConverterTest {
|
|||
+ "horizontal numbers");
|
||||
}
|
||||
|
||||
// Set the screen density so we see that px are handled differently to dp.
|
||||
@Config(qualifiers = "xhdpi")
|
||||
@Test
|
||||
public void convert_supportsAbsoluteSizeSpan_px() {
|
||||
SpannableString spanned = new SpannableString("String with 10px section");
|
||||
spanned.setSpan(
|
||||
new AbsoluteSizeSpan(/* size= */ 10, /* dip= */ false),
|
||||
"String with ".length(),
|
||||
"String with 10px".length(),
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
|
||||
|
||||
// 10 Android px are converted to 5 CSS px because WebView treats 1 CSS px as 1 Android dp
|
||||
// and we're using screen density xhdpi i.e. density=2.
|
||||
assertThat(html).isEqualTo("String with <span style='font-size:5.00px;'>10px</span> section");
|
||||
}
|
||||
|
||||
// Set the screen density so we see that px are handled differently to dp.
|
||||
@Config(qualifiers = "xhdpi")
|
||||
@Test
|
||||
public void convert_supportsAbsoluteSizeSpan_dp() {
|
||||
SpannableString spanned = new SpannableString("String with 10dp section");
|
||||
spanned.setSpan(
|
||||
new AbsoluteSizeSpan(/* size= */ 10, /* dip= */ true),
|
||||
"String with ".length(),
|
||||
"String with 10dp".length(),
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
|
||||
|
||||
assertThat(html).isEqualTo("String with <span style='font-size:10.00px;'>10dp</span> section");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convert_supportsTypefaceSpan() {
|
||||
SpannableString spanned = new SpannableString("String with Times New Roman section");
|
||||
|
|
@ -95,7 +139,7 @@ public class SpannedToHtmlConverterTest {
|
|||
"String with Times New Roman".length(),
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
String html = SpannedToHtmlConverter.convert(spanned);
|
||||
String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
|
||||
|
||||
assertThat(html)
|
||||
.isEqualTo(
|
||||
|
|
@ -112,7 +156,7 @@ public class SpannedToHtmlConverterTest {
|
|||
"String with unstyled".length(),
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
String html = SpannedToHtmlConverter.convert(spanned);
|
||||
String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
|
||||
|
||||
assertThat(html).isEqualTo("String with unstyled section");
|
||||
}
|
||||
|
|
@ -137,7 +181,7 @@ public class SpannedToHtmlConverterTest {
|
|||
"String with bold, italic and bold-italic".length(),
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
String html = SpannedToHtmlConverter.convert(spanned);
|
||||
String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
|
||||
|
||||
assertThat(html)
|
||||
.isEqualTo(
|
||||
|
|
@ -159,7 +203,7 @@ public class SpannedToHtmlConverterTest {
|
|||
"String with over-annotated and under-annotated".length(),
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
String html = SpannedToHtmlConverter.convert(spanned);
|
||||
String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
|
||||
|
||||
assertThat(html)
|
||||
.isEqualTo(
|
||||
|
|
@ -185,28 +229,31 @@ public class SpannedToHtmlConverterTest {
|
|||
"String with underlined".length(),
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
String html = SpannedToHtmlConverter.convert(spanned);
|
||||
String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
|
||||
|
||||
assertThat(html).isEqualTo("String with <u>underlined</u> section.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convert_escapesHtmlInUnspannedString() {
|
||||
String html = SpannedToHtmlConverter.convert("String with <b>bold</b> tags");
|
||||
String html = SpannedToHtmlConverter.convert("String with <b>bold</b> tags", displayDensity);
|
||||
|
||||
assertThat(html).isEqualTo("String with <b>bold</b> tags");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convert_handlesLinebreakInUnspannedString() {
|
||||
String html = SpannedToHtmlConverter.convert("String with\nnew line and\r\ncrlf style too");
|
||||
String html =
|
||||
SpannedToHtmlConverter.convert(
|
||||
"String with\nnew line and\r\ncrlf style too", displayDensity);
|
||||
|
||||
assertThat(html).isEqualTo("String with<br>new line and<br>crlf style too");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convert_doesntConvertAmpersandLineFeedToBrTag() {
|
||||
String html = SpannedToHtmlConverter.convert("String with new line ampersand code");
|
||||
String html =
|
||||
SpannedToHtmlConverter.convert("String with new line ampersand code", displayDensity);
|
||||
|
||||
assertThat(html).isEqualTo("String with&#10;new line ampersand code");
|
||||
}
|
||||
|
|
@ -220,14 +267,16 @@ public class SpannedToHtmlConverterTest {
|
|||
"String with <foo>unrecognised</foo>".length(),
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
String html = SpannedToHtmlConverter.convert(spanned);
|
||||
String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
|
||||
|
||||
assertThat(html).isEqualTo("String with <i><foo>unrecognised</foo></i> tags");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convert_handlesLinebreakInSpannedString() {
|
||||
String html = SpannedToHtmlConverter.convert("String with\nnew line and\r\ncrlf style too");
|
||||
String html =
|
||||
SpannedToHtmlConverter.convert(
|
||||
"String with\nnew line and\r\ncrlf style too", displayDensity);
|
||||
|
||||
assertThat(html).isEqualTo("String with<br>new line and<br>crlf style too");
|
||||
}
|
||||
|
|
@ -236,7 +285,7 @@ public class SpannedToHtmlConverterTest {
|
|||
public void convert_convertsNonAsciiCharactersToAmpersandCodes() {
|
||||
String html =
|
||||
SpannedToHtmlConverter.convert(
|
||||
new SpannableString("Strìng with 優しいの non-ASCII characters"));
|
||||
new SpannableString("Strìng with 優しいの non-ASCII characters"), displayDensity);
|
||||
|
||||
assertThat(html)
|
||||
.isEqualTo("Strìng with 優しいの non-ASCII characters");
|
||||
|
|
@ -256,7 +305,7 @@ public class SpannedToHtmlConverterTest {
|
|||
"String with unrecognised".length(),
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
String html = SpannedToHtmlConverter.convert(spanned);
|
||||
String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
|
||||
|
||||
assertThat(html).isEqualTo("String with unrecognised span");
|
||||
}
|
||||
|
|
@ -270,7 +319,7 @@ public class SpannedToHtmlConverterTest {
|
|||
spanned.setSpan(new StyleSpan(Typeface.ITALIC), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
spanned.setSpan(new UnderlineSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
String html = SpannedToHtmlConverter.convert(spanned);
|
||||
String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
|
||||
|
||||
assertThat(html).isEqualTo("String with <b><i><u>italic-bold-underlined</u></i></b> section");
|
||||
}
|
||||
|
|
@ -287,7 +336,7 @@ public class SpannedToHtmlConverterTest {
|
|||
"String with italic and bold".length(),
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
String html = SpannedToHtmlConverter.convert(spanned);
|
||||
String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
|
||||
|
||||
assertThat(html).isEqualTo("String with <i>italic and <b>bold</b></i> section");
|
||||
}
|
||||
|
|
@ -306,7 +355,7 @@ public class SpannedToHtmlConverterTest {
|
|||
"String with italic and bold section".length(),
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
String html = SpannedToHtmlConverter.convert(spanned);
|
||||
String html = SpannedToHtmlConverter.convert(spanned, displayDensity);
|
||||
|
||||
assertThat(html).isEqualTo("<i>String with italic <b>and bold</i> section</b>");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue