/* * Copyright 2018 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.media.subtitle; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.view.accessibility.CaptioningManager; import android.view.accessibility.CaptioningManager.CaptionStyle; import android.view.accessibility.CaptioningManager.CaptioningChangeListener; import androidx.annotation.RequiresApi; /** * Abstract widget class to render a closed caption track. */ @RequiresApi(28) abstract class ClosedCaptionWidget extends ViewGroup implements SubtitleTrack.RenderingWidget { interface ClosedCaptionLayout { void setCaptionStyle(CaptionStyle captionStyle); void setFontScale(float scale); } /** Captioning manager, used to obtain and track caption properties. */ private final CaptioningManager mManager; /** Current caption style. */ protected CaptionStyle mCaptionStyle; /** Callback for rendering changes. */ protected OnChangedListener mListener; /** Concrete layout of CC. */ protected ClosedCaptionLayout mClosedCaptionLayout; /** Whether a caption style change listener is registered. */ private boolean mHasChangeListener; ClosedCaptionWidget(Context context) { this(context, null); } ClosedCaptionWidget(Context context, AttributeSet attrs) { this(context, attrs, 0); } ClosedCaptionWidget(Context context, AttributeSet attrs, int defStyle) { this(context, attrs, defStyle, 0); } ClosedCaptionWidget(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); // Cannot render text over video when layer type is hardware. setLayerType(View.LAYER_TYPE_SOFTWARE, null); mManager = (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE); mCaptionStyle = mManager.getUserStyle(); mClosedCaptionLayout = createCaptionLayout(context); mClosedCaptionLayout.setCaptionStyle(mCaptionStyle); mClosedCaptionLayout.setFontScale(mManager.getFontScale()); addView((ViewGroup) mClosedCaptionLayout, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); requestLayout(); } public abstract ClosedCaptionLayout createCaptionLayout(Context context); @Override public void setOnChangedListener(OnChangedListener listener) { mListener = listener; } @Override public void setSize(int width, int height) { final int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); final int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); measure(widthSpec, heightSpec); layout(0, 0, width, height); } @Override public void setVisible(boolean visible) { if (visible) { setVisibility(View.VISIBLE); } else { setVisibility(View.GONE); } manageChangeListener(); } @Override public void onAttachedToWindow() { super.onAttachedToWindow(); manageChangeListener(); } @Override public void onDetachedFromWindow() { super.onDetachedFromWindow(); manageChangeListener(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); ((ViewGroup) mClosedCaptionLayout).measure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { ((ViewGroup) mClosedCaptionLayout).layout(l, t, r, b); } /** * Manages whether this renderer is listening for caption style changes. */ private final CaptioningChangeListener mCaptioningListener = new CaptioningChangeListener() { @Override public void onUserStyleChanged(CaptionStyle userStyle) { mCaptionStyle = userStyle; mClosedCaptionLayout.setCaptionStyle(mCaptionStyle); } @Override public void onFontScaleChanged(float fontScale) { mClosedCaptionLayout.setFontScale(fontScale); } }; private void manageChangeListener() { final boolean needsListener = isAttachedToWindow() && getVisibility() == View.VISIBLE; if (mHasChangeListener != needsListener) { mHasChangeListener = needsListener; if (needsListener) { mManager.addCaptioningChangeListener(mCaptioningListener); } else { mManager.removeCaptioningChangeListener(mCaptioningListener); } } } }