All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.android.app.MediaRouteButton Maven / Gradle / Ivy

Go to download

A library jar that provides APIs for Applications written for the Google Android Platform.

There is a newer version: 15-robolectric-12650502
Show newest version
/*
 * Copyright (C) 2012 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 android.app;

import com.android.internal.R;
import com.android.internal.app.MediaRouteDialogPresenter;

import android.annotation.NonNull;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
import android.media.MediaRouter;
import android.media.MediaRouter.RouteGroup;
import android.media.MediaRouter.RouteInfo;
import android.util.AttributeSet;
import android.view.SoundEffectConstants;
import android.view.View;

public class MediaRouteButton extends View {
    private final MediaRouter mRouter;
    private final MediaRouterCallback mCallback;

    private int mRouteTypes;

    private boolean mAttachedToWindow;

    private Drawable mRemoteIndicator;
    private boolean mRemoteActive;
    private boolean mIsConnecting;

    private int mMinWidth;
    private int mMinHeight;

    private OnClickListener mExtendedSettingsClickListener;

    // The checked state is used when connected to a remote route.
    private static final int[] CHECKED_STATE_SET = {
        R.attr.state_checked
    };

    // The activated state is used while connecting to a remote route.
    private static final int[] ACTIVATED_STATE_SET = {
        R.attr.state_activated
    };

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

    public MediaRouteButton(Context context, AttributeSet attrs) {
        this(context, attrs, com.android.internal.R.attr.mediaRouteButtonStyle);
    }

    public MediaRouteButton(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public MediaRouteButton(
            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);

        mRouter = (MediaRouter)context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
        mCallback = new MediaRouterCallback();

        final TypedArray a = context.obtainStyledAttributes(attrs,
                com.android.internal.R.styleable.MediaRouteButton, defStyleAttr, defStyleRes);
        setRemoteIndicatorDrawable(a.getDrawable(
                com.android.internal.R.styleable.MediaRouteButton_externalRouteEnabledDrawable));
        mMinWidth = a.getDimensionPixelSize(
                com.android.internal.R.styleable.MediaRouteButton_minWidth, 0);
        mMinHeight = a.getDimensionPixelSize(
                com.android.internal.R.styleable.MediaRouteButton_minHeight, 0);
        final int routeTypes = a.getInteger(
                com.android.internal.R.styleable.MediaRouteButton_mediaRouteTypes,
                MediaRouter.ROUTE_TYPE_LIVE_AUDIO);
        a.recycle();

        setClickable(true);

        setRouteTypes(routeTypes);
    }

    /**
     * Gets the media route types for filtering the routes that the user can
     * select using the media route chooser dialog.
     *
     * @return The route types.
     */
    public int getRouteTypes() {
        return mRouteTypes;
    }

    /**
     * Sets the types of routes that will be shown in the media route chooser dialog
     * launched by this button.
     *
     * @param types The route types to match.
     */
    public void setRouteTypes(int types) {
        if (mRouteTypes != types) {
            if (mAttachedToWindow && mRouteTypes != 0) {
                mRouter.removeCallback(mCallback);
            }

            mRouteTypes = types;

            if (mAttachedToWindow && types != 0) {
                mRouter.addCallback(types, mCallback,
                        MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
            }

            refreshRoute();
        }
    }

    public void setExtendedSettingsClickListener(OnClickListener listener) {
        mExtendedSettingsClickListener = listener;
    }

    /**
     * Show the route chooser or controller dialog.
     * 

* If the default route is selected or if the currently selected route does * not match the {@link #getRouteTypes route types}, 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. *

* This will attach a {@link DialogFragment} to the containing Activity. *

*/ public void showDialog() { showDialogInternal(); } boolean showDialogInternal() { if (!mAttachedToWindow) { return false; } DialogFragment f = MediaRouteDialogPresenter.showDialogFragment(getActivity(), mRouteTypes, mExtendedSettingsClickListener); return f != null; } private Activity getActivity() { // 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(); } throw new IllegalStateException("The MediaRouteButton's Context is not an Activity."); } @Override public void setContentDescription(CharSequence contentDescription) { super.setContentDescription(contentDescription); setTooltipText(contentDescription); } @Override public boolean performClick() { // Send the appropriate accessibility events and call listeners boolean handled = super.performClick(); if (!handled) { playSoundEffect(SoundEffectConstants.CLICK); } return showDialogInternal() || handled; } @Override 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. Checked is used to express the connecting // drawable state and it's mutually exclusive with activated for the purposes // of state selection here. if (mIsConnecting) { mergeDrawableStates(drawableState, CHECKED_STATE_SET); } else if (mRemoteActive) { mergeDrawableStates(drawableState, ACTIVATED_STATE_SET); } return drawableState; } @Override protected void drawableStateChanged() { super.drawableStateChanged(); final Drawable remoteIndicator = mRemoteIndicator; if (remoteIndicator != null && remoteIndicator.isStateful() && remoteIndicator.setState(getDrawableState())) { invalidateDrawable(remoteIndicator); } } private void setRemoteIndicatorDrawable(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(); } @Override protected boolean verifyDrawable(@NonNull Drawable who) { return super.verifyDrawable(who) || who == mRemoteIndicator; } @Override public void jumpDrawablesToCurrentState() { super.jumpDrawablesToCurrentState(); if (mRemoteIndicator != null) { mRemoteIndicator.jumpToCurrentState(); } } @Override public void setVisibility(int visibility) { super.setVisibility(visibility); if (mRemoteIndicator != null) { mRemoteIndicator.setVisible(getVisibility() == VISIBLE, false); } } @Override public void onAttachedToWindow() { super.onAttachedToWindow(); mAttachedToWindow = true; if (mRouteTypes != 0) { mRouter.addCallback(mRouteTypes, mCallback, MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY); } refreshRoute(); } @Override public void onDetachedFromWindow() { mAttachedToWindow = false; if (mRouteTypes != 0) { mRouter.removeCallback(mCallback); } super.onDetachedFromWindow(); } @Override protected void onMeasure(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 width = Math.max(mMinWidth, mRemoteIndicator != null ? mRemoteIndicator.getIntrinsicWidth() + getPaddingLeft() + getPaddingRight() : 0); final int height = Math.max(mMinHeight, mRemoteIndicator != null ? mRemoteIndicator.getIntrinsicHeight() + getPaddingTop() + getPaddingBottom() : 0); int measuredWidth; switch (widthMode) { case MeasureSpec.EXACTLY: measuredWidth = widthSize; break; case MeasureSpec.AT_MOST: measuredWidth = Math.min(widthSize, width); break; default: case MeasureSpec.UNSPECIFIED: measuredWidth = width; break; } int measuredHeight; switch (heightMode) { case MeasureSpec.EXACTLY: measuredHeight = heightSize; break; case MeasureSpec.AT_MOST: measuredHeight = Math.min(heightSize, height); break; default: case MeasureSpec.UNSPECIFIED: measuredHeight = height; break; } setMeasuredDimension(measuredWidth, measuredHeight); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mRemoteIndicator == null) return; 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); } private void refreshRoute() { final MediaRouter.RouteInfo route = mRouter.getSelectedRoute(); final boolean isRemote = !route.isDefault() && route.matchesTypes(mRouteTypes); 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(); } if (mAttachedToWindow) { setEnabled(mRouter.isRouteAvailable(mRouteTypes, MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE)); } if (mRemoteIndicator != null && mRemoteIndicator.getCurrent() instanceof AnimationDrawable) { AnimationDrawable curDrawable = (AnimationDrawable) mRemoteIndicator.getCurrent(); if (mAttachedToWindow) { if ((needsRefresh || isConnecting) && !curDrawable.isRunning()) { curDrawable.start(); } } else if (isRemote && !isConnecting) { // When the route is already connected before the view is attached, show the last // frame of the connected animation immediately. if (curDrawable.isRunning()) { curDrawable.stop(); } curDrawable.selectDrawable(curDrawable.getNumberOfFrames() - 1); } } } private final class MediaRouterCallback extends MediaRouter.SimpleCallback { @Override public void onRouteAdded(MediaRouter router, RouteInfo info) { refreshRoute(); } @Override public void onRouteRemoved(MediaRouter router, RouteInfo info) { refreshRoute(); } @Override public void onRouteChanged(MediaRouter router, RouteInfo info) { refreshRoute(); } @Override public void onRouteSelected(MediaRouter router, int type, RouteInfo info) { refreshRoute(); } @Override public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) { refreshRoute(); } @Override public void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group, int index) { refreshRoute(); } @Override public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) { refreshRoute(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy