1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 */ 17 package com.google.android.exoplayer2.ui; 18 19 import static java.lang.annotation.RetentionPolicy.SOURCE; 20 21 import android.content.Context; 22 import android.content.res.Resources; 23 import android.util.AttributeSet; 24 import android.util.TypedValue; 25 import android.view.View; 26 import android.view.accessibility.CaptioningManager; 27 import android.widget.FrameLayout; 28 import androidx.annotation.Dimension; 29 import androidx.annotation.IntDef; 30 import androidx.annotation.Nullable; 31 import androidx.annotation.RequiresApi; 32 import com.google.android.exoplayer2.text.CaptionStyleCompat; 33 import com.google.android.exoplayer2.text.Cue; 34 import com.google.android.exoplayer2.text.TextOutput; 35 import com.google.android.exoplayer2.util.Util; 36 import java.lang.annotation.Documented; 37 import java.lang.annotation.Retention; 38 import java.util.Collections; 39 import java.util.List; 40 41 /** A view for displaying subtitle {@link Cue}s. */ 42 public final class SubtitleView extends FrameLayout implements TextOutput { 43 44 /** 45 * The default fractional text size. 46 * 47 * @see SubtitleView#setFractionalTextSize(float, boolean) 48 */ 49 public static final float DEFAULT_TEXT_SIZE_FRACTION = 0.0533f; 50 51 /** 52 * The default bottom padding to apply when {@link Cue#line} is {@link Cue#DIMEN_UNSET}, as a 53 * fraction of the viewport height. 54 * 55 * @see #setBottomPaddingFraction(float) 56 */ 57 public static final float DEFAULT_BOTTOM_PADDING_FRACTION = 0.08f; 58 59 /** 60 * Indicates a {@link SubtitleTextView} should be used to display subtitles. This is the default. 61 */ 62 public static final int VIEW_TYPE_TEXT = 1; 63 64 /** 65 * Indicates a {@link SubtitleWebView} should be used to display subtitles. 66 * 67 * <p>This will instantiate a {@link android.webkit.WebView} and use CSS and HTML styling to 68 * render the subtitles. This supports some additional styling features beyond those supported by 69 * {@link SubtitleTextView} such as vertical text. 70 */ 71 public static final int VIEW_TYPE_WEB = 2; 72 73 /** 74 * The type of {@link View} to use to display subtitles. 75 * 76 * <p>One of: 77 * 78 * <ul> 79 * <li>{@link #VIEW_TYPE_TEXT} 80 * <li>{@link #VIEW_TYPE_WEB} 81 * </ul> 82 */ 83 @Documented 84 @Retention(SOURCE) 85 @IntDef({VIEW_TYPE_TEXT, VIEW_TYPE_WEB}) 86 public @interface ViewType {} 87 88 private @ViewType int viewType; 89 private Output output; 90 private View innerSubtitleView; 91 SubtitleView(Context context)92 public SubtitleView(Context context) { 93 this(context, null); 94 } 95 SubtitleView(Context context, @Nullable AttributeSet attrs)96 public SubtitleView(Context context, @Nullable AttributeSet attrs) { 97 super(context, attrs); 98 SubtitleTextView subtitleTextView = new SubtitleTextView(context, attrs); 99 output = subtitleTextView; 100 innerSubtitleView = subtitleTextView; 101 addView(innerSubtitleView); 102 viewType = VIEW_TYPE_TEXT; 103 } 104 105 @Override onCues(List<Cue> cues)106 public void onCues(List<Cue> cues) { 107 setCues(cues); 108 } 109 110 /** 111 * Sets the cues to be displayed by the view. 112 * 113 * @param cues The cues to display, or null to clear the cues. 114 */ setCues(@ullable List<Cue> cues)115 public void setCues(@Nullable List<Cue> cues) { 116 output.onCues(cues != null ? cues : Collections.emptyList()); 117 } 118 119 /** 120 * Set the type of {@link View} used to display subtitles. 121 * 122 * <p>NOTE: {@link #VIEW_TYPE_WEB} is currently very experimental, and doesn't support most 123 * styling and layout properties of {@link Cue}. 124 * 125 * @param viewType The {@link ViewType} to use. 126 */ setViewType(@iewType int viewType)127 public void setViewType(@ViewType int viewType) { 128 if (this.viewType == viewType) { 129 return; 130 } 131 switch (viewType) { 132 case VIEW_TYPE_TEXT: 133 setView(new SubtitleTextView(getContext())); 134 break; 135 case VIEW_TYPE_WEB: 136 setView(new SubtitleWebView(getContext())); 137 break; 138 default: 139 throw new IllegalArgumentException(); 140 } 141 this.viewType = viewType; 142 } 143 setView(T view)144 private <T extends View & Output> void setView(T view) { 145 removeView(innerSubtitleView); 146 innerSubtitleView = view; 147 output = view; 148 addView(view); 149 } 150 151 /** 152 * Set the text size to a given unit and value. 153 * 154 * <p>See {@link TypedValue} for the possible dimension units. 155 * 156 * @param unit The desired dimension unit. 157 * @param size The desired size in the given units. 158 */ setFixedTextSize(@imension int unit, float size)159 public void setFixedTextSize(@Dimension int unit, float size) { 160 Context context = getContext(); 161 Resources resources; 162 if (context == null) { 163 resources = Resources.getSystem(); 164 } else { 165 resources = context.getResources(); 166 } 167 setTextSize( 168 Cue.TEXT_SIZE_TYPE_ABSOLUTE, 169 TypedValue.applyDimension(unit, size, resources.getDisplayMetrics())); 170 } 171 172 /** 173 * Sets the text size to one derived from {@link CaptioningManager#getFontScale()}, or to a 174 * default size before API level 19. 175 */ setUserDefaultTextSize()176 public void setUserDefaultTextSize() { 177 float fontScale = Util.SDK_INT >= 19 && !isInEditMode() ? getUserCaptionFontScaleV19() : 1f; 178 setFractionalTextSize(DEFAULT_TEXT_SIZE_FRACTION * fontScale); 179 } 180 181 /** 182 * Sets the text size to be a fraction of the view's remaining height after its top and bottom 183 * padding have been subtracted. 184 * <p> 185 * Equivalent to {@code #setFractionalTextSize(fractionOfHeight, false)}. 186 * 187 * @param fractionOfHeight A fraction between 0 and 1. 188 */ setFractionalTextSize(float fractionOfHeight)189 public void setFractionalTextSize(float fractionOfHeight) { 190 setFractionalTextSize(fractionOfHeight, false); 191 } 192 193 /** 194 * Sets the text size to be a fraction of the height of this view. 195 * 196 * @param fractionOfHeight A fraction between 0 and 1. 197 * @param ignorePadding Set to true if {@code fractionOfHeight} should be interpreted as a 198 * fraction of this view's height ignoring any top and bottom padding. Set to false if 199 * {@code fractionOfHeight} should be interpreted as a fraction of this view's remaining 200 * height after the top and bottom padding has been subtracted. 201 */ setFractionalTextSize(float fractionOfHeight, boolean ignorePadding)202 public void setFractionalTextSize(float fractionOfHeight, boolean ignorePadding) { 203 setTextSize( 204 ignorePadding 205 ? Cue.TEXT_SIZE_TYPE_FRACTIONAL_IGNORE_PADDING 206 : Cue.TEXT_SIZE_TYPE_FRACTIONAL, 207 fractionOfHeight); 208 } 209 setTextSize(@ue.TextSizeType int textSizeType, float textSize)210 private void setTextSize(@Cue.TextSizeType int textSizeType, float textSize) { 211 output.setTextSize(textSizeType, textSize); 212 } 213 214 /** 215 * Sets whether styling embedded within the cues should be applied. Enabled by default. 216 * Overrides any setting made with {@link SubtitleView#setApplyEmbeddedFontSizes}. 217 * 218 * @param applyEmbeddedStyles Whether styling embedded within the cues should be applied. 219 */ setApplyEmbeddedStyles(boolean applyEmbeddedStyles)220 public void setApplyEmbeddedStyles(boolean applyEmbeddedStyles) { 221 output.setApplyEmbeddedStyles(applyEmbeddedStyles); 222 } 223 224 /** 225 * Sets whether font sizes embedded within the cues should be applied. Enabled by default. 226 * Only takes effect if {@link SubtitleView#setApplyEmbeddedStyles} is set to true. 227 * 228 * @param applyEmbeddedFontSizes Whether font sizes embedded within the cues should be applied. 229 */ setApplyEmbeddedFontSizes(boolean applyEmbeddedFontSizes)230 public void setApplyEmbeddedFontSizes(boolean applyEmbeddedFontSizes) { 231 output.setApplyEmbeddedFontSizes(applyEmbeddedFontSizes); 232 } 233 234 /** 235 * Sets the caption style to be equivalent to the one returned by 236 * {@link CaptioningManager#getUserStyle()}, or to a default style before API level 19. 237 */ setUserDefaultStyle()238 public void setUserDefaultStyle() { 239 setStyle( 240 Util.SDK_INT >= 19 && isCaptionManagerEnabled() && !isInEditMode() 241 ? getUserCaptionStyleV19() 242 : CaptionStyleCompat.DEFAULT); 243 } 244 245 /** 246 * Sets the caption style. 247 * 248 * @param style A style for the view. 249 */ setStyle(CaptionStyleCompat style)250 public void setStyle(CaptionStyleCompat style) { 251 output.setStyle(style); 252 } 253 254 /** 255 * Sets the bottom padding fraction to apply when {@link Cue#line} is {@link Cue#DIMEN_UNSET}, 256 * as a fraction of the view's remaining height after its top and bottom padding have been 257 * subtracted. 258 * <p> 259 * Note that this padding is applied in addition to any standard view padding. 260 * 261 * @param bottomPaddingFraction The bottom padding fraction. 262 */ setBottomPaddingFraction(float bottomPaddingFraction)263 public void setBottomPaddingFraction(float bottomPaddingFraction) { 264 output.setBottomPaddingFraction(bottomPaddingFraction); 265 } 266 267 @RequiresApi(19) isCaptionManagerEnabled()268 private boolean isCaptionManagerEnabled() { 269 CaptioningManager captioningManager = 270 (CaptioningManager) getContext().getSystemService(Context.CAPTIONING_SERVICE); 271 return captioningManager.isEnabled(); 272 } 273 274 @RequiresApi(19) getUserCaptionFontScaleV19()275 private float getUserCaptionFontScaleV19() { 276 CaptioningManager captioningManager = 277 (CaptioningManager) getContext().getSystemService(Context.CAPTIONING_SERVICE); 278 return captioningManager.getFontScale(); 279 } 280 281 @RequiresApi(19) getUserCaptionStyleV19()282 private CaptionStyleCompat getUserCaptionStyleV19() { 283 CaptioningManager captioningManager = 284 (CaptioningManager) getContext().getSystemService(Context.CAPTIONING_SERVICE); 285 return CaptionStyleCompat.createFromCaptionStyle(captioningManager.getUserStyle()); 286 } 287 288 /* package */ interface Output { onCues(List<Cue> cues)289 void onCues(List<Cue> cues); setTextSize(@ue.TextSizeType int textSizeType, float textSize)290 void setTextSize(@Cue.TextSizeType int textSizeType, float textSize); setApplyEmbeddedStyles(boolean applyEmbeddedStyles)291 void setApplyEmbeddedStyles(boolean applyEmbeddedStyles); setApplyEmbeddedFontSizes(boolean applyEmbeddedFontSizes)292 void setApplyEmbeddedFontSizes(boolean applyEmbeddedFontSizes); setStyle(CaptionStyleCompat style)293 void setStyle(CaptionStyleCompat style); setBottomPaddingFraction(float bottomPaddingFraction)294 void setBottomPaddingFraction(float bottomPaddingFraction); 295 } 296 } 297