mirror of
https://github.com/samsonjs/media.git
synced 2026-03-27 09:45:47 +00:00
Split some of SubtitleView out into SubtitleTextView
SubtitleView now becomes a ViewGroup that owns a SubtitleTextView. It handles some common styling defaults, but delegates the underlying values down into SubtitleTextView through the SubtitleView.Output interface. When I add a SubtitleWebView this will also be a ViewGroup containing a WebView and will implement SubtitleView.Output and convert Cue styling into HTML & CSS. PiperOrigin-RevId: 291903294
This commit is contained in:
parent
331edb4fb2
commit
f3157e703f
3 changed files with 265 additions and 144 deletions
|
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.Canvas;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.text.CaptionStyleCompat;
|
||||
import com.google.android.exoplayer2.text.Cue;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A {@link SubtitleView.Output} that uses Android's native text tooling via {@link
|
||||
* SubtitlePainter}.
|
||||
*/
|
||||
/* package */ final class SubtitleTextView extends View implements SubtitleView.Output {
|
||||
|
||||
private final List<SubtitlePainter> painters;
|
||||
|
||||
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 SubtitleTextView(Context context) {
|
||||
this(context, /* attrs= */ null);
|
||||
}
|
||||
|
||||
public SubtitleTextView(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
painters = new ArrayList<>();
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCues(List<Cue> cues) {
|
||||
if (this.cues == cues || this.cues.isEmpty() && cues.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
this.cues = cues;
|
||||
// Ensure we have sufficient painters.
|
||||
while (painters.size() < cues.size()) {
|
||||
painters.add(new SubtitlePainter(getContext()));
|
||||
}
|
||||
// Invalidate to trigger drawing.
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTextSize(@Cue.TextSizeType int textSizeType, float textSize) {
|
||||
if (this.textSizeType == textSizeType && this.textSize == textSize) {
|
||||
return;
|
||||
}
|
||||
this.textSizeType = textSizeType;
|
||||
this.textSize = textSize;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplyEmbeddedStyles(boolean applyEmbeddedStyles) {
|
||||
if (this.applyEmbeddedStyles == applyEmbeddedStyles
|
||||
&& this.applyEmbeddedFontSizes == applyEmbeddedStyles) {
|
||||
return;
|
||||
}
|
||||
this.applyEmbeddedStyles = applyEmbeddedStyles;
|
||||
this.applyEmbeddedFontSizes = applyEmbeddedStyles;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplyEmbeddedFontSizes(boolean applyEmbeddedFontSizes) {
|
||||
if (this.applyEmbeddedFontSizes == applyEmbeddedFontSizes) {
|
||||
return;
|
||||
}
|
||||
this.applyEmbeddedFontSizes = applyEmbeddedFontSizes;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStyle(CaptionStyleCompat style) {
|
||||
if (this.style == style) {
|
||||
return;
|
||||
}
|
||||
this.style = style;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBottomPaddingFraction(float bottomPaddingFraction) {
|
||||
if (this.bottomPaddingFraction == bottomPaddingFraction) {
|
||||
return;
|
||||
}
|
||||
this.bottomPaddingFraction = bottomPaddingFraction;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchDraw(Canvas canvas) {
|
||||
@Nullable List<Cue> cues = this.cues;
|
||||
if (cues.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int rawViewHeight = getHeight();
|
||||
|
||||
// Calculate the cue box bounds relative to the canvas after padding is taken into account.
|
||||
int left = getPaddingLeft();
|
||||
int top = getPaddingTop();
|
||||
int right = getWidth() - getPaddingRight();
|
||||
int bottom = rawViewHeight - getPaddingBottom();
|
||||
if (bottom <= top || right <= left) {
|
||||
// No space to draw subtitles.
|
||||
return;
|
||||
}
|
||||
int viewHeightMinusPadding = bottom - top;
|
||||
|
||||
float defaultViewTextSizePx =
|
||||
SubtitleViewUtils.resolveTextSize(
|
||||
textSizeType, textSize, rawViewHeight, viewHeightMinusPadding);
|
||||
if (defaultViewTextSizePx <= 0) {
|
||||
// Text has no height.
|
||||
return;
|
||||
}
|
||||
|
||||
int cueCount = cues.size();
|
||||
for (int i = 0; i < cueCount; i++) {
|
||||
Cue cue = cues.get(i);
|
||||
float cueTextSizePx =
|
||||
SubtitleViewUtils.resolveCueTextSize(cue, rawViewHeight, viewHeightMinusPadding);
|
||||
SubtitlePainter painter = painters.get(i);
|
||||
painter.draw(
|
||||
cue,
|
||||
applyEmbeddedStyles,
|
||||
applyEmbeddedFontSizes,
|
||||
style,
|
||||
defaultViewTextSizePx,
|
||||
cueTextSizePx,
|
||||
bottomPaddingFraction,
|
||||
canvas,
|
||||
left,
|
||||
top,
|
||||
right,
|
||||
bottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,34 +12,32 @@
|
|||
* 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 android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.accessibility.CaptioningManager;
|
||||
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.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A view for displaying subtitle {@link Cue}s.
|
||||
*/
|
||||
public final class SubtitleView extends View implements TextOutput {
|
||||
/** A view for displaying subtitle {@link Cue}s. */
|
||||
public final class SubtitleView extends ViewGroup implements TextOutput {
|
||||
|
||||
/**
|
||||
* The default fractional text size.
|
||||
*
|
||||
* @see #setFractionalTextSize(float, boolean)
|
||||
* @see SubtitleView#setFractionalTextSize(float, boolean)
|
||||
*/
|
||||
public static final float DEFAULT_TEXT_SIZE_FRACTION = 0.0533f;
|
||||
|
||||
|
|
@ -51,31 +49,20 @@ public final class SubtitleView extends View implements TextOutput {
|
|||
*/
|
||||
public static final float DEFAULT_BOTTOM_PADDING_FRACTION = 0.08f;
|
||||
|
||||
private final List<SubtitlePainter> painters;
|
||||
|
||||
@Nullable private List<Cue> cues;
|
||||
@Cue.TextSizeType private int textSizeType;
|
||||
private float textSize;
|
||||
private boolean applyEmbeddedStyles;
|
||||
private boolean applyEmbeddedFontSizes;
|
||||
private CaptionStyleCompat style;
|
||||
private float bottomPaddingFraction;
|
||||
private final SubtitleTextView subtitleTextView;
|
||||
|
||||
public SubtitleView(Context context) {
|
||||
this(context, /* attrs= */ null);
|
||||
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);
|
||||
painters = new ArrayList<>();
|
||||
textSizeType = Cue.TEXT_SIZE_TYPE_FRACTIONAL;
|
||||
textSize = DEFAULT_TEXT_SIZE_FRACTION;
|
||||
applyEmbeddedStyles = true;
|
||||
applyEmbeddedFontSizes = true;
|
||||
style = CaptionStyleCompat.DEFAULT;
|
||||
bottomPaddingFraction = DEFAULT_BOTTOM_PADDING_FRACTION;
|
||||
subtitleTextView = new SubtitleTextView(context, attrs);
|
||||
addView(subtitleTextView);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onCues(List<Cue> cues) {
|
||||
setCues(cues);
|
||||
|
|
@ -87,17 +74,14 @@ public final class SubtitleView extends View implements TextOutput {
|
|||
* @param cues The cues to display, or null to clear the cues.
|
||||
*/
|
||||
public void setCues(@Nullable List<Cue> cues) {
|
||||
if (this.cues == cues) {
|
||||
return;
|
||||
subtitleTextView.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);
|
||||
}
|
||||
this.cues = cues;
|
||||
// Ensure we have sufficient painters.
|
||||
int cueCount = (cues == null) ? 0 : cues.size();
|
||||
while (painters.size() < cueCount) {
|
||||
painters.add(new SubtitlePainter(getContext()));
|
||||
}
|
||||
// Invalidate to trigger drawing.
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -160,13 +144,7 @@ public final class SubtitleView extends View implements TextOutput {
|
|||
}
|
||||
|
||||
private void setTextSize(@Cue.TextSizeType int textSizeType, float textSize) {
|
||||
if (this.textSizeType == textSizeType && this.textSize == textSize) {
|
||||
return;
|
||||
}
|
||||
this.textSizeType = textSizeType;
|
||||
this.textSize = textSize;
|
||||
// Invalidate to trigger drawing.
|
||||
invalidate();
|
||||
subtitleTextView.setTextSize(textSizeType, textSize);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -176,14 +154,7 @@ public final class SubtitleView extends View implements TextOutput {
|
|||
* @param applyEmbeddedStyles Whether styling embedded within the cues should be applied.
|
||||
*/
|
||||
public void setApplyEmbeddedStyles(boolean applyEmbeddedStyles) {
|
||||
if (this.applyEmbeddedStyles == applyEmbeddedStyles
|
||||
&& this.applyEmbeddedFontSizes == applyEmbeddedStyles) {
|
||||
return;
|
||||
}
|
||||
this.applyEmbeddedStyles = applyEmbeddedStyles;
|
||||
this.applyEmbeddedFontSizes = applyEmbeddedStyles;
|
||||
// Invalidate to trigger drawing.
|
||||
invalidate();
|
||||
subtitleTextView.setApplyEmbeddedStyles(applyEmbeddedStyles);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -193,12 +164,7 @@ public final class SubtitleView extends View implements TextOutput {
|
|||
* @param applyEmbeddedFontSizes Whether font sizes embedded within the cues should be applied.
|
||||
*/
|
||||
public void setApplyEmbeddedFontSizes(boolean applyEmbeddedFontSizes) {
|
||||
if (this.applyEmbeddedFontSizes == applyEmbeddedFontSizes) {
|
||||
return;
|
||||
}
|
||||
this.applyEmbeddedFontSizes = applyEmbeddedFontSizes;
|
||||
// Invalidate to trigger drawing.
|
||||
invalidate();
|
||||
subtitleTextView.setApplyEmbeddedFontSizes(applyEmbeddedFontSizes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -218,12 +184,7 @@ public final class SubtitleView extends View implements TextOutput {
|
|||
* @param style A style for the view.
|
||||
*/
|
||||
public void setStyle(CaptionStyleCompat style) {
|
||||
if (this.style == style) {
|
||||
return;
|
||||
}
|
||||
this.style = style;
|
||||
// Invalidate to trigger drawing.
|
||||
invalidate();
|
||||
subtitleTextView.setStyle(style);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -236,87 +197,7 @@ public final class SubtitleView extends View implements TextOutput {
|
|||
* @param bottomPaddingFraction The bottom padding fraction.
|
||||
*/
|
||||
public void setBottomPaddingFraction(float bottomPaddingFraction) {
|
||||
if (this.bottomPaddingFraction == bottomPaddingFraction) {
|
||||
return;
|
||||
}
|
||||
this.bottomPaddingFraction = bottomPaddingFraction;
|
||||
// Invalidate to trigger drawing.
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchDraw(Canvas canvas) {
|
||||
List<Cue> cues = this.cues;
|
||||
if (cues == null || cues.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int rawViewHeight = getHeight();
|
||||
|
||||
// Calculate the cue box bounds relative to the canvas after padding is taken into account.
|
||||
int left = getPaddingLeft();
|
||||
int top = getPaddingTop();
|
||||
int right = getWidth() - getPaddingRight();
|
||||
int bottom = rawViewHeight - getPaddingBottom();
|
||||
if (bottom <= top || right <= left) {
|
||||
// No space to draw subtitles.
|
||||
return;
|
||||
}
|
||||
int viewHeightMinusPadding = bottom - top;
|
||||
|
||||
float defaultViewTextSizePx =
|
||||
resolveTextSize(textSizeType, textSize, rawViewHeight, viewHeightMinusPadding);
|
||||
if (defaultViewTextSizePx <= 0) {
|
||||
// Text has no height.
|
||||
return;
|
||||
}
|
||||
|
||||
int cueCount = cues.size();
|
||||
for (int i = 0; i < cueCount; i++) {
|
||||
Cue cue = cues.get(i);
|
||||
float cueTextSizePx = resolveCueTextSize(cue, rawViewHeight, viewHeightMinusPadding);
|
||||
SubtitlePainter painter = painters.get(i);
|
||||
painter.draw(
|
||||
cue,
|
||||
applyEmbeddedStyles,
|
||||
applyEmbeddedFontSizes,
|
||||
style,
|
||||
defaultViewTextSizePx,
|
||||
cueTextSizePx,
|
||||
bottomPaddingFraction,
|
||||
canvas,
|
||||
left,
|
||||
top,
|
||||
right,
|
||||
bottom);
|
||||
}
|
||||
}
|
||||
|
||||
private float resolveCueTextSize(Cue cue, int rawViewHeight, int viewHeightMinusPadding) {
|
||||
if (cue.textSizeType == Cue.TYPE_UNSET || cue.textSize == Cue.DIMEN_UNSET) {
|
||||
return 0;
|
||||
}
|
||||
float defaultCueTextSizePx =
|
||||
resolveTextSize(cue.textSizeType, cue.textSize, rawViewHeight, viewHeightMinusPadding);
|
||||
return Math.max(defaultCueTextSizePx, 0);
|
||||
}
|
||||
|
||||
private float resolveTextSize(
|
||||
@Cue.TextSizeType int textSizeType,
|
||||
float textSize,
|
||||
int rawViewHeight,
|
||||
int viewHeightMinusPadding) {
|
||||
switch (textSizeType) {
|
||||
case Cue.TEXT_SIZE_TYPE_ABSOLUTE:
|
||||
return textSize;
|
||||
case Cue.TEXT_SIZE_TYPE_FRACTIONAL:
|
||||
return textSize * viewHeightMinusPadding;
|
||||
case Cue.TEXT_SIZE_TYPE_FRACTIONAL_IGNORE_PADDING:
|
||||
return textSize * rawViewHeight;
|
||||
case Cue.TYPE_UNSET:
|
||||
default:
|
||||
return Cue.DIMEN_UNSET;
|
||||
}
|
||||
subtitleTextView.setBottomPaddingFraction(bottomPaddingFraction);
|
||||
}
|
||||
|
||||
@TargetApi(19)
|
||||
|
|
@ -340,4 +221,17 @@ public final class SubtitleView extends View implements TextOutput {
|
|||
return CaptionStyleCompat.createFromCaptionStyle(captioningManager.getUserStyle());
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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 com.google.android.exoplayer2.text.Cue;
|
||||
|
||||
/** Utility class for subtitle layout logic. */
|
||||
/* package */ final class SubtitleViewUtils {
|
||||
|
||||
public static float resolveCueTextSize(Cue cue, int rawViewHeight, int viewHeightMinusPadding) {
|
||||
if (cue.textSizeType == Cue.TYPE_UNSET || cue.textSize == Cue.DIMEN_UNSET) {
|
||||
return 0;
|
||||
}
|
||||
float defaultCueTextSizePx =
|
||||
resolveTextSize(cue.textSizeType, cue.textSize, rawViewHeight, viewHeightMinusPadding);
|
||||
return Math.max(defaultCueTextSizePx, 0);
|
||||
}
|
||||
|
||||
public static float resolveTextSize(
|
||||
@Cue.TextSizeType int textSizeType,
|
||||
float textSize,
|
||||
int rawViewHeight,
|
||||
int viewHeightMinusPadding) {
|
||||
switch (textSizeType) {
|
||||
case Cue.TEXT_SIZE_TYPE_ABSOLUTE:
|
||||
return textSize;
|
||||
case Cue.TEXT_SIZE_TYPE_FRACTIONAL:
|
||||
return textSize * viewHeightMinusPadding;
|
||||
case Cue.TEXT_SIZE_TYPE_FRACTIONAL_IGNORE_PADDING:
|
||||
return textSize * rawViewHeight;
|
||||
case Cue.TYPE_UNSET:
|
||||
default:
|
||||
return Cue.DIMEN_UNSET;
|
||||
}
|
||||
}
|
||||
|
||||
private SubtitleViewUtils() {}
|
||||
}
|
||||
Loading…
Reference in a new issue