Add SubtitleWebView

PiperOrigin-RevId: 291927263
This commit is contained in:
ibaker 2020-01-28 15:18:52 +00:00 committed by Ian Baker
parent b83534ee03
commit 2fd8cf0206
2 changed files with 223 additions and 20 deletions

View file

@ -16,18 +16,24 @@
*/
package com.google.android.exoplayer2.ui;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.CaptioningManager;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.text.CaptionStyleCompat;
import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.text.TextOutput;
import com.google.android.exoplayer2.util.Util;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.util.Collections;
import java.util.List;
@ -49,20 +55,50 @@ public final class SubtitleView extends ViewGroup implements TextOutput {
*/
public static final float DEFAULT_BOTTOM_PADDING_FRACTION = 0.08f;
private final SubtitleTextView subtitleTextView;
/**
* Indicates a {@link SubtitleTextView} should be used to display subtitles. This is the default.
*/
public static final int VIEW_TYPE_TEXT = 1;
/**
* Indicates a {@link SubtitleWebView} should be used to display subtitles.
*
* <p>This will instantiate a {@link android.webkit.WebView} and use CSS and HTML styling to
* render the subtitles. This supports some additional styling features beyond those supported by
* {@link SubtitleTextView} such as vertical text.
*/
public static final int VIEW_TYPE_WEB = 2;
/**
* The type of {@link View} to use to display subtitles.
*
* <p>One of:
*
* <ul>
* <li>{@link #VIEW_TYPE_TEXT}
* <li>{@link #VIEW_TYPE_WEB}
* </ul>
*/
@IntDef
@Documented
@Retention(SOURCE)
public @interface ViewType {}
private Output output;
private View innerSubtitleView;
public SubtitleView(Context context) {
this(context, null);
}
// The null checker doesn't like the `addView()` call,
@SuppressWarnings("nullness:method.invocation.invalid")
public SubtitleView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
subtitleTextView = new SubtitleTextView(context, attrs);
addView(subtitleTextView);
SubtitleTextView subtitleTextView = new SubtitleTextView(context, attrs);
output = subtitleTextView;
innerSubtitleView = subtitleTextView;
addView(innerSubtitleView);
}
@Override
public void onCues(List<Cue> cues) {
setCues(cues);
@ -74,20 +110,43 @@ public final class SubtitleView extends ViewGroup implements TextOutput {
* @param cues The cues to display, or null to clear the cues.
*/
public void setCues(@Nullable List<Cue> cues) {
subtitleTextView.onCues(cues != null ? cues : Collections.emptyList());
output.onCues(cues != null ? cues : Collections.emptyList());
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed) {
subtitleTextView.layout(l, t, r, b);
innerSubtitleView.layout(l, t, r, b);
}
}
/**
* Set the type of {@link View} used to display subtitles.
*
* <p>NOTE: {@link #VIEW_TYPE_WEB} is currently very experimental, and doesn't support most
* styling and layout properties of {@link Cue}.
*/
public void setViewType(@ViewType int viewType) {
if (viewType == VIEW_TYPE_TEXT && !(innerSubtitleView instanceof SubtitleTextView)) {
setView(new SubtitleTextView(getContext()));
} else if (viewType == VIEW_TYPE_WEB && !(innerSubtitleView instanceof SubtitleWebView)) {
setView(new SubtitleWebView(getContext()));
} else {
throw new IllegalArgumentException();
}
}
private <T extends View & Output> void setView(T view) {
removeView(innerSubtitleView);
innerSubtitleView = view;
output = view;
addView(view);
}
/**
* Set the text size to a given unit and value.
* <p>
* See {@link TypedValue} for the possible dimension units.
*
* <p>See {@link TypedValue} for the possible dimension units.
*
* @param unit The desired dimension unit.
* @param size The desired size in the given units.
@ -144,7 +203,7 @@ public final class SubtitleView extends ViewGroup implements TextOutput {
}
private void setTextSize(@Cue.TextSizeType int textSizeType, float textSize) {
subtitleTextView.setTextSize(textSizeType, textSize);
output.setTextSize(textSizeType, textSize);
}
/**
@ -154,7 +213,7 @@ public final class SubtitleView extends ViewGroup implements TextOutput {
* @param applyEmbeddedStyles Whether styling embedded within the cues should be applied.
*/
public void setApplyEmbeddedStyles(boolean applyEmbeddedStyles) {
subtitleTextView.setApplyEmbeddedStyles(applyEmbeddedStyles);
output.setApplyEmbeddedStyles(applyEmbeddedStyles);
}
/**
@ -164,7 +223,7 @@ public final class SubtitleView extends ViewGroup implements TextOutput {
* @param applyEmbeddedFontSizes Whether font sizes embedded within the cues should be applied.
*/
public void setApplyEmbeddedFontSizes(boolean applyEmbeddedFontSizes) {
subtitleTextView.setApplyEmbeddedFontSizes(applyEmbeddedFontSizes);
output.setApplyEmbeddedFontSizes(applyEmbeddedFontSizes);
}
/**
@ -184,7 +243,7 @@ public final class SubtitleView extends ViewGroup implements TextOutput {
* @param style A style for the view.
*/
public void setStyle(CaptionStyleCompat style) {
subtitleTextView.setStyle(style);
output.setStyle(style);
}
/**
@ -197,7 +256,7 @@ public final class SubtitleView extends ViewGroup implements TextOutput {
* @param bottomPaddingFraction The bottom padding fraction.
*/
public void setBottomPaddingFraction(float bottomPaddingFraction) {
subtitleTextView.setBottomPaddingFraction(bottomPaddingFraction);
output.setBottomPaddingFraction(bottomPaddingFraction);
}
@TargetApi(19)
@ -223,15 +282,10 @@ public final class SubtitleView extends ViewGroup implements TextOutput {
/* package */ interface Output {
void onCues(List<Cue> cues);
void setTextSize(@Cue.TextSizeType int textSizeType, float textSize);
void setApplyEmbeddedStyles(boolean applyEmbeddedStyles);
void setApplyEmbeddedFontSizes(boolean applyEmbeddedFontSizes);
void setStyle(CaptionStyleCompat style);
void setBottomPaddingFraction(float bottomPaddingFraction);
}
}

View file

@ -0,0 +1,149 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.google.android.exoplayer2.ui;
import static com.google.android.exoplayer2.ui.SubtitleView.DEFAULT_BOTTOM_PADDING_FRACTION;
import static com.google.android.exoplayer2.ui.SubtitleView.DEFAULT_TEXT_SIZE_FRACTION;
import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.webkit.WebView;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.text.CaptionStyleCompat;
import com.google.android.exoplayer2.text.Cue;
import java.util.Collections;
import java.util.List;
/**
* A {@link SubtitleView.Output} that uses a {@link WebView} to render subtitles.
*
* <p>This is useful for subtitle styling not supported by Android's native text libraries such as
* vertical text.
*
* <p>NOTE: This is currently extremely experimental and doesn't support most {@link Cue} styling
* properties.
*/
/* package */ final class SubtitleWebView extends ViewGroup implements SubtitleView.Output {
private final WebView webView;
private List<Cue> cues;
@Cue.TextSizeType private int textSizeType;
private float textSize;
private boolean applyEmbeddedStyles;
private boolean applyEmbeddedFontSizes;
private CaptionStyleCompat style;
private float bottomPaddingFraction;
public SubtitleWebView(Context context) {
this(context, null);
}
public SubtitleWebView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
cues = Collections.emptyList();
textSizeType = Cue.TEXT_SIZE_TYPE_FRACTIONAL;
textSize = DEFAULT_TEXT_SIZE_FRACTION;
applyEmbeddedStyles = true;
applyEmbeddedFontSizes = true;
style = CaptionStyleCompat.DEFAULT;
bottomPaddingFraction = DEFAULT_BOTTOM_PADDING_FRACTION;
webView = new WebView(context, attrs);
webView.setBackgroundColor(Color.TRANSPARENT);
addView(webView);
}
@Override
public void onCues(List<Cue> cues) {
this.cues = cues;
updateWebView();
}
@Override
public void setTextSize(@Cue.TextSizeType int textSizeType, float textSize) {
if (this.textSizeType == textSizeType && this.textSize == textSize) {
return;
}
this.textSizeType = textSizeType;
this.textSize = textSize;
updateWebView();
}
@Override
public void setApplyEmbeddedStyles(boolean applyEmbeddedStyles) {
if (this.applyEmbeddedStyles == applyEmbeddedStyles
&& this.applyEmbeddedFontSizes == applyEmbeddedStyles) {
return;
}
this.applyEmbeddedStyles = applyEmbeddedStyles;
this.applyEmbeddedFontSizes = applyEmbeddedStyles;
updateWebView();
}
@Override
public void setApplyEmbeddedFontSizes(boolean applyEmbeddedFontSizes) {
if (this.applyEmbeddedFontSizes == applyEmbeddedFontSizes) {
return;
}
this.applyEmbeddedFontSizes = applyEmbeddedFontSizes;
updateWebView();
}
@Override
public void setStyle(CaptionStyleCompat style) {
if (this.style == style) {
return;
}
this.style = style;
updateWebView();
}
@Override
public void setBottomPaddingFraction(float bottomPaddingFraction) {
if (this.bottomPaddingFraction == bottomPaddingFraction) {
return;
}
this.bottomPaddingFraction = bottomPaddingFraction;
updateWebView();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed) {
webView.layout(l, t, r, b);
}
}
private void updateWebView() {
StringBuilder cueText = new StringBuilder();
for (int i = 0; i < cues.size(); i++) {
if (i > 0) {
cueText.append("<br>");
}
cueText.append(cues.get(i).text);
}
webView.loadData(
"<html><body><p style=\"color:red;font-size:20px;height:150px\">"
+ cueText
+ "</p></body></html>",
"text/html",
/* encoding= */ null);
}
}