FileDocCategorySizeDatePackage
MediaRouteButton.javaAPI DocAndroid 5.1 API19524Thu Mar 12 22:22:56 GMT 2015android.support.v7.app

MediaRouteButton

public class MediaRouteButton extends android.view.View
The media route button allows the user to select routes and to control the currently selected route.

The application must specify the kinds of routes that the user should be allowed to select by specifying a {@link MediaRouteSelector selector} with the {@link #setRouteSelector} method.

When the default route is selected or when the currently selected route does not match the {@link #getRouteSelector() selector}, the button will appear in an inactive state indicating that the application is not connected to a route of the kind that it wants to use. Clicking on the button opens a {@link MediaRouteChooserDialog} to allow the user to select a route. If no non-default routes match the selector and it is not possible for an active scan to discover any matching routes, then the button is disabled and cannot be clicked.

When a non-default route is selected that matches the selector, the button will appear in an active state indicating that the application is connected to a route of the kind that it wants to use. The button may also appear in an intermediary connecting state if the route is in the process of connecting to the destination but has not yet completed doing so. In either case, clicking on the button opens a {@link MediaRouteControllerDialog} to allow the user to control or disconnect from the current route.

Prerequisites

To use the media route button, the activity must be a subclass of {@link FragmentActivity} from the android.support.v4 support library. Refer to support library documentation for details.

see
MediaRouteActionProvider
see
#setRouteSelector

Fields Summary
private static final String
TAG
private static final String
CHOOSER_FRAGMENT_TAG
private static final String
CONTROLLER_FRAGMENT_TAG
private final android.support.v7.media.MediaRouter
mRouter
private final MediaRouterCallback
mCallback
private android.support.v7.media.MediaRouteSelector
mSelector
private MediaRouteDialogFactory
mDialogFactory
private boolean
mAttachedToWindow
private android.graphics.drawable.Drawable
mRemoteIndicator
private boolean
mRemoteActive
private boolean
mCheatSheetEnabled
private boolean
mIsConnecting
private int
mMinWidth
private int
mMinHeight
private static final int[]
CHECKED_STATE_SET
private static final int[]
CHECKABLE_STATE_SET
Constructors Summary
public MediaRouteButton(android.content.Context context)


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

        this(context, attrs, R.attr.mediaRouteButtonStyle);
    
public MediaRouteButton(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr)

        super(MediaRouterThemeHelper.createThemedContext(context), attrs, defStyleAttr);
        context = getContext();

        mRouter = MediaRouter.getInstance(context);
        mCallback = new MediaRouterCallback();

        TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.MediaRouteButton, defStyleAttr, 0);
        setRemoteIndicatorDrawable(a.getDrawable(
                R.styleable.MediaRouteButton_externalRouteEnabledDrawable));
        mMinWidth = a.getDimensionPixelSize(
                R.styleable.MediaRouteButton_android_minWidth, 0);
        mMinHeight = a.getDimensionPixelSize(
                R.styleable.MediaRouteButton_android_minHeight, 0);
        a.recycle();

        setClickable(true);
        setLongClickable(true);
    
Methods Summary
protected voiddrawableStateChanged()

        super.drawableStateChanged();

        if (mRemoteIndicator != null) {
            int[] myDrawableState = getDrawableState();
            mRemoteIndicator.setState(myDrawableState);
            invalidate();
        }
    
private android.app.ActivitygetActivity()

        // Gross way of unwrapping the Activity so we can get the FragmentManager
        Context context = getContext();
        while (context instanceof ContextWrapper) {
            if (context instanceof Activity) {
                return (Activity)context;
            }
            context = ((ContextWrapper)context).getBaseContext();
        }
        return null;
    
public MediaRouteDialogFactorygetDialogFactory()
Gets the media route dialog factory to use when showing the route chooser or controller dialog.

return
The dialog factory, never null.

        return mDialogFactory;
    
private android.support.v4.app.FragmentManagergetFragmentManager()

        Activity activity = getActivity();
        if (activity instanceof FragmentActivity) {
            return ((FragmentActivity)activity).getSupportFragmentManager();
        }
        return null;
    
public android.support.v7.media.MediaRouteSelectorgetRouteSelector()
Gets the media route selector for filtering the routes that the user can select using the media route chooser dialog.

return
The selector, never null.

        return mSelector;
    
public voidjumpDrawablesToCurrentState()

        // We can't call super to handle the background so we do it ourselves.
        //super.jumpDrawablesToCurrentState();
        if (getBackground() != null) {
            DrawableCompat.jumpToCurrentState(getBackground());
        }

        // Handle our own remote indicator.
        if (mRemoteIndicator != null) {
            DrawableCompat.jumpToCurrentState(mRemoteIndicator);
        }
    
public voidonAttachedToWindow()

        super.onAttachedToWindow();

        mAttachedToWindow = true;
        if (!mSelector.isEmpty()) {
            mRouter.addCallback(mSelector, mCallback);
        }
        refreshRoute();
    
protected int[]onCreateDrawableState(int extraSpace)

        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);

        // Technically we should be handling this more completely, but these
        // are implementation details here. Checkable is used to express the connecting
        // drawable state and it's mutually exclusive with check for the purposes
        // of state selection here.
        if (mIsConnecting) {
            mergeDrawableStates(drawableState, CHECKABLE_STATE_SET);
        } else if (mRemoteActive) {
            mergeDrawableStates(drawableState, CHECKED_STATE_SET);
        }
        return drawableState;
    
public voidonDetachedFromWindow()

        mAttachedToWindow = false;
        if (!mSelector.isEmpty()) {
            mRouter.removeCallback(mCallback);
        }

        super.onDetachedFromWindow();
    
protected voidonDraw(android.graphics.Canvas canvas)

        super.onDraw(canvas);

        if (mRemoteIndicator != null) {
            final int left = getPaddingLeft();
            final int right = getWidth() - getPaddingRight();
            final int top = getPaddingTop();
            final int bottom = getHeight() - getPaddingBottom();

            final int drawWidth = mRemoteIndicator.getIntrinsicWidth();
            final int drawHeight = mRemoteIndicator.getIntrinsicHeight();
            final int drawLeft = left + (right - left - drawWidth) / 2;
            final int drawTop = top + (bottom - top - drawHeight) / 2;

            mRemoteIndicator.setBounds(drawLeft, drawTop,
                    drawLeft + drawWidth, drawTop + drawHeight);
            mRemoteIndicator.draw(canvas);
        }
    
protected voidonMeasure(int widthMeasureSpec, int heightMeasureSpec)

        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        final int minWidth = Math.max(mMinWidth,
                mRemoteIndicator != null ? mRemoteIndicator.getIntrinsicWidth() : 0);
        final int minHeight = Math.max(mMinHeight,
                mRemoteIndicator != null ? mRemoteIndicator.getIntrinsicHeight() : 0);

        int width;
        switch (widthMode) {
            case MeasureSpec.EXACTLY:
                width = widthSize;
                break;
            case MeasureSpec.AT_MOST:
                width = Math.min(widthSize, minWidth + getPaddingLeft() + getPaddingRight());
                break;
            default:
            case MeasureSpec.UNSPECIFIED:
                width = minWidth + getPaddingLeft() + getPaddingRight();
                break;
        }

        int height;
        switch (heightMode) {
            case MeasureSpec.EXACTLY:
                height = heightSize;
                break;
            case MeasureSpec.AT_MOST:
                height = Math.min(heightSize, minHeight + getPaddingTop() + getPaddingBottom());
                break;
            default:
            case MeasureSpec.UNSPECIFIED:
                height = minHeight + getPaddingTop() + getPaddingBottom();
                break;
        }

        setMeasuredDimension(width, height);
    
public booleanperformClick()

        // Send the appropriate accessibility events and call listeners
        boolean handled = super.performClick();
        if (!handled) {
            playSoundEffect(SoundEffectConstants.CLICK);
        }
        return showDialog() || handled;
    
public booleanperformLongClick()

        if (super.performLongClick()) {
            return true;
        }

        if (!mCheatSheetEnabled) {
            return false;
        }

        final CharSequence contentDesc = getContentDescription();
        if (TextUtils.isEmpty(contentDesc)) {
            // Don't show the cheat sheet if we have no description
            return false;
        }

        final int[] screenPos = new int[2];
        final Rect displayFrame = new Rect();
        getLocationOnScreen(screenPos);
        getWindowVisibleDisplayFrame(displayFrame);

        final Context context = getContext();
        final int width = getWidth();
        final int height = getHeight();
        final int midy = screenPos[1] + height / 2;
        final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;

        Toast cheatSheet = Toast.makeText(context, contentDesc, Toast.LENGTH_SHORT);
        if (midy < displayFrame.height()) {
            // Show along the top; follow action buttons
            cheatSheet.setGravity(Gravity.TOP | GravityCompat.END,
                    screenWidth - screenPos[0] - width / 2, height);
        } else {
            // Show along the bottom center
            cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height);
        }
        cheatSheet.show();
        performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
        return true;
    
private voidrefreshRoute()

        if (mAttachedToWindow) {
            final MediaRouter.RouteInfo route = mRouter.getSelectedRoute();
            final boolean isRemote = !route.isDefault() && route.matchesSelector(mSelector);
            final boolean isConnecting = isRemote && route.isConnecting();

            boolean needsRefresh = false;
            if (mRemoteActive != isRemote) {
                mRemoteActive = isRemote;
                needsRefresh = true;
            }
            if (mIsConnecting != isConnecting) {
                mIsConnecting = isConnecting;
                needsRefresh = true;
            }

            if (needsRefresh) {
                refreshDrawableState();
            }

            setEnabled(mRouter.isRouteAvailable(mSelector,
                    MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE));
        }
    
voidsetCheatSheetEnabled(boolean enable)
Sets whether to enable showing a toast with the content descriptor of the button when the button is long pressed.

        mCheatSheetEnabled = enable;
    
public voidsetDialogFactory(MediaRouteDialogFactory factory)
Sets the media route dialog factory to use when showing the route chooser or controller dialog.

param
factory The dialog factory, must not be null.

        if (factory == null) {
            throw new IllegalArgumentException("factory must not be null");
        }

        mDialogFactory = factory;
    
private voidsetRemoteIndicatorDrawable(android.graphics.drawable.Drawable d)

        if (mRemoteIndicator != null) {
            mRemoteIndicator.setCallback(null);
            unscheduleDrawable(mRemoteIndicator);
        }
        mRemoteIndicator = d;
        if (d != null) {
            d.setCallback(this);
            d.setState(getDrawableState());
            d.setVisible(getVisibility() == VISIBLE, false);
        }

        refreshDrawableState();
    
public voidsetRouteSelector(android.support.v7.media.MediaRouteSelector selector)
Sets the media route selector for filtering the routes that the user can select using the media route chooser dialog.

param
selector The selector, must not be null.

        if (selector == null) {
            throw new IllegalArgumentException("selector must not be null");
        }

        if (!mSelector.equals(selector)) {
            if (mAttachedToWindow) {
                if (!mSelector.isEmpty()) {
                    mRouter.removeCallback(mCallback);
                }
                if (!selector.isEmpty()) {
                    mRouter.addCallback(selector, mCallback);
                }
            }
            mSelector = selector;
            refreshRoute();
        }
    
public voidsetVisibility(int visibility)

        super.setVisibility(visibility);

        if (mRemoteIndicator != null) {
            mRemoteIndicator.setVisible(getVisibility() == VISIBLE, false);
        }
    
public booleanshowDialog()
Show the route chooser or controller dialog.

If the default route is selected or if the currently selected route does not match the {@link #getRouteSelector selector}, then shows the route chooser dialog. Otherwise, shows the route controller dialog to offer the user a choice to disconnect from the route or perform other control actions such as setting the route's volume.

The application can customize the dialogs by calling {@link #setDialogFactory} to provide a customized dialog factory.

return
True if the dialog was actually shown.
throws
IllegalStateException if the activity is not a subclass of {@link FragmentActivity}.

        if (!mAttachedToWindow) {
            return false;
        }

        final FragmentManager fm = getFragmentManager();
        if (fm == null) {
            throw new IllegalStateException("The activity must be a subclass of FragmentActivity");
        }

        MediaRouter.RouteInfo route = mRouter.getSelectedRoute();
        if (route.isDefault() || !route.matchesSelector(mSelector)) {
            if (fm.findFragmentByTag(CHOOSER_FRAGMENT_TAG) != null) {
                Log.w(TAG, "showDialog(): Route chooser dialog already showing!");
                return false;
            }
            MediaRouteChooserDialogFragment f =
                    mDialogFactory.onCreateChooserDialogFragment();
            f.setRouteSelector(mSelector);
            f.show(fm, CHOOSER_FRAGMENT_TAG);
        } else {
            if (fm.findFragmentByTag(CONTROLLER_FRAGMENT_TAG) != null) {
                Log.w(TAG, "showDialog(): Route controller dialog already showing!");
                return false;
            }
            MediaRouteControllerDialogFragment f =
                    mDialogFactory.onCreateControllerDialogFragment();
            f.show(fm, CONTROLLER_FRAGMENT_TAG);
        }
        return true;
    
protected booleanverifyDrawable(android.graphics.drawable.Drawable who)

        return super.verifyDrawable(who) || who == mRemoteIndicator;