public final class

SubtitleView

extends FrameLayout

implements Player.Listener

 java.lang.Object

↳FrameLayout

↳androidx.media3.ui.SubtitleView

Gradle dependencies

compile group: 'androidx.media3', name: 'media3-ui', version: '1.0.0-alpha03'

  • groupId: androidx.media3
  • artifactId: media3-ui
  • version: 1.0.0-alpha03

Artifact androidx.media3:media3-ui:1.0.0-alpha03 it located at Google repository (https://maven.google.com/)

Overview

A view for displaying subtitle Cues.

Summary

Fields
public static final floatDEFAULT_BOTTOM_PADDING_FRACTION

The default bottom padding to apply when Cue.line is Cue.DIMEN_UNSET, as a fraction of the viewport height.

public static final floatDEFAULT_TEXT_SIZE_FRACTION

The default fractional text size.

public static final intVIEW_TYPE_CANVAS

Indicates subtitles should be displayed using a .

public static final intVIEW_TYPE_WEB

Indicates subtitles should be displayed using a .

Constructors
publicSubtitleView(Context context)

publicSubtitleView(Context context, AttributeSet attrs)

Methods
public voidonCues(java.util.List<Cue> cues)

public voidsetApplyEmbeddedFontSizes(boolean applyEmbeddedFontSizes)

Sets whether font sizes embedded within the cues should be applied.

public voidsetApplyEmbeddedStyles(boolean applyEmbeddedStyles)

Sets whether styling embedded within the cues should be applied.

public voidsetBottomPaddingFraction(float bottomPaddingFraction)

Sets the bottom padding fraction to apply when Cue.line is Cue.DIMEN_UNSET, as a fraction of the view's remaining height after its top and bottom padding have been subtracted.

public voidsetCues(java.util.List<Cue> cues)

Sets the cues to be displayed by the view.

public voidsetFixedTextSize(int unit, float size)

Sets the text size to a given unit and value.

public voidsetFractionalTextSize(float fractionOfHeight)

Sets the text size to be a fraction of the view's remaining height after its top and bottom padding have been subtracted.

public voidsetFractionalTextSize(float fractionOfHeight, boolean ignorePadding)

Sets the text size to be a fraction of the height of this view.

public voidsetStyle(CaptionStyleCompat style)

Sets the caption style.

public voidsetUserDefaultStyle()

Styles the captions using CaptioningManager if CaptioningManager is available and enabled.

public voidsetUserDefaultTextSize()

Sets the text size based on CaptioningManager if CaptioningManager is available and enabled.

public voidsetViewType(int viewType)

Sets the type of View used to display subtitles.

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Fields

public static final float DEFAULT_TEXT_SIZE_FRACTION

The default fractional text size.

See also: SubtitleView.setFractionalTextSize(float, boolean)

public static final float DEFAULT_BOTTOM_PADDING_FRACTION

The default bottom padding to apply when Cue.line is Cue.DIMEN_UNSET, as a fraction of the viewport height.

See also: SubtitleView.setBottomPaddingFraction(float)

public static final int VIEW_TYPE_CANVAS

Indicates subtitles should be displayed using a . This is the default.

public static final int VIEW_TYPE_WEB

Indicates subtitles should be displayed using a .

This will use CSS and HTML styling to render the subtitles. This supports some additional styling features beyond those supported by SubtitleView.VIEW_TYPE_CANVAS such as vertical text.

Constructors

public SubtitleView(Context context)

public SubtitleView(Context context, AttributeSet attrs)

Methods

public void onCues(java.util.List<Cue> cues)

public void setCues(java.util.List<Cue> cues)

Sets the cues to be displayed by the view.

Parameters:

cues: The cues to display, or null to clear the cues.

public void setViewType(int viewType)

Sets the type of View used to display subtitles.

NOTE: SubtitleView.VIEW_TYPE_WEB is currently very experimental, and doesn't support most styling and layout properties of Cue.

Parameters:

viewType: The SubtitleView.ViewType to use.

public void setFixedTextSize(int unit, float size)

Sets the text size to a given unit and value.

See for the possible dimension units.

Parameters:

unit: The desired dimension unit.
size: The desired size in the given units.

public void setUserDefaultTextSize()

Sets the text size based on CaptioningManager if CaptioningManager is available and enabled.

Otherwise (and always before API level 19) uses a default font scale of 1.0.

public void setFractionalTextSize(float fractionOfHeight)

Sets the text size to be a fraction of the view's remaining height after its top and bottom padding have been subtracted.

Equivalent to #setFractionalTextSize(fractionOfHeight, false).

Parameters:

fractionOfHeight: A fraction between 0 and 1.

public void setFractionalTextSize(float fractionOfHeight, boolean ignorePadding)

Sets the text size to be a fraction of the height of this view.

Parameters:

fractionOfHeight: A fraction between 0 and 1.
ignorePadding: Set to true if fractionOfHeight should be interpreted as a fraction of this view's height ignoring any top and bottom padding. Set to false if fractionOfHeight should be interpreted as a fraction of this view's remaining height after the top and bottom padding has been subtracted.

public void setApplyEmbeddedStyles(boolean applyEmbeddedStyles)

Sets whether styling embedded within the cues should be applied. Enabled by default. Overrides any setting made with SubtitleView.setApplyEmbeddedFontSizes(boolean).

Parameters:

applyEmbeddedStyles: Whether styling embedded within the cues should be applied.

public void setApplyEmbeddedFontSizes(boolean applyEmbeddedFontSizes)

Sets whether font sizes embedded within the cues should be applied. Enabled by default. Only takes effect if SubtitleView.setApplyEmbeddedStyles(boolean) is set to true.

Parameters:

applyEmbeddedFontSizes: Whether font sizes embedded within the cues should be applied.

public void setUserDefaultStyle()

Styles the captions using CaptioningManager if CaptioningManager is available and enabled.

Otherwise (and always before API level 19) uses a default style.

public void setStyle(CaptionStyleCompat style)

Sets the caption style.

Parameters:

style: A style for the view.

public void setBottomPaddingFraction(float bottomPaddingFraction)

Sets the bottom padding fraction to apply when Cue.line is Cue.DIMEN_UNSET, as a fraction of the view's remaining height after its top and bottom padding have been subtracted.

Note that this padding is applied in addition to any standard view padding.

Parameters:

bottomPaddingFraction: The bottom padding fraction.

Source

/*
 * 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 androidx.media3.ui;

import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.SOURCE;

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.accessibility.CaptioningManager;
import android.webkit.WebView;
import android.widget.FrameLayout;
import androidx.annotation.Dimension;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.media3.common.Player;
import androidx.media3.common.text.Cue;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/** A view for displaying subtitle {@link Cue}s. */
@UnstableApi
public final class SubtitleView extends FrameLayout implements Player.Listener {

  /**
   * An output for displaying subtitles.
   *
   * <p>Implementations of this also need to extend {@link View} in order to be attached to the
   * Android view hierarchy.
   */
  /* package */ interface Output {

    /**
     * Updates the list of cues displayed.
     *
     * @param cues The cues to display.
     * @param style A {@link CaptionStyleCompat} to use for styling unset properties of cues.
     * @param defaultTextSize The default font size to apply when {@link Cue#textSize} is {@link
     *     Cue#DIMEN_UNSET}.
     * @param defaultTextSizeType The type of {@code defaultTextSize}.
     * @param bottomPaddingFraction The bottom padding to apply when {@link Cue#line} is {@link
     *     Cue#DIMEN_UNSET}, as a fraction of the view's remaining height after its top and bottom
     *     padding have been subtracted.
     * @see #setStyle(CaptionStyleCompat)
     * @see #setTextSize(int, float)
     * @see #setBottomPaddingFraction(float)
     */
    void update(
        List<Cue> cues,
        CaptionStyleCompat style,
        float defaultTextSize,
        @Cue.TextSizeType int defaultTextSizeType,
        float bottomPaddingFraction);
  }

  /**
   * The default fractional text size.
   *
   * @see SubtitleView#setFractionalTextSize(float, boolean)
   */
  public static final float DEFAULT_TEXT_SIZE_FRACTION = 0.0533f;

  /**
   * The default bottom padding to apply when {@link Cue#line} is {@link Cue#DIMEN_UNSET}, as a
   * fraction of the viewport height.
   *
   * @see #setBottomPaddingFraction(float)
   */
  public static final float DEFAULT_BOTTOM_PADDING_FRACTION = 0.08f;

  /** Indicates subtitles should be displayed using a {@link Canvas}. This is the default. */
  public static final int VIEW_TYPE_CANVAS = 1;

  /**
   * Indicates subtitles should be displayed using a {@link WebView}.
   *
   * <p>This will use CSS and HTML styling to render the subtitles. This supports some additional
   * styling features beyond those supported by {@link #VIEW_TYPE_CANVAS} 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_CANVAS}
   *   <li>{@link #VIEW_TYPE_WEB}
   * </ul>
   */
  @Documented
  @Retention(SOURCE)
  @Target(TYPE_USE)
  @IntDef({VIEW_TYPE_CANVAS, VIEW_TYPE_WEB})
  public @interface ViewType {}

  private List<Cue> cues;
  private CaptionStyleCompat style;
  @Cue.TextSizeType private int defaultTextSizeType;
  private float defaultTextSize;
  private float bottomPaddingFraction;
  private boolean applyEmbeddedStyles;
  private boolean applyEmbeddedFontSizes;

  private @ViewType int viewType;
  private Output output;
  private View innerSubtitleView;

  public SubtitleView(Context context) {
    this(context, null);
  }

  public SubtitleView(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
    cues = Collections.emptyList();
    style = CaptionStyleCompat.DEFAULT;
    defaultTextSizeType = Cue.TEXT_SIZE_TYPE_FRACTIONAL;
    defaultTextSize = DEFAULT_TEXT_SIZE_FRACTION;
    bottomPaddingFraction = DEFAULT_BOTTOM_PADDING_FRACTION;
    applyEmbeddedStyles = true;
    applyEmbeddedFontSizes = true;

    CanvasSubtitleOutput canvasSubtitleOutput = new CanvasSubtitleOutput(context);
    output = canvasSubtitleOutput;
    innerSubtitleView = canvasSubtitleOutput;
    addView(innerSubtitleView);
    viewType = VIEW_TYPE_CANVAS;
  }

  @Override
  public void onCues(List<Cue> cues) {
    setCues(cues);
  }

  /**
   * Sets the cues to be displayed by the view.
   *
   * @param cues The cues to display, or null to clear the cues.
   */
  public void setCues(@Nullable List<Cue> cues) {
    this.cues = (cues != null ? cues : Collections.emptyList());
    updateOutput();
  }

  /**
   * Sets 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}.
   *
   * @param viewType The {@link ViewType} to use.
   */
  public void setViewType(@ViewType int viewType) {
    if (this.viewType == viewType) {
      return;
    }
    switch (viewType) {
      case VIEW_TYPE_CANVAS:
        setView(new CanvasSubtitleOutput(getContext()));
        break;
      case VIEW_TYPE_WEB:
        setView(new WebViewSubtitleOutput(getContext()));
        break;
      default:
        throw new IllegalArgumentException();
    }
    this.viewType = viewType;
  }

  private <T extends View & Output> void setView(T view) {
    removeView(innerSubtitleView);
    if (innerSubtitleView instanceof WebViewSubtitleOutput) {
      ((WebViewSubtitleOutput) innerSubtitleView).destroy();
    }
    innerSubtitleView = view;
    output = view;
    addView(view);
  }

  /**
   * Sets the text size to a given unit and value.
   *
   * <p>See {@link TypedValue} for the possible dimension units.
   *
   * @param unit The desired dimension unit.
   * @param size The desired size in the given units.
   */
  public void setFixedTextSize(@Dimension int unit, float size) {
    Context context = getContext();
    Resources resources;
    if (context == null) {
      resources = Resources.getSystem();
    } else {
      resources = context.getResources();
    }
    setTextSize(
        Cue.TEXT_SIZE_TYPE_ABSOLUTE,
        TypedValue.applyDimension(unit, size, resources.getDisplayMetrics()));
  }

  /**
   * Sets the text size based on {@link CaptioningManager#getFontScale()} if {@link
   * CaptioningManager} is available and enabled.
   *
   * <p>Otherwise (and always before API level 19) uses a default font scale of 1.0.
   */
  public void setUserDefaultTextSize() {
    setFractionalTextSize(DEFAULT_TEXT_SIZE_FRACTION * getUserCaptionFontScale());
  }

  /**
   * Sets the text size to be a fraction of the view's remaining height after its top and bottom
   * padding have been subtracted.
   *
   * <p>Equivalent to {@code #setFractionalTextSize(fractionOfHeight, false)}.
   *
   * @param fractionOfHeight A fraction between 0 and 1.
   */
  public void setFractionalTextSize(float fractionOfHeight) {
    setFractionalTextSize(fractionOfHeight, false);
  }

  /**
   * Sets the text size to be a fraction of the height of this view.
   *
   * @param fractionOfHeight A fraction between 0 and 1.
   * @param ignorePadding Set to true if {@code fractionOfHeight} should be interpreted as a
   *     fraction of this view's height ignoring any top and bottom padding. Set to false if {@code
   *     fractionOfHeight} should be interpreted as a fraction of this view's remaining height after
   *     the top and bottom padding has been subtracted.
   */
  public void setFractionalTextSize(float fractionOfHeight, boolean ignorePadding) {
    setTextSize(
        ignorePadding
            ? Cue.TEXT_SIZE_TYPE_FRACTIONAL_IGNORE_PADDING
            : Cue.TEXT_SIZE_TYPE_FRACTIONAL,
        fractionOfHeight);
  }

  private void setTextSize(@Cue.TextSizeType int textSizeType, float textSize) {
    this.defaultTextSizeType = textSizeType;
    this.defaultTextSize = textSize;
    updateOutput();
  }

  /**
   * Sets whether styling embedded within the cues should be applied. Enabled by default. Overrides
   * any setting made with {@link SubtitleView#setApplyEmbeddedFontSizes}.
   *
   * @param applyEmbeddedStyles Whether styling embedded within the cues should be applied.
   */
  public void setApplyEmbeddedStyles(boolean applyEmbeddedStyles) {
    this.applyEmbeddedStyles = applyEmbeddedStyles;
    updateOutput();
  }

  /**
   * Sets whether font sizes embedded within the cues should be applied. Enabled by default. Only
   * takes effect if {@link SubtitleView#setApplyEmbeddedStyles} is set to true.
   *
   * @param applyEmbeddedFontSizes Whether font sizes embedded within the cues should be applied.
   */
  public void setApplyEmbeddedFontSizes(boolean applyEmbeddedFontSizes) {
    this.applyEmbeddedFontSizes = applyEmbeddedFontSizes;
    updateOutput();
  }

  /**
   * Styles the captions using {@link CaptioningManager#getUserStyle()} if {@link CaptioningManager}
   * is available and enabled.
   *
   * <p>Otherwise (and always before API level 19) uses a default style.
   */
  public void setUserDefaultStyle() {
    setStyle(getUserCaptionStyle());
  }

  /**
   * Sets the caption style.
   *
   * @param style A style for the view.
   */
  public void setStyle(CaptionStyleCompat style) {
    this.style = style;
    updateOutput();
  }

  /**
   * Sets the bottom padding fraction to apply when {@link Cue#line} is {@link Cue#DIMEN_UNSET}, as
   * a fraction of the view's remaining height after its top and bottom padding have been
   * subtracted.
   *
   * <p>Note that this padding is applied in addition to any standard view padding.
   *
   * @param bottomPaddingFraction The bottom padding fraction.
   */
  public void setBottomPaddingFraction(float bottomPaddingFraction) {
    this.bottomPaddingFraction = bottomPaddingFraction;
    updateOutput();
  }

  private float getUserCaptionFontScale() {
    if (Util.SDK_INT < 19 || isInEditMode()) {
      return 1f;
    }
    @Nullable
    CaptioningManager captioningManager =
        (CaptioningManager) getContext().getSystemService(Context.CAPTIONING_SERVICE);
    return captioningManager != null && captioningManager.isEnabled()
        ? captioningManager.getFontScale()
        : 1f;
  }

  private CaptionStyleCompat getUserCaptionStyle() {
    if (Util.SDK_INT < 19 || isInEditMode()) {
      return CaptionStyleCompat.DEFAULT;
    }
    @Nullable
    CaptioningManager captioningManager =
        (CaptioningManager) getContext().getSystemService(Context.CAPTIONING_SERVICE);
    return captioningManager != null && captioningManager.isEnabled()
        ? CaptionStyleCompat.createFromCaptionStyle(captioningManager.getUserStyle())
        : CaptionStyleCompat.DEFAULT;
  }

  private void updateOutput() {
    output.update(
        getCuesWithStylingPreferencesApplied(),
        style,
        defaultTextSize,
        defaultTextSizeType,
        bottomPaddingFraction);
  }

  /**
   * Returns {@link #cues} with {@link #applyEmbeddedStyles} and {@link #applyEmbeddedFontSizes}
   * applied.
   *
   * <p>If {@link #applyEmbeddedStyles} is false then all styling spans are removed from {@link
   * Cue#text}, {@link Cue#textSize} and {@link Cue#textSizeType} are set to {@link Cue#DIMEN_UNSET}
   * and {@link Cue#windowColorSet} is set to false.
   *
   * <p>Otherwise if {@link #applyEmbeddedFontSizes} is false then only size-related styling spans
   * are removed from {@link Cue#text} and {@link Cue#textSize} and {@link Cue#textSizeType} are set
   * to {@link Cue#DIMEN_UNSET}
   */
  private List<Cue> getCuesWithStylingPreferencesApplied() {
    if (applyEmbeddedStyles && applyEmbeddedFontSizes) {
      return cues;
    }
    List<Cue> strippedCues = new ArrayList<>(cues.size());
    for (int i = 0; i < cues.size(); i++) {
      strippedCues.add(removeEmbeddedStyling(cues.get(i)));
    }
    return strippedCues;
  }

  private Cue removeEmbeddedStyling(Cue cue) {
    Cue.Builder strippedCue = cue.buildUpon();
    if (!applyEmbeddedStyles) {
      SubtitleViewUtils.removeAllEmbeddedStyling(strippedCue);
    } else if (!applyEmbeddedFontSizes) {
      SubtitleViewUtils.removeEmbeddedFontSizes(strippedCue);
    }
    return strippedCue.build();
  }
}