FileDocCategorySizeDatePackage
TvView.javaAPI DocAndroid 5.1 API39686Thu Mar 12 22:22:30 GMT 2015android.media.tv

TvView

public class TvView extends android.view.ViewGroup
Displays TV contents. The TvView class provides a high level interface for applications to show TV programs from various TV sources that implement {@link TvInputService}. (Note that the list of TV inputs available on the system can be obtained by calling {@link TvInputManager#getTvInputList() TvInputManager.getTvInputList()}.)

Once the application supplies the URI for a specific TV channel to {@link #tune(String, Uri)} method, it takes care of underlying service binding (and unbinding if the current TvView is already bound to a service) and automatically allocates/deallocates resources needed. In addition to a few essential methods to control how the contents are presented, it also provides a way to dispatch input events to the connected TvInputService in order to enable custom key actions for the TV input.

Fields Summary
private static final String
TAG
private static final boolean
DEBUG
private static final int
ZORDER_MEDIA
private static final int
ZORDER_MEDIA_OVERLAY
private static final int
ZORDER_ON_TOP
private static final int
CAPTION_DEFAULT
private static final int
CAPTION_ENABLED
private static final int
CAPTION_DISABLED
private static final WeakReference
NULL_TV_VIEW
private static final Object
sMainTvViewLock
private static WeakReference
sMainTvView
private final android.os.Handler
mHandler
private android.media.tv.TvInputManager.Session
mSession
private android.view.SurfaceView
mSurfaceView
private android.view.Surface
mSurface
private boolean
mOverlayViewCreated
private android.graphics.Rect
mOverlayViewFrame
private final TvInputManager
mTvInputManager
private MySessionCallback
mSessionCallback
private TvInputCallback
mCallback
private OnUnhandledInputEventListener
mOnUnhandledInputEventListener
private boolean
mHasStreamVolume
private float
mStreamVolume
private int
mCaptionEnabled
private String
mAppPrivateCommandAction
private android.os.Bundle
mAppPrivateCommandData
private boolean
mSurfaceChanged
private int
mSurfaceFormat
private int
mSurfaceWidth
private int
mSurfaceHeight
private final android.util.AttributeSet
mAttrs
private final int
mDefStyleAttr
private int
mWindowZOrder
private boolean
mUseRequestedSurfaceLayout
private int
mSurfaceViewLeft
private int
mSurfaceViewRight
private int
mSurfaceViewTop
private int
mSurfaceViewBottom
private final SurfaceHolder.Callback
mSurfaceHolderCallback
private final android.media.tv.TvInputManager.Session.FinishedInputEventCallback
mFinishedInputEventCallback
Constructors Summary
public TvView(android.content.Context context)


       
        this(context, null, 0);
    
public TvView(android.content.Context context, android.util.AttributeSet attrs)

        this(context, attrs, 0);
    
public TvView(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr)

        super(context, attrs, defStyleAttr);
        mAttrs = attrs;
        mDefStyleAttr = defStyleAttr;
        resetSurfaceView();
        mTvInputManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE);
    
Methods Summary
private voidcreateSessionOverlayView()

        if (mSession == null || !isAttachedToWindow()
                || mOverlayViewCreated || mWindowZOrder != ZORDER_MEDIA) {
            return;
        }
        mOverlayViewFrame = getViewFrameOnScreen();
        mSession.createOverlayView(this, mOverlayViewFrame);
        mOverlayViewCreated = true;
    
protected voiddispatchDraw(android.graphics.Canvas canvas)

        if (mWindowZOrder != ZORDER_ON_TOP) {
            // Punch a hole so that the underlying overlay view and surface can be shown.
            canvas.drawColor(0, PorterDuff.Mode.CLEAR);
        }
        super.dispatchDraw(canvas);
    
public booleandispatchGenericMotionEvent(android.view.MotionEvent event)

        if (super.dispatchGenericMotionEvent(event)) {
            return true;
        }
        if (DEBUG) Log.d(TAG, "dispatchGenericMotionEvent(" + event + ")");
        if (mSession == null) {
            return false;
        }
        InputEvent copiedEvent = event.copy();
        int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback,
                mHandler);
        return ret != Session.DISPATCH_NOT_HANDLED;
    
public booleandispatchKeyEvent(android.view.KeyEvent event)

        if (super.dispatchKeyEvent(event)) {
            return true;
        }
        if (DEBUG) Log.d(TAG, "dispatchKeyEvent(" + event + ")");
        if (mSession == null) {
            return false;
        }
        InputEvent copiedEvent = event.copy();
        int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback,
                mHandler);
        return ret != Session.DISPATCH_NOT_HANDLED;
    
private voiddispatchSurfaceChanged(int format, int width, int height)

        if (mSession == null) {
            return;
        }
        mSession.dispatchSurfaceChanged(format, width, height);
    
public booleandispatchTouchEvent(android.view.MotionEvent event)

        if (super.dispatchTouchEvent(event)) {
            return true;
        }
        if (DEBUG) Log.d(TAG, "dispatchTouchEvent(" + event + ")");
        if (mSession == null) {
            return false;
        }
        InputEvent copiedEvent = event.copy();
        int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback,
                mHandler);
        return ret != Session.DISPATCH_NOT_HANDLED;
    
public booleandispatchTrackballEvent(android.view.MotionEvent event)

        if (super.dispatchTrackballEvent(event)) {
            return true;
        }
        if (DEBUG) Log.d(TAG, "dispatchTrackballEvent(" + event + ")");
        if (mSession == null) {
            return false;
        }
        InputEvent copiedEvent = event.copy();
        int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback,
                mHandler);
        return ret != Session.DISPATCH_NOT_HANDLED;
    
public booleandispatchUnhandledInputEvent(android.view.InputEvent event)
Dispatches an unhandled input event to the next receiver.

Except system keys, TvView always consumes input events in the normal flow. This is called asynchronously from where the event is dispatched. It gives the host application a chance to dispatch the unhandled input events.

param
event The input event.
return
{@code true} if the event was handled by the view, {@code false} otherwise.

        if (mOnUnhandledInputEventListener != null) {
            if (mOnUnhandledInputEventListener.onUnhandledInputEvent(event)) {
                return true;
            }
        }
        return onUnhandledInputEvent(event);
    
public voiddispatchWindowFocusChanged(boolean hasFocus)

        super.dispatchWindowFocusChanged(hasFocus);
        // Other app may have shown its own main TvView.
        // Set main again to regain main session.
        synchronized (sMainTvViewLock) {
            if (hasFocus && this == sMainTvView.get() && mSession != null) {
                mSession.setMain();
            }
        }
    
public voiddraw(android.graphics.Canvas canvas)

        if (mWindowZOrder != ZORDER_ON_TOP) {
            // Punch a hole so that the underlying overlay view and surface can be shown.
            canvas.drawColor(0, PorterDuff.Mode.CLEAR);
        }
        super.draw(canvas);
    
public booleangatherTransparentRegion(android.graphics.Region region)

        if (mWindowZOrder != ZORDER_ON_TOP) {
            if (region != null) {
                int width = getWidth();
                int height = getHeight();
                if (width > 0 && height > 0) {
                    int location[] = new int[2];
                    getLocationInWindow(location);
                    int left = location[0];
                    int top = location[1];
                    region.op(left, top, left + width, top + height, Region.Op.UNION);
                }
            }
        }
        return super.gatherTransparentRegion(region);
    
public java.lang.StringgetSelectedTrack(int type)
Returns the ID of the selected track for a given type. Returns {@code null} if the information is not available or the track is not selected.

param
type The type of the selected tracks. The type can be {@link TvTrackInfo#TYPE_AUDIO}, {@link TvTrackInfo#TYPE_VIDEO} or {@link TvTrackInfo#TYPE_SUBTITLE}.
see
#selectTrack
see
#getTracks

        if (mSession == null) {
            return null;
        }
        return mSession.getSelectedTrack(type);
    
public java.util.ListgetTracks(int type)
Returns the list of tracks. Returns {@code null} if the information is not available.

param
type The type of the tracks. The type can be {@link TvTrackInfo#TYPE_AUDIO}, {@link TvTrackInfo#TYPE_VIDEO} or {@link TvTrackInfo#TYPE_SUBTITLE}.
see
#selectTrack
see
#getSelectedTrack

        if (mSession == null) {
            return null;
        }
        return mSession.getTracks(type);
    
private android.graphics.RectgetViewFrameOnScreen()

        int[] location = new int[2];
        getLocationOnScreen(location);
        return new Rect(location[0], location[1],
                location[0] + getWidth(), location[1] + getHeight());
    
protected voidonAttachedToWindow()

        super.onAttachedToWindow();
        createSessionOverlayView();
    
protected voidonDetachedFromWindow()

        removeSessionOverlayView();
        super.onDetachedFromWindow();
    
protected voidonLayout(boolean changed, int left, int top, int right, int bottom)

        if (DEBUG) {
            Log.d(TAG, "onLayout (left=" + left + ", top=" + top + ", right=" + right
                    + ", bottom=" + bottom + ",)");
        }
        if (mUseRequestedSurfaceLayout) {
            mSurfaceView.layout(mSurfaceViewLeft, mSurfaceViewTop, mSurfaceViewRight,
                    mSurfaceViewBottom);
        } else {
            mSurfaceView.layout(0, 0, right - left, bottom - top);
        }
    
protected voidonMeasure(int widthMeasureSpec, int heightMeasureSpec)

        mSurfaceView.measure(widthMeasureSpec, heightMeasureSpec);
        int width = mSurfaceView.getMeasuredWidth();
        int height = mSurfaceView.getMeasuredHeight();
        int childState = mSurfaceView.getMeasuredState();
        setMeasuredDimension(resolveSizeAndState(width, widthMeasureSpec, childState),
                resolveSizeAndState(height, heightMeasureSpec,
                        childState << MEASURED_HEIGHT_STATE_SHIFT));
    
public booleanonUnhandledInputEvent(android.view.InputEvent event)
Called when an unhandled input event also has not been handled by the user provided callback. This is the last chance to handle the unhandled input event in the TvView.

param
event The input event.
return
If you handled the event, return {@code true}. If you want to allow the event to be handled by the next receiver, return {@code false}.

        return false;
    
protected voidonVisibilityChanged(android.view.View changedView, int visibility)

        super.onVisibilityChanged(changedView, visibility);
        mSurfaceView.setVisibility(visibility);
        if (visibility == View.VISIBLE) {
            createSessionOverlayView();
        } else {
            removeSessionOverlayView();
        }
    
private voidrelayoutSessionOverlayView()

        if (mSession == null || !isAttachedToWindow() || !mOverlayViewCreated
                || mWindowZOrder != ZORDER_MEDIA) {
            return;
        }
        Rect viewFrame = getViewFrameOnScreen();
        if (viewFrame.equals(mOverlayViewFrame)) {
            return;
        }
        mSession.relayoutOverlayView(viewFrame);
        mOverlayViewFrame = viewFrame;
    
private voidrelease()

        mAppPrivateCommandAction = null;
        mAppPrivateCommandData = null;

        setSessionSurface(null);
        removeSessionOverlayView();
        mUseRequestedSurfaceLayout = false;
        mSession.release();
        mSession = null;
        mSessionCallback = null;
    
private voidremoveSessionOverlayView()

        if (mSession == null || !mOverlayViewCreated) {
            return;
        }
        mSession.removeOverlayView();
        mOverlayViewCreated = false;
        mOverlayViewFrame = null;
    
public voidrequestUnblockContent(TvContentRating unblockedRating)
Requests to unblock TV content according to the given rating.

This notifies TV input that blocked content is now OK to play.

param
unblockedRating A TvContentRating to unblock.
see
TvInputService.Session#notifyContentBlocked(TvContentRating)
hide

        if (mSession != null) {
            mSession.requestUnblockContent(unblockedRating);
        }
    
public voidreset()
Resets this TvView.

This method is primarily used to un-tune the current TvView.

        if (DEBUG) Log.d(TAG, "reset()");
        synchronized (sMainTvViewLock) {
            if (this == sMainTvView.get()) {
                sMainTvView = NULL_TV_VIEW;
            }
        }
        resetInternal();
    
private voidresetInternal()

        if (mSession != null) {
            release();
            resetSurfaceView();
        }
    
private voidresetSurfaceView()

        if (mSurfaceView != null) {
            mSurfaceView.getHolder().removeCallback(mSurfaceHolderCallback);
            removeView(mSurfaceView);
        }
        mSurface = null;
        mSurfaceView = new SurfaceView(getContext(), mAttrs, mDefStyleAttr) {
            @Override
            protected void updateWindow(boolean force, boolean redrawNeeded) {
                super.updateWindow(force, redrawNeeded);
                relayoutSessionOverlayView();
            }};
        mSurfaceView.getHolder().addCallback(mSurfaceHolderCallback);
        if (mWindowZOrder == ZORDER_MEDIA_OVERLAY) {
            mSurfaceView.setZOrderMediaOverlay(true);
        } else if (mWindowZOrder == ZORDER_ON_TOP) {
            mSurfaceView.setZOrderOnTop(true);
        }
        addView(mSurfaceView);
    
public voidselectTrack(int type, java.lang.String trackId)
Selects a track.

param
type The type of the track to select. The type can be {@link TvTrackInfo#TYPE_AUDIO}, {@link TvTrackInfo#TYPE_VIDEO} or {@link TvTrackInfo#TYPE_SUBTITLE}.
param
trackId The ID of the track to select. {@code null} means to unselect the current track for a given type.
see
#getTracks
see
#getSelectedTrack

        if (mSession != null) {
            mSession.selectTrack(type, trackId);
        }
    
public voidsendAppPrivateCommand(java.lang.String action, android.os.Bundle data)
Calls {@link TvInputService.Session#appPrivateCommand(String, Bundle) TvInputService.Session.appPrivateCommand()} on the current TvView.

param
action The name of the private command to send. This must be a scoped name, i.e. prefixed with a package name you own, so that different developers will not create conflicting commands.
param
data An optional bundle to send with the command.
hide

        if (TextUtils.isEmpty(action)) {
            throw new IllegalArgumentException("action cannot be null or an empty string");
        }
        if (mSession != null) {
            mSession.sendAppPrivateCommand(action, data);
        } else {
            Log.w(TAG, "sendAppPrivateCommand - session not created (action " + action + " cached)");
            if (mAppPrivateCommandAction != null) {
                Log.w(TAG, "previous cached action " + action + " removed");
            }
            mAppPrivateCommandAction = action;
            mAppPrivateCommandData = data;
        }
    
public voidsetCallback(android.media.tv.TvView$TvInputCallback callback)
Sets the callback to be invoked when an event is dispatched to this TvView.

param
callback The callback to receive events. A value of {@code null} removes any existing callbacks.

        mCallback = callback;
    
public voidsetCaptionEnabled(boolean enabled)
Enables or disables the caption in this TvView.

Note that this method does not take any effect unless the current TvView is tuned.

param
enabled {@code true} to enable, {@code false} to disable.

        mCaptionEnabled = enabled ? CAPTION_ENABLED : CAPTION_DISABLED;
        if (mSession != null) {
            mSession.setCaptionEnabled(enabled);
        }
    
public voidsetMain()
Sets this as the main {@link TvView}.

The main {@link TvView} is a {@link TvView} whose corresponding TV input determines the HDMI-CEC active source device. For an HDMI port input, one of source devices that is connected to that HDMI port becomes the active source. For an HDMI-CEC logical device input, the corresponding HDMI-CEC logical device becomes the active source. For any non-HDMI input (including the tuner, composite, S-Video, etc.), the internal device (= TV itself) becomes the active source.

First tuned {@link TvView} becomes main automatically, and keeps to be main until either {@link #reset} is called for the main {@link TvView} or {@link #setMain} is called for other {@link TvView}.

hide

        synchronized (sMainTvViewLock) {
            sMainTvView = new WeakReference<>(this);
            if (hasWindowFocus() && mSession != null) {
                mSession.setMain();
            }
        }
    
public voidsetOnUnhandledInputEventListener(android.media.tv.TvView$OnUnhandledInputEventListener listener)
Registers a callback to be invoked when an input event is not handled by the bound TV input.

param
listener The callback to be invoked when the unhandled input event is received.

        mOnUnhandledInputEventListener = listener;
    
private voidsetSessionSurface(android.view.Surface surface)

        if (mSession == null) {
            return;
        }
        mSession.setSurface(surface);
    
public voidsetStreamVolume(float volume)
Sets the relative stream volume of this session to handle a change of audio focus.

param
volume A volume value between 0.0f to 1.0f.

        if (DEBUG) Log.d(TAG, "setStreamVolume(" + volume + ")");
        mHasStreamVolume = true;
        mStreamVolume = volume;
        if (mSession == null) {
            // Volume will be set once the connection has been made.
            return;
        }
        mSession.setStreamVolume(volume);
    
public voidsetZOrderMediaOverlay(boolean isMediaOverlay)
Sets the Z order of a window owning the surface of this TvView above the normal TvView but below an application.

see
SurfaceView#setZOrderMediaOverlay
hide

        if (isMediaOverlay) {
            mWindowZOrder = ZORDER_MEDIA_OVERLAY;
            removeSessionOverlayView();
        } else {
            mWindowZOrder = ZORDER_MEDIA;
            createSessionOverlayView();
        }
        if (mSurfaceView != null) {
            // ZOrderOnTop(false) removes WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
            // from WindowLayoutParam as well as changes window type.
            mSurfaceView.setZOrderOnTop(false);
            mSurfaceView.setZOrderMediaOverlay(isMediaOverlay);
        }
    
public voidsetZOrderOnTop(boolean onTop)
Sets the Z order of a window owning the surface of this TvView on top of an application.

see
SurfaceView#setZOrderOnTop
hide

        if (onTop) {
            mWindowZOrder = ZORDER_ON_TOP;
            removeSessionOverlayView();
        } else {
            mWindowZOrder = ZORDER_MEDIA;
            createSessionOverlayView();
        }
        if (mSurfaceView != null) {
            mSurfaceView.setZOrderMediaOverlay(false);
            mSurfaceView.setZOrderOnTop(onTop);
        }
     
public voidtune(java.lang.String inputId, android.net.Uri channelUri, android.os.Bundle params)
Tunes to a given channel.

param
inputId The ID of TV input which will play the given channel.
param
channelUri The URI of a channel.
param
params Extra parameters which might be handled with the tune event.
hide

        if (DEBUG) Log.d(TAG, "tune(" + channelUri + ")");
        if (TextUtils.isEmpty(inputId)) {
            throw new IllegalArgumentException("inputId cannot be null or an empty string");
        }
        synchronized (sMainTvViewLock) {
            if (sMainTvView.get() == null) {
                sMainTvView = new WeakReference<>(this);
            }
        }
        if (mSessionCallback != null && mSessionCallback.mInputId.equals(inputId)) {
            if (mSession != null) {
                mSession.tune(channelUri, params);
            } else {
                // Session is not created yet. Replace the channel which will be set once the
                // session is made.
                mSessionCallback.mChannelUri = channelUri;
                mSessionCallback.mTuneParams = params;
            }
        } else {
            resetInternal();
            // When createSession() is called multiple times before the callback is called,
            // only the callback of the last createSession() call will be actually called back.
            // The previous callbacks will be ignored. For the logic, mSessionCallback
            // is newly assigned for every createSession request and compared with
            // MySessionCreateCallback.this.
            mSessionCallback = new MySessionCallback(inputId, channelUri, params);
            if (mTvInputManager != null) {
                mTvInputManager.createSession(inputId, mSessionCallback, mHandler);
            }
        }
    
public voidtune(java.lang.String inputId, android.net.Uri channelUri)
Tunes to a given channel.

param
inputId The ID of TV input which will play the given channel.
param
channelUri The URI of a channel.

        tune(inputId, channelUri, null);